Merge pull request #18 from SCS-ASSE-FS21-Group1/executor1

Executor1
This commit is contained in:
reynisson 2021-10-13 16:30:04 +02:00 committed by GitHub
commit ee1f504a15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 547 additions and 31 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true

View File

@ -9,9 +9,9 @@
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>ch.unisg</groupId> <groupId>ch.unisg</groupId>
<artifactId>executor1</artifactId> <artifactId>executorBase</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>executor1</name> <name>executorBase</name>
<description>Demo project for Spring Boot</description> <description>Demo project for Spring Boot</description>
<properties> <properties>
<java.version>11</java.version> <java.version>11</java.version>
@ -30,6 +30,10 @@
<scope>runtime</scope> <scope>runtime</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
@ -40,6 +44,18 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,4 +1,4 @@
package ch.unisg.executor1; package ch.unisg.executorBase;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -0,0 +1,29 @@
package ch.unisg.executorBase.common;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
public class SelfValidating<T> {
private Validator validator;
public SelfValidating() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
/**
* Evaluates all Bean Validations on the attributes of this
* instance.
*/
protected void validateSelf() {
Set<ConstraintViolation<T>> violations = validator.validate((T) this);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}

View File

@ -0,0 +1,36 @@
package ch.unisg.executorBase.executor.adapter.in.web;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
import ch.unisg.executorBase.executor.domain.ExecutorType;
@RestController
public class TaskAvailableController {
private final TaskAvailableUseCase taskAvailableUseCase;
public TaskAvailableController(TaskAvailableUseCase taskAvailableUseCase) {
this.taskAvailableUseCase = taskAvailableUseCase;
}
@GetMapping(path = "/newtask/{taskType}")
public ResponseEntity<String> retrieveTaskFromTaskList(@PathVariable("taskType") String taskType) {
if (ExecutorType.contains(taskType.toUpperCase())) {
TaskAvailableCommand command = new TaskAvailableCommand(
ExecutorType.valueOf(taskType.toUpperCase()));
taskAvailableUseCase.newTaskAvailable(command);
}
// Add the content type as a response header
HttpHeaders responseHeaders = new HttpHeaders();
return new ResponseEntity<>("OK", responseHeaders, HttpStatus.OK);
}
}

View File

@ -0,0 +1,58 @@
package ch.unisg.executorBase.executor.adapter.out.web;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashMap;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEventPort;
import ch.unisg.executorBase.executor.domain.ExecutionFinishedEvent;
public class ExecutionFinishedEventAdapter implements ExecutionFinishedEventPort {
//This is the base URI of the service interested in this event (in my setup, running locally as separate Spring Boot application)
String server = "http://127.0.0.1:8082";
@Override
public void publishExecutionFinishedEvent(ExecutionFinishedEvent event) {
///Here we would need to work with DTOs in case the payload of calls becomes more complex
var values = new HashMap<String, String>() {{
put("result",event.getResult());
put("status",event.getStatus());
}};
var objectMapper = new ObjectMapper();
String requestBody = null;
try {
requestBody = objectMapper.writeValueAsString(values);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/task/"+event.getTaskID()))
.PUT(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
/** Needs the other service running
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
**/
System.out.println("Finish execution event sent with result:" + event.getResult());
}
}

View File

@ -0,0 +1,45 @@
package ch.unisg.executorBase.executor.adapter.out.web;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.executorBase.executor.application.port.out.GetAssignmentPort;
import ch.unisg.executorBase.executor.domain.ExecutorType;
import ch.unisg.executorBase.executor.domain.Task;
@Component
@Primary
public class GetAssignmentAdapter implements GetAssignmentPort {
//This is the base URI of the service interested in this event (in my setup, running locally as separate Spring Boot application)
String server = "http://127.0.0.1:8082";
@Override
public Task getAssignment(ExecutorType executorType) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/assignment/" + executorType))
.GET()
.build();
/** Needs the other service running
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
**/
// TODO return null or a new Task here depending on the response of the http call
return new Task("123");
}
}

View File

@ -0,0 +1,62 @@
package ch.unisg.executorBase.executor.adapter.out.web;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.HashMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
import ch.unisg.executorBase.executor.domain.ExecutorType;
@Component
@Primary
public class NotifyExecutorPoolAdapter implements NotifyExecutorPoolPort {
//This is the base URI of the service interested in this event (in my setup, running locally as separate Spring Boot application)
String server = "http://127.0.0.1:8083";
@Override
public boolean notifyExecutorPool(String ip, int port, ExecutorType executorType) {
var values = new HashMap<String, String>() {{
put("ip", ip);
put("port", Integer.toString(port));
put("executorType", executorType.toString());
}};
var objectMapper = new ObjectMapper();
String requestBody = null;
try {
requestBody = objectMapper.writeValueAsString(values);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(server+"/executor/new/"))
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
/** Needs the other service running
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
**/
// TODO return true or false depending on result of http request;
return true;
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
package ch.unisg.executorBase.executor.application.port.out;
import ch.unisg.executorBase.executor.domain.ExecutorType;
import ch.unisg.executorBase.executor.domain.Task;
public interface GetAssignmentPort {
Task getAssignment(ExecutorType executorType);
}

View File

@ -0,0 +1,7 @@
package ch.unisg.executorBase.executor.application.port.out;
import ch.unisg.executorBase.executor.domain.ExecutorType;
public interface NotifyExecutorPoolPort {
boolean notifyExecutorPool(String ip, int port, ExecutorType executorType);
}

View File

@ -0,0 +1,15 @@
package ch.unisg.executorBase.executor.application.service;
import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
import ch.unisg.executorBase.executor.domain.ExecutorType;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class NotifyExecutorPoolService {
private final NotifyExecutorPoolPort notifyExecutorPoolPort;
public boolean notifyExecutorPool(String ip, int port, ExecutorType executorType) {
return notifyExecutorPoolPort.notifyExecutorPool(ip, port, executorType);
}
}

View File

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

View File

@ -0,0 +1,85 @@
package ch.unisg.executorBase.executor.domain;
import ch.unisg.executorBase.executor.application.port.out.ExecutionFinishedEventPort;
import ch.unisg.executorBase.executor.application.port.out.GetAssignmentPort;
import ch.unisg.executorBase.executor.application.port.out.NotifyExecutorPoolPort;
import java.util.concurrent.TimeUnit;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import ch.unisg.executorBase.executor.adapter.out.web.ExecutionFinishedEventAdapter;
import ch.unisg.executorBase.executor.adapter.out.web.GetAssignmentAdapter;
import ch.unisg.executorBase.executor.adapter.out.web.NotifyExecutorPoolAdapter;
import ch.unisg.executorBase.executor.application.service.NotifyExecutorPoolService;
import lombok.Getter;
public abstract class ExecutorBase {
@Getter
private String ip;
@Getter
private ExecutorType executorType;
@Getter
private int port;
@Getter
private ExecutorStatus status;
// TODO Violation of the Dependency Inversion Principle?, but we havn't really got a better solutions to send a http request / access a service from a domain model
// TODO I guess we can somehow autowire this but I don't know why it's not working :D
private final NotifyExecutorPoolPort notifyExecutorPoolPort = new NotifyExecutorPoolAdapter();
private final NotifyExecutorPoolService notifyExecutorPoolService = new NotifyExecutorPoolService(notifyExecutorPoolPort);
private final GetAssignmentPort getAssignmentPort = new GetAssignmentAdapter();
private final ExecutionFinishedEventPort executionFinishedEventPort = new ExecutionFinishedEventAdapter();
public ExecutorBase(ExecutorType executorType) {
System.out.println("Starting Executor");
// TODO set this automaticly
this.ip = "localhost";
this.port = 8084;
this.executorType = executorType;
this.status = ExecutorStatus.STARTING_UP;
if(!notifyExecutorPoolService.notifyExecutorPool(this.ip, this.port, this.executorType)) {
System.exit(0);
} else {
System.out.println(true);
this.status = ExecutorStatus.IDLING;
getAssignment();
}
}
// public static ExecutorBase getExecutor() {
// return executor;
// }
public void getAssignment() {
Task newTask = getAssignmentPort.getAssignment(this.getExecutorType());
if (newTask != null) {
this.executeTask(newTask);
} else {
this.status = ExecutorStatus.IDLING;
}
}
private void executeTask(Task task) {
System.out.println("Starting execution");
this.status = ExecutorStatus.EXECUTING;
task.setResult(execution());
executionFinishedEventPort.publishExecutionFinishedEvent(new ExecutionFinishedEvent(task.getTaskID(), task.getResult(), "SUCCESS"));
System.out.println("Finish execution");
getAssignment();
}
protected abstract String execution();
}

View File

@ -0,0 +1,7 @@
package ch.unisg.executorBase.executor.domain;
public enum ExecutorStatus {
STARTING_UP,
EXECUTING,
IDLING,
}

View File

@ -0,0 +1,18 @@
package ch.unisg.executorBase.executor.domain;
public enum ExecutorType {
ADDITION, ROBOT;
public static boolean contains(String test) {
for (ExecutorType x : ExecutorType.values()) {
if (x.name().equals(test)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,20 @@
package ch.unisg.executorBase.executor.domain;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
public class Task {
@Getter
private String taskID;
@Getter
@Setter
private String result;
public Task(String taskID) {
this.taskID = taskID;
}
}

View File

@ -1,10 +1,10 @@
package ch.unisg.executor1; package ch.unisg.executorBase;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest @SpringBootTest
class Executor1ApplicationTests { class executorBaseApplicationTests {
@Test @Test
void contextLoads() { void contextLoads() {

View File

@ -1,12 +0,0 @@
package ch.unisg.executor1;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/")
public String index() {
return "Hello World! Executor1";
}
}

View File

@ -40,6 +40,11 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>ch.unisg</groupId>
<artifactId>executorBase</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -3,11 +3,14 @@ package ch.unisg.executor2;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import ch.unisg.executor2.executor.domain.Executor;
@SpringBootApplication @SpringBootApplication
public class Executor2Application { public class Executor2Application {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Executor2Application.class, args); SpringApplication.run(Executor2Application.class, args);
Executor.getExecutor();
} }
} }

View File

@ -1,12 +0,0 @@
package ch.unisg.executor2;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/")
public String index() {
return "Hello World! Executor2";
}
}

View File

@ -0,0 +1,28 @@
package ch.unisg.executor2.executor.application.service;
import org.springframework.stereotype.Component;
import ch.unisg.executor2.executor.domain.Executor;
import ch.unisg.executorBase.executor.application.port.in.TaskAvailableCommand;
import ch.unisg.executorBase.executor.application.port.in.TaskAvailableUseCase;
import ch.unisg.executorBase.executor.domain.ExecutorStatus;
import lombok.RequiredArgsConstructor;
import javax.transaction.Transactional;
@RequiredArgsConstructor
@Component
@Transactional
public class TaskAvailableService implements TaskAvailableUseCase {
@Override
public void newTaskAvailable(TaskAvailableCommand command) {
Executor executor = Executor.getExecutor();
if (executor.getExecutorType() == command.getTaskType() &&
executor.getStatus() == ExecutorStatus.IDLING) {
executor.getAssignment();
}
}
}

View File

@ -0,0 +1,36 @@
package ch.unisg.executor2.executor.domain;
import java.util.concurrent.TimeUnit;
import ch.unisg.executorBase.executor.domain.ExecutorBase;
import ch.unisg.executorBase.executor.domain.ExecutorType;
public class Executor extends ExecutorBase {
private static final Executor executor = new Executor(ExecutorType.ADDITION);
public static Executor getExecutor() {
return executor;
}
private Executor(ExecutorType executorType) {
super(executorType);
}
@Override
protected
String execution() {
int a = 20;
int b = 20;
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
int result = a + b;
return Integer.toString(result);
}
}