[New] REST and WS API extends the Activity JSON with ExecutionPolicy data

This commit is contained in:
Robert von Burg 2022-01-19 19:41:08 +01:00
parent f28e3d7683
commit a596793380
6 changed files with 95 additions and 123 deletions

View File

@ -159,6 +159,7 @@ public class Tags {
public static final String WITH_VERSION = "withVersion";
public static final String PARAMS = "params";
public static final String OPERATION = "operation";
public static final String EXECUTION_POLICY = "executionPolicy";
public static final String APP_VERSION = "appVersion";
public static final String SYSTEM_STATE = "systemState";

View File

@ -2,6 +2,7 @@ package li.strolch.rest.endpoint;
import static li.strolch.execution.ExecutionHandler.PARAM_STATE;
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
import static li.strolch.rest.model.ToJsonHelper.inExecutionActivityToJson;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
@ -18,13 +19,13 @@ import li.strolch.execution.service.*;
import li.strolch.model.Locator;
import li.strolch.model.State;
import li.strolch.model.activity.Activity;
import li.strolch.model.json.StrolchElementToJsonVisitor;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.rest.RestfulStrolchComponent;
import li.strolch.rest.helper.ResponseUtil;
import li.strolch.service.LocatorArgument;
import li.strolch.service.StringMapArgument;
import li.strolch.service.api.Service;
import li.strolch.service.api.ServiceArgument;
import li.strolch.service.api.ServiceHandler;
import li.strolch.service.api.ServiceResult;
@ -47,8 +48,6 @@ public class ControlResource {
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
StrolchElementToJsonVisitor visitor = new StrolchElementToJsonVisitor().withVersion().withLocator();
try (StrolchTransaction tx = openTx(cert, realm)) {
ExecutionHandler executionHandler = tx.getContainer().getComponent(ExecutionHandler.class);
JsonArray activitiesJ = executionHandler.getActiveActivitiesLocator(realm) //
@ -56,7 +55,7 @@ public class ControlResource {
.map(locator -> tx.getActivityBy(locator.get(1), locator.get(2))) //
.filter(Objects::nonNull) //
.sorted(Comparator.comparing(Activity::getId)) //
.map(activity -> activity.accept(visitor)) //
.map(activity -> activity.accept(inExecutionActivityToJson(tx.getRealmName(), executionHandler))) //
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
ExecutionHandlerState state = executionHandler.getState(tx.getRealmName());
@ -69,7 +68,6 @@ public class ControlResource {
public Response clearAllActivities(@Context HttpServletRequest request, @QueryParam("realm") String realm) {
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
RestfulStrolchComponent instance = RestfulStrolchComponent.getInstance();
ClearAllCurrentExecutionsService svc = new ClearAllCurrentExecutionsService();
@ -77,7 +75,6 @@ public class ControlResource {
arg.realm = realm;
ServiceResult svcResult = instance.getServiceHandler().doService(cert, svc, arg);
return ResponseUtil.toResponse(svcResult);
}
@ -114,9 +111,7 @@ public class ControlResource {
@QueryParam("type") String type, @QueryParam("id") String id, @QueryParam("state") String stateS) {
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
Locator locator = Activity.locatorFor(type, id);
RestfulStrolchComponent instance = RestfulStrolchComponent.getInstance();
StartActivityExecutionService svc = new StartActivityExecutionService();
@ -124,7 +119,6 @@ public class ControlResource {
arg.locator = locator;
ServiceResult svcResult = instance.getServiceHandler().doService(cert, svc, arg);
return ResponseUtil.toResponse(svcResult);
}
@ -142,76 +136,21 @@ public class ControlResource {
ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler();
ServiceResult svcResult;
Service<LocatorArgument, ServiceResult> svc;
switch (state) {
case CREATED: {
SetActionToCreatedService svc = new SetActionToCreatedService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case PLANNED: {
SetActionToPlannedService svc = new SetActionToPlannedService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case EXECUTION: {
ExecuteActionService svc = new ExecuteActionService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case WARNING: {
SetActionToWarningService svc = new SetActionToWarningService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case ERROR: {
SetActionToErrorService svc = new SetActionToErrorService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case STOPPED: {
SetActionToStoppedService svc = new SetActionToStoppedService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case EXECUTED: {
SetActionToExecutedService svc = new SetActionToExecutedService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
case CLOSED: {
SetActionToClosedService svc = new SetActionToClosedService();
svcResult = serviceHandler.doService(cert, svc, arg);
break;
}
default:
throw new UnsupportedOperationException("Unhandled state " + state);
case CREATED -> svc = new SetActionToCreatedService();
case PLANNED -> svc = new SetActionToPlannedService();
case EXECUTION -> svc = new ExecuteActionService();
case WARNING -> svc = new SetActionToWarningService();
case ERROR -> svc = new SetActionToErrorService();
case STOPPED -> svc = new SetActionToStoppedService();
case EXECUTED -> svc = new SetActionToExecutedService();
case CLOSED -> svc = new SetActionToClosedService();
default -> throw new UnsupportedOperationException("Unhandled state " + state);
}
svcResult = serviceHandler.doService(cert, svc, arg);
return ResponseUtil.toResponse(svcResult);
}
@ -221,9 +160,7 @@ public class ControlResource {
@QueryParam("type") String type, @QueryParam("id") String id) {
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
RestfulStrolchComponent instance = RestfulStrolchComponent.getInstance();
Locator locator = Activity.locatorFor(type, id);
RemoveActivityFromExecutionService svc = new RemoveActivityFromExecutionService();
@ -232,7 +169,6 @@ public class ControlResource {
arg.locator = locator;
ServiceResult svcResult = instance.getServiceHandler().doService(cert, svc, arg);
return ResponseUtil.toResponse(svcResult);
}
}

View File

@ -0,0 +1,32 @@
package li.strolch.rest.model;
import static li.strolch.model.Tags.Json.EXECUTION_POLICY;
import li.strolch.execution.Controller;
import li.strolch.execution.ExecutionHandler;
import li.strolch.execution.policy.ExecutionPolicy;
import li.strolch.model.json.StrolchElementToJsonVisitor;
public class ToJsonHelper {
public static StrolchElementToJsonVisitor inExecutionActivityToJson(String realmName,
ExecutionHandler executionHandler) {
StrolchElementToJsonVisitor visitor = new StrolchElementToJsonVisitor().withVersion().withLocator();
return visitor.actionHook((action, actionJ) -> {
if (action.inCreatedPhase()) {
actionJ.addProperty(EXECUTION_POLICY, "-");
return;
}
Controller controller = executionHandler.getController(realmName, action.getRootElement().getLocator());
if (controller == null) {
actionJ.addProperty(EXECUTION_POLICY, "-");
return;
}
ExecutionPolicy executionPolicy = controller.getExecutionPolicy(action.getLocator());
actionJ.addProperty(EXECUTION_POLICY, executionPolicy == null ? "-" : executionPolicy.getClass().getName());
});
}
}

View File

@ -1,9 +1,9 @@
package li.strolch.websocket;
import static li.strolch.model.StrolchModelConstants.ROLE_STROLCH_ADMIN;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.rest.StrolchRestfulConstants.MSG;
import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM;
import static li.strolch.model.StrolchModelConstants.ROLE_STROLCH_ADMIN;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessage;
import static li.strolch.utils.helper.StringHelper.*;
@ -17,8 +17,8 @@ import java.util.Map;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.ObserverHandler;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.exception.StrolchNotAuthenticatedException;
import li.strolch.model.Tags;
import li.strolch.privilege.model.Certificate;
@ -31,18 +31,18 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
public static final Logger logger = LoggerFactory.getLogger(WebSocketClient.class);
private final StrolchSessionHandler sessionHandler;
private final ComponentContainer container;
private final Session session;
private final EndpointConfig config;
private final String remoteIp;
private final Map<String, WebSocketObserverHandler> observerHandlersByRealm;
protected final StrolchSessionHandler sessionHandler;
protected final StrolchAgent agent;
protected final Session session;
protected final EndpointConfig config;
protected final String remoteIp;
protected final Map<String, WebSocketObserverHandler> observerHandlersByRealm;
private Certificate certificate;
protected Certificate certificate;
public WebSocketClient(ComponentContainer container, Session session, EndpointConfig config) {
this.container = container;
this.sessionHandler = container.getComponent(StrolchSessionHandler.class);
public WebSocketClient(StrolchAgent agent, Session session, EndpointConfig config) {
this.agent = agent;
this.sessionHandler = agent.getComponent(StrolchSessionHandler.class);
this.session = session;
this.config = config;
this.remoteIp = WebSocketRemoteIp.get();
@ -62,27 +62,20 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
logger.info("Handling message " + msgType);
switch (msgType) {
case "Authenticate":
handleAuthenticate(jsonObject);
break;
case "ObserverRegister":
case "Authenticate" -> handleAuthenticate(jsonObject);
case "ObserverRegister" -> {
assertAuthenticated(msgType);
handleRegister(jsonObject);
break;
case "ObserverUnregister":
}
case "ObserverUnregister" -> {
assertAuthenticated(msgType);
handleUnregister(jsonObject);
break;
default:
logger.error("Unhandled Event msgType: " + msgType);
}
default -> logger.error("Unhandled Event msgType: " + msgType);
}
}
public void assertAuthenticated(String type) {
if (this.certificate == null) {
logger.error("Received " + type + " request, but not yet authed!");
close(CloseReason.CloseCodes.PROTOCOL_ERROR, "Not yet authed!");
@ -92,8 +85,8 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
try {
this.sessionHandler.validate(this.certificate, this.remoteIp);
} catch (RuntimeException e) {
logger.error("Received " + type + " request, but authentication is not valid anymore: " + ExceptionHelper
.getExceptionMessage(e));
logger.error("Received " + type + " request, but authentication is not valid anymore: "
+ ExceptionHelper.getExceptionMessage(e));
close(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "Not yet authed!");
}
}
@ -122,15 +115,16 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
JsonObject params = jsonObject.get(PARAMS).getAsJsonObject();
this.observerHandlersByRealm.computeIfAbsent(objectType, s -> {
ObserverHandler observerHandler = this.container.getRealm(realm).getObserverHandler();
return getWebSocketObserverHandler(observerHandler);
ObserverHandler observerHandler = this.agent.getRealm(realm).getObserverHandler();
return getWebSocketObserverHandler(realm, observerHandler);
}).register(objectType, type, params);
logger.info(
this.certificate.getUsername() + " registered for " + objectType + " " + type + " params: " + params);
String username = this.certificate.getUsername();
logger.info(username + " registered for " + objectType + " " + type + " params: " + params);
}
protected WebSocketObserverHandler getWebSocketObserverHandler(ObserverHandler observerHandler) {
return new WebSocketObserverHandler(observerHandler, this);
protected WebSocketObserverHandler getWebSocketObserverHandler(String realm, ObserverHandler observerHandler) {
return new WebSocketObserverHandler(this.agent, realm, observerHandler, this);
}
private void handleUnregister(JsonObject jsonObject) {
@ -148,9 +142,8 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
}
private void handleAuthenticate(JsonObject jsonObject) {
if (!jsonObject.has("authToken") || !jsonObject.has("username")) {
logger.error("Received invalid authentication request: " + jsonObject.toString());
logger.error("Received invalid authentication request: " + jsonObject);
close(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "Invalid authentication");
return;
}
@ -159,7 +152,7 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
String username = jsonObject.get("username").getAsString();
if (authToken.isEmpty() || username.isEmpty()) {
logger.error("Received invalid authentication request: " + jsonObject.toString());
logger.error("Received invalid authentication request: " + jsonObject);
close(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "Invalid authentication");
return;
}
@ -204,9 +197,7 @@ public class WebSocketClient implements MessageHandler.Whole<String> {
} catch (IOException e) {
logger.error("Failed to close client after invalid authentication!", e);
}
this.observerHandlersByRealm.keySet().forEach(realm -> {
this.observerHandlersByRealm.get(realm).unregisterAll();
});
this.observerHandlersByRealm.keySet().forEach(realm -> this.observerHandlersByRealm.get(realm).unregisterAll());
}
public void onError(Throwable t) {

View File

@ -4,18 +4,18 @@ import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.rest.RestfulStrolchComponent;
@ServerEndpoint("/websocket/strolch/observer")
public class WebSocketEndpoint {
private ConcurrentHashMap<Session, WebSocketClient> clientMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Session, WebSocketClient> clientMap = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
ComponentContainer container = RestfulStrolchComponent.getInstance().getContainer();
WebSocketClient webSocketClient = new WebSocketClient(container, session, config);
StrolchAgent agent = RestfulStrolchComponent.getInstance().getAgent();
WebSocketClient webSocketClient = new WebSocketClient(agent, session, config);
this.clientMap.put(session, webSocketClient);
session.addMessageHandler(webSocketClient);
}

View File

@ -3,6 +3,7 @@ package li.strolch.websocket;
import static li.strolch.model.Tags.Json.*;
import static li.strolch.rest.StrolchRestfulConstants.DATA;
import static li.strolch.rest.StrolchRestfulConstants.MSG;
import static li.strolch.rest.model.ToJsonHelper.inExecutionActivityToJson;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessage;
import static li.strolch.utils.helper.StringHelper.DASH;
@ -16,6 +17,8 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import li.strolch.agent.api.Observer;
import li.strolch.agent.api.ObserverHandler;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.execution.ExecutionHandler;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Tags;
import li.strolch.model.json.StrolchElementToJsonVisitor;
@ -28,13 +31,18 @@ public class WebSocketObserverHandler implements Observer {
protected static final Logger logger = LoggerFactory.getLogger(WebSocketObserverHandler.class);
protected final StrolchAgent agent;
protected final String realmName;
protected ObserverHandler observerHandler;
protected WebSocketClient client;
protected MapOfSets<String, String> observedTypes;
protected Map<String, JsonObject> params;
public WebSocketObserverHandler(ObserverHandler observerHandler, WebSocketClient client) {
public WebSocketObserverHandler(StrolchAgent agent, String realmName, ObserverHandler observerHandler,
WebSocketClient client) {
this.agent = agent;
this.realmName = realmName;
this.observerHandler = observerHandler;
this.client = client;
this.observedTypes = new MapOfSets<>();
@ -113,7 +121,11 @@ public class WebSocketObserverHandler implements Observer {
protected JsonObject toJson(StrolchRootElement e) {
StrolchElementToJsonVisitor visitor = new StrolchElementToJsonVisitor();
StrolchElementToJsonVisitor visitor;
if (e.isActivity())
visitor = inExecutionActivityToJson(this.realmName, this.agent.getComponent(ExecutionHandler.class));
else
visitor = new StrolchElementToJsonVisitor();
JsonObject params = this.params.get(e.getType());
if (params == null)