From 1c4da284800a7067d9fc00003bd8e28355d5557f Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Nov 2021 11:59:27 +0100 Subject: [PATCH 1/4] fixed multiple bugs & updated cicd workflows --- .deployment/docker-compose.yml | 52 +++++++++---------- .github/workflows/build-and-deploy.yml | 29 ++++++----- .github/workflows/ci.assignment.yml | 41 --------------- ...cutor2.yml => ci.executor-computation.yml} | 12 +++-- ...ci.executor1.yml => ci.executor-robot.yml} | 10 ++-- .github/workflows/ci.roster.yml | 45 ++++++++++++++++ .../in/web/TaskAvailableController.java | 6 +++ .../web/ExecutionFinishedEventAdapter.java | 11 +++- .../adapter/out/web/GetAssignmentAdapter.java | 10 ++-- .../out/web/NotifyExecutorPoolAdapter.java | 7 +-- .../executor/domain/ExecutorBase.java | 13 +++-- .../src/main/resources/application.properties | 2 +- .../executor/domain/Executor.java | 16 +++--- .../ch/unisg/roster/RosterApplication.java | 28 ++++++++++ ...ExecutorAddedEventListenerMqttAdapter.java | 5 +- .../mqtt/ExecutorEventsMqttDispatcher.java | 4 +- .../adapter/in/web/NewTaskController.java | 8 +++ .../out/web/PublishNewTaskEventAdapter.java | 28 +++++----- .../application/service/NewTaskService.java | 1 + 19 files changed, 205 insertions(+), 123 deletions(-) delete mode 100644 .github/workflows/ci.assignment.yml rename .github/workflows/{ci.executor2.yml => ci.executor-computation.yml} (75%) rename .github/workflows/{ci.executor1.yml => ci.executor-robot.yml} (77%) create mode 100644 .github/workflows/ci.roster.yml diff --git a/.deployment/docker-compose.yml b/.deployment/docker-compose.yml index 01a5a77..5a1329f 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" - assignment: + roster: image: openjdk - command: "java -jar /data/assignment-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/roster-0.0.1-SNAPSHOT.jar" restart: unless-stopped volumes: - ./:/data/ labels: - "traefik.enable=true" - - "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" + - "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" 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" - executor1: + executor-computation: image: openjdk - command: "java -jar /data/executor1-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/executor-computation-0.0.1-SNAPSHOT.jar" restart: unless-stopped depends_on: - executor-pool - - assignment + - roster volumes: - ./:/data/ labels: - "traefik.enable=true" - - "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" + - "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" - executor2: + executor-robot: image: openjdk - command: "java -jar /data/executor2-0.0.1-SNAPSHOT.jar" + command: "java -jar /data/executor-robot-0.0.1-SNAPSHOT.jar" restart: unless-stopped depends_on: - executor-pool - - assignment + - roster volumes: - ./:/data/ labels: - "traefik.enable=true" - - "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" + - "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" diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index d223887..7290452 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -33,30 +33,33 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - 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 common library + run: mvn -f common/pom.xml --batch-mode --update-snapshots install - - name: Build with Maven + - 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 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 with Maven + - name: Build executor-base library run: mvn -f executor-base/pom.xml --batch-mode --update-snapshots install - - 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-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 executor2/pom.xml --batch-mode --update-snapshots verify - - run: cp ./executor2/target/executor2-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 + - name: Build tapas-task service 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 with Maven + - name: Build tapas-auction-house service 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 deleted file mode 100644 index 394fe54..0000000 --- a/.github/workflows/ci.assignment.yml +++ /dev/null @@ -1,41 +0,0 @@ -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.executor2.yml b/.github/workflows/ci.executor-computation.yml similarity index 75% rename from .github/workflows/ci.executor2.yml rename to .github/workflows/ci.executor-computation.yml index 5ae38f0..5832883 100644 --- a/.github/workflows/ci.executor2.yml +++ b/.github/workflows/ci.executor-computation.yml @@ -1,15 +1,17 @@ -name: CI Executor 2 +name: CI executor-computation on: push: branches: [main, dev] paths: - "executor-base/**" - - "executor2/**" + - "executor-computation/**" + - "common/**" pull_request: branches: [main, dev] paths: - "executor-base/**" - - "executor2/**" + - "executor-computation/**" + - "common/**" workflow_dispatch: jobs: @@ -36,10 +38,12 @@ 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 executor2/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor2 + 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 diff --git a/.github/workflows/ci.executor1.yml b/.github/workflows/ci.executor-robot.yml similarity index 77% rename from .github/workflows/ci.executor1.yml rename to .github/workflows/ci.executor-robot.yml index 708d7d4..2ccf607 100644 --- a/.github/workflows/ci.executor1.yml +++ b/.github/workflows/ci.executor-robot.yml @@ -4,12 +4,14 @@ on: branches: [main, dev] paths: - "executor-base/**" - - "executor1/**" + - "executor-robot/**" + - "common/**" pull_request: branches: [main, dev] paths: - "executor-base/**" - - "executor1/**" + - "executor-robot/**" + - "common/**" workflow_dispatch: jobs: @@ -36,10 +38,12 @@ 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 executor1/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor1 + 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 diff --git a/.github/workflows/ci.roster.yml b/.github/workflows/ci.roster.yml new file mode 100644 index 0000000..b38355e --- /dev/null +++ b/.github/workflows/ci.roster.yml @@ -0,0 +1,45 @@ +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 8fda5ac..66ef496 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,5 +1,7 @@ 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; @@ -19,6 +21,8 @@ public class TaskAvailableController { this.taskAvailableUseCase = taskAvailableUseCase; } + Logger logger = Logger.getLogger(TaskAvailableController.class.getName()); + /** * Controller for notification about new events. * @return 200 OK @@ -26,6 +30,8 @@ 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 a5ae910..58f6287 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,8 +16,9 @@ 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; + String server = "http://localhost:8082"; Logger logger = Logger.getLogger(ExecutionFinishedEventAdapter.class.getName()); @@ -28,6 +29,9 @@ 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()) @@ -41,6 +45,9 @@ 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) { @@ -50,7 +57,7 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort logger.log(Level.SEVERE, e.getLocalizedMessage(), e); } - logger.log(Level.INFO, "Finish execution event sent with result: {}", event.getResult()); + logger.log(Level.INFO, "Finish execution event sent with result: {0}", 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 3ed4e37..dd82c81 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,8 +23,9 @@ 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; + String server = "http://127.0.0.1:8082"; Logger logger = Logger.getLogger(GetAssignmentAdapter.class.getName()); @@ -51,12 +52,15 @@ 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 {}", response.body()); + logger.log(Level.INFO, "getAssignment request result:\n {0}", response.body()); if (response.body().equals("")) { return null; } JSONObject responseBody = new JSONObject(response.body()); - return new Task(responseBody.getString("taskID"), responseBody.getString("input")); + + String[] input = { "1", "+", "2" }; + // TODO Add input in roster + tasklist + return new Task(responseBody.getString("taskID"), 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 2dba64f..abc0cf5 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,8 +22,9 @@ import ch.unisg.executorbase.executor.domain.ExecutorType; @Primary public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort { - @Value("${executor-pool.url}") - String server; + // TODO Not working for now bc it doesn't get autowired + @Value("${executor.pool.url}") + String server = "http://127.0.0.1:8083"; Logger logger = Logger.getLogger(NotifyExecutorPoolAdapter.class.getName()); @@ -36,7 +37,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 122ea4b..b8e8631 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,9 +23,8 @@ public abstract class ExecutorBase { @Getter private ExecutorStatus status; - // 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 + // TODO Violation of the Dependency Inversion Principle?, + // TODO do this with only services private final NotifyExecutorPoolPort notifyExecutorPoolPort = new NotifyExecutorPoolAdapter(); private final NotifyExecutorPoolService notifyExecutorPoolService = new NotifyExecutorPoolService(notifyExecutorPoolPort); private final GetAssignmentPort getAssignmentPort = new GetAssignmentAdapter(); @@ -38,8 +37,8 @@ public abstract class ExecutorBase { this.status = ExecutorStatus.STARTING_UP; this.executorType = executorType; // TODO set this automaticly - this.executorURI = new ExecutorURI("localhost:8084"); - + this.executorURI = new ExecutorURI("http://localhost:8084"); + // TODO do this in main // 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); @@ -55,6 +54,8 @@ 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 { @@ -72,6 +73,8 @@ 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 3eee96a..4316ebf 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/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java b/executorcomputation/src/main/java/ch/unisg/executorcomputation/executor/domain/Executor.java index eb09699..c3fbb67 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,16 +21,18 @@ 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; @@ -40,7 +42,9 @@ 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 dd57a5d..bc18c54 100644 --- a/roster/src/main/java/ch/unisg/roster/RosterApplication.java +++ b/roster/src/main/java/ch/unisg/roster/RosterApplication.java @@ -1,13 +1,41 @@ 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 1c3cbcd..10e907e 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,6 +17,9 @@ public class ExecutorAddedEventListenerMqttAdapter extends ExecutorEventMqttList @Override public boolean handleEvent(MqttMessage message) { + + System.out.println("New Executor added!"); + String payload = new String(message.getPayload()); try { @@ -25,7 +28,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 caa6202..c1b7649 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-group-tutors/executors/added", new ExecutorAddedEventListenerMqttAdapter()); - router.put("ch/unisg/tapas-group-tutors/executors/removed", new ExecutorRemovedEventListenerMqttAdapter()); + router.put("ch/unisg/tapas/executors/added", new ExecutorAddedEventListenerMqttAdapter()); + router.put("ch/unisg/tapas/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 af01346..98b3ac7 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,5 +1,7 @@ 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; @@ -18,6 +20,8 @@ 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 @@ -25,10 +29,14 @@ 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 6a6b7f7..0b16567 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,21 +34,23 @@ public class PublishNewTaskEventAdapter implements NewTaskEventPort { @Override public void publishNewTaskEvent(NewTaskEvent event) { - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(server + "/newtask/" + event.taskType.getValue())) - .GET() - .build(); + System.out.println(server2); + + // 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 588ed04..c6e1685 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,6 +29,7 @@ public class NewTaskService implements NewTaskUseCase { public boolean addNewTaskToQueue(NewTaskCommand command) { ExecutorRegistry executorRegistry = ExecutorRegistry.getInstance(); + if (!executorRegistry.containsTaskType(command.getTaskType())) { return false; } -- 2.45.1 From 4c5da8eed6557df6549b2d4334c4357d001c8721 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 15 Nov 2021 12:03:25 +0100 Subject: [PATCH 2/4] fix naming --- .github/workflows/ci.executor-robot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.executor-robot.yml b/.github/workflows/ci.executor-robot.yml index 2ccf607..7a216aa 100644 --- a/.github/workflows/ci.executor-robot.yml +++ b/.github/workflows/ci.executor-robot.yml @@ -1,4 +1,4 @@ -name: CI Executor 1 +name: CI executor-robot on: push: branches: [main, dev] -- 2.45.1 From 8fba9136b2abfa65aa095852f0abb80860297803 Mon Sep 17 00:00:00 2001 From: reynisson Date: Tue, 16 Nov 2021 17:42:14 +0100 Subject: [PATCH 3/4] Implemented auctioning of tasks workflow in auction house --- .../formats/AuctionJsonRepresentation.java | 5 +- .../common/formats/BidJsonRepresentation.java | 43 ++++++ .../formats/TaskJsonRepresentation.java | 115 +++++++++++++++++ .../BidReceivedEventListenerMqttAdapter.java | 52 ++++++++ .../adapter/in/web/AddBidWebController.java | 38 ++++++ ...PublishAuctionStartedEventMqttAdapter.java | 2 +- .../out/web/AuctionWonEventHttpAdapter.java | 60 +++++++++ .../handler/BidReceivedHandler.java | 17 +++ .../application/port/in/BidReceivedEvent.java | 17 +++ .../port/in/BidReceivedEventHandler.java | 5 + .../unisg/tapas/auctionhouse/domain/Task.java | 122 ++++++++++++++++++ .../src/main/resources/application.properties | 2 +- 12 files changed, 474 insertions(+), 4 deletions(-) create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/BidJsonRepresentation.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/BidReceivedEventListenerMqttAdapter.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/AddBidWebController.java rename tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/out/messaging/{websub => mqtt}/PublishAuctionStartedEventMqttAdapter.java (95%) create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/handler/BidReceivedHandler.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEvent.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/application/port/in/BidReceivedEventHandler.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/domain/Task.java 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..467c550 --- /dev/null +++ b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java @@ -0,0 +1,115 @@ +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); + } +} 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/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/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 -- 2.45.1 From e869fb96992ad47f9c8192dd5e5622487a571c5b Mon Sep 17 00:00:00 2001 From: reynisson Date: Tue, 16 Nov 2021 18:51:39 +0100 Subject: [PATCH 4/4] Bidding workflow --- .../src/main/resources/application.properties | 2 +- .../tapas/TapasAuctionHouseApplication.java | 2 +- .../formats/TaskJsonRepresentation.java | 9 ++ .../mqtt/AuctionEventsMqttDispatcher.java | 1 + ...uctionStartedEventListenerMqttAdapter.java | 86 +++++++++++++++++++ .../in/web/WinningBidWebController.java | 59 +++++++++++++ 6 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/messaging/mqtt/ExternalAuctionStartedEventListenerMqttAdapter.java create mode 100644 tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/in/web/WinningBidWebController.java 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/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/TaskJsonRepresentation.java b/tapas-auction-house/src/main/java/ch/unisg/tapas/auctionhouse/adapter/common/formats/TaskJsonRepresentation.java index 467c550..782978c 100644 --- 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 @@ -112,4 +112,13 @@ final public class TaskJsonRepresentation { 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/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/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()); + } + } +} -- 2.45.1