diff --git a/.deployment/docker-compose.yml b/.deployment/docker-compose.yml index 5a1329f..01a5a77 100644 --- a/.deployment/docker-compose.yml +++ b/.deployment/docker-compose.yml @@ -56,20 +56,20 @@ services: - "traefik.http.routers.tapas-auction-house.entryPoints=web,websecure" - "traefik.http.routers.tapas-auction-house.tls.certresolver=le" - roster: + assignment: image: openjdk - command: "java -jar /data/roster-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/assignment-0.0.1-SNAPSHOT.jar" restart: unless-stopped volumes: - ./:/data/ labels: - "traefik.enable=true" - - "traefik.http.routers.roster.rule=Host(`roster.${PUB_IP}.nip.io`)" - - "traefik.http.routers.roster.service=roster" - - "traefik.http.services.roster.loadbalancer.server.port=8082" - - "traefik.http.routers.roster.tls=true" - - "traefik.http.routers.roster.entryPoints=web,websecure" - - "traefik.http.routers.roster.tls.certresolver=le" + - "traefik.http.routers.assignment.rule=Host(`assignment.${PUB_IP}.nip.io`)" + - "traefik.http.routers.assignment.service=assignment" + - "traefik.http.services.assignment.loadbalancer.server.port=8082" + - "traefik.http.routers.assignment.tls=true" + - "traefik.http.routers.assignment.entryPoints=web,websecure" + - "traefik.http.routers.assignment.tls.certresolver=le" executor-pool: image: openjdk @@ -86,38 +86,38 @@ services: - "traefik.http.routers.executor-pool.entryPoints=web,websecure" - "traefik.http.routers.executor-pool.tls.certresolver=le" - executor-computation: + executor1: image: openjdk - command: "java -jar /data/executor-computation-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/executor1-0.0.1-SNAPSHOT.jar" restart: unless-stopped depends_on: - executor-pool - - roster + - assignment volumes: - ./:/data/ labels: - "traefik.enable=true" - - "traefik.http.routers.executor-computation.rule=Host(`executor-computation.${PUB_IP}.nip.io`)" - - "traefik.http.routers.executor-computation.service=executor-computation" - - "traefik.http.services.executor-computation.loadbalancer.server.port=8084" - - "traefik.http.routers.executor-computation.tls=true" - - "traefik.http.routers.executor-computation.entryPoints=web,websecure" - - "traefik.http.routers.executor-computation.tls.certresolver=le" + - "traefik.http.routers.executor1.rule=Host(`executor1.${PUB_IP}.nip.io`)" + - "traefik.http.routers.executor1.service=executor1" + - "traefik.http.services.executor1.loadbalancer.server.port=8084" + - "traefik.http.routers.executor1.tls=true" + - "traefik.http.routers.executor1.entryPoints=web,websecure" + - "traefik.http.routers.executor1.tls.certresolver=le" - executor-robot: + executor2: image: openjdk - command: "java -jar /data/executor-robot-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/executor2-0.0.1-SNAPSHOT.jar" restart: unless-stopped depends_on: - executor-pool - - roster + - assignment volumes: - ./:/data/ labels: - "traefik.enable=true" - - "traefik.http.routers.executor-robot.rule=Host(`executor-robot.${PUB_IP}.nip.io`)" - - "traefik.http.routers.executor-robot.service=executor-robot" - - "traefik.http.services.executor-robot.loadbalancer.server.port=8085" - - "traefik.http.routers.executor-robot.tls=true" - - "traefik.http.routers.executor-robot.entryPoints=web,websecure" - - "traefik.http.routers.executor-robot.tls.certresolver=le" + - "traefik.http.routers.executor2.rule=Host(`executor2.${PUB_IP}.nip.io`)" + - "traefik.http.routers.executor2.service=executor2" + - "traefik.http.services.executor2.loadbalancer.server.port=8085" + - "traefik.http.routers.executor2.tls=true" + - "traefik.http.routers.executor2.entryPoints=web,websecure" + - "traefik.http.routers.executor2.tls.certresolver=le" diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 7290452..d223887 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -33,33 +33,30 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Build common library - run: mvn -f common/pom.xml --batch-mode --update-snapshots install + - name: Build with Maven + run: mvn -f assignment/pom.xml --batch-mode --update-snapshots verify + - run: cp ./assignment/target/assignment-0.0.1-SNAPSHOT.jar ./target - - name: Build roster service - run: mvn -f roster/pom.xml --batch-mode --update-snapshots verify - - run: cp ./roster/target/roster-0.0.1-SNAPSHOT.jar ./target - - - name: Build executor-pool service + - name: Build with Maven run: mvn -f executor-pool/pom.xml --batch-mode --update-snapshots verify - run: cp ./executor-pool/target/executor-pool-0.0.1-SNAPSHOT.jar ./target - - name: Build executor-base library + - name: Build with Maven run: mvn -f executor-base/pom.xml --batch-mode --update-snapshots install - - name: Build executor-computation service - run: mvn -f executor-computation/pom.xml --batch-mode --update-snapshots verify - - run: cp ./executor-computation/target/executor-computation-0.0.1-SNAPSHOT.jar ./target + - name: Build with Maven + run: mvn -f executor1/pom.xml --batch-mode --update-snapshots verify + - run: cp ./executor1/target/executor1-0.0.1-SNAPSHOT.jar ./target - - name: Build executor-robot service - run: mvn -f executor-robot/pom.xml --batch-mode --update-snapshots verify - - run: cp ./executor-robot/target/executor-robot-0.0.1-SNAPSHOT.jar ./target + - name: Build with Maven + run: mvn -f executor2/pom.xml --batch-mode --update-snapshots verify + - run: cp ./executor2/target/executor2-0.0.1-SNAPSHOT.jar ./target - - name: Build tapas-task service + - name: Build with Maven run: mvn -f tapas-tasks/pom.xml --batch-mode --update-snapshots verify - run: cp ./tapas-tasks/target/tapas-tasks-0.0.1-SNAPSHOT.jar ./target - - name: Build tapas-auction-house service + - name: Build with Maven run: mvn -f tapas-auction-house/pom.xml --batch-mode --update-snapshots verify - run: cp ./tapas-auction-house/target/tapas-auction-house-0.0.1-SNAPSHOT.jar ./target diff --git a/.github/workflows/ci.assignment.yml b/.github/workflows/ci.assignment.yml new file mode 100644 index 0000000..394fe54 --- /dev/null +++ b/.github/workflows/ci.assignment.yml @@ -0,0 +1,41 @@ +name: CI Assignment +on: + push: + branches: [main, dev] + paths: + - "assignment/**" + pull_request: + branches: [main, dev] + paths: + - "assignment/**" + + workflow_dispatch: +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarCloud packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -f assignment/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-assignment diff --git a/.github/workflows/ci.executor-robot.yml b/.github/workflows/ci.executor1.yml similarity index 76% rename from .github/workflows/ci.executor-robot.yml rename to .github/workflows/ci.executor1.yml index 7a216aa..708d7d4 100644 --- a/.github/workflows/ci.executor-robot.yml +++ b/.github/workflows/ci.executor1.yml @@ -1,17 +1,15 @@ -name: CI executor-robot +name: CI Executor 1 on: push: branches: [main, dev] paths: - "executor-base/**" - - "executor-robot/**" - - "common/**" + - "executor1/**" pull_request: branches: [main, dev] paths: - "executor-base/**" - - "executor-robot/**" - - "common/**" + - "executor1/**" workflow_dispatch: jobs: @@ -38,12 +36,10 @@ jobs: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Build executorBase - run: mvn -f common/pom.xml -B install - name: Build executorBase run: mvn -f executor-base/pom.xml -B install - name: Build and analyze env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -f executor-robot/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor1 + run: mvn -f executor1/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor1 diff --git a/.github/workflows/ci.executor-computation.yml b/.github/workflows/ci.executor2.yml similarity index 75% rename from .github/workflows/ci.executor-computation.yml rename to .github/workflows/ci.executor2.yml index 5832883..5ae38f0 100644 --- a/.github/workflows/ci.executor-computation.yml +++ b/.github/workflows/ci.executor2.yml @@ -1,17 +1,15 @@ -name: CI executor-computation +name: CI Executor 2 on: push: branches: [main, dev] paths: - "executor-base/**" - - "executor-computation/**" - - "common/**" + - "executor2/**" pull_request: branches: [main, dev] paths: - "executor-base/**" - - "executor-computation/**" - - "common/**" + - "executor2/**" workflow_dispatch: jobs: @@ -38,12 +36,10 @@ jobs: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Build common - run: mvn -f common/pom.xml -B install - name: Build executorBase run: mvn -f executor-base/pom.xml -B install - name: Build and analyze env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -f executor-computation/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor2 + run: mvn -f executor2/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor2 diff --git a/.github/workflows/ci.roster.yml b/.github/workflows/ci.roster.yml deleted file mode 100644 index b38355e..0000000 --- a/.github/workflows/ci.roster.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: CI Roster -on: - push: - branches: [main, dev] - paths: - - "roster/**" - - "common/**" - pull_request: - branches: [main, dev] - paths: - - "roster/**" - - "common/**" - - workflow_dispatch: -jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Cache SonarCloud packages - uses: actions/cache@v1 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Cache Maven packages - uses: actions/cache@v1 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Build common package - run: mvn -f common/pom.xml -B install - - name: Build and analyze - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -f roster/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-assignment diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java index 66ef496..8fda5ac 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java @@ -1,7 +1,5 @@ package ch.unisg.executorbase.executor.adapter.in.web; -import java.util.logging.Logger; - import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,8 +19,6 @@ public class TaskAvailableController { this.taskAvailableUseCase = taskAvailableUseCase; } - Logger logger = Logger.getLogger(TaskAvailableController.class.getName()); - /** * Controller for notification about new events. * @return 200 OK @@ -30,8 +26,6 @@ public class TaskAvailableController { @GetMapping(path = "/newtask/{taskType}", consumes = { "application/json" }) public ResponseEntity retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) { - logger.info("New " + taskType + " available"); - if (ExecutorType.contains(taskType.toUpperCase())) { TaskAvailableCommand command = new TaskAvailableCommand( ExecutorType.valueOf(taskType.toUpperCase())); diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java index 58f6287..a5ae910 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java @@ -16,9 +16,8 @@ import ch.unisg.executorbase.executor.domain.ExecutionFinishedEvent; public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort { - // TODO url doesn't get mapped bc no autowiring @Value("${roster.url}") - String server = "http://localhost:8082"; + String server; Logger logger = Logger.getLogger(ExecutionFinishedEventAdapter.class.getName()); @@ -29,9 +28,6 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort @Override public void publishExecutionFinishedEvent(ExecutionFinishedEvent event) { - System.out.println("HI"); - System.out.println(server); - String body = new JSONObject() .put("taskID", event.getTaskID()) .put("result", event.getResult()) @@ -45,9 +41,6 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); - - System.out.println(server); - try { client.send(request, HttpResponse.BodyHandlers.ofString()); } catch (InterruptedException e) { @@ -57,7 +50,7 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort logger.log(Level.SEVERE, e.getLocalizedMessage(), e); } - logger.log(Level.INFO, "Finish execution event sent with result: {0}", event.getResult()); + logger.log(Level.INFO, "Finish execution event sent with result: {}", event.getResult()); } diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java index dd82c81..3ed4e37 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java @@ -23,9 +23,8 @@ import org.json.JSONObject; @Primary public class GetAssignmentAdapter implements GetAssignmentPort { - // TODO Not working for now bc it doesn't get autowired @Value("${roster.url}") - String server = "http://127.0.0.1:8082"; + String server; Logger logger = Logger.getLogger(GetAssignmentAdapter.class.getName()); @@ -52,15 +51,12 @@ public class GetAssignmentAdapter implements GetAssignmentPort { try { logger.info("Sending getAssignment Request"); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - logger.log(Level.INFO, "getAssignment request result:\n {0}", response.body()); + logger.log(Level.INFO, "getAssignment request result:\n {}", response.body()); if (response.body().equals("")) { return null; } JSONObject responseBody = new JSONObject(response.body()); - - String[] input = { "1", "+", "2" }; - // TODO Add input in roster + tasklist - return new Task(responseBody.getString("taskID"), input); + return new Task(responseBody.getString("taskID"), responseBody.getString("input")); } catch (InterruptedException e) { logger.log(Level.SEVERE, e.getLocalizedMessage(), e); diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/NotifyExecutorPoolAdapter.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/NotifyExecutorPoolAdapter.java index abc0cf5..2dba64f 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/NotifyExecutorPoolAdapter.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/NotifyExecutorPoolAdapter.java @@ -22,9 +22,8 @@ import ch.unisg.executorbase.executor.domain.ExecutorType; @Primary public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort { - // TODO Not working for now bc it doesn't get autowired - @Value("${executor.pool.url}") - String server = "http://127.0.0.1:8083"; + @Value("${executor-pool.url}") + String server; Logger logger = Logger.getLogger(NotifyExecutorPoolAdapter.class.getName()); @@ -37,7 +36,7 @@ public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort { String body = new JSONObject() .put("executorTaskType", executorType) - .put("executorUri", executorURI.getValue()) + .put("executorURI", executorURI.getValue()) .toString(); HttpClient client = HttpClient.newHttpClient(); diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java index b8e8631..122ea4b 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java @@ -23,8 +23,9 @@ public abstract class ExecutorBase { @Getter private ExecutorStatus status; - // TODO Violation of the Dependency Inversion Principle?, - // TODO do this with only services + // TODO Violation of the Dependency Inversion Principle?, but we havn't really got a better solutions to send a http request / access a service from a domain model + // TODO I guess we can implement the execution as a service but there still is the problem with the startup request. + // TODO I guess we can somehow autowire this but I don't know why it's not working :D private final NotifyExecutorPoolPort notifyExecutorPoolPort = new NotifyExecutorPoolAdapter(); private final NotifyExecutorPoolService notifyExecutorPoolService = new NotifyExecutorPoolService(notifyExecutorPoolPort); private final GetAssignmentPort getAssignmentPort = new GetAssignmentAdapter(); @@ -37,8 +38,8 @@ public abstract class ExecutorBase { this.status = ExecutorStatus.STARTING_UP; this.executorType = executorType; // TODO set this automaticly - this.executorURI = new ExecutorURI("http://localhost:8084"); - // TODO do this in main + this.executorURI = new ExecutorURI("localhost:8084"); + // Notify executor-pool about existence. If executor-pools response is successfull start with getting an assignment, else shut down executor. if(!notifyExecutorPoolService.notifyExecutorPool(this.executorURI, this.executorType)) { System.exit(0); @@ -54,8 +55,6 @@ public abstract class ExecutorBase { **/ public void getAssignment() { Task newTask = getAssignmentPort.getAssignment(this.getExecutorType(), this.getExecutorURI()); - System.out.println("New assignment"); - System.out.println(newTask); if (newTask != null) { this.executeTask(newTask); } else { @@ -73,8 +72,6 @@ public abstract class ExecutorBase { task.setResult(execution(task.getInput())); - System.out.println(task.getResult()); - // TODO implement logic if execution was not successful executionFinishedEventPort.publishExecutionFinishedEvent( new ExecutionFinishedEvent(task.getTaskID(), task.getResult(), "SUCCESS")); diff --git a/executor-base/src/main/resources/application.properties b/executor-base/src/main/resources/application.properties index 4316ebf..3eee96a 100644 --- a/executor-base/src/main/resources/application.properties +++ b/executor-base/src/main/resources/application.properties @@ -1,6 +1,6 @@ server.port=8081 roster.url=http://127.0.0.1:8082 -executor.pool.url=http://127.0.0.1:8083 +executor-pool.url=http://127.0.0.1:8083 executor1.url=http://127.0.0.1:8084 executor2.url=http://127.0.0.1:8085 task-list.url=http://127.0.0.1:8081 diff --git a/executor-pool/src/main/resources/application.properties b/executor-pool/src/main/resources/application.properties index 0c9ba7e..c8fd60a 100644 --- a/executor-pool/src/main/resources/application.properties +++ b/executor-pool/src/main/resources/application.properties @@ -1,3 +1,3 @@ server.port=8083 -mqtt.broker.uri=tcp://localhost:1883 +mqtt.broker.uri=tcp://broker.hivemq.com:1883 diff --git a/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java b/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java index c3fbb67..eb09699 100644 --- a/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java +++ b/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java @@ -21,18 +21,16 @@ public class Executor extends ExecutorBase { protected String execution(String... input) { - System.out.println(input); - double result = Double.NaN; int a = Integer.parseInt(input[0]); int b = Integer.parseInt(input[2]); String operation = input[1]; - // try { - // TimeUnit.SECONDS.sleep(20); - // } catch (InterruptedException e) { - // e.printStackTrace(); - // } + try { + TimeUnit.SECONDS.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } if (operation == "+") { result = a + b; @@ -42,9 +40,7 @@ public class Executor extends ExecutorBase { result = a - b; } - System.out.println("finish"); - return Double.toString(result); } -} +} \ No newline at end of file diff --git a/roster/src/main/java/ch/unisg/roster/RosterApplication.java b/roster/src/main/java/ch/unisg/roster/RosterApplication.java index bc18c54..dd57a5d 100644 --- a/roster/src/main/java/ch/unisg/roster/RosterApplication.java +++ b/roster/src/main/java/ch/unisg/roster/RosterApplication.java @@ -1,41 +1,13 @@ package ch.unisg.roster; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.eclipse.paho.client.mqttv3.MqttException; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import ch.unisg.roster.roster.adapter.common.clients.TapasMqttClient; -import ch.unisg.roster.roster.adapter.in.messaging.mqtt.ExecutorEventMqttListener; -import ch.unisg.roster.roster.adapter.in.messaging.mqtt.ExecutorEventsMqttDispatcher; - @SpringBootApplication public class RosterApplication { - static Logger logger = Logger.getLogger(RosterApplication.class.getName()); - - public static String MQTT_BROKER = "tcp://localhost:1883"; - public static void main(String[] args) { SpringApplication.run(RosterApplication.class, args); - - bootstrapMarketplaceWithMqtt(); } - /** - * Connects to an MQTT broker, presumably the one used by all TAPAS groups to communicate with - * one another - */ - private static void bootstrapMarketplaceWithMqtt() { - try { - ExecutorEventsMqttDispatcher dispatcher = new ExecutorEventsMqttDispatcher(); - TapasMqttClient client = TapasMqttClient.getInstance(MQTT_BROKER, dispatcher); - client.startReceivingMessages(); - } catch (MqttException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - } - } diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorAddedEventListenerMqttAdapter.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorAddedEventListenerMqttAdapter.java index 10e907e..1c3cbcd 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorAddedEventListenerMqttAdapter.java +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorAddedEventListenerMqttAdapter.java @@ -17,9 +17,6 @@ public class ExecutorAddedEventListenerMqttAdapter extends ExecutorEventMqttList @Override public boolean handleEvent(MqttMessage message) { - - System.out.println("New Executor added!"); - String payload = new String(message.getPayload()); try { @@ -28,7 +25,7 @@ public class ExecutorAddedEventListenerMqttAdapter extends ExecutorEventMqttList JsonNode data = new ObjectMapper().readTree(payload); String taskType = data.get("executorTaskType").asText(); - String executorId = data.get("executorUri").asText(); + String executorId = data.get("executorURI").asText(); ExecutorAddedEvent executorAddedEvent = new ExecutorAddedEvent( new ExecutorURI(executorId), diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorEventsMqttDispatcher.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorEventsMqttDispatcher.java index c1b7649..caa6202 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorEventsMqttDispatcher.java +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/messaging/mqtt/ExecutorEventsMqttDispatcher.java @@ -16,8 +16,8 @@ public class ExecutorEventsMqttDispatcher { // TODO: Register here your topics and event listener adapters private void initRouter() { - router.put("ch/unisg/tapas/executors/added", new ExecutorAddedEventListenerMqttAdapter()); - router.put("ch/unisg/tapas/executors/removed", new ExecutorRemovedEventListenerMqttAdapter()); + router.put("ch/unisg/tapas-group-tutors/executors/added", new ExecutorAddedEventListenerMqttAdapter()); + router.put("ch/unisg/tapas-group-tutors/executors/removed", new ExecutorRemovedEventListenerMqttAdapter()); } /** diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/web/NewTaskController.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/web/NewTaskController.java index 98b3ac7..af01346 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/adapter/in/web/NewTaskController.java +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/in/web/NewTaskController.java @@ -1,7 +1,5 @@ package ch.unisg.roster.roster.adapter.in.web; -import java.util.logging.Logger; - import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -20,8 +18,6 @@ public class NewTaskController { this.newTaskUseCase = newTaskUseCase; } - Logger logger = Logger.getLogger(NewTaskController.class.getName()); - /** * Controller which handles the new task event from the tasklist * @return 201 Create or 409 Conflict @@ -29,14 +25,10 @@ public class NewTaskController { @PostMapping(path = "/task", consumes = {"application/task+json"}) public ResponseEntity newTaskController(@RequestBody Task task) { - logger.info("New task with id:" + task.getTaskID()); - NewTaskCommand command = new NewTaskCommand(task.getTaskID(), task.getTaskType()); boolean success = newTaskUseCase.addNewTaskToQueue(command); - logger.info("Could create task: " + success); - if (success) { return new ResponseEntity<>(HttpStatus.CREATED); } diff --git a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/web/PublishNewTaskEventAdapter.java b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/web/PublishNewTaskEventAdapter.java index 0b16567..6a6b7f7 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/adapter/out/web/PublishNewTaskEventAdapter.java +++ b/roster/src/main/java/ch/unisg/roster/roster/adapter/out/web/PublishNewTaskEventAdapter.java @@ -34,23 +34,21 @@ public class PublishNewTaskEventAdapter implements NewTaskEventPort { @Override public void publishNewTaskEvent(NewTaskEvent event) { - System.out.println(server2); - - // HttpClient client = HttpClient.newHttpClient(); - // HttpRequest request = HttpRequest.newBuilder() - // .uri(URI.create(server + "/newtask/" + event.taskType.getValue())) - // .GET() - // .build(); + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(server + "/newtask/" + event.taskType.getValue())) + .GET() + .build(); - // try { - // client.send(request, HttpResponse.BodyHandlers.ofString()); - // } catch (InterruptedException e) { - // logger.log(Level.SEVERE, e.getLocalizedMessage(), e); - // Thread.currentThread().interrupt(); - // } catch (IOException e) { - // logger.log(Level.SEVERE, e.getLocalizedMessage(), e); - // } + try { + client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, e.getLocalizedMessage(), e); + Thread.currentThread().interrupt(); + } catch (IOException e) { + logger.log(Level.SEVERE, e.getLocalizedMessage(), e); + } HttpClient client2 = HttpClient.newHttpClient(); HttpRequest request2 = HttpRequest.newBuilder() diff --git a/roster/src/main/java/ch/unisg/roster/roster/application/service/NewTaskService.java b/roster/src/main/java/ch/unisg/roster/roster/application/service/NewTaskService.java index c6e1685..588ed04 100644 --- a/roster/src/main/java/ch/unisg/roster/roster/application/service/NewTaskService.java +++ b/roster/src/main/java/ch/unisg/roster/roster/application/service/NewTaskService.java @@ -29,7 +29,6 @@ public class NewTaskService implements NewTaskUseCase { public boolean addNewTaskToQueue(NewTaskCommand command) { ExecutorRegistry executorRegistry = ExecutorRegistry.getInstance(); - if (!executorRegistry.containsTaskType(command.getTaskType())) { return false; } diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/TapasAuctionHouseApplication.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/TapasAuctionHouseApplication.java index 7438032..18c7631 100644 --- a/tapas-auction-house/src/main/java/ch/unisg/tapas/TapasAuctionHouseApplication.java +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/TapasAuctionHouseApplication.java @@ -27,7 +27,7 @@ public class TapasAuctionHouseApplication { private ConfigProperties config; public static String RESOURCE_DIRECTORY = "https://api.interactions.ics.unisg.ch/auction-houses/"; - public static String MQTT_BROKER = "tcp://localhost:1883"; + public static String MQTT_BROKER = "tcp://broker.hivemq.com:1883"; public static void main(String[] args) { SpringApplication tapasAuctioneerApp = new SpringApplication(TapasAuctionHouseApplication.class); diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/AuctionJsonRepresentation.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/AuctionJsonRepresentation.java index ea4cf2c..757c8c8 100644 --- a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/AuctionJsonRepresentation.java +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/AuctionJsonRepresentation.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.Setter; import java.sql.Timestamp; +import java.text.SimpleDateFormat; /** * Used to expose a representation of the state of an auction through an interface. This class is @@ -15,7 +16,7 @@ import java.sql.Timestamp; * to modify this class as you see fit! */ public class AuctionJsonRepresentation { - public static final String MEDIA_TYPE = "application/json"; + public static final String MEDIA_TYPE = "application/auction+json"; @Getter @Setter private String auctionId; @@ -56,7 +57,7 @@ public class AuctionJsonRepresentation { ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - + mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); return mapper.writeValueAsString(representation); } } diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/BidJsonRepresentation.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/BidJsonRepresentation.java new file mode 100644 index 0000000..7ae3dda --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/BidJsonRepresentation.java @@ -0,0 +1,43 @@ +package ch.unisg.tapas.auctionhouse.adapter.common.formats; + +import ch.unisg.tapas.auctionhouse.domain.Bid; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Getter; +import lombok.Setter; + +import java.text.SimpleDateFormat; + +public class BidJsonRepresentation { + public static final String MEDIA_TYPE = "application/bid+json"; + + @Getter @Setter + private String auctionId; + + @Getter @Setter + private String bidderName; + + @Getter @Setter + private String bidderAuctionHouseUri; + + @Getter @Setter + private String bidderTaskListUri; + + public BidJsonRepresentation() {} + + public BidJsonRepresentation(Bid bid){ + this.auctionId = bid.getAuctionId().getValue(); + this.bidderName = bid.getBidderName().getValue(); + this.bidderAuctionHouseUri = bid.getBidderAuctionHouseUri().getValue().toString(); + this.bidderTaskListUri = bid.getBidderTaskListUri().getValue().toString(); + } + + public static String serialize(Bid bid) throws JsonProcessingException { + BidJsonRepresentation representation = new BidJsonRepresentation(bid); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(representation); + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java new file mode 100644 index 0000000..782978c --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java @@ -0,0 +1,124 @@ +package ch.unisg.tapas.auctionhouse.adapter.common.formats; + +import ch.unisg.tapas.auctionhouse.domain.Task; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Getter; +import lombok.Setter; + +/** + * This class is used to expose and consume representations of tasks through the HTTP interface. The + * representations conform to the custom JSON-based media type "application/task+json". The media type + * is just an identifier and can be registered with + * IANA to promote interoperability. + */ +final public class TaskJsonRepresentation { + // The media type used for this task representation format + public static final String MEDIA_TYPE = "application/task+json"; + + // A task identifier specific to our implementation (e.g., a UUID). This identifier is then used + // to generate the task's URI. URIs are standard uniform identifiers and use a universal syntax + // that can be referenced (and dereferenced) independent of context. In our uniform HTTP API, + // we identify tasks via URIs and not implementation-specific identifiers. + @Getter @Setter + private String taskId; + + // A string that represents the task's name + @Getter + private final String taskName; + + // A string that identifies the task's type. This string could also be a URI (e.g., defined in some + // Web ontology, as we shall see later in the course), but it's not constrained to be a URI. + // The task's type can be used to assign executors to tasks, to decide what tasks to bid for, etc. + @Getter + private final String taskType; + + // The task's status: OPEN, ASSIGNED, RUNNING, or EXECUTED (see Task.Status) + @Getter @Setter + private String taskStatus; + + // If this task is a delegated task (i.e., a shadow of another task), this URI points to the + // original task. Because URIs are standard and uniform, we can just dereference this URI to + // retrieve a representation of the original task. + @Getter @Setter + private String originalTaskUri; + + // The service provider who executes this task. The service provider is a any string that identifies + // a TAPAS group (e.g., tapas-group1). This identifier could also be a URI (if we have a good reason + // for it), but it's not constrained to be a URI. + @Getter @Setter + private String serviceProvider; + + // A string that provides domain-specific input data for this task. In the context of this project, + // we can parse and interpret the input data based on the task's type. + @Getter @Setter + private String inputData; + + // A string that provides domain-specific output data for this task. In the context of this project, + // we can parse and interpret the output data based on the task's type. + @Getter @Setter + private String outputData; + + /** + * Instantiate a task representation with a task name and type. + * + * @param taskName string that represents the task's name + * @param taskType string that represents the task's type + */ + public TaskJsonRepresentation(String taskName, String taskType) { + this.taskName = taskName; + this.taskType = taskType; + + this.taskStatus = null; + this.originalTaskUri = null; + this.serviceProvider = null; + this.inputData = null; + this.outputData = null; + } + + /** + * Instantiate a task representation from a domain concept. + * + * @param task the task + */ + public TaskJsonRepresentation(Task task) { + this(task.getTaskName().getValue(), task.getTaskType().getValue()); + + this.taskId = task.getTaskId().getValue(); + this.taskStatus = task.getTaskStatus().getValue().name(); + + this.originalTaskUri = (task.getOriginalTaskUri() == null) ? + null : task.getOriginalTaskUri().getValue(); + + this.serviceProvider = (task.getProvider() == null) ? null : task.getProvider().getValue(); + this.inputData = (task.getInputData() == null) ? null : task.getInputData().getValue(); + this.outputData = (task.getOutputData() == null) ? null : task.getOutputData().getValue(); + } + + /** + * Convenience method used to serialize a task provided as a domain concept in the format exposed + * through the uniform HTTP API. + * + * @param task the task as defined in the domain + * @return a string serialization using the JSON-based representation format defined for tasks + * @throws JsonProcessingException if a runtime exception occurs during object serialization + */ + public static String serialize(Task task) throws JsonProcessingException { + TaskJsonRepresentation representation = new TaskJsonRepresentation(task); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return mapper.writeValueAsString(representation); + } + + public String serialize() throws JsonProcessingException { + TaskJsonRepresentation representation = this; + + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return mapper.writeValueAsString(representation); + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/AuctionEventsMqttDispatcher.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/AuctionEventsMqttDispatcher.java index 7d30453..91872f2 100644 --- a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/AuctionEventsMqttDispatcher.java +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/AuctionEventsMqttDispatcher.java @@ -28,6 +28,7 @@ public class AuctionEventsMqttDispatcher { private void initRouter() { router.put("ch/unisg/tapas/executors/added", new ExecutorAddedEventListenerMqttAdapter()); router.put("ch/unisg/tapas/executors/removed", new ExecutorRemovedEventListenerMqttAdapter()); + router.put("ch/unisg/tapas/auctions", new ExternalAuctionStartedEventListenerMqttAdapter()); } /** diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/BidReceivedEventListenerMqttAdapter.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/BidReceivedEventListenerMqttAdapter.java new file mode 100644 index 0000000..29f45da --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/BidReceivedEventListenerMqttAdapter.java @@ -0,0 +1,52 @@ +package ch.unisg.tapas.auctionhouse.adapter.in.messaging.mqtt; + +import ch.unisg.tapas.auctionhouse.application.handler.BidReceivedHandler; +import ch.unisg.tapas.auctionhouse.application.handler.ExecutorAddedHandler; +import ch.unisg.tapas.auctionhouse.application.port.in.ExecutorAddedEvent; +import ch.unisg.tapas.auctionhouse.domain.Auction; +import ch.unisg.tapas.auctionhouse.domain.Bid; +import ch.unisg.tapas.auctionhouse.application.port.in.BidReceivedEvent; +import ch.unisg.tapas.auctionhouse.domain.ExecutorRegistry; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +import java.net.URI; + +public class BidReceivedEventListenerMqttAdapter extends AuctionEventMqttListener { + private static final Logger LOGGER = LogManager.getLogger(BidReceivedEventListenerMqttAdapter.class); + + @Override + public boolean handleEvent(MqttMessage message){ + String payload = new String(message.getPayload()); + + try { + // Note: this message representation is provided only as an example. You should use a + // representation that makes sense in the context of your application. + JsonNode data = new ObjectMapper().readTree(payload); + + String auctionId = data.get("auctionId").asText(); + String bidderName = data.get("bidderName").asText(); + String bidderAuctionHouseUri = data.get("bidderAuctionHouseUri").asText(); + String bidderTaskListUri = data.get("bidderTaskListUri").asText(); + + BidReceivedEvent bidReceivedEvent = new BidReceivedEvent( new Bid( + new Auction.AuctionId(auctionId), + new Bid.BidderName(bidderName), + new Bid.BidderAuctionHouseUri(URI.create(bidderAuctionHouseUri)), + new Bid.BidderTaskListUri(URI.create(bidderTaskListUri)) + )); + + BidReceivedHandler bidReceivedHandler = new BidReceivedHandler(); + bidReceivedHandler.handleNewBidReceivedEvent(bidReceivedEvent); + } catch (JsonProcessingException | NullPointerException e) { + LOGGER.error(e.getMessage(), e); + return false; + } + + return true; + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/ExternalAuctionStartedEventListenerMqttAdapter.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/ExternalAuctionStartedEventListenerMqttAdapter.java new file mode 100644 index 0000000..5e17d96 --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/ExternalAuctionStartedEventListenerMqttAdapter.java @@ -0,0 +1,86 @@ +package ch.unisg.tapas.auctionhouse.adapter.in.messaging.mqtt; + +import ch.unisg.tapas.auctionhouse.adapter.common.formats.BidJsonRepresentation; +import ch.unisg.tapas.auctionhouse.adapter.common.formats.TaskJsonRepresentation; +import ch.unisg.tapas.auctionhouse.application.handler.BidReceivedHandler; +import ch.unisg.tapas.auctionhouse.application.port.in.BidReceivedEvent; +import ch.unisg.tapas.auctionhouse.domain.Auction; +import ch.unisg.tapas.auctionhouse.domain.Bid; +import ch.unisg.tapas.auctionhouse.domain.ExecutorRegistry; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.beans.factory.annotation.Value; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.sql.Timestamp; + +public class ExternalAuctionStartedEventListenerMqttAdapter extends AuctionEventMqttListener{ + private static final Logger LOGGER = LogManager.getLogger(ExternalAuctionStartedEventListenerMqttAdapter.class); + + @Value("${auction.house.uri}") + String auctionHouseURI; + + @Value("${tasks.list.uri}") + String taskListURI; + + @Override + public boolean handleEvent(MqttMessage message){ + String payload = new String(message.getPayload()); + + try { + // Note: this message representation is provided only as an example. You should use a + // representation that makes sense in the context of your application. + JsonNode data = new ObjectMapper().readTree(payload); + + String auctionId = data.get("auctionId").asText(); + String auctionHouseUri = data.get("auctionHouseUri").asText(); + String taskUri = data.get("taskUri").asText(); + String taskType = data.get("taskType").asText(); + String deadline = data.get("deadline").asText(); + + var capable = ExecutorRegistry.getInstance().containsTaskType(new Auction.AuctionedTaskType(taskType)); + // TODO check deadline + if(capable){ + var bid = new Bid( + new Auction.AuctionId(auctionId), + new Bid.BidderName("Group-1"), + new Bid.BidderAuctionHouseUri(URI.create(auctionHouseUri)), + new Bid.BidderTaskListUri(URI.create(taskListURI)) + ); + + String body = BidJsonRepresentation.serialize(bid); + LOGGER.info(body); + var postURI = URI.create(auctionHouseUri + "/bid"); + HttpRequest postRequest = HttpRequest.newBuilder() + .uri(postURI) + .header("Content-Type", BidJsonRepresentation.MEDIA_TYPE) + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + HttpClient client = HttpClient.newHttpClient(); + var postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString()); + + LOGGER.info(postResponse.statusCode()); + } + } catch (JsonProcessingException | NullPointerException e) { + LOGGER.error(e.getMessage(), e); + return false; + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e){ + LOGGER.error(e.getMessage(), e); + } + + return true; + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/AddBidWebController.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/AddBidWebController.java new file mode 100644 index 0000000..3431c8d --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/AddBidWebController.java @@ -0,0 +1,38 @@ +package ch.unisg.tapas.auctionhouse.adapter.in.web; + +import ch.unisg.tapas.auctionhouse.adapter.common.formats.AuctionJsonRepresentation; +import ch.unisg.tapas.auctionhouse.adapter.common.formats.BidJsonRepresentation; +import ch.unisg.tapas.auctionhouse.application.handler.BidReceivedHandler; +import ch.unisg.tapas.auctionhouse.application.port.in.BidReceivedEvent; +import ch.unisg.tapas.auctionhouse.domain.Auction; +import ch.unisg.tapas.auctionhouse.domain.Bid; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.net.URI; + +// TODO Fix structure due to MQTT +@RestController +public class AddBidWebController { + @PostMapping(path = "/bid", consumes = BidJsonRepresentation.MEDIA_TYPE) + public ResponseEntity addBid(@RequestBody BidJsonRepresentation payload) { + BidReceivedEvent bidReceivedEvent = new BidReceivedEvent(new Bid( + new Auction.AuctionId(payload.getAuctionId()), + new Bid.BidderName(payload.getBidderName()), + new Bid.BidderAuctionHouseUri(URI.create(payload.getBidderAuctionHouseUri())), + new Bid.BidderTaskListUri(URI.create(payload.getBidderTaskListUri())) + )); + + BidReceivedHandler bidReceivedHandler = new BidReceivedHandler(); + bidReceivedHandler.handleNewBidReceivedEvent(bidReceivedEvent); + + HttpHeaders responseHeaders = new HttpHeaders(); + + return new ResponseEntity<>(responseHeaders, HttpStatus.NO_CONTENT); + } + +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/WinningBidWebController.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/WinningBidWebController.java new file mode 100644 index 0000000..9d252b5 --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/WinningBidWebController.java @@ -0,0 +1,59 @@ +package ch.unisg.tapas.auctionhouse.adapter.in.web; + +import ch.unisg.tapas.auctionhouse.adapter.common.formats.AuctionJsonRepresentation; +import ch.unisg.tapas.auctionhouse.adapter.common.formats.BidJsonRepresentation; +import ch.unisg.tapas.auctionhouse.adapter.common.formats.TaskJsonRepresentation; +import ch.unisg.tapas.auctionhouse.adapter.in.messaging.mqtt.ExternalAuctionStartedEventListenerMqttAdapter; +import ch.unisg.tapas.auctionhouse.domain.Task; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +@RestController +public class WinningBidWebController { + private static final Logger LOGGER = LogManager.getLogger(WinningBidWebController.class); + + @Value("${tasks.list.uri}") + String taskListURI; + + @PostMapping(path = "/taskwinner", consumes = TaskJsonRepresentation.MEDIA_TYPE) + public ResponseEntity winningBid(@RequestBody TaskJsonRepresentation payload){ + try { + var body = payload.serialize(); + LOGGER.info(body); + var postURI = URI.create(taskListURI + "/tasks/"); + HttpRequest postRequest = HttpRequest.newBuilder() + .uri(postURI) + .header("Content-Type", TaskJsonRepresentation.MEDIA_TYPE) + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + HttpClient client = HttpClient.newHttpClient(); + var postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString()); + + LOGGER.info(postResponse.statusCode()); + + + HttpHeaders responseHeaders = new HttpHeaders(); + return new ResponseEntity<>(responseHeaders, HttpStatus.NO_CONTENT); + } + catch ( + IOException | InterruptedException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/websub/PublishAuctionStartedEventMqttAdapter.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/mqtt/PublishAuctionStartedEventMqttAdapter.java similarity index 95% rename from tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/websub/PublishAuctionStartedEventMqttAdapter.java rename to tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/mqtt/PublishAuctionStartedEventMqttAdapter.java index d5bb0fc..a041b4f 100644 --- a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/websub/PublishAuctionStartedEventMqttAdapter.java +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/mqtt/PublishAuctionStartedEventMqttAdapter.java @@ -1,4 +1,4 @@ -package ch.unisg.tapas.auctionhouse.adapter.out.messaging.websub; +package ch.unisg.tapas.auctionhouse.adapter.out.messaging.mqtt; import ch.unisg.tapas.auctionhouse.adapter.common.clients.TapasMqttClient; import ch.unisg.tapas.auctionhouse.adapter.common.formats.AuctionJsonRepresentation; diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/web/AuctionWonEventHttpAdapter.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/web/AuctionWonEventHttpAdapter.java index 26949f2..4583892 100644 --- a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/web/AuctionWonEventHttpAdapter.java +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/web/AuctionWonEventHttpAdapter.java @@ -1,10 +1,24 @@ package ch.unisg.tapas.auctionhouse.adapter.out.web; +import ch.unisg.tapas.auctionhouse.adapter.common.formats.TaskJsonRepresentation; +import ch.unisg.tapas.auctionhouse.application.handler.AuctionStartedHandler; import ch.unisg.tapas.auctionhouse.application.port.out.AuctionWonEventPort; +import ch.unisg.tapas.auctionhouse.domain.AuctionRegistry; import ch.unisg.tapas.auctionhouse.domain.AuctionWonEvent; +import ch.unisg.tapas.auctionhouse.domain.Task; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + /** * This class is a template for sending auction won events via HTTP. This class was created here only * as a placeholder, it is up to you to decide how such events should be sent (e.g., via HTTP, @@ -13,8 +27,54 @@ import org.springframework.stereotype.Component; @Component @Primary public class AuctionWonEventHttpAdapter implements AuctionWonEventPort { + private static final Logger LOGGER = LogManager.getLogger(AuctionWonEventHttpAdapter.class); + + @Value("${tasks.list.uri}") + String server; + @Override public void publishAuctionWonEvent(AuctionWonEvent event) { + try{ + var auction = AuctionRegistry.getInstance().getAuctionById(event.getWinningBid().getAuctionId()); + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(auction.get().getTaskUri().getValue()) + .GET() + .build(); + var response = client.send(request, HttpResponse.BodyHandlers.ofString()); + LOGGER.info(response.body()); + JSONObject responseBody = new JSONObject(response.body()); + + var task = new Task( + new Task.TaskName(responseBody.getString("taskName")), + new Task.TaskType(responseBody.getString("taskType")), + new Task.OriginalTaskUri(auction.get().getTaskUri().getValue().toString()), + new Task.TaskStatus(ch.unisg.tapas.auctionhouse.domain.Task.Status.ASSIGNED), + new Task.TaskId(responseBody.getString("taskId")), + new Task.InputData(responseBody.getString("inputData")), + new Task.ServiceProvider("TODO") + ); + + String body = TaskJsonRepresentation.serialize(task); + LOGGER.info(body); + var postURI = URI.create(auction.get().getAuctionHouseUri().getValue().toString() + "/taskwinner"); + HttpRequest postRequest = HttpRequest.newBuilder() + .uri(postURI) + .header("Content-Type", TaskJsonRepresentation.MEDIA_TYPE) + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + var postResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); + + LOGGER.info(postResponse.statusCode()); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e){ + LOGGER.error(e.getMessage(), e); + } } } diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/handler/BidReceivedHandler.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/handler/BidReceivedHandler.java new file mode 100644 index 0000000..dc992ac --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/handler/BidReceivedHandler.java @@ -0,0 +1,17 @@ +package ch.unisg.tapas.auctionhouse.application.handler; + +import ch.unisg.tapas.auctionhouse.application.port.in.BidReceivedEvent; +import ch.unisg.tapas.auctionhouse.application.port.in.BidReceivedEventHandler; +import ch.unisg.tapas.auctionhouse.domain.AuctionRegistry; +import org.springframework.stereotype.Component; + +@Component +public class BidReceivedHandler implements BidReceivedEventHandler { + @Override + public boolean handleNewBidReceivedEvent(BidReceivedEvent bidReceivedEvent){ + var auction = AuctionRegistry.getInstance().getAuctionById(bidReceivedEvent.bid.getAuctionId()); + // TODO Handle if auction not there + auction.get().addBid(bidReceivedEvent.bid); + return true; + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEvent.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEvent.java new file mode 100644 index 0000000..560f50b --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEvent.java @@ -0,0 +1,17 @@ +package ch.unisg.tapas.auctionhouse.application.port.in; + +import ch.unisg.tapas.auctionhouse.domain.Bid; +import ch.unisg.tapas.common.SelfValidating; +import lombok.Getter; + +import javax.validation.constraints.NotNull; + +public class BidReceivedEvent extends SelfValidating { + @NotNull + public Bid bid; + + public BidReceivedEvent(Bid bid){ + this.bid = bid; + validateSelf(); + } +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEventHandler.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEventHandler.java new file mode 100644 index 0000000..b17ac6b --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEventHandler.java @@ -0,0 +1,5 @@ +package ch.unisg.tapas.auctionhouse.application.port.in; + +public interface BidReceivedEventHandler { + boolean handleNewBidReceivedEvent(BidReceivedEvent bidReceivedEvent); +} diff --git a/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/domain/Task.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/domain/Task.java new file mode 100644 index 0000000..3fd0d89 --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/domain/Task.java @@ -0,0 +1,122 @@ +package ch.unisg.tapas.auctionhouse.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.Value; + +import java.util.UUID; + +/**This is a domain entity**/ +public class Task { + public enum Status { + OPEN, ASSIGNED, RUNNING, EXECUTED + } + + @Getter + private final TaskId taskId; + + @Getter + private final TaskName taskName; + + @Getter + private final TaskType taskType; + + @Getter @Setter + public TaskStatus taskStatus; // had to make public for CompleteTaskService + + @Getter + public TaskResult taskResult; // same as above + + @Getter + private final OriginalTaskUri originalTaskUri; + + @Getter @Setter + private ServiceProvider provider; + + @Getter @Setter + private InputData inputData; + + @Getter @Setter + private OutputData outputData; + + public Task(TaskName taskName, TaskType taskType, OriginalTaskUri taskUri) { + this.taskName = taskName; + this.taskType = taskType; + this.taskStatus = new TaskStatus(Status.OPEN); + this.taskId = new TaskId(UUID.randomUUID().toString()); + this.taskResult = new TaskResult(""); + this.originalTaskUri = taskUri; + + this.inputData = null; + this.outputData = null; + } + + public Task(TaskName taskName, TaskType taskType, OriginalTaskUri taskUri, TaskStatus taskStatus, TaskId taskId, InputData inputData, ServiceProvider serviceProvider) { + this.taskName = taskName; + this.taskType = taskType; + this.taskStatus = taskStatus; + this.taskId = taskId; + this.taskResult = new TaskResult(""); + this.originalTaskUri = taskUri; + this.provider = serviceProvider; + + this.inputData = inputData; + this.outputData = new OutputData(""); + } + + protected static Task createTaskWithNameAndType(TaskName name, TaskType type) { + //This is a simple debug message to see that the request has reached the right method in the core + System.out.println("New Task: " + name.getValue() + " " + type.getValue()); + return new Task(name, type, null); + } + + protected static Task createTaskWithNameAndTypeAndOriginalTaskUri(TaskName name, TaskType type, + OriginalTaskUri originalTaskUri) { + return new Task(name, type, originalTaskUri); + } + + @Value + public static class TaskId { + String value; + } + + @Value + public static class TaskName { + String value; + } + + @Value + public static class TaskType { + String value; + } + + @Value + public static class OriginalTaskUri { + String value; + } + + @Value + public static class TaskStatus { + Status value; + } + + @Value + public static class ServiceProvider { + String value; + } + + @Value + public static class InputData { + String value; + } + + @Value + public static class OutputData { + String value; + } + + @Value + public static class TaskResult{ + private String value; + } +} diff --git a/tapas-auction-house/src/main/resources/application.properties b/tapas-auction-house/src/main/resources/application.properties index 706362e..7b94a5a 100644 --- a/tapas-auction-house/src/main/resources/application.properties +++ b/tapas-auction-house/src/main/resources/application.properties @@ -5,7 +5,7 @@ websub.hub.publish=https://websub.appspot.com/ group=tapas-group-tutors auction.house.uri=https://tapas-auction-house.86-119-34-23.nip.io/ -tasks.list.uri=https://tapas-tasks.86-119-34-23.nip.io/ +tasks.list.uri=http://localhost:8081 application.environment=development auctionhouse.uri=http://localhost:8086