Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDirk Fauth2020-11-19 13:16:45 +0000
committerDirk Fauth2020-11-19 13:16:45 +0000
commita4ace7cc30573b7d6be9c4a695eff8a3767050fb (patch)
tree499d0b44eaa4bb1d6bac006829a9b932c93b1247
parent595bf13b637f7d6baf1fb71010f306464f6cf421 (diff)
downloadorg.eclipse.app4mc.cloud-a4ace7cc30573b7d6be9c4a695eff8a3767050fb.tar.gz
org.eclipse.app4mc.cloud-a4ace7cc30573b7d6be9c4a695eff8a3767050fb.tar.xz
org.eclipse.app4mc.cloud-a4ace7cc30573b7d6be9c4a695eff8a3767050fb.zip
Added preparation for tree based service execution
Change-Id: I0112de33aa1458ffdd33ea9b755bffb30019671f Signed-off-by: Dirk Fauth <Dirk.Fauth@de.bosch.com>
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java26
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java178
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java38
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java11
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java38
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java191
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java71
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java54
-rw-r--r--manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java40
-rw-r--r--manager/src/main/resources/templates/selectedServices.html24
-rw-r--r--manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java470
11 files changed, 929 insertions, 212 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 1bd7830..333c0a0 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
@@ -66,10 +66,10 @@ public class CloudServiceExecutionRunnable implements Runnable {
public void run() {
try {
Path inputFile = this.storageService.load(this.uuid, this.originalFilename);
- for (CloudServiceDefinition service : this.workflowStatus.getSelectedServices()) {
- if (StringUtils.isEmpty(service)) {
- continue;
- }
+
+ // TODO implement tree based processing with nested threads
+
+ for (ServiceNode node : this.workflowStatus.getSelectedServices()) {
// if an error occurred stop the workflow
if (!this.workflowStatus.getErrors().isEmpty()) {
@@ -77,7 +77,7 @@ public class CloudServiceExecutionRunnable implements Runnable {
}
try {
- inputFile = executeCloudService(service, this.workflowStatus, inputFile);
+ inputFile = executeCloudService(node, this.workflowStatus, inputFile);
} catch (ProcessingFailedException e) {
this.workflowStatus.addError(e.getMessage());
}
@@ -102,8 +102,10 @@ public class CloudServiceExecutionRunnable implements Runnable {
}
}
- private Path executeCloudService(CloudServiceDefinition csd, WorkflowStatus workflowStatus, Path inputFile) {
+ private Path executeCloudService(ServiceNode node, WorkflowStatus workflowStatus, Path inputFile) {
try {
+ CloudServiceDefinition csd = node.getService();
+ ServiceConfiguration config = node.getServiceConfiguration();
String serviceKey = csd.getKey();
String serviceName = csd.getName();
String baseUrl = csd.getBaseUrl();
@@ -112,7 +114,6 @@ public class CloudServiceExecutionRunnable implements Runnable {
MultipartBody multipartBody = Unirest.post(baseUrl)
.field("file", Files.newInputStream(inputFile), inputFile.getFileName().toString());
- ServiceConfiguration config = workflowStatus.getConfiguration(serviceKey);
if (config != null) {
config.getParameterList().forEach(param -> {
if (!StringUtils.isEmpty(param.getValue()) && !param.isManagerParameter()) {
@@ -168,9 +169,8 @@ public class CloudServiceExecutionRunnable implements Runnable {
long end = System.currentTimeMillis();
long timeout = 60_000l;
- ServiceConfiguration serviceConfiguration = workflowStatus.getConfiguration(serviceKey);
- if (serviceConfiguration != null) {
- ServiceConfigurationParameter timeoutParam = serviceConfiguration.getParameter("timeout");
+ if (config != null) {
+ ServiceConfigurationParameter timeoutParam = config.getParameter("timeout");
if (timeoutParam != null) {
try {
timeout = Long.valueOf(timeoutParam.getValue());
@@ -294,8 +294,8 @@ public class CloudServiceExecutionRunnable implements Runnable {
}
boolean deleteResult = true;
- if (serviceConfiguration != null) {
- ServiceConfigurationParameter deleteParam = serviceConfiguration.getParameter("deleteResult");
+ if (config != null) {
+ ServiceConfigurationParameter deleteParam = config.getParameter("deleteResult");
if (deleteParam != null) {
deleteResult = Boolean.valueOf(deleteParam.getValue());
}
@@ -316,7 +316,7 @@ public class CloudServiceExecutionRunnable implements Runnable {
return result;
} catch (Exception e) {
- throw new ProcessingFailedException("Error in " + csd.getName() + " workflow: " + e.getMessage(), e);
+ throw new ProcessingFailedException("Error in " + node.getService().getName() + " workflow: " + e.getMessage(), e);
}
}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java
new file mode 100644
index 0000000..8951344
--- /dev/null
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java
@@ -0,0 +1,178 @@
+/*********************************************************************************
+ * Copyright (c) 2020 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.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+
+public class ServiceNode {
+
+ private final String id;
+ private final CloudServiceDefinition service;
+ private final ServiceConfiguration config;
+
+ private final List<ServiceNode> children = new ArrayList<>();
+
+ /**
+ * Create a structural node like for example the root node.
+ */
+ public ServiceNode(String id) {
+ if (id == null) {
+ throw new IllegalArgumentException("id can not be null!");
+ }
+
+ this.id = id;
+ this.service = null;
+ this.config = null;
+ }
+
+ /**
+ * Create a service node for the given {@link CloudServiceDefinition} and
+ * corresponding {@link ServiceConfiguration}.
+ *
+ * @param service The {@link CloudServiceDefinition} to store in this node.
+ * @param config The {@link ServiceConfiguration} for the service.
+ */
+ public ServiceNode(CloudServiceDefinition service, ServiceConfiguration config) {
+ if (service == null) {
+ throw new IllegalArgumentException("service parameter can not be null!");
+ }
+ this.id = service.getKey();
+ this.service = service;
+ this.config = config;
+ }
+
+ /**
+ *
+ * @return The id of this node. A custom string for a structural node, the key
+ * of the {@link CloudServiceDefinition} for a service node.
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ *
+ * @return The {@link CloudServiceDefinition} stored in this node.
+ */
+ public CloudServiceDefinition getService() {
+ return service;
+ }
+
+ /**
+ *
+ * @return The {@link ServiceConfiguration} for the
+ * {@link CloudServiceDefinition} stored in this node. Can be
+ * <code>null</code>.
+ */
+ public ServiceConfiguration getServiceConfiguration() {
+ return config;
+ }
+
+ /**
+ * Adds the given {@link ServiceNode} to the local children list.
+ *
+ * @param node The {@link ServiceNode} to add.
+ */
+ public void addChild(ServiceNode node) {
+ this.children.add(node);
+ }
+
+ /**
+ * Adds a new child that references the given {@link CloudServiceDefinition}
+ * with its corresponding {@link ServiceConfiguration}.
+ *
+ * @param service The {@link CloudServiceDefinition} that should be added as a
+ * child node. Can not be <code>null</code>.
+ * @param config The corresponding {@link ServiceConfiguration} that provides
+ * configurations for the {@link CloudServiceDefinition}. Can be
+ * <code>null</code>.
+ */
+ public void addChild(CloudServiceDefinition service, ServiceConfiguration config) {
+ this.children.add(new ServiceNode(service, config));
+ }
+
+ /**
+ * Removes the given {@link ServiceNode} from the local children list.
+ *
+ * @param node The {@link ServiceNode} to remove.
+ */
+ public void removeChild(ServiceNode node) {
+ this.children.remove(node);
+ }
+
+ /**
+ * Removes the given {@link ServiceNode} with the given id from the local
+ * children list.
+ *
+ * @param id The id of the {@link ServiceNode} to remove.
+ */
+ public void removeChild(String id) {
+ for (Iterator<ServiceNode> it = this.children.iterator(); it.hasNext(); ) {
+ ServiceNode child = it.next();
+ if (child.getId().equals(id)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * Removes the {@link ServiceNode} from the local children list that references
+ * the given {@link CloudServiceDefinition}.
+ *
+ * @param toRemove The {@link CloudServiceDefinition} for which the
+ * {@link ServiceNode} should be removed.
+ */
+ public void removeChild(CloudServiceDefinition toRemove) {
+ for (Iterator<ServiceNode> it = this.children.iterator(); it.hasNext(); ) {
+ ServiceNode child = it.next();
+ if (child.getService().getKey().equals(toRemove.getKey())) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ *
+ * @return An unmodifiable list of the child {@link ServiceNode}s in this node.
+ */
+ public List<ServiceNode> getChildren() {
+ return Collections.unmodifiableList(this.children);
+ }
+
+ /**
+ * Clear the locally mapped services in this node.
+ */
+ public void clearChildren() {
+ this.children.clear();
+ }
+
+ /**
+ *
+ * @return <code>true</code> if this node is only used for structuring (e.g. a
+ * root node), <code>false</code> if it references a service definition.
+ */
+ public boolean isStructuralNode() {
+ return this.service == null;
+ }
+
+ @Override
+ public String toString() {
+ return this.id;
+ }
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
index 81ced37..a85fb56 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
@@ -113,7 +113,7 @@ public class WorkflowConfigController {
this.cloudServiceDefinitions);
// add default service configurations if not specified
- addDefaultConfigurations(workflowStatus);
+ WorkflowStatusHelper.addDefaultConfigurations(workflowStatus);
if (workflowStatus == null) {
redirectAttributes.addFlashAttribute(
@@ -151,42 +151,6 @@ public class WorkflowConfigController {
return "redirect:/workflow?uuid=" + uuid;
}
- private void addDefaultConfigurations(WorkflowStatus workflowStatus) {
- for (CloudServiceDefinition csd : workflowStatus.getSelectedServices()) {
- // check if a configuration is already available
- ServiceConfiguration configuration = workflowStatus.getConfiguration(csd.getKey());
- if (configuration == null) {
- configuration = new ServiceConfiguration(csd.getName());
- workflowStatus.addConfiguration(csd.getKey(), configuration);
- }
-
- ServiceConfigurationParameter timeOut = configuration.getParameter("timeout");
- if (timeOut == null) {
- timeOut = new ServiceConfigurationParameter();
- timeOut.setName("Timeout (in ms)");
- timeOut.setKey("timeout");
- timeOut.setValue("60000");
- if ("app4mc_sim".equals(csd.getKey())) {
- timeOut.setValue("-1");
- }
- timeOut.setType("Long");
- timeOut.setManagerParameter(true);
- configuration.addParameter(timeOut);
- }
-
- ServiceConfigurationParameter deleteResult = configuration.getParameter("deleteResult");
- if (deleteResult == null) {
- deleteResult = new ServiceConfigurationParameter();
- deleteResult.setName("Delete result ");
- deleteResult.setKey("deleteResult");
- deleteResult.setValue("true");
- deleteResult.setType("boolean");
- deleteResult.setManagerParameter(true);
- configuration.addParameter(deleteResult);
- }
- }
- }
-
@ModelAttribute("exampleModelFiles")
public List<String> exampleModelFiles() {
return this.exampleModelFiles;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
index 23d4e6a..de19c13 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
@@ -209,6 +209,8 @@ public class WorkflowController {
return "workflow";
}
+ // TODO extend selection and removal of services to handle parents
+
@PostMapping("/select/{selected}")
public String selectService(
@PathVariable(name = "selected") String selected,
@@ -223,8 +225,7 @@ public class WorkflowController {
return "workflow";
}
- ws.addSelectedService(csd);
- ws.addConfiguration(csd.getKey(), WorkflowStatusHelper.getConfigurationForService(csd));
+ ws.addSelectedService(csd, WorkflowStatusHelper.getConfigurationForService(csd));
// render the form view
return "workflow";
@@ -242,7 +243,6 @@ public class WorkflowController {
if (csd != null) {
ws.removeSelectedService(csd);
- ws.removeConfiguration(selected);
}
// render the form view
@@ -331,11 +331,10 @@ public class WorkflowController {
return ResponseEntity.notFound().build();
}
- // create archive of results
+ // create workflow configuration json
try {
WorkflowStatus config = new WorkflowStatus();
- existing.getSelectedServices().forEach(config::addSelectedService);
- existing.getConfigurations().entrySet().forEach(entry -> config.addConfiguration(entry.getKey(), entry.getValue()));
+ config.addAllSelectedServices(existing.getSelectedServices());
return ResponseEntity
.ok()
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
index 6d95103..440ffa7 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
@@ -97,7 +97,7 @@ public class WorkflowRestController {
this.cloudServiceDefinitions);
// add default service configurations if not specified
- addDefaultConfigurations(workflowStatus);
+ WorkflowStatusHelper.addDefaultConfigurations(workflowStatus);
if (workflowStatus == null) {
return ResponseEntity
@@ -246,42 +246,6 @@ public class WorkflowRestController {
return ResponseEntity.ok().build();
}
- private void addDefaultConfigurations(WorkflowStatus workflowStatus) {
- for (CloudServiceDefinition csd : workflowStatus.getSelectedServices()) {
- // check if a configuration is already available
- ServiceConfiguration configuration = workflowStatus.getConfiguration(csd.getKey());
- if (configuration == null) {
- configuration = new ServiceConfiguration(csd.getName());
- workflowStatus.addConfiguration(csd.getKey(), configuration);
- }
-
- ServiceConfigurationParameter timeOut = configuration.getParameter("timeout");
- if (timeOut == null) {
- timeOut = new ServiceConfigurationParameter();
- timeOut.setName("Timeout (in ms)");
- timeOut.setKey("timeout");
- timeOut.setValue("60000");
- if ("app4mc_sim".equals(csd.getKey())) {
- timeOut.setValue("-1");
- }
- timeOut.setType("Long");
- timeOut.setManagerParameter(true);
- configuration.addParameter(timeOut);
- }
-
- ServiceConfigurationParameter deleteResult = configuration.getParameter("deleteResult");
- if (deleteResult == null) {
- deleteResult = new ServiceConfigurationParameter();
- deleteResult.setName("Delete result ");
- deleteResult.setKey("deleteResult");
- deleteResult.setValue("true");
- deleteResult.setType("boolean");
- deleteResult.setManagerParameter(true);
- configuration.addParameter(deleteResult);
- }
- }
- }
-
@PreDestroy
public void dispose() {
this.executor.shutdownNow();
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java
index 99d4f25..74bf577 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java
@@ -16,18 +16,23 @@ package org.eclipse.app4mc.cloud.manager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using = WorkflowStatusSerializer.class)
public class WorkflowStatus {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowStatus.class);
+
private String name;
private String uuid;
- private ArrayList<CloudServiceDefinition> selectedServices = new ArrayList<>();
- private HashMap<String, ServiceConfiguration> serviceConfigurations = new LinkedHashMap<>();
+ private ServiceNode rootNode = new ServiceNode("root");
+
private ArrayList<String> messages = new ArrayList<>();
private ArrayList<String> errors = new ArrayList<>();
private HashMap<String, String> results = new LinkedHashMap<>();
@@ -43,6 +48,11 @@ public class WorkflowStatus {
this.name = name;
}
+ /**
+ *
+ * @return The unique id of the workflow execution or <code>null</code> if the
+ * execution was not performed yet.
+ */
public String getUuid() {
return uuid;
}
@@ -51,32 +61,178 @@ public class WorkflowStatus {
this.uuid = uuid;
}
- public ArrayList<CloudServiceDefinition> getSelectedServices() {
- return selectedServices;
+ /**
+ *
+ * @return The services on root level configured for this {@link WorkflowStatus}.
+ */
+ public List<ServiceNode> getSelectedServices() {
+ return this.rootNode.getChildren();
+ }
+
+ /**
+ * Add a {@link CloudServiceDefinition} and its corresponding
+ * {@link ServiceConfiguration} on the root level of the services to execute by
+ * this {@link WorkflowStatus}.
+ *
+ * @param service The {@link CloudServiceDefinition} to add.
+ * @param config The {@link ServiceConfiguration} for the given
+ * {@link CloudServiceDefinition}. Can be <code>null</code>.
+ */
+ public void addSelectedService(CloudServiceDefinition service, ServiceConfiguration config) {
+ this.rootNode.addChild(new ServiceNode(service, config));
}
- public void addSelectedService(CloudServiceDefinition service) {
- this.selectedServices.add(service);
+ /**
+ * Add a {@link CloudServiceDefinition} and its corresponding
+ * {@link ServiceConfiguration} as a child of an already registered service node
+ * denoted by the given parent key.
+ *
+ * @param parentKey The key to identify an existing service node. A service path
+ * can be represented as key by concatenating the keys with a
+ * dot, e.g. migration.validation if validation is a child of
+ * migration. Note: root should not be added as part of the key.
+ * @param service The {@link CloudServiceDefinition} to add.
+ * @param config The {@link ServiceConfiguration} for the given
+ * {@link CloudServiceDefinition}. Can be <code>null</code>.
+ */
+ public void addSelectedService(String parentKey, CloudServiceDefinition service, ServiceConfiguration config) {
+ String[] keys = parentKey.split("\\.");
+ ServiceNode parentNode = this.rootNode;
+ ServiceNode node = null;
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ node = parentNode.getChildren().stream()
+ .filter(child -> child.getId().equals(key))
+ .findFirst()
+ .orElse(null);
+
+ if (i == (keys.length - 1)) {
+ if (node != null) {
+ // we found a node, so simply add the child
+ node.addChild(new ServiceNode(service, config));
+ } else if (node == null) {
+ // we did not find the node, so probably a structural node should be added
+ node = new ServiceNode(key);
+ node.addChild(new ServiceNode(service, config));
+ parentNode.addChild(node);
+ }
+ }
+
+ parentNode = node;
+ }
}
- public void removeSelectedService(CloudServiceDefinition service) {
- this.selectedServices.remove(service);
+ /**
+ * Adds all provided {@link ServiceNode}s on root level of this
+ * {@link WorkflowStatus}. Used for copy mechanisms.
+ *
+ * @param nodes The nodes to add on root level.
+ */
+ public void addAllSelectedServices(List<ServiceNode> nodes) {
+ nodes.forEach(this.rootNode::addChild);
}
- public ServiceConfiguration getConfiguration(String key) {
- return this.serviceConfigurations.get(key);
+ /**
+ * Removes the given {@link CloudServiceDefinition} from the root level of this
+ * {@link WorkflowStatus}.
+ *
+ * @param service The {@link CloudServiceDefinition} to remove.
+ */
+ public void removeSelectedService(CloudServiceDefinition service) {
+ this.rootNode.removeChild(service);
+ }
+
+ /**
+ * Removes the given {@link CloudServiceDefinition} from the service node
+ * denoted by the given parent key.
+ *
+ * @param parentKey The key to identify the existing service node from which the
+ * given service should be removed. A service path can be
+ * represented as key by concatenating the keys with a dot,
+ * e.g. migration.validation if validation is a child of
+ * migration. Note: root should not be added as part of the
+ * key.
+ * @param service The {@link CloudServiceDefinition} to remove.
+ */
+ public void removeSelectedService(String parentKey, CloudServiceDefinition service) {
+ ServiceNode node = getServiceNode(parentKey);
+ if (node != null) {
+ node.removeChild(service);
+ }
}
- public HashMap<String, ServiceConfiguration> getConfigurations() {
- return this.serviceConfigurations;
+ /**
+ * Removes a service node denoted by the given key.
+ *
+ * @param key The key to identify the service node to remove. A service path can
+ * be represented as key by concatenating the keys with a dot, e.g.
+ * migration.validation if validation is a child of migration. Note:
+ * root should not be added as part of the key.
+ */
+ public void removeServiceNode(String key) {
+ // find the parent node
+ ServiceNode node = key.contains(".") ? getServiceNode(key.substring(0, key.lastIndexOf('.'))) : this.rootNode;
+ node.removeChild(key.substring(key.lastIndexOf('.') + 1));
}
- public void addConfiguration(String key, ServiceConfiguration config) {
- this.serviceConfigurations.put(key, config);
+ /**
+ *
+ * @param key The key for which the {@link CloudServiceDefinition} is requested. A
+ * key is typically the key of a {@link CloudServiceDefinition}. It
+ * is possible to provide a key path by concatenating keys with a
+ * dot, e.g. migration.validation in case validation is a child of
+ * migration.
+ * @return The {@link CloudServiceDefinition} for the specified key or
+ * <code>null</code> if there is no {@link CloudServiceDefinition} for the
+ * specified key registered.
+ */
+ public CloudServiceDefinition getService(String key) {
+ ServiceNode node = getServiceNode(key);
+ if (node != null) {
+ return node.getService();
+ }
+ return null;
}
- public void removeConfiguration(String key) {
- this.serviceConfigurations.remove(key);
+ /**
+ *
+ * @param key The key for which the {@link ServiceConfiguration} is requested. A
+ * key is typically the key of a {@link CloudServiceDefinition}. It
+ * is possible to provide a key path by concatenating keys with a
+ * dot, e.g. migration.validation in case validation is a child of
+ * migration.
+ * @return The {@link ServiceConfiguration} for the specified key or
+ * <code>null</code> if there is no {@link ServiceConfiguration} for the
+ * specified key registered.
+ */
+ public ServiceConfiguration getConfiguration(String key) {
+ ServiceNode node = getServiceNode(key);
+ if (node != null) {
+ return node.getServiceConfiguration();
+ }
+ return null;
+ }
+
+ private ServiceNode getServiceNode(String nodeKey) {
+ String[] keys = nodeKey.split("\\.");
+ ServiceNode parentNode = this.rootNode;
+ ServiceNode node = null;
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ node = parentNode.getChildren().stream()
+ .filter(child -> child.getId().equals(key))
+ .findFirst()
+ .orElse(null);
+
+ if (node == null) {
+ LOGGER.warn("Could not resolve key '{}' in nodeKey '{}'", key, nodeKey);
+ break;
+ }
+
+ parentNode = node;
+ }
+
+ return node;
}
public ArrayList<String> getMessages() {
@@ -126,8 +282,7 @@ public class WorkflowStatus {
}
public void clear() {
- this.selectedServices.clear();
- this.serviceConfigurations.clear();
+ this.rootNode.clearChildren();
this.messages.clear();
this.errors.clear();
this.results.clear();
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java
index 25a4aa2..fbdbadc 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java
+++ b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java
@@ -14,7 +14,9 @@
package org.eclipse.app4mc.cloud.manager;
import java.io.IOException;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
@@ -61,27 +63,7 @@ public class WorkflowStatusDeserializer extends StdDeserializer<WorkflowStatus>
JsonNode services = node.get("services");
if (services != null) {
services.fields().forEachRemaining(serviceField -> {
- // resolve the service
- String serviceKey = serviceField.getKey();
- CloudServiceDefinition csd = this.cloudServiceDefinitions.stream()
- .filter(sd -> sd.getKey().equals(serviceKey))
- .findFirst()
- .orElse(null);
- if (csd != null) {
- result.addSelectedService(csd);
-
- // resolve the service configuration
- ServiceConfiguration config = WorkflowStatusHelper.getConfigurationForService(csd);
- if (config != null) {
- serviceField.getValue().fields().forEachRemaining(configField -> {
- ServiceConfigurationParameter param = config.getParameter(configField.getKey());
- if (param != null) {
- param.setValue(configField.getValue().asText());
- }
- });
- result.addConfiguration(serviceKey, config);
- }
- }
+ deserializeServiceNode(result, null, serviceField.getKey(), serviceField.getValue().fields());
});
}
@@ -111,4 +93,51 @@ public class WorkflowStatusDeserializer extends StdDeserializer<WorkflowStatus>
return result;
}
+
+ private void deserializeServiceNode(
+ WorkflowStatus result,
+ String parentKey,
+ String serviceKey,
+ Iterator<Map.Entry<String, JsonNode>> fields) {
+
+ CloudServiceDefinition csd = this.cloudServiceDefinitions.stream()
+ .filter(sd -> sd.getKey().equals(serviceKey))
+ .findFirst()
+ .orElse(null);
+ if (csd != null) {
+ // resolve the service configuration
+ ServiceConfiguration config = WorkflowStatusHelper.getConfigurationForService(csd);
+
+ // add the service to the workflow status so that the recursive calls work for nested service nodes
+ if (parentKey == null) {
+ result.addSelectedService(csd, config);
+ } else {
+ result.addSelectedService(parentKey, csd, config);
+ }
+
+ fields.forEachRemaining(configField -> {
+ if (!configField.getValue().isContainerNode()) {
+ // service parameter are simple values, no containers
+ ServiceConfigurationParameter param = config.getParameter(configField.getKey());
+ if (param != null) {
+ param.setValue(configField.getValue().asText());
+ }
+ } else {
+ // resolve nested services
+ deserializeServiceNode(
+ result,
+ parentKey != null ? parentKey + "." + serviceKey : serviceKey,
+ configField.getKey(),
+ configField.getValue().fields());
+ }
+ });
+ } else {
+ // there is no service definition for the given key, so it is probably a structural node
+ String nodeKey = parentKey != null ? parentKey + "." + serviceKey : serviceKey;
+
+ fields.forEachRemaining(serviceField -> {
+ deserializeServiceNode(result, nodeKey, serviceField.getKey(), serviceField.getValue().fields());
+ });
+ }
+ }
}
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 c815aa9..7b91cc7 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
@@ -134,6 +134,60 @@ public final class WorkflowStatusHelper {
}
/**
+ * Helper method that adds default configuration parameter to a
+ * {@link WorkflowStatus} in case they are not set.
+ *
+ * @param workflowStatus The {@link WorkflowStatus} that should be enriched by
+ * default configuration parameter.
+ */
+ public static void addDefaultConfigurations(WorkflowStatus workflowStatus) {
+ for (ServiceNode node : workflowStatus.getSelectedServices()) {
+ addDefaultConfigurations(node);
+ }
+ }
+
+ /**
+ * Helper method that adds default configuration parameter to a
+ * {@link ServiceNode} in case they are not set.
+ *
+ * @param node The {@link ServiceNode} that should be enriched by
+ * default configuration parameter.
+ */
+ public static void addDefaultConfigurations(ServiceNode node) {
+ // check if a configuration is already available
+ ServiceConfiguration configuration = node.getServiceConfiguration();
+
+ ServiceConfigurationParameter timeOut = configuration.getParameter("timeout");
+ if (timeOut == null) {
+ timeOut = new ServiceConfigurationParameter();
+ timeOut.setName("Timeout (in ms)");
+ timeOut.setKey("timeout");
+ timeOut.setValue("60000");
+ if ("app4mc_sim".equals(node.getService().getKey())) {
+ timeOut.setValue("-1");
+ }
+ timeOut.setType("Long");
+ timeOut.setManagerParameter(true);
+ configuration.addParameter(timeOut);
+ }
+
+ ServiceConfigurationParameter deleteResult = configuration.getParameter("deleteResult");
+ if (deleteResult == null) {
+ deleteResult = new ServiceConfigurationParameter();
+ deleteResult.setName("Delete result ");
+ deleteResult.setKey("deleteResult");
+ deleteResult.setValue("true");
+ deleteResult.setType("boolean");
+ deleteResult.setManagerParameter(true);
+ configuration.addParameter(deleteResult);
+ }
+
+ for (ServiceNode child : node.getChildren()) {
+ addDefaultConfigurations(child);
+ }
+ }
+
+ /**
* Serializes the given {@link WorkflowStatus} to the provided {@link File} as JSON.
*
* @param ws The {@link WorkflowStatus} to serialize.
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 5b4c41f..2f487bc 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
@@ -52,20 +52,8 @@ public class WorkflowStatusSerializer extends StdSerializer<WorkflowStatus> {
gen.writeFieldName("services");
gen.writeStartObject();
- for (CloudServiceDefinition csd : value.getSelectedServices()) {
- gen.writeFieldName(csd.getKey());
- gen.writeStartObject();
-
- ServiceConfiguration configuration = value.getConfiguration(csd.getKey());
- if (configuration != null) {
- for (ServiceConfigurationParameter param : configuration.getParameterList()) {
- if (param.getValue() != null) {
- gen.writeObjectField(param.getKey(), param.getValue());
- }
- }
- }
-
- gen.writeEndObject();
+ for (ServiceNode node : value.getSelectedServices()) {
+ serializeServiceNode(node, gen);
}
gen.writeEndObject();
@@ -89,4 +77,28 @@ public class WorkflowStatusSerializer extends StdSerializer<WorkflowStatus> {
gen.writeEndObject();
}
+
+ private void serializeServiceNode(ServiceNode node, JsonGenerator gen) throws IOException {
+ gen.writeFieldName(node.getId());
+ gen.writeStartObject();
+
+ 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());
+ }
+ }
+ }
+ }
+
+ if (!node.getChildren().isEmpty()) {
+ for (ServiceNode child : node.getChildren()) {
+ serializeServiceNode(child, gen);
+ }
+ }
+
+ gen.writeEndObject();
+ }
}
diff --git a/manager/src/main/resources/templates/selectedServices.html b/manager/src/main/resources/templates/selectedServices.html
index e195987..d8383ae 100644
--- a/manager/src/main/resources/templates/selectedServices.html
+++ b/manager/src/main/resources/templates/selectedServices.html
@@ -7,16 +7,16 @@
<body>
<div th:fragment="servicesList" id="selectedServices" th:object="${workflowStatus}">
<ul class="list-group">
- <li th:each="selected : ${workflowStatus.selectedServices}" class="list-group-item d-flex justify-content-between" th:classappend="*{uuid != null} ? disabled_service : ''">
+ <li th:each="selected : *{selectedServices}" class="list-group-item d-flex justify-content-between" th:classappend="*{uuid != null} ? disabled_service : ''">
<p
class="p-0 flex-grow-1 plist"
- th:text="${selected.name}"
- th:title="${selected.description}"
+ th:text="${selected.service.name}"
+ th:title="${selected.service.description}"
data-toggle="tooltip"
th:classappend="*{uuid != null} ? disabled_service : ''">Service</p>
<a
th:if="*{uuid == null}"
- th:attr="onclick=|removeSelectedServices('${selected.key}')|"
+ th:attr="onclick=|removeSelectedServices('${selected.service.key}')|"
class="btn btn-primary btn-sm btn-rmv">
<i class="fas fa-times"></i>
</a>
@@ -36,17 +36,17 @@
</option>
</select><br>
- <div th:if="not *{configurations.isEmpty()}">
- <div th:each="config : *{configurations}" class="mb-3">
- <h4 th:text="${config.value.serviceName + ' Configuration'}">Config</h4>
- <div th:each="parameter, parameterStatus : ${config.value.parameterList}">
+ <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: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"
type="text"
- th:field="*{configurations[__${config.key}__].parameterList[__${parameterStatus.index}__].value}"
+ th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
th:disabled="*{uuid != null}"/>
</div>
<!-- single boolean value -->
@@ -54,7 +54,7 @@
<input
class="form-check-input"
type="checkbox"
- th:field="*{configurations[__${config.key}__].parameterList[__${parameterStatus.index}__].value}"
+ 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>
@@ -65,7 +65,7 @@
<div th:each="pv : ${parameter.possibleValues}" class="form-check">
<input
type="checkbox"
- th:field="*{configurations[__${config.key}__].parameterList[__${parameterStatus.index}__].value}"
+ th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
th:value="${pv}"
th:disabled="*{uuid != null}">
<label class="form-check-label" th:text="${pv}">Value</label>
@@ -76,7 +76,7 @@
<label th:text="${parameter.name}">Label</label>
<select
class="custom-select"
- th:field="*{configurations[__${config.key}__].parameterList[__${parameterStatus.index}__].value}"
+ th:field="*{selectedServices[__${nodeStatus.index}__].serviceConfiguration.parameterList[__${parameterStatus.index}__].value}"
th:disabled="*{uuid != null}">
<option value=""></option>
<option
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 c89cdcd..9d15843 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
@@ -16,11 +16,13 @@ package org.eclipse.app4mc.cloud.manager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
import org.junit.jupiter.api.BeforeAll;
@@ -33,38 +35,149 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
public class WorkflowStatusHelperTest {
- private static String JSON_STRING;
+ private static String JSON_STRING_SIMPLE_CHAIN;
+ private static String JSON_STRING_ALL_NESTED;
+ private static String JSON_STRING_STRUCTURED_NESTED;
+ private static CloudServiceDefinition MIGRATION = new CloudServiceDefinition(
+ "migration",
+ "Migration",
+ "http://localhost:8080/app4mc/converter/",
+ "Migrates an input model file to model version 0.9.9");
+ private static CloudServiceDefinition VALIDATION = new CloudServiceDefinition(
+ "validation",
+ "Validation",
+ "http://localhost:8181/app4mc/validation/",
+ "Validates the input model file");
+
+ 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");
+ 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");
+ 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");
+ 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");
+
+ private static CloudServiceDefinition CHART_VISUALIZER = new CloudServiceDefinition(
+ "chart_visualizer",
+ "Chart Visualizer",
+ "http://localhost:8083/app4mc/visualization/barchart/",
+ "Visualization of interpreter results");
+
@BeforeAll
public static void beforeAll() {
- StringBuilder builder = new StringBuilder();
- builder.append("{").append(System.lineSeparator());
- builder.append(" \"name\" : \"Test Name\",").append(System.lineSeparator());
- builder.append(" \"uuid\" : \"1234\",").append(System.lineSeparator());
- builder.append(" \"services\" : {").append(System.lineSeparator());
- builder.append(" \"label_core_interpreter\" : {").append(System.lineSeparator());
- builder.append(" \"timeout\" : \"360000\",").append(System.lineSeparator());
- builder.append(" \"deleteResult\" : \"false\"").append(System.lineSeparator());
- builder.append(" },").append(System.lineSeparator());
- builder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
- builder.append(" },").append(System.lineSeparator());
- builder.append(" \"cancelled\" : false,").append(System.lineSeparator());
- builder.append(" \"done\" : true,").append(System.lineSeparator());
- builder.append(" \"messages\" : [ \"Message One\", \"Message Two\", \"Message Three\" ],").append(System.lineSeparator());
- builder.append(" \"errors\" : [ ],").append(System.lineSeparator());
- builder.append(" \"results\" : {").append(System.lineSeparator());
- builder.append(" \"Label per Core Interpreter Result\" : \"_label per core interpreter/nodesdata.json\",").append(System.lineSeparator());
- builder.append(" \"Chart Visualizer Result\" : \"_chart visualizer/barchart.html\"").append(System.lineSeparator());
- builder.append(" }").append(System.lineSeparator());
- builder.append("}");
-
- JSON_STRING = builder.toString();
+ StringBuilder simpleChainBuilder = new StringBuilder();
+ simpleChainBuilder.append("{").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"name\" : \"Test Name\",").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"uuid\" : \"1234\",").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"services\" : {").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"label_core_interpreter\" : {").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"timeout\" : \"360000\",").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"deleteResult\" : \"false\"").append(System.lineSeparator());
+ simpleChainBuilder.append(" },").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
+ simpleChainBuilder.append(" },").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"cancelled\" : false,").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"done\" : true,").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"messages\" : [ \"Message One\", \"Message Two\", \"Message Three\" ],").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"results\" : {").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"Label per Core Interpreter Result\" : \"_label per core interpreter/nodesdata.json\",").append(System.lineSeparator());
+ simpleChainBuilder.append(" \"Chart Visualizer Result\" : \"_chart visualizer/barchart.html\"").append(System.lineSeparator());
+ simpleChainBuilder.append(" }").append(System.lineSeparator());
+ simpleChainBuilder.append("}");
+
+ JSON_STRING_SIMPLE_CHAIN = simpleChainBuilder.toString();
+
+ StringBuilder allNestedBuilder = new StringBuilder();
+ allNestedBuilder.append("{").append(System.lineSeparator());
+ allNestedBuilder.append(" \"services\" : {").append(System.lineSeparator());
+ allNestedBuilder.append(" \"migration\" : {").append(System.lineSeparator());
+ allNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ allNestedBuilder.append(" \"deleteResult\" : \"true\",").append(System.lineSeparator());
+ allNestedBuilder.append(" \"validation\" : {").append(System.lineSeparator());
+ allNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ allNestedBuilder.append(" \"deleteResult\" : \"true\",").append(System.lineSeparator());
+ allNestedBuilder.append(" \"label_core_interpreter\" : { }").append(System.lineSeparator());
+ allNestedBuilder.append(" }").append(System.lineSeparator());
+ allNestedBuilder.append(" }").append(System.lineSeparator());
+ allNestedBuilder.append(" }").append(System.lineSeparator());
+ allNestedBuilder.append("}");
+
+ JSON_STRING_ALL_NESTED = allNestedBuilder.toString();
+
+ StringBuilder structuredNestedBuilder = new StringBuilder();
+ structuredNestedBuilder.append("{").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"name\" : \"Test Name\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"uuid\" : \"1234\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"services\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"migration\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"true\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"validation\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"true\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"profiles\" : \"Amalthea Standard Validations\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_core_chart\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_core_interpreter\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"360000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"false\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_memory_chart\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_memory_interpreter\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"true\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_task_chart\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_task_interpreter\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"true\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_size_chart\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"label_size_interpreter\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"timeout\" : \"60000\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"deleteResult\" : \"true\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"chart_visualizer\" : { }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" }").append(System.lineSeparator());
+ structuredNestedBuilder.append(" },").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"cancelled\" : false,").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"done\" : true,").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"messages\" : [ \"Message One\", \"Message Two\", \"Message Three\" ],").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"results\" : {").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"Label per Core Interpreter Result\" : \"_label per core interpreter/nodesdata.json\",").append(System.lineSeparator());
+ structuredNestedBuilder.append(" \"Chart Visualizer Result\" : \"_chart visualizer/barchart.html\"").append(System.lineSeparator());
+ structuredNestedBuilder.append(" }").append(System.lineSeparator());
+ structuredNestedBuilder.append("}");
+
+ JSON_STRING_STRUCTURED_NESTED = structuredNestedBuilder.toString();
}
@Test
public void shouldSerializeWorkflowStatus() throws JsonProcessingException {
- CloudServiceDefinition service1 = new CloudServiceDefinition("label_core_interpreter", "Label per Core Interpreter", "http://localhost:8080/test", "BlaBlaBla");
- CloudServiceDefinition service2 = new CloudServiceDefinition("chart_visualizer", "Chart Visualizer", "http://localhost:8080/another", "FuBar");
+ ServiceConfiguration config1 = WorkflowStatusHelper.getConfigurationForService(LABEL_CORE);
+ config1.getParameter("timeout").setValue("360000");
+ config1.getParameter("deleteResult").setValue("false");
ObjectMapper mapper = new ObjectMapper();
@@ -73,14 +186,9 @@ public class WorkflowStatusHelperTest {
ws.setUuid("1234");
ws.done();
- ws.addSelectedService(service1);
- ws.addSelectedService(service2);
+ ws.addSelectedService(LABEL_CORE, config1);
+ ws.addSelectedService(CHART_VISUALIZER, null);
- ServiceConfiguration config1 = WorkflowStatusHelper.getConfigurationForService(service1);
- config1.getParameter("timeout").setValue("360000");
- config1.getParameter("deleteResult").setValue("false");
- ws.addConfiguration(service1.getKey(), config1);
-
ws.addMessage("Message One");
ws.addMessage("Message Two");
ws.addMessage("Message Three");
@@ -90,20 +198,17 @@ public class WorkflowStatusHelperTest {
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ws);
- assertEquals(JSON_STRING, json);
+ assertEquals(JSON_STRING_SIMPLE_CHAIN, json);
}
@Test
public void shouldDeserializeWorkflowStatus() throws JsonMappingException, JsonProcessingException {
- CloudServiceDefinition service1 = new CloudServiceDefinition("label_core_interpreter", "Label per Core Interpreter", "http://localhost:8080/test", "BlaBlaBla");
- CloudServiceDefinition service2 = new CloudServiceDefinition("chart_visualizer", "Chart Visualizer", "http://localhost:8080/another", "FuBar");
-
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
- module.addDeserializer(WorkflowStatus.class, new WorkflowStatusDeserializer(Arrays.asList(service1, service2)));
+ module.addDeserializer(WorkflowStatus.class, new WorkflowStatusDeserializer(Arrays.asList(LABEL_CORE, CHART_VISUALIZER)));
mapper.registerModule(module);
- WorkflowStatus ws = mapper.readValue(JSON_STRING, WorkflowStatus.class);
+ WorkflowStatus ws = mapper.readValue(JSON_STRING_SIMPLE_CHAIN, WorkflowStatus.class);
assertEquals("Test Name", ws.getName());
assertEquals("1234", ws.getUuid());
@@ -127,24 +232,281 @@ public class WorkflowStatusHelperTest {
assertEquals("_label per core interpreter/nodesdata.json", results.get("Label per Core Interpreter Result"));
assertEquals("_chart visualizer/barchart.html", results.get("Chart Visualizer Result"));
- ArrayList<CloudServiceDefinition> services = ws.getSelectedServices();
+ List<ServiceNode> services = ws.getSelectedServices();
assertNotNull(services);
assertEquals(2, services.size());
- assertEquals("label_core_interpreter", services.get(0).getKey());
- assertEquals("Label per Core Interpreter", services.get(0).getName());
- assertEquals("http://localhost:8080/test", services.get(0).getBaseUrl());
- assertEquals("BlaBlaBla", services.get(0).getDescription());
- assertEquals("chart_visualizer", services.get(1).getKey());
- assertEquals("Chart Visualizer", services.get(1).getName());
- assertEquals("http://localhost:8080/another", services.get(1).getBaseUrl());
- assertEquals("FuBar", services.get(1).getDescription());
-
- HashMap<String,ServiceConfiguration> configurations = ws.getConfigurations();
- assertEquals(2, configurations.size());
- assertEquals("360000", configurations.get("label_core_interpreter").getParameter("timeout").getValue());
- assertEquals("false", configurations.get("label_core_interpreter").getParameter("deleteResult").getValue());
+ assertEquals("label_core_interpreter", services.get(0).getService().getKey());
+ assertEquals("Label per Core Interpreter", services.get(0).getService().getName());
+ assertEquals("http://localhost:8084/app4mc/interpreter/label-per-core/", services.get(0).getService().getBaseUrl());
+ assertEquals("Parses an Amalthea model to inspect label access operations per core", services.get(0).getService().getDescription());
+ assertEquals(0, services.get(0).getChildren().size());
+ assertEquals("chart_visualizer", services.get(1).getService().getKey());
+ assertEquals("Chart Visualizer", services.get(1).getService().getName());
+ assertEquals("http://localhost:8083/app4mc/visualization/barchart/", services.get(1).getService().getBaseUrl());
+ assertEquals("Visualization of interpreter results", services.get(1).getService().getDescription());
+ assertEquals(0, services.get(1).getChildren().size());
+
+ assertEquals("360000", ws.getConfiguration("label_core_interpreter").getParameter("timeout").getValue());
+ assertEquals("false", ws.getConfiguration("label_core_interpreter").getParameter("deleteResult").getValue());
// check that the default configuration set as no configuration is in the JSON
- assertEquals("60000", configurations.get("chart_visualizer").getParameter("timeout").getValue());
- assertEquals("true", configurations.get("chart_visualizer").getParameter("deleteResult").getValue());
+ assertEquals("60000", ws.getConfiguration("chart_visualizer").getParameter("timeout").getValue());
+ assertEquals("true", ws.getConfiguration("chart_visualizer").getParameter("deleteResult").getValue());
+ }
+
+
+ @Test
+ public void shouldSerializeAllNestedServices() throws JsonProcessingException {
+ WorkflowStatus ws = new WorkflowStatus();
+ ws.addSelectedService(MIGRATION, WorkflowStatusHelper.getConfigurationForService(MIGRATION));
+ ws.addSelectedService("migration", VALIDATION, WorkflowStatusHelper.getConfigurationForService(VALIDATION));
+ ws.addSelectedService("migration.validation", LABEL_CORE, null);
+
+ ObjectMapper mapper = new ObjectMapper();
+ String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ws);
+
+ assertEquals(JSON_STRING_ALL_NESTED, json);
+ }
+
+ @Test
+ public void shouldDeserializeAllNestedServices() throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(WorkflowStatus.class, new WorkflowStatusDeserializer(Arrays.asList(MIGRATION, VALIDATION, LABEL_CORE)));
+ mapper.registerModule(module);
+
+ WorkflowStatus ws = mapper.readValue(JSON_STRING_ALL_NESTED, WorkflowStatus.class);
+
+ List<ServiceNode> services = ws.getSelectedServices();
+ assertNotNull(services);
+ assertEquals(1, services.size());
+ assertEquals("migration", services.get(0).getService().getKey());
+ assertEquals(1, services.get(0).getChildren().size());
+
+ ServiceNode child = services.get(0).getChildren().get(0);
+ assertEquals("validation", child.getService().getKey());
+ assertEquals(1, child.getChildren().size());
+
+ child = child.getChildren().get(0);
+ assertEquals("label_core_interpreter", child.getService().getKey());
+ assertEquals(0, child.getChildren().size());
+ }
+
+ @Test
+ public void shouldSerializeWorkflowStatusStructuredNested() throws JsonProcessingException {
+ ServiceConfiguration validationConfig = WorkflowStatusHelper.getConfigurationForService(VALIDATION);
+ ServiceConfigurationParameter validationProfiles = new ServiceConfigurationParameter();
+ validationProfiles.setName("Validation profiles");
+ validationProfiles.setKey("profiles");
+ validationProfiles.setType("String");
+ validationProfiles.setCardinality("multiple");
+ validationProfiles.setPossibleValues(Arrays.asList("Amalthea Standard Validations", "INCHRON Validations"));
+ validationConfig.addParameter(validationProfiles);
+
+ validationConfig.getParameter("profiles").setValue("Amalthea Standard Validations");
+
+ ServiceConfiguration labelCoreConfig = WorkflowStatusHelper.getConfigurationForService(LABEL_CORE);
+ labelCoreConfig.getParameter("timeout").setValue("360000");
+ labelCoreConfig.getParameter("deleteResult").setValue("false");
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ WorkflowStatus ws = new WorkflowStatus();
+ ws.setName("Test Name");
+ ws.setUuid("1234");
+ ws.done();
+
+ ws.addSelectedService(MIGRATION, WorkflowStatusHelper.getConfigurationForService(MIGRATION));
+ ws.addSelectedService(VALIDATION, validationConfig);
+
+ // create structural nodes for sub chains
+ ws.addSelectedService("validation.label_core_chart", LABEL_CORE, labelCoreConfig);
+ ws.addSelectedService("validation.label_core_chart", CHART_VISUALIZER, null);
+
+ ws.addSelectedService("validation.label_memory_chart", LABEL_MEMORY, WorkflowStatusHelper.getConfigurationForService(LABEL_MEMORY));
+ ws.addSelectedService("validation.label_memory_chart", CHART_VISUALIZER, null);
+
+ ws.addSelectedService("validation.label_task_chart", LABEL_TASK, WorkflowStatusHelper.getConfigurationForService(LABEL_TASK));
+ ws.addSelectedService("validation.label_task_chart", CHART_VISUALIZER, null);
+
+ ws.addSelectedService("validation.label_size_chart", LABEL_SIZE, WorkflowStatusHelper.getConfigurationForService(LABEL_SIZE));
+ ws.addSelectedService("validation.label_size_chart", CHART_VISUALIZER, null);
+
+
+ ws.addMessage("Message One");
+ ws.addMessage("Message Two");
+ ws.addMessage("Message Three");
+
+ ws.addResult("Label per Core Interpreter Result", "_label per core interpreter/nodesdata.json");
+ ws.addResult("Chart Visualizer Result", "_chart visualizer/barchart.html");
+
+ String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ws);
+
+ assertEquals(JSON_STRING_STRUCTURED_NESTED, json);
+ }
+
+ @Test
+ public void shouldDeserializeWorkflowStatusStructuredNested() throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(
+ WorkflowStatus.class,
+ new WorkflowStatusDeserializer(Arrays.asList(MIGRATION, VALIDATION, LABEL_CORE, LABEL_MEMORY, LABEL_TASK, LABEL_SIZE, CHART_VISUALIZER)));
+ mapper.registerModule(module);
+
+ WorkflowStatus ws = mapper.readValue(JSON_STRING_STRUCTURED_NESTED, WorkflowStatus.class);
+
+ List<ServiceNode> services = ws.getSelectedServices();
+ assertNotNull(services);
+ assertEquals(2, services.size());
+ assertEquals("migration", services.get(0).getService().getKey());
+ assertEquals(0, services.get(0).getChildren().size());
+
+ assertEquals("validation", services.get(1).getService().getKey());
+ // validation has 4 structure nodes for 4 different sub process chains
+ assertEquals(4, services.get(1).getChildren().size());
+
+ ServiceNode child = services.get(1).getChildren().get(0);
+ assertTrue(child.isStructuralNode(), "child is not a structural node");
+ assertEquals("label_core_chart", child.getId());
+ assertEquals(2, child.getChildren().size());
+
+ ServiceNode subChild = child.getChildren().get(0);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("label_core_interpreter", subChild.getId());
+ assertEquals("label_core_interpreter", subChild.getService().getKey());
+ assertEquals("360000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("false", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ subChild = child.getChildren().get(1);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("chart_visualizer", subChild.getId());
+ assertEquals("chart_visualizer", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ child = services.get(1).getChildren().get(1);
+ assertTrue(child.isStructuralNode(), "child is not a structural node");
+ assertEquals("label_memory_chart", child.getId());
+ assertEquals(2, child.getChildren().size());
+
+ subChild = child.getChildren().get(0);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("label_memory_interpreter", subChild.getId());
+ assertEquals("label_memory_interpreter", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ subChild = child.getChildren().get(1);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("chart_visualizer", subChild.getId());
+ assertEquals("chart_visualizer", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ child = services.get(1).getChildren().get(2);
+ assertTrue(child.isStructuralNode(), "child is not a structural node");
+ assertEquals("label_task_chart", child.getId());
+ assertEquals(2, child.getChildren().size());
+
+ subChild = child.getChildren().get(0);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("label_task_interpreter", subChild.getId());
+ assertEquals("label_task_interpreter", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ subChild = child.getChildren().get(1);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("chart_visualizer", subChild.getId());
+ assertEquals("chart_visualizer", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ child = services.get(1).getChildren().get(3);
+ assertTrue(child.isStructuralNode(), "child is not a structural node");
+ assertEquals("label_size_chart", child.getId());
+ assertEquals(2, child.getChildren().size());
+
+ subChild = child.getChildren().get(0);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("label_size_interpreter", subChild.getId());
+ assertEquals("label_size_interpreter", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+
+ subChild = child.getChildren().get(1);
+ assertFalse(subChild.isStructuralNode(), "child is a structural node");
+ assertNotNull(subChild.getService());
+ assertEquals("chart_visualizer", subChild.getId());
+ assertEquals("chart_visualizer", subChild.getService().getKey());
+ assertEquals("60000", subChild.getServiceConfiguration().getParameter("timeout").getValue());
+ assertEquals("true", subChild.getServiceConfiguration().getParameter("deleteResult").getValue());
+ }
+
+ @Test
+ public void shouldOperateOnWorkflowStatusStructuredNested() throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(
+ WorkflowStatus.class,
+ new WorkflowStatusDeserializer(Arrays.asList(MIGRATION, VALIDATION, LABEL_CORE, LABEL_MEMORY, LABEL_TASK, LABEL_SIZE, CHART_VISUALIZER)));
+ mapper.registerModule(module);
+
+ WorkflowStatus ws = mapper.readValue(JSON_STRING_STRUCTURED_NESTED, WorkflowStatus.class);
+
+ // test direct access on services via workflow status using concatenated keys
+ CloudServiceDefinition service = ws.getService("validation.label_core_chart.chart_visualizer");
+ assertEquals(CHART_VISUALIZER, service);
+ service = ws.getService("validation.label_memory_chart.chart_visualizer");
+ assertEquals(CHART_VISUALIZER, service);
+ service = ws.getService("validation.label_task_chart.chart_visualizer");
+ assertEquals(CHART_VISUALIZER, service);
+ service = ws.getService("validation.label_size_chart.chart_visualizer");
+ assertEquals(CHART_VISUALIZER, service);
+
+ assertNull(ws.getService("some.stupid.key"));
+
+ ServiceConfiguration config = ws.getConfiguration("validation.label_core_chart.label_core_interpreter");
+ assertEquals("360000", config.getParameter("timeout").getValue());
+ assertEquals("false", config.getParameter("deleteResult").getValue());
+
+ assertNull(ws.getConfiguration("some.stupid.key"));
+
+ List<ServiceNode> services = ws.getSelectedServices();
+ assertNotNull(services);
+ assertEquals(2, services.size());
+ assertEquals("migration", services.get(0).getService().getKey());
+
+ // remove migration on root level
+ ws.removeSelectedService(MIGRATION);
+
+ services = ws.getSelectedServices();
+ assertNotNull(services);
+ assertEquals(1, services.size());
+ assertEquals("validation", services.get(0).getService().getKey());
+ assertEquals(4, services.get(0).getChildren().size());
+
+ // remove structural node label_task_chart
+ ws.removeServiceNode("validation.label_task_chart");
+
+ assertEquals(3, services.get(0).getChildren().size());
+
+ ServiceNode serviceNode = services.get(0).getChildren().get(2);
+ assertTrue(serviceNode.isStructuralNode());
+ assertEquals("label_size_chart", serviceNode.getId());
+ assertEquals(2, serviceNode.getChildren().size());
+
+ // remove chart from label_size_chart
+ ws.removeSelectedService("validation.label_size_chart", CHART_VISUALIZER);
+// ws.removeServiceNode("validation.label_size_chart.chart_visualizer");
+
+ assertEquals(1, serviceNode.getChildren().size());
}
}

Back to the top