Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDirk Fauth2021-02-16 11:35:46 +0000
committerDirk Fauth2021-02-16 11:39:27 +0000
commitb48faaaa16f009f19c2da906ed78a6b06784de09 (patch)
tree72c92543db009c0f333b200aaf6f7d07056fc263
parentf55e77a52bc978f88bf2e01647792f24cc436f26 (diff)
downloadorg.eclipse.app4mc.cloud-b48faaaa16f009f19c2da906ed78a6b06784de09.tar.gz
org.eclipse.app4mc.cloud-b48faaaa16f009f19c2da906ed78a6b06784de09.tar.xz
org.eclipse.app4mc.cloud-b48faaaa16f009f19c2da906ed78a6b06784de09.zip
Bug 571223 - Add configuration resource
Change-Id: Ic0cab0fb8f898dd47c9305d684a7c85ce0a00cd0 Signed-off-by: Dirk Fauth <Dirk.Fauth@de.bosch.com>
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java59
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java18
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java140
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java140
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java13
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java172
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java113
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java23
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java3
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java16
-rw-r--r--manager/src/main/resources/services.txt8
-rw-r--r--manager/src/main/resources/services_online.txt8
-rw-r--r--manager/src/main/resources/templates/admin.html8
-rw-r--r--manager/src/main/resources/templates/selectedServices.html26
-rw-r--r--manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java189
-rw-r--r--manager/src/test/resources/conf_def_migration.json20
-rw-r--r--manager/src/test/resources/conf_def_rtc.json29
-rw-r--r--manager/src/test/resources/conf_def_validation.json14
-rw-r--r--org.eclipse.app4m.amlt2systemc.cloud/openapi.yaml198
-rw-r--r--org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/src/org/eclipse/app4mc/amlt2systemc/cloud/http/TransformationServlet.java48
-rw-r--r--org.eclipse.app4mc.converter.cloud/converter-service/src/main/java/org/eclipse/app4mc/org/eclipse/app4mc/converter/cloud/MigrationRestService.java69
-rw-r--r--org.eclipse.app4mc.converter.cloud/openapi.yaml17
-rw-r--r--org.eclipse.app4mc.validation.cloud/openapi.yaml19
-rw-r--r--org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/src/org/eclipse/app4mc/validation/cloud/http/ValidationServlet.java59
26 files changed, 1192 insertions, 220 deletions
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java
index f5574c2..be89149 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java
@@ -53,8 +53,14 @@ public class CloudServiceExecutionRunnable implements Runnable {
try {
Path inputFile = this.storageService.load(this.uuid, this.originalFilename);
- ServiceNodeProcessingTask rootTask = new ServiceNodeProcessingTask(this.workflowStatus.getServiceRootNode(),
- inputFile, storageService, messagingTemplate, uuid, workflowStatus);
+ ServiceNodeProcessingTask rootTask =
+ new ServiceNodeProcessingTask(
+ this.workflowStatus.getServiceRootNode(),
+ inputFile,
+ this.storageService,
+ this.messagingTemplate,
+ this.uuid,
+ this.workflowStatus);
logger.info("started root task");
rootTask.fork();
@@ -68,33 +74,34 @@ public class CloudServiceExecutionRunnable implements Runnable {
this.workflowStatus,
this.storageService.load(this.uuid, "workflowstatus.json").toFile());
- // ensure that the done message is sent in any way to ensure the ui gets the final update
- // needed in case the process finishes while the page reloads
- long start = System.currentTimeMillis();
- long end = System.currentTimeMillis();
-
- long timeout = 10_000l;
-
- while (!this.workflowStatus.isConnected()) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Restore interrupted state...
- Thread.currentThread().interrupt();
- }
+ if (this.messagingTemplate != null) {
+ // ensure that the done message is sent in any way to ensure the ui gets the final update
+ // needed in case the process finishes while the page reloads
+ long start = System.currentTimeMillis();
+ long end = System.currentTimeMillis();
- end = System.currentTimeMillis();
+ long timeout = 10_000l;
- // don't wait longer than 10 seconds to get connected
- if (timeout > 0 && (end - start) > timeout) {
- break;
+ while (!this.workflowStatus.isConnected()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Restore interrupted state...
+ Thread.currentThread().interrupt();
+ }
+
+ end = System.currentTimeMillis();
+
+ // don't wait longer than 10 seconds to get connected
+ if (timeout > 0 && (end - start) > timeout) {
+ break;
+ }
}
+
+ this.messagingTemplate.convertAndSend(
+ "/topic/process-updates/" + this.uuid,
+ new ProcessLog(Action.DONE, null, this.uuid));
}
-
- this.messagingTemplate.convertAndSend(
- "/topic/process-updates/" + this.uuid,
- new ProcessLog(Action.DONE, null, this.uuid));
}
}
-
-}
+} \ No newline at end of file
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java
index f113c98..de52dff 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -23,6 +23,10 @@ public class ServiceConfiguration {
* Needed for user interface.
*/
private String serviceName;
+ /**
+ * The description of the service to show in the user interface, so a user knows what a service is supposed to do.
+ */
+ private String serviceDescription;
private ArrayList<ServiceConfigurationParameter> parameter = new ArrayList<>();
public ServiceConfiguration() {
@@ -41,14 +45,20 @@ public class ServiceConfiguration {
this.serviceName = serviceName;
}
+ public String getServiceDescription() {
+ return serviceDescription;
+ }
+
+ public void setServiceDescription(String serviceDescription) {
+ this.serviceDescription = serviceDescription;
+ }
+
public void addParameter(ServiceConfigurationParameter param) {
this.parameter.add(param);
}
public List<ServiceConfigurationParameter> getParameterList() {
- ArrayList<ServiceConfigurationParameter> params = new ArrayList<>(this.parameter);
- params.sort((o1, o2) -> o1.getKey().compareTo(o2.getKey()));
- return params;
+ return new ArrayList<>(this.parameter);
}
public ServiceConfigurationParameter getParameter(String key) {
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java
new file mode 100644
index 0000000..19e4bbe
--- /dev/null
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java
@@ -0,0 +1,140 @@
+/*********************************************************************************
+ * Copyright (c) 2021 Robert Bosch GmbH and others.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Robert Bosch GmbH - initial API and implementation
+ ********************************************************************************
+ */
+package org.eclipse.app4mc.cloud.manager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * The service configuration definition that is provided by a service.
+ */
+@JsonDeserialize(using = ServiceConfigurationDefinitionDeserializer.class)
+public class ServiceConfigurationDefinition {
+
+ /**
+ * The description of the service to explain what the service does.
+ */
+ private String description;
+ /**
+ * The file type that is accepted as input, e.g. amxmi, json, btf.
+ */
+ private String inputType;
+ /**
+ * The accepted input version, for the Amalthea Model e.g. 0.9.9 or 1.0.0.
+ */
+ private String inputVersion;
+ /**
+ * Whether the service supports input provided as archive.
+ */
+ private boolean inputArchiveSupported = false;
+
+ /**
+ * The file type that is produced as a result, e.g. amxmi, json, btf.
+ */
+ private String outputType;
+ /**
+ * The produced output version, for the Amalthea Model e.g. 0.9.9 or 1.0.0.
+ */
+ private String outputVersion;
+ /**
+ * Whether the service supports producing the output as archive.
+ */
+ private boolean outputArchiveSupported = false;
+
+ /**
+ * The configuration parameter the service supports.
+ */
+ private ArrayList<ServiceConfigurationParameter> parameter = new ArrayList<>();
+
+ public ServiceConfigurationDefinition() {
+ // empty constructor needed for JSON serialization
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getInputType() {
+ return inputType;
+ }
+
+ public void setInputType(String inputType) {
+ this.inputType = inputType;
+ }
+
+ public String getInputVersion() {
+ return inputVersion;
+ }
+
+ public void setInputVersion(String inputVersion) {
+ this.inputVersion = inputVersion;
+ }
+
+ public boolean isInputArchiveSupported() {
+ return this.inputArchiveSupported;
+ }
+
+ public void setInputArchiveSupported(boolean supported) {
+ this.inputArchiveSupported = supported;
+ }
+
+ public String getOutputType() {
+ return outputType;
+ }
+
+ public void setOutputType(String outputType) {
+ this.outputType = outputType;
+ }
+
+ public String getOutputVersion() {
+ return outputVersion;
+ }
+
+ public void setOutputVersion(String outputVersion) {
+ this.outputVersion = outputVersion;
+ }
+
+ public boolean isOutputArchiveSupported() {
+ return this.outputArchiveSupported;
+ }
+
+ public void setOutputArchiveSupported(boolean supported) {
+ this.outputArchiveSupported = supported;
+ }
+
+ public void addParameter(ServiceConfigurationParameter param) {
+ this.parameter.add(param);
+ }
+
+ public List<ServiceConfigurationParameter> getParameterList() {
+ ArrayList<ServiceConfigurationParameter> params = new ArrayList<>(this.parameter);
+ params.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
+ return params;
+ }
+
+ public ServiceConfigurationParameter getParameter(String key) {
+ for (ServiceConfigurationParameter param : this.parameter) {
+ if (param.getKey().equals(key)) {
+ return param;
+ }
+ }
+ return null;
+ }
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java
new file mode 100644
index 0000000..491a476
--- /dev/null
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java
@@ -0,0 +1,140 @@
+/*********************************************************************************
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Robert Bosch GmbH - initial API and implementation
+ ********************************************************************************
+ */
+package org.eclipse.app4mc.cloud.manager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+/**
+ * Custom {@link StdDeserializer} for {@link ServiceConfigurationDefinition} objects.
+ */
+public class ServiceConfigurationDefinitionDeserializer extends StdDeserializer<ServiceConfigurationDefinition> {
+
+ private static final long serialVersionUID = 857606567523680918L;
+
+ protected ServiceConfigurationDefinitionDeserializer() {
+ super(ServiceConfigurationDefinition.class);
+ }
+
+ @Override
+ public ServiceConfigurationDefinition deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ ServiceConfigurationDefinition result = new ServiceConfigurationDefinition();
+ JsonNode node = jp.getCodec().readTree(jp);
+
+ JsonNode description = node.get("description");
+ if (description != null) {
+ result.setDescription(description.asText());
+ }
+
+ JsonNode input = node.get("input");
+ if (input != null) {
+ JsonNode typeNode = input.get("type");
+ if (typeNode != null) {
+ result.setInputType(typeNode.asText());
+ }
+ JsonNode versionNode = input.get("version");
+ if (versionNode != null) {
+ result.setInputVersion(versionNode.asText());
+ }
+ JsonNode archiveNode = input.get("archive-supported");
+ if (archiveNode != null) {
+ result.setInputArchiveSupported(archiveNode.asBoolean());
+ }
+ }
+
+ JsonNode output = node.get("output");
+ if (output != null) {
+ JsonNode typeNode = output.get("type");
+ if (typeNode != null) {
+ result.setOutputType(typeNode.asText());
+ }
+ JsonNode versionNode = output.get("version");
+ if (versionNode != null) {
+ result.setOutputVersion(versionNode.asText());
+ }
+ JsonNode archiveNode = output.get("archive-supported");
+ if (archiveNode != null) {
+ result.setOutputArchiveSupported(archiveNode.asBoolean());
+ }
+ }
+
+ JsonNode parameter = node.get("parameter");
+ if (parameter != null) {
+ parameter.fields().forEachRemaining(parameterField -> {
+ deserializeParameterNode(result, parameterField.getKey(), parameterField.getValue());
+ });
+ }
+
+ return result;
+ }
+
+ private void deserializeParameterNode(
+ ServiceConfigurationDefinition result,
+ String parameterKey,
+ JsonNode fields) {
+
+ ServiceConfigurationParameter param = new ServiceConfigurationParameter();
+ param.setKey(parameterKey);
+
+ JsonNode name = fields.get("name");
+ if (name != null) {
+ param.setName(name.asText());
+ }
+
+ JsonNode description = fields.get("description");
+ if (description != null) {
+ param.setDescription(description.asText());
+ }
+
+ JsonNode type = fields.get("type");
+ if (type != null) {
+ param.setType(type.asText());
+ }
+
+ JsonNode value = fields.get("value");
+ if (value != null) {
+ param.setValue(value.asText());
+ }
+
+ JsonNode cardinality = fields.get("cardinality");
+ if (cardinality != null) {
+ param.setCardinality(cardinality.asText());
+ }
+
+ JsonNode mandatory = fields.get("mandatory");
+ if (mandatory != null) {
+ param.setMandatory(mandatory.asBoolean());
+ }
+
+ JsonNode values = fields.get("values");
+ if (values != null) {
+ if (values.isArray()) {
+ ArrayList<String> valueList = new ArrayList<>();
+ values.forEach(v -> valueList.add(v.asText()));
+ param.setPossibleValues(valueList);
+ } else {
+ param.setPossibleValues(Arrays.asList(values.asText()));
+ }
+ }
+
+ result.addParameter(param);
+ }
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java
index 9f21b40..317eddc 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -19,9 +19,10 @@ import java.util.List;
public class ServiceConfigurationParameter {
private String name;
+ private String description;
private String key;
private String value;
- private String type;
+ private String type = "String";
private String cardinality = "single";
private boolean mandatory = false;
private List<String> possibleValues = new ArrayList<>();
@@ -35,6 +36,14 @@ public class ServiceConfigurationParameter {
this.name = name;
}
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
public String getKey() {
return key;
}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java
index 92b9b4b..a6a17b8 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java
@@ -51,8 +51,14 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
- public ServiceNodeProcessingTask(ServiceNode n, Path inputFile, StorageService storageService,
- SimpMessageSendingOperations messagingTemplate, String uuid, WorkflowStatus ws) {
+ public ServiceNodeProcessingTask(
+ ServiceNode n,
+ Path inputFile,
+ StorageService storageService,
+ SimpMessageSendingOperations messagingTemplate,
+ String uuid,
+ WorkflowStatus ws) {
+
this.storageService = storageService;
this.messagingTemplate = messagingTemplate;
this.workflowStatus = ws;
@@ -63,41 +69,31 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
@Override
protected Path compute() {
- try {
- if (this.serviceNode.isStructuralNode()) {
- logger.info("created structural node thread: {}", this.serviceNode.getId());
- Path retval = processStructuralNode(this.serviceNode, this.inputFile);
- return retval;
- } else {
- logger.info("created branch node thread: {}", this.serviceNode.getId());
- Path retval = processNonStructuralNode(this.serviceNode, this.inputFile);
- if (this.workflowStatus.isCancelled()) {
- addMessage(this.workflowStatus, "Workflow cancelled by user");
- }
- return retval;
+ if (this.serviceNode.isStructuralNode()) {
+ logger.info("created structural node thread: {}", this.serviceNode.getId());
+ return processStructuralNode(this.serviceNode, this.inputFile);
+ } else {
+ logger.info("created branch node thread: {}", this.serviceNode.getId());
+ Path retval = processNonStructuralNode(this.serviceNode, this.inputFile);
+ if (this.workflowStatus.isCancelled()) {
+ addMessage(this.workflowStatus, "Workflow cancelled by user");
}
- } catch (ProcessingFailedException e) {
- this.workflowStatus.addError(e.getMessage());
- return null;
+ return retval;
}
-
}
private Path processStructuralNode(ServiceNode node, Path inputFile) {
Path nextInput = inputFile;
- for (ServiceNode n : node.getChildren()) {
- try {
- nextInput = processNonStructuralNode(n, nextInput);
- // if an error occurs without throwing an exception, successor tasks do not get executed
- if (n.isFailed()) {
- break;
- }
- if (this.workflowStatus.isCancelled()) {
- addMessage(this.workflowStatus, "Workflow cancelled by user");
- break;
- }
- } catch (ProcessingFailedException e) {
- this.workflowStatus.addError(e.getMessage());
+ for (ServiceNode childNode : node.getChildren()) {
+ nextInput = processNonStructuralNode(childNode, nextInput);
+
+ // if an error occurs without throwing an exception, successor tasks do not get executed
+ if (childNode.isFailed()) {
+ break;
+ }
+
+ if (this.workflowStatus.isCancelled()) {
+ addMessage(this.workflowStatus, "Workflow cancelled by user");
break;
}
}
@@ -105,52 +101,53 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
}
private Path processNonStructuralNode(ServiceNode node, Path inputFile) {
- if (node.getChildren().isEmpty()) {
- return processLeafNode(node, inputFile);
- } else if (node.getChildren().size() > 1) {
- try {
- Path currOutput = executeCloudService(node, inputFile);
- // if an error occurs without throwing an exception, successor tasks do not get executed
- if (node.isFailed()) {
- return null;
- }
- List<ServiceNodeProcessingTask> lActiveProcessingTask = new ArrayList<>();
- for (ServiceNode n : node.getChildren()) {
- lActiveProcessingTask.add(new ServiceNodeProcessingTask(n, currOutput, storageService,
- messagingTemplate, uuid, workflowStatus));
- }
- logger.info("launching new branches");
- ForkJoinTask.invokeAll(lActiveProcessingTask);
- return currOutput;
- } catch (ProcessingFailedException e) {
- this.workflowStatus.addError(e.getMessage());
- return null;
+
+ // first execute the service of the provided node
+ Path currOutput = null;
+ try {
+ currOutput = executeCloudService(node, inputFile);
+ } catch (ProcessingFailedException e) {
+ addError(this.workflowStatus, e.getMessage(), node);
+ }
+
+ // check if the service node execution failed
+ // if an error occurred without throwing an exception, successor tasks do not get executed
+ if (node.isFailed()) {
+ return null;
+ }
+
+ if (node.getChildren().size() > 1) {
+ ArrayList<ServiceNodeProcessingTask> childTasks = new ArrayList<>();
+ for (ServiceNode childNode : node.getChildren()) {
+ childTasks.add(new ServiceNodeProcessingTask(
+ childNode,
+ currOutput,
+ storageService,
+ messagingTemplate,
+ uuid,
+ workflowStatus));
}
- } else {
- try {
- Path currOutput = executeCloudService(node, inputFile);
- // if an error occurs without throwing an exception, successor tasks do not get executed
- if (node.isFailed()) {
- return null;
- }
- ServiceNode nextNode = node.getChildren().get(0);
- if (nextNode.isStructuralNode()) {
- ServiceNodeProcessingTask nextNodeTask = new ServiceNodeProcessingTask(nextNode, currOutput,
- storageService, messagingTemplate, uuid, workflowStatus);
- ForkJoinPool.commonPool().invoke(nextNodeTask);
- } else {
- processNonStructuralNode(nextNode, currOutput);
- }
- return currOutput;
- } catch (ProcessingFailedException e) {
- this.workflowStatus.addError(e.getMessage());
- return null;
+
+ logger.info("launching new branches");
+ ForkJoinTask.invokeAll(childTasks);
+ } else if (node.getChildren().size() == 1) {
+ ServiceNode nextNode = node.getChildren().get(0);
+ if (nextNode.isStructuralNode()) {
+ ServiceNodeProcessingTask nextNodeTask =
+ new ServiceNodeProcessingTask(
+ nextNode,
+ currOutput,
+ storageService,
+ messagingTemplate,
+ uuid,
+ workflowStatus);
+ ForkJoinPool.commonPool().invoke(nextNodeTask);
+ } else {
+ processNonStructuralNode(nextNode, currOutput);
}
}
- }
-
- private Path processLeafNode(ServiceNode node, Path inputFile) {
- return executeCloudService(node, inputFile);
+
+ return currOutput;
}
private Path executeCloudService(ServiceNode node, Path inputFile) {
@@ -160,6 +157,10 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
String serviceName = csd.getName();
String baseUrl = csd.getBaseUrl();
+ if (workflowStatus.isCancelled()) {
+ return null;
+ }
+
// upload to service
MultipartBody multipartBody = Unirest.post(baseUrl)
.field("file", Files.newInputStream(inputFile), inputFile.getFileName().toString());
@@ -196,11 +197,13 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
// error
Object body = uploadResponse.getBody();
if (body != null && !body.toString().isEmpty()) {
- workflowStatus.addError("Upload to " + serviceName + " failed! Error code: " + uploadResponse.getStatus() + " - " + body +" - Workflow stopped!");
- node.markFailed();
+ addError(workflowStatus,
+ "Upload to " + serviceName + " failed! Error code: " + uploadResponse.getStatus() + " - " + body +" - Workflow stopped!",
+ node);
} else {
- workflowStatus.addError("Upload to " + serviceName + " failed! Error code: " + uploadResponse.getStatus() + " - Workflow stopped!");
- node.markFailed();
+ addError(workflowStatus,
+ "Upload to " + serviceName + " failed! Error code: " + uploadResponse.getStatus() + " - Workflow stopped!",
+ node);
}
return null;
}
@@ -331,16 +334,14 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
}
if (error) {
- workflowStatus.addError(serviceName + " failed with errors");
- node.markFailed();
+ addError(workflowStatus, serviceName + " failed with errors", node);
} else {
addMessage(workflowStatus, serviceName + " successfull");
}
} else {
String errorUrl = HeaderHelper.getUrlFromLink(linkHeaders, "error", baseUrl);
if (errorUrl != null) {
- workflowStatus.addError(serviceName + " processing finished with error");
- node.markFailed();
+ addError(workflowStatus, serviceName + " processing finished with error", node);
// download error file
HttpResponse<File> errorResponse = Unirest.get(errorUrl)
@@ -361,8 +362,7 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
// extract delete
deleteUrl = HeaderHelper.getUrlFromLink(errorResponse.getHeaders().get("Link"), "delete", baseUrl);
} else {
- workflowStatus.addError(serviceName + " has no result and no error");
- node.markFailed();
+ addError(workflowStatus, serviceName + " has no result and no error", node);
}
}
@@ -426,4 +426,8 @@ public class ServiceNodeProcessingTask extends RecursiveTask<Path> {
}
}
+ private void addError(WorkflowStatus workflowStatus, String message, ServiceNode node) {
+ workflowStatus.addError(message);
+ node.markFailed();
+ }
}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java
index 19810dd..7d54b44 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java
@@ -14,19 +14,19 @@
package org.eclipse.app4mc.cloud.manager;
import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
+import kong.unirest.HttpResponse;
+import kong.unirest.JsonNode;
import kong.unirest.Unirest;
public final class WorkflowStatusHelper {
@@ -48,71 +48,45 @@ public final class WorkflowStatusHelper {
*/
public static ServiceConfiguration getConfigurationForService(CloudServiceDefinition csd) {
String selected = csd.getKey();
- ServiceConfiguration config = null;
-
- if ("validation".equals(selected)) {
- // TODO general: check if the selected service has a configuration URL
- // TODO build configuration based on provided service configuration
-
- List<String> allProfiles = new ArrayList<>();
+ ServiceConfiguration config = new ServiceConfiguration(csd.getName());
+
+ if (csd.isConfigurationAvailable()) {
try {
- String baseUrl = csd.getBaseUrl();
- if (!baseUrl.endsWith("/")) {
- baseUrl += "/";
+ String configUrl = csd.getBaseUrl();
+ if (!configUrl.endsWith("/")) {
+ configUrl += "/";
+ }
+ configUrl += "config";
+ HttpResponse<JsonNode> configResponse = Unirest.get(configUrl).asJson();
+ if (configResponse.getStatus() == 200) {
+ JsonNode configJson = configResponse.getBody();
+ if (configJson != null) {
+ ServiceConfigurationDefinition configDefinition = WorkflowStatusHelper.loadServiceConfigurationDefinition(configJson.toString());
+ configDefinition.getParameterList().forEach(p -> config.addParameter(p));
+ if (configDefinition.getDescription() != null) {
+ config.setServiceDescription(configDefinition.getDescription());
+ }
+ } else {
+ LOG.info("Failed to access configuration resource for " + csd.getName() + ": No JSON body in response");
+ }
+ } else {
+ LOG.info("Failed to access configuration resource for " + csd.getName() + ": " + configResponse.getStatusText());
}
- List<?> jsonResult = Unirest.get(baseUrl + "profiles").asJson().getBody().getArray().toList();
- allProfiles = jsonResult.stream().map(Object::toString).collect(Collectors.toList());
} catch (Exception e) {
- // do nothing, we will handle configurations in a different way in the future
- }
-
- if (allProfiles != null && !allProfiles.isEmpty()) {
- config = new ServiceConfiguration(csd.getName());
-
- ServiceConfigurationParameter validationProfiles = new ServiceConfigurationParameter();
- validationProfiles.setName("Validation profiles");
- validationProfiles.setKey("profiles");
- validationProfiles.setType("String");
- validationProfiles.setCardinality("multiple");
- validationProfiles.setPossibleValues(allProfiles);
- config.addParameter(validationProfiles);
+ LOG.info("Failed to access configuration resource for " + csd.getName() + ": " + e.getMessage());
}
- } else if ("rtc_analysis".equals(selected)) {
- config = new ServiceConfiguration(csd.getName());
-
- ServiceConfigurationParameter ascPriorities = new ServiceConfigurationParameter();
- ascPriorities.setName("Ascending priorities");
- ascPriorities.setKey("analysis-ascending-priorities");
- ascPriorities.setType("boolean");
- config.addParameter(ascPriorities);
-
- ServiceConfigurationParameter enableFlows = new ServiceConfigurationParameter();
- enableFlows.setName("Include flow analysis");
- enableFlows.setKey("analysis-enable-flows");
- enableFlows.setType("boolean");
- config.addParameter(enableFlows);
-
- ServiceConfigurationParameter ignoreOverUtil = new ServiceConfigurationParameter();
- ignoreOverUtil.setName("Ignore over utilization");
- ignoreOverUtil.setKey("analysis-ignore-overutilization");
- ignoreOverUtil.setType("boolean");
- config.addParameter(ignoreOverUtil);
-
- ServiceConfigurationParameter timeUnit = new ServiceConfigurationParameter();
- timeUnit.setName("Time unit");
- timeUnit.setKey("analysis-time-unit");
- timeUnit.setType("String");
- timeUnit.setPossibleValues(Arrays.asList("s", "ms", "us", "ns"));
- config.addParameter(timeUnit);
}
- // specify default configuration parameter
- if (config == null) {
- config = new ServiceConfiguration(csd.getName());
+ if (!StringUtils.isEmpty(csd.getDescription())) {
+ // service description is provided in the manager administration panel, so we
+ // override the value provided by the service itself
+ config.setServiceDescription(csd.getDescription());
}
-
+
+ // specify default configuration parameter for the manager
ServiceConfigurationParameter timeOut = new ServiceConfigurationParameter();
timeOut.setName("Timeout (in ms)");
+ timeOut.setDescription("The number of milliseconds the workflow should wait for the service to finish.\n-1 means to wait infinitely.");
timeOut.setKey("timeout");
timeOut.setValue("60000");
if ("app4mc_sim".equals(selected)) {
@@ -124,6 +98,7 @@ public final class WorkflowStatusHelper {
ServiceConfigurationParameter deleteResult = new ServiceConfigurationParameter();
deleteResult.setName("Delete result ");
+ deleteResult.setDescription("Flag to configure if the result should be deleted in the cloud service.\nDefault is true to keep the infrastructure clean.");
deleteResult.setKey("deleteResult");
deleteResult.setValue("true");
deleteResult.setType("boolean");
@@ -242,4 +217,24 @@ public final class WorkflowStatusHelper {
}
return null;
}
+
+ /**
+ * Deserializes the given JSON String.
+ *
+ * @param input The String to deserialize.
+ * @return The deserialized {@link ServiceConfigurationDefinition}.
+ */
+ public static ServiceConfigurationDefinition loadServiceConfigurationDefinition(String input) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+
+ SimpleModule module = new SimpleModule();
+ mapper.registerModule(module);
+
+ return mapper.readValue(input, ServiceConfigurationDefinition.class);
+ } catch (Exception e) {
+ LOG.error("Failed to load service configuration definition", e);
+ }
+ return null;
+ }
}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java
index 553cc36..01d1055 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -31,11 +31,7 @@ public class WorkflowStatusSerializer extends StdSerializer<WorkflowStatus> {
private static final long serialVersionUID = 8920512086262532108L;
public WorkflowStatusSerializer() {
- this(null);
- }
-
- public WorkflowStatusSerializer(Class<WorkflowStatus> t) {
- super(t);
+ super(WorkflowStatus.class);
}
@Override
@@ -84,11 +80,16 @@ public class WorkflowStatusSerializer extends StdSerializer<WorkflowStatus> {
if (!node.isStructuralNode()) {
ServiceConfiguration configuration = node.getServiceConfiguration();
if (configuration != null) {
- for (ServiceConfigurationParameter param : configuration.getParameterList()) {
- if (param.getValue() != null) {
- gen.writeObjectField(param.getKey(), param.getValue());
- }
- }
+ configuration.getParameterList().stream()
+ .sorted((o1, o2) -> o1.getKey().compareTo(o2.getKey()))
+ .filter(p -> p.getValue() != null)
+ .forEach(param -> {
+ try {
+ gen.writeObjectField(param.getKey(), param.getValue());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
index 263d549..ce3a56c 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
@@ -62,7 +62,8 @@ public class ApplicationConfig {
String name = split[1];
String url = split[2];
String desc = split.length >= 4 ? split[3] : "";
- return new CloudServiceDefinition(key, name, url, desc);
+ boolean configAvailable = split.length >= 5 ? Boolean.getBoolean(split[4]) : true;
+ return new CloudServiceDefinition(key, name, url, desc, configAvailable);
}).collect(Collectors.toList());
definitions.addAll(services);
} catch (IOException e) {
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java
index da9b347..8229b4e 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -19,16 +19,22 @@ public class CloudServiceDefinition {
String name;
String baseUrl;
String description;
+ boolean configurationAvailable = true;
public CloudServiceDefinition() {
// empty default constructor
}
public CloudServiceDefinition(String key, String name, String baseUrl, String description) {
+ this(key, name, baseUrl, description, true);
+ }
+
+ public CloudServiceDefinition(String key, String name, String baseUrl, String description, boolean configAvailable) {
this.key = key;
this.name = name;
this.baseUrl = baseUrl;
this.description = description;
+ this.configurationAvailable = configAvailable;
}
public String getKey() {
@@ -62,5 +68,13 @@ public class CloudServiceDefinition {
public void setDescription(String description) {
this.description = description;
}
+
+ public boolean isConfigurationAvailable() {
+ return configurationAvailable;
+ }
+
+ public void setConfigurationAvailable(boolean configurationAvailable) {
+ this.configurationAvailable = configurationAvailable;
+ }
}
diff --git a/manager/src/main/resources/services.txt b/manager/src/main/resources/services.txt
index ac82627..318335e 100644
--- a/manager/src/main/resources/services.txt
+++ b/manager/src/main/resources/services.txt
@@ -1,6 +1,6 @@
-migration;Migration;http://localhost:8080/app4mc/converter/;Migrates an input model file to model version 0.9.9
-validation;Validation;http://localhost:8181/app4mc/validation/;Validates the input model file
-rtc_analysis;RTC Analysis;http://localhost:8081/app4mc/analysis/;Executes Real-Time Calculus (RTC) techniques on a provided Amalthea model to analyse timing properties of heterogeneous embedded systems
+migration;Migration;http://localhost:8080/app4mc/converter/
+validation;Validation;http://localhost:8181/app4mc/validation/
+rtc_analysis;RTC Analysis;http://localhost:8081/app4mc/analysis/
rtc_interpreter;RTC Evaluation;http://localhost:8082/app4mc/interpreter/rtc/;Converts the results of a RTC Analysis into an input for the Chart Visualizer
label_core_interpreter;Label per Core Evaluation;http://localhost:8084/app4mc/interpreter/label-per-core/;Parses an Amalthea model to inspect label access operations per core
label_memory_interpreter;Label per Memory Evaluation;http://localhost:8084/app4mc/interpreter/label-per-memory/;Parses an Amalthea model to inspect label access operations per memory
@@ -8,6 +8,6 @@ label_task_interpreter;Label per Task Evaluation;http://localhost:8084/app4mc/in
label_size_interpreter;Label Size Evaluation;http://localhost:8084/app4mc/interpreter/label-size/;Parses an Amalthea model to inspect the number of labels grouped by size
chart_visualizer;Chart Visualizer;http://localhost:8083/app4mc/visualization/barchart/;Visualization of interpreter results
amlt2inchron;Amalthea -> INCHRON;https://am2inc.dev1.inchron.de/projects;Transforms an Amalthea model to an INCHRON model
-amlt2systemc;Amalthea -> SystemC;http://localhost:8282/app4mc/amlt2systemc/;Transforms an Amalthea model to simulation code
+amlt2systemc;Amalthea -> SystemC;http://localhost:8282/app4mc/amlt2systemc/
app4mc_sim;APP4MC.Sim;http://139.30.201.29:2323/app4mc/simulation/;Executes simulation and generates a BTF traces
inchron_btf_visualization;INCHRON BTF Trace Visualization;https://trace.dev1.inchron.de/traces/;Visualization of trace data provided by INCHRON \ No newline at end of file
diff --git a/manager/src/main/resources/services_online.txt b/manager/src/main/resources/services_online.txt
index 85f8073..98cc92e 100644
--- a/manager/src/main/resources/services_online.txt
+++ b/manager/src/main/resources/services_online.txt
@@ -1,6 +1,6 @@
-migration;Migration;https://app4mc.eclipseprojects.io/app4mc/converter/;Migrates an input model file to model version 0.9.9
-validation;Validation;https://app4mc.eclipseprojects.io/app4mc/validation/;Validates the input model file
-rtc_analysis;RTC Analysis;https://app4mc.eclipseprojects.io/app4mc/analysis/;Executes Real-Time Calculus (RTC) techniques on a provided Amalthea model to analyse timing properties of heterogeneous embedded systems
+migration;Migration;https://app4mc.eclipseprojects.io/app4mc/converter/
+validation;Validation;https://app4mc.eclipseprojects.io/app4mc/validation/
+rtc_analysis;RTC Analysis;https://app4mc.eclipseprojects.io/app4mc/analysis/
rtc_interpreter;RTC Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/rtc/;Converts the results of a RTC Analysis into an input for the Chart Visualizer
label_core_interpreter;Label per Core Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/label-per-core/;Parses an Amalthea model to inspect label access operations per core
label_memory_interpreter;Label per Memory Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/label-per-memory/;Parses an Amalthea model to inspect label access operations per memory
@@ -8,6 +8,6 @@ label_task_interpreter;Label per Task Evaluation;https://app4mc.eclipseprojects.
label_size_interpreter;Label Size Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/label-size/;Parses an Amalthea model to inspect the number of labels grouped by size
chart_visualizer;Chart Visualizer;https://app4mc.eclipseprojects.io/app4mc/visualization/barchart/;Visualization of interpreter results
amlt2inchron;Amalthea -> INCHRON;https://am2inc.dev1.inchron.de/projects;Transforms an Amalthea model to an INCHRON model
-amlt2systemc;Amalthea -> SystemC;https://app4mc.eclipseprojects.io/app4mc/amlt2systemc/;Transforms an Amalthea model to simulation code
+amlt2systemc;Amalthea -> SystemC;https://app4mc.eclipseprojects.io/app4mc/amlt2systemc/
app4mc_sim;APP4MC.Sim;http://139.30.201.29:2323/app4mc/simulation/;Executes simulation and generates a BTF traces
inchron_btf_visualization;INCHRON BTF Trace Visualization;https://trace.dev1.inchron.de/traces/;Visualization of trace data provided by INCHRON \ No newline at end of file
diff --git a/manager/src/main/resources/templates/admin.html b/manager/src/main/resources/templates/admin.html
index 15d2528..06d6bab 100644
--- a/manager/src/main/resources/templates/admin.html
+++ b/manager/src/main/resources/templates/admin.html
@@ -25,6 +25,7 @@
<th>Service Name</th>
<th>Service Base URL</th>
<th>Service Description</th>
+ <th>Service Configuration</th>
<th></th>
</tr>
</thead>
@@ -50,6 +51,13 @@
th:field="*{services[__${stat.index}__].description}"
class="form-control fm-ctrl-right" />
</td>
+ <td class="text-center">
+ <input
+ type="checkbox"
+ th:field="*{services[__${stat.index}__].configurationAvailable}"
+ class="form-check-input"
+ style="text-center" />
+ </td>
<td>
<a th:if="*{services[__${stat.index}__].name != null}"
th:attr="onclick=|removeService('*{services[__${stat.index}__].key}')|"
diff --git a/manager/src/main/resources/templates/selectedServices.html b/manager/src/main/resources/templates/selectedServices.html
index d8383ae..26b0114 100644
--- a/manager/src/main/resources/templates/selectedServices.html
+++ b/manager/src/main/resources/templates/selectedServices.html
@@ -39,29 +39,39 @@
<div th:if="not *{selectedServices.isEmpty()}">
<div th:each="node, nodeStatus : *{selectedServices}" class="mb-3">
<h4 th:text="${node.service.name + ' Configuration'}">Config</h4>
+ <div th:text="${node.serviceConfiguration.serviceDescription}" class="font-italic mb-1">Description</div>
<div th:each="parameter, parameterStatus : ${node.serviceConfiguration.parameterList}">
<!-- single non-boolean value -->
<div th:if="${parameter.cardinality == 'single' and parameter.type != 'boolean' and parameter.possibleValues.isEmpty()}">
<label class="form-check-label" th:text="${parameter.name}">Label</label>
<input
- class="form-control"
+ class="form-control mb-1"
type="text"
th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
- th:disabled="*{uuid != null}"/>
+ th:disabled="*{uuid != null}"
+ th:title="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].description}"
+ data-toggle="tooltip"/>
</div>
<!-- single boolean value -->
<div th:if="${parameter.cardinality == 'single' and parameter.type == 'boolean'}" class="form-check">
<input
- class="form-check-input"
+ class="form-check-input mb-1"
type="checkbox"
th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
th:value="true"
th:disabled="*{uuid != null}">
- <label class="form-check-label" th:text="${parameter.name}" th:for="${parameter.key}">Label</label>
+ <label
+ class="form-check-label"
+ th:text="${parameter.name}"
+ th:for="${parameter.key}"
+ th:title="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].description}"
+ data-toggle="tooltip">Label</label>
</div>
<!-- multiple possible values + cardinality multiple = checkboxes -->
<div th:if="${parameter.cardinality == 'multiple' and parameter.possibleValues.size() > 1}">
- <label th:text="${parameter.name}">Label</label>
+ <label
+ th:text="${parameter.name}"th:title="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].description}"
+ data-toggle="tooltip">Label</label>
<div th:each="pv : ${parameter.possibleValues}" class="form-check">
<input
type="checkbox"
@@ -75,9 +85,11 @@
<div th:if="${parameter.cardinality == 'single' and parameter.possibleValues.size() > 1}">
<label th:text="${parameter.name}">Label</label>
<select
- class="custom-select"
+ class="form-control mb-1"
th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
- th:disabled="*{uuid != null}">
+ th:disabled="*{uuid != null}"
+ th:title="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].description}"
+ data-toggle="tooltip">
<option value=""></option>
<option
th:each="pv : ${parameter.possibleValues}"
diff --git a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java b/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java
index 19dd243..3681757 100644
--- a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java
+++ b/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java
@@ -20,7 +20,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.util.ArrayList;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -44,39 +49,46 @@ public class WorkflowStatusHelperTest {
"migration",
"Migration",
"http://localhost:8080/app4mc/converter/",
- "Migrates an input model file to model version 0.9.9");
+ "Migrates an input model file to model version 0.9.9",
+ false);
private static CloudServiceDefinition VALIDATION = new CloudServiceDefinition(
"validation",
"Validation",
"http://localhost:8181/app4mc/validation/",
- "Validates the input model file");
+ "Validates the input model file",
+ false);
private static CloudServiceDefinition LABEL_CORE = new CloudServiceDefinition(
"label_core_interpreter",
"Label per Core Interpreter",
"http://localhost:8084/app4mc/interpreter/label-per-core/",
- "Parses an Amalthea model to inspect label access operations per core");
+ "Parses an Amalthea model to inspect label access operations per core",
+ false);
private static CloudServiceDefinition LABEL_MEMORY = new CloudServiceDefinition(
"label_memory_interpreter",
"Label per Memory Interpreter",
"http://localhost:8084/app4mc/interpreter/label-per-memory/",
- "Parses an Amalthea model to inspect label access operations per memory");
+ "Parses an Amalthea model to inspect label access operations per memory",
+ false);
private static CloudServiceDefinition LABEL_TASK = new CloudServiceDefinition(
"label_task_interpreter",
"Label per Task Interpreter",
"http://localhost:8084/app4mc/interpreter/label-per-task/",
- "Parses an Amalthea model to inspect label access operations per task");
+ "Parses an Amalthea model to inspect label access operations per task",
+ false);
private static CloudServiceDefinition LABEL_SIZE = new CloudServiceDefinition(
"label_size_interpreter",
"Label Size Interpreter",
"http://localhost:8084/app4mc/interpreter/label-size/",
- "Parses an Amalthea model to inspect the number of labels grouped by size");
+ "Parses an Amalthea model to inspect the number of labels grouped by size",
+ false);
private static CloudServiceDefinition CHART_VISUALIZER = new CloudServiceDefinition(
"chart_visualizer",
"Chart Visualizer",
"http://localhost:8083/app4mc/visualization/barchart/",
- "Visualization of interpreter results");
+ "Visualization of interpreter results",
+ false);
@BeforeAll
public static void beforeAll() {
@@ -510,4 +522,165 @@ public class WorkflowStatusHelperTest {
assertEquals(1, serviceNode.getChildren().size());
}
+
+ @Test
+ public void shouldDeserializeMigrationServiceDefinition() throws IOException {
+ Path resourceDirectory = Paths.get("src", "test", "resources", "conf_def_migration.json");
+ StringBuilder builder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(resourceDirectory)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+ }
+
+ ServiceConfigurationDefinition definition =
+ WorkflowStatusHelper.loadServiceConfigurationDefinition(builder.toString());
+
+ assertEquals("Migrates an input model file to model version 1.0.0", definition.getDescription());
+
+ assertEquals("amxmi", definition.getInputType());
+ assertNull(definition.getInputVersion());
+ assertTrue(definition.isInputArchiveSupported());
+
+ assertEquals("amxmi", definition.getOutputType());
+ assertEquals("1.0.0", definition.getOutputVersion());
+ assertTrue(definition.isOutputArchiveSupported());
+
+ assertEquals(1, definition.getParameterList().size());
+
+ ServiceConfigurationParameter versionParam = definition.getParameter("version");
+ assertNotNull(versionParam);
+ assertEquals("version", versionParam.getKey());
+ assertEquals("Output Model Version", versionParam.getName());
+ assertEquals("The model version to which the input should be migrated to", versionParam.getDescription());
+ assertEquals("1.0.0", versionParam.getValue());
+ assertEquals("String", versionParam.getType());
+ assertEquals("single", versionParam.getCardinality());
+ assertFalse(versionParam.isMandatory());
+ assertFalse(versionParam.isManagerParameter());
+
+ List<String> possibleValues = versionParam.getPossibleValues();
+ assertNotNull(possibleValues);
+ assertEquals(4, possibleValues.size());
+ assertTrue(possibleValues.contains("1.0.0"));
+ assertTrue(possibleValues.contains("0.9.9"));
+ assertTrue(possibleValues.contains("0.9.8"));
+ assertTrue(possibleValues.contains("0.9.7"));
+ }
+
+ @Test
+ public void shouldDeserializeValidationServiceDefinition() throws IOException {
+ Path resourceDirectory = Paths.get("src", "test", "resources", "conf_def_validation.json");
+ StringBuilder builder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(resourceDirectory)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+ }
+
+ ServiceConfigurationDefinition definition =
+ WorkflowStatusHelper.loadServiceConfigurationDefinition(builder.toString());
+
+ assertEquals("amxmi", definition.getInputType());
+ assertEquals("1.0.0", definition.getInputVersion());
+ assertTrue(definition.isInputArchiveSupported());
+
+ assertNull(definition.getOutputType());
+ assertNull(definition.getOutputVersion());
+ assertFalse(definition.isOutputArchiveSupported());
+
+ assertEquals(1, definition.getParameterList().size());
+
+ ServiceConfigurationParameter profilesParam = definition.getParameter("profiles");
+ assertNotNull(profilesParam);
+ assertEquals("profiles", profilesParam.getKey());
+ assertEquals("Validation profiles", profilesParam.getName());
+ assertNull(profilesParam.getValue());
+ assertEquals("String", profilesParam.getType());
+ assertEquals("multiple", profilesParam.getCardinality());
+ assertFalse(profilesParam.isMandatory());
+ assertFalse(profilesParam.isManagerParameter());
+
+ List<String> possibleValues = profilesParam.getPossibleValues();
+ assertNotNull(possibleValues);
+ assertEquals(3, possibleValues.size());
+ assertTrue(possibleValues.contains("Amalthea"));
+ assertTrue(possibleValues.contains("INCHRON"));
+ assertTrue(possibleValues.contains("Timing Architects"));
+ }
+
+ @Test
+ public void shouldDeserializeRtcServiceDefinition() throws IOException {
+ Path resourceDirectory = Paths.get("src", "test", "resources", "conf_def_rtc.json");
+ StringBuilder builder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(resourceDirectory)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+ }
+
+ ServiceConfigurationDefinition definition =
+ WorkflowStatusHelper.loadServiceConfigurationDefinition(builder.toString());
+
+ assertEquals("amxmi", definition.getInputType());
+ assertEquals("1.0.0", definition.getInputVersion());
+ assertFalse(definition.isInputArchiveSupported());
+
+ assertEquals("json", definition.getOutputType());
+ assertNull(definition.getOutputVersion());
+ assertFalse(definition.isOutputArchiveSupported());
+
+ assertEquals(4, definition.getParameterList().size());
+
+ ServiceConfigurationParameter param = definition.getParameter("analysis-ascending-priorities");
+ assertNotNull(param);
+ assertEquals("analysis-ascending-priorities", param.getKey());
+ assertEquals("Ascending priorities", param.getName());
+ assertNull(param.getValue());
+ assertEquals("boolean", param.getType());
+ assertEquals("single", param.getCardinality());
+ assertFalse(param.isMandatory());
+ assertFalse(param.isManagerParameter());
+
+ param = definition.getParameter("analysis-enable-flows");
+ assertNotNull(param);
+ assertEquals("analysis-enable-flows", param.getKey());
+ assertEquals("Include flow analysis", param.getName());
+ assertNull(param.getValue());
+ assertEquals("boolean", param.getType());
+ assertEquals("single", param.getCardinality());
+ assertFalse(param.isMandatory());
+ assertFalse(param.isManagerParameter());
+
+ param = definition.getParameter("analysis-ignore-overutilization");
+ assertNotNull(param);
+ assertEquals("analysis-ignore-overutilization", param.getKey());
+ assertEquals("Ignore over utilization", param.getName());
+ assertNull(param.getValue());
+ assertEquals("boolean", param.getType());
+ assertEquals("single", param.getCardinality());
+ assertFalse(param.isMandatory());
+ assertFalse(param.isManagerParameter());
+
+ param = definition.getParameter("analysis-time-unit");
+ assertNotNull(param);
+ assertEquals("analysis-time-unit", param.getKey());
+ assertEquals("Time unit", param.getName());
+ assertNull(param.getValue());
+ assertEquals("String", param.getType());
+ assertEquals("single", param.getCardinality());
+ assertFalse(param.isMandatory());
+ assertFalse(param.isManagerParameter());
+
+ List<String> possibleValues = param.getPossibleValues();
+ assertNotNull(possibleValues);
+ assertEquals(4, possibleValues.size());
+ assertTrue(possibleValues.contains("s"));
+ assertTrue(possibleValues.contains("ms"));
+ assertTrue(possibleValues.contains("us"));
+ assertTrue(possibleValues.contains("ns"));
+ }
}
diff --git a/manager/src/test/resources/conf_def_migration.json b/manager/src/test/resources/conf_def_migration.json
new file mode 100644
index 0000000..c894d25
--- /dev/null
+++ b/manager/src/test/resources/conf_def_migration.json
@@ -0,0 +1,20 @@
+{
+ "description" : "Migrates an input model file to model version 1.0.0",
+ "input" : {
+ "type" : "amxmi",
+ "archive-supported" : true
+ },
+ "output" : {
+ "type" : "amxmi",
+ "version" : "1.0.0",
+ "archive-supported" : true
+ },
+ "parameter" : {
+ "version" : {
+ "name" : "Output Model Version",
+ "description" : "The model version to which the input should be migrated to",
+ "value" : "1.0.0",
+ "values" : ["1.0.0", "0.9.9", "0.9.8", "0.9.7"]
+ }
+ }
+} \ No newline at end of file
diff --git a/manager/src/test/resources/conf_def_rtc.json b/manager/src/test/resources/conf_def_rtc.json
new file mode 100644
index 0000000..842f360
--- /dev/null
+++ b/manager/src/test/resources/conf_def_rtc.json
@@ -0,0 +1,29 @@
+{
+ "input" : {
+ "type" : "amxmi",
+ "version" : "1.0.0",
+ "archive-supported" : false
+ },
+ "output" : {
+ "type" : "json",
+ "archive-supported" : false
+ },
+ "parameter" : {
+ "analysis-ascending-priorities" : {
+ "name" : "Ascending priorities",
+ "type" : "boolean"
+ },
+ "analysis-enable-flows" : {
+ "name" : "Include flow analysis",
+ "type" : "boolean"
+ },
+ "analysis-ignore-overutilization" : {
+ "name" : "Ignore over utilization",
+ "type" : "boolean"
+ },
+ "analysis-time-unit" : {
+ "name" : "Time unit",
+ "values" : ["s", "ms", "us", "ns"]
+ }
+ }
+} \ No newline at end of file
diff --git a/manager/src/test/resources/conf_def_validation.json b/manager/src/test/resources/conf_def_validation.json
new file mode 100644
index 0000000..c6ccc62
--- /dev/null
+++ b/manager/src/test/resources/conf_def_validation.json
@@ -0,0 +1,14 @@
+{
+ "input" : {
+ "type" : "amxmi",
+ "version" : "1.0.0",
+ "archive-supported" : true
+ },
+ "parameter" : {
+ "profiles" : {
+ "name" : "Validation profiles",
+ "cardinality" : "multiple",
+ "values" : ["Amalthea", "INCHRON", "Timing Architects"]
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.app4m.amlt2systemc.cloud/openapi.yaml b/org.eclipse.app4m.amlt2systemc.cloud/openapi.yaml
new file mode 100644
index 0000000..3e41242
--- /dev/null
+++ b/org.eclipse.app4m.amlt2systemc.cloud/openapi.yaml
@@ -0,0 +1,198 @@
+openapi: 3.0.0
+info:
+ version: 0.1.0
+ title: APP4MC amlt to SystemC Transformation API
+ description: APP4MC Transformation API to transform an Amalthea model file into simulation code.
+
+servers:
+ - url: http://localhost:8080/app4mc/validation
+ - url: https://app4mc.eclipseprojects.io/app4mc/amlt2systemc
+
+paths:
+ /:
+ post:
+ summary: Upload the file to transform and start the transformation process asynchronously
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ responses:
+ '201':
+ description: Upload succeeded and transformation process started
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ description: ID of the created transformation resource
+ headers:
+ Location:
+ schema:
+ type: string
+ format: uri
+ description: The URI to the status URL
+ links:
+ status:
+ operationId: getStatus
+ parameters:
+ statusId: '$response.body#/id'
+ description: >
+ The `id` value returned in the response can be used as
+ the `statusId` parameter in `GET /{statusId}`.
+ '400':
+ description: No model file provided as upload
+ '404':
+ description: Upload failed
+
+ /{statusId}:
+ get:
+ summary: Get the status of the triggered transformation process
+ operationId: getStatus
+ parameters:
+ - in: path
+ name: statusId
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Processing finished successfully
+ headers:
+ Cache-Control:
+ schema:
+ type: string
+ enum:
+ - private, no-store, no-cache, must-revalidate
+ links:
+ result:
+ operationId: getDownload
+ parameters:
+ statusId: $request.path.statusId
+ '202':
+ description: Transformation process in progress
+ headers:
+ Cache-Control:
+ schema:
+ type: string
+ enum:
+ - private, no-store, no-cache, must-revalidate
+ '204':
+ description: Processing finished with an error
+ headers:
+ Cache-Control:
+ schema:
+ type: string
+ enum:
+ - private, no-store, no-cache, must-revalidate
+ links:
+ result:
+ operationId: getError
+ parameters:
+ statusId: $request.path.statusId
+ '404':
+ description: Resource not available
+
+ delete:
+ summary: Delete the uploaded and result resource from the server
+ operationId: deleteResource
+ parameters:
+ - in: path
+ name: statusId
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Resource deleted successfully
+ '404':
+ description: Resource not available
+
+ /{statusId}/download:
+ get:
+ summary: Download the transformation result file
+ operationId: getDownload
+ parameters:
+ - in: path
+ name: statusId
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Transformation successful
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ links:
+ delete:
+ operationId: deleteResource
+ parameters:
+ statusId: $request.path.statusId
+ '400':
+ description: Transformation finished with errors
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ links:
+ delete:
+ operationId: deleteResource
+ parameters:
+ statusId: $request.path.statusId
+ '404':
+ description: Progress still running / Resource not available / No result available
+ content:
+ application/json:
+ schema:
+ type: string
+
+ /{statusId}/error:
+ get:
+ summary: Download the error file of the transformation process
+ operationId: getError
+ parameters:
+ - in: path
+ name: statusId
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Error occured
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ links:
+ delete:
+ operationId: deleteResource
+ parameters:
+ statusId: $request.path.statusId
+ '404':
+ description: Resource not available / No error occured
+ content:
+ application/json:
+ schema:
+ type: string
+
+ /config:
+ get:
+ summary: Get the configuration definition of the service
+ responses:
+ '200':
+ description: The configuration definition of the service
+ content:
+ application/json:
+ schema:
+ type: string
diff --git a/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/META-INF/MANIFEST.MF b/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/META-INF/MANIFEST.MF
index abc415b..de0d068 100644
--- a/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/META-INF/MANIFEST.MF
@@ -7,8 +7,10 @@ Automatic-Module-Name: org.eclipse.app4mc.validation.cloud.http
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.fasterxml.jackson.core;version="2.9.9",
com.fasterxml.jackson.databind;version="2.9.93",
+ com.fasterxml.jackson.databind.node;version="2.9.93",
javax.servlet;version="3.1.0",
javax.servlet.http;version="3.1.0",
+ org.eclipse.app4mc.amalthea.model,
org.osgi.framework;version="1.9.0",
org.osgi.service.component.annotations;version="1.3.0";resolution:=optional,
org.osgi.service.event;version="1.4.0",
diff --git a/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/src/org/eclipse/app4mc/amlt2systemc/cloud/http/TransformationServlet.java b/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/src/org/eclipse/app4mc/amlt2systemc/cloud/http/TransformationServlet.java
index 2051956..aa74ed2 100644
--- a/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/src/org/eclipse/app4mc/amlt2systemc/cloud/http/TransformationServlet.java
+++ b/org.eclipse.app4m.amlt2systemc.cloud/org.eclipse.app4mc.amlt2systemc.cloud.http/src/org/eclipse/app4mc/amlt2systemc/cloud/http/TransformationServlet.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
+import org.eclipse.app4mc.amalthea.model.AmaltheaPackage;
import org.eclipse.app4mc.transformation.ServiceConstants;
import org.eclipse.app4mc.transformation.TransformationProcessor;
import org.osgi.framework.BundleContext;
@@ -53,6 +54,10 @@ import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
@Component(
service=Servlet.class,
property= {
@@ -74,6 +79,18 @@ public class TransformationServlet extends HttpServlet {
private static final String ERROR_FILE = "error.txt";
+ private static final String MODEL_VERSION;
+ static {
+ // Extracting namespace from AmaltheaPackage
+ String nsURI = AmaltheaPackage.eNS_URI;
+
+ // Extracting AMALTHEA metamodel version
+ MODEL_VERSION = nsURI.lastIndexOf('/') != -1
+ ? nsURI.substring(nsURI.lastIndexOf('/') + 1)
+ : nsURI;
+
+ }
+
private final String defaultBaseDir = System.getProperty("java.io.tmpdir");
private ExecutorService executor = Executors.newFixedThreadPool(1);
@@ -212,6 +229,7 @@ public class TransformationServlet extends HttpServlet {
return;
}
+ // GET /app4mc/amlt2systemc/config
// GET /app4mc/amlt2systemc/{id}
// GET /app4mc/amlt2systemc/{id}/download
// GET /app4mc/amlt2systemc/{id}/error
@@ -229,7 +247,33 @@ public class TransformationServlet extends HttpServlet {
return;
}
- if (splitPath.length == 2) {
+ if (splitPath.length == 2 && "config".equals(splitPath[1])) {
+ response.setContentType("application/json");
+ ObjectMapper mapper = new ObjectMapper();
+
+ ObjectNode config = mapper.createObjectNode();
+ config.put("description", "Transform an Amalthea model to simulation code.");
+
+ ObjectNode input = mapper.createObjectNode();
+ input.put("type", "amxmi");
+ input.put("version", MODEL_VERSION);
+ input.put("archive-supported", true);
+ config.set("input", input);
+
+ ObjectNode output = mapper.createObjectNode();
+ output.put("type", "app4mc-sim-model");
+ output.put("archive-supported", true);
+ config.set("output", output);
+
+ try {
+ String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(config);
+ response.getWriter().write(json);
+ } catch (JsonProcessingException e) {
+ response.getWriter().write(mapper.writeValueAsString(e));
+ }
+
+ return;
+ } else if (splitPath.length == 2) {
response.setHeader("Cache-Control", "private, no-store, no-cache, must-revalidate");
String uuid = splitPath[1];
diff --git a/org.eclipse.app4mc.converter.cloud/converter-service/src/main/java/org/eclipse/app4mc/org/eclipse/app4mc/converter/cloud/MigrationRestService.java b/org.eclipse.app4mc.converter.cloud/converter-service/src/main/java/org/eclipse/app4mc/org/eclipse/app4mc/converter/cloud/MigrationRestService.java
index 6800b89..c438c52 100644
--- a/org.eclipse.app4mc.converter.cloud/converter-service/src/main/java/org/eclipse/app4mc/org/eclipse/app4mc/converter/cloud/MigrationRestService.java
+++ b/org.eclipse.app4mc.converter.cloud/converter-service/src/main/java/org/eclipse/app4mc/org/eclipse/app4mc/converter/cloud/MigrationRestService.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -13,13 +13,16 @@
*/
package org.eclipse.app4mc.org.eclipse.app4mc.converter.cloud;
+import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -64,6 +67,11 @@ import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
@Component(service=MigrationRestService.class)
@JaxrsResource
@Produces(MediaType.APPLICATION_JSON)
@@ -88,6 +96,47 @@ public class MigrationRestService {
@Reference
MigrationProcessor migrationProcessor;
+ @GET
+ @Path("config")
+ public String config() {
+ ObjectMapper mapper = new ObjectMapper();
+
+ ObjectNode config = mapper.createObjectNode();
+ config.put("description", "Migrate the input model file to the specified output model version.");
+
+ ObjectNode input = mapper.createObjectNode();
+ input.put("type", "amxmi");
+ input.put("archive-supported", true);
+ config.set("input", input);
+
+ ObjectNode output = mapper.createObjectNode();
+ output.put("type", "amxmi");
+ output.put("version", ModelVersion.getLatestVersion());
+ output.put("archive-supported", true);
+ config.set("output", output);
+
+ ObjectNode parameter = mapper.createObjectNode();
+ ObjectNode version = mapper.createObjectNode();
+ version.put("name", "Output Model Version");
+ version.put("description", "The model version to which the input should be migrated to.");
+ // the default value to use is the latest supported version
+ version.put("value", ModelVersion.getLatestVersion());
+ ArrayNode values = version.putArray("values");
+
+ List<String> versions = ModelVersion.getAllSupportedVersions();
+ Collections.reverse(versions);
+ versions.forEach(v -> values.add(v));
+
+ parameter.set("version", version);
+ config.set("parameter", parameter);
+
+ try {
+ return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(config);
+ } catch (JsonProcessingException e) {
+ return "Error in generating configuration definition: " + e.getMessage();
+ }
+ }
+
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(@Context HttpServletRequest request, @Context UriInfo uriInfo, @Context ServletContext context) throws IOException, ServletException {
@@ -111,11 +160,12 @@ public class MigrationRestService {
// mark uuid in progress
getRegistry(context).put(uuid, PROGRESS_MARKER);
+ // check if the output model version was provided as parameter
+ String outputModelVersion = getOutputModelVersion(request);
+
// trigger asynchronous processing
executor.execute(() -> {
- String outputModelVersion = ModelVersion.getLatestVersion();
-
try (MigrationSettings migrationSettings = new MigrationSettings()) {
migrationSettings.setProject(uploaded.getParent().toFile());
migrationSettings.setMigrationModelVersion(outputModelVersion);
@@ -157,6 +207,19 @@ public class MigrationRestService {
.build();
}
+ private String getOutputModelVersion(HttpServletRequest request) throws IOException, ServletException {
+ String outputModelVersion = ModelVersion.getLatestVersion();
+
+ Part versionPart = request.getPart("version");
+ if (versionPart != null) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(versionPart.getInputStream()))) {
+ outputModelVersion = reader.readLine();
+ }
+ }
+
+ return outputModelVersion;
+ }
+
@Path("{uuid}")
@GET
public Response status(@PathParam("uuid") String uuid, @Context UriInfo uriInfo, @Context ServletContext context) throws IOException {
diff --git a/org.eclipse.app4mc.converter.cloud/openapi.yaml b/org.eclipse.app4mc.converter.cloud/openapi.yaml
index 78d0491..07bdbaa 100644
--- a/org.eclipse.app4mc.converter.cloud/openapi.yaml
+++ b/org.eclipse.app4mc.converter.cloud/openapi.yaml
@@ -1,11 +1,12 @@
openapi: 3.0.0
info:
- version: 0.9.9
+ version: 1.0.0
title: APP4MC Migration API
description: APP4MC Model Migration API to migrate older model versions to the current one
servers:
- url: http://localhost:8080/app4mc/converter
+ - url: https://app4mc.eclipseprojects.io/app4mc/converter
paths:
/:
@@ -20,6 +21,9 @@ paths:
file:
type: string
format: binary
+ version:
+ type: string
+ description: The version to which the model should be converted to. Parameter is optional and defaults to the latest supported version in this service.
responses:
'201':
description: Upload succeeded and migration process started
@@ -172,3 +176,14 @@ paths:
application/json:
schema:
type: string
+
+ /config:
+ get:
+ summary: Get the configuration definition of the service
+ responses:
+ '200':
+ description: The configuration definition of the service
+ content:
+ application/json:
+ schema:
+ type: string
diff --git a/org.eclipse.app4mc.validation.cloud/openapi.yaml b/org.eclipse.app4mc.validation.cloud/openapi.yaml
index 61f8369..44aed55 100644
--- a/org.eclipse.app4mc.validation.cloud/openapi.yaml
+++ b/org.eclipse.app4mc.validation.cloud/openapi.yaml
@@ -1,11 +1,12 @@
openapi: 3.0.0
info:
- version: 0.9.9
+ version: 1.0.0
title: APP4MC Validation API
description: APP4MC Validation API to validate an Amalthea model file
servers:
- url: http://localhost:8080/app4mc/validation
+ - url: https://app4mc.eclipseprojects.io/app4mc/validation
paths:
/:
@@ -20,6 +21,9 @@ paths:
file:
type: string
format: binary
+ profiles:
+ type: string
+ description: The validation profiles to execute. Parameter is optional and defaults to the Amalthea Standard Validations profile.
responses:
'201':
description: Upload succeeded and validation process started
@@ -171,7 +175,7 @@ paths:
/{statusId}/error:
get:
- summary: Download the error file of the migration process
+ summary: Download the error file of the validation process
operationId: getError
parameters:
- in: path
@@ -198,3 +202,14 @@ paths:
application/json:
schema:
type: string
+
+ /config:
+ get:
+ summary: Get the configuration definition of the service
+ responses:
+ '200':
+ description: The configuration definition of the service
+ content:
+ application/json:
+ schema:
+ type: string
diff --git a/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/META-INF/MANIFEST.MF b/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/META-INF/MANIFEST.MF
index 799db2f..0e57afe 100644
--- a/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/META-INF/MANIFEST.MF
@@ -7,6 +7,7 @@ Automatic-Module-Name: org.eclipse.app4mc.validation.cloud.http
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.fasterxml.jackson.core;version="2.9.9",
com.fasterxml.jackson.databind;version="2.9.93",
+ com.fasterxml.jackson.databind.node;version="2.9.93",
javax.servlet;version="3.1.0",
javax.servlet.http;version="3.1.0",
org.osgi.service.component.annotations;version="[1.3.0,1.4.0)";resolution:=optional,
diff --git a/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/src/org/eclipse/app4mc/validation/cloud/http/ValidationServlet.java b/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/src/org/eclipse/app4mc/validation/cloud/http/ValidationServlet.java
index 8d02b69..a7f2499 100644
--- a/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/src/org/eclipse/app4mc/validation/cloud/http/ValidationServlet.java
+++ b/org.eclipse.app4mc.validation.cloud/org.eclipse.app4mc.validation.cloud.http/src/org/eclipse/app4mc/validation/cloud/http/ValidationServlet.java
@@ -1,5 +1,5 @@
/*********************************************************************************
- * Copyright (c) 2020 Robert Bosch GmbH and others.
+ * Copyright (c) 2020, 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -45,6 +45,7 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.eclipse.app4mc.amalthea.model.Amalthea;
+import org.eclipse.app4mc.amalthea.model.AmaltheaPackage;
import org.eclipse.app4mc.amalthea.model.io.AmaltheaLoader;
import org.eclipse.app4mc.validation.core.IProfile;
import org.eclipse.app4mc.validation.util.ProfileManager;
@@ -57,6 +58,8 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
@Component(
service=Servlet.class,
@@ -82,6 +85,18 @@ public class ValidationServlet extends HttpServlet {
private static final String PASSED_MARKER = "passed";
private static final String FAILED_MARKER = "failed";
+ private static final String MODEL_VERSION;
+ static {
+ // Extracting namespace from AmaltheaPackage
+ String nsURI = AmaltheaPackage.eNS_URI;
+
+ // Extracting AMALTHEA metamodel version
+ MODEL_VERSION = nsURI.lastIndexOf('/') != -1
+ ? nsURI.substring(nsURI.lastIndexOf('/') + 1)
+ : nsURI;
+
+ }
+
private final String defaultBaseDir = System.getProperty("java.io.tmpdir");
private ExecutorService executor = Executors.newFixedThreadPool(1);
@@ -244,6 +259,7 @@ public class ValidationServlet extends HttpServlet {
}
// GET /app4mc/validation/profiles
+ // GET /app4mc/validation/config
// GET /app4mc/validation/{id}
// GET /app4mc/validation/{id}/download
// GET /app4mc/validation/{id}/error
@@ -276,6 +292,47 @@ public class ValidationServlet extends HttpServlet {
}
return;
+ } else if (splitPath.length == 2 && "config".equals(splitPath[1])) {
+ response.setContentType("application/json");
+ ObjectMapper mapper = new ObjectMapper();
+
+ ObjectNode config = mapper.createObjectNode();
+ config.put("description", "Validate the input model file.");
+
+ ObjectNode input = mapper.createObjectNode();
+ input.put("type", "amxmi");
+ input.put("version", MODEL_VERSION);
+ input.put("archive-supported", true);
+ config.set("input", input);
+
+ // the validation produces no output that is consumed by a following service
+
+ ObjectNode parameter = mapper.createObjectNode();
+ ObjectNode profiles = mapper.createObjectNode();
+ profiles.put("name", "Validation Profiles");
+ profiles.put("description", "The validation profiles that should be executed.\nIf nothing is selected the Amalthea Standard Validations profile is executed.");
+ profiles.put("cardinality", "multiple");
+ // the default value to use is the Amalthea Standard Validations profile
+ profiles.put("value", "Amalthea Standard Validations");
+ ArrayNode values = profiles.putArray("values");
+
+ List<String> availableProfiles = manager.getRegisteredValidationProfiles().values().stream()
+ .sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
+ .map(profile -> profile.getName())
+ .collect(Collectors.toList());
+ availableProfiles.forEach(v -> values.add(v));
+
+ parameter.set("profiles", profiles);
+ config.set("parameter", parameter);
+
+ try {
+ String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(config);
+ response.getWriter().write(json);
+ } catch (JsonProcessingException e) {
+ response.getWriter().write(mapper.writeValueAsString(e));
+ }
+
+ return;
} else if (splitPath.length == 2) {
response.setHeader("Cache-Control", "private, no-store, no-cache, must-revalidate");

Back to the top