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..e83e74d --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/ApplyForTaskController.java @@ -0,0 +1,36 @@ +package ch.unisg.assignment.assignment.adapter.in.web; + +import javax.validation.ConstraintViolationException; + +import org.springframework.http.HttpStatus; +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 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) { + try { + ApplyForTaskCommand command = new ApplyForTaskCommand(executorInfo.getExecutorType(), + executorInfo.getIp(), executorInfo.getPort()); + + return applyForTaskUseCase.applyForTask(command); + + } catch (ConstraintViolationException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } +} 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..d95fdd5 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/in/web/NewTaskController.java @@ -0,0 +1,41 @@ +package ch.unisg.assignment.assignment.adapter.in.web; + +import javax.validation.ConstraintViolationException; + +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 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 addNewTaskTaskToTaskList(@RequestBody Task task) { + try { + 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); + } catch (ConstraintViolationException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } +} 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/out/web/PublishNewTaskEventAdapter.java b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishNewTaskEventAdapter.java new file mode 100644 index 0000000..c08651b --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishNewTaskEventAdapter.java @@ -0,0 +1,40 @@ +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 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.NewTaskEvent; + +@Component +@Primary +public class PublishNewTaskEventAdapter implements NewTaskEventPort { + + String server = "http://127.0.0.1:8085"; + + @Override + public void publishNewTaskEvent(NewTaskEvent event) { + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(server + "/newtask/" + event.taskType)) + .GET() + .build(); + + + try { + client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + // 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..7f38d60 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskAssignedEventAdapter.java @@ -0,0 +1,46 @@ +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 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.TaskAssignedEvent; + +@Component +@Primary +public class PublishTaskAssignedEventAdapter implements TaskAssignedEventPort { + + String server = "http://127.0.0.1:8085"; + + @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) { + e.printStackTrace(); + // 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..d63d710 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/adapter/out/web/PublishTaskCompletedEventAdapter.java @@ -0,0 +1,49 @@ +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 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.TaskCompletedEvent; + +@Component +@Primary +public class PublishTaskCompletedEventAdapter implements TaskCompletedEventPort { + + String server = "http://127.0.0.1:8081"; + + @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) { + e.printStackTrace(); + // 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..76d0e41 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/ApplyForTaskCommand.java @@ -0,0 +1,29 @@ +package ch.unisg.assignment.assignment.application.port.in; + +import javax.validation.constraints.NotNull; + +import ch.unisg.assignment.common.SelfValidating; +import lombok.EqualsAndHashCode; +import lombok.Value; + +@Value +@EqualsAndHashCode(callSuper=false) +public class ApplyForTaskCommand extends SelfValidating{ + + @NotNull + private final String taskType; + + @NotNull + private final String executorIP; + + + @NotNull + private final int executorPort; + + public ApplyForTaskCommand(String taskType, String executorIP, int 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..98f4579 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/NewTaskCommand.java @@ -0,0 +1,23 @@ +package ch.unisg.assignment.assignment.application.port.in; + +import javax.validation.constraints.NotNull; + +import ch.unisg.assignment.common.SelfValidating; + +import lombok.Value; + +@Value +public class NewTaskCommand extends SelfValidating { + + @NotNull + private final String taskID; + + @NotNull + private final String taskType; + + public NewTaskCommand(String taskID, String 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..474456d --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/application/port/in/TaskCompletedCommand.java @@ -0,0 +1,33 @@ +package ch.unisg.assignment.assignment.application.port.in; + +import javax.validation.constraints.NotNull; + +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 String taskType; + + @NotNull + private final String taskStatus; + + @NotNull + private final String taskResult; + + public TaskCompletedCommand(String taskID, String 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..521af1a --- /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.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..0cd1cae --- /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.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..37b4ff3 --- /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.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..dac9a70 --- /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.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..b662cd2 --- /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.NewTaskEvent; +import ch.unisg.assignment.assignment.domain.Roster; +import ch.unisg.assignment.assignment.domain.Task; +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())) { + 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..1295336 --- /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.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..3b7432c --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/ExecutorInfo.java @@ -0,0 +1,18 @@ +package ch.unisg.assignment.assignment.domain; + +import lombok.Getter; +import lombok.Setter; + +public class ExecutorInfo { + @Getter + @Setter + private String ip; + + @Getter + @Setter + private int port; + + @Getter + @Setter + private String executorType; +} diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/NewTaskEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/NewTaskEvent.java new file mode 100644 index 0000000..57084ee --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/NewTaskEvent.java @@ -0,0 +1,9 @@ +package ch.unisg.assignment.assignment.domain; + +public class NewTaskEvent { + public String taskType; + + public NewTaskEvent(String taskType) { + this.taskType = taskType; + } +} 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..054c094 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Roster.java @@ -0,0 +1,49 @@ +package ch.unisg.assignment.assignment.domain; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +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().toUpperCase())) { + queues.get(task.getTaskType().toUpperCase()).add(task); + } else { + queues.put(task.getTaskType().toUpperCase(), new ArrayList<>(Arrays.asList(task))); + } + } + + public Task assignTaskToExecutor(String taskType, String executorIP, int executorPort) { + if (!queues.containsKey(taskType.toUpperCase())) { + return null; + } + if (queues.get(taskType.toUpperCase()).isEmpty()) { + return null; + } + + Task task = queues.get(taskType.toUpperCase()).remove(0); + + rosterMap.put(task.getTaskID(), new RosterItem(task.getTaskID(), task.getTaskType(), + 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..6e050d4 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/RosterItem.java @@ -0,0 +1,27 @@ +package ch.unisg.assignment.assignment.domain; + +import lombok.Getter; + +public class RosterItem { + + @Getter + private String taskID; + + @Getter + private String taskType; + + @Getter + private String executorIP; + + @Getter + private int executorPort; + + + public RosterItem(String taskID, String taskType, String executorIP, int 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..6db5111 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/Task.java @@ -0,0 +1,27 @@ +package ch.unisg.assignment.assignment.domain; + +import lombok.Getter; +import lombok.Setter; + +public class Task { + + @Getter + private String taskID; + + @Getter + private String taskType; + + @Getter + @Setter + private String result; + + @Getter + @Setter + private String status; + + public Task(String taskID, String taskType) { + this.taskID = taskID; + this.taskType = taskType; + } + +} diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskAssignedEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskAssignedEvent.java new file mode 100644 index 0000000..8acd144 --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskAssignedEvent.java @@ -0,0 +1,9 @@ +package ch.unisg.assignment.assignment.domain; + +public class TaskAssignedEvent { + public String taskID; + + public TaskAssignedEvent(String taskID) { + this.taskID = taskID; + } +} diff --git a/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskCompletedEvent.java b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskCompletedEvent.java new file mode 100644 index 0000000..89f26ae --- /dev/null +++ b/assignment/src/main/java/ch/unisg/assignment/assignment/domain/TaskCompletedEvent.java @@ -0,0 +1,15 @@ +package ch.unisg.assignment.assignment.domain; + +public class TaskCompletedEvent { + public String taskID; + + public String status; + + public 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/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/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/pom.xml b/executor-base/pom.xml index 6ad675a..32ec88a 100644 --- a/executor-base/pom.xml +++ b/executor-base/pom.xml @@ -56,6 +56,12 @@ javax.transaction-api 1.2 + + + org.json + json + 20210307 + 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 index 1d4a80a..5119ac5 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/common/SelfValidating.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/common/SelfValidating.java @@ -21,6 +21,7 @@ public class SelfValidating { * 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 index 75d3a02..182f2ba 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/in/web/TaskAvailableController.java @@ -21,7 +21,7 @@ public class TaskAvailableController { @GetMapping(path = "/newtask/{taskType}") public ResponseEntity retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) { - + if (ExecutorType.contains(taskType.toUpperCase())) { TaskAvailableCommand command = new TaskAvailableCommand( ExecutorType.valueOf(taskType.toUpperCase())); diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java index 2f53a2b..e688868 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/ExecutionFinishedEventAdapter.java @@ -8,6 +8,9 @@ import java.net.http.HttpResponse; import java.util.HashMap; import com.fasterxml.jackson.databind.ObjectMapper; + +import org.json.JSONObject; + import com.fasterxml.jackson.core.JsonProcessingException; import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEventPort; @@ -20,39 +23,30 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort @Override public void publishExecutionFinishedEvent(ExecutionFinishedEvent event) { - ///Here we would need to work with DTOs in case the payload of calls becomes more complex - var values = new HashMap() {{ - put("result",event.getResult()); - put("status",event.getStatus()); - }}; - - var objectMapper = new ObjectMapper(); - String requestBody = null; - try { - requestBody = objectMapper.writeValueAsString(values); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } + 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())) - .PUT(HttpRequest.BodyPublishers.ofString(requestBody)) + .header("Content-Type", "application/json") + .PUT(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) { + client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch (IOException | InterruptedException e) { e.printStackTrace(); + // 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 index b3e7875..b558075 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/adapter/out/web/GetAssignmentAdapter.java @@ -1,8 +1,10 @@ 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 org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @@ -11,6 +13,8 @@ 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 { @@ -19,27 +23,36 @@ public class GetAssignmentAdapter implements GetAssignmentPort { String server = "http://127.0.0.1:8082"; @Override - public Task getAssignment(ExecutorType executorType) { - + 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+"/assignment/" + executorType)) - .GET() + .uri(URI.create(server+"/task/apply")) + .header("Content-Type", "application/json") + .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) { + if (response.body().equals("")) { + return null; + } + + return new Task(new JSONObject(response.body()).getString("taskID")); + + } catch (IOException | InterruptedException e) { e.printStackTrace(); + // Restore interrupted state... + Thread.currentThread().interrupt(); } - **/ - // TODO return null or a new Task here depending on the response of the http call - - return new Task("123"); + return null; } - + } 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 index 916c8eb..cfa32bb 100644 --- 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 @@ -9,7 +9,7 @@ import lombok.Value; @Value public class TaskAvailableCommand extends SelfValidating { - + @NotNull private final ExecutorType taskType; 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 index 1f205b8..79d3a0a 100644 --- 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 @@ -4,5 +4,5 @@ import ch.unisg.executorBase.executor.domain.ExecutorType; import ch.unisg.executorBase.executor.domain.Task; public interface GetAssignmentPort { - Task getAssignment(ExecutorType executorType); + Task getAssignment(ExecutorType executorType, String ip, int port); } diff --git a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java index e639cb3..c9df1a8 100644 --- a/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java +++ b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/ExecutorBase.java @@ -4,13 +4,6 @@ import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEven import ch.unisg.executorBase.executor.application.port.out.GetAssignmentPort; import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort; -import java.util.concurrent.TimeUnit; - -import javax.transaction.Transactional; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Configurable; - 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; @@ -44,23 +37,19 @@ public abstract class ExecutorBase { 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 { - System.out.println(true); this.status = ExecutorStatus.IDLING; getAssignment(); } } - // public static ExecutorBase getExecutor() { - // return executor; - // } - public void getAssignment() { - Task newTask = getAssignmentPort.getAssignment(this.getExecutorType()); + Task newTask = getAssignmentPort.getAssignment(this.getExecutorType(), this.getIp(), + this.getPort()); if (newTask != null) { this.executeTask(newTask); } else { @@ -71,15 +60,16 @@ public abstract class ExecutorBase { 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")); + 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/Task.java b/executor-base/src/main/java/ch/unisg/executorBase/executor/domain/Task.java index 6719613..fec330f 100644 --- 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 @@ -1,11 +1,10 @@ package ch.unisg.executorBase.executor.domain; -import lombok.Data; import lombok.Getter; import lombok.Setter; public class Task { - + @Getter private String taskID; 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..db1b4b5 --- /dev/null +++ b/executor2/src/main/java/ch/unisg/executor2/executor/adapter/in/web/TaskAvailableController.java @@ -0,0 +1,36 @@ +package ch.unisg.executor2.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/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 index 4484ebb..6fa918d 100644 --- 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 @@ -20,7 +20,7 @@ public class TaskAvailableService implements TaskAvailableUseCase { Executor executor = Executor.getExecutor(); - if (executor.getExecutorType() == command.getTaskType() && + 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 index bb9308b..6aa1656 100644 --- a/executor2/src/main/java/ch/unisg/executor2/executor/domain/Executor.java +++ b/executor2/src/main/java/ch/unisg/executor2/executor/domain/Executor.java @@ -5,7 +5,7 @@ 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() { @@ -19,7 +19,7 @@ public class Executor extends ExecutorBase { @Override protected String execution() { - + int a = 20; int b = 20; try { 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