diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c4f3e5b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+end_of_line = lf
+insert_final_newline = true
diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index 43bb471..42b93ad 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -4,75 +4,94 @@
name: Build and Deploy
on:
- push:
- branches:
- - main
+ push:
+ branches:
+ - main
- workflow_dispatch:
+ workflow_dispatch:
jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: read
+ build:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
- steps:
- - uses: actions/checkout@v2
- - name: Set up JDK 11
- uses: actions/setup-java@v2
- with:
- java-version: "11"
- distribution: "adopt"
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ java-version: "11"
+ distribution: "adopt"
- - run: mkdir ./target
+ - run: mkdir ./target
- - 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: Cache Maven packages
+ uses: actions/cache@v1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
- - name: Build with Maven
- run: mvn -f app/pom.xml --batch-mode --update-snapshots verify
- - run: cp ./app/target/app-0.1.0.jar ./target
+ - 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
- - run: cp ./.deployment/docker-compose.yml ./target
- - name: Archive artifacts
- uses: actions/upload-artifact@v1
- with:
- name: app
- path: ./target/
+ - 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.jar ./target
- deploy:
- runs-on: ubuntu-latest
- needs: [build]
- steps:
- - name: Download app artifacts
- uses: actions/download-artifact@v1
- with:
- name: app
- - name: Copy host via scp
- uses: appleboy/scp-action@master
- env:
- HOST: ${{ secrets.SSH_HOST }}
- USERNAME: ${{ secrets.SSH_USER }}
- PORT: 22
- KEY: ${{ secrets.SSH_PRIVATE_KEY }}
- with:
- source: "app/*"
- target: "/home/${{ secrets.SSH_USER }}/"
- strip_components: 1
- rm: false
- overwrite: true
+ - name: Build with Maven
+ run: mvn -f executor1/pom.xml --batch-mode --update-snapshots verify
+ - run: cp ./executor1/target/executor1-0.0.1.jar ./target
- - name: Executing remote command
- uses: appleboy/ssh-action@master
- with:
- host: ${{ secrets.SSH_HOST }}
- USERNAME: ${{ secrets.SSH_USER }}
- PORT: 22
- KEY: ${{ secrets.SSH_PRIVATE_KEY }}
- script: |
- cd /home/${{ secrets.SSH_USER }}/
- touch acme.json
- sudo chmod 0600 acme.json
- sudo echo "PUB_IP=$(wget -qO- http://ipecho.net/plain | xargs echo)" | sed -e 's/\./-/g' > .env
- sudo docker-compose up -d
+ - name: Build with Maven
+ run: mvn -f executor2/pom.xml --batch-mode --update-snapshots verify
+ - run: cp ./executor2/target/executor2-0.0.1.jar ./target
+
+ - 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.jar ./target
+
+ - run: cp ./.deployment/docker-compose.yml ./target
+ - name: Archive artifacts
+ uses: actions/upload-artifact@v1
+ with:
+ name: app
+ path: ./target/
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: [build]
+ steps:
+ - name: Download app artifacts
+ uses: actions/download-artifact@v1
+ with:
+ name: app
+ - name: Copy host via scp
+ uses: appleboy/scp-action@master
+ env:
+ HOST: ${{ secrets.SSH_HOST }}
+ USERNAME: ${{ secrets.SSH_USER }}
+ PORT: 22
+ KEY: ${{ secrets.SSH_PRIVATE_KEY }}
+ with:
+ source: "app/*"
+ target: "/home/${{ secrets.SSH_USER }}/"
+ strip_components: 1
+ rm: false
+ overwrite: true
+
+ - name: Executing remote command
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.SSH_HOST }}
+ USERNAME: ${{ secrets.SSH_USER }}
+ PORT: 22
+ KEY: ${{ secrets.SSH_PRIVATE_KEY }}
+ script: |
+ cd /home/${{ secrets.SSH_USER }}/
+ touch acme.json
+ sudo chmod 0600 acme.json
+ sudo echo "PUB_IP=$(wget -qO- http://ipecho.net/plain | xargs echo)" | sed -e 's/\./-/g' > .env
+ sudo docker-compose up -d
diff --git a/.github/workflows/ci.executor1.yml b/.github/workflows/ci.executor1.yml
index 277d313..708d7d4 100644
--- a/.github/workflows/ci.executor1.yml
+++ b/.github/workflows/ci.executor1.yml
@@ -1,41 +1,45 @@
name: CI Executor 1
on:
- push:
- branches: [main, dev]
- paths:
- - "executor1/**"
- pull_request:
- branches: [main, dev]
- paths:
- - "executor1/**"
+ push:
+ branches: [main, dev]
+ paths:
+ - "executor-base/**"
+ - "executor1/**"
+ pull_request:
+ branches: [main, dev]
+ paths:
+ - "executor-base/**"
+ - "executor1/**"
- workflow_dispatch:
+ 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 executor1/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor1
+ 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 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
diff --git a/.github/workflows/ci.executor2.yml b/.github/workflows/ci.executor2.yml
index d763cd1..5ae38f0 100644
--- a/.github/workflows/ci.executor2.yml
+++ b/.github/workflows/ci.executor2.yml
@@ -1,41 +1,45 @@
name: CI Executor 2
on:
- push:
- branches: [main, dev]
- paths:
- - "executor2/**"
- pull_request:
- branches: [main, dev]
- paths:
- - "executor2/**"
+ push:
+ branches: [main, dev]
+ paths:
+ - "executor-base/**"
+ - "executor2/**"
+ pull_request:
+ branches: [main, dev]
+ paths:
+ - "executor-base/**"
+ - "executor2/**"
- workflow_dispatch:
+ 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 executor2/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=scs-asse-fs21-group1_tapas-executor2
+ 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 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
diff --git a/assignment/pom.xml b/assignment/pom.xml
index 43af6c3..99996b8 100644
--- a/assignment/pom.xml
+++ b/assignment/pom.xml
@@ -30,6 +30,11 @@
runtime
true
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.projectlombok
lombok
@@ -40,6 +45,24 @@
spring-boot-starter-test
test
+
+ javax.validation
+ validation-api
+ 1.1.0.Final
+
+
+ javax.transaction
+ javax.transaction-api
+ 1.2
+
+
+
+ org.json
+ json
+ 20210307
+
+
+
diff --git a/assignment/src/main/java/ch/unisg/assignment/TestController.java b/assignment/src/main/java/ch/unisg/assignment/TestController.java
deleted file mode 100644
index ac8e4f9..0000000
--- a/assignment/src/main/java/ch/unisg/assignment/TestController.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package ch.unisg.assignment;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class TestController {
- @RequestMapping("/")
- public String index() {
- return "Hello World! Assignment";
- }
-}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/ApplyForTaskController.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/ApplyForTaskController.java
new file mode 100644
index 0000000..1d0111d
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/ApplyForTaskController.java
@@ -0,0 +1,29 @@
+package ch.unisg.assignment.assignment.adapter.in.web;
+
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import ch.unisg.assignment.assignment.application.port.in.ApplyForTaskCommand;
+import ch.unisg.assignment.assignment.application.port.in.ApplyForTaskUseCase;
+import ch.unisg.assignment.assignment.domain.ExecutorInfo;
+import ch.unisg.assignment.assignment.domain.Task;
+
+@RestController
+public class ApplyForTaskController {
+ private final ApplyForTaskUseCase applyForTaskUseCase;
+
+ public ApplyForTaskController(ApplyForTaskUseCase applyForTaskUseCase) {
+ this.applyForTaskUseCase = applyForTaskUseCase;
+ }
+
+ @PostMapping(path = "/task/apply", consumes = {"application/json"})
+ public Task applyForTask(@RequestBody ExecutorInfo executorInfo) {
+
+ ApplyForTaskCommand command = new ApplyForTaskCommand(executorInfo.getExecutorType(),
+ executorInfo.getIp(), executorInfo.getPort());
+
+ return applyForTaskUseCase.applyForTask(command);
+
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/NewTaskController.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/NewTaskController.java
new file mode 100644
index 0000000..18bad8f
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/NewTaskController.java
@@ -0,0 +1,34 @@
+package ch.unisg.assignment.assignment.adapter.in.web;
+
+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 ch.unisg.assignment.assignment.application.port.in.NewTaskCommand;
+import ch.unisg.assignment.assignment.application.port.in.NewTaskUseCase;
+import ch.unisg.assignment.assignment.domain.Task;
+
+@RestController
+public class NewTaskController {
+ private final NewTaskUseCase newTaskUseCase;
+
+ public NewTaskController(NewTaskUseCase newTaskUseCase) {
+ this.newTaskUseCase = newTaskUseCase;
+ }
+
+ @PostMapping(path = "/task", consumes = {"application/json"})
+ public ResponseEntity newTaskController(@RequestBody Task task) {
+
+ NewTaskCommand command = new NewTaskCommand(task.getTaskID(), task.getTaskType());
+
+ boolean success = newTaskUseCase.addNewTaskToQueue(command);
+
+ if (success) {
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+ return new ResponseEntity<>(HttpStatus.CONFLICT);
+
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/TaskCompletedController.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/TaskCompletedController.java
new file mode 100644
index 0000000..e8335ed
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/TaskCompletedController.java
@@ -0,0 +1,34 @@
+package ch.unisg.assignment.assignment.adapter.in.web;
+
+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 ch.unisg.assignment.assignment.application.port.in.TaskCompletedCommand;
+import ch.unisg.assignment.assignment.application.port.in.TaskCompletedUseCase;
+import ch.unisg.assignment.assignment.domain.Task;
+
+@RestController
+public class TaskCompletedController {
+
+ private final TaskCompletedUseCase taskCompletedUseCase;
+
+ public TaskCompletedController(TaskCompletedUseCase taskCompletedUseCase) {
+ this.taskCompletedUseCase = taskCompletedUseCase;
+ }
+
+ @PostMapping(path = "/task/completed", consumes = {"application/json"})
+ public ResponseEntity addNewTaskTaskToTaskList(@RequestBody Task task) {
+
+ TaskCompletedCommand command = new TaskCompletedCommand(task.getTaskID(), task.getTaskType(),
+ task.getStatus(), task.getResult());
+
+ taskCompletedUseCase.taskCompleted(command);
+
+ return new ResponseEntity<>(HttpStatus.OK);
+
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/WebControllerExceptionHandler.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/WebControllerExceptionHandler.java
new file mode 100644
index 0000000..08a0895
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/WebControllerExceptionHandler.java
@@ -0,0 +1,31 @@
+package ch.unisg.assignment.assignment.adapter.in.web;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import ch.unisg.assignment.common.exception.ErrorResponse;
+import ch.unisg.assignment.common.exception.InvalidIP4Exception;
+import ch.unisg.assignment.common.exception.PortOutOfRangeException;
+
+@ControllerAdvice
+public class WebControllerExceptionHandler {
+
+ @ExceptionHandler(PortOutOfRangeException.class)
+ public ResponseEntity handleException(PortOutOfRangeException e){
+
+ ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST, e.getLocalizedMessage());
+ return new ResponseEntity<>(error, error.getHttpStatus());
+
+ }
+
+ @ExceptionHandler(InvalidIP4Exception.class)
+ public ResponseEntity handleException(InvalidIP4Exception e){
+
+ ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST, e.getLocalizedMessage());
+ return new ResponseEntity<>(error, error.getHttpStatus());
+
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishNewTaskEventAdapter.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishNewTaskEventAdapter.java
new file mode 100644
index 0000000..1db2b84
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishNewTaskEventAdapter.java
@@ -0,0 +1,44 @@
+package ch.unisg.assignment.assignment.adapter.out.web;
+
+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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.out.NewTaskEventPort;
+import ch.unisg.assignment.assignment.domain.event.NewTaskEvent;
+
+@Component
+@Primary
+public class PublishNewTaskEventAdapter implements NewTaskEventPort {
+
+ String server = "http://127.0.0.1:8085";
+
+ Logger logger = Logger.getLogger(PublishNewTaskEventAdapter.class.getName());
+
+ @Override
+ public void publishNewTaskEvent(NewTaskEvent event) {
+
+ 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 (IOException | InterruptedException e) {
+ logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskAssignedEventAdapter.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskAssignedEventAdapter.java
new file mode 100644
index 0000000..85bb9ab
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskAssignedEventAdapter.java
@@ -0,0 +1,50 @@
+package ch.unisg.assignment.assignment.adapter.out.web;
+
+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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.json.JSONObject;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.out.TaskAssignedEventPort;
+import ch.unisg.assignment.assignment.domain.event.TaskAssignedEvent;
+
+@Component
+@Primary
+public class PublishTaskAssignedEventAdapter implements TaskAssignedEventPort {
+
+ String server = "http://127.0.0.1:8085";
+
+ Logger logger = Logger.getLogger(PublishTaskAssignedEventAdapter.class.getName());
+
+ @Override
+ public void publishTaskAssignedEvent(TaskAssignedEvent event) {
+
+ String body = new JSONObject()
+ .put("taskId", event.taskID)
+ .toString();
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(server + "/tasks/completeTask"))
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(body))
+ .build();
+
+
+ try {
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ } catch (IOException | InterruptedException e) {
+ logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskCompletedEventAdapter.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskCompletedEventAdapter.java
new file mode 100644
index 0000000..f9f2833
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskCompletedEventAdapter.java
@@ -0,0 +1,53 @@
+package ch.unisg.assignment.assignment.adapter.out.web;
+
+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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.json.JSONObject;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.out.TaskCompletedEventPort;
+import ch.unisg.assignment.assignment.domain.event.TaskCompletedEvent;
+
+@Component
+@Primary
+public class PublishTaskCompletedEventAdapter implements TaskCompletedEventPort {
+
+ String server = "http://127.0.0.1:8081";
+
+ Logger logger = Logger.getLogger(PublishTaskCompletedEventAdapter.class.getName());
+
+ @Override
+ public void publishTaskCompleted(TaskCompletedEvent event) {
+
+ String body = new JSONObject()
+ .put("taskId", event.taskID)
+ .put("status", event.status)
+ .put("taskResult", event.result)
+ .toString();
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(server + "/tasks/completeTask"))
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(body))
+ .build();
+
+
+ try {
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ } catch (IOException | InterruptedException e) {
+ logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskCommand.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskCommand.java
new file mode 100644
index 0000000..df36d58
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskCommand.java
@@ -0,0 +1,32 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+import javax.validation.constraints.NotNull;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import ch.unisg.assignment.assignment.domain.valueobject.IP4Adress;
+import ch.unisg.assignment.assignment.domain.valueobject.Port;
+import ch.unisg.assignment.common.SelfValidating;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper=false)
+public class ApplyForTaskCommand extends SelfValidating{
+
+ @NotNull
+ private final ExecutorType taskType;
+
+ @NotNull
+ private final IP4Adress executorIP;
+
+
+ @NotNull
+ private final Port executorPort;
+
+ public ApplyForTaskCommand(ExecutorType taskType, IP4Adress executorIP, Port executorPort) {
+ this.taskType = taskType;
+ this.executorIP = executorIP;
+ this.executorPort = executorPort;
+ this.validateSelf();
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskUseCase.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskUseCase.java
new file mode 100644
index 0000000..1e7180a
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskUseCase.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+import ch.unisg.assignment.assignment.domain.Task;
+
+public interface ApplyForTaskUseCase {
+ Task applyForTask(ApplyForTaskCommand applyForTaskCommand);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskCommand.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskCommand.java
new file mode 100644
index 0000000..ab6838e
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskCommand.java
@@ -0,0 +1,25 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+import javax.validation.constraints.NotNull;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import ch.unisg.assignment.common.SelfValidating;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper=false)
+public class NewTaskCommand extends SelfValidating {
+
+ @NotNull
+ private final String taskID;
+
+ @NotNull
+ private final ExecutorType taskType;
+
+ public NewTaskCommand(String taskID, ExecutorType taskType) {
+ this.taskID = taskID;
+ this.taskType = taskType;
+ this.validateSelf();
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskUseCase.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskUseCase.java
new file mode 100644
index 0000000..21f084e
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskUseCase.java
@@ -0,0 +1,5 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+public interface NewTaskUseCase {
+ boolean addNewTaskToQueue(NewTaskCommand newTaskCommand);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedCommand.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedCommand.java
new file mode 100644
index 0000000..e324e89
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedCommand.java
@@ -0,0 +1,34 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+import javax.validation.constraints.NotNull;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import ch.unisg.assignment.common.SelfValidating;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper=false)
+public class TaskCompletedCommand extends SelfValidating{
+
+ @NotNull
+ private final String taskID;
+
+ @NotNull
+ private final ExecutorType taskType;
+
+ @NotNull
+ private final String taskStatus;
+
+ @NotNull
+ private final String taskResult;
+
+ public TaskCompletedCommand(String taskID, ExecutorType taskType, String taskStatus, String taskResult) {
+ this.taskID = taskID;
+ this.taskType = taskType;
+ this.taskStatus = taskStatus;
+ this.taskResult = taskResult;
+ this.validateSelf();
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedUseCase.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedUseCase.java
new file mode 100644
index 0000000..1902952
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedUseCase.java
@@ -0,0 +1,5 @@
+package ch.unisg.assignment.assignment.application.port.in;
+
+public interface TaskCompletedUseCase {
+ void taskCompleted(TaskCompletedCommand taskCompletedCommand);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/NewTaskEventPort.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/NewTaskEventPort.java
new file mode 100644
index 0000000..909a9ba
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/NewTaskEventPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.assignment.application.port.out;
+
+import ch.unisg.assignment.assignment.domain.event.NewTaskEvent;
+
+public interface NewTaskEventPort {
+ void publishNewTaskEvent(NewTaskEvent event);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskAssignedEventPort.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskAssignedEventPort.java
new file mode 100644
index 0000000..fefd4a1
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskAssignedEventPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.assignment.application.port.out;
+
+import ch.unisg.assignment.assignment.domain.event.TaskAssignedEvent;
+
+public interface TaskAssignedEventPort {
+ void publishTaskAssignedEvent(TaskAssignedEvent taskAssignedEvent);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskCompletedEventPort.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskCompletedEventPort.java
new file mode 100644
index 0000000..43a8aa5
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/out/TaskCompletedEventPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.assignment.application.port.out;
+
+import ch.unisg.assignment.assignment.domain.event.TaskCompletedEvent;
+
+public interface TaskCompletedEventPort {
+ void publishTaskCompleted(TaskCompletedEvent event);
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/ApplyForTaskService.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/ApplyForTaskService.java
new file mode 100644
index 0000000..0593a30
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/ApplyForTaskService.java
@@ -0,0 +1,34 @@
+package ch.unisg.assignment.assignment.application.service;
+
+import javax.transaction.Transactional;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.in.ApplyForTaskCommand;
+import ch.unisg.assignment.assignment.application.port.in.ApplyForTaskUseCase;
+import ch.unisg.assignment.assignment.application.port.out.TaskAssignedEventPort;
+import ch.unisg.assignment.assignment.domain.Roster;
+import ch.unisg.assignment.assignment.domain.Task;
+import ch.unisg.assignment.assignment.domain.event.TaskAssignedEvent;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class ApplyForTaskService implements ApplyForTaskUseCase {
+
+ private final TaskAssignedEventPort taskAssignedEventPort;
+
+ @Override
+ public Task applyForTask(ApplyForTaskCommand command) {
+ Task task = Roster.getInstance().assignTaskToExecutor(command.getTaskType(),
+ command.getExecutorIP(), command.getExecutorPort());
+
+ if (task != null) {
+ taskAssignedEventPort.publishTaskAssignedEvent(new TaskAssignedEvent(task.getTaskID()));
+ }
+
+ return task;
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/NewTaskService.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/NewTaskService.java
new file mode 100644
index 0000000..069ee29
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/NewTaskService.java
@@ -0,0 +1,46 @@
+package ch.unisg.assignment.assignment.application.service;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.transaction.Transactional;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.in.NewTaskCommand;
+import ch.unisg.assignment.assignment.application.port.in.NewTaskUseCase;
+import ch.unisg.assignment.assignment.application.port.out.NewTaskEventPort;
+import ch.unisg.assignment.assignment.domain.Roster;
+import ch.unisg.assignment.assignment.domain.Task;
+import ch.unisg.assignment.assignment.domain.event.NewTaskEvent;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class NewTaskService implements NewTaskUseCase {
+
+ private final NewTaskEventPort newTaskEventPort;
+
+ @Override
+ public boolean addNewTaskToQueue(NewTaskCommand command) {
+
+ // TODO Get availableTaskTypes from executor pool
+ List availableTaskTypes = Arrays.asList("ADDITION", "ROBOT");
+
+ if (!availableTaskTypes.contains(command.getTaskType().getValue())) {
+ return false;
+ }
+
+ Task task = new Task(command.getTaskID(), command.getTaskType());
+
+ Roster.getInstance().addTaskToQueue(task);
+
+ // TODO this event should be in the roster function xyz
+ NewTaskEvent newTaskEvent = new NewTaskEvent(task.getTaskType());
+ newTaskEventPort.publishNewTaskEvent(newTaskEvent);
+
+ return true;
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/TaskCompletedService.java b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/TaskCompletedService.java
new file mode 100644
index 0000000..c8273ff
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/service/TaskCompletedService.java
@@ -0,0 +1,31 @@
+package ch.unisg.assignment.assignment.application.service;
+
+import javax.transaction.Transactional;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.assignment.assignment.application.port.in.TaskCompletedCommand;
+import ch.unisg.assignment.assignment.application.port.in.TaskCompletedUseCase;
+import ch.unisg.assignment.assignment.application.port.out.TaskCompletedEventPort;
+import ch.unisg.assignment.assignment.domain.Roster;
+import ch.unisg.assignment.assignment.domain.event.TaskCompletedEvent;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class TaskCompletedService implements TaskCompletedUseCase {
+
+ private final TaskCompletedEventPort taskCompletedEventPort;
+
+ @Override
+ public void taskCompleted(TaskCompletedCommand command) {
+
+ Roster.getInstance().taskCompleted(command.getTaskID());
+
+ taskCompletedEventPort.publishTaskCompleted(new TaskCompletedEvent(command.getTaskID(),
+ command.getTaskStatus(), command.getTaskResult()));
+
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/ExecutorInfo.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/ExecutorInfo.java
new file mode 100644
index 0000000..6b19dcc
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/ExecutorInfo.java
@@ -0,0 +1,21 @@
+package ch.unisg.assignment.assignment.domain;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import ch.unisg.assignment.assignment.domain.valueobject.IP4Adress;
+import ch.unisg.assignment.assignment.domain.valueobject.Port;
+import lombok.Getter;
+import lombok.Setter;
+
+public class ExecutorInfo {
+ @Getter
+ @Setter
+ private IP4Adress ip;
+
+ @Getter
+ @Setter
+ private Port port;
+
+ @Getter
+ @Setter
+ private ExecutorType executorType;
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Roster.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Roster.java
new file mode 100644
index 0000000..521a748
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Roster.java
@@ -0,0 +1,53 @@
+package ch.unisg.assignment.assignment.domain;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import ch.unisg.assignment.assignment.domain.valueobject.IP4Adress;
+import ch.unisg.assignment.assignment.domain.valueobject.Port;
+
+public class Roster {
+
+ private static final Roster roster = new Roster();
+
+ private HashMap> queues = new HashMap<>();
+
+ private HashMap rosterMap = new HashMap<>();
+
+ public static Roster getInstance() {
+ return roster;
+ }
+
+ private Roster() {}
+
+ public void addTaskToQueue(Task task) {
+ if (queues.containsKey(task.getTaskType().getValue())) {
+ queues.get(task.getTaskType().getValue()).add(task);
+ } else {
+ queues.put(task.getTaskType().getValue(), new ArrayList<>(Arrays.asList(task)));
+ }
+ }
+
+ public Task assignTaskToExecutor(ExecutorType taskType, IP4Adress executorIP, Port executorPort) {
+ if (!queues.containsKey(taskType.getValue())) {
+ return null;
+ }
+ if (queues.get(taskType.getValue()).isEmpty()) {
+ return null;
+ }
+
+ Task task = queues.get(taskType.getValue()).remove(0);
+
+ rosterMap.put(task.getTaskID(), new RosterItem(task.getTaskID(),
+ task.getTaskType().getValue(), executorIP, executorPort));
+
+ return task;
+ }
+
+ public void taskCompleted(String taskID) {
+ rosterMap.remove(taskID);
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/RosterItem.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/RosterItem.java
new file mode 100644
index 0000000..2c3bb52
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/RosterItem.java
@@ -0,0 +1,29 @@
+package ch.unisg.assignment.assignment.domain;
+
+import ch.unisg.assignment.assignment.domain.valueobject.IP4Adress;
+import ch.unisg.assignment.assignment.domain.valueobject.Port;
+import lombok.Getter;
+
+public class RosterItem {
+
+ @Getter
+ private String taskID;
+
+ @Getter
+ private String taskType;
+
+ @Getter
+ private IP4Adress executorIP;
+
+ @Getter
+ private Port executorPort;
+
+
+ public RosterItem(String taskID, String taskType, IP4Adress executorIP, Port executorPort) {
+ this.taskID = taskID;
+ this.taskType = taskType;
+ this.executorIP = executorIP;
+ this.executorPort = executorPort;
+ }
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Task.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Task.java
new file mode 100644
index 0000000..7daa738
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Task.java
@@ -0,0 +1,35 @@
+package ch.unisg.assignment.assignment.domain;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+import lombok.Getter;
+import lombok.Setter;
+
+public class Task {
+
+ @Getter
+ private String taskID;
+
+ @Getter
+ private ExecutorType taskType;
+
+ @Getter
+ @Setter
+ private String result;
+
+ @Getter
+ @Setter
+ private String status;
+
+ public Task(String taskID, String taskType) {
+ this.taskID = taskID;
+ this.taskType = new ExecutorType(taskType);
+ }
+
+ public Task(String taskID, ExecutorType taskType) {
+ this.taskID = taskID;
+ this.taskType = taskType;
+ }
+
+ public Task() {}
+
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/NewTaskEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/NewTaskEvent.java
new file mode 100644
index 0000000..34e7f0b
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/NewTaskEvent.java
@@ -0,0 +1,11 @@
+package ch.unisg.assignment.assignment.domain.event;
+
+import ch.unisg.assignment.assignment.domain.valueobject.ExecutorType;
+
+public class NewTaskEvent {
+ public final ExecutorType taskType;
+
+ public NewTaskEvent(ExecutorType taskType) {
+ this.taskType = taskType;
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskAssignedEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskAssignedEvent.java
new file mode 100644
index 0000000..d0178d4
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskAssignedEvent.java
@@ -0,0 +1,9 @@
+package ch.unisg.assignment.assignment.domain.event;
+
+public class TaskAssignedEvent {
+ public final String taskID;
+
+ public TaskAssignedEvent(String taskID) {
+ this.taskID = taskID;
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskCompletedEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskCompletedEvent.java
new file mode 100644
index 0000000..432a8f0
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/event/TaskCompletedEvent.java
@@ -0,0 +1,15 @@
+package ch.unisg.assignment.assignment.domain.event;
+
+public class TaskCompletedEvent {
+ public final String taskID;
+
+ public final String status;
+
+ public final String result;
+
+ public TaskCompletedEvent(String taskID, String status, String result) {
+ this.taskID = taskID;
+ this.status = status;
+ this.result = result;
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/ExecutorType.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/ExecutorType.java
new file mode 100644
index 0000000..bc5f467
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/ExecutorType.java
@@ -0,0 +1,12 @@
+package ch.unisg.assignment.assignment.domain.valueobject;
+
+import lombok.Value;
+
+@Value
+public class ExecutorType {
+ private String value;
+
+ public ExecutorType(String type) {
+ this.value = type.toUpperCase();
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/IP4Adress.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/IP4Adress.java
new file mode 100644
index 0000000..cd23b6b
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/IP4Adress.java
@@ -0,0 +1,23 @@
+package ch.unisg.assignment.assignment.domain.valueobject;
+
+import ch.unisg.assignment.common.exception.InvalidIP4Exception;
+import lombok.Value;
+
+@Value
+public class IP4Adress {
+ private String value;
+
+ public IP4Adress(String ip4) throws InvalidIP4Exception {
+ if (ip4.equalsIgnoreCase("localhost") ||
+ ip4.matches("^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)(\\.(?!$)|$)){4}$")) {
+ this.value = ip4;
+ } else {
+ throw new InvalidIP4Exception();
+ }
+ }
+}
+
+
+
+
+
diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/Port.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/Port.java
new file mode 100644
index 0000000..a66dbbd
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/valueobject/Port.java
@@ -0,0 +1,17 @@
+package ch.unisg.assignment.assignment.domain.valueobject;
+
+import ch.unisg.assignment.common.exception.PortOutOfRangeException;
+import lombok.Value;
+
+@Value
+public class Port {
+ private int value;
+
+ public Port(int port) throws PortOutOfRangeException {
+ if (1024 <= port && port <= 65535) {
+ this.value = port;
+ } else {
+ throw new PortOutOfRangeException();
+ }
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/common/SelfValidating.java b/assignment/src/main/java/ch/unisg/assignment/common/SelfValidating.java
new file mode 100644
index 0000000..a8d366f
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/common/SelfValidating.java
@@ -0,0 +1,31 @@
+package ch.unisg.assignment.common;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.util.Set;
+
+public abstract class SelfValidating {
+
+ private Validator validator;
+
+ protected SelfValidating() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+ }
+
+ /**
+ * Evaluates all Bean Validations on the attributes of this
+ * instance.
+ */
+ protected void validateSelf() {
+ @SuppressWarnings("unchecked")
+ Set> violations = validator.validate((T) this);
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(violations);
+ }
+ }
+}
+
diff --git a/assignment/src/main/java/ch/unisg/assignment/common/exception/ErrorResponse.java b/assignment/src/main/java/ch/unisg/assignment/common/exception/ErrorResponse.java
new file mode 100644
index 0000000..2fb834e
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/common/exception/ErrorResponse.java
@@ -0,0 +1,13 @@
+package ch.unisg.assignment.common.exception;
+
+import org.springframework.http.HttpStatus;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+@Data
+@RequiredArgsConstructor
+public class ErrorResponse {
+ private final HttpStatus httpStatus;
+ private final String message;
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/common/exception/InvalidIP4Exception.java b/assignment/src/main/java/ch/unisg/assignment/common/exception/InvalidIP4Exception.java
new file mode 100644
index 0000000..fecbfcb
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/common/exception/InvalidIP4Exception.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.common.exception;
+
+public class InvalidIP4Exception extends Exception {
+ public InvalidIP4Exception() {
+ super("IP4 is invalid");
+ }
+}
diff --git a/assignment/src/main/java/ch/unisg/assignment/common/exception/PortOutOfRangeException.java b/assignment/src/main/java/ch/unisg/assignment/common/exception/PortOutOfRangeException.java
new file mode 100644
index 0000000..2772256
--- /dev/null
+++ b/assignment/src/main/java/ch/unisg/assignment/common/exception/PortOutOfRangeException.java
@@ -0,0 +1,7 @@
+package ch.unisg.assignment.common.exception;
+
+public class PortOutOfRangeException extends Exception {
+ public PortOutOfRangeException() {
+ super("Port is out of available range (1024-65535)");
+ }
+}
diff --git a/assignment/src/main/resources/application.properties b/assignment/src/main/resources/application.properties
index 4d360de..3cf12af 100644
--- a/assignment/src/main/resources/application.properties
+++ b/assignment/src/main/resources/application.properties
@@ -1 +1 @@
-server.port=8081
+server.port=8082
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 76b8af1..8c4bfcf 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,62 +1,62 @@
version: "3.6"
services:
- tapas-tasks:
- container_name: tapas-tasks
- build:
- context: "./tapas-tasks"
- dockerfile: "Dockerfile"
- target: development
- ports:
- - "8081:8081"
- - "5005:5005"
- volumes:
- - ./tapas-tasks/src:/opt/app/src
- - ./tapas-tasks/target:/opt/app/target
- assignment:
- container_name: assignment
- build:
- context: "./assignment"
- dockerfile: "Dockerfile"
- target: development
- ports:
- - "8082:8081"
- - "5006:5005"
- volumes:
- - ./assignment/src:/opt/app/src
- - ./assignment/target:/opt/app/target
- executor-pool:
- container_name: executor-pool
- build:
- context: "./executor-pool"
- dockerfile: "Dockerfile"
- target: development
- ports:
- - "8083:8081"
- - "5007:5005"
- volumes:
- - ./executor-pool/src:/opt/app/src
- - ./executor-pool/target:/opt/app/target
- executor1:
- container_name: executor1
- build:
- context: "./executor1"
- dockerfile: "Dockerfile"
- target: development
- ports:
- - "8084:8081"
- - "5008:5005"
- volumes:
- - ./executor1/src:/opt/app/src
- - ./executor1/target:/opt/app/target
- executor2:
- container_name: executor2
- build:
- context: "./executor2"
- dockerfile: "Dockerfile"
- target: development
- ports:
- - "8085:8081"
- - "5009:5005"
- volumes:
- - ./executor2/src:/opt/app/src
- - ./executor2/target:/opt/app/target
+ tapas-tasks:
+ container_name: tapas-tasks
+ build:
+ context: "./tapas-tasks"
+ dockerfile: "Dockerfile"
+ target: development
+ ports:
+ - "8081:8081"
+ - "5005:5005"
+ volumes:
+ - ./tapas-tasks/src:/opt/app/src
+ - ./tapas-tasks/target:/opt/app/target
+ assignment:
+ container_name: assignment
+ build:
+ context: "./assignment"
+ dockerfile: "Dockerfile"
+ target: development
+ ports:
+ - "8082:8082"
+ - "5006:5005"
+ volumes:
+ - ./assignment/src:/opt/app/src
+ - ./assignment/target:/opt/app/target
+ executor-pool:
+ container_name: executor-pool
+ build:
+ context: "./executor-pool"
+ dockerfile: "Dockerfile"
+ target: development
+ ports:
+ - "8083:8081"
+ - "5007:5005"
+ volumes:
+ - ./executor-pool/src:/opt/app/src
+ - ./executor-pool/target:/opt/app/target
+ executor1:
+ container_name: executor1
+ build:
+ context: "./executor1"
+ dockerfile: "Dockerfile"
+ target: development
+ ports:
+ - "8084:8081"
+ - "5008:5005"
+ volumes:
+ - ./executor1/src:/opt/app/src
+ - ./executor1/target:/opt/app/target
+ executor2:
+ container_name: executor2
+ build:
+ context: "./executor2"
+ dockerfile: "Dockerfile"
+ target: development
+ ports:
+ - "8085:8085"
+ - "5009:5005"
+ volumes:
+ - ./executor2/src:/opt/app/src
+ - ./executor2/target:/opt/app/target
diff --git a/executor-base/.gitignore b/executor-base/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/executor-base/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/executor-base/.mvn/wrapper/MavenWrapperDownloader.java b/executor-base/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..e76d1f3
--- /dev/null
+++ b/executor-base/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/executor-base/.mvn/wrapper/maven-wrapper.jar b/executor-base/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/executor-base/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/executor-base/.mvn/wrapper/maven-wrapper.properties b/executor-base/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..abd303b
--- /dev/null
+++ b/executor-base/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/executor-base/Dockerfile b/executor-base/Dockerfile
new file mode 100644
index 0000000..db90fb6
--- /dev/null
+++ b/executor-base/Dockerfile
@@ -0,0 +1,18 @@
+FROM openjdk:11 AS development
+
+WORKDIR /opt/app
+
+# ENV SPRING_DATASOURCE_URL=jdbc:mysql://backend-db:3306/db
+
+COPY .mvn/ .mvn
+COPY mvnw pom.xml mvnw.cmd ./
+
+RUN apt-get clean && apt-get update && apt-get install dos2unix
+RUN dos2unix mvnw
+
+RUN ./mvnw dependency:go-offline
+
+COPY src /opt/app/src
+COPY *target /opt/app/target
+
+CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.jvmArguments=\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005\"", "-Dspring.devtools.restart.enabled=true"]
diff --git a/executor-base/mvnw b/executor-base/mvnw
new file mode 100755
index 0000000..a16b543
--- /dev/null
+++ b/executor-base/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/executor-base/mvnw.cmd b/executor-base/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/executor-base/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/executor-base/pom.xml b/executor-base/pom.xml
new file mode 100644
index 0000000..2acba18
--- /dev/null
+++ b/executor-base/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.5
+
+
+ ch.unisg
+ executorBase
+ 0.0.1-SNAPSHOT
+ executorBase
+ Demo project for Spring Boot
+
+ 11
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ javax.validation
+ validation-api
+ 1.1.0.Final
+
+
+
+ javax.transaction
+ javax.transaction-api
+ 1.2
+
+
+
+ org.json
+ json
+ 20210307
+
+
+
+
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/Executor1Application.java b/executor-base/src/main/java/ch/unisg/executorBase/Executor1Application.java
new file mode 100644
index 0000000..9bd3fa5
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/Executor1Application.java
@@ -0,0 +1,13 @@
+package ch.unisg.executorBase;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Executor1Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Executor1Application.class, args);
+ }
+
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/common/SelfValidating.java b/executor-base/src/main/java/ch/unisg/executorBase/common/SelfValidating.java
new file mode 100644
index 0000000..5119ac5
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/common/SelfValidating.java
@@ -0,0 +1,30 @@
+package ch.unisg.executorBase.common;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.util.Set;
+
+public class SelfValidating {
+
+ private Validator validator;
+
+ public SelfValidating() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+ }
+
+ /**
+ * Evaluates all Bean Validations on the attributes of this
+ * instance.
+ */
+ protected void validateSelf() {
+ @SuppressWarnings("unchecked")
+ Set> violations = validator.validate((T) this);
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(violations);
+ }
+ }
+}
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
new file mode 100644
index 0000000..182f2ba
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java
@@ -0,0 +1,36 @@
+package ch.unisg.executorBase.executor.adapter.in.web;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+@RestController
+public class TaskAvailableController {
+ private final TaskAvailableUseCase taskAvailableUseCase;
+
+ public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
+ this.taskAvailableUseCase = taskAvailableUseCase;
+ }
+
+ @GetMapping(path = "/newtask/{taskType}")
+ public ResponseEntity retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
+
+ if (ExecutorType.contains(taskType.toUpperCase())) {
+ TaskAvailableCommand command = new TaskAvailableCommand(
+ ExecutorType.valueOf(taskType.toUpperCase()));
+ taskAvailableUseCase.newTaskAvailable(command);
+ }
+
+ // Add the content type as a response header
+ HttpHeaders responseHeaders = new HttpHeaders();
+
+ return new ResponseEntity<>("OK", responseHeaders, HttpStatus.OK);
+ }
+}
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
new file mode 100644
index 0000000..fd26d47
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java
@@ -0,0 +1,50 @@
+package ch.unisg.executorBase.executor.adapter.out.web;
+
+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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.json.JSONObject;
+
+import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEventPort;
+import ch.unisg.executorBase.executor.domain.ExecutionFinishedEvent;
+
+public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort {
+
+ String server = "http://127.0.0.1:8082";
+
+ Logger logger = Logger.getLogger(ExecutionFinishedEventAdapter.class.getName());
+
+ @Override
+ public void publishExecutionFinishedEvent(ExecutionFinishedEvent event) {
+
+ String body = new JSONObject()
+ .put("taskID", event.getTaskID())
+ .put("result", event.getResult())
+ .put("status", event.getStatus())
+ .toString();
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(server+"/task/"+event.getTaskID()))
+ .header("Content-Type", "application/json")
+ .PUT(HttpRequest.BodyPublishers.ofString(body))
+ .build();
+
+ try {
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+ } catch (IOException | InterruptedException e) {
+ logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+
+ System.out.println("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
new file mode 100644
index 0000000..05852fa
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java
@@ -0,0 +1,61 @@
+package ch.unisg.executorBase.executor.adapter.out.web;
+
+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.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executorBase.executor.application.port.out.GetAssignmentPort;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+import ch.unisg.executorBase.executor.domain.Task;
+
+import org.json.JSONObject;
+
+@Component
+@Primary
+public class GetAssignmentAdapter implements GetAssignmentPort {
+
+ String server = "http://127.0.0.1:8082";
+
+ Logger logger = Logger.getLogger(GetAssignmentAdapter.class.getName());
+
+ @Override
+ public Task getAssignment(ExecutorType executorType, String ip, int port) {
+
+ String body = new JSONObject()
+ .put("executorType", executorType)
+ .put("ip", ip)
+ .put("port", port)
+ .toString();
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(server+"/task/apply"))
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(body))
+ .build();
+
+ try {
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ if (response.body().equals("")) {
+ return null;
+ }
+
+ return new Task(new JSONObject(response.body()).getString("taskID"));
+
+ } catch (IOException | InterruptedException e) {
+ logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+
+ return null;
+ }
+
+}
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
new file mode 100644
index 0000000..bf465f7
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/NotifyExecutorPoolAdapter.java
@@ -0,0 +1,50 @@
+package ch.unisg.executorBase.executor.adapter.out.web;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+
+import org.json.JSONObject;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+@Component
+@Primary
+public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort {
+
+ String server = "http://127.0.0.1:8083";
+
+ @Override
+ public boolean notifyExecutorPool(String ip, int port, ExecutorType executorType) {
+
+ String body = new JSONObject()
+ .put("executorType", executorType)
+ .put("ip", ip)
+ .put("port", port)
+ .toString();
+
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(server+"/executor/new/"))
+ .POST(HttpRequest.BodyPublishers.ofString(body))
+ .build();
+
+ /** Needs the other service running
+ try {
+ HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ **/
+
+ // TODO return true or false depending on result of http request;
+
+ return true;
+ }
+
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableCommand.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableCommand.java
new file mode 100644
index 0000000..cfa32bb
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableCommand.java
@@ -0,0 +1,20 @@
+package ch.unisg.executorBase.executor.application.port.in;
+
+import ch.unisg.executorBase.common.SelfValidating;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+import javax.validation.constraints.NotNull;
+
+import lombok.Value;
+
+@Value
+public class TaskAvailableCommand extends SelfValidating {
+
+ @NotNull
+ private final ExecutorType taskType;
+
+ public TaskAvailableCommand(ExecutorType taskType) {
+ this.taskType = taskType;
+ this.validateSelf();
+ }
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableUseCase.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableUseCase.java
new file mode 100644
index 0000000..cc5215f
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/in/TaskAvailableUseCase.java
@@ -0,0 +1,5 @@
+package ch.unisg.executorBase.executor.application.port.in;
+
+public interface TaskAvailableUseCase {
+ void newTaskAvailable(TaskAvailableCommand command);
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/ExecutionFinishedEventPort.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/ExecutionFinishedEventPort.java
new file mode 100644
index 0000000..1bf668e
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/ExecutionFinishedEventPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.executorBase.executor.application.port.out;
+
+import ch.unisg.executorBase.executor.domain.ExecutionFinishedEvent;
+
+public interface ExecutionFinishedEventPort {
+ void publishExecutionFinishedEvent(ExecutionFinishedEvent event);
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/GetAssignmentPort.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/GetAssignmentPort.java
new file mode 100644
index 0000000..79d3a0a
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/GetAssignmentPort.java
@@ -0,0 +1,8 @@
+package ch.unisg.executorBase.executor.application.port.out;
+
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+import ch.unisg.executorBase.executor.domain.Task;
+
+public interface GetAssignmentPort {
+ Task getAssignment(ExecutorType executorType, String ip, int port);
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/NotifyExecutorPoolPort.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/NotifyExecutorPoolPort.java
new file mode 100644
index 0000000..6d41ab4
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/port/out/NotifyExecutorPoolPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.executorBase.executor.application.port.out;
+
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+public interface NotifyExecutorPoolPort {
+ boolean notifyExecutorPool(String ip, int port, ExecutorType executorType);
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/NotifyExecutorPoolService.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/NotifyExecutorPoolService.java
new file mode 100644
index 0000000..a5ccb64
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/NotifyExecutorPoolService.java
@@ -0,0 +1,15 @@
+package ch.unisg.executorBase.executor.application.service;
+
+import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class NotifyExecutorPoolService {
+
+ private final NotifyExecutorPoolPort notifyExecutorPoolPort;
+
+ public boolean notifyExecutorPool(String ip, int port, ExecutorType executorType) {
+ return notifyExecutorPoolPort.notifyExecutorPool(ip, port, executorType);
+ }
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/TaskAvailableService.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/TaskAvailableService.java
new file mode 100644
index 0000000..a4f5e6e
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/application/service/TaskAvailableService.java
@@ -0,0 +1,20 @@
+package ch.unisg.executorBase.executor.application.service;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import lombok.RequiredArgsConstructor;
+
+import javax.transaction.Transactional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class TaskAvailableService implements TaskAvailableUseCase {
+
+ @Override
+ public void newTaskAvailable(TaskAvailableCommand command) {
+ // Placeholder so spring can create a bean
+ }
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutionFinishedEvent.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutionFinishedEvent.java
new file mode 100644
index 0000000..31fd0e6
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutionFinishedEvent.java
@@ -0,0 +1,21 @@
+package ch.unisg.executorBase.executor.domain;
+
+import lombok.Getter;
+
+public class ExecutionFinishedEvent {
+
+ @Getter
+ private String taskID;
+
+ @Getter
+ private String result;
+
+ @Getter
+ private String status;
+
+ public ExecutionFinishedEvent(String taskID, String result, String status) {
+ this.taskID = taskID;
+ this.result = result;
+ this.status = status;
+ }
+}
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
new file mode 100644
index 0000000..c9df1a8
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java
@@ -0,0 +1,75 @@
+package ch.unisg.executorBase.executor.domain;
+
+import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEventPort;
+import ch.unisg.executorBase.executor.application.port.out.GetAssignmentPort;
+import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
+
+import ch.unisg.executorBase.executor.adapter.out.web.ExecutionFinishedEventAdapter;
+import ch.unisg.executorBase.executor.adapter.out.web.GetAssignmentAdapter;
+import ch.unisg.executorBase.executor.adapter.out.web.NotifyExecutorPoolAdapter;
+import ch.unisg.executorBase.executor.application.service.NotifyExecutorPoolService;
+import lombok.Getter;
+
+public abstract class ExecutorBase {
+
+ @Getter
+ private String ip;
+
+ @Getter
+ private ExecutorType executorType;
+
+ @Getter
+ private int port;
+
+ @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 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();
+ private final ExecutionFinishedEventPort executionFinishedEventPort = new ExecutionFinishedEventAdapter();
+
+ public ExecutorBase(ExecutorType executorType) {
+ System.out.println("Starting Executor");
+ // TODO set this automaticly
+ this.ip = "localhost";
+ this.port = 8084;
+ this.executorType = executorType;
+
+ this.status = ExecutorStatus.STARTING_UP;
+ if(!notifyExecutorPoolService.notifyExecutorPool(this.ip, this.port, this.executorType)) {
+ System.exit(0);
+ } else {
+ this.status = ExecutorStatus.IDLING;
+ getAssignment();
+ }
+ }
+
+ public void getAssignment() {
+ Task newTask = getAssignmentPort.getAssignment(this.getExecutorType(), this.getIp(),
+ this.getPort());
+ if (newTask != null) {
+ this.executeTask(newTask);
+ } else {
+ this.status = ExecutorStatus.IDLING;
+ }
+ }
+
+ private void executeTask(Task task) {
+ System.out.println("Starting execution");
+ this.status = ExecutorStatus.EXECUTING;
+
+ task.setResult(execution());
+
+ executionFinishedEventPort.publishExecutionFinishedEvent(
+ new ExecutionFinishedEvent(task.getTaskID(), task.getResult(), "SUCCESS"));
+
+ System.out.println("Finish execution");
+ getAssignment();
+ }
+
+ protected abstract String execution();
+
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorStatus.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorStatus.java
new file mode 100644
index 0000000..8bd5bc3
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorStatus.java
@@ -0,0 +1,7 @@
+package ch.unisg.executorBase.executor.domain;
+
+public enum ExecutorStatus {
+ STARTING_UP,
+ EXECUTING,
+ IDLING,
+}
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorType.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorType.java
new file mode 100644
index 0000000..0b2b305
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorType.java
@@ -0,0 +1,18 @@
+package ch.unisg.executorBase.executor.domain;
+
+public enum ExecutorType {
+ ADDITION, ROBOT;
+
+ public static boolean contains(String test) {
+
+ for (ExecutorType x : ExecutorType.values()) {
+ if (x.name().equals(test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/Task.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/Task.java
new file mode 100644
index 0000000..fec330f
--- /dev/null
+++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/Task.java
@@ -0,0 +1,19 @@
+package ch.unisg.executorBase.executor.domain;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class Task {
+
+ @Getter
+ private String taskID;
+
+ @Getter
+ @Setter
+ private String result;
+
+ public Task(String taskID) {
+ this.taskID = taskID;
+ }
+
+}
diff --git a/executor-base/src/main/resources/application.properties b/executor-base/src/main/resources/application.properties
new file mode 100644
index 0000000..4d360de
--- /dev/null
+++ b/executor-base/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=8081
diff --git a/executor-base/src/test/java/ch/unisg/executorBase/Executor1ApplicationTests.java b/executor-base/src/test/java/ch/unisg/executorBase/Executor1ApplicationTests.java
new file mode 100644
index 0000000..6fec034
--- /dev/null
+++ b/executor-base/src/test/java/ch/unisg/executorBase/Executor1ApplicationTests.java
@@ -0,0 +1,13 @@
+package ch.unisg.executorBase;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class executorBaseApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/executor1/pom.xml b/executor1/pom.xml
index 1534025..8a5b9e3 100644
--- a/executor1/pom.xml
+++ b/executor1/pom.xml
@@ -40,6 +40,11 @@
spring-boot-starter-test
test
+
+ ch.unisg
+ executorBase
+ 0.0.1-SNAPSHOT
+
diff --git a/executor1/src/main/java/ch/unisg/executor1/Executor1Application.java b/executor1/src/main/java/ch/unisg/executor1/Executor1Application.java
index 4eed560..dfb8d8c 100644
--- a/executor1/src/main/java/ch/unisg/executor1/Executor1Application.java
+++ b/executor1/src/main/java/ch/unisg/executor1/Executor1Application.java
@@ -3,11 +3,14 @@ package ch.unisg.executor1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import ch.unisg.executor1.executor.domain.Executor;
+
@SpringBootApplication
public class Executor1Application {
public static void main(String[] args) {
SpringApplication.run(Executor1Application.class, args);
+ Executor.getExecutor();
}
}
diff --git a/executor1/src/main/java/ch/unisg/executor1/TestController.java b/executor1/src/main/java/ch/unisg/executor1/TestController.java
deleted file mode 100644
index a2f32b4..0000000
--- a/executor1/src/main/java/ch/unisg/executor1/TestController.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package ch.unisg.executor1;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class TestController {
- @RequestMapping("/")
- public String index() {
- return "Hello World! Executor1";
- }
-}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/adapter/in/web/TaskAvailableController.java b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/in/web/TaskAvailableController.java
new file mode 100644
index 0000000..5501885
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/in/web/TaskAvailableController.java
@@ -0,0 +1,36 @@
+package ch.unisg.executor1.executor.adapter.in.web;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+@RestController
+public class TaskAvailableController {
+ private final TaskAvailableUseCase taskAvailableUseCase;
+
+ public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
+ this.taskAvailableUseCase = taskAvailableUseCase;
+ }
+
+ @GetMapping(path = "/newtask/{taskType}")
+ public ResponseEntity retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
+
+ if (ExecutorType.contains(taskType.toUpperCase())) {
+ TaskAvailableCommand command = new TaskAvailableCommand(
+ ExecutorType.valueOf(taskType.toUpperCase()));
+ taskAvailableUseCase.newTaskAvailable(command);
+ }
+
+ // Add the content type as a response header
+ HttpHeaders responseHeaders = new HttpHeaders();
+
+ return new ResponseEntity<>("OK", responseHeaders, HttpStatus.OK);
+ }
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/DeleteUserFromRobotAdapter.java b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/DeleteUserFromRobotAdapter.java
new file mode 100644
index 0000000..94c2309
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/DeleteUserFromRobotAdapter.java
@@ -0,0 +1,42 @@
+package ch.unisg.executor1.executor.adapter.out;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executor1.executor.application.port.out.DeleteUserFromRobotPort;
+
+@Component
+@Primary
+public class DeleteUserFromRobotAdapter implements DeleteUserFromRobotPort {
+
+ @Override
+ public boolean deleteUserFromRobot(String key) {
+
+ String url = "https://api.interactions.ics.unisg.ch/leubot1/v1.3.0/user/" + key;
+
+ var request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .header("Content-Type", "application/json")
+ .DELETE()
+ .build();
+
+ var client = HttpClient.newHttpClient();
+
+ try {
+ var response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.statusCode());
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/InstructionToRobotAdapter.java b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/InstructionToRobotAdapter.java
new file mode 100644
index 0000000..f8b7012
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/InstructionToRobotAdapter.java
@@ -0,0 +1,46 @@
+package ch.unisg.executor1.executor.adapter.out;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executor1.executor.application.port.out.InstructionToRobotPort;
+
+@Component
+@Primary
+public class InstructionToRobotAdapter implements InstructionToRobotPort {
+
+ @Override
+ public boolean instructionToRobot(String key) {
+
+ String putEndpoint = "https://api.interactions.ics.unisg.ch/leubot1/v1.3.0/elbow";
+
+ String inputJson = "{ \"value\": 400}";
+ var request = HttpRequest.newBuilder()
+ .uri(URI.create(putEndpoint))
+ .header("Content-Type", "application/json")
+ .header("X-API-KEY", key)
+ .PUT(HttpRequest.BodyPublishers.ofString(inputJson))
+ .build();
+
+ var client = HttpClient.newHttpClient();
+
+ try {
+ var response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.statusCode());
+ System.out.println(response.headers());
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return false;
+
+ }
+
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/UserToRobotAdapter.java b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/UserToRobotAdapter.java
new file mode 100644
index 0000000..f874892
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/adapter/out/UserToRobotAdapter.java
@@ -0,0 +1,46 @@
+package ch.unisg.executor1.executor.adapter.out;
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executor1.executor.application.port.out.UserToRobotPort;
+
+@Component
+@Primary
+public class UserToRobotAdapter implements UserToRobotPort {
+
+ @Override
+ public String userToRobot() {
+ String postEndpoint = "https://api.interactions.ics.unisg.ch/leubot1/v1.3.0/user";
+
+ String inputJson = "{ \"name\":\"keanu rahimian\", \"email\":\"keanu.rahimian@student.unisg.ch\"}";
+ var request = HttpRequest.newBuilder()
+ .uri(URI.create(postEndpoint))
+ .header("Content-Type", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(inputJson))
+ .build();
+
+ var client = HttpClient.newHttpClient();
+
+ try {
+ var response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ System.out.println(response.statusCode());
+ System.out.println(response.headers());
+ String url = response.headers().map().get("location").toString();
+ String key = url.split("/")[url.split("/").length-1].split("]")[0];
+ return key;
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/DeleteUserFromRobotPort.java b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/DeleteUserFromRobotPort.java
new file mode 100644
index 0000000..fc6f5d7
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/DeleteUserFromRobotPort.java
@@ -0,0 +1,5 @@
+package ch.unisg.executor1.executor.application.port.out;
+
+public interface DeleteUserFromRobotPort {
+ boolean deleteUserFromRobot(String key);
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/InstructionToRobotPort.java b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/InstructionToRobotPort.java
new file mode 100644
index 0000000..bbf4034
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/InstructionToRobotPort.java
@@ -0,0 +1,5 @@
+package ch.unisg.executor1.executor.application.port.out;
+
+public interface InstructionToRobotPort {
+ boolean instructionToRobot(String key);
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/UserToRobotPort.java b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/UserToRobotPort.java
new file mode 100644
index 0000000..5b011c1
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/application/port/out/UserToRobotPort.java
@@ -0,0 +1,7 @@
+package ch.unisg.executor1.executor.application.port.out;
+
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+public interface UserToRobotPort {
+ String userToRobot();
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/application/service/TaskAvailableService.java b/executor1/src/main/java/ch/unisg/executor1/executor/application/service/TaskAvailableService.java
new file mode 100644
index 0000000..d502053
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/application/service/TaskAvailableService.java
@@ -0,0 +1,28 @@
+package ch.unisg.executor1.executor.application.service;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executor1.executor.domain.Executor;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import ch.unisg.executorBase.executor.domain.ExecutorStatus;
+import lombok.RequiredArgsConstructor;
+
+import javax.transaction.Transactional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class TaskAvailableService implements TaskAvailableUseCase {
+
+ @Override
+ public void newTaskAvailable(TaskAvailableCommand command) {
+
+ Executor executor = Executor.getExecutor();
+
+ if (executor.getExecutorType() == command.getTaskType() &&
+ executor.getStatus() == ExecutorStatus.IDLING) {
+ executor.getAssignment();
+ }
+ }
+}
diff --git a/executor1/src/main/java/ch/unisg/executor1/executor/domain/Executor.java b/executor1/src/main/java/ch/unisg/executor1/executor/domain/Executor.java
new file mode 100644
index 0000000..4a7734c
--- /dev/null
+++ b/executor1/src/main/java/ch/unisg/executor1/executor/domain/Executor.java
@@ -0,0 +1,42 @@
+package ch.unisg.executor1.executor.domain;
+
+import java.net.http.HttpClient;
+import java.net.http.HttpResponse;
+import java.util.concurrent.TimeUnit;
+
+import ch.unisg.executor1.executor.adapter.out.DeleteUserFromRobotAdapter;
+import ch.unisg.executor1.executor.adapter.out.InstructionToRobotAdapter;
+import ch.unisg.executor1.executor.adapter.out.UserToRobotAdapter;
+import ch.unisg.executor1.executor.application.port.out.DeleteUserFromRobotPort;
+import ch.unisg.executor1.executor.application.port.out.InstructionToRobotPort;
+import ch.unisg.executor1.executor.application.port.out.UserToRobotPort;
+import ch.unisg.executorBase.executor.domain.ExecutorBase;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+public class Executor extends ExecutorBase {
+
+ private static final Executor executor = new Executor(ExecutorType.ROBOT);
+ private final UserToRobotPort userToRobotPort = new UserToRobotAdapter();
+ private final InstructionToRobotPort instructionToRobotPort = new InstructionToRobotAdapter();
+ private final DeleteUserFromRobotPort deleteUserFromRobotPort = new DeleteUserFromRobotAdapter();
+
+ public static Executor getExecutor() {
+ return executor;
+ }
+
+ private Executor(ExecutorType executorType) {
+ super(executorType);
+ }
+
+ @Override
+ protected
+ String execution() {
+
+ String key = userToRobotPort.userToRobot();
+ boolean result1 = instructionToRobotPort.instructionToRobot(key);
+ deleteUserFromRobotPort.deleteUserFromRobot(key);
+
+ return Boolean.toString(result1);
+ }
+
+}
\ No newline at end of file
diff --git a/executor1/src/main/resources/application.properties b/executor1/src/main/resources/application.properties
index 4d360de..5e3bb81 100644
--- a/executor1/src/main/resources/application.properties
+++ b/executor1/src/main/resources/application.properties
@@ -1 +1 @@
-server.port=8081
+server.port=8084
diff --git a/executor1/src/test/java/ch/unisg/executor1/Executor1ApplicationTests.java b/executor1/src/test/java/ch/unisg/executor2/Executor2ApplicationTests.java
similarity index 70%
rename from executor1/src/test/java/ch/unisg/executor1/Executor1ApplicationTests.java
rename to executor1/src/test/java/ch/unisg/executor2/Executor2ApplicationTests.java
index 889c9cd..5724a1c 100644
--- a/executor1/src/test/java/ch/unisg/executor1/Executor1ApplicationTests.java
+++ b/executor1/src/test/java/ch/unisg/executor2/Executor2ApplicationTests.java
@@ -1,10 +1,10 @@
-package ch.unisg.executor1;
+package ch.unisg.executor2;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
-class Executor1ApplicationTests {
+class Executor2ApplicationTests {
@Test
void contextLoads() {
diff --git a/executor2/pom.xml b/executor2/pom.xml
index 681cebd..1f970e0 100644
--- a/executor2/pom.xml
+++ b/executor2/pom.xml
@@ -40,6 +40,18 @@
spring-boot-starter-test
test
+
+ ch.unisg
+ executorBase
+ 0.0.1-SNAPSHOT
+ compile
+
+
+
+ org.json
+ json
+ 20210307
+
diff --git a/executor2/src/main/java/ch/unisg/executor2/Executor2Application.java b/executor2/src/main/java/ch/unisg/executor2/Executor2Application.java
index d31e277..03edb3d 100644
--- a/executor2/src/main/java/ch/unisg/executor2/Executor2Application.java
+++ b/executor2/src/main/java/ch/unisg/executor2/Executor2Application.java
@@ -3,11 +3,14 @@ package ch.unisg.executor2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import ch.unisg.executor2.executor.domain.Executor;
+
@SpringBootApplication
public class Executor2Application {
public static void main(String[] args) {
SpringApplication.run(Executor2Application.class, args);
+ Executor.getExecutor();
}
}
diff --git a/executor2/src/main/java/ch/unisg/executor2/TestController.java b/executor2/src/main/java/ch/unisg/executor2/TestController.java
deleted file mode 100644
index c98bb02..0000000
--- a/executor2/src/main/java/ch/unisg/executor2/TestController.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package ch.unisg.executor2;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class TestController {
- @RequestMapping("/")
- public String index() {
- return "Hello World! Executor2";
- }
-}
diff --git a/executor2/src/main/java/ch/unisg/executor2/executor/adapter/in/web/TaskAvailableController.java b/executor2/src/main/java/ch/unisg/executor2/executor/adapter/in/web/TaskAvailableController.java
new file mode 100644
index 0000000..a14b58f
--- /dev/null
+++ b/executor2/src/main/java/ch/unisg/executor2/executor/adapter/in/web/TaskAvailableController.java
@@ -0,0 +1,35 @@
+package ch.unisg.executor2.executor.adapter.in.web;
+
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+@RestController
+public class TaskAvailableController {
+ private final TaskAvailableUseCase taskAvailableUseCase;
+
+ public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
+ this.taskAvailableUseCase = taskAvailableUseCase;
+ }
+
+ @GetMapping(path = "/newtask/{taskType}")
+ public ResponseEntity retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
+
+ if (ExecutorType.contains(taskType.toUpperCase())) {
+ TaskAvailableCommand command = new TaskAvailableCommand(
+ ExecutorType.valueOf(taskType.toUpperCase()));
+ CompletableFuture.runAsync(() -> taskAvailableUseCase.newTaskAvailable(command));
+ }
+
+ return new ResponseEntity<>("OK", new HttpHeaders(), HttpStatus.OK);
+ }
+}
diff --git a/executor2/src/main/java/ch/unisg/executor2/executor/application/service/TaskAvailableService.java b/executor2/src/main/java/ch/unisg/executor2/executor/application/service/TaskAvailableService.java
new file mode 100644
index 0000000..6fa918d
--- /dev/null
+++ b/executor2/src/main/java/ch/unisg/executor2/executor/application/service/TaskAvailableService.java
@@ -0,0 +1,28 @@
+package ch.unisg.executor2.executor.application.service;
+
+import org.springframework.stereotype.Component;
+
+import ch.unisg.executor2.executor.domain.Executor;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
+import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
+import ch.unisg.executorBase.executor.domain.ExecutorStatus;
+import lombok.RequiredArgsConstructor;
+
+import javax.transaction.Transactional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class TaskAvailableService implements TaskAvailableUseCase {
+
+ @Override
+ public void newTaskAvailable(TaskAvailableCommand command) {
+
+ Executor executor = Executor.getExecutor();
+
+ if (executor.getExecutorType() == command.getTaskType() &&
+ executor.getStatus() == ExecutorStatus.IDLING) {
+ executor.getAssignment();
+ }
+ }
+}
diff --git a/executor2/src/main/java/ch/unisg/executor2/executor/domain/Executor.java b/executor2/src/main/java/ch/unisg/executor2/executor/domain/Executor.java
new file mode 100644
index 0000000..6aa1656
--- /dev/null
+++ b/executor2/src/main/java/ch/unisg/executor2/executor/domain/Executor.java
@@ -0,0 +1,36 @@
+package ch.unisg.executor2.executor.domain;
+
+import java.util.concurrent.TimeUnit;
+import ch.unisg.executorBase.executor.domain.ExecutorBase;
+import ch.unisg.executorBase.executor.domain.ExecutorType;
+
+public class Executor extends ExecutorBase {
+
+ private static final Executor executor = new Executor(ExecutorType.ADDITION);
+
+ public static Executor getExecutor() {
+ return executor;
+ }
+
+ private Executor(ExecutorType executorType) {
+ super(executorType);
+ }
+
+ @Override
+ protected
+ String execution() {
+
+ int a = 20;
+ int b = 20;
+ try {
+ TimeUnit.SECONDS.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ int result = a + b;
+
+ return Integer.toString(result);
+ }
+
+}
diff --git a/executor2/src/main/resources/application.properties b/executor2/src/main/resources/application.properties
index 4d360de..cd2d02b 100644
--- a/executor2/src/main/resources/application.properties
+++ b/executor2/src/main/resources/application.properties
@@ -1 +1 @@
-server.port=8081
+server.port=8085
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
index 40fa5da..90d1716 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/TapasTasksApplication.java
@@ -10,8 +10,8 @@ public class TapasTasksApplication {
public static void main(String[] args) {
- SpringApplication tapasTasksApp = new SpringApplication(TapasTasksApplication.class);
- tapasTasksApp.run(args);
+ SpringApplication tapasTasksApp = new SpringApplication(TapasTasksApplication.class);
+ tapasTasksApp.run(args);
}
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/CompleteTaskWebController.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/CompleteTaskWebController.java
new file mode 100644
index 0000000..e160c2b
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/CompleteTaskWebController.java
@@ -0,0 +1,41 @@
+package ch.unisg.tapastasks.tasks.adapter.in.web;
+
+import ch.unisg.tapastasks.tasks.application.port.in.CompleteTaskCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.CompleteTaskUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+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 javax.validation.ConstraintViolationException;
+
+@RestController
+public class CompleteTaskWebController {
+ private final CompleteTaskUseCase completeTaskUseCase;
+
+ public CompleteTaskWebController(CompleteTaskUseCase completeTaskUseCase){
+ this.completeTaskUseCase = completeTaskUseCase;
+ }
+
+ @PostMapping(path = "/tasks/completeTask", consumes = {TaskMediaType.TASK_MEDIA_TYPE})
+ public ResponseEntity completeTask (@RequestBody Task task){
+ try {
+ CompleteTaskCommand command = new CompleteTaskCommand(
+ task.getTaskId(), task.getTaskResult()
+ );
+
+ Task updateATask = completeTaskUseCase.completeTask(command);
+
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.add(HttpHeaders.CONTENT_TYPE, TaskMediaType.TASK_MEDIA_TYPE);
+
+ return new ResponseEntity<>(TaskMediaType.serialize(updateATask), responseHeaders, HttpStatus.ACCEPTED);
+ } catch(ConstraintViolationException e){
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
+ }
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/DeleteTaskWebController.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/DeleteTaskWebController.java
new file mode 100644
index 0000000..af721d1
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/DeleteTaskWebController.java
@@ -0,0 +1,49 @@
+package ch.unisg.tapastasks.tasks.adapter.in.web;
+
+
+import ch.unisg.tapastasks.tasks.application.port.in.DeleteTaskCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.DeleteTaskUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+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 javax.validation.ConstraintViolationException;
+import java.util.Optional;
+
+@RestController
+public class DeleteTaskWebController {
+ private final DeleteTaskUseCase deleteClassUseCase;
+
+ public DeleteTaskWebController(DeleteTaskUseCase deleteClassUseCase){
+ this.deleteClassUseCase = deleteClassUseCase;
+ }
+
+ @PostMapping(path="/tasks/deleteTask", consumes = {TaskMediaType.TASK_MEDIA_TYPE})
+ public ResponseEntity deleteTask (@RequestBody Task task){
+ try {
+ DeleteTaskCommand command = new DeleteTaskCommand(task.getTaskId());
+
+ Optional deleteATask = deleteClassUseCase.deleteTask(command);
+
+ // Check if the task with the given identifier exists
+ if (deleteATask.isEmpty()) {
+
+ // If not, through a 404 Not Found status code
+ throw new ResponseStatusException(HttpStatus.NOT_FOUND);
+ }
+
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.add(HttpHeaders.CONTENT_TYPE, TaskMediaType.TASK_MEDIA_TYPE);
+
+
+ return new ResponseEntity<>(TaskMediaType.serialize(deleteATask.get()), responseHeaders, HttpStatus.ACCEPTED);
+ } catch(ConstraintViolationException e){
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
+ }
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskAssignedWebController.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskAssignedWebController.java
new file mode 100644
index 0000000..9dfa6a2
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskAssignedWebController.java
@@ -0,0 +1,42 @@
+package ch.unisg.tapastasks.tasks.adapter.in.web;
+
+import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+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 javax.validation.ConstraintViolationException;
+
+@RestController
+public class TaskAssignedWebController {
+ private final TaskAssignedUseCase taskAssignedUseCase;
+
+ public TaskAssignedWebController(TaskAssignedUseCase taskAssignedUseCase){
+ this.taskAssignedUseCase = taskAssignedUseCase;
+ }
+
+ @PostMapping(path="/tasks/assignTask", consumes= {TaskMediaType.TASK_MEDIA_TYPE})
+ public ResponseEntity assignTask(@RequestBody Task task){
+ try{
+ TaskAssignedCommand command = new TaskAssignedCommand(
+ task.getTaskId()
+ );
+
+ Task updateATask = taskAssignedUseCase.assignTask(command);
+
+ HttpHeaders responseHeaders = new HttpHeaders();
+ responseHeaders.add(HttpHeaders.CONTENT_TYPE, TaskMediaType.TASK_MEDIA_TYPE);
+
+ return new ResponseEntity<>(TaskMediaType.serialize(updateATask), responseHeaders, HttpStatus.ACCEPTED);
+ } catch (ConstraintViolationException e){
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
+ }
+ }
+
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskMediaType.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskMediaType.java
index 3c555e5..d9a0a46 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskMediaType.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/in/web/TaskMediaType.java
@@ -15,7 +15,7 @@ final public class TaskMediaType {
payload.put("taskType", task.getTaskType().getValue());
payload.put("taskState", task.getTaskState().getValue());
payload.put("taskListName", TaskList.getTapasTaskList().getTaskListName().getValue());
-
+ payload.put("taskResult", task.getTaskResult().getValue());
return payload.toString();
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/PublishNewTaskAddedEventWebAdapter.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/PublishNewTaskAddedEventWebAdapter.java
index db02f2a..7ae8e90 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/PublishNewTaskAddedEventWebAdapter.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/adapter/out/web/PublishNewTaskAddedEventWebAdapter.java
@@ -29,6 +29,8 @@ public class PublishNewTaskAddedEventWebAdapter implements NewTaskAddedEventPort
var values = new HashMap() {{
put("taskname",event.taskName);
put("tasklist",event.taskListName);
+ put("taskId", event.taskId);
+ put("taskType", event.taskType);
}};
var objectMapper = new ObjectMapper();
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskCommand.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskCommand.java
new file mode 100644
index 0000000..0634165
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskCommand.java
@@ -0,0 +1,23 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.common.SelfValidating;
+import ch.unisg.tapastasks.tasks.domain.Task.TaskId;
+import ch.unisg.tapastasks.tasks.domain.Task.TaskResult;
+import lombok.Value;
+
+import javax.validation.constraints.NotNull;
+
+@Value
+public class CompleteTaskCommand extends SelfValidating {
+ @NotNull
+ private final TaskId taskId;
+
+ @NotNull
+ private final TaskResult taskResult;
+
+ public CompleteTaskCommand(TaskId taskId, TaskResult taskResult){
+ this.taskId = taskId;
+ this.taskResult = taskResult;
+ this.validateSelf();
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskUseCase.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskUseCase.java
new file mode 100644
index 0000000..6fba7e6
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/CompleteTaskUseCase.java
@@ -0,0 +1,7 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+
+public interface CompleteTaskUseCase {
+ Task completeTask(CompleteTaskCommand command);
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskCommand.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskCommand.java
new file mode 100644
index 0000000..24acbb8
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskCommand.java
@@ -0,0 +1,18 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.common.SelfValidating;
+import ch.unisg.tapastasks.tasks.domain.Task.TaskId;
+import lombok.Value;
+
+import javax.validation.constraints.NotNull;
+
+@Value
+public class DeleteTaskCommand extends SelfValidating {
+ @NotNull
+ private final TaskId taskId;
+
+ public DeleteTaskCommand(TaskId taskId){
+ this.taskId=taskId;
+ this.validateSelf();
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskUseCase.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskUseCase.java
new file mode 100644
index 0000000..8ba206f
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/DeleteTaskUseCase.java
@@ -0,0 +1,9 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+
+import java.util.Optional;
+
+public interface DeleteTaskUseCase {
+ Optional deleteTask(DeleteTaskCommand command);
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedCommand.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedCommand.java
new file mode 100644
index 0000000..7a5e383
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedCommand.java
@@ -0,0 +1,18 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.common.SelfValidating;
+import ch.unisg.tapastasks.tasks.domain.Task.TaskId;
+import lombok.Value;
+
+import javax.validation.constraints.NotNull;
+
+@Value
+public class TaskAssignedCommand extends SelfValidating {
+ @NotNull
+ private final TaskId taskId;
+
+ public TaskAssignedCommand(TaskId taskId){
+ this.taskId=taskId;
+ this.validateSelf();
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedUseCase.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedUseCase.java
new file mode 100644
index 0000000..3a84587
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/port/in/TaskAssignedUseCase.java
@@ -0,0 +1,7 @@
+package ch.unisg.tapastasks.tasks.application.port.in;
+
+import ch.unisg.tapastasks.tasks.domain.Task;
+
+public interface TaskAssignedUseCase {
+ Task assignTask(TaskAssignedCommand command);
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
index 7e3320e..2aa360c 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/AddNewTaskToTaskListService.java
@@ -30,8 +30,6 @@ public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase
//the core and then the integration event in the application layer.
if (newTask != null) {
NewTaskAddedEvent newTaskAdded = new NewTaskAddedEvent(newTask.getTaskName().getValue(),
- taskList.getTaskListName().getValue());
-
newTaskAddedEventPort.publishNewTaskAddedEvent(newTaskAdded);
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/CompleteTaskService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/CompleteTaskService.java
new file mode 100644
index 0000000..bade832
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/CompleteTaskService.java
@@ -0,0 +1,35 @@
+package ch.unisg.tapastasks.tasks.application.service;
+
+import ch.unisg.tapastasks.tasks.application.port.in.CompleteTaskCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.CompleteTaskUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.Task.*;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import javax.transaction.Transactional;
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class CompleteTaskService implements CompleteTaskUseCase {
+
+ @Override
+ public Task completeTask(CompleteTaskCommand command){
+ // TODO Retrieve the task based on ID
+ TaskList taskList = TaskList.getTapasTaskList();
+ Optional updatedTask = taskList.retrieveTaskById(command.getTaskId());
+
+ // TODO Update the status and result (and save?)
+ Task newTask = updatedTask.get();
+ newTask.taskResult = new TaskResult(command.getTaskResult().getValue());
+ newTask.taskState = new TaskState(Task.State.EXECUTED);
+
+
+ // TODO return the updated task
+ return newTask;
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/DeleteTaskService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/DeleteTaskService.java
new file mode 100644
index 0000000..05d1da5
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/DeleteTaskService.java
@@ -0,0 +1,25 @@
+package ch.unisg.tapastasks.tasks.application.service;
+
+
+import ch.unisg.tapastasks.tasks.application.port.in.DeleteTaskCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.DeleteTaskUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import javax.transaction.Transactional;
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class DeleteTaskService implements DeleteTaskUseCase {
+
+ @Override
+ public Optional deleteTask(DeleteTaskCommand command){
+ TaskList taskList = TaskList.getTapasTaskList();
+ return taskList.deleteTaskById(command.getTaskId());
+
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/TaskAssignedService.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/TaskAssignedService.java
new file mode 100644
index 0000000..baa6059
--- /dev/null
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/application/service/TaskAssignedService.java
@@ -0,0 +1,31 @@
+package ch.unisg.tapastasks.tasks.application.service;
+
+import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedCommand;
+import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedUseCase;
+import ch.unisg.tapastasks.tasks.domain.Task;
+import ch.unisg.tapastasks.tasks.domain.Task.*;
+import ch.unisg.tapastasks.tasks.domain.TaskList;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import javax.transaction.Transactional;
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@Component
+@Transactional
+public class TaskAssignedService implements TaskAssignedUseCase {
+
+ @Override
+ public Task assignTask(TaskAssignedCommand command) {
+ // retrieve the task based on ID
+ TaskList taskList = TaskList.getTapasTaskList();
+ Optional task = taskList.retrieveTaskById(command.getTaskId());
+
+ // update the status to assigned
+ Task updatedTask = task.get();
+ updatedTask.taskState = new TaskState(State.ASSIGNED);
+
+ return updatedTask;
+ }
+}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/NewTaskAddedEvent.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/NewTaskAddedEvent.java
index 32f5966..a4703f2 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/NewTaskAddedEvent.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/NewTaskAddedEvent.java
@@ -4,9 +4,14 @@ package ch.unisg.tapastasks.tasks.domain;
public class NewTaskAddedEvent {
public String taskName;
public String taskListName;
+ public String taskId;
+ public String taskType;
- public NewTaskAddedEvent(String taskName, String taskListName) {
+ public NewTaskAddedEvent(String taskName, String taskListName, String taskId, String taskType) {
this.taskName = taskName;
this.taskListName = taskListName;
+ this.taskId = taskId;
+ this.taskType = taskType;
}
+
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
index 0dcafc3..3decd1f 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/Task.java
@@ -22,13 +22,18 @@ public class Task {
private final TaskType taskType;
@Getter
- private TaskState taskState;
+ public TaskState taskState; // had to make public for CompleteTaskService
+
+ @Getter
+ public TaskResult taskResult; // same as above
+
public Task(TaskName taskName, TaskType taskType) {
this.taskName = taskName;
this.taskType = taskType;
this.taskState = new TaskState(State.OPEN);
this.taskId = new TaskId(UUID.randomUUID().toString());
+ this.taskResult = new TaskResult("");
}
protected static Task createTaskWithNameAndType(TaskName name, TaskType type) {
@@ -56,4 +61,9 @@ public class Task {
public static class TaskType {
private String value;
}
+
+ @Value
+ public static class TaskResult{
+ private String value;
+ }
}
diff --git a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/TaskList.java b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/TaskList.java
index 2b90da5..ccdc59a 100644
--- a/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/TaskList.java
+++ b/tapas-tasks/src/main/java/ch/unisg/tapastasks/tasks/domain/TaskList.java
@@ -20,8 +20,7 @@ public class TaskList {
//Note: We do not care about the management of task lists, there is only one within this service
//--> using the Singleton pattern here to make lives easy; we will later load it from a repo
- //TODO change "tutors" to your group name ("groupx")
- private static final TaskList taskList = new TaskList(new TaskListName("tapas-tasks-tutors"));
+ private static final TaskList taskList = new TaskList(new TaskListName("tapas-tasks-group1"));
private TaskList(TaskListName taskListName) {
this.taskListName = taskListName;
@@ -55,6 +54,17 @@ public class TaskList {
return Optional.empty();
}
+ public Optional deleteTaskById(Task.TaskId id) {
+ for (Task task: listOfTasks.value){
+ if(task.getTaskId().getValue().equalsIgnoreCase(id.getValue())){
+ listOfTasks.value.remove(task);
+ return Optional.of(task);
+ }
+ }
+
+ return Optional.empty();
+ }
+
@Value
public static class TaskListName {
private String value;