Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
reynisson 2021-12-22 21:17:06 +00:00
commit e519baf0f9
94 changed files with 789 additions and 1282 deletions

View File

@ -90,9 +90,7 @@ services:
environment:
task.list.uri: http://tapas-tasks:8081
auction.house.uri: http://tapas-auction-house:8086
executor.robot.uri: http://executor-robot:8084
executor.computation.uri: http://executor-computation:8085
executor.humidity.uri: http://executor-humidity:8087
executor.pool.uri: http://executor-pool:8083
mqtt.broker.uri: tcp://broker.hivemq.com:1883
spring.data.mongodb.uri: mongodb://root:password@tapas-db:27017
labels:
@ -135,13 +133,15 @@ services:
volumes:
- ./:/data/
environment:
EXECUTOR_POOL_URI: http://executor-pool:8083
ROSTER_URI: http://roster:8082
executor.type: COMPUTATION
executor.uri: http://executor-computation:8085
executor.pool.uri: http://executor-pool:8083
roster.uri: http://roster:8082
labels:
- "traefik.enable=true"
- "traefik.http.routers.executor-computation.rule=Host(`executor-computation.${PUB_IP}.nip.io`)"
- "traefik.http.routers.executor-computation.service=executor-computation"
- "traefik.http.services.executor-computation.loadbalancer.server.port=8084"
- "traefik.http.services.executor-computation.loadbalancer.server.port=8085"
- "traefik.http.routers.executor-computation.tls=true"
- "traefik.http.routers.executor-computation.entryPoints=web,websecure"
- "traefik.http.routers.executor-computation.tls.certresolver=le"
@ -156,13 +156,15 @@ services:
volumes:
- ./:/data/
environment:
EXECUTOR_POOL_URI: http://executor-pool:8083
ROSTER_URI: http://roster:8082
executor.type: SMALLROBOT
executor.uri: http://executor-robot:8084
executor.pool.uri: http://executor-pool:8083
roster.uri: http://roster:8082
labels:
- "traefik.enable=true"
- "traefik.http.routers.executor-robot.rule=Host(`executor-robot.${PUB_IP}.nip.io`)"
- "traefik.http.routers.executor-robot.service=executor-robot"
- "traefik.http.services.executor-robot.loadbalancer.server.port=8085"
- "traefik.http.services.executor-robot.loadbalancer.server.port=8084"
- "traefik.http.routers.executor-robot.tls=true"
- "traefik.http.routers.executor-robot.entryPoints=web,websecure"
- "traefik.http.routers.executor-robot.tls.certresolver=le"
@ -177,8 +179,10 @@ services:
volumes:
- ./:/data/
environment:
EXECUTOR_POOL_URI: http://executor-pool:8083
ROSTER_URI: http://roster:8082
executor.type: HUMIDITY
executor.uri: http://executor-humidity:8087
executor.pool.uri: http://executor-pool:8083
roster.uri: http://roster:8082
labels:
- "traefik.enable=true"
- "traefik.http.routers.executor-computation.rule=Host(`executor-humidity.${PUB_IP}.nip.io`)"

View File

@ -1,3 +0,0 @@
# Californium3 CoAP Properties file
# Wed Dec 15 21:41:00 CET 2021
#

145
README.md
View File

@ -1,125 +1,63 @@
# TAPAS
This is the main GitHub project for your implementation of the TAPAS application.
## Run application in developent
We use Docker & docker-compose in development to easly start all the microservices and other needed application (db's, message-broker's) at once. All microservices have hot-reloads enabled by default!
#### Start
```
docker-compose up
```
#### Rebuild container
```
docker-compose up --build
```
#### Start detached
```
docker-compose up -d
```
#### Stop detached
```
docker-compose down
```
## Available Services
Ports and debug ports of each service are listed below:
| Name | Port | Debug Port |
| ------------------ | ---- | ---------- |
| Tasklist | 8081 | 5005 |
| Assignment Service | 8082 | 5006 |
| Executor Pool | 8083 | 5007 |
| Executor 1 | 8084 | 5008 |
| Executor 2 | 8085 | 5009 |
This is the main GitHub project for the implementation of the TAPAS application from Group 1.
## Project Structure
This project is structured as follows:
- [tapas-tasks](tapas-tasks): standalone project for the Tapas-Tasks micro-service (Spring Boot project)
- [tapas-tasks/src](tapas-tasks/src): source code of the project (following the Hexagonal Architecture)
- [tapas-tasks/pom.xml](tapas-tasks\pom.xml): Maven pom-file
- [app](app): folder as placeholder for a second micro-service (Spring Boot project)
- [docker-compose.yml](docker-compose.yml): Docker Compose configuration file for all services
- [.github/workflows/build-and-deploy.yml](.github/workflows/build-and-deploy.yml): GitHub actions script (CI/CD workflow)
- [.deployment/docker-compose.yml](.deployment/docker-compose.yml): Docker Compose configuration file for all services (deployment)
- [.experiments](.experiments): Experiment files for chaos monkey tests
- [.github/workflows](.github/workflows): GitHub actions scripts (CI/CD workflow)
- [common](common): common library for shared elements across the whole application
- [common/src](common/src): source code of the library
- [common/pom.xml](common/pom.xml): Maven pom-file
- [doc/architecture/decisions](doc/architecture/decisions): ADRs
- [doc/workflow.png](doc/workflow.png): Workflow diagram
- [executor-base](executor-base): library for the executors. Includes the logic for executors to connect to the system
- [executor-base/src](executor-base/src): source code of the library
- [executor-base/pom.xml](executor-base/pom.xml): Maven pom-file
- [executor-computation](executor-computation): standalone project for the computation executor micro-service (Spring Boot project)
- [executor-computation/src](executor-computation/src): source code of the project
- [executor-computation/pom.xml](executor-computation/pom.xml): Maven pom-file
- [executor-humidity](executor-humidity): standalone project for the humidity executor micro-service (Spring Boot project)
- [executor-humidity/src](executor-humidity/src): source code of the project
- [executor-humidity/pom.xml](executor-humidity/pom.xml): Maven pom-file
- [executor-pool](executor-pool): standalone project for the executor-pool micro-service (Spring Boot project)
- [executor-pool/src](executor-pool/src): source code of the project (following the Hexagonal Architecture)
- [executor-pool/pom.xml](executor-pool/pom.xml): Maven pom-file
- [executor-robot](executor-robot): standalone project for the robot executor micro-service (Spring Boot project)
## How to Add a New Service with Spring Boot
- [executor-robot/src](executor-robot/src): source code of the project
- [executor-robot/pom.xml](executor-robot/pom.xml): Maven pom-file
### Create a new Spring Boot project
- [mocks](mocks): some auction-house mock files to test localy
- Recommended: use [Spring Initialzr](https://start.spring.io/) (Maven, Spring Boot 2.5.5, Jar, Java 11, dependencies as needed)
- Set the Spring application properties for your service (e.g., port of the web server) in `src/resources/application.properties`
- [roster](roster): standalone project for the Roster micro-service (Spring Boot project)
### Update the Docker Compose file
- [roster/src](roster/src): source code of the project (following the Hexagonal Architecture)
- [roster/pom.xml](roster/pom.xml): Maven pom-file
Your TAPAS application is a multi-container Docker application ran with [Docker Compose](https://docs.docker.com/compose/).
To add your newly created service to the Docker Compose configuration file, you need to create a new service
definition in [docker-compose.yml](docker-compose.yml):
- [tapas-auction-house](tapas-auction-house): standalone project for the Tapas-Aution-House micro-service (Spring Boot project)
- copy and edit the `tapas-tasks` service definition from [lines 29-42](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/docker-compose.yml#L29-L42)
- change `command` (see [line 31](https://github.com/scs-asse/tapas/blob/main/docker-compose.yml#L31))
to use the name of the JAR file generated by Maven for your service
- note: if you change the version of your service, you need to update this line to reflect the change
- update the Traefik label names to reflect the name of your new service (see [lines 37-42](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/docker-compose.yml#L37-L42))
- e.g., change `traefik.http.routers.tapas-tasks.rule` to `traefik.http.routers.<new-service-name>.rule`
- update the Traefik `rule` (see [line 37](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/docker-compose.yml#L37)) with the name of your new service: `` Host(`<new-service-name>.${PUB_IP}.nip.io`) ``
- update the Traefik `port` (see [line 39](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/docker-compose.yml#L39)) with the port configured for your new service
- [tapas-auction-house/src](tapas-auction-house/src): source code of the project (following the Hexagonal Architecture)
- [tapas-auction-house/pom.xml](tapas-auction-house/pom.xml): Maven pom-file
### Update the GitHub Actions Workflow
- [tapas-tasks](tapas-tasks): standalone project for the Tapas-Tasks micro-service (Spring Boot project)
This project uses GitHub Actions to build and deploy your TAPAS application whenever a new commit is
pushed on the `main` branch. You can add your new service to the GitHub Actions workflow defined in
[.github/workflows/build-and-deploy.yml](.github/workflows/build-and-deploy.yml):
- [tapas-tasks/src](tapas-tasks/src): source code of the project (following the Hexagonal Architecture)
- [tapas-tasks/pom.xml](tapas-tasks/pom.xml): Maven pom-file
- copy and edit the definition for `tapas-tasks` from [line 28-30](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/.github/workflows/build-and-deploy.yml#L28-L30)
- update the `mvn` command used to build your service to point to the `pom.xml` file of your new service (see [line 29](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/.github/workflows/build-and-deploy.yml#L29))
- update the `cp` command to point to the JAR file of your new service directive (see [line 30](https://github.com/scs-asse/tapas/blob/424a5f5aa2d6524acfe95d93000571884ed9d66f/.github/workflows/build-and-deploy.yml#L30))
- note you will need to update the complete file path (folder structure and JAR name)
### How to Run Your Service Locally
You can run and test your micro-service on your local machine just like a regular Maven project:
- Run from IntelliJ:
- Reload _pom.xml_ if necessary
- Run the micro-service's main class from IntelliJ for all required projects
- Use Maven to run from the command line:
```shell
mvn spring-boot:run
```
## How to Deploy on your VM
1. Start your Ubuntu VM on Switch.
- VM shuts down automatically at 2 AM
- Group admins can do this via https://engines.switch.ch/horizon
2. Push new code to the _main_ branch
- Check the status of the workflow on the _Actions_ page of the GitHub project
- We recommend to test your project locally before pushing the code to GitHub. The GitHub Organizations
used in the course are on a free tier plan, which comes with [various limits](https://github.com/pricing).
3. Open in your browser `https://app.<server-ip>.nip.io`
For the server IP address (see below), you should use dashes instead of dots, e.g.: `127.0.0.1` becomes `127-0-0-1`.
- [docker-compose.yml](docker-compose.yml): Docker Compose configuration file for local development
## VM Configurations
Specs (we can upgrade if needed):
- 1 CPU
- 2 GB RAM
- 20 GB HD
- Ubuntu 20.04
- 1 CPU
- 2 GB RAM
- 20 GB HD
- Ubuntu 20.04
| Name | Server IP |
| ------------------ | ------------- |
@ -128,8 +66,3 @@ Specs (we can upgrade if needed):
| SCS-ASSE-VM-Group3 | 86.119.34.242 |
| SCS-ASSE-VM-Group4 | 86.119.35.199 |
| SCS-ASSE-VM-Group5 | 86.119.35.72 |
## Architecture Decision Records
We recommend you to use [adr-tools](https://github.com/npryce/adr-tools) to manage your ADRs here in
this GitHub project in a dedicated folder. The tool works best on a Mac OS or Linux machine.

View File

@ -1,13 +0,0 @@
# Dockerfile/Docker-Compose file based on an initial version authored by Alexander Lontke (ASSE, Fall Semester 2021)
FROM maven as build
COPY . /app
RUN mvn -f app/pom.xml --batch-mode --update-snapshots verify
FROM openjdk
COPY --from=build /app/target/app-0.1.0.jar ./app-0.1.0.jar
CMD java -jar app-0.1.0.jar

View File

@ -1,21 +0,0 @@
package ch.unisg.executorbase.executor.application.port.in;
import javax.validation.constraints.NotNull;
import ch.unisg.common.validation.SelfValidating;
import ch.unisg.executorbase.executor.domain.ExecutorType;
import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(callSuper=false)
public class TaskAvailableCommand extends SelfValidating<TaskAvailableCommand> {
@NotNull
private final ExecutorType taskType;
public TaskAvailableCommand(ExecutorType taskType) {
this.taskType = taskType;
this.validateSelf();
}
}

View File

@ -1,5 +0,0 @@
package ch.unisg.executorbase.executor.application.port.in;
public interface TaskAvailableUseCase {
void newTaskAvailable(TaskAvailableCommand command);
}

View File

@ -1,7 +0,0 @@
package ch.unisg.executorbase.executor.application.port.out;
import ch.unisg.executorbase.executor.domain.ExecutionFinishedEvent;
public interface ExecutionFinishedEventPort {
void publishExecutionFinishedEvent(ExecutionFinishedEvent event);
}

View File

@ -1,9 +0,0 @@
package ch.unisg.executorbase.executor.application.port.out;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.executorbase.executor.domain.ExecutorType;
import ch.unisg.executorbase.executor.domain.Task;
public interface GetAssignmentPort {
Task getAssignment(ExecutorType executorType, ExecutorURI executorURI);
}

View File

@ -1,8 +0,0 @@
package ch.unisg.executorbase.executor.application.port.out;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.executorbase.executor.domain.ExecutorType;
public interface NotifyExecutorPoolPort {
boolean notifyExecutorPool(ExecutorURI executorURI, ExecutorType executorType);
}

View File

@ -1,16 +0,0 @@
package ch.unisg.executorbase.executor.application.service;
import ch.unisg.common.valueobject.ExecutorURI;
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(ExecutorURI executorURI, ExecutorType executorType) {
return notifyExecutorPoolPort.notifyExecutorPool(executorURI, executorType);
}
}

View File

@ -1,20 +0,0 @@
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, implementation of this service is inside the individual executors
}
}

View File

@ -1,21 +0,0 @@
package ch.unisg.executorbase.executor.domain;
import lombok.Getter;
public class ExecutionFinishedEvent {
@Getter
private String taskID;
@Getter
private String outputData;
@Getter
private String status;
public ExecutionFinishedEvent(String taskID, String outputData, String status) {
this.taskID = taskID;
this.outputData = outputData;
this.status = status;
}
}

View File

@ -1,93 +0,0 @@
package ch.unisg.executorbase.executor.domain;
import java.util.logging.Level;
import java.util.logging.Logger;
import ch.unisg.common.valueobject.ExecutorURI;
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.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.application.service.NotifyExecutorPoolService;
import lombok.Getter;
public abstract class ExecutorBase {
@Getter
private ExecutorURI executorURI;
@Getter
private ExecutorType executorType;
@Getter
private ExecutorStatus status;
// TODO Violation of the Dependency Inversion Principle?,
// TODO do this with only services
private final NotifyExecutorPoolPort notifyExecutorPoolPort = new NotifyExecutorPoolAdapter();
private final NotifyExecutorPoolService notifyExecutorPoolService = new NotifyExecutorPoolService(notifyExecutorPoolPort);
private final GetAssignmentPort getAssignmentPort = new GetAssignmentAdapter();
private final ExecutionFinishedEventPort executionFinishedEventPort = new ExecutionFinishedEventAdapter();
Logger logger = Logger.getLogger(ExecutorBase.class.getName());
protected ExecutorBase(ExecutorType executorType, String uri) {
logger.info("ExecutorBase | Starting Executor");
this.status = ExecutorStatus.STARTING_UP;
this.executorType = executorType;
this.executorURI = new ExecutorURI(uri);
// TODO do this in main
// Notify executor-pool about existence. If executor-pools response is successfull start with getting an assignment, else shut down executor.
logger.info("ExecutorBase | Notifying executor-pool about existens");
if(!notifyExecutorPoolService.notifyExecutorPool(this.executorURI, this.executorType)) {
logger.log(Level.WARNING, "ExecutorBase | Executor could not connect to executor pool! Shuting down!");
System.exit(0);
} else {
logger.info("ExecutorBase | Executor conntected to executor pool");
this.status = ExecutorStatus.IDLING;
getAssignment();
}
}
/**
* Requests a new task from the task queue
* @return void
**/
public void getAssignment() {
Task newTask = getAssignmentPort.getAssignment(this.getExecutorType(), this.getExecutorURI());
if (newTask != null) {
logger.info("ExecutorBase | Executor got a new task");
this.executeTask(newTask);
} else {
logger.info("ExecutorBase | Executor got no new task");
this.status = ExecutorStatus.IDLING;
}
}
/**
* Start the execution of a task
* @return void
**/
private void executeTask(Task task) {
logger.info("ExecutorBase | Starting execution");
this.status = ExecutorStatus.EXECUTING;
task.setOutputData(execution(task.getInputData()));
// TODO implement logic if execution was not successful
executionFinishedEventPort.publishExecutionFinishedEvent(
new ExecutionFinishedEvent(task.getTaskID(), task.getOutputData(), "SUCCESS"));
logger.info("ExecutorBase | Finish execution");
getAssignment();
}
/**
* Implementation of the actual execution method of an executor
* @return the execution result
**/
protected abstract String execution(String input);
}

View File

@ -1,22 +0,0 @@
package ch.unisg.executorbase.executor.domain;
public enum ExecutorType {
COMPUTATION, SMALLROBOT, HUMIDITY;
/**
* Checks if the give executor type exists.
* @return Whether the given executor type exists
**/
public static boolean contains(String test) {
for (ExecutorType x : ExecutorType.values()) {
if (x.name().equals(test)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,66 @@
package ch.unisg.executorbase;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.executorbase.services.GetAssignmentService;
import ch.unisg.executorbase.services.NotifyExecutorPoolService;
import lombok.Getter;
@Component
public class Executor {
@Getter
ExecutorStatus executorStatus = ExecutorStatus.STARTING_UP;
@Getter
@Value("${executor.type}")
String executorType;
@Getter
@Value("${executor.uri}")
ExecutorURI executorUri;
@Autowired
NotifyExecutorPoolService notifyExecutorPoolService;
@Autowired
GetAssignmentService getAssignmentService;
private Logger logger = Logger.getLogger(Executor.class.getName());
public Executor() {
executorStatus = ExecutorStatus.IDLING;
}
public void init() {
if(!notifyExecutorPoolService.executorStarted(this.executorUri, this.executorType)) {
logger.log(Level.WARNING, "ExecutorBase | Executor could not connect to executor pool! Shuting down!");
System.exit(0);
} else {
logger.info("ExecutorBase | Executor conntected to executor pool");
this.setIdling();
getAssignmentService.getAssignment();
}
}
// @PreDestroy
// public void preDestroy() {
// System.out.println("TEST");
// notifyExecutorPoolService.executorStopped(this.executorUri);
// }
public void setIdling() {
this.executorStatus = ExecutorStatus.IDLING;
}
public void setExecuting() {
this.executorStatus = ExecutorStatus.EXECUTING;
}
}

View File

@ -0,0 +1,27 @@
package ch.unisg.executorbase;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import ch.unisg.executorbase.services.NotifyExecutorPoolService;
public class ExecutorBase {
@Autowired
Executor executor;
@Autowired
NotifyExecutorPoolService notifyExecutorPoolService;
@PostConstruct
private void initialiseRoster(){
executor.init();
}
@PreDestroy
public void test() {
System.out.println("TEST");
}
}

View File

@ -1,4 +1,4 @@
package ch.unisg.executorbase.executor.domain;
package ch.unisg.executorbase;
public enum ExecutorStatus {
STARTING_UP, // Executor is starting

View File

@ -1,4 +1,4 @@
package ch.unisg.executorbase.executor.domain;
package ch.unisg.executorbase;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,7 +1,9 @@
package ch.unisg.executorbase.executor.adapter.in.web;
package ch.unisg.executorbase.controller;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -9,17 +11,13 @@ 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;
import ch.unisg.executorbase.services.TaskAvailableService;
@RestController
public class TaskAvailableController {
private final TaskAvailableUseCase taskAvailableUseCase;
public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
this.taskAvailableUseCase = taskAvailableUseCase;
}
@Autowired
private TaskAvailableService taskAvailableService;
Logger logger = Logger.getLogger(TaskAvailableController.class.getName());
@ -27,16 +25,12 @@ public class TaskAvailableController {
* Controller for notification about new events.
* @return 200 OK
**/
@GetMapping(path = "/newtask/{taskType}", consumes = { "application/json" })
@GetMapping(path = "/newtask/{taskType}")
public ResponseEntity<String> retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
logger.info("ExecutorBase | New " + taskType + " task available");
if (ExecutorType.contains(taskType.toUpperCase())) {
TaskAvailableCommand command = new TaskAvailableCommand(
ExecutorType.valueOf(taskType.toUpperCase()));
taskAvailableUseCase.newTaskAvailable(command);
}
CompletableFuture.runAsync(() -> taskAvailableService.newTaskAvailable(taskType.toUpperCase()));
// Add the content type as a response header
HttpHeaders responseHeaders = new HttpHeaders();

View File

@ -0,0 +1,42 @@
package ch.unisg.executorbase.services;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import ch.unisg.executorbase.Executor;
import ch.unisg.executorbase.Task;
public abstract class ExecuteTaskServiceBase implements ExecuteTaskServiceInterface {
private Logger logger = Logger.getLogger(ExecuteTaskServiceBase.class.getName());
@Autowired
private Executor executor;
@Autowired
private GetAssignmentService getAssignmentService;
@Autowired
private ExecutionFinishedService executionFinishedService;
/**
* Start the execution of a task
* @return void
**/
public void executeTask(Task task) {
logger.info("ExecutorBase | Starting execution");
executor.setExecuting();
task.setOutputData(execution(task.getInputData()));
// TODO implement logic if execution was not successful
executionFinishedService.publishExecutionFinishedEvent(task.getTaskID(), task.getOutputData(), "SUCCESS");
logger.info("ExecutorBase | Finish execution");
getAssignmentService.getAssignment();
}
}

View File

@ -0,0 +1,13 @@
package ch.unisg.executorbase.services;
import ch.unisg.executorbase.Task;
public interface ExecuteTaskServiceInterface {
void executeTask(Task task);
/**
* Implementation of the actual execution method of an executor
* @return the execution result
**/
String execution(String input);
}

View File

@ -1,4 +1,4 @@
package ch.unisg.executorbase.executor.adapter.out.web;
package ch.unisg.executorbase.services;
import java.io.IOException;
import java.net.URI;
@ -9,36 +9,34 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import ch.unisg.executorbase.executor.application.port.out.ExecutionFinishedEventPort;
import ch.unisg.executorbase.executor.domain.ExecutionFinishedEvent;
@Component
public class ExecutionFinishedService {
public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort {
@Value("${roster.uri}")
private String rosterUri;
String server = System.getenv("ROSTER_URI") == null ?
"http://localhost:8082" : System.getenv("ROSTER_URI");
Logger logger = Logger.getLogger(ExecutionFinishedEventAdapter.class.getName());
private Logger logger = Logger.getLogger(ExecutionFinishedService.class.getName());
/**
* Publishes the execution finished event
* @return void
**/
@Override
public void publishExecutionFinishedEvent(ExecutionFinishedEvent event) {
public void publishExecutionFinishedEvent(String taskID, String outputData, String status) {
logger.log(Level.INFO, "ExecutorBase | Sending finish execution event....");
String body = new JSONObject()
.put("taskID", event.getTaskID())
.put("outputData", event.getOutputData())
.put("status", event.getStatus())
.put("taskID", taskID)
.put("outputData", outputData)
.put("status", status)
.toString();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/task/completed"))
.uri(URI.create(rosterUri+"/task/completed"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
@ -53,7 +51,7 @@ public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
logger.log(Level.INFO, "ExecutorBase | Finish execution event sent with result: {0}", event.getOutputData());
logger.log(Level.INFO, "ExecutorBase | Finish execution event sent with result: {0}", outputData);
}
}

View File

@ -1,4 +1,4 @@
package ch.unisg.executorbase.executor.adapter.out.web;
package ch.unisg.executorbase.services;
import java.io.IOException;
import java.net.URI;
@ -8,42 +8,57 @@ import java.net.http.HttpResponse;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import ch.unisg.common.valueobject.ExecutorURI;
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;
import ch.unisg.executorbase.Executor;
import ch.unisg.executorbase.Task;
@Component
@Primary
public class GetAssignmentAdapter implements GetAssignmentPort {
public class GetAssignmentService {
String server = System.getenv("ROSTER_URI") == null ?
"http://localhost:8082" : System.getenv("ROSTER_URI");
@Value("${roster.uri}")
String rosterUri;
Logger logger = Logger.getLogger(GetAssignmentAdapter.class.getName());
Logger logger = Logger.getLogger(GetAssignmentService.class.getName());
@Autowired
private Executor executor;
@Autowired
private ExecuteTaskServiceInterface executeTaskService;
public void getAssignment() {
Task newTask = requestAssignment();
if (newTask != null) {
logger.info("ExecutorBase | Executor got a new task");
executeTaskService.executeTask(newTask);
} else {
logger.info("ExecutorBase | Executor got no new task");
executor.setIdling();
}
}
/**
* Requests a new task assignment
* @return the assigned task
* @see Task
**/
@Override
public Task getAssignment(ExecutorType executorType, ExecutorURI executorURI) {
private Task requestAssignment() {
String body = new JSONObject()
.put("executorType", executorType)
.put("executorURI", executorURI.getValue())
.toString();
.put("executorType", executor.getExecutorType())
.put("executorURI", executor.getExecutorUri().getValue())
.toString();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/task/apply"))
.uri(URI.create(rosterUri + "/task/apply"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
@ -72,5 +87,4 @@ public class GetAssignmentAdapter implements GetAssignmentPort {
return null;
}
}

View File

@ -1,4 +1,4 @@
package ch.unisg.executorbase.executor.adapter.out.web;
package ch.unisg.executorbase.services;
import java.io.IOException;
import java.net.URI;
@ -9,29 +9,27 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.executorbase.executor.application.port.out.NotifyExecutorPoolPort;
import ch.unisg.executorbase.executor.domain.ExecutorType;
@Component
@Primary
public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort {
public class NotifyExecutorPoolService {
String server = System.getenv("EXECUTOR_POOL_URI") == null ?
"http://localhost:8083" : System.getenv("EXECUTOR_POOL_URI");
@Value("${executor.pool.uri}")
String executorPoolUri;
Logger logger = Logger.getLogger(NotifyExecutorPoolAdapter.class.getName());
Logger logger = Logger.getLogger(NotifyExecutorPoolService.class.getName());
/**
* Notifies the executor-pool about the startup of this executor
* @return if the notification was successful
**/
@Override
public boolean notifyExecutorPool(ExecutorURI executorURI, ExecutorType executorType) {
public boolean executorStarted(ExecutorURI executorURI, String executorType) {
String body = new JSONObject()
.put("executorTaskType", executorType)
@ -40,7 +38,7 @@ public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/executor-pool/executors"))
.uri(URI.create(executorPoolUri + "/executor-pool/executors"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
@ -58,6 +56,28 @@ public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort {
}
return false;
}
}
/**
* Notifies the executor-pool about the shutdown of this executor
**/
public static void executorStopped(String executorURI) {
System.out.println("TEST");
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8083" + "/executor-pool/executors/" + executorURI))
// .uri(URI.create(executorPoolUri + "/executor-pool/executors/" + executorURI))
.header("Content-Type", "application/json")
.DELETE()
.build();
try {
client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// Thread.currentThread().interrupt();
} catch (IOException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
}
}

View File

@ -0,0 +1,28 @@
package ch.unisg.executorbase.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ch.unisg.executorbase.Executor;
import ch.unisg.executorbase.ExecutorStatus;
import lombok.RequiredArgsConstructor;
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Component
@Transactional
public class TaskAvailableService {
@Autowired
Executor executor;
@Autowired
GetAssignmentService getAssignmentService;
public void newTaskAvailable(String taskType) {
if (executor.getExecutorStatus() == ExecutorStatus.IDLING && executor.getExecutorType().equalsIgnoreCase(taskType)) {
getAssignmentService.getAssignment();
}
}
}

View File

@ -1,6 +1,4 @@
server.port=8081
roster.url=http://127.0.0.1:8082
executor.pool.url=http://127.0.0.1:8083
roster.uri=http://localhost:8082
spring.profiles.active=chaos-monkey
chaos.monkey.enabled=false

View File

@ -78,6 +78,12 @@
<artifactId>js-scriptengine</artifactId>
<version>21.3.0</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,40 @@
package ch.unisg.executorcomputation;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.executorbase.services.ExecuteTaskServiceBase;
@Component
@Primary
public class ExecuteTaskService extends ExecuteTaskServiceBase {
Logger executorLogger = Logger.getLogger(ExecuteTaskService.class.getName());
@Override
public String execution(String input) {
executorLogger.info("Executor | Starting execution with inputData: " + input);
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String result = "";
try {
result = engine.eval(input).toString();
} catch (ScriptException e1) {
// TODO some logic if execution fails
executorLogger.severe(e1.getMessage());
return result;
}
executorLogger.info("Executor | Finish execution");
return result;
}
}

View File

@ -1,22 +1,30 @@
package ch.unisg.executorcomputation;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import ch.unisg.executorcomputation.executor.domain.Executor;
import ch.unisg.executorbase.ExecutorBase;
import ch.unisg.executorbase.services.NotifyExecutorPoolService;
@SpringBootApplication
public class ExecutorcomputationApplication {
@ComponentScan({"ch.unisg.executorbase", "ch.unisg.executorcomputation"})
public class ExecutorcomputationApplication extends ExecutorBase {
static Logger logger = Logger.getLogger(ExecutorcomputationApplication.class.getName());
public static void main(String[] args) {
/**
* This is not a nice solution but I didn't get the @PreDestroy hook to work... This is the
* only solution which was working so I had to make the executorStopped function static
* for now.
*/
Thread printingHook = new Thread(() -> NotifyExecutorPoolService.executorStopped("http://executor-computation:8085"));
Runtime.getRuntime().addShutdownHook(printingHook);
SpringApplication.run(ExecutorcomputationApplication.class, args);
Executor.getExecutor();
}
}

View File

@ -1,35 +0,0 @@
package ch.unisg.executorcomputation.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<String> 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);
}
}

View File

@ -1,28 +0,0 @@
package ch.unisg.executorcomputation.executor.application.service;
import org.springframework.stereotype.Component;
import ch.unisg.executorcomputation.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();
}
}
}

View File

@ -1,57 +0,0 @@
package ch.unisg.executorcomputation.executor.domain;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import ch.unisg.executorbase.executor.domain.ExecutorBase;
import ch.unisg.executorbase.executor.domain.ExecutorType;
public class Executor extends ExecutorBase {
private static Logger executorLogger = Logger.getLogger(Executor.class.getName());
private static final Executor executor = new Executor(ExecutorType.COMPUTATION, "http://localhost:8085");
public static Executor getExecutor() {
return executor;
}
private Executor(ExecutorType executorType, String uri) {
super(executorType, uri);
}
@Override
protected
String execution(String inputData) {
executorLogger.info("Executor | Starting execution with inputData: " + inputData);
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String result = "";
try {
result = engine.eval(inputData).toString();
} catch (ScriptException e1) {
// TODO some logic if execution fails
executorLogger.severe(e1.getMessage());
return result;
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
return result;
// executorLogger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// Thread.currentThread().interrupt();
}
executorLogger.info("Executor | Finish execution");
return result;
}
}

View File

@ -1,5 +1,10 @@
server.port=8085
executor.type=COMPUTATION
executor.uri=http://localhost:8085
roster.uri=http://localhost:8082
executor.pool.uri=http://localhost:8083
spring.profiles.active=chaos-monkey
chaos.monkey.enabled=false
management.endpoint.chaosmonkey.enabled=true

View File

@ -0,0 +1,27 @@
package ch.unisg.executorhumidity;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.executorbase.services.ExecuteTaskServiceBase;
@Component
@Primary
public class ExecuteTaskService extends ExecuteTaskServiceBase {
@Autowired
GetHumidityService getHumidityService;
private Logger executorLogger = Logger.getLogger(ExecuteTaskService.class.getName());
@Override
public String execution(String input) {
executorLogger.info("Executor | Starting execution with inputData: " + input);
return getHumidityService.getHumidity();
}
}

View File

@ -2,15 +2,24 @@ package ch.unisg.executorhumidity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import ch.unisg.executorhumidity.executor.domain.Executor;
import ch.unisg.executorbase.ExecutorBase;
import ch.unisg.executorbase.services.NotifyExecutorPoolService;
@SpringBootApplication
public class ExecutorhumidityApplication {
@ComponentScan({"ch.unisg.executorbase", "ch.unisg.executorhumidity"})
public class ExecutorhumidityApplication extends ExecutorBase {
public static void main(String[] args) {
/**
* This is not a nice solution but I didn't get the @PreDestroy hook to work... This is the
* only solution which was working so I had to make the executorStopped function static
* for now.
*/
Thread printingHook = new Thread(() -> NotifyExecutorPoolService.executorStopped("http://executor-humidity:8087"));
Runtime.getRuntime().addShutdownHook(printingHook);
SpringApplication.run(ExecutorhumidityApplication.class, args);
Executor.getExecutor();
}
}

View File

@ -1,23 +1,4 @@
package ch.unisg.executorhumidity.executor.adapter.out;
import ch.unisg.executorhumidity.executor.application.port.out.GetHumidityPort;
import ch.unisg.ics.interactions.wot.td.ThingDescription;
import ch.unisg.ics.interactions.wot.td.affordances.Form;
import ch.unisg.ics.interactions.wot.td.affordances.PropertyAffordance;
import ch.unisg.ics.interactions.wot.td.clients.TDCoapRequest;
import ch.unisg.ics.interactions.wot.td.clients.TDCoapResponse;
import ch.unisg.ics.interactions.wot.td.io.TDGraphReader;
import ch.unisg.ics.interactions.wot.td.schemas.ObjectSchema;
import ch.unisg.ics.interactions.wot.td.vocabularies.TD;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
package ch.unisg.executorhumidity;
import java.io.IOException;
import java.io.StringReader;
@ -32,15 +13,29 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import ch.unisg.ics.interactions.wot.td.ThingDescription;
import ch.unisg.ics.interactions.wot.td.affordances.Form;
import ch.unisg.ics.interactions.wot.td.affordances.PropertyAffordance;
import ch.unisg.ics.interactions.wot.td.clients.TDCoapRequest;
import ch.unisg.ics.interactions.wot.td.clients.TDCoapResponse;
import ch.unisg.ics.interactions.wot.td.io.TDGraphReader;
import ch.unisg.ics.interactions.wot.td.schemas.ObjectSchema;
import ch.unisg.ics.interactions.wot.td.vocabularies.TD;
@Component
@Primary
public class GetHumidityAdapter implements GetHumidityPort {
public class GetHumidityService {
String endpoint = System.getenv("SEARCH_ENGINE_URI") == null ?
"https://api.interactions.ics.unisg.ch/search/searchEngine" : System.getenv("SEARCH_ENGINE_URI");
@Value("${search.engine.uri}")
String endpoint;
@Override
public String getHumidity() {
String input = "@prefix dct: <http://purl.org/dc/terms/> . select ?title where { ?title dct:title 'Mirogate' }";
@ -83,18 +78,14 @@ public class GetHumidityAdapter implements GetHumidityPort {
if (humidity.isPresent()) {
Optional<Form> form = humidity.get().getFirstFormForOperationType(TD.readProperty);
// System.out.println(humidity.get().getDataSchema().getDatatype());
if (form.isPresent()) {
TDCoapRequest request = new TDCoapRequest(form.get(), TD.readProperty);
try {
TDCoapResponse response = request.execute();
Map<String, Object> payload = response.getPayloadAsObject((ObjectSchema) humidity.get().getDataSchema());
Object result = payload.get("https://interactions.ics.unisg.ch/mirogate#HumidityValue");
return result.toString();
} catch (IOException e) {
e.printStackTrace();
}
TDCoapResponse response = request.execute();
Map<String, Object> payload = response.getPayloadAsObject((ObjectSchema) humidity.get().getDataSchema());
Object result = payload.get("https://interactions.ics.unisg.ch/mirogate#HumidityValue");
return result.toString();
}
}

View File

@ -1,38 +0,0 @@
package ch.unisg.executorhumidity.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;
import java.util.concurrent.CompletableFuture;
@RestController
public class TaskAvailableController {
private final TaskAvailableUseCase taskAvailableUseCase;
public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
this.taskAvailableUseCase = taskAvailableUseCase;
}
@GetMapping(path="/newtask/{taskType}")
public ResponseEntity<String> retrieveTaskfromTaskList(@PathVariable("taskType") String taskType) {
if (ExecutorType.contains(taskType.toUpperCase())) {
TaskAvailableCommand command = new TaskAvailableCommand(
ExecutorType.valueOf(taskType.toUpperCase()));
CompletableFuture.runAsync(() -> taskAvailableUseCase.newTaskAvailable(command));
}
// Add the content type as a response header
HttpHeaders responseHeaders = new HttpHeaders();
return new ResponseEntity<>("OK", responseHeaders, HttpStatus.OK);
}
}

View File

@ -1,9 +0,0 @@
package ch.unisg.executorhumidity.executor.application.port.out;
import org.eclipse.californium.elements.exception.ConnectorException;
import java.io.IOException;
public interface GetHumidityPort {
String getHumidity();
}

View File

@ -1,27 +0,0 @@
package ch.unisg.executorhumidity.executor.application.service;
import ch.unisg.executorbase.executor.domain.ExecutorStatus;
import ch.unisg.executorhumidity.executor.domain.Executor;
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) {
Executor executor = Executor.getExecutor();
if(executor.getExecutorType() == command.getTaskType() &&
executor.getStatus() == ExecutorStatus.IDLING) {
executor.getAssignment();
}
}
}

View File

@ -1,28 +0,0 @@
package ch.unisg.executorhumidity.executor.domain;
import ch.unisg.executorbase.executor.domain.ExecutorBase;
import ch.unisg.executorbase.executor.domain.ExecutorType;
import ch.unisg.executorhumidity.executor.adapter.out.GetHumidityAdapter;
import ch.unisg.executorhumidity.executor.application.port.out.GetHumidityPort;
public class Executor extends ExecutorBase {
private static final Executor executor = new Executor(ExecutorType.HUMIDITY, "http://localhost:8087");
private final GetHumidityPort getHumidityPort = new GetHumidityAdapter();
private Executor(ExecutorType executorType, String uri) {
super(executorType, uri);
}
public static Executor getExecutor() {return executor;}
@Override
protected
String execution(String input) {
String result = getHumidityPort.getHumidity();
return result;
}
}

View File

@ -1,6 +1,10 @@
server.port=8087
search.engine.uri=https://api.interactions.ics.unisg.ch/search/searchEngine
executor.type=HUMIDITY
executor.uri=http://localhost:8087
roster.uri=http://localhost:8082
executor.pool.uri=http://localhost:8083
spring.profiles.active=chaos-monkey
chaos.monkey.enabled=false

View File

@ -1,7 +1,5 @@
package ch.unisg.executorpool.adapter.common.clients;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
@ -9,7 +7,6 @@ import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class TapasMqttClient {
private static final Logger LOGGER = LogManager.getLogger(TapasMqttClient.class);
private static TapasMqttClient tapasClient = null;

View File

@ -1,6 +1,5 @@
package ch.unisg.executorpool.adapter.in.web;
import ch.unisg.executorpool.adapter.common.clients.TapasMqttClient;
import ch.unisg.executorpool.adapter.common.formats.ExecutorJsonRepresentation;
import ch.unisg.executorpool.application.port.in.AddNewExecutorToExecutorPoolUseCase;
import ch.unisg.executorpool.application.port.in.AddNewExecutorToExecutorPoolCommand;
@ -14,10 +13,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import javax.validation.ConstraintViolationException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import org.eclipse.paho.client.mqttv3.*;
@RestController
public class AddNewExecutorToExecutorPoolWebController {

View File

@ -1,6 +1,5 @@
package ch.unisg.executorpool.adapter.out.messaging;
import ch.unisg.common.ConfigProperties;
import ch.unisg.executorpool.adapter.common.clients.TapasMqttClient;
import ch.unisg.executorpool.adapter.common.formats.ExecutorJsonRepresentation;
import ch.unisg.executorpool.application.port.out.ExecutorAddedEventPort;
@ -13,20 +12,12 @@ import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.net.URI;
@Component
@Primary
public class PublishExecutorAddedEventAdapter implements ExecutorAddedEventPort {
private static final Logger LOGGER = LogManager.getLogger(PublishExecutorAddedEventAdapter.class);
// TODO Can't autowire. Find fix
/*
@Autowired
private ConfigProperties config;
*/
@Autowired
private Environment environment;

View File

@ -1,13 +1,14 @@
package ch.unisg.executorpool.application.port.in;
import ch.unisg.common.SelfValidating;
import ch.unisg.executorpool.domain.ExecutorPool;
import ch.unisg.executorpool.domain.ExecutorClass.ExecutorUri;
import ch.unisg.executorpool.domain.ExecutorClass.ExecutorTaskType;
import lombok.EqualsAndHashCode;
import lombok.Value;
import javax.validation.constraints.NotNull;
@Value
@EqualsAndHashCode(callSuper=false)
public class AddNewExecutorToExecutorPoolCommand extends SelfValidating<AddNewExecutorToExecutorPoolCommand> {
@NotNull
private final ExecutorUri executorUri;

View File

@ -1,7 +1,6 @@
package ch.unisg.executorpool.application.port.in;
import ch.unisg.executorpool.domain.ExecutorClass;
import ch.unisg.executorpool.domain.ExecutorPool;
import java.util.List;

View File

@ -16,7 +16,6 @@ import static org.assertj.core.api.BDDAssertions.*;
import ch.unisg.executorpool.adapter.common.formats.ExecutorJsonRepresentation;
import ch.unisg.executorpool.application.port.out.AddExecutorPort;
import ch.unisg.executorpool.domain.ExecutorPool;
import ch.unisg.executorpool.domain.ExecutorClass;
import ch.unisg.executorpool.domain.ExecutorClass.ExecutorTaskType;
import ch.unisg.executorpool.domain.ExecutorClass.ExecutorUri;
@ -27,16 +26,13 @@ public class AddNewExecutorToExecutorPoolSystemTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private AddExecutorPort addExecutorPort;
@Test
void AddNewExecutorToExecutorPool() throws JSONException {
ExecutorTaskType executorTaskType = new ExecutorTaskType("system-integration-test-type");
ExecutorUri executorUri = new ExecutorUri(java.net.URI.create("example.org"));
ResponseEntity response = whenAddNewExecutorToEmptyPool(executorTaskType, executorUri);
ResponseEntity<String> response = whenAddNewExecutorToEmptyPool(executorTaskType, executorUri);
JSONObject responseJson = new JSONObject(response.getBody().toString());
String respExecutorUri = responseJson.getString("executorUri");
@ -50,7 +46,7 @@ public class AddNewExecutorToExecutorPoolSystemTest {
}
private ResponseEntity whenAddNewExecutorToEmptyPool(ExecutorTaskType executorTaskType,
ExecutorUri executorUri) throws JSONException {
ExecutorUri executorUri) throws JSONException {
ExecutorPool.getExecutorPool().getListOfExecutors().getValue().clear();

View File

@ -1,9 +1,6 @@
package ch.unisg.executorpool;
import java.util.Optional;
import ch.unisg.executorpool.application.port.out.LoadExecutorPort;
import org.bson.json.JsonObject;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.BDDMockito.eq;

View File

@ -1,7 +1,6 @@
package ch.unisg.executorpool;
import java.net.URI;
import java.util.Optional;
import org.junit.jupiter.api.Test;

View File

@ -0,0 +1,27 @@
package ch.unisg.executorrobot;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.executorbase.services.ExecuteTaskServiceBase;
@Component
@Primary
public class ExecuteTaskService extends ExecuteTaskServiceBase {
@Autowired
RobotService robotService;
private Logger executorLogger = Logger.getLogger(ExecuteTaskService.class.getName());
@Override
public String execution(String input) {
executorLogger.info("Executor | Starting execution with inputData: " + input);
robotService.executeRobotTask();
return "";
}
}

View File

@ -1,18 +1,26 @@
package ch.unisg.executorrobot;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import ch.unisg.executorrobot.executor.domain.Executor;
import ch.unisg.executorbase.ExecutorBase;
import ch.unisg.executorbase.services.NotifyExecutorPoolService;
@SpringBootApplication
public class ExecutorrobotApplication {
@ComponentScan({"ch.unisg.executorbase", "ch.unisg.executorrobot"})
public class ExecutorrobotApplication extends ExecutorBase {
public static void main(String[] args) {
/**
* This is not a nice solution but I didn't get the @PreDestroy hook to work... This is the
* only solution which was working so I had to make the executorStopped function static
* for now.
*/
// Thread printingHook = new Thread(() -> NotifyExecutorPoolService.executorStopped("http://localhost:8084"));
Thread printingHook = new Thread(() -> NotifyExecutorPoolService.executorStopped("http://executor-robot:8084"));
Runtime.getRuntime().addShutdownHook(printingHook);
SpringApplication.run(ExecutorrobotApplication.class, args);
Executor.getExecutor();
}
}

View File

@ -1,4 +1,5 @@
package ch.unisg.executorrobot.executor.adapter.out;
package ch.unisg.executorrobot;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
@ -9,11 +10,20 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import ch.unisg.ics.interactions.wot.td.ThingDescription;
import ch.unisg.ics.interactions.wot.td.affordances.ActionAffordance;
import ch.unisg.ics.interactions.wot.td.affordances.Form;
@ -27,25 +37,15 @@ import ch.unisg.ics.interactions.wot.td.security.SecurityScheme;
import ch.unisg.ics.interactions.wot.td.vocabularies.TD;
import ch.unisg.ics.interactions.wot.td.vocabularies.WoTSec;
import org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import ch.unisg.executorrobot.executor.application.port.out.UserToRobotPort;
@Component
@Primary
public class UserToRobotAdapter implements UserToRobotPort {
public class RobotService {
@Override
public String userToRobot() {
@Value("${search.engine.uri}")
String endpoint;
String endpoint = "https://api.interactions.ics.unisg.ch/search/searchEngine";
private Logger logger = Logger.getLogger(RobotService.class.getName());
public String executeRobotTask() {
String input = "@prefix dct: <http://purl.org/dc/terms/> . select ?title where { ?title dct:title 'leubot1' }";
@ -83,10 +83,10 @@ public class UserToRobotAdapter implements UserToRobotPort {
ThingDescription td = TDGraphReader.readFromURL(ThingDescription.TDFormat.RDF_TURTLE, leubot1Uri);
String apiUrl = getAPIKey(td);
System.out.println("FOUND API URL " + apiUrl);
logger.info("Executor | FOUND API URL " + apiUrl);
String apiKey = apiUrl.split("/")[apiUrl.split("/").length-1].split("]")[0];
System.out.println("FOUND KEY " + apiKey);
logger.info("Executor | FOUND KEY " + apiKey);
if(apiKey == null) {
// TODO implement logic if execution failed
@ -160,7 +160,7 @@ public class UserToRobotAdapter implements UserToRobotPort {
try {
TDHttpResponse response = request.execute();
System.out.println("Received response with status code: " + response.getStatusCode());
logger.info("Executor | Received response with status code: " + response.getStatusCode());
String url = response.getHeaders().get("Location");
return url;
@ -210,7 +210,7 @@ public class UserToRobotAdapter implements UserToRobotPort {
try {
TDHttpResponse response = request.execute();
System.out.println("Received response with status code: " + response.getStatusCode());
logger.info("Executor | Received response with status code: " + response.getStatusCode());
return true;
} catch (IOException e) {
e.printStackTrace();
@ -230,12 +230,11 @@ public class UserToRobotAdapter implements UserToRobotPort {
try {
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
logger.info("Executor | Delete user from robot response code: " + response.statusCode());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -1,38 +0,0 @@
package ch.unisg.executorrobot.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<String> retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
if (ExecutorType.contains(taskType.toUpperCase())) {
TaskAvailableCommand command = new TaskAvailableCommand(
ExecutorType.valueOf(taskType.toUpperCase()));
CompletableFuture.runAsync(() -> taskAvailableUseCase.newTaskAvailable(command));
}
// Add the content type as a response header
HttpHeaders responseHeaders = new HttpHeaders();
return new ResponseEntity<>("OK", responseHeaders, HttpStatus.OK);
}
}

View File

@ -1,7 +0,0 @@
package ch.unisg.executorrobot.executor.application.port.out;
import ch.unisg.executorbase.executor.domain.ExecutorType;
public interface UserToRobotPort {
String userToRobot();
}

View File

@ -1,28 +0,0 @@
package ch.unisg.executorrobot.executor.application.service;
import org.springframework.stereotype.Component;
import ch.unisg.executorrobot.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();
}
}
}

View File

@ -1,28 +0,0 @@
package ch.unisg.executorrobot.executor.domain;
import ch.unisg.executorrobot.executor.adapter.out.UserToRobotAdapter;
import ch.unisg.executorrobot.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.SMALLROBOT, "http://localhost:8084");
private final UserToRobotPort userToRobotPort = new UserToRobotAdapter();
public static Executor getExecutor() {
return executor;
}
private Executor(ExecutorType executorType, String uri) {
super(executorType, uri);
}
@Override
protected
String execution(String input) {
userToRobotPort.userToRobot();
return "";
}
}

View File

@ -1 +1,19 @@
server.port=8084
executor.type=SMALLROBOT
executor.uri=http://localhost:8084
roster.uri=http://localhost:8082
executor.pool.uri=http://localhost:8083
search.engine.uri=https://api.interactions.ics.unisg.ch/search/searchEngine
spring.profiles.active=chaos-monkey
chaos.monkey.enabled=false
management.endpoint.chaosmonkey.enabled=true
management.endpoint.chaosmonkeyjmx.enabled=true
# include specific endpoints
management.endpoints.web.exposure.include=health,info,chaosmonkey
chaos.monkey.watcher.controller=true
chaos.monkey.watcher.restController=true
chaos.monkey.watcher.service=true
chaos.monkey.watcher.repository=true
chaos.monkey.watcher.component=true

View File

@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.function.ServerRequest.Headers;
import ch.unisg.roster.roster.application.port.in.NewTaskCommand;
import ch.unisg.roster.roster.application.port.in.NewTaskUseCase;

View File

@ -4,7 +4,10 @@ import ch.unisg.roster.roster.domain.ExecutorInfo;
import org.apache.logging.log4j.Level;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
@ -15,11 +18,12 @@ import java.util.LinkedList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Component
@Primary
public class GetExecutorsInExecutorPoolWebAdapter {
private static final Logger LOGGER = LogManager.getLogger(GetExecutorsInExecutorPoolWebAdapter.class);
// TODO get from config
@Value("${executor.pool.uri}")
String server = "http://localhost:8083";
public List<ExecutorInfo> getExecutorsInExecutorPool(){

View File

@ -5,29 +5,22 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.roster.roster.application.port.out.NewTaskEventPort;
import ch.unisg.roster.roster.domain.ExecutorRegistry;
import ch.unisg.roster.roster.domain.event.NewTaskEvent;
@Component
@Primary
public class PublishNewTaskEventAdapter implements NewTaskEventPort {
@Value("${executor.robot.uri}")
private String server;
@Value("${executor.computation.uri}")
private String server2;
@Value("${executor.humidity.uri}")
private String server3;
Logger logger = Logger.getLogger(PublishNewTaskEventAdapter.class.getName());
/**
@ -37,53 +30,26 @@ public class PublishNewTaskEventAdapter implements NewTaskEventPort {
@Override
public void publishNewTaskEvent(NewTaskEvent event) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server + "/newtask/" + event.taskType.getValue()))
.GET()
.build();
Set<ExecutorURI> executors = ExecutorRegistry.getInstance().getExecutorsByType(event.taskType.getValue());
for (ExecutorURI uri : executors) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri.getValue() + "/newtask/" + event.taskType.getValue()))
.GET()
.build();
try {
client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
try {
client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
}
HttpClient client2 = HttpClient.newHttpClient();
HttpRequest request2 = HttpRequest.newBuilder()
.uri(URI.create(server2 + "/newtask/" + event.taskType.getValue()))
.GET()
.build();
try {
client2.send(request2, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
HttpClient client3 = HttpClient.newHttpClient();
HttpRequest request3 = HttpRequest.newBuilder()
.uri(URI.create(server3 + "/newtask/" + event.taskType.getValue()))
.GET()
.build();
try {
client3.send(request3, HttpResponse.BodyHandlers.ofString());
} catch (InterruptedException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
Thread.currentThread().interrupt();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
}
}

View File

@ -45,7 +45,7 @@ public class PublishTaskAssignedEventAdapter implements TaskAssignedEventPort {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server + "/tasks/" + event.taskID))
.header("Content-Type", "application/task+json")
.header("Content-Type", "application/json-patch+json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(body))
.build();
@ -57,27 +57,6 @@ public class PublishTaskAssignedEventAdapter implements TaskAssignedEventPort {
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
// String body = new JSONObject()
// .put("taskId", event.taskID)
// .toString();
// HttpClient client = HttpClient.newHttpClient();
// HttpRequest request = HttpRequest.newBuilder()
// .uri(URI.create(server + "/tasks/assignTask"))
// .header("Content-Type", "application/task+json")
// .POST(HttpRequest.BodyPublishers.ofString(body))
// .build();
// try {
// client.send(request, HttpResponse.BodyHandlers.ofString());
// } catch (InterruptedException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// Thread.currentThread().interrupt();
// } catch (IOException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// }
}
}

View File

@ -38,10 +38,10 @@ public class PublishTaskCompletedEventAdapter implements TaskCompletedEventPort
JSONObject op1 = new JSONObject()
.put("op", "replace")
.put("path", "/taskStatus")
.put("value", event.status);
.put("value", "EXECUTED");
JSONObject op2 = new JSONObject()
.put("op", "replace")
.put("op", "add")
.put("path", "/outputData")
.put("value", event.result);
@ -50,7 +50,7 @@ public class PublishTaskCompletedEventAdapter implements TaskCompletedEventPort
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server + "/tasks/" + event.taskID))
.header("Content-Type", "application/task+json")
.header("Content-Type", "application/json-patch+json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(body))
.build();
@ -62,29 +62,6 @@ public class PublishTaskCompletedEventAdapter implements TaskCompletedEventPort
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
// String body = new JSONObject()
// .put("taskId", event.taskID)
// .put("status", event.status)
// .put("outputData", event.result)
// .toString();
// HttpClient client = HttpClient.newHttpClient();
// HttpRequest request = HttpRequest.newBuilder()
// .uri(URI.create(server + "/tasks/completeTask/"))
// .header("Content-Type", "application/task+json")
// .POST(HttpRequest.BodyPublishers.ofString(body))
// .build();
// try {
// client.send(request, HttpResponse.BodyHandlers.ofString());
// } catch (InterruptedException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// Thread.currentThread().interrupt();
// } catch (IOException e) {
// logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
// }
}
}

View File

@ -5,8 +5,6 @@ import ch.unisg.common.valueobject.ExecutorURI;
import lombok.Getter;
import lombok.Setter;
import java.net.URI;
public class ExecutorInfo {
@Getter
@Setter

View File

@ -4,6 +4,7 @@ import java.util.*;
import ch.unisg.common.valueobject.ExecutorURI;
import ch.unisg.roster.roster.domain.valueobject.ExecutorType;
import lombok.Getter;
/**
* Registry that keeps a track of executors internal to the TAPAS application and the types of tasks
@ -98,4 +99,8 @@ public class ExecutorRegistry {
this.executors.putAll(executors);
}
public Set<ExecutorURI> getExecutorsByType(String executorType) {
return this.executors.get(new ExecutorType(executorType));
}
}

View File

@ -1,9 +1,8 @@
server.port=8082
executor.robot.uri=http://127.0.0.1:8084
executor.computation.uri=http://127.0.0.1:8085
executor.humidity.uri=http://127.0.0.1:8087
auction.house.uri=http://127.0.0.1:8086
task.list.uri=http://127.0.0.1:8081
executor.pool.uri=http://localhost:8083
# mqtt.broker.uri=tcp://localhost:1883
mqtt.broker.uri=tcp://broker.hivemq.com

View File

@ -49,7 +49,7 @@ public class AuctionStartedEventListenerWebSubAdapter {
@PostMapping(path = "/auction-started/74c72c7f-2739-4124-aa35-a3225171a97c")
public ResponseEntity<Void> handleExecutorAddedEvent(@RequestBody String payload) throws URISyntaxException {
System.out.println("new auctions :O");
System.out.println("new websub auctions");
System.out.println(payload);
@ -57,28 +57,15 @@ public class AuctionStartedEventListenerWebSubAdapter {
if (auctions.length() > 0) {
JSONObject auction = auctions.getJSONObject(0);
System.out.print(auction);
// try {
// System.out.println(auction.getString("deadline"));
// System.out.println(AuctionJsonRepresentation.deserialize(auction.toString()));
// auctionStartedHandler.handleAuctionStartedEvent(
// new AuctionStartedEvent(AuctionJsonRepresentation.deserialize(auction.toString())));
// } catch (JsonProcessingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
String auctionHouseURI = "https://tapas-auction-house.86-119-35-40.nip.io/";
String auctionHouseURI = "https://tapas-auction-house.86-119-35-40.nip.io/";
String taskListURI = "https://tapas-tasks.86-119-35-40.nip.io";
String taskListURI = "https://tapas-tasks.86-119-35-40.nip.io";
// TODO Sanitize URIs
String auctionId = auction.getString("auctionId");
String auctionHouseUri = auction.getString("auctionHouseUri");
String taskUri = auction.getString("taskUri");
String taskType = auction.getString("taskType");
String deadline = auction.getString("deadline");
// TODO Sanitize URIs
String auctionId = auction.getString("auctionId");
String auctionHouseUri = auction.getString("auctionHouseUri");
var bid = new Bid(
new Auction.AuctionId(auctionId),
@ -87,10 +74,10 @@ public class AuctionStartedEventListenerWebSubAdapter {
new Bid.BidderTaskListUri(URI.create(taskListURI))
);
String body;
try {
body = BidJsonRepresentation.serialize(bid);
//LOGGER.info(body);
String body;
try {
body = BidJsonRepresentation.serialize(bid);
//LOGGER.info(body);
var postURI = URI.create(auctionHouseUri + "/bid");
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(postURI)
@ -98,34 +85,20 @@ public class AuctionStartedEventListenerWebSubAdapter {
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpClient client = HttpClient.newHttpClient();
var postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//LOGGER.info(postResponse.statusCode());
HttpClient client = HttpClient.newHttpClient();
var postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
// for (JSONObject auction : auctions) {
// auctionStartedHandler.handleAuctionStartedEvent(
// new AuctionStartedEvent(
// new Auction(new AuctionId(auction.getAuctionId()),
// new AuctionHouseUri(new URI(auction.getAuctionHouseUri())),
// new AuctionedTaskUri(new URI(auction.getTaskUri())),
// new AuctionedTaskType(auction.getTaskType()),
// new AuctionDeadline(auction.getDeadline()))
// ));
// }
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -7,6 +7,7 @@ import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedEventHandler;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.Task.TaskId;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
@ -18,8 +19,11 @@ import java.util.Optional;
*
* See also {@link TaskAssignedEvent}, {@link Task}, and {@link TaskEventHttpDispatcher}.
*/
@RequiredArgsConstructor
public class TaskAssignedEventListenerHttpAdapter extends TaskEventListener {
private final TaskAssignedEventHandler taskAssignedEventHandler;
/**
* Handles the task assigned event.
*
@ -32,7 +36,6 @@ public class TaskAssignedEventListenerHttpAdapter extends TaskEventListener {
Optional<Task.ServiceProvider> serviceProvider = representation.extractFirstServiceProviderChange();
TaskAssignedEvent taskAssignedEvent = new TaskAssignedEvent(new TaskId(taskId), serviceProvider);
TaskAssignedEventHandler taskAssignedEventHandler = new TaskAssignedHandler();
return taskAssignedEventHandler.handleTaskAssigned(taskAssignedEvent);
}

View File

@ -3,10 +3,14 @@ package ch.unisg.tapastasks.tasks.adapter.in.messaging.http;
import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonPatchRepresentation;
import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
import ch.unisg.tapastasks.tasks.adapter.in.messaging.UnknownEventException;
import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.in.TaskExecutedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.in.TaskStartedEventHandler;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskNotFoundException;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jsonpatch.JsonPatch;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -37,6 +41,7 @@ import java.util.logging.Logger;
* For some sample HTTP requests, see the README.
*/
@RestController
@RequiredArgsConstructor
public class TaskEventHttpDispatcher {
// The standard media type for JSON Patch registered with IANA
// See: https://www.iana.org/assignments/media-types/application/json-patch+json
@ -44,6 +49,9 @@ public class TaskEventHttpDispatcher {
Logger logger = Logger.getLogger(TaskEventHttpDispatcher.class.getName());
private final TaskStartedEventHandler taskStartedEventHandler;
private final TaskAssignedEventHandler taskAssignedEventHandler;
private final TaskExecutedEventHandler taskExecutedEventHandler;
/**
* Handles HTTP PATCH requests with a JSON Patch payload. Routes the requests based on the
* the operations requested in the patch. In this implementation, one HTTP Patch request is
@ -58,6 +66,7 @@ public class TaskEventHttpDispatcher {
@PatchMapping(path = "/tasks/{taskId}", consumes = {JSON_PATCH_MEDIA_TYPE})
public ResponseEntity<String> dispatchTaskEvents(@PathVariable("taskId") String taskId,
@RequestBody JsonNode payload) {
logger.info("TaskList | Incoming patch task request for task: " + taskId);
try {
// Throw an exception if the JSON Patch format is invalid. This call is only used to
@ -74,13 +83,13 @@ public class TaskEventHttpDispatcher {
if (status.isPresent()) {
switch (status.get()) {
case ASSIGNED:
listener = new TaskAssignedEventListenerHttpAdapter();
listener = new TaskAssignedEventListenerHttpAdapter(taskAssignedEventHandler);
break;
case RUNNING:
listener = new TaskStartedEventListenerHttpAdapter();
listener = new TaskStartedEventListenerHttpAdapter(taskStartedEventHandler);
break;
case EXECUTED:
listener = new TaskExecutedEventListenerHttpAdapter();
listener = new TaskExecutedEventListenerHttpAdapter(taskExecutedEventHandler);
break;
default:
break;

View File

@ -6,6 +6,7 @@ import ch.unisg.tapastasks.tasks.application.port.in.TaskExecutedEvent;
import ch.unisg.tapastasks.tasks.application.port.in.TaskExecutedEventHandler;
import ch.unisg.tapastasks.tasks.domain.Task;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
@ -17,17 +18,22 @@ import java.util.Optional;
*
* See also {@link TaskExecutedEvent}, {@link Task}, and {@link TaskEventHttpDispatcher}.
*/
@RequiredArgsConstructor
public class TaskExecutedEventListenerHttpAdapter extends TaskEventListener {
private final TaskExecutedEventHandler taskExecutedEventHandler;
public Task handleTaskEvent(String taskId, JsonNode payload) {
System.out.println(payload);
TaskJsonPatchRepresentation representation = new TaskJsonPatchRepresentation(payload);
Optional<Task.ServiceProvider> serviceProvider = representation.extractFirstServiceProviderChange();
Optional<Task.OutputData> outputData = representation.extractFirstOutputDataAddition();
System.out.println(outputData);
TaskExecutedEvent taskExecutedEvent = new TaskExecutedEvent(new Task.TaskId(taskId),
serviceProvider, outputData);
TaskExecutedEventHandler taskExecutedEventHandler = new TaskExecutedHandler();
return taskExecutedEventHandler.handleTaskExecuted(taskExecutedEvent);
}

View File

@ -7,6 +7,7 @@ import ch.unisg.tapastasks.tasks.application.port.in.TaskStartedEventHandler;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.Task.TaskId;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
@ -18,14 +19,16 @@ import java.util.Optional;
*
* See also {@link TaskStartedEvent}, {@link Task}, and {@link TaskEventHttpDispatcher}.
*/
@RequiredArgsConstructor
public class TaskStartedEventListenerHttpAdapter extends TaskEventListener {
private final TaskStartedEventHandler taskStartedEventHandler;
public Task handleTaskEvent(String taskId, JsonNode payload) {
TaskJsonPatchRepresentation representation = new TaskJsonPatchRepresentation(payload);
Optional<Task.ServiceProvider> serviceProvider = representation.extractFirstServiceProviderChange();
TaskStartedEvent taskStartedEvent = new TaskStartedEvent(new TaskId(taskId), serviceProvider);
TaskStartedEventHandler taskStartedEventHandler = new TaskStartedHandler();
return taskStartedEventHandler.handleTaskStarted(taskStartedEvent);
}

View File

@ -9,9 +9,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
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.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import javax.validation.ConstraintViolationException;
@ -26,10 +24,10 @@ public class DeleteTaskWebController {
}
// TODO change to DELETE and why are we using task URI here?
@PostMapping(path="/tasks/deleteTask", consumes = {TaskJsonRepresentation.MEDIA_TYPE})
public ResponseEntity<String> deleteTask (@RequestBody Task task){
@DeleteMapping(path="/tasks/{taskId}")
public ResponseEntity<String> deleteTask (@PathVariable String taskId){
try {
DeleteTaskCommand command = new DeleteTaskCommand(task.getTaskId(), task.getOriginalTaskUri());
DeleteTaskCommand command = new DeleteTaskCommand(new Task.TaskId(taskId));
Optional<Task> deleteATask = deleteClassUseCase.deleteTask(command);

View File

@ -1,7 +1,6 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import java.util.Objects;
@ -22,6 +21,7 @@ class TaskMapper {
);
}
// TODO stupid task list name
MongoTaskDocument mapToMongoDocument(Task task) {
return new MongoTaskDocument(
task.getTaskId().getValue(),
@ -31,7 +31,7 @@ class TaskMapper {
task.getTaskStatus().getValue().toString(),
Objects.isNull(task.getInputData()) ? null : task.getInputData().getValue(),
Objects.isNull(task.getOutputData()) ? null : task.getOutputData().getValue(),
TaskList.getTapasTaskList().getTaskListName().getValue()
"task-list-name"
);
}
}

View File

@ -1,9 +1,9 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.DeleteTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -12,7 +12,8 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor
public class TaskPersistenceAdapter implements
AddTaskPort,
LoadTaskPort {
LoadTaskPort,
DeleteTaskPort {
@Autowired
private final TaskRepository taskRepository;
@ -26,8 +27,13 @@ public class TaskPersistenceAdapter implements
}
@Override
public Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName) {
MongoTaskDocument mongoTaskDocument = taskRepository.findByTaskId(taskId.getValue(),taskListName.getValue());
public Task loadTask(Task.TaskId taskId) {
MongoTaskDocument mongoTaskDocument = taskRepository.findByTaskId(taskId.getValue());
return taskMapper.mapToDomainEntity(mongoTaskDocument);
}
@Override
public void deleteTask(Task.TaskId taskId) {
taskRepository.deleteByTaskId(taskId.getValue());
}
}

View File

@ -8,7 +8,9 @@ import java.util.List;
@Repository
public interface TaskRepository extends MongoRepository<MongoTaskDocument,String> {
public MongoTaskDocument findByTaskId(String taskId, String taskListName);
public MongoTaskDocument findByTaskId(String taskId);
public List<MongoTaskDocument> findByTaskListName(String taskListName);
public List<MongoTaskDocument> getAllBy();
public void deleteByTaskId(String taskId);
}

View File

@ -2,18 +2,36 @@ package ch.unisg.tapastasks.tasks.application.handler;
import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedEvent;
import ch.unisg.tapastasks.tasks.application.port.in.TaskAssignedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import ch.unisg.tapastasks.tasks.domain.TaskNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
import java.util.Optional;
@RequiredArgsConstructor
@Component
@Transactional
public class TaskAssignedHandler implements TaskAssignedEventHandler {
// TODO Why do we need this event handler when a service exists that does the same?
private final AddTaskPort addTaskToRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
@Override
public Task handleTaskAssigned(TaskAssignedEvent taskAssignedEvent) throws TaskNotFoundException {
TaskList taskList = TaskList.getTapasTaskList();
return taskList.changeTaskStatusToAssigned(taskAssignedEvent.getTaskId(),
taskAssignedEvent.getServiceProvider());
// retrieve the task based on ID
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(taskAssignedEvent.getTaskId()));
// update the status to assigned
Task updatedTask = taskFromRepo.get();
updatedTask.setTaskStatus(new Task.TaskStatus(Task.Status.ASSIGNED));
// save updated task in repo
addTaskToRepositoryPort.addTask(updatedTask);
return updatedTask;
}
}

View File

@ -2,18 +2,44 @@ package ch.unisg.tapastasks.tasks.application.handler;
import ch.unisg.tapastasks.tasks.application.port.in.TaskExecutedEvent;
import ch.unisg.tapastasks.tasks.application.port.in.TaskExecutedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import ch.unisg.tapastasks.tasks.domain.TaskNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
import java.util.Optional;
@RequiredArgsConstructor
@Component
@Transactional
public class TaskExecutedHandler implements TaskExecutedEventHandler {
private final AddTaskPort addTaskToRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
@Override
public Task handleTaskExecuted(TaskExecutedEvent taskExecutedEvent) throws TaskNotFoundException {
TaskList taskList = TaskList.getTapasTaskList();
return taskList.changeTaskStatusToExecuted(taskExecutedEvent.getTaskId(),
taskExecutedEvent.getServiceProvider(), taskExecutedEvent.getOutputData());
// retrieve the task based on ID
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(taskExecutedEvent.getTaskId()));
// update the status to assigned
Task updatedTask = taskFromRepo.get();
updatedTask.setTaskStatus(new Task.TaskStatus(Task.Status.EXECUTED));
if(taskExecutedEvent.getServiceProvider().isPresent()){
updatedTask.setProvider(taskExecutedEvent.getServiceProvider().get());
}
if(taskExecutedEvent.getOutputData().isPresent()){
updatedTask.setOutputData(taskExecutedEvent.getOutputData().get());
}
// save updated task in repo
addTaskToRepositoryPort.addTask(updatedTask);
return updatedTask;
}
}

View File

@ -2,18 +2,40 @@ package ch.unisg.tapastasks.tasks.application.handler;
import ch.unisg.tapastasks.tasks.application.port.in.TaskStartedEvent;
import ch.unisg.tapastasks.tasks.application.port.in.TaskStartedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import ch.unisg.tapastasks.tasks.domain.TaskNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
import java.util.Optional;
@RequiredArgsConstructor
@Component
@Transactional
public class TaskStartedHandler implements TaskStartedEventHandler {
private final AddTaskPort addTaskToRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
@Override
public Task handleTaskStarted(TaskStartedEvent taskStartedEvent) throws TaskNotFoundException {
TaskList taskList = TaskList.getTapasTaskList();
return taskList.changeTaskStatusToRunning(taskStartedEvent.getTaskId(),
taskStartedEvent.getServiceProvider());
// retrieve the task based on ID
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(taskStartedEvent.getTaskId()));
// update the status to assigned
Task updatedTask = taskFromRepo.get();
updatedTask.setTaskStatus(new Task.TaskStatus(Task.Status.RUNNING));
if(taskStartedEvent.getServiceProvider().isPresent()){
updatedTask.setProvider(taskStartedEvent.getServiceProvider().get());
}
// save updated task in repo
addTaskToRepositoryPort.addTask(updatedTask);
return updatedTask;
}
}

View File

@ -14,12 +14,8 @@ public class DeleteTaskCommand extends SelfValidating<DeleteTaskCommand> {
@NotNull
private final TaskId taskId;
@NotNull
private final OriginalTaskUri taskUri;
public DeleteTaskCommand(TaskId taskId, OriginalTaskUri taskUri){
public DeleteTaskCommand(TaskId taskId){
this.taskId=taskId;
this.taskUri = taskUri;
this.validateSelf();
}
}

View File

@ -0,0 +1,7 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.Task;
public interface DeleteTaskPort {
void deleteTask(Task.TaskId taskId);
}

View File

@ -1,10 +1,9 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
public interface LoadTaskPort {
Task loadTask(Task.TaskId taskId, TaskList.TaskListName taskListName);
Task loadTask(Task.TaskId taskId);
}

View File

@ -1,11 +0,0 @@
package ch.unisg.tapastasks.tasks.application.port.out;
import ch.unisg.tapastasks.tasks.domain.TaskList;
public interface TaskListLock {
void lockTaskList(TaskList.TaskListName taskListName);
void releaseTaskList(TaskList.TaskListName taskListName);
}

View File

@ -4,11 +4,9 @@ import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListUseCase;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@ -28,34 +26,32 @@ public class AddNewTaskToTaskListService implements AddNewTaskToTaskListUseCase
@Override
public Task addNewTaskToTaskList(AddNewTaskToTaskListCommand command) {
TaskList taskList = TaskList.getTapasTaskList();
Task newTask;
if (command.getOriginalTaskUri().isPresent() && command.getInputData().isPresent()) {
newTask = taskList.addNewTaskWithNameAndTypeAndOriginalTaskUriAndInputData(command.getTaskName(),
newTask = Task.createTaskWithNameAndTypeAndOriginalTaskUriAndInputData(command.getTaskName(),
command.getTaskType(), command.getOriginalTaskUri().get(), command.getInputData().get());
} else if (command.getOriginalTaskUri().isPresent()) {
newTask = taskList.addNewTaskWithNameAndTypeAndOriginalTaskUri(command.getTaskName(),
newTask = Task.createTaskWithNameAndTypeAndOriginalTaskUri(command.getTaskName(),
command.getTaskType(), command.getOriginalTaskUri().get());
} else if (command.getInputData().isPresent()) {
newTask = taskList.addNewTaskWithNameAndTypeAndInputData(command.getTaskName(),
newTask = Task.createTaskWithNameAndTypeAndInputData(command.getTaskName(),
command.getTaskType(), command.getInputData().get());
} else {
newTask = taskList.addNewTaskWithNameAndType(command.getTaskName(), command.getTaskType());
newTask = Task.createTaskWithNameAndType(command.getTaskName(), command.getTaskType());
}
addTaskToRepositoryPort.addTask(newTask);
//Here we are using the application service to emit the domain event to the outside of the bounded context.
//This event should be considered as a light-weight "integration event" to communicate with other services.
//Domain events are usually rather "fat". In our implementation we simplify at this point. In general, it is
//not recommended to emit a domain event via an application service! You should first emit the domain event in
//the core and then the integration event in the application layer.
// TODO What to do with this task list name
if (newTask != null) {
addTaskToRepositoryPort.addTask(newTask);
NewTaskAddedEvent newTaskAdded = new NewTaskAddedEvent(
newTask.getTaskName().getValue(),
taskList.getTaskListName().getValue(),
"tapas-tasks-group1",
newTask.getTaskId().getValue(),
newTask.getTaskType().getValue(),
Objects.isNull(newTask.getInputData()) ? null : newTask.getInputData().getValue()

View File

@ -2,11 +2,12 @@ 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.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.ExternalTaskExecutedEvent;
import ch.unisg.tapastasks.tasks.application.port.out.ExternalTaskExecutedEventHandler;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
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;
@ -18,26 +19,31 @@ import java.util.Optional;
@Transactional
public class CompleteTaskService implements CompleteTaskUseCase {
private final AddTaskPort addTaskToRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
private final ExternalTaskExecutedEventHandler externalTaskExecutedEventHandler;
@Override
public Task completeTask(CompleteTaskCommand command){
TaskList taskList = TaskList.getTapasTaskList();
Optional<Task> updatedTask = taskList.retrieveTaskById(command.getTaskId());
// retrieve the task based on ID
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(command.getTaskId()));
Task newTask = updatedTask.get();
newTask.setOutputData(command.getOutputData());
newTask.setTaskStatus(new TaskStatus(Task.Status.EXECUTED));
Task updatedTask = taskFromRepo.get();
updatedTask.setOutputData(command.getOutputData());
updatedTask.setTaskStatus(new TaskStatus(Task.Status.EXECUTED));
if (newTask.getOriginalTaskUri() != null) {
// save updated task in repo
addTaskToRepositoryPort.addTask(updatedTask);
if (updatedTask.getOriginalTaskUri() != null) {
ExternalTaskExecutedEvent event = new ExternalTaskExecutedEvent(
newTask.getTaskId(),
newTask.getOriginalTaskUri(),
newTask.getOutputData()
updatedTask.getTaskId(),
updatedTask.getOriginalTaskUri(),
updatedTask.getOutputData()
);
externalTaskExecutedEventHandler.handleEvent(event);
}
return newTask;
return updatedTask;
}
}

View File

@ -3,10 +3,12 @@ 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.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.CanTaskBeDeletedPort;
import ch.unisg.tapastasks.tasks.application.port.out.DeleteTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.DeleteTaskEvent;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import jdk.jshell.spi.ExecutionControl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -19,18 +21,20 @@ import java.util.Optional;
@Transactional
public class DeleteTaskService implements DeleteTaskUseCase {
private final DeleteTaskPort deleteTaskFromRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
private final CanTaskBeDeletedPort canTaskBeDeletedPort;
@Override
public Optional<Task> deleteTask(DeleteTaskCommand command){
// retrieve the task based on ID
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(command.getTaskId()));
TaskList taskList = TaskList.getTapasTaskList();
Optional<Task> updatedTask = taskList.retrieveTaskById(command.getTaskId());
if(updatedTask.isPresent() && updatedTask.get().getTaskStatus().getValue().equals(Task.Status.OPEN)){
if(taskFromRepo.isPresent() && taskFromRepo.get().getTaskStatus().getValue().equals(Task.Status.OPEN)){
// If task exists, and it is open then we try deleting it from the roster
if(canTaskBeDeletedPort.canTaskBeDeletedEvent(new DeleteTaskEvent(command.getTaskId().getValue(), command.getTaskUri().getValue()))){
return taskList.deleteTaskById(command.getTaskId());
if(canTaskBeDeletedPort.canTaskBeDeletedEvent(new DeleteTaskEvent(command.getTaskId().getValue()))){
deleteTaskFromRepositoryPort.deleteTask(taskFromRepo.get().getTaskId());
return taskFromRepo;
}
}

View File

@ -1,18 +0,0 @@
package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.springframework.stereotype.Component;
@Component
public class NoOpTaskListLock implements TaskListLock {
@Override
public void lockTaskList(TaskList.TaskListName taskListName) {
//do nothing
}
@Override
public void releaseTaskList(TaskList.TaskListName taskListName) {
//do nothing
}
}

View File

@ -4,7 +4,6 @@ import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListQue
import ch.unisg.tapastasks.tasks.application.port.in.RetrieveTaskFromTaskListUseCase;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@ -22,12 +21,9 @@ public class RetrieveTaskFromTaskListService implements RetrieveTaskFromTaskList
@Override
public Optional<Task> retrieveTaskFromTaskList(RetrieveTaskFromTaskListQuery query) {
TaskList taskList = TaskList.getTapasTaskList();
Optional<Task> task = taskList.retrieveTaskById(query.getTaskId());
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(query.getTaskId()));
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(query.getTaskId(), taskList.getTaskListName()));
return task;
return taskFromRepo;
}
}

View File

@ -2,9 +2,10 @@ 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.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.LoadTaskPort;
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;
@ -16,16 +17,21 @@ import java.util.Optional;
@Transactional
public class TaskAssignedService implements TaskAssignedUseCase {
private final AddTaskPort addTaskToRepositoryPort;
private final LoadTaskPort loadTaskFromRepositoryPort;
@Override
public Task assignTask(TaskAssignedCommand command) {
// retrieve the task based on ID
TaskList taskList = TaskList.getTapasTaskList();
Optional<Task> task = taskList.retrieveTaskById(command.getTaskId());
Optional<Task> taskFromRepo = Optional.ofNullable(loadTaskFromRepositoryPort.loadTask(command.getTaskId()));
// update the status to assigned
Task updatedTask = task.get();
Task updatedTask = taskFromRepo.get();
updatedTask.setTaskStatus(new TaskStatus(Status.ASSIGNED));
// save updated task in repo
addTaskToRepositoryPort.addTask(updatedTask);
return updatedTask;
}
}

View File

@ -2,10 +2,8 @@ package ch.unisg.tapastasks.tasks.domain;
public class DeleteTaskEvent {
public String taskId;
public String taskUri;
public DeleteTaskEvent(String taskId, String taskUri){
public DeleteTaskEvent(String taskId){
this.taskId = taskId;
this.taskUri = taskUri;
}
}

View File

@ -91,7 +91,7 @@ public class Task {
this.outputData = outputData;
}
protected static Task createTaskWithNameAndType(TaskName name, TaskType type) {
public static Task createTaskWithNameAndType(TaskName name, TaskType type) {
return new Task(name, type);
}
@ -100,13 +100,13 @@ public class Task {
return new Task(name, type, originalTaskUri);
}
protected static Task createTaskWithNameAndTypeAndInputData(TaskName name, TaskType type,
InputData inputData) {
public static Task createTaskWithNameAndTypeAndInputData(TaskName name, TaskType type,
InputData inputData) {
return new Task(name, type, inputData);
}
protected static Task createTaskWithNameAndTypeAndOriginalTaskUriAndInputData(TaskName name, TaskType type,
OriginalTaskUri originalTaskUri, InputData inputData) {
public static Task createTaskWithNameAndTypeAndOriginalTaskUriAndInputData(TaskName name, TaskType type,
OriginalTaskUri originalTaskUri, InputData inputData) {
return new Task(name, type, originalTaskUri, inputData);
}

View File

@ -1,152 +0,0 @@
package ch.unisg.tapastasks.tasks.domain;
import lombok.Getter;
import lombok.Value;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
/**This is our aggregate root**/
public class TaskList {
Logger logger = Logger.getLogger(TaskList.class.getName());
@Getter
private final TaskListName taskListName;
@Getter
private final ListOfTasks listOfTasks;
//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
private static final TaskList taskList = new TaskList(new TaskListName("tapas-tasks-group1"));
private TaskList(TaskListName taskListName) {
this.taskListName = taskListName;
this.listOfTasks = new ListOfTasks(new LinkedList<Task>());
}
public static TaskList getTapasTaskList() {
return taskList;
}
//Only the aggregate root is allowed to create new tasks and add them to the task list.
//Note: Here we could add some sophisticated invariants/business rules that the aggregate root checks
public Task addNewTaskWithNameAndType(Task.TaskName name, Task.TaskType type) {
Task newTask = Task.createTaskWithNameAndType(name, type);
this.addNewTaskToList(newTask);
return newTask;
}
public Task addNewTaskWithNameAndTypeAndOriginalTaskUri(Task.TaskName name, Task.TaskType type,
Task.OriginalTaskUri originalTaskUri) {
Task newTask = Task.createTaskWithNameAndTypeAndOriginalTaskUri(name, type, originalTaskUri);
this.addNewTaskToList(newTask);
return newTask;
}
public Task addNewTaskWithNameAndTypeAndInputData(Task.TaskName name, Task.TaskType type,
Task.InputData inputData) {
Task newTask = Task.createTaskWithNameAndTypeAndInputData(name, type, inputData);
this.addNewTaskToList(newTask);
return newTask;
}
public Task addNewTaskWithNameAndTypeAndOriginalTaskUriAndInputData(Task.TaskName name, Task.TaskType type,
Task.OriginalTaskUri originalTaskUri, Task.InputData inputData) {
Task newTask = Task.createTaskWithNameAndTypeAndOriginalTaskUriAndInputData(name, type, originalTaskUri, inputData);
this.addNewTaskToList(newTask);
return newTask;
}
private void addNewTaskToList(Task newTask) {
//Here we would also publish a domain event to other entities in the core interested in this event.
//However, we skip this here as it makes the core even more complex (e.g., we have to implement a light-weight
//domain event publisher and subscribers (see "Implementing Domain-Driven Design by V. Vernon, pp. 296ff).
listOfTasks.value.add(newTask);
String message = "New task created! Id: " + newTask.getTaskId().getValue() +
" | Name: " + newTask.getTaskName().getValue();
if (newTask.getInputData() != null) {
message += " | Input data: " + newTask.getInputData().getValue();
}
logger.log(Level.INFO, message);
logger.log(Level.INFO, "Number of tasks: {0}", listOfTasks.value.size());
}
public Optional<Task> retrieveTaskById(Task.TaskId id) {
for (Task task : listOfTasks.value) {
if (task.getTaskId().getValue().equalsIgnoreCase(id.getValue())) {
return Optional.of(task);
}
}
return Optional.empty();
}
public Optional<Task> 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();
}
public Task changeTaskStatusToAssigned(Task.TaskId id, Optional<Task.ServiceProvider> serviceProvider)
throws TaskNotFoundException {
return changeTaskStatus(id, new Task.TaskStatus(Task.Status.ASSIGNED), serviceProvider, Optional.empty());
}
public Task changeTaskStatusToRunning(Task.TaskId id, Optional<Task.ServiceProvider> serviceProvider)
throws TaskNotFoundException {
return changeTaskStatus(id, new Task.TaskStatus(Task.Status.RUNNING), serviceProvider, Optional.empty());
}
public Task changeTaskStatusToExecuted(Task.TaskId id, Optional<Task.ServiceProvider> serviceProvider,
Optional<Task.OutputData> outputData) throws TaskNotFoundException {
return changeTaskStatus(id, new Task.TaskStatus(Task.Status.EXECUTED), serviceProvider, outputData);
}
private Task changeTaskStatus(Task.TaskId id, Task.TaskStatus status, Optional<Task.ServiceProvider> serviceProvider,
Optional<Task.OutputData> outputData) {
Optional<Task> taskOpt = retrieveTaskById(id);
if (taskOpt.isEmpty()) {
throw new TaskNotFoundException();
}
Task task = taskOpt.get();
task.setTaskStatus(status);
if (serviceProvider.isPresent()) {
task.setProvider(serviceProvider.get());
}
if (outputData.isPresent()) {
task.setOutputData(outputData.get());
}
return task;
}
@Value
public static class TaskListName {
private String value;
}
@Value
public static class ListOfTasks {
private List<Task> value;
}
}

View File

@ -3,7 +3,6 @@ package ch.unisg.tapastasks;
import ch.unisg.tapastasks.tasks.adapter.in.formats.TaskJsonRepresentation;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
@ -42,7 +41,7 @@ public class AddNewTaskToTaskListSystemTest {
then(respTaskId).isNotEmpty();
then(respTaskName).isEqualTo(taskName.getValue());
then(respTaskType).isEqualTo(taskType.getValue());
then(TaskList.getTapasTaskList().getListOfTasks().getValue()).hasSize(1);
//then(TaskList.getTapasTaskList().getListOfTasks().getValue()).hasSize(1);
}
@ -51,7 +50,7 @@ public class AddNewTaskToTaskListSystemTest {
Task.TaskType taskType,
Task.OriginalTaskUri originalTaskUri) throws JSONException {
TaskList.getTapasTaskList().getListOfTasks().getValue().clear();
//TaskList.getTapasTaskList().getListOfTasks().getValue().clear();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", TaskJsonRepresentation.MEDIA_TYPE);

View File

@ -1,7 +1,6 @@
package ch.unisg.tapastasks.tasks.adapter.out.persistence.mongodb;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
@ -45,7 +44,7 @@ public class TaskPersistenceAdapterTest {
);
adapterUnderTest.addTask(testTask);
MongoTaskDocument retrievedDoc = taskRepository.findByTaskId(testTaskId,testTaskListName);
MongoTaskDocument retrievedDoc = taskRepository.findByTaskId(testTaskId);
assertThat(retrievedDoc.taskId).isEqualTo(testTaskId);
assertThat(retrievedDoc.taskName).isEqualTo(testTaskName);

View File

@ -3,10 +3,8 @@ package ch.unisg.tapastasks.tasks.application.service;
import ch.unisg.tapastasks.tasks.application.port.in.AddNewTaskToTaskListCommand;
import ch.unisg.tapastasks.tasks.application.port.out.AddTaskPort;
import ch.unisg.tapastasks.tasks.application.port.out.NewTaskAddedEventPort;
import ch.unisg.tapastasks.tasks.application.port.out.TaskListLock;
import ch.unisg.tapastasks.tasks.domain.NewTaskAddedEvent;
import ch.unisg.tapastasks.tasks.domain.Task;
import ch.unisg.tapastasks.tasks.domain.TaskList;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@ -19,7 +17,6 @@ import static org.assertj.core.api.Assertions.*;
public class AddNewTaskToTaskListServiceTest {
private final AddTaskPort addTaskPort = Mockito.mock(AddTaskPort.class);
private final TaskListLock taskListLock = Mockito.mock(TaskListLock.class);
private final NewTaskAddedEventPort newTaskAddedEventPort = Mockito.mock(NewTaskAddedEventPort.class);
private final AddNewTaskToTaskListService addNewTaskToTaskListService = new AddNewTaskToTaskListService(
newTaskAddedEventPort, addTaskPort);
@ -46,11 +43,6 @@ public class AddNewTaskToTaskListServiceTest {
}
private TaskList givenAnEmptyTaskList(TaskList taskList) {
taskList.getListOfTasks().getValue().clear();
return taskList;
}
private Task givenATaskWithNameAndTypeAndURI(Task.TaskName taskName, Task.TaskType taskType,
Optional<Task.OriginalTaskUri> originalTaskUri) {
Task task = Mockito.mock(Task.class);

View File

@ -11,7 +11,7 @@ public class TaskListTest {
@Test
void addNewTaskToTaskListSuccess() {
TaskList taskList = TaskList.getTapasTaskList();
/*TaskList taskList = TaskList.getTapasTaskList();
taskList.getListOfTasks().getValue().clear();
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task"),
new Task.TaskType("My-Test-Type"));
@ -19,11 +19,12 @@ public class TaskListTest {
assertThat(newTask.getTaskName().getValue()).isEqualTo("My-Test-Task");
assertThat(taskList.getListOfTasks().getValue()).hasSize(1);
assertThat(taskList.getListOfTasks().getValue().get(0)).isEqualTo(newTask);
*/
}
@Test
void retrieveTaskSuccess() {
/*
TaskList taskList = TaskList.getTapasTaskList();
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task2"),
new Task.TaskType("My-Test-Type2"));
@ -31,11 +32,12 @@ public class TaskListTest {
Task retrievedTask = taskList.retrieveTaskById(newTask.getTaskId()).get();
assertThat(retrievedTask).isEqualTo(newTask);
*/
}
@Test
void retrieveTaskFailure() {
/*
TaskList taskList = TaskList.getTapasTaskList();
Task newTask = taskList.addNewTaskWithNameAndType(new Task.TaskName("My-Test-Task3"),
new Task.TaskType("My-Test-Type3"));
@ -45,5 +47,7 @@ public class TaskListTest {
Optional<Task> retrievedTask = taskList.retrieveTaskById(fakeId);
assertThat(retrievedTask.isPresent()).isFalse();
*/
}
}