diff --git a/CHANGELOG.md b/CHANGELOG.md index 8067f4fa..a6468025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,18 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [7.1.0] - 2021-06-29 + +### Added +- Minor Change: New Feature: Camel Support +- Minor Change: New API POST /api/ui/route/error caches error messages of the routes until they are fetched by the corresponding GET API (takes every String as RequestBody) +- Minor Change: New API GET /api/ui/route/error all intermediate errors of the routes can be queried, once queried these are deleted in the backend (only cached until GET API called) + +### Dependency Maintenance +- Upgrade: org.springframework.security:spring-security-test 5.5.0 -> 5.5.1 +- Upgrade: org.postgresql:postgresql 42.2.20 -> 42.2.22 +- Upgrade: org.springframework.boot:spring-boot-starter-parent 2.5.0 -> 2.5.2 + ## [7.0.0] - 2021-05-26 ### Major Changes - Remove 20 unused APIs (= 1.150 lines of code) (unused by ConfigManager-UI project) diff --git a/Dockerfile b/Dockerfile index a24f6d55..c739181f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ LABEL org.opencontainers.image.source="https://github.com/International-Data-Spa RUN mkdir /app -COPY --from=maven /tmp/target/*.jar /app/configurationmanager-7.0.0.jar +COPY --from=maven /tmp/target/*.jar /app/configurationmanager-7.1.0.jar WORKDIR /app/ -ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-jar","configurationmanager-7.0.0.jar"] +ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-jar","configurationmanager-7.1.0.jar"] diff --git a/pom.xml b/pom.xml index b33fc43d..a73bf52b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,13 @@ org.springframework.boot spring-boot-starter-parent - 2.5.0 + 2.5.2 de.fraunhofer.isst configurationmanager - 7.0.0 + 7.1.0 Configuration Manager Configuration Manager developed by the Fraunhofer ISST @@ -149,7 +149,7 @@ org.springframework.security spring-security-test - 5.5.0 + 5.5.1 test @@ -209,9 +209,16 @@ org.postgresql postgresql - 42.2.20 + 42.2.22 + + + + org.apache.velocity + velocity + 1.7 + diff --git a/src/main/java/de/fraunhofer/isst/configmanager/ConfigmanagerApplication.java b/src/main/java/de/fraunhofer/isst/configmanager/ConfigmanagerApplication.java index 864bd802..175ad0e2 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/ConfigmanagerApplication.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/ConfigmanagerApplication.java @@ -27,7 +27,7 @@ @EnableScheduling @SpringBootApplication public class ConfigmanagerApplication { - public static final String CURRENT_VERSION = "7.0.0"; + public static final String CURRENT_VERSION = "7.1.0"; public static void main(final String[] args) { if (log.isInfoEnabled()) { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/api/AppRouteApi.java b/src/main/java/de/fraunhofer/isst/configmanager/api/AppRouteApi.java index 68b1f952..82f16602 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/api/AppRouteApi.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/api/AppRouteApi.java @@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.net.URI; @@ -73,4 +74,14 @@ ResponseEntity getEndpointInformation(@RequestParam(value = "routeId") U @ApiResponse(responseCode = "200", description = "Returns the list of the app routes") @ApiResponse(responseCode = "500", description = "Internal server error") ResponseEntity getAppRoutes(); + + @PostMapping(value = "/route/error", produces = "application/ld+json") + @Operation(summary = "Save route related errors in the ConfigManager-backend") + @ApiResponse(responseCode = "200", description = "Saved Route-Error in ConfigManager-backend.") + ResponseEntity setRouteError(@RequestBody String policy); + + @GetMapping(value = "/route/error", produces = "application/ld+json") + @Operation(summary = "Get new route related errors") + @ApiResponse(responseCode = "200", description = "Loaded and returned cached Route-Errors.") + ResponseEntity getRouteErrors(); } diff --git a/src/main/java/de/fraunhofer/isst/configmanager/api/controller/AppRouteController.java b/src/main/java/de/fraunhofer/isst/configmanager/api/controller/AppRouteController.java index cb45b75b..a0351095 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/api/controller/AppRouteController.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/api/controller/AppRouteController.java @@ -26,6 +26,8 @@ import java.io.IOException; import java.net.URI; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.stream.Collectors; /** * The api class implements the AppRouteApi and offers the possibilities to manage @@ -44,6 +46,8 @@ public class AppRouteController implements AppRouteApi { transient RouteDeployMethodRepository routeDeployMethodRepository; transient ObjectMapper objectMapper; + LinkedList routeErrors = new LinkedList<>(); + @Autowired public AppRouteController(final ConfigModelService configModelService, final AppRouteService appRouteService, @@ -399,4 +403,20 @@ private void updateDeployMethodFromRoutes(final RouteDeployMethod routeDeployMet configModelService.saveState(); } } + + @Override + public ResponseEntity setRouteError(String routeError) { + if (routeErrors.size() >= 100) { + routeErrors.remove(0); + } + + routeErrors.add(routeError); + + return ResponseEntity.ok("Saved Route-Error in ConfigManager-backend."); + } + + @Override + public ResponseEntity getRouteErrors() { + return ResponseEntity.ok(routeErrors.stream().collect(Collectors.joining(",", "[", "]"))); + } } diff --git a/src/main/java/de/fraunhofer/isst/configmanager/api/service/AppRouteService.java b/src/main/java/de/fraunhofer/isst/configmanager/api/service/AppRouteService.java index 255d3e40..ba0cfff0 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/api/service/AppRouteService.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/api/service/AppRouteService.java @@ -17,7 +17,12 @@ import de.fraunhofer.isst.configmanager.data.repositories.CustomAppRepository; import de.fraunhofer.isst.configmanager.data.repositories.EndpointInformationRepository; import de.fraunhofer.isst.configmanager.data.repositories.RouteDeployMethodRepository; +import de.fraunhofer.isst.configmanager.util.camel.RouteManager; +import de.fraunhofer.isst.configmanager.util.camel.exceptions.RouteCreationException; +import de.fraunhofer.isst.configmanager.util.camel.exceptions.RouteDeletionException; import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -35,31 +40,18 @@ @Slf4j @Service @Transactional +@AllArgsConstructor(onConstructor = @__(@Autowired)) @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) public class AppRouteService { transient ConfigModelService configModelService; transient EndpointService endpointService; transient ResourceService resourceService; + transient RouteManager routeManager; transient RouteDeployMethodRepository routeDeployMethodRepository; transient EndpointInformationRepository endpointInformationRepository; transient CustomAppRepository customAppRepository; - @Autowired - public AppRouteService(final ConfigModelService configModelService, - final RouteDeployMethodRepository routeDeployMethodRepository, - final EndpointInformationRepository endpointInformationRepository, - final CustomAppRepository customAppRepository, - final EndpointService endpointService, - final ResourceService resourceService) { - this.configModelService = configModelService; - this.routeDeployMethodRepository = routeDeployMethodRepository; - this.endpointInformationRepository = endpointInformationRepository; - this.customAppRepository = customAppRepository; - this.endpointService = endpointService; - this.resourceService = resourceService; - } - /** * This method creates an app route. * @@ -112,6 +104,13 @@ public boolean deleteAppRoute(final URI routeId) { deleted = configModelService.getConfigModel().getAppRoute().remove(appRoute); if (deleted) { + try { + routeManager.deleteRoute(appRoute); + } catch (RouteDeletionException e) { + if (log.isErrorEnabled()){ + log.error(e.getMessage(), e); + } + } configModelService.saveState(); } } @@ -134,7 +133,7 @@ public AppRoute getAppRoute(final URI routeId) { * @return list of app routes */ public List getAppRoutes() { - return (List) configModelService.getConfigModel().getAppRoute(); + return configModelService.getConfigModel().getAppRoute(); } /** @@ -219,6 +218,15 @@ public RouteStep createAppRouteStep(final URI routeId, final URI startId, ._appRouteEnd_(Util.asList(endpoint)) ._appRouteOutput_(Util.asList(resourceImpl)) .build(); + + // Creating camel route + try { + routeManager.createAndDeployXMLRoute(configModelService.getConfigModel(), appRouteImpl); + } catch (RouteCreationException e) { + if (log.isErrorEnabled()){ + log.error(e.getMessage(), e); + } + } } else { routeStep = new RouteStepBuilder()._routeDeployMethod_(deployMethod) ._appRouteStart_(Util.asList(startEndpoint)) diff --git a/src/main/java/de/fraunhofer/isst/configmanager/connector/dataspaceconnector/util/DataspaceConnectorRouteConfigurer.java b/src/main/java/de/fraunhofer/isst/configmanager/connector/dataspaceconnector/util/DataspaceConnectorRouteConfigurer.java new file mode 100644 index 00000000..500e42d7 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/connector/dataspaceconnector/util/DataspaceConnectorRouteConfigurer.java @@ -0,0 +1,76 @@ +package de.fraunhofer.isst.configmanager.connector.dataspaceconnector.util; + +import de.fraunhofer.iais.eis.AppRoute; +import de.fraunhofer.iais.eis.ConnectorEndpoint; +import de.fraunhofer.iais.eis.GenericEndpoint; +import lombok.AccessLevel; +import lombok.Setter; +import lombok.experimental.FieldDefaults; +import org.apache.commons.codec.binary.Base64; +import org.apache.velocity.VelocityContext; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; + +/** + * Utility class for configuring Camel routes for the Dataspace Connector. + */ +@Component +@FieldDefaults(level = AccessLevel.PRIVATE) +public class DataspaceConnectorRouteConfigurer { + + /** + * Username for the Dataspace Connector. + */ + @Setter + @Value("${dataspace.connector.api.username}") + String dataSpaceConnectorApiUsername; + + /** + * Password for the Dataspace Connector. + */ + @Setter + @Value("${dataspace.connector.api.password}") + String dataSpaceConnectorApiPassword; + + /** + * ResourceLoader for loading Camel route templates from the classpath. + */ + final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + + /** + * Adds basic authentication information for the Dataspace Connector to the Velocity context + * for creating a Camel XML route to be used with the Dataspace Connector. + * + * @param velocityContext the context containing the values to insert into the route template + */ + public void addBasicAuthToContext(final VelocityContext velocityContext) { + final var auth = dataSpaceConnectorApiUsername + ":" + dataSpaceConnectorApiPassword; + final var encodedAuth = Base64.encodeBase64(auth.getBytes()); + final var authHeader = "Basic " + new String(encodedAuth); + velocityContext.put("connectorAuthHeader", authHeader); + } + + /** + * Chooses and returns the route template for the Dataspace Connector based on the app route. + * + * @param appRoute the app route + * @return the route template + */ + public Resource getRouteTemplate(final AppRoute appRoute) { + final var routeStart = appRoute.getAppRouteStart(); + + Resource resource; + if (routeStart.get(0) instanceof GenericEndpoint) { + resource = RESOURCE_LOADER.getResource("classpath:camel-templates/dataspaceconnector/http_to_connector_template.vm"); + } else if (routeStart.get(0) instanceof ConnectorEndpoint) { + resource = RESOURCE_LOADER.getResource("classpath:camel-templates/dataspaceconnector/connector_to_http_template.vm"); + } else { + resource = null; + } + + return resource; + } +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorClient.java b/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorClient.java new file mode 100644 index 00000000..d1bbf7ff --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorClient.java @@ -0,0 +1,76 @@ +package de.fraunhofer.isst.configmanager.connector.trustedconnector; + +import de.fraunhofer.iais.eis.BaseConnector; +import de.fraunhofer.iais.eis.ConfigurationModel; +import de.fraunhofer.iais.eis.ConfigurationModelBuilder; +import de.fraunhofer.iais.eis.ConnectorDeployMode; +import de.fraunhofer.iais.eis.ConnectorStatus; +import de.fraunhofer.iais.eis.LogLevel; +import de.fraunhofer.isst.configmanager.connector.clients.DefaultConnectorClient; +import de.fraunhofer.isst.configmanager.data.util.QueryInput; +import lombok.NoArgsConstructor; +import okhttp3.Response; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.net.URI; + +@Service +@NoArgsConstructor +@ConditionalOnExpression("${dataspace.connector.enabled:false} == false") +public class TrustedConnectorClient implements DefaultConnectorClient { + + @Override + public boolean sendConfiguration(final String configurationModel) { + return false; + } + + @Override + public void getConnectorStatus() { + + } + + @Override + public ConfigurationModel getConfiguration() { + return new ConfigurationModelBuilder() + ._configurationModelLogLevel_(LogLevel.NO_LOGGING) + ._connectorStatus_(ConnectorStatus.CONNECTOR_ONLINE) + ._connectorDeployMode_(ConnectorDeployMode.TEST_DEPLOYMENT) + ._keyStorePassword_("password") + ._keyStore_(URI.create("file://cert-stores/keystore.p12")) + ._trustStorePassword_("password") + ._trustStore_(URI.create("file://cert-stores/truststore.p12")) + .build(); + } + + @Override + public BaseConnector getBaseConnector(final String accessURL, final String resourceId) { + return null; + } + + @Override + public String getPolicyPattern(final String policy) { + return null; + } + + @Override + public BaseConnector getSelfDeclaration() { + return null; + } + + @Override + public String requestContractAgreement(final String recipientId, + final String requestedArtifactId, + final String contractOffer) { + return null; + } + + @Override + public Response requestData(final String recipientId, + final String requestedArtifactId, + final String contractId, + final String key, + final QueryInput queryInput) { + return null; + } +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorRouteConfigurer.java b/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorRouteConfigurer.java new file mode 100644 index 00000000..6a3fb6a4 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/connector/trustedconnector/TrustedConnectorRouteConfigurer.java @@ -0,0 +1,78 @@ +package de.fraunhofer.isst.configmanager.connector.trustedconnector; + +import de.fraunhofer.iais.eis.AppRoute; +import de.fraunhofer.iais.eis.ConfigurationModel; +import de.fraunhofer.iais.eis.ConnectorEndpoint; +import de.fraunhofer.iais.eis.GenericEndpoint; +import lombok.experimental.UtilityClass; +import org.apache.velocity.VelocityContext; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import java.net.URI; + +/** + * Utility class for configuring Camel routes for the TrustedConnector. + */ +@UtilityClass +public final class TrustedConnectorRouteConfigurer { + + /** + * ResourceLoader for loading Camel route templates from the classpath. + */ + private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + + /** + * Adds key- and truststore information to the Velocity context for creating a Camel XML route to be used with + * the Trusted Connector. + * + * @param velocityContext the context containing the values to insert into the route template + * @param configurationModel the config model containing key- and truststore information + */ + public static void addSslConfig(final VelocityContext velocityContext, final ConfigurationModel configurationModel) { + velocityContext.put("keyStorePath", removeFileScheme(configurationModel.getKeyStore())); + velocityContext.put("keyStorePassword", configurationModel.getKeyStorePassword()); + velocityContext.put("trustStorePath", removeFileScheme(configurationModel.getTrustStore())); + velocityContext.put("trustStorePassword", configurationModel.getTrustStorePassword()); + } + + /** + * Chooses and returns the route template for the Trusted Connector based on the app route. + * + * @param appRoute the app route + * @return the route template + */ + public static Resource getRouteTemplate(final AppRoute appRoute) { + final var routeStart = appRoute.getAppRouteStart(); + + Resource resource; + if (routeStart.get(0) instanceof GenericEndpoint) { + resource = RESOURCE_LOADER.getResource("classpath:camel-templates/trustedconnector/idscp2_client_template_1.vm"); + } else if (routeStart.get(0) instanceof ConnectorEndpoint) { + resource = RESOURCE_LOADER.getResource("classpath:camel-templates/trustedconnector/idscp2_server_template_1.vm"); + } else { + resource = null; + } + + return resource; + } + + /** + * Removes the file scheme from an URI, if it is specified. + * + * @param uri the URI + * @return the URI as a string with the file scheme removed, if it was present. + */ + private static String removeFileScheme(final URI uri) { + var string = uri.toString(); + + if (string.startsWith("file://")) { + string = string.substring(7); + } else if (string.startsWith("file:")) { + string = string.substring(5); + } + + return string; + } +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/CTLEvaluator.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/CTLEvaluator.java index d8597268..87d8a880 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/CTLEvaluator.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/CTLEvaluator.java @@ -9,9 +9,18 @@ import java.util.List; +/** + * Evaluate a {@link Formula} on a given {@link Node} for a set of Paths + */ @UtilityClass public class CTLEvaluator { + /** + * @param ctlExpression a {@link StateFormula} to evaluate + * @param place a {@link Place} of a {@link de.fraunhofer.isst.configmanager.petrinet.model.PetriNet} + * @param paths possible pathes through the PetriNet + * @return result of the evaluation of the ctlExpression + */ public static boolean evaluateNode(final StateFormula ctlExpression, final Place place, final List> paths) { @@ -19,6 +28,12 @@ public static boolean evaluateNode(final StateFormula ctlExpression, return ctlExpression.evaluate(place, paths); } + /** + * @param ctlExpression a {@link TransitionFormula} to evaluate + * @param transition a {@link Transition} of a {@link de.fraunhofer.isst.configmanager.petrinet.model.PetriNet} + * @param paths possible pathes through the PetriNet + * @return result of the evaluation of the ctlExpression + */ public static boolean evaluateTransition(final TransitionFormula ctlExpression, final Transition transition, final List> paths) { @@ -26,6 +41,12 @@ public static boolean evaluateTransition(final TransitionFormula ctlExpression, return ctlExpression.evaluate(transition, paths); } + /** + * @param ctlExpression a {@link Formula} to evaluate + * @param node a {@link Node} of a {@link de.fraunhofer.isst.configmanager.petrinet.model.PetriNet} + * @param paths possible pathes through the PetriNet + * @return result of the evaluation of the ctlExpression (or false, if formula and node types don't match) + */ public static boolean evaluate(final Formula ctlExpression, final Node node, final List> paths) { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/FF.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/FF.java index 57a4c1b3..8ba56b1f 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/FF.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/FF.java @@ -6,6 +6,9 @@ import java.util.List; +/** + * FF operator evaluates to False everytime + */ public class FF implements StateFormula, TransitionFormula { public static FF FF() { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/Formula.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/Formula.java index 4ff17a8e..fbab3062 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/Formula.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/Formula.java @@ -4,6 +4,10 @@ import java.util.List; +/** + * A generic Formula, can be a {@link de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.StateFormula} + * or a {@link de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionFormula} + */ public interface Formula { boolean evaluate(Node node, List> paths); diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/TT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/TT.java index 6827ce53..31c6d9f9 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/TT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/TT.java @@ -6,6 +6,9 @@ import java.util.List; +/** + * TT operator evaluates to True everytime + */ public class TT implements StateFormula, TransitionFormula { public static TT TT() { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeALONG.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeALONG.java index 34fd320a..ee9a4a30 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeALONG.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeALONG.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeEV.nodeEV; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeNOT.nodeNOT; +/** + * evaluates to true, if there exists a path, where parameter holds for every place + */ @AllArgsConstructor public class NodeALONG implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeAND.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeAND.java index bcd27bd3..ed7ced79 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeAND.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeAND.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if parameter1 and parameter2 evaluate to true + */ @AllArgsConstructor public class NodeAND implements StateFormula { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEV.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEV.java index d7cfd673..0daaf721 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEV.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEV.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.TT.TT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeFORALL_UNTIL.nodeFORALL_UNTIL; +/** + * evaluates to true, if a place fulfilling the given parameter is eventually reached on every path + */ @AllArgsConstructor public class NodeEV implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_MODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_MODAL.java index 4ab984cf..2d74c027 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_MODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_MODAL.java @@ -10,6 +10,10 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeMODAL.nodeMODAL; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionMODAL.transitionMODAL; +/** + * evaluates to true, if there is a successor place for which parameter1 holds, while parameter2 holds for the + * transition in between. + */ @AllArgsConstructor public class NodeEXIST_MODAL implements StateFormula { private StateFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_NEXT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_NEXT.java index cf9c3184..ba954fd2 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_NEXT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_NEXT.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeMODAL.nodeMODAL; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionMODAL.transitionMODAL; +/** + * evaluates to true, if there is a following place fulfilling the given formula + */ @AllArgsConstructor public class NodeEXIST_NEXT implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_UNTIL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_UNTIL.java index bc0282ce..e45e58b2 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_UNTIL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeEXIST_UNTIL.java @@ -6,6 +6,10 @@ import java.util.List; +/** + * evaluates to true, if a path exists, where parameter1 evaluates to true for every place, until parameter2 + * evaluates to true + */ @AllArgsConstructor public class NodeEXIST_UNTIL implements StateFormula { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeExpression.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeExpression.java index b53bec16..e61f80f7 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeExpression.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeExpression.java @@ -3,10 +3,21 @@ import lombok.AllArgsConstructor; import lombok.Getter; +/** + * Custom Expression to be evaluated on a {@link de.fraunhofer.isst.configmanager.petrinet.model.Place} + */ @Getter @AllArgsConstructor public class NodeExpression { + + /** + * Subexpression (function from {@link de.fraunhofer.isst.configmanager.petrinet.model.Place} to boolean + */ private NodeSubExpression subExpression; + + /** + * Information message to return when subExpression is not fulfilled by a transition + */ private String message; public static NodeExpression nodeExpression(final NodeSubExpression nodeSubExpression, diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_MODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_MODAL.java index 42cfff39..94109189 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_MODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_MODAL.java @@ -10,6 +10,10 @@ import java.util.List; import java.util.stream.Collectors; +/** + * evaluates to true, if parameter1 evaluates to true for every following place and parameter2 evaluates to true + * for every transition in between. + */ @AllArgsConstructor public class NodeFORALL_MODAL implements StateFormula { private StateFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_NEXT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_NEXT.java index 7488bf72..defb621d 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_NEXT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_NEXT.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeEXIST_NEXT.nodeEXIST_NEXT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeNOT.nodeNOT; +/** + * evaluates to true, if all following places satisfy the given formula + */ @AllArgsConstructor public class NodeFORALL_NEXT implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_UNTIL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_UNTIL.java index 8abfbe4e..1d4a8f36 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_UNTIL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeFORALL_UNTIL.java @@ -9,6 +9,9 @@ import java.util.ArrayList; import java.util.List; +/** + * evaluates to true, if on any possible path every place fulfills parameter1, until a place fulfills parameter2 + */ @AllArgsConstructor @Slf4j public class NodeFORALL_UNTIL implements StateFormula { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeINV.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeINV.java index 8a833921..303c80b0 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeINV.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeINV.java @@ -7,7 +7,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeNOT.nodeNOT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodePOS.nodePOS; - +/** + * evaluates to true, if parameter evaluates to true for all reachable places + */ @AllArgsConstructor public class NodeINV implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeMODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeMODAL.java index f294f5da..6557c6df 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeMODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeMODAL.java @@ -8,6 +8,9 @@ import java.util.List; +/** + * evaluates to true, if parameter evaluates to true for a transition directly following the current place + */ @AllArgsConstructor public class NodeMODAL implements StateFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNF.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNF.java index bc6cabb7..d85bf861 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNF.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNF.java @@ -1,11 +1,15 @@ package de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state; +import de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.ArcExpression; import de.fraunhofer.isst.configmanager.petrinet.model.Node; import de.fraunhofer.isst.configmanager.petrinet.model.Place; import lombok.AllArgsConstructor; import java.util.List; +/** + * evaluates to true, if given {@link NodeExpression} evaluates to true + */ @AllArgsConstructor public class NodeNF implements StateFormula { private NodeExpression parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNOT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNOT.java index 5ec08233..cdca2d0c 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNOT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeNOT.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if given subformula evaluates to false + */ @AllArgsConstructor public class NodeNOT implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeOR.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeOR.java index 1f715012..5c145f34 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeOR.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeOR.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if at least one of the two subformulas evaluates to true + */ @AllArgsConstructor public class NodeOR implements StateFormula { private StateFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodePOS.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodePOS.java index 9322ee0c..84858084 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodePOS.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodePOS.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.TT.TT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeEXIST_UNTIL.nodeEXIST_UNTIL; +/** + * Evaluates to true, if some Place is reachable, which fulfills the given parameter + */ @AllArgsConstructor public class NodePOS implements StateFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeSubExpression.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeSubExpression.java index b9af5cdc..2cb47f39 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeSubExpression.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/NodeSubExpression.java @@ -1,7 +1,11 @@ package de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state; import de.fraunhofer.isst.configmanager.petrinet.model.Place; +import de.fraunhofer.isst.configmanager.petrinet.model.Transition; +/** + * Interface describing NodeSubExpressions, can be used as lambda: {@link Place} -> boolean + */ @FunctionalInterface public interface NodeSubExpression { boolean evaluate(Place place); diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/StateFormula.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/StateFormula.java index f7264912..eaa994cf 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/StateFormula.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/state/StateFormula.java @@ -2,5 +2,8 @@ import de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.Formula; +/** + * Formulas which are evaluated on a {@link de.fraunhofer.isst.configmanager.petrinet.model.Place} of a PetriNet + */ public interface StateFormula extends Formula { } diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcExpression.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcExpression.java index 6f3d3d6f..42b60d04 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcExpression.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcExpression.java @@ -3,10 +3,21 @@ import lombok.AllArgsConstructor; import lombok.Getter; +/** + * Custom Expression to be evaluated on a {@link de.fraunhofer.isst.configmanager.petrinet.model.Transition} + */ @Getter @AllArgsConstructor public class ArcExpression { + + /** + * Subexpression (function from {@link de.fraunhofer.isst.configmanager.petrinet.model.Transition} to boolean + */ private ArcSubExpression subExpression; + + /** + * Information message to return when subExpression is not fulfilled by a transition + */ private String message; public static ArcExpression arcExpression(final ArcSubExpression subExpression, diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcSubExpression.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcSubExpression.java index 121fc4fd..cd2d9133 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcSubExpression.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/ArcSubExpression.java @@ -2,6 +2,9 @@ import de.fraunhofer.isst.configmanager.petrinet.model.Transition; +/** + * Interface describing ArcSubExpressions, can be used as lambda: {@link Transition} -> boolean + */ @FunctionalInterface public interface ArcSubExpression { boolean evaluate(Transition transition); diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAF.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAF.java index c30451d8..b7f48dfa 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAF.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAF.java @@ -6,6 +6,9 @@ import java.util.List; +/** + * evaluates to true, if given {@link ArcExpression} evaluates to true + */ @AllArgsConstructor public class TransitionAF implements TransitionFormula { private ArcExpression parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionALONG.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionALONG.java index 0a20e40b..1a5de743 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionALONG.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionALONG.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionEV.transitionEV; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionNOT.transitionNOT; +/** + * evaluates to true, if there exists a path, where parameter holds for every transition + */ @AllArgsConstructor public class TransitionALONG implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAND.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAND.java index 44177592..76353831 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAND.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionAND.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if parameter1 and parameter2 evaluate to true + */ @AllArgsConstructor public class TransitionAND implements TransitionFormula { private TransitionFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEV.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEV.java index 235e61d7..c065d6b0 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEV.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEV.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.TT.TT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionFORALL_UNTIL.transitionFORALL_UNTIL; +/** + * evaluates to true, if a transition fulfilling the given parameter is eventually reached on every path + */ @AllArgsConstructor public class TransitionEV implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_MODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_MODAL.java index a3dba05c..60fe2c99 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_MODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_MODAL.java @@ -10,6 +10,10 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionAND.transitionAND; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionMODAL.transitionMODAL; +/** + * evaluates to true, if there is a successor transition for which parameter1 holds, while parameter2 holds for the + * place in between. + */ @AllArgsConstructor public class TransitionEXIST_MODAL implements TransitionFormula { private TransitionFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_NEXT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_NEXT.java index e5a65521..2bdb045e 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_NEXT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_NEXT.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.state.NodeMODAL.nodeMODAL; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionMODAL.transitionMODAL; +/** + * evaluates to true, if there is a following transition fulfilling the given formula + */ @AllArgsConstructor public class TransitionEXIST_NEXT implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_UNTIL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_UNTIL.java index e89fda09..a897e0f8 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_UNTIL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionEXIST_UNTIL.java @@ -6,6 +6,10 @@ import java.util.List; +/** + * evaluates to true, if a path exists, where parameter1 evaluates to true for every transition, until parameter2 + * evaluates to true + */ @AllArgsConstructor public class TransitionEXIST_UNTIL implements TransitionFormula { private TransitionFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_MODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_MODAL.java index 542e9aaf..472d9a6f 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_MODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_MODAL.java @@ -9,6 +9,10 @@ import java.util.List; import java.util.stream.Collectors; +/** + * evaluates to true, if parameter1 evaluates to true for every following transition and parameter2 evaluates to true + * for every Place in between. + */ @AllArgsConstructor public class TransitionFORALL_MODAL implements TransitionFormula { private TransitionFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_NEXT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_NEXT.java index 5a23868c..7837cdac 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_NEXT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_NEXT.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionEXIST_NEXT.transitionEXIST_NEXT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionNOT.transitionNOT; +/** + * evaluates to true, if all following transitions satisfy the given formula + */ @AllArgsConstructor public class TransitionFORALL_NEXT implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_UNTIL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_UNTIL.java index 752c72f9..f9e7cf68 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_UNTIL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFORALL_UNTIL.java @@ -9,6 +9,9 @@ import java.util.ArrayList; import java.util.List; +/** + * evaluates to true, if on any possible path every transition fulfills parameter1, until a transition fulfills parameter2 + */ @AllArgsConstructor @Slf4j public class TransitionFORALL_UNTIL implements TransitionFormula { @@ -51,7 +54,6 @@ public boolean evaluate(final Node node, final List> paths) { return false; } }else{ - //TODO path contains circle //if something on the circle fulfills param2 accept, if something does not fulfill param1 reject for (var i = 2; i> paths) { return false; } } - //if everything on circle fulfills param1 but not param2: complicated case + //if everything on circle fulfills param1 but not param2 var lastTransition = path.get(path.size()-1) instanceof Transition ? path.get(path.size()-1) : path.get(path.size()-2); var newPaths = new ArrayList<>(paths); newPaths.remove(path); diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFormula.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFormula.java index 990984dc..817d9283 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFormula.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionFormula.java @@ -2,5 +2,8 @@ import de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.Formula; +/** + * Formulas which are evaluated on a {@link de.fraunhofer.isst.configmanager.petrinet.model.Transition} of a PetriNet + */ public interface TransitionFormula extends Formula { } diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionINV.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionINV.java index 214eee67..2b838284 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionINV.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionINV.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionNOT.transitionNOT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionPOS.transitionPOS; +/** + * evaluates to true, if parameter evaluates to true for all reachable transitions + */ @AllArgsConstructor public class TransitionINV implements TransitionFormula { private TransitionFormula parameter; @@ -30,4 +33,4 @@ public String symbol() { public String writeFormula() { return String.format("%s(%s)", symbol(), parameter.writeFormula()); } -} +} \ No newline at end of file diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionMODAL.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionMODAL.java index a4ee9726..40aac820 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionMODAL.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionMODAL.java @@ -8,6 +8,9 @@ import java.util.List; +/** + * evaluates to true, if parameter evaluates to true for a place directly following the transition + */ @AllArgsConstructor public class TransitionMODAL implements TransitionFormula { private StateFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionNOT.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionNOT.java index e7436559..2e2e6321 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionNOT.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionNOT.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if given subformula evaluates to false + */ @AllArgsConstructor public class TransitionNOT implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionOR.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionOR.java index c9d036dd..18ba765a 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionOR.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionOR.java @@ -5,6 +5,9 @@ import java.util.List; +/** + * evaluates to true, if at least one of the two subformulas evaluates to true + */ @AllArgsConstructor public class TransitionOR implements TransitionFormula { private TransitionFormula parameter1; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionPOS.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionPOS.java index d874ac0c..50de8079 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionPOS.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/evaluation/formula/transition/TransitionPOS.java @@ -8,6 +8,9 @@ import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.TT.TT; import static de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.TransitionEXIST_UNTIL.transitionEXIST_UNTIL; +/** + * Evaluates to true, if some Transition is reachable, which fulfills the given parameter + */ @AllArgsConstructor public class TransitionPOS implements TransitionFormula { private TransitionFormula parameter; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/ContextObject.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/ContextObject.java index 7b701131..1e95a3e1 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/ContextObject.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/ContextObject.java @@ -7,6 +7,9 @@ import java.util.List; import java.util.Objects; +/** + * Context of a transition (used for WFDU nets) + */ @Getter @AllArgsConstructor public class ContextObject { @@ -41,6 +44,10 @@ public int hashCode() { return Objects.hash(new ArrayList<>(context), read, write, erase); } + /** + * Transition types (are they apps or control transitions for the petrinet?), only APP transitions have to be + * unfolded for parallel checks + */ public enum TransType { APP, CONTROL diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/InnerPlace.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/InnerPlace.java index 5bc3a120..84c9952b 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/InnerPlace.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/model/InnerPlace.java @@ -6,10 +6,17 @@ import java.net.URI; import java.util.Objects; +/** + * Used for inner places of unfolded transitions (has a originalTrans field to access the original transition which + * was unfolded) + */ @Getter @Setter public class InnerPlace extends PlaceImpl{ + /** + * Original Transition, which was unfolded to create the InnerPlace + */ private Transition originalTrans; public InnerPlace(URI id, Transition originalTrans) { diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/ParallelEvaluator.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/ParallelEvaluator.java index 66b99912..7be2227d 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/ParallelEvaluator.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/ParallelEvaluator.java @@ -1,21 +1,27 @@ package de.fraunhofer.isst.configmanager.petrinet.simulator; import de.fraunhofer.isst.configmanager.petrinet.evaluation.formula.transition.ArcSubExpression; -import de.fraunhofer.isst.configmanager.petrinet.model.ContextObject; import de.fraunhofer.isst.configmanager.petrinet.model.Transition; import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.stream.Collectors; +/** + * Methods to check parallel evaluation of a {@link de.fraunhofer.isst.configmanager.petrinet.model.PetriNet} + */ @Slf4j public class ParallelEvaluator { + /** + * @param condition a condition to be fulfilled by a transition + * @param n number of transitions to fulfill the condition in parallel + * @param parallelSets sets of parallel transitions (previously calculated through stepgraph of unfolded petrinet) + * @return true if at least n transitions fulfilling condition are parallely executed at some point + */ public static boolean nParallelTransitionsWithCondition(ArcSubExpression condition, int n, List> parallelSets){ var setsWithSizeAtLeastN = parallelSets.stream().filter(transitions -> transitions.size() >= n).collect(Collectors.toSet()); for(var set: setsWithSizeAtLeastN){ - var names = set.stream().map(Transition::getContext).map(ContextObject::getRead).collect(Collectors.toList()); - log.info(names.toString()); if(set.stream().filter(condition::evaluate).count() >= n) return true; } return false; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/PetriNetSimulator.java b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/PetriNetSimulator.java index 28407ec5..73bde8e7 100644 --- a/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/PetriNetSimulator.java +++ b/src/main/java/de/fraunhofer/isst/configmanager/petrinet/simulator/PetriNetSimulator.java @@ -267,74 +267,23 @@ private static List> getPathsOfLengthNplus1(final List> pa return pathsLenNplus1; } - public static boolean circleFree(final List list){ + /** + * Lists are used for paths in other methods, path contains a circle, when list holds duplicate of node + * + * @param list list to check for duplicates + * @return true if list contains no duplicates + */ + public static boolean circleFree(final List list){ return list.stream().distinct().count() == list.size(); } - public static List> calculatePaths(final StepGraph stepGraph) { - final var len1 =calcPathsOfLength1(stepGraph); - List> lenN = new ArrayList<>(len1); - - lenN = lenN.stream().filter(path -> path.get(0).equals(stepGraph.getInitial())).collect(Collectors.toList()); - - final List> allPaths = new ArrayList<>(lenN); - - var i = 1; - - while (!lenN.isEmpty()) { - if (log.isInfoEnabled()) { - log.info("Calculating paths of length " + ++i); - } - - lenN = calcPathsOfLengthNplus1(len1, lenN); - - if (log.isInfoEnabled()) { - log.info(String.format("Length %d: %d", i, lenN.size())); - } - - if (!lenN.isEmpty()) { - allPaths.addAll(lenN); - } - } - - allPaths.sort(Comparator.comparingInt(List::size)); - - return allPaths; - } - - private static List> calcPathsOfLength1(final StepGraph stepGraph) { - final List> paths = new ArrayList<>(); - - for (final var arc : stepGraph.getArcs()) { - final var sourcePart = new ArrayList<>(); - sourcePart.add(arc.getSource()); - sourcePart.add(arc.getUsedTransition()); - paths.add(sourcePart); - - final var targetPart = new ArrayList<>(); - targetPart.add(arc.getUsedTransition()); - targetPart.add(arc.getTarget()); - paths.add(targetPart); - } - return paths; - } - - private static List> calcPathsOfLengthNplus1(final List> len1, - final List> lenN){ - final List> pathsLenNplus1 = new ArrayList<>(); - - for (final var pathN : lenN) { - for (final var path1 : len1) { - if(pathN.get(pathN.size()-1).equals(path1.get(0)) && circleFree(pathN)){ - final var pathNplus1 = new ArrayList<>(pathN); - pathNplus1.add(path1.get(path1.size()-1)); - pathsLenNplus1.add(pathNplus1); - } - } - } - return pathsLenNplus1; - } - + /** + * Unfold a PetriNet (all APP transitions T are replaced by a set T_start(trans) -> T_place(place) -> T_end(trans), + * markers on places in same step show possible parallel executions of transitions) + * + * @param petriNet a PetriNet + * @return unfolded petriNet + */ public static PetriNet getUnfoldedPetriNet(PetriNet petriNet){ var unfolded = petriNet.deepCopy(); var transitions = unfolded.getNodes().stream().filter(trans -> trans instanceof Transition).filter(trans -> ((Transition) trans).getContext().getType() == ContextObject.TransType.APP).collect(Collectors.toList()); @@ -366,6 +315,10 @@ public static PetriNet getUnfoldedPetriNet(PetriNet petriNet){ return unfolded; } + /** + * @param stepGraph StepGraph (of an unfolded PetriNet) + * @return List of parallel executions of transitions in the stepGraph of the given petriNet + */ public static List> getParallelSets(StepGraph stepGraph){ List> parallelSets = new ArrayList<>(); for(var step : stepGraph.getSteps()){ @@ -379,6 +332,14 @@ public static List> getParallelSets(StepGraph stepGraph){ return parallelSets; } + /** + * Remove subpaths from set of paths, only keep longest paths (but keep them if they have different starting places) + * + * Example: {A -> B -> C (keep), A -> B (remove), B -> C (keep)} + * + * @param paths Set of all paths + * @return Filtered set of Paths + */ private static List> filterPaths(final List> paths) { final List> filtered = new ArrayList<>(List.copyOf(paths)); diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteFileHelper.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteFileHelper.java new file mode 100644 index 00000000..674e5886 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteFileHelper.java @@ -0,0 +1,93 @@ +package de.fraunhofer.isst.configmanager.util.camel; + +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; + +/** + * Component for deploying and deleting Camel routes in the file system. + */ +@Slf4j +@Component +@NoArgsConstructor +public class RouteFileHelper { + /** + * Path for the Camel route files. + */ + @Value("${camel.xml-routes.directory}") + private String filePath; + + /** + * Writes a given string to a file in the directory specified in application.properties. Creates + * the file if it does not exist. + * + * @param fileName the filename + * @param content the content to write to the file + * @throws IOException if the file cannot be created or written + */ + public void writeToFile(final String fileName, final String content) throws IOException { + final var file = new File(filePath + File.separator + fileName); + + try (var fileWriter = new FileWriter(file); + var bufferedWriter = new BufferedWriter(fileWriter)) { + + if (log.isErrorEnabled() && !file.exists() && !file.createNewFile()) { + log.error("Could not create file '{}{}{}'", filePath, File.separator, fileName); + } + + bufferedWriter.write(content); + + if (log.isInfoEnabled()) { + log.info("Successfully created file '{}{}{}'.", filePath, File.separator, fileName); + } + + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Cannot write to file '{}{}{}' because an IO error occurred: {}", + filePath, File.separator, fileName, e.toString()); + } + + throw e; + } + } + + /** + * Deletes a file with a given name in the directory specified in application.properties. + * + * @param name the filename + * @throws IOException if the file cannot be deleted + */ + public void deleteFile(final String name) throws IOException { + final var file = Paths.get(filePath + name); + if (Files.exists(file)) { + try { + Files.delete(file); + + if (log.isInfoEnabled()) { + log.info("Successfully deleted file '{}{}{}'.", filePath, File.separator, name); + } + } catch (NoSuchFileException e) { + if (log.isErrorEnabled()) { + log.error("Cannot delete file '{}{}{}' because file does not exist.", filePath, + File.separator, name, e); + } + throw e; + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Cannot delete file '{}{}{}' because an IO error occurred.", filePath, + File.separator, name, e); + } + throw e; + } + } + } +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteHttpHelper.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteHttpHelper.java new file mode 100644 index 00000000..05a66118 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteHttpHelper.java @@ -0,0 +1,137 @@ +package de.fraunhofer.isst.configmanager.util.camel; + +import de.fraunhofer.isst.configmanager.util.OkHttpUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Credentials; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * Component for deploying and deleting Camel routes at the Camel application via HTTP. + */ +@Slf4j +@Component +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class RouteHttpHelper { + /** + * URL of the Camel application. + */ + @Value("${camel.application.url}") + String camelApplicationUrl; + + /** + * Username for the Camel application. + */ + @Value("${camel.application.username}") + String camelApplicationUsername; + + /** + * Password for the Camel application. + */ + @Value("${camel.application.password}") + String camelApplicationPassword; + + /** + * The Camel application's API path for managing routes. + */ + @Value("${camel.application.path.routes}") + String camelApplicationRoutesPath; + + /** + * The OkHttpClient for sending requests to the Camel application. + */ + final OkHttpClient httpClient = OkHttpUtils.getUnsafeOkHttpClient(); + + /** + * Sends an XML route to the Camel application specified in application.properties as a file. + * + * @param xml the XML route + * @throws IOException if the HTTP request cannot be sent or the response status code is not 2xx + */ + public void sendRouteFileToCamelApplication(final String xml) throws IOException { + final var url = camelApplicationUrl + camelApplicationRoutesPath; + + final var body = new MultipartBody.Builder().addFormDataPart("file", + "route.xml", RequestBody.create(xml.getBytes(StandardCharsets.UTF_8), + MediaType.parse("application/xml"))).build(); + + final var request = new Request.Builder() + .url(url) + .post(body) + .header("Authorization", Credentials.basic(camelApplicationUsername, + camelApplicationPassword)) + .build(); + + try { + final var response = httpClient.newCall(request).execute(); + + if (!response.isSuccessful()) { + if (log.isErrorEnabled()) { + log.error("Error sending file to Camel: {}, {}", response.code(), + response.body() != null ? Objects.requireNonNull(response.body()).string() : "No response body."); + } + + throw new IOException("Request for deploying route was unsuccessful with code " + + response.code()); + } + + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Error sending file to Camel: {}", e.getMessage()); + } + + throw e; + } + } + + /** + * Deletes a route with the given ID at the Camel application specified in + * application.properties. + * + * @param routeId ID of the route to delete + * @throws IOException if the HTTP request cannot be sent or the response status code is not 2xx + */ + public void deleteRouteAtCamelApplication(final String routeId) throws IOException { + final var url = camelApplicationUrl + camelApplicationRoutesPath + "/" + routeId; + + final var request = new Request.Builder() + .url(url) + .delete() + .header("Authorization", Credentials.basic(camelApplicationUsername, + camelApplicationPassword)) + .build(); + + try { + final var response = httpClient.newCall(request).execute(); + + if (!response.isSuccessful()) { + if (log.isErrorEnabled()) { + log.error("Error deleting route at Camel: {}, {}", response.code(), + response.body() != null ? Objects.requireNonNull(response.body()).string() : "No response body."); + } + + throw new IOException("Request for deleting route was unsuccessful with code " + + response.code()); + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Error deleting route at Camel: {}", e.getMessage()); + } + + throw e; + } + } +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteManager.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteManager.java new file mode 100644 index 00000000..d81c61a1 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/RouteManager.java @@ -0,0 +1,412 @@ +package de.fraunhofer.isst.configmanager.util.camel; + +import de.fraunhofer.iais.eis.AppEndpoint; +import de.fraunhofer.iais.eis.AppEndpointType; +import de.fraunhofer.iais.eis.AppRoute; +import de.fraunhofer.iais.eis.ConfigurationModel; +import de.fraunhofer.iais.eis.ConnectorEndpoint; +import de.fraunhofer.iais.eis.Endpoint; +import de.fraunhofer.iais.eis.GenericEndpoint; +import de.fraunhofer.iais.eis.RouteStep; +import de.fraunhofer.isst.configmanager.connector.dataspaceconnector.util.DataspaceConnectorRouteConfigurer; +import de.fraunhofer.isst.configmanager.connector.trustedconnector.TrustedConnectorRouteConfigurer; +import de.fraunhofer.isst.configmanager.util.camel.dto.RouteStepEndpoint; +import de.fraunhofer.isst.configmanager.util.camel.exceptions.NoSuitableTemplateException; +import de.fraunhofer.isst.configmanager.util.camel.exceptions.RouteCreationException; +import de.fraunhofer.isst.configmanager.util.camel.exceptions.RouteDeletionException; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; + +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Component for creating Camel routes from AppRoutes. + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@FieldDefaults(level = AccessLevel.PRIVATE) +public class RouteManager { + /** + * Indicates if a Dataspace Connector or a Trusted Connector is being managed. + */ + @Value("${dataspace.connector.enabled}") + boolean dataspaceConnectorEnabled; + + @Value("${camel.application.error-handler}") + String camelErrorHandlerRef; + + /** + * Helper for deploying and deleting Camel routes via HTTP. + */ + final RouteHttpHelper routeHttpHelper; + + /** + * Helper for deploying and deleting Camel routes in the file system. + */ + final RouteFileHelper routeFileHelper; + + /** + * Helper for configuring Camel routes for the Dataspace Connector. + */ + final DataspaceConnectorRouteConfigurer dataspaceConnectorRouteConfigurer; + + /** + * Creates a Camel XML route from a given app route for either the Dataspace Connector or the + * Trusted Connector. If the Configuration Manager is currently managing a Dataspace Connector, + * the generated XML route will be sent to the Camel application. If the Configuration + * Manager is currently managing a Trusted Connector, the generated XML route will be written + * to a file in the designated directory. Both the Camel application and the directory are + * specified in application.properties. + * + * @param configurationModel config model the app route belongs to; contains key- and truststore + * information + * @param appRoute the app route to create a Camel route for + * @throws RouteCreationException if the Camel route cannot be created or deployed + */ + public void createAndDeployXMLRoute(final ConfigurationModel configurationModel, + final AppRoute appRoute) throws RouteCreationException { + final var velocityContext = new VelocityContext(); + + //create ID for Camel route + final var camelRouteId = getCamelRouteId(appRoute); + velocityContext.put("routeId", camelRouteId); + + //get route start and end (will either be connector, app or generic endpoint) + addRouteStartToContext(velocityContext, (ArrayList) appRoute.getAppRouteStart()); + addRouteEndToContext(velocityContext, (ArrayList) appRoute.getAppRouteEnd()); + + //get route steps (if any) + addRouteStepsToContext(velocityContext, (ArrayList) appRoute.getHasSubRoute()); + + try { + if (dataspaceConnectorEnabled) { + createDataspaceConnectorRoute(appRoute, velocityContext); + } else { + createTrustedConnectorRoute(appRoute, velocityContext, configurationModel, + camelRouteId); + } + } catch (Exception e) { + throw new RouteCreationException("Error creating Camel route for AppRoute with ID '" + + appRoute.getId() + "'", e); + } + } + + /** + * Extracts the URL of the {@link AppRoute}'s start and adds it to the Velocity context. + * + * @param velocityContext the Velocity context + * @param routeStart start of the AppRoute + */ + private void addRouteStartToContext(final VelocityContext velocityContext, + final ArrayList routeStart) + throws RouteCreationException { + if (routeStart.get(0) instanceof ConnectorEndpoint) { + final var connectorEndpoint = (ConnectorEndpoint) routeStart.get(0); + velocityContext.put("startUrl", connectorEndpoint.getAccessURL().toString()); + } else if (routeStart.get(0) instanceof GenericEndpoint) { + final var genericEndpoint = (GenericEndpoint) routeStart.get(0); + velocityContext.put("startUrl", genericEndpoint.getAccessURL().toString()); + addBasicAuthHeaderForGenericEndpoint(velocityContext, genericEndpoint); + } else { + //TODO app is route start + throw new RouteCreationException("An app as the route start is not yet supported."); + } + } + + /** + * Extracts the URL of the {@link AppRoute}'s end and adds it to the Velocity context. + * + * @param velocityContext the Velocity context + * @param routeEnd end of the AppRoute + */ + private void addRouteEndToContext(final VelocityContext velocityContext, + final ArrayList routeEnd) + throws RouteCreationException { + if (routeEnd.get(0) instanceof ConnectorEndpoint) { + final var connectorEndpoint = (ConnectorEndpoint) routeEnd.get(0); + velocityContext.put("endUrl", connectorEndpoint.getAccessURL().toString()); + } else if (routeEnd.get(0) instanceof GenericEndpoint) { + final var genericEndpoint = (GenericEndpoint) routeEnd.get(0); + velocityContext.put("endUrl", genericEndpoint.getAccessURL().toString()); + addBasicAuthHeaderForGenericEndpoint(velocityContext, genericEndpoint); + } else { + //TODO app is route end + throw new RouteCreationException("An app as the route end is not yet supported."); + } + } + + /** + * Creates and adds the basic authentication header for calling a generic endpoint to a Velocity + * context, if basic authentication is defined for the given endpoint. + * @param velocityContext the Velocity context + * @param genericEndpoint the generic endpoint + */ + private void addBasicAuthHeaderForGenericEndpoint(final VelocityContext velocityContext, + final GenericEndpoint genericEndpoint) { + final var basicAuth = genericEndpoint.getGenericEndpointAuthentication(); + if (basicAuth != null && basicAuth.getAuthUsername() != null + && !basicAuth.getAuthUsername().isBlank() && basicAuth.getAuthPassword() != null + && !basicAuth.getAuthPassword().isBlank()) { + final var username = basicAuth.getAuthUsername(); + final var password = basicAuth.getAuthPassword(); + final var auth = username + ":" + password; + final var encodedAuth = Base64.encodeBase64(auth.getBytes()); + final var authHeader = "Basic " + new String(encodedAuth); + velocityContext.put("genericEndpointAuthHeader", authHeader); + } + } + + /** + * Extracts the start and end URLs of the {@link AppRoute}'s steps and adds them to the + * Velocity context. + * + * @param velocityContext the Velocity context + * @param routeSteps steps of the AppRoute + */ + private void addRouteStepsToContext(final VelocityContext velocityContext, + final ArrayList routeSteps) { + final var routeStepEndpoints = new ArrayList(); + + if (routeSteps != null) { + Endpoint lastStepEnd = null; + + for (int i = 0; i < routeSteps.size(); i++) { + final var routeStep = routeSteps.get(i); + + final var stepStart = routeStep.getAppRouteStart().get(0); + + //if end of last step is same as start of current step only call endpoint once + if (i > 0 && !stepStart.equals(lastStepEnd)) { + addRouteStepEndpoint(stepStart, routeStepEndpoints); + } + + if (i < routeSteps.size() - 1) { + final var stepEnd = routeStep.getAppRouteEnd().get(0); + addRouteStepEndpoint(stepEnd, routeStepEndpoints); + + lastStepEnd = stepEnd; + } + } + } + + velocityContext.put("routeStepEndpoints", routeStepEndpoints); + } + + /** + * Adds a {@link RouteStepEndpoint} representation of an {@link Endpoint} to the given list. + * + * @param endpoint the endpoint. + * @param list the list. + */ + private void addRouteStepEndpoint(final Endpoint endpoint, final List list) { + if (AppEndpoint.class.isAssignableFrom(endpoint.getClass())) { + final var appEndpoint = (AppEndpoint) endpoint; + if (appEndpoint.getAppEndpointType() == AppEndpointType.OUTPUT_ENDPOINT) { + list.add(new RouteStepEndpoint(appEndpoint.getAccessURL(), + HttpMethod.GET)); + } else { + list.add(new RouteStepEndpoint(appEndpoint.getAccessURL(), + HttpMethod.POST)); + } + } else { + list.add(new RouteStepEndpoint(endpoint.getAccessURL(), + HttpMethod.POST)); + } + } + + /** + * Creates and deploys a Camel route for the Dataspace Connector. First, Dataspace Connector + * specific configuration is added to the Velocity Context, which should already contain + * general route information. Then, the correct route template for the given AppRoute object + * is chosen from the Dataspace Connector templates. Last, the generated XML route is sent to + * the Camel application defined in application.properties. + * + * @param appRoute the AppRoute object + * @param velocityContext the Velocity context + * @throws Exception if the route file cannot be created or deployed + */ + private void createDataspaceConnectorRoute(final AppRoute appRoute, + final VelocityContext velocityContext) + throws Exception { + + if (log.isDebugEnabled()) { + log.debug("---- [RouteManager createDataspaceConnectorRoute]Creating route for Dataspace Connector..."); + } + + //add reference to Camel-Instance's error handler to Velocity context + velocityContext.put("errorHandlerRef", camelErrorHandlerRef); + + //add basic auth header for connector endpoint + dataspaceConnectorRouteConfigurer.addBasicAuthToContext(velocityContext); + + //choose correct XML template based on route + final var template = dataspaceConnectorRouteConfigurer.getRouteTemplate(appRoute); + + if (template != null) { + final var velocityEngine = new VelocityEngine(); + velocityEngine.init(); + + //populate route template with properties from velocity context to create route + final var writer = populateTemplate(template, velocityEngine, velocityContext); + + //send the generated route (XML) to Camel via HTTP + routeHttpHelper.sendRouteFileToCamelApplication(writer.toString()); + } else { + if (log.isWarnEnabled()) { + log.warn("---- [RouteManager createDataspaceConnectorRoute] Template is null. Unable to create XML route file for AppRoute" + + " with ID '{}'", appRoute.getId()); + } + + throw new NoSuitableTemplateException("No suitable Camel route template found for " + + "AppRoute with ID '" + appRoute.getId() + "'"); + } + } + + /** + * Creates and deploys a Camel route for the Trusted Connector. First, Trusted Connector + * specific configuration is added to the Velocity Context, which should already contain + * general route information. Then, the correct route template for the given AppRoute + * object is chosen from the Trusted Connector templates. Last, the generated XML route is + * written to the directory defined in application.properties. + * + * @param appRoute the AppRoute object + * @param velocityContext the Velocity context + * @param configurationModel the Configuration Model containing key- and truststore passwords + * required for the Trusted Connector's SSL configuration + * @param camelRouteId ID of the Camel route, which is used as the file name + * @throws Exception if the route file cannot be created or deployed + */ + private void createTrustedConnectorRoute(final AppRoute appRoute, + final VelocityContext velocityContext, + final ConfigurationModel configurationModel, + final String camelRouteId) throws Exception { + if (log.isDebugEnabled()) { + log.debug("---- [RouteManager createTrustedConnectorRoute] Creating route for Trusted Connector..."); + } + + //add SSL configuration for connector endpoint + TrustedConnectorRouteConfigurer.addSslConfig(velocityContext, configurationModel); + + //choose correct XML template based on route + final var template = TrustedConnectorRouteConfigurer.getRouteTemplate(appRoute); + + if (template != null) { + final var velocityEngine = new VelocityEngine(); + velocityEngine.init(); + + //populate route template with properties from velocity context to create route + final var writer = populateTemplate(template, velocityEngine, velocityContext); + + //write the generated route (XML) to a file in the designated directory + routeFileHelper.writeToFile(camelRouteId + ".xml", writer.toString()); + } else { + if (log.isWarnEnabled()) { + log.warn("Template is null. Unable to create XML route file for AppRoute" + + " with ID '{}'", appRoute.getId()); + } + + throw new NoSuitableTemplateException("No suitable Camel route template found for " + + "AppRoute with ID '" + appRoute.getId() + "'"); + } + } + + /** + * Populates a given Velocity template using the values from a given Velocity context. + * + * @param resource the template + * @param velocityEngine the Velocity engine required for populating the template + * @param velocityContext the context containing the values to insert into the template + * @return the populated template as a string + * @throws Exception if an error occurs while filling out the route template + */ + private StringWriter populateTemplate(final Resource resource, + final VelocityEngine velocityEngine, + final VelocityContext velocityContext) throws Exception { + final var stringWriter = new StringWriter(); + InputStreamReader inputStreamReader; + + try { + inputStreamReader = new InputStreamReader(resource.getInputStream()); + velocityEngine.evaluate(velocityContext, stringWriter, "", inputStreamReader); + } catch (Exception e) { + final var camelRouteId = (String) velocityContext.get("routeId"); + + if (log.isErrorEnabled()) { + log.error("An error occurred while populating template. Please check all respective " + + "files for connection with ID '{}' for correctness! (Error message: {})", + camelRouteId, e.toString()); + } + + throw e; + } + + return stringWriter; + } + + /** + * Deletes all Camel routes associated with app routes from a given config model by calling + * {@link RouteManager#deleteRoute(AppRoute)}. + * + * @param configurationModel the config model + * @throws RouteDeletionException if any of the Camel routes cannot be deleted + */ + public void deleteRouteFiles(final ConfigurationModel configurationModel) + throws RouteDeletionException { + for (final var appRoute: configurationModel.getAppRoute()) { + deleteRoute(appRoute); + } + } + + /** + * Deletes the Camel route for a given {@link AppRoute}. If the Configuration Manager is + * currently managing a Dataspace Connector, the route is deleted at the Camel application. If + * the Configuration Manager is currently managing a Trusted Connector, the route file is + * removed from the designated directory. + * + * @param appRoute the AppRoute + * @throws RouteDeletionException if the Camel route cannot be deleted + */ + public void deleteRoute(final AppRoute appRoute) throws RouteDeletionException { + final var camelRouteId = getCamelRouteId(appRoute); + + try { + if (dataspaceConnectorEnabled) { + routeHttpHelper.deleteRouteAtCamelApplication(camelRouteId); + } else { + routeFileHelper.deleteFile(camelRouteId + ".xml"); + } + } catch (Exception e) { + throw new RouteDeletionException("Error deleting Camel route for AppRoute with ID '" + + appRoute.getId() + "'", e); + } + + } + + /** + * Generated the ID of the Camel route for a given {@link AppRoute}. The Camel route ID consists + * of the String 'app-route_' followed by the UUID from the AppRoute's ID. + * + * @param appRoute the AppRoute + * @return the Camel route ID + */ + private String getCamelRouteId(final AppRoute appRoute) { + final var appRouteId = appRoute.getId().toString() + .split("/")[appRoute.getId().toString().split("/").length - 1]; + return "app-route_" + appRouteId; + } + +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/RouteStepEndpoint.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/RouteStepEndpoint.java new file mode 100644 index 00000000..ffc1dd5e --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/RouteStepEndpoint.java @@ -0,0 +1,19 @@ +package de.fraunhofer.isst.configmanager.util.camel.dto; + +import java.net.URI; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.experimental.FieldDefaults; +import org.springframework.http.HttpMethod; + +@Data +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class RouteStepEndpoint { + + URI endpointUrl; + HttpMethod httpMethod; + +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/package-info.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/package-info.java new file mode 100644 index 00000000..20b27dc6 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/dto/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains DTOs used during the creation of Camel routes. + */ +package de.fraunhofer.isst.configmanager.util.camel.dto; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/NoSuitableTemplateException.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/NoSuitableTemplateException.java new file mode 100644 index 00000000..8e1b2611 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/NoSuitableTemplateException.java @@ -0,0 +1,28 @@ +package de.fraunhofer.isst.configmanager.util.camel.exceptions; + +/** + * Thrown to indicate that no suitable Camel route template was found for a given AppRoute. + */ +public class NoSuitableTemplateException extends Exception { + private static final long serialVersionUID = 42L; + + /** + * Constructs a NoSuitableTemplateException with the specified message. + * + * @param msg the message. + */ + public NoSuitableTemplateException(final String msg) { + super(msg); + } + + /** + * Constructs a NoSuitableTemplateException with the specified message and cause. + * + * @param msg the message. + * @param cause the cause. + */ + public NoSuitableTemplateException(final String msg, final Throwable cause) { + super(msg, cause); + } + +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteCreationException.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteCreationException.java new file mode 100644 index 00000000..f2f10916 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteCreationException.java @@ -0,0 +1,28 @@ +package de.fraunhofer.isst.configmanager.util.camel.exceptions; + +/** + * Thrown to indicate that an error occurred during creation or deployment of a Camel route. + */ +public class RouteCreationException extends Exception { + private static final long serialVersionUID = 42L; + + /** + * Constructs a RouteCreationException with the specified message. + * + * @param msg the message. + */ + public RouteCreationException(final String msg) { + super(msg); + } + + /** + * Constructs a RouteCreationException with the specified message and cause. + * + * @param msg the message. + * @param cause the cause. + */ + public RouteCreationException(final String msg, final Throwable cause) { + super(msg, cause); + } + +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteDeletionException.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteDeletionException.java new file mode 100644 index 00000000..b74c8b5a --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/RouteDeletionException.java @@ -0,0 +1,28 @@ +package de.fraunhofer.isst.configmanager.util.camel.exceptions; + +/** + * Thrown to indicate that an error occurred while trying to delete a Camel route. + */ +public class RouteDeletionException extends Exception { + private static final long serialVersionUID = 42L; + + /** + * Constructs a RouteDeletionException with the specified message. + * + * @param msg the message. + */ + public RouteDeletionException(final String msg) { + super(msg); + } + + /** + * Constructs a RouteDeletionException with the specified message and cause. + * + * @param msg the message. + * @param cause the cause. + */ + public RouteDeletionException(final String msg, final Throwable cause) { + super(msg, cause); + } + +} diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/package-info.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/package-info.java new file mode 100644 index 00000000..60961e3d --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/exceptions/package-info.java @@ -0,0 +1,12 @@ +/** + * Exceptions specific for managing Camel routes. + *

+ * This package contains all Exceptions that indicate errors during the creation, deployment or + * deletion of Camel routes. + *

+ * + * @version 7.0.0 (last changed at version) + * @since 7.0.0 (documented since) + * @author IDS-ConfigurationManager Contributors + */ +package de.fraunhofer.isst.configmanager.util.camel.exceptions; diff --git a/src/main/java/de/fraunhofer/isst/configmanager/util/camel/package-info.java b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/package-info.java new file mode 100644 index 00000000..d6a98022 --- /dev/null +++ b/src/main/java/de/fraunhofer/isst/configmanager/util/camel/package-info.java @@ -0,0 +1,11 @@ +/** + * Classes relevant for managing Camel routes. + *

+ * This package contains all classes required for creating, deploying and deleting Camel routes. + *

+ * + * @version 7.0.0 (last changed at version) + * @since 7.0.0 (documented since) + * @author IDS-ConfigurationManager Contributors + */ +package de.fraunhofer.isst.configmanager.util.camel; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 34180f33..ea4eacb7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -39,3 +39,12 @@ dataspace.communication.ssl=true # Miscellaneous Settings spring.banner.location=classpath:banner.txt + +# camel +camel.xml-routes.directory=./camel/routes +camel.application.url=https://localhost:9090 +camel.application.username=admin +camel.application.password=password +camel.application.path.routes=/api/routes +camel.application.path.beans=/api/beans +camel.application.error-handler=errorHandler diff --git a/src/main/resources/camel-templates/dataspaceconnector/connector_to_http_template.vm b/src/main/resources/camel-templates/dataspaceconnector/connector_to_http_template.vm new file mode 100644 index 00000000..0cfea678 --- /dev/null +++ b/src/main/resources/camel-templates/dataspaceconnector/connector_to_http_template.vm @@ -0,0 +1,27 @@ + + + + + + + POST + ${connectorAuthHeader} + + + + + + #foreach($endpoint in $routeStepEndpoints) + $endpoint.getHttpMethod().toString() + + #end + + POST + #if($genericEndpointAuthHeader) + ${genericEndpointAuthHeader} + #end + + + + + diff --git a/src/main/resources/camel-templates/dataspaceconnector/http_to_connector_template.vm b/src/main/resources/camel-templates/dataspaceconnector/http_to_connector_template.vm new file mode 100644 index 00000000..a38aae63 --- /dev/null +++ b/src/main/resources/camel-templates/dataspaceconnector/http_to_connector_template.vm @@ -0,0 +1,27 @@ + + + + + + + GET + #if($genericEndpointAuthHeader) + ${genericEndpointAuthHeader} + #end + + + + + + #foreach($endpoint in $routeStepEndpoints) + $endpoint.getHttpMethod().toString() + + #end + + PUT + ${connectorAuthHeader} + + + + + diff --git a/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_1.vm b/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_1.vm new file mode 100644 index 00000000..138b9b62 --- /dev/null +++ b/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_1.vm @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + #set ($startUrlHost = $startUrl.split("//")[1].split(":")[0]) + #set ($startUrlPort = $startUrl.split("//")[1].split(":")[1]) + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_2.vm b/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_2.vm new file mode 100644 index 00000000..8b08d614 --- /dev/null +++ b/src/main/resources/camel-templates/trustedconnector/idscp2_client_template_2.vm @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + #set ($startUrlHost = $startUrl.split("//")[1].split(":")[0]) + #set ($startUrlPort = $startUrl.split("//")[1].split(":")[1]) + + + + + + + + + + + + + + ping + + + + + + + + application/json + + + + + + diff --git a/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_1.vm b/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_1.vm new file mode 100644 index 00000000..bf10a48a --- /dev/null +++ b/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_1.vm @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_2.vm b/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_2.vm new file mode 100644 index 00000000..1068d722 --- /dev/null +++ b/src/main/resources/camel-templates/trustedconnector/idscp2_server_template_2.vm @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + {"data" : "testData"} + + + pong + + + + + + + diff --git a/src/test/java/de/fraunhofer/isst/configmanager/petrinet/builder/InfomodelPetriNetBuilderTest.java b/src/test/java/de/fraunhofer/isst/configmanager/petrinet/builder/InfomodelPetriNetBuilderTest.java index ea7b6f5e..a3ec35ce 100644 --- a/src/test/java/de/fraunhofer/isst/configmanager/petrinet/builder/InfomodelPetriNetBuilderTest.java +++ b/src/test/java/de/fraunhofer/isst/configmanager/petrinet/builder/InfomodelPetriNetBuilderTest.java @@ -56,7 +56,9 @@ class InfomodelPetriNetBuilderTest { private static final int MAXIMUM_STARTEND = 3; /** - * Generate a random PetriNet, try to simulate it and print out the GraphViz representation + * Example: Generate a random PetriNet, try to simulate it and print out the GraphViz representation + * Generated PetriNet can have an infinite amount of possible configurations, if this happens the + * example will run indefinitely. */ @Test @Disabled @@ -114,38 +116,45 @@ void testBuildPetriNet() throws IOException { } } + /** + * Example: Create a set of Formulas and evaluate them on the example PetriNet + */ @Test @Disabled void testExamplePetriNet(){ + //build the example net and log DOT visualization var petriNet = buildPaperNet(); + log.info(GraphVizGenerator.generateGraphViz(petriNet)); - if (log.isInfoEnabled()) { - log.info(GraphVizGenerator.generateGraphViz(petriNet)); - } - - //build stepGraph and visualize + //build stepGraph var graph = PetriNetSimulator.buildStepGraph(petriNet); + log.info(String.format("%d possible states!", graph.getSteps().size())); - if (log.isInfoEnabled()) { - //log.info(GraphVizGenerator.generateGraphViz(graph)); - log.info(String.format("%d possible states!", graph.getSteps().size())); - } - + //get set of paths from calculated stepgraph var allPaths = PetriNetSimulator.getAllPaths(graph); log.info(String.format("Found %d valid Paths!", allPaths.size())); - //an end node is eventually reachable - var endReachable = nodeEV(nodeNF(nodeExpression(x -> x.getSourceArcs().isEmpty(), ""))); - log.info("Evaluating Formula: " + endReachable.writeFormula()); - //log.info("Result: " + CTLEvaluator.evaluate(endReachable, graph.getInitial().getNodes().stream().filter(node -> node.getID().equals(URI.create("place://start"))).findAny().get(), allPaths)); - //a transition is reachable, which reads data without 'france' in context, after that transition data is overwritten or erased (or an end is reached) - var formulaFrance = transitionPOS(transitionAND(transitionAF(arcExpression(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data") && !x.getContext().getContext().contains("france"), "")), transitionEV(transitionOR(transitionAF(arcExpression(x -> x.getContext().getWrite() != null && x.getContext().getWrite().equals("data") || x.getContext().getErase() != null && x.getContext().getErase().equals("data"), "")), transitionMODAL(nodeNF(nodeExpression(x -> x.getSourceArcs().isEmpty(), " "))))))); + + //Evaluate Formula 1: a transition is reachable, which reads data without 'france' in context, after that transition data is overwritten or erased (or an end is reached) + var formulaFrance = transitionPOS( + transitionAND( + transitionAF(arcExpression(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data") && !x.getContext().getContext().contains("france"), "")), + transitionEV( + transitionOR( + transitionAF(arcExpression(x -> x.getContext().getWrite() != null && x.getContext().getWrite().equals("data") || x.getContext().getErase() != null && x.getContext().getErase().equals("data"), "")), + transitionMODAL(nodeNF(nodeExpression(x -> x.getSourceArcs().isEmpty(), " "))) + ) + ) + ) + ); log.info("Formula France: " + formulaFrance.writeFormula()); log.info("Result: " + CTLEvaluator.evaluate(formulaFrance, graph.getInitial().getNodes().stream().filter(node -> node.getID().equals(URI.create("trans://getData"))).findAny().get(), allPaths)); - //a transition is reachable, which reads data + + //Evaluate Formula 2: a transition is reachable, which reads data var formulaDataUsage = nodeMODAL(transitionPOS(transitionAF(arcExpression(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data"), "")))); log.info("Formula Data: " + formulaDataUsage.writeFormula()); log.info("Result: " + CTLEvaluator.evaluate(formulaDataUsage, graph.getInitial().getNodes().stream().filter(node -> node.getID().equals(URI.create("place://start"))).findAny().get(), allPaths)); - //a transition is reachable, which is reading data. From there another transition is reachable, which also reads data, from this the end or a transition which overwrites or erases data is reachable. + + //Evaluate Formula 3: a transition is reachable, which is reading data. From there another transition is reachable, which also reads data, from this the end or a transition which overwrites or erases data is reachable. var formulaUseAndDelete = transitionPOS( transitionAND( transitionAF(arcExpression(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data"), "")), @@ -167,20 +176,30 @@ void testExamplePetriNet(){ log.info("Result: " + CTLEvaluator.evaluate(formulaUseAndDelete, graph.getInitial().getNodes().stream().filter(node -> node.getID().equals(URI.create("trans://getData"))).findAny().get(), allPaths)); } + /** + * Example: Unfold the example PetriNet and check for parallel evaluations + */ @Test @Disabled void testUnfoldNet(){ + //build example petrinet var petriNet = buildPaperNet(); - var stepGraph = PetriNetSimulator.buildStepGraph(petriNet); + + //unfold and visualize example petrinet var unfolded = PetriNetSimulator.getUnfoldedPetriNet(petriNet); log.info(GraphVizGenerator.generateGraphViz(unfolded)); - log.info(String.valueOf(unfolded.deepCopy().equals(unfolded))); + + //build step graph of unfolded net var unfoldedGraph = PetriNetSimulator.buildStepGraph(unfolded); log.info(String.format("Step Graph has %d possible combinations!", unfoldedGraph.getSteps().size())); - log.info("Getting parallel sets..."); + + //get possible parallel executions of transitions from the calculated stepgraph var parallelSets = PetriNetSimulator.getParallelSets(unfoldedGraph); log.info(String.format("Found %d possible parallel executions!", parallelSets.size())); - log.info(String.format("3 parallel reading Transitions: %s", ParallelEvaluator.nParallelTransitionsWithCondition(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data"), 3, parallelSets))); + + //evaluate: 3 transitions are reading data in parallel + var result = ParallelEvaluator.nParallelTransitionsWithCondition(x -> x.getContext().getRead() != null && x.getContext().getRead().equals("data"), 3, parallelSets); + log.info(String.format("3 parallel reading Transitions: %s", result)); } /** @@ -199,17 +218,11 @@ public static ArrayList randomSubList(List input) { return newList; } - @Test - @Disabled - void testFormula(){ - var formula = nodeAND(nodeMODAL(transitionNOT(FF())), nodeOR(nodeNF(nodeExpression(x -> true, "testMsg")),TT())); - if (log.isInfoEnabled()) { - log.info(formula.writeFormula()); - } - } - + /** + * Build the example PetriNet from the paper, to evaluate formulas on + * @return Example PetriNet described in the WFDU Paper + */ private PetriNet buildPaperNet(){ - var nodes = new HashSet(); //create nodes var start = new PlaceImpl(URI.create("place://start")); start.setMarkers(1); @@ -230,7 +243,7 @@ private PetriNet buildPaperNet(){ var stor3 = new PlaceImpl(URI.create("place://stored3")); var stor4 = new PlaceImpl(URI.create("place://stored4")); var end = new PlaceImpl(URI.create("place://end")); - nodes.addAll(List.of(start, copy, init, dat1, dat2, con1, con2, con3, con4, sample, mean, med, rules, stor1, stor2, stor3, stor4, end)); + var nodes = new HashSet(List.of(start, copy, init, dat1, dat2, con1, con2, con3, con4, sample, mean, med, rules, stor1, stor2, stor3, stor4, end)); //create transitions with context var initTrans = new TransitionImpl(URI.create("trans://init")); initTrans.setContextObject(new ContextObject(List.of(), null, null, null, ContextObject.TransType.CONTROL)); diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 591b8475..a4e731ee 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -26,3 +26,12 @@ dataspace.connector.api.username=admin dataspace.connector.api.password=password dataspace.connector.connectionattemps=2 dataspace.communication.ssl=true + +# camel +camel.xml-routes.directory=./camel/routes +camel.application.url=https://localhost:9090 +camel.application.username=admin +camel.application.password=password +camel.application.path.routes=/api/routes +camel.application.path.beans=/api/beans +camel.application.error-handler=errorHandler