Bug 571652 - Extract workflow execution logic in separate artifact

Change-Id: Iaf66003397a0b2f65120fb3a71fbf8d2361e804f
Signed-off-by: Dirk Fauth <Dirk.Fauth@de.bosch.com>
diff --git a/manager/Dockerfile b/manager/Dockerfile
index 069b4e6..ba18511 100644
--- a/manager/Dockerfile
+++ b/manager/Dockerfile
@@ -1,6 +1,6 @@
 FROM amazoncorretto:8u275
 
-COPY target/manager-0.0.1-SNAPSHOT.jar app.jar
-COPY src/main/resources/services_online.txt services.txt
+COPY application/target/manager-0.0.1-SNAPSHOT.jar app.jar
+COPY application/src/main/resources/services_online.txt services.txt
 
 ENTRYPOINT ["java","-Dservice.configuration=services.txt","-jar","/app.jar"]
\ No newline at end of file
diff --git a/manager/about.html b/manager/app4mc-cloud-manager-app/about.html
similarity index 100%
rename from manager/about.html
rename to manager/app4mc-cloud-manager-app/about.html
diff --git a/manager/epl-2.0.html b/manager/app4mc-cloud-manager-app/epl-2.0.html
similarity index 100%
rename from manager/epl-2.0.html
rename to manager/app4mc-cloud-manager-app/epl-2.0.html
diff --git a/manager/app4mc-cloud-manager-app/pom.xml b/manager/app4mc-cloud-manager-app/pom.xml
new file mode 100644
index 0000000..6f189fd
--- /dev/null
+++ b/manager/app4mc-cloud-manager-app/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.eclipse.app4mc.cloud</groupId>
+		<artifactId>app4mc-cloud-manager</artifactId>
+		<version>0.0.1-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	
+	<artifactId>app4mc-cloud-manager-app</artifactId>
+	<name>APP4MC Cloud Manager Application</name>
+	<description>APP4MC Cloud Manager application to orchestrate AMALTHEA model processing workflows in the cloud</description>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-hateoas</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-jdbc</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-thymeleaf</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-websocket</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-tomcat</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.junit.vintage</groupId>
+					<artifactId>junit-vintage-engine</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-configuration-processor</artifactId>
+			<optional>true</optional>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		
+		<dependency>
+		    <groupId>com.konghq</groupId>
+		    <artifactId>unirest-java</artifactId>
+		    <version>3.11.01</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>org.webjars</groupId>
+		    <artifactId>bootstrap</artifactId>
+		    <version>4.5.2</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>webjars-locator-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>sockjs-client</artifactId>
+			<version>1.1.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>stomp-websocket</artifactId>
+			<version>2.3.3-1</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.webjars</groupId>
+		    <artifactId>jquery-form</artifactId>
+		    <version>4.2.2</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.webjars</groupId>
+		    <artifactId>font-awesome</artifactId>
+		    <version>5.13.0</version>
+		</dependency>
+		<dependency>
+		    <groupId>org.thymeleaf.extras</groupId>
+		    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
+		</dependency>
+
+		<dependency>
+		    <groupId>org.eclipse.app4mc.cloud</groupId>
+		    <artifactId>app4mc-cloud-manager-workflow</artifactId>
+		    <version>${project.version}</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/AboutPageController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/AboutPageController.java
similarity index 100%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/AboutPageController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/AboutPageController.java
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.java
similarity index 92%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.java
index 7569953..8ea94e6 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplication.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,7 +19,7 @@
 
 import kong.unirest.Unirest;
 
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = "org.eclipse.app4mc.cloud")
 public class App4McCloudManagerApplication {
 
 	public static void main(String[] args) {
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/MvcConfig.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/MvcConfig.java
similarity index 100%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/MvcConfig.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/MvcConfig.java
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServletInitializer.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/ServletInitializer.java
similarity index 100%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServletInitializer.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/ServletInitializer.java
diff --git a/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/StompMessageDispatcher.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/StompMessageDispatcher.java
new file mode 100644
index 0000000..f3f5571
--- /dev/null
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/StompMessageDispatcher.java
@@ -0,0 +1,32 @@
+/*********************************************************************************
+ * 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 org.eclipse.app4mc.cloud.execution.MessageDispatcher;
+import org.springframework.messaging.simp.SimpMessageSendingOperations;
+
+public class StompMessageDispatcher implements MessageDispatcher {
+
+	private final SimpMessageSendingOperations messagingTemplate;
+	
+	public StompMessageDispatcher(SimpMessageSendingOperations messagingTemplate) {
+		this.messagingTemplate = messagingTemplate;
+	}
+
+	@Override
+	public void send(String destination, Object payload) {
+		this.messagingTemplate.convertAndSend(destination, payload);
+	}
+
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WebSecurityConfig.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WebSecurityConfig.java
similarity index 100%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WebSecurityConfig.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WebSecurityConfig.java
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WebSocketConfig.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WebSocketConfig.java
similarity index 100%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WebSocketConfig.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WebSocketConfig.java
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
similarity index 84%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
index 591a0d5..4b10226 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowConfigController.java
@@ -13,6 +13,7 @@
  */
 package org.eclipse.app4mc.cloud.manager;
 
+import java.io.IOException;
 import java.text.DateFormat;
 import java.util.Date;
 import java.util.List;
@@ -23,10 +24,13 @@
 import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceExecutionRunnable;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatus;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatusHelper;
 import org.eclipse.app4mc.cloud.manager.administration.WorkflowStatusMap;
-import org.eclipse.app4mc.cloud.manager.storage.StorageFileNotFoundException;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.manager.storage.SpringFileSystemStorageService;
+import org.eclipse.app4mc.cloud.storage.StorageFileNotFoundException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.ResponseEntity;
@@ -64,7 +68,7 @@
 	List<String> exampleConfigFiles;
 
 	@Autowired
-	private StorageService storageService;
+	private SpringFileSystemStorageService storageService;
 
 	@Autowired
     private SimpMessageSendingOperations messagingTemplate;
@@ -80,8 +84,6 @@
 		return "workflowConfig";
 	}
 	
-	// TODO handle example config
-	
 	@PostMapping("/workflowConfig")
 	public String handleFileUpload(
 			@RequestParam(name = "file", required = false) MultipartFile file, 
@@ -111,7 +113,15 @@
 		String message = null;
 		if (file != null && !file.isEmpty()) {
 			// upload the input file
-			uuid = this.storageService.store(file);
+			try {
+				uuid = this.storageService.store(file.getInputStream(), file.getOriginalFilename());
+			} catch (IOException e) {
+				redirectAttributes.addFlashAttribute(
+						"message",
+						"Failed to upload the input file: " + e.getMessage());
+				
+				return "redirect:/workflowConfig";
+			}
 			filename = file.getOriginalFilename();
 			message = filename + " successfully uploaded";
 		} else {
@@ -124,7 +134,15 @@
 		WorkflowStatus workflowStatus = null;
 		if (configFile != null && !configFile.isEmpty()) {
 			// upload the config file
-			this.storageService.store(uuid, configFile);
+			try {
+				this.storageService.store(uuid, configFile.getInputStream(), configFile.getOriginalFilename());
+			} catch (IOException e) {
+				redirectAttributes.addFlashAttribute(
+						"message",
+						"Failed to upload the config file: " + e.getMessage());
+				
+				return "redirect:/workflowConfig";
+			}
 			
 			// load the workflow status from config file
 			workflowStatus = WorkflowStatusHelper.loadWorkflowStatus(
@@ -168,7 +186,7 @@
 		if (workflowStatus.getSelectedServices() != null) {
 			CloudServiceExecutionRunnable runnable = new CloudServiceExecutionRunnable(
 					this.storageService,
-					this.messagingTemplate,
+					new StompMessageDispatcher(this.messagingTemplate),
 					uuid, 
 					filename, 
 					workflowStatus);
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
similarity index 92%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
index 40423a7..8143b30 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowController.java
@@ -14,6 +14,7 @@
  */
 package org.eclipse.app4mc.cloud.manager;
 
+import java.io.IOException;
 import java.text.DateFormat;
 import java.util.Date;
 import java.util.List;
@@ -24,11 +25,18 @@
 import javax.annotation.PreDestroy;
 import javax.servlet.http.HttpServletRequest;
 
-import org.eclipse.app4mc.cloud.manager.ProcessLog.Action;
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceExecutionRunnable;
+import org.eclipse.app4mc.cloud.execution.ProcessLog;
+import org.eclipse.app4mc.cloud.execution.ProcessLog.Action;
+import org.eclipse.app4mc.cloud.execution.ServiceConfiguration;
+import org.eclipse.app4mc.cloud.execution.ServiceConfigurationParameter;
+import org.eclipse.app4mc.cloud.execution.ServiceNode;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatus;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatusHelper;
 import org.eclipse.app4mc.cloud.manager.administration.WorkflowStatusMap;
-import org.eclipse.app4mc.cloud.manager.storage.StorageFileNotFoundException;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.manager.storage.SpringFileSystemStorageService;
+import org.eclipse.app4mc.cloud.storage.StorageFileNotFoundException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.Resource;
@@ -55,7 +63,7 @@
 @SessionAttributes("workflowStatus")
 public class WorkflowController {
 
-	private final StorageService storageService;
+	private final SpringFileSystemStorageService storageService;
 
 	private ExecutorService executor = Executors.newCachedThreadPool();
 	
@@ -80,7 +88,7 @@
     public SimpMessageSendingOperations messagingTemplate;
 	
 	@Autowired
-	public WorkflowController(StorageService storageService) {
+	public WorkflowController(SpringFileSystemStorageService storageService) {
 		this.storageService = storageService;
 	}
 
@@ -137,7 +145,12 @@
 		String message = null;
 		if (file != null && !file.isEmpty()) {
 			// upload the input file
-			uuid = this.storageService.store(file);
+			try {
+				uuid = this.storageService.store(file.getInputStream(), file.getOriginalFilename());
+			} catch (IOException e) {
+				workflowStatus.addError("Failed to upload the input file: " + e.getMessage());
+				return "redirect:/workflow";
+			}
 			filename = file.getOriginalFilename();
 			message = filename + " successfully uploaded";
 		} else {
@@ -160,7 +173,7 @@
 		if (workflowStatus.getSelectedServices() != null) {
 			CloudServiceExecutionRunnable runnable = new CloudServiceExecutionRunnable(
 					storageService,
-					messagingTemplate,
+					new StompMessageDispatcher(this.messagingTemplate),
 					uuid, 
 					filename, 
 					workflowStatus);
@@ -210,7 +223,9 @@
 			// add default configuration to ignore over utilization
 			ServiceConfiguration configuration = ws.getConfiguration("rtc_analysis");
 			ServiceConfigurationParameter parameter = configuration.getParameter("analysis-ignore-overutilization");
-			parameter.setValue("true");
+			if (parameter != null) {
+				parameter.setValue("true");
+			}
 		}
 		
 		return "workflow";
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
similarity index 88%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
index 440ffa7..22bf030 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowRestController.java
@@ -13,6 +13,7 @@
  */
 package org.eclipse.app4mc.cloud.manager;
 
+import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.DateFormat;
 import java.util.Date;
@@ -25,10 +26,13 @@
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceExecutionRunnable;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatus;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatusHelper;
 import org.eclipse.app4mc.cloud.manager.administration.WorkflowStatusMap;
-import org.eclipse.app4mc.cloud.manager.storage.StorageFileNotFoundException;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.manager.storage.SpringFileSystemStorageService;
+import org.eclipse.app4mc.cloud.storage.StorageFileNotFoundException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.hateoas.Link;
 import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
@@ -66,7 +70,7 @@
 	List<CloudServiceDefinition> cloudServiceDefinitions;
 
 	@Autowired
-	private StorageService storageService;
+	private SpringFileSystemStorageService storageService;
 
 	@Autowired
     private SimpMessageSendingOperations messagingTemplate;
@@ -84,12 +88,25 @@
 		}
 
 		// upload the input file
-		String uuid = this.storageService.store(file);
+		String uuid;
+		try {
+			uuid = this.storageService.store(file.getInputStream(), file.getOriginalFilename());
+		} catch (IOException e) {
+			return ResponseEntity
+					.badRequest()
+					.body("Failed to upload the input file: " + e.getMessage());
+		}
 		String filename = file.getOriginalFilename();
 		String message = filename + " successfully uploaded";
 		
 		// upload the config file
-		this.storageService.store(uuid, configFile);
+		try {
+			this.storageService.store(uuid, configFile.getInputStream(), file.getOriginalFilename());
+		} catch (IOException e) {
+			return ResponseEntity
+					.badRequest()
+					.body("Failed to upload the config file: " + e.getMessage());
+		}
 		
 		// load the workflow status from config file
 		WorkflowStatus workflowStatus = WorkflowStatusHelper.loadWorkflowStatus(
@@ -122,7 +139,7 @@
 		if (workflowStatus.getSelectedServices() != null) {
 			CloudServiceExecutionRunnable runnable = new CloudServiceExecutionRunnable(
 					this.storageService,
-					this.messagingTemplate,
+					new StompMessageDispatcher(this.messagingTemplate),
 					uuid, 
 					filename, 
 					workflowStatus);
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.java
similarity index 86%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.java
index 2043f50..dfefb08 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowResultController.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
@@ -18,9 +18,11 @@
 
 import javax.annotation.Resource;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatus;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatusHelper;
 import org.eclipse.app4mc.cloud.manager.administration.WorkflowStatusMap;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.storage.StorageService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
@@ -48,7 +50,8 @@
 						uuid -> {
 							WorkflowStatus status = WorkflowStatusHelper.loadWorkflowStatus(
 									this.storageService.load(uuid, "workflowstatus.json").toFile(),
-									this.cloudServiceDefinitions);
+									this.cloudServiceDefinitions,
+									false);
 							return (status != null && status.getName() != null) ? status.getName() : uuid;
 						}, 
 						uuid -> MvcUriComponentsBuilder.fromMethodName(WorkflowController.class,
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java
similarity index 98%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java
index b6713b6..98ba132 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/AdminController.java
@@ -19,6 +19,7 @@
 
 import javax.annotation.Resource;
 
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.util.StringUtils;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
similarity index 97%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
index ce3a56c..fa2a403 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/ApplicationConfig.java
@@ -24,6 +24,7 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java
similarity index 94%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java
index 31ad030..f73e9cb 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinitionDTO.java
@@ -16,6 +16,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.app4mc.cloud.execution.CloudServiceDefinition;
+
 public class CloudServiceDefinitionDTO {
 
 	private ArrayList<CloudServiceDefinition> services = new ArrayList<>();
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java
similarity index 94%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java
rename to manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java
index c791801..e04384f 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/administration/WorkflowStatusMap.java
@@ -15,7 +15,7 @@
 
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.eclipse.app4mc.cloud.manager.WorkflowStatus;
+import org.eclipse.app4mc.cloud.execution.WorkflowStatus;
 
 public class WorkflowStatusMap {
 
diff --git a/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/storage/SpringFileSystemStorageService.java b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/storage/SpringFileSystemStorageService.java
new file mode 100644
index 0000000..573a1a4
--- /dev/null
+++ b/manager/app4mc-cloud-manager-app/src/main/java/org/eclipse/app4mc/cloud/manager/storage/SpringFileSystemStorageService.java
@@ -0,0 +1,89 @@
+/*********************************************************************************
+ * 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.storage;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import org.eclipse.app4mc.cloud.storage.FileSystemStorageService;
+import org.eclipse.app4mc.cloud.storage.StorageException;
+import org.eclipse.app4mc.cloud.storage.StorageFileNotFoundException;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SpringFileSystemStorageService extends FileSystemStorageService {
+
+	public Resource loadAsResource(String uuid, String... path) {
+		Path file = load(uuid, path);
+		try {
+			Resource resource = new UrlResource(file.toUri());
+			if (resource.exists() || resource.isReadable()) {
+				return resource;
+			}
+			else {
+				throw new StorageFileNotFoundException("Could not read file: " + file);
+			}
+		} catch (MalformedURLException e) {
+			throw new StorageFileNotFoundException("Could not read file: " + file, e);
+		}
+	}
+
+	public String copyExample(String filename, Resource[] exampleResources) {
+		try {
+			Path path = Files.createTempDirectory(TEMP_DIR_PREFIX);
+			
+			// extract uuid from pathname
+			String uuid = path.toString().substring(path.toString().lastIndexOf('_') + 1);
+			
+			copyExample(path, filename, exampleResources);
+			
+			return uuid;
+		} catch (IOException e) {
+			throw new StorageException("Failed to copy example file " + filename, e);
+		}
+	}
+	
+	public void copyExample(String uuid, String filename, Resource[] exampleResources) {
+		Path path = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
+		copyExample(path, filename, exampleResources);
+	}
+	
+	private void copyExample(Path path, String filename, Resource[] exampleResources) {
+		try {
+			Resource resource = Arrays.stream(exampleResources)
+					.filter(res -> filename.equals(res.getFilename()))
+					.findFirst()
+					.orElse(null);
+			if (resource != null) {
+				Files.copy(resource.getInputStream(), path.resolve(filename));
+			} else {
+				throw new StorageException("Could not access example file " + filename);
+			}
+		} catch (IOException e) {
+			throw new StorageException("Failed to copy example file " + filename, e);
+		}
+	}
+	
+	public Resource getResultArchive(String uuid) throws IOException {
+		Path folder = load(uuid);
+		zipResult(folder);
+		return loadAsResource(uuid, "result.zip");
+	}
+
+}
diff --git a/manager/src/main/resources/application.properties b/manager/app4mc-cloud-manager-app/src/main/resources/application.properties
similarity index 100%
rename from manager/src/main/resources/application.properties
rename to manager/app4mc-cloud-manager-app/src/main/resources/application.properties
diff --git a/manager/src/main/resources/services.txt b/manager/app4mc-cloud-manager-app/src/main/resources/services.txt
similarity index 72%
rename from manager/src/main/resources/services.txt
rename to manager/app4mc-cloud-manager-app/src/main/resources/services.txt
index 318335e..75505f1 100644
--- a/manager/src/main/resources/services.txt
+++ b/manager/app4mc-cloud-manager-app/src/main/resources/services.txt
@@ -1,13 +1,13 @@
 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
-label_task_interpreter;Label per Task Evaluation;http://localhost:8084/app4mc/interpreter/label-per-task/;Parses an Amalthea model to inspect label access operations per task
-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
+rtc_interpreter;RTC Evaluation;http://localhost:8082/app4mc/interpreter/rtc/;Converts the results of a RTC Analysis into an input for the Chart Visualizer;false
+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;false
+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;false
+label_task_interpreter;Label per Task Evaluation;http://localhost:8084/app4mc/interpreter/label-per-task/;Parses an Amalthea model to inspect label access operations per task;false
+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;false
+chart_visualizer;Chart Visualizer;http://localhost:8083/app4mc/visualization/barchart/;Visualization of interpreter results;false
+amlt2inchron;Amalthea -> INCHRON;https://am2inc.dev1.inchron.de/projects;Transforms an Amalthea model to an INCHRON model;false
 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
+app4mc_sim;APP4MC.Sim;http://139.30.201.29:2323/app4mc/simulation/;Executes simulation and generates a BTF traces;false
+inchron_btf_visualization;INCHRON BTF Trace Visualization;https://trace.dev1.inchron.de/traces/;Visualization of trace data provided by INCHRON;false
\ No newline at end of file
diff --git a/manager/src/main/resources/services_online.txt b/manager/app4mc-cloud-manager-app/src/main/resources/services_online.txt
similarity index 77%
rename from manager/src/main/resources/services_online.txt
rename to manager/app4mc-cloud-manager-app/src/main/resources/services_online.txt
index 98cc92e..d2f7d5a 100644
--- a/manager/src/main/resources/services_online.txt
+++ b/manager/app4mc-cloud-manager-app/src/main/resources/services_online.txt
@@ -1,13 +1,13 @@
 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
-label_task_interpreter;Label per Task Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/label-per-task/;Parses an Amalthea model to inspect label access operations per task
-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
+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;false
+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;false
+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;false
+label_task_interpreter;Label per Task Evaluation;https://app4mc.eclipseprojects.io/app4mc/interpreter/label-per-task/;Parses an Amalthea model to inspect label access operations per task;false
+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;false
+chart_visualizer;Chart Visualizer;https://app4mc.eclipseprojects.io/app4mc/visualization/barchart/;Visualization of interpreter results;false
+amlt2inchron;Amalthea -> INCHRON;https://am2inc.dev1.inchron.de/projects;Transforms an Amalthea model to an INCHRON model;false
 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
+app4mc_sim;APP4MC.Sim;http://139.30.201.29:2323/app4mc/simulation/;Executes simulation and generates a BTF traces;false
+inchron_btf_visualization;INCHRON BTF Trace Visualization;https://trace.dev1.inchron.de/traces/;Visualization of trace data provided by INCHRON;false
\ No newline at end of file
diff --git a/manager/src/main/resources/static/css/styles.css b/manager/app4mc-cloud-manager-app/src/main/resources/static/css/styles.css
similarity index 100%
rename from manager/src/main/resources/static/css/styles.css
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/css/styles.css
diff --git a/manager/src/main/resources/static/example_configurations/all_label_interpreter_mixed.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_label_interpreter_mixed.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/all_label_interpreter_mixed.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_label_interpreter_mixed.json
diff --git a/manager/src/main/resources/static/example_configurations/all_label_interpreter_tree.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_label_interpreter_tree.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/all_label_interpreter_tree.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_label_interpreter_tree.json
diff --git a/manager/src/main/resources/static/example_configurations/all_services_mixed.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_services_mixed.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/all_services_mixed.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_services_mixed.json
diff --git a/manager/src/main/resources/static/example_configurations/all_services_tree.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_services_tree.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/all_services_tree.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/all_services_tree.json
diff --git a/manager/src/main/resources/static/example_configurations/rtc_analysis_sequential.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/rtc_analysis_sequential.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/rtc_analysis_sequential.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/rtc_analysis_sequential.json
diff --git a/manager/src/main/resources/static/example_configurations/systemc_simulation_sequential.json b/manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/systemc_simulation_sequential.json
similarity index 100%
rename from manager/src/main/resources/static/example_configurations/systemc_simulation_sequential.json
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/example_configurations/systemc_simulation_sequential.json
diff --git a/manager/src/main/resources/static/examples/WATERS-2017_FMTV-Model.amxmi b/manager/app4mc-cloud-manager-app/src/main/resources/static/examples/WATERS-2017_FMTV-Model.amxmi
similarity index 100%
rename from manager/src/main/resources/static/examples/WATERS-2017_FMTV-Model.amxmi
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/examples/WATERS-2017_FMTV-Model.amxmi
diff --git a/manager/src/main/resources/static/examples/democarWithFixedPriorityScheduler.amxmi b/manager/app4mc-cloud-manager-app/src/main/resources/static/examples/democarWithFixedPriorityScheduler.amxmi
similarity index 100%
rename from manager/src/main/resources/static/examples/democarWithFixedPriorityScheduler.amxmi
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/examples/democarWithFixedPriorityScheduler.amxmi
diff --git a/manager/src/main/resources/static/images/Logo_Panorama.png b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/Logo_Panorama.png
similarity index 100%
rename from manager/src/main/resources/static/images/Logo_Panorama.png
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/Logo_Panorama.png
Binary files differ
diff --git a/manager/src/main/resources/static/images/Logo_Panorama_small.png b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/Logo_Panorama_small.png
similarity index 100%
rename from manager/src/main/resources/static/images/Logo_Panorama_small.png
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/Logo_Panorama_small.png
Binary files differ
diff --git a/manager/src/main/resources/static/images/app4mc-logo.png b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/app4mc-logo.png
similarity index 100%
rename from manager/src/main/resources/static/images/app4mc-logo.png
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/app4mc-logo.png
Binary files differ
diff --git a/manager/src/main/resources/static/images/app4mc-logo_small.png b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/app4mc-logo_small.png
similarity index 100%
rename from manager/src/main/resources/static/images/app4mc-logo_small.png
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/app4mc-logo_small.png
Binary files differ
diff --git a/manager/src/main/resources/static/images/bg-header2.jpg b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/bg-header2.jpg
similarity index 100%
rename from manager/src/main/resources/static/images/bg-header2.jpg
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/bg-header2.jpg
Binary files differ
diff --git a/manager/src/main/resources/static/images/bmbf.svg b/manager/app4mc-cloud-manager-app/src/main/resources/static/images/bmbf.svg
similarity index 100%
rename from manager/src/main/resources/static/images/bmbf.svg
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/images/bmbf.svg
diff --git a/manager/src/main/resources/static/js/admin.js b/manager/app4mc-cloud-manager-app/src/main/resources/static/js/admin.js
similarity index 100%
rename from manager/src/main/resources/static/js/admin.js
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/js/admin.js
diff --git a/manager/src/main/resources/static/js/workflow.js b/manager/app4mc-cloud-manager-app/src/main/resources/static/js/workflow.js
similarity index 100%
rename from manager/src/main/resources/static/js/workflow.js
rename to manager/app4mc-cloud-manager-app/src/main/resources/static/js/workflow.js
diff --git a/manager/src/main/resources/templates/about.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/about.html
similarity index 100%
rename from manager/src/main/resources/templates/about.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/about.html
diff --git a/manager/src/main/resources/templates/admin.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/admin.html
similarity index 100%
rename from manager/src/main/resources/templates/admin.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/admin.html
diff --git a/manager/src/main/resources/templates/configuration.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/configuration.html
similarity index 100%
rename from manager/src/main/resources/templates/configuration.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/configuration.html
diff --git a/manager/src/main/resources/templates/error.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/error.html
similarity index 100%
rename from manager/src/main/resources/templates/error.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/error.html
diff --git a/manager/src/main/resources/templates/fragment.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/fragment.html
similarity index 100%
rename from manager/src/main/resources/templates/fragment.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/fragment.html
diff --git a/manager/src/main/resources/templates/index.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/index.html
similarity index 100%
rename from manager/src/main/resources/templates/index.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/index.html
diff --git a/manager/src/main/resources/templates/login.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/login.html
similarity index 100%
rename from manager/src/main/resources/templates/login.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/login.html
diff --git a/manager/src/main/resources/templates/selectedServices.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/selectedServices.html
similarity index 100%
rename from manager/src/main/resources/templates/selectedServices.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/selectedServices.html
diff --git a/manager/src/main/resources/templates/serviceChildren.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/serviceChildren.html
similarity index 100%
rename from manager/src/main/resources/templates/serviceChildren.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/serviceChildren.html
diff --git a/manager/src/main/resources/templates/status.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/status.html
similarity index 100%
rename from manager/src/main/resources/templates/status.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/status.html
diff --git a/manager/src/main/resources/templates/workflow.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/workflow.html
similarity index 100%
rename from manager/src/main/resources/templates/workflow.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/workflow.html
diff --git a/manager/src/main/resources/templates/workflowConfig.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/workflowConfig.html
similarity index 100%
rename from manager/src/main/resources/templates/workflowConfig.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/workflowConfig.html
diff --git a/manager/src/main/resources/templates/workflowResults.html b/manager/app4mc-cloud-manager-app/src/main/resources/templates/workflowResults.html
similarity index 100%
rename from manager/src/main/resources/templates/workflowResults.html
rename to manager/app4mc-cloud-manager-app/src/main/resources/templates/workflowResults.html
diff --git a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplicationTests.java b/manager/app4mc-cloud-manager-app/src/test/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplicationTests.java
similarity index 100%
rename from manager/src/test/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplicationTests.java
rename to manager/app4mc-cloud-manager-app/src/test/java/org/eclipse/app4mc/cloud/manager/App4McCloudManagerApplicationTests.java
diff --git a/manager/app4mc-cloud-manager-workflow/about.html b/manager/app4mc-cloud-manager-workflow/about.html
new file mode 100644
index 0000000..73f8186
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/about.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+	<h2>About This Content</h2>
+
+	<p>June 18, 2020</p>
+	<h3>License</h3>
+
+	<p>
+		The Eclipse Foundation makes available all content in this plug-in
+		(&quot;Content&quot;). Unless otherwise indicated below, the Content
+		is provided to you under the terms and conditions of the Eclipse
+		Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+		available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+		For purposes of the EPL, &quot;Program&quot; will mean the Content.
+	</p>
+
+	<p>
+		If you did not receive this Content directly from the Eclipse
+		Foundation, the Content is being redistributed by another party
+		(&quot;Redistributor&quot;) and different terms and conditions may
+		apply to your use of any object code in the Content. Check the
+		Redistributor's license that was provided with the Content. If no such
+		license exists, contact the Redistributor. Unless otherwise indicated
+		below, the terms and conditions of the EPL still apply to any source
+		code in the Content and such source code may be obtained at <a
+			href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+	</p>
+
+	<h3>Third Party Content</h3>
+	
+	<p>The Content includes items that have been sourced from third parties as set out below. If you 
+	did not receive this Content directly from the Eclipse Foundation, the following is provided 
+	for informational purposes only, and you should look to the Redistributor&rsquo;s license for 
+	terms and conditions of use.</p>
+	
+	<h4>Unirest for Java</h4>
+	
+	<p>&quot;Unirest for Java&quot; is included as is. It is made available at <a href="https://github.com/Kong/unirest-java" target="_blank">https://github.com/Kong/unirest-java</a>.
+	Use of Unirest for Java is governed by the terms and conditions of the MIT License. A copy of the MIT is provided with the Content (<a href="about_files/mit.txt" target="_blank">mit.txt</a>) and is also available at <a href="https://github.com/Kong/unirest-java/blob/main/LICENSE">https://github.com/Kong/unirest-java/blob/main/LICENSE</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/manager/epl-2.0.html b/manager/app4mc-cloud-manager-workflow/about_files/epl-2.0.html
similarity index 100%
copy from manager/epl-2.0.html
copy to manager/app4mc-cloud-manager-workflow/about_files/epl-2.0.html
diff --git a/manager/app4mc-cloud-manager-workflow/about_files/mit.txt b/manager/app4mc-cloud-manager-workflow/about_files/mit.txt
new file mode 100644
index 0000000..0450f7b
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/about_files/mit.txt
@@ -0,0 +1,22 @@
+The MIT License
+
+ * Copyright for portions of unirest-java are held by Kong Inc (c) 2013-2019.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/manager/app4mc-cloud-manager-workflow/bnd.bnd b/manager/app4mc-cloud-manager-workflow/bnd.bnd
new file mode 100644
index 0000000..f6c2be2
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/bnd.bnd
@@ -0,0 +1,11 @@
+package-version=${version;===;${Bundle-Version}}
+Implementation-Vendor: Eclipse APP4MC
+Bundle-Vendor: Eclipse APP4MC
+Automatic-Module-Name: org.eclipse.app4mc.cloud.execution
+Bundle-SymbolicName: org.eclipse.app4mc.cloud.execution
+Export-Package: \
+  org.eclipse.app4mc.cloud.execution;version="${package-version}",\
+  org.eclipse.app4mc.cloud.storage;version="${package-version}",\
+  kong.unirest;version="3.11.01",\
+  kong.unirest.apache;version="3.11.01",\
+  kong.unirest.json;version="3.11.01"
\ No newline at end of file
diff --git a/manager/app4mc-cloud-manager-workflow/pom.xml b/manager/app4mc-cloud-manager-workflow/pom.xml
new file mode 100644
index 0000000..21465a1
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.eclipse.app4mc.cloud</groupId>
+		<artifactId>app4mc-cloud-manager</artifactId>
+		<version>0.0.1-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+
+	<artifactId>app4mc-cloud-manager-workflow</artifactId>
+	<name>APP4MC Cloud Manager Workflow Execution</name>
+	<description>Execution logic for AMALTHEA model processing workflow in the cloud</description>
+
+	<dependencies>
+		<dependency>
+		    <groupId>com.fasterxml.jackson.core</groupId>
+		    <artifactId>jackson-databind</artifactId>
+		</dependency>
+		
+		<dependency>
+		    <groupId>com.konghq</groupId>
+		    <artifactId>unirest-java</artifactId>
+		    <version>3.11.01</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-simple</artifactId>
+			<scope>test</scope>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter</artifactId>
+			<scope>test</scope>
+		</dependency>
+		
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-maven-plugin</artifactId>
+				<version>5.1.1</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>bnd-process</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-resources-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>copy-resources</id>
+						<phase>validate</phase>
+						<goals>
+							<goal>copy-resources</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.outputDirectory}</outputDirectory>
+							<resources>          
+								<resource>
+									<directory>.</directory>
+									<includes>
+										<include>about_files/*</include>
+										<include>about.html</include>
+									</includes>
+								</resource>
+							</resources>              
+						</configuration>            
+					</execution>
+				</executions>
+			</plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+		</plugins>
+	</build>
+</project>
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceDefinition.java
similarity index 96%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceDefinition.java
index 8229b4e..602ee00 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/administration/CloudServiceDefinition.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceDefinition.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager.administration;
+package org.eclipse.app4mc.cloud.execution;
 
 public class CloudServiceDefinition {
 	
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceExecutionRunnable.java
similarity index 87%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceExecutionRunnable.java
index be89149..a047307 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/CloudServiceExecutionRunnable.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/CloudServiceExecutionRunnable.java
@@ -12,20 +12,19 @@
  *     Dortmund University of Applied Sciences and Arts - Bug 570871
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.nio.file.Path;
 
-import org.eclipse.app4mc.cloud.manager.ProcessLog.Action;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.execution.ProcessLog.Action;
+import org.eclipse.app4mc.cloud.storage.StorageService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.messaging.simp.SimpMessageSendingOperations;
 
 public class CloudServiceExecutionRunnable implements Runnable {
 
 	private final StorageService storageService;
-	private final SimpMessageSendingOperations messagingTemplate;
+	private final MessageDispatcher messagingTemplate;
 
 	private String uuid;
 	private String originalFilename;
@@ -35,7 +34,7 @@
 		
 	public CloudServiceExecutionRunnable(
 			StorageService storageService, 
-			SimpMessageSendingOperations messagingTemplate, 
+			MessageDispatcher messagingTemplate, 
 			String uuid, 
 			String originalFilename, 
 			WorkflowStatus workflowStatus) {
@@ -98,7 +97,7 @@
 					}
 				}
 				
-				this.messagingTemplate.convertAndSend(
+				this.messagingTemplate.send(
 						"/topic/process-updates/" + this.uuid, 
 						new ProcessLog(Action.DONE, null, this.uuid));
 			}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/HeaderHelper.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/HeaderHelper.java
similarity index 97%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/HeaderHelper.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/HeaderHelper.java
index 7b30d75..b30efd1 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/HeaderHelper.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/HeaderHelper.java
@@ -11,15 +11,13 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.springframework.util.StringUtils;
-
 import kong.unirest.HttpResponse;
 
 /**
@@ -209,7 +207,7 @@
 	 *         <code>false</code> in any other case.
 	 */
 	public static boolean isValid(String toCheck, String base) {
-		if (StringUtils.isEmpty(toCheck) || StringUtils.isEmpty(base)) {
+		if ((toCheck == null || "".equals(toCheck)) || (base == null || "".equals(base))) {
 			throw new ProcessingFailedException("URLs to compare can not be empty");
 		}
 		
diff --git a/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/MessageDispatcher.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/MessageDispatcher.java
new file mode 100644
index 0000000..af508de
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/MessageDispatcher.java
@@ -0,0 +1,31 @@
+/*********************************************************************************
+ * 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.execution;
+
+/**
+ * Interface for implementing a dispatcher that handles messages sent by the
+ * cloud service execution. For the manager web application, the implementation
+ * wraps the SimpMessageSendingOperations. This way a dependency from the
+ * execution library to a websocket implementation can be avoided.
+ */
+public interface MessageDispatcher {
+
+	/**
+	 * Send a message to the given destination.
+	 * @param destination the target destination
+	 * @param payload the payload to send
+	 */
+	void send(String destination, Object payload);
+
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessLog.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessLog.java
similarity index 96%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessLog.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessLog.java
index 73a2dd0..dbf3a54 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessLog.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessLog.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 /**
  * The message model class to transport a processing log message from server to the client.
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessingFailedException.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessingFailedException.java
similarity index 95%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessingFailedException.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessingFailedException.java
index 03393d1..cd4d2ad 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ProcessingFailedException.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ProcessingFailedException.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 /**
  * Exception that is thrown when an error occurs in the workflow processing, e.g. validation failed.
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfiguration.java
similarity index 97%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfiguration.java
index 91b01da..483d33d 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfiguration.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfiguration.java
@@ -12,7 +12,7 @@
  *     Dortmund University of Applied Sciences and Arts
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinition.java
similarity index 98%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinition.java
index 19e4bbe..4d0911a 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinition.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinition.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinitionDeserializer.java
similarity index 98%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinitionDeserializer.java
index 491a476..afe5cf1 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationDefinitionDeserializer.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationDefinitionDeserializer.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationParameter.java
similarity index 97%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationParameter.java
index 317eddc..eb57419 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceConfigurationParameter.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceConfigurationParameter.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNode.java
similarity index 97%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNode.java
index 94d4143..a8914a8 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNode.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNode.java
@@ -12,15 +12,13 @@
  *     Dortmund University of Applied Sciences and Arts
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 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 {
 	public static final String ROOT_NODE_ID = "root";
 
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNodeProcessingTask.java
similarity index 92%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNodeProcessingTask.java
index 1a785af..91ab580 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/ServiceNodeProcessingTask.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/ServiceNodeProcessingTask.java
@@ -12,7 +12,7 @@
  *     Dortmund University of Applied Sciences and Arts - Bug 570871
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.io.File;
 import java.nio.file.Files;
@@ -24,14 +24,10 @@
 import java.util.concurrent.ForkJoinTask;
 import java.util.concurrent.RecursiveTask;
 
-import org.apache.http.HttpStatus;
-import org.eclipse.app4mc.cloud.manager.ProcessLog.Action;
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
-import org.eclipse.app4mc.cloud.manager.storage.StorageService;
+import org.eclipse.app4mc.cloud.execution.ProcessLog.Action;
+import org.eclipse.app4mc.cloud.storage.StorageService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.messaging.simp.SimpMessageSendingOperations;
-import org.springframework.util.StringUtils;
 
 import kong.unirest.HttpResponse;
 import kong.unirest.MultipartBody;
@@ -46,7 +42,7 @@
 	private final Path inputFile;
 
 	private final StorageService storageService;
-	private final SimpMessageSendingOperations messagingTemplate;
+	private final MessageDispatcher messagingTemplate;
 	private WorkflowStatus workflowStatus;
 
 	private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -55,7 +51,7 @@
 			ServiceNode n, 
 			Path inputFile, 
 			StorageService storageService,
-			SimpMessageSendingOperations messagingTemplate, 
+			MessageDispatcher messagingTemplate, 
 			String uuid, 
 			WorkflowStatus ws) {
 		
@@ -167,7 +163,7 @@
 			
 			if (config != null) {
 				config.getParameterList().forEach(param -> {
-					if (!StringUtils.isEmpty(param.getValue()) && !param.isManagerParameter()) {
+					if ((param.getValue() != null && !"".equals(param.getValue())) && !param.isManagerParameter()) {
 						if ("multiple".equals(param.getCardinality())) {
 							multipartBody.field(param.getKey(), Arrays.asList(param.getValue().split(",")));
 						} else {
@@ -183,7 +179,7 @@
 			String statusUrl = null;
 			if (uploadResponse.getStatus() == 201) {
 				statusUrl = uploadResponse.getHeaders().getFirst("Location");
-				if (StringUtils.isEmpty(statusUrl)) {
+				if (statusUrl == null || "".equals(statusUrl)) {
 					// fallback check for Link header if Location header is not set
 					statusUrl = HeaderHelper.getUrlFromLink(uploadResponse.getHeaders().get("Link"), "status", baseUrl);
 				} else if (!HeaderHelper.isValid(statusUrl, baseUrl)) {
@@ -263,9 +259,9 @@
 					
 					// send a processing message every 5 seconds to the client to indicate that the
 					// service is still progressing
-					if ((((end - start) / 1000) % 5) == 0) {
+					if ((((end - start) / 1000) % 5) == 0 && this.messagingTemplate != null) {
 						synchronized (this.messagingTemplate) {
-								this.messagingTemplate.convertAndSend(
+								this.messagingTemplate.send(
 								"/topic/process-updates/" + this.uuid, 
 								new ProcessLog(Action.PROCESSING, serviceName + " is processing ..."));
 
@@ -306,7 +302,7 @@
 						}
 						
 						String resultName = resultResponse.getHeaders().getFirst("x-app4mc-result-name");
-						if (StringUtils.isEmpty(resultName)) {
+						if (resultName == null || "".equals(resultName)) {
 							resultName = serviceName;
 						}
 
@@ -323,7 +319,7 @@
 							result = migrationResult;
 						}
 
-						if (resultResponse.getStatus() != HttpStatus.SC_OK) {
+						if (resultResponse.getStatus() != 200) {
 							error = true;
 						}
 						
@@ -418,11 +414,13 @@
 	}
 	
 	private void addMessage(WorkflowStatus workflowStatus, String message) {
-		synchronized(this.messagingTemplate){
-			workflowStatus.addMessage(message);
-			this.messagingTemplate.convertAndSend(
-				"/topic/process-updates/" + this.uuid, 
-				new ProcessLog(Action.UPDATE, message));
+		if (this.messagingTemplate != null) {
+			synchronized(this.messagingTemplate){
+				workflowStatus.addMessage(message);
+				this.messagingTemplate.send(
+						"/topic/process-updates/" + this.uuid, 
+						new ProcessLog(Action.UPDATE, message));
+			}
 		}
 	}
 
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatus.java
similarity index 98%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatus.java
index e02f670..d60e246 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatus.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatus.java
@@ -12,14 +12,13 @@
  *     Dortmund University of Applied Sciences and Arts
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 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;
 
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusDeserializer.java
similarity index 81%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusDeserializer.java
index fbdbadc..621f8b3 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusDeserializer.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusDeserializer.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
@@ -11,15 +11,13 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
-
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.DeserializationContext;
@@ -36,14 +34,20 @@
 	private static final long serialVersionUID = -272147724534718705L;
 	
 	private List<CloudServiceDefinition> cloudServiceDefinitions;
+	private boolean loadServiceDetails = true;
 	
     public WorkflowStatusDeserializer(List<CloudServiceDefinition> cloudServiceDefinitions) { 
-        this(null, cloudServiceDefinitions); 
+        this(null, cloudServiceDefinitions, true); 
     } 
-
-    public WorkflowStatusDeserializer(Class<?> vc, List<CloudServiceDefinition> cloudServiceDefinitions) { 
-        super(vc);
-        this.cloudServiceDefinitions = cloudServiceDefinitions;
+    
+    public WorkflowStatusDeserializer(List<CloudServiceDefinition> cloudServiceDefinitions, boolean loadServiceDetails) { 
+    	this(null, cloudServiceDefinitions, loadServiceDetails); 
+    } 
+    
+    public WorkflowStatusDeserializer(Class<?> vc, List<CloudServiceDefinition> cloudServiceDefinitions, boolean loadServiceDetails) { 
+    	super(vc);
+    	this.cloudServiceDefinitions = cloudServiceDefinitions;
+    	this.loadServiceDetails = loadServiceDetails;
     }
 
     @Override
@@ -60,11 +64,15 @@
         	result.setUuid(uuid.asText());
         }
 
-        JsonNode services = node.get("services");
-        if (services != null) {
-        	services.fields().forEachRemaining(serviceField -> {
-        		deserializeServiceNode(result, null, serviceField.getKey(), serviceField.getValue().fields());
-        	});
+        // only load the service details if needed
+        // can be disabled for loading a list of executed workflows
+        if (this.loadServiceDetails) {
+        	JsonNode services = node.get("services");
+        	if (services != null) {
+        		services.fields().forEachRemaining(serviceField -> {
+        			deserializeServiceNode(result, null, serviceField.getKey(), serviceField.getValue().fields());
+        		});
+        	}
         }
     	
         JsonNode cancelled = node.get("cancelled");
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelper.java
similarity index 89%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelper.java
index 7d54b44..ede3e01 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelper.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelper.java
@@ -11,15 +11,13 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.io.File;
 import java.util.List;
 
-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;
@@ -77,7 +75,7 @@
 			}
 		}
 		
-		if (!StringUtils.isEmpty(csd.getDescription())) {
+		if (csd.getDescription() != null && !"".equals(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());
@@ -194,7 +192,8 @@
 	}
 
 	/**
-	 * Deserializes the given JSON {@link File}.
+	 * Deserializes the given JSON {@link File}. Will load the service details of
+	 * the executed services.
 	 * 
 	 * @param file                    The {@link File} to deserialize.
 	 * @param cloudServiceDefinitions The {@link CloudServiceDefinition}s needed to
@@ -202,12 +201,26 @@
 	 * @return The deserialized {@link WorkflowStatus}.
 	 */
 	public static WorkflowStatus loadWorkflowStatus(File file, List<CloudServiceDefinition> cloudServiceDefinitions) {
+		return loadWorkflowStatus(file, cloudServiceDefinitions, true);
+	}
+	
+	/**
+	 * Deserializes the given JSON {@link File}.
+	 * 
+	 * @param file                    The {@link File} to deserialize.
+	 * @param cloudServiceDefinitions The {@link CloudServiceDefinition}s needed to
+	 *                                resolve the used services.
+	 * @param loadServiceDetails      <code>true</code> if the service details
+	 *                                should be loaded, <code>false</code> if not.
+	 * @return The deserialized {@link WorkflowStatus}.
+	 */
+	public static WorkflowStatus loadWorkflowStatus(File file, List<CloudServiceDefinition> cloudServiceDefinitions, boolean loadServiceDetails) {
 		try {
 			if (file.exists()) {
 				ObjectMapper mapper = new ObjectMapper();
 				
 				SimpleModule module = new SimpleModule();
-				module.addDeserializer(WorkflowStatus.class, new WorkflowStatusDeserializer(cloudServiceDefinitions));
+				module.addDeserializer(WorkflowStatus.class, new WorkflowStatusDeserializer(cloudServiceDefinitions, loadServiceDetails));
 				mapper.registerModule(module);
 				
 				return mapper.readValue(file, WorkflowStatus.class);
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusSerializer.java
similarity index 95%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusSerializer.java
index 01d1055..9ba52f6 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusSerializer.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusSerializer.java
@@ -11,12 +11,10 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import java.io.IOException;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
-
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
diff --git a/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/FileSystemStorageService.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/FileSystemStorageService.java
new file mode 100644
index 0000000..1bef96d
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/FileSystemStorageService.java
@@ -0,0 +1,143 @@
+/*********************************************************************************
+ * 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.storage;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Comparator;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FileSystemStorageService implements StorageService {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileSystemStorageService.class);
+	
+	protected static final String TEMP_DIR_PREFIX = "app4mc_mgr_";
+	
+	protected final String defaultBaseDir = System.getProperty("java.io.tmpdir");
+	protected final Path tempLocation = Paths.get(defaultBaseDir);
+	
+	@Override
+	public String store(InputStream input, String originalFilename) {
+		try {
+			Path path = Files.createTempDirectory(TEMP_DIR_PREFIX);
+			
+			// extract uuid from pathname
+			String uuid = path.toString().substring(path.toString().lastIndexOf('_') + 1);
+			
+			store(path, input, originalFilename);
+			
+			return uuid;
+		} catch (IOException e) {
+			throw new StorageException("Failed to create temporary directory for uploading file " + originalFilename, e);
+		}
+	}
+
+	@Override
+	public void store(String uuid, InputStream input, String originalFilename) {
+		Path path = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
+		store(path, input, originalFilename);
+	}
+
+	private void store(Path path, InputStream input, String originalFilename) {
+		try {
+			Files.copy(input, path.resolve(originalFilename));
+		} catch (IOException e) {
+			throw new StorageException("Failed to store file " + originalFilename, e);
+		}
+	}
+	
+	@Override
+	public Stream<Path> loadAll() {
+		try {
+			return Files.walk(this.tempLocation, 1)
+					.filter(path -> !path.equals(this.tempLocation))
+					.filter(path -> path.getFileName().toString().startsWith(TEMP_DIR_PREFIX));
+		} catch (IOException e) {
+			throw new StorageException("Failed to read stored files", e);
+		}
+
+	}
+
+	@Override
+	public Path load(String uuid, String... path) {
+		Path result = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
+		for (String p : path) {
+			result = result.resolve(p);
+		}
+		return result;
+	}
+
+	@Override
+	public void delete(String uuid) {
+		Path path = load(uuid);
+		try {
+			Files.walk(path)
+				.sorted(Comparator.reverseOrder())
+				.map(Path::toFile)
+				.forEach(File::delete);
+		} catch (IOException e) {
+			throw new StorageFileNotFoundException("Could not delete: " + path);
+		}
+	}
+	
+	@Override
+	public void deleteAll() {
+		loadAll().forEach(path -> {
+			try {
+				Files.walk(path)
+			      .sorted(Comparator.reverseOrder())
+			      .map(Path::toFile)
+			      .forEach(File::delete);
+			} catch (IOException e) {
+				LOG.error("Could not delete: {}", path, e);
+			}
+		});
+	}
+	
+	public static void zipResult(Path folder) throws IOException {
+		Path outputFileName = folder.resolve("result.zip");
+	    if (Files.exists(outputFileName)) {
+			Files.delete(outputFileName);
+	    }
+	    Path zipFile = Files.createFile(outputFileName);
+		try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile))) {
+	        Files.walk(folder)
+	        	.filter(path -> !Files.isDirectory(path))
+	        	.filter(path -> !path.getFileName().startsWith("."))
+	        	.filter(path -> !path.getFileName().endsWith("result.zip"))
+	        	.forEach(path -> {
+	        		try {
+	        			ZipEntry zipEntry = new ZipEntry(folder.relativize(path).toString());
+	        			zipOutputStream.putNextEntry(zipEntry);
+	        			Files.copy(path, zipOutputStream);
+	        			zipOutputStream.closeEntry();
+	        		} catch (IOException e) {
+	        			LOG.error("Failed to include {} to zip archive", path.getFileName(), e);
+	        		}
+		       });
+		} catch (Exception e){
+			LOG.error("Failed to produce result zip archive", e);
+		}
+	}
+
+}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageException.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageException.java
similarity index 93%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageException.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageException.java
index 7e42e45..e69e321 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageException.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageException.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager.storage;
+package org.eclipse.app4mc.cloud.storage;
 
 public class StorageException extends RuntimeException {
 
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageFileNotFoundException.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageFileNotFoundException.java
similarity index 93%
rename from manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageFileNotFoundException.java
rename to manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageFileNotFoundException.java
index d560b69..d92bd90 100644
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageFileNotFoundException.java
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageFileNotFoundException.java
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager.storage;
+package org.eclipse.app4mc.cloud.storage;
 
 public class StorageFileNotFoundException extends StorageException {
 
diff --git a/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageService.java b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageService.java
new file mode 100644
index 0000000..29e2255
--- /dev/null
+++ b/manager/app4mc-cloud-manager-workflow/src/main/java/org/eclipse/app4mc/cloud/storage/StorageService.java
@@ -0,0 +1,34 @@
+/*********************************************************************************
+ * 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.storage;
+
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+public interface StorageService {
+
+	String store(InputStream input, String originalFilename);
+	
+	void store(String uuid, InputStream input, String originalFilename);
+
+	Stream<Path> loadAll();
+
+	Path load(String uuid, String... path);
+
+	void delete(String uuid);
+	
+	void deleteAll();
+
+}
diff --git a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowControllerTest.java b/manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/HeaderHelperTest.java
similarity index 96%
rename from manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowControllerTest.java
rename to manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/HeaderHelperTest.java
index 7173b3f..eba33c0 100644
--- a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowControllerTest.java
+++ b/manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/HeaderHelperTest.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
@@ -11,7 +11,7 @@
  *     Robert Bosch GmbH - initial API and implementation
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -23,7 +23,7 @@
 
 import org.junit.jupiter.api.Test;
 
-class WorkflowControllerTest {
+class HeaderHelperTest {
 
 	@Test
 	void shouldCheckValidUrls() {
diff --git a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java b/manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelperTest.java
similarity index 99%
rename from manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java
rename to manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelperTest.java
index 3681757..8305105 100644
--- a/manager/src/test/java/org/eclipse/app4mc/cloud/manager/WorkflowStatusHelperTest.java
+++ b/manager/app4mc-cloud-manager-workflow/src/test/java/org/eclipse/app4mc/cloud/execution/WorkflowStatusHelperTest.java
@@ -12,7 +12,7 @@
  *     Dortmund University of Applied Sciences and Arts - Bug 570871
  ********************************************************************************
  */
-package org.eclipse.app4mc.cloud.manager;
+package org.eclipse.app4mc.cloud.execution;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -30,7 +30,6 @@
 import java.util.HashMap;
 import java.util.List;
 
-import org.eclipse.app4mc.cloud.manager.administration.CloudServiceDefinition;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
diff --git a/manager/src/test/resources/conf_def_migration.json b/manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_migration.json
similarity index 100%
rename from manager/src/test/resources/conf_def_migration.json
rename to manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_migration.json
diff --git a/manager/src/test/resources/conf_def_rtc.json b/manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_rtc.json
similarity index 100%
rename from manager/src/test/resources/conf_def_rtc.json
rename to manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_rtc.json
diff --git a/manager/src/test/resources/conf_def_validation.json b/manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_validation.json
similarity index 100%
rename from manager/src/test/resources/conf_def_validation.json
rename to manager/app4mc-cloud-manager-workflow/src/test/resources/conf_def_validation.json
diff --git a/manager/pom.xml b/manager/pom.xml
index 6896e2a..bb4fff9 100644
--- a/manager/pom.xml
+++ b/manager/pom.xml
@@ -2,130 +2,38 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 	<modelVersion>4.0.0</modelVersion>
+
 	<parent>
 		<groupId>org.springframework.boot</groupId>
 		<artifactId>spring-boot-starter-parent</artifactId>
 		<version>2.3.1.RELEASE</version>
 		<relativePath/> <!-- lookup parent from repository -->
 	</parent>
+
 	<groupId>org.eclipse.app4mc.cloud</groupId>
-	<artifactId>manager</artifactId>
+	<artifactId>app4mc-cloud-manager</artifactId>
 	<version>0.0.1-SNAPSHOT</version>
-	<name>APP4MC Cloud Manager</name>
-	<description>Manager application to orchestrate AMALTHEA model processing in the cloud</description>
+	<packaging>pom</packaging>
 
 	<properties>
 		<java.version>1.8</java.version>
 	</properties>
 
-	<dependencies>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-hateoas</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-jdbc</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-thymeleaf</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-websocket</artifactId>
-		</dependency>
-		
-		<dependency>
-			<groupId>com.h2database</groupId>
-			<artifactId>h2</artifactId>
-			<scope>runtime</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-tomcat</artifactId>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-test</artifactId>
-			<scope>test</scope>
-			<exclusions>
-				<exclusion>
-					<groupId>org.junit.vintage</groupId>
-					<artifactId>junit-vintage-engine</artifactId>
-				</exclusion>
-			</exclusions>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-configuration-processor</artifactId>
-			<optional>true</optional>
-		</dependency>
+    <licenses>
+        <license>
+            <name>Eclipse Public License - v 2.0</name>
+            <url>https://www.eclipse.org/legal/epl-2.0/</url>
+        </license>
+    </licenses>
+    
+    <scm>
+        <connection>scm:git:git://git.eclipse.org/gitroot/app4mc/org.eclipse.app4mc.cloud.git</connection>
+        <developerConnection>scm:git:https://git.eclipse.org/r/app4mc/org.eclipse.app4mc.cloud.git</developerConnection>
+        <url>https://git.eclipse.org/c/app4mc/org.eclipse.app4mc.cloud.git/</url>
+    </scm>
 
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-security</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.security</groupId>
-			<artifactId>spring-security-test</artifactId>
-			<scope>test</scope>
-		</dependency>
-		
-		<dependency>
-		    <groupId>com.konghq</groupId>
-		    <artifactId>unirest-java</artifactId>
-		    <version>3.11.01</version>
-		</dependency>
-		
-		<dependency>
-		    <groupId>org.webjars</groupId>
-		    <artifactId>bootstrap</artifactId>
-		    <version>4.5.2</version>
-		</dependency>
-		
-		<dependency>
-			<groupId>org.webjars</groupId>
-			<artifactId>webjars-locator-core</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.webjars</groupId>
-			<artifactId>sockjs-client</artifactId>
-			<version>1.1.2</version>
-		</dependency>
-		<dependency>
-			<groupId>org.webjars</groupId>
-			<artifactId>stomp-websocket</artifactId>
-			<version>2.3.3-1</version>
-		</dependency>
-		<dependency>
-		    <groupId>org.webjars</groupId>
-		    <artifactId>jquery-form</artifactId>
-		    <version>4.2.2</version>
-		</dependency>
-		<dependency>
-		    <groupId>org.webjars</groupId>
-		    <artifactId>font-awesome</artifactId>
-		    <version>5.13.0</version>
-		</dependency>
-		<dependency>
-		    <groupId>org.thymeleaf.extras</groupId>
-		    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
-		</dependency>
-	</dependencies>
-
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-maven-plugin</artifactId>
-			</plugin>
-		</plugins>
-	</build>
-
+	<modules>
+		<module>app4mc-cloud-manager-workflow</module>
+		<module>app4mc-cloud-manager-app</module>
+	</modules>
 </project>
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/FileSystemStorageService.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/FileSystemStorageService.java
deleted file mode 100644
index d3a325f..0000000
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/FileSystemStorageService.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*********************************************************************************
- * 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.storage;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.stream.Stream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.UrlResource;
-import org.springframework.stereotype.Service;
-import org.springframework.util.FileSystemUtils;
-import org.springframework.web.multipart.MultipartFile;
-
-@Service
-public class FileSystemStorageService implements StorageService {
-
-	private static final Logger LOG = LoggerFactory.getLogger(FileSystemStorageService.class);
-	
-	private static final String TEMP_DIR_PREFIX = "app4mc_mgr_";
-	
-	private final String defaultBaseDir = System.getProperty("java.io.tmpdir");
-	private final Path tempLocation = Paths.get(defaultBaseDir);
-	
-	@Override
-	public String store(MultipartFile file) {
-		try {
-			Path path = Files.createTempDirectory(TEMP_DIR_PREFIX);
-			
-			// extract uuid from pathname
-			String uuid = path.toString().substring(path.toString().lastIndexOf('_') + 1);
-			
-			store(path, file);
-			
-			return uuid;
-		} catch (IOException e) {
-			throw new StorageException("Failed to create temporary directory for uploading file " + file.getOriginalFilename(), e);
-		}
-	}
-
-	@Override
-	public void store(String uuid, MultipartFile file) {
-		Path path = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
-		store(path, file);
-	}
-
-	private void store(Path path, MultipartFile file) {
-		try {
-			if (file.isEmpty()) {
-				throw new StorageException("Failed to store empty file " + file.getOriginalFilename());
-			}
-			
-			Files.copy(file.getInputStream(), path.resolve(file.getOriginalFilename()));
-		} catch (IOException e) {
-			throw new StorageException("Failed to store file " + file.getOriginalFilename(), e);
-		}
-	}
-	
-	@Override
-	public Stream<Path> loadAll() {
-		try {
-			return Files.walk(this.tempLocation, 1)
-					.filter(path -> !path.equals(this.tempLocation))
-					.filter(path -> path.getFileName().toString().startsWith(TEMP_DIR_PREFIX));
-		} catch (IOException e) {
-			throw new StorageException("Failed to read stored files", e);
-		}
-
-	}
-
-	@Override
-	public Path load(String uuid, String... path) {
-		Path result = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
-		for (String p : path) {
-			result = result.resolve(p);
-		}
-		return result;
-	}
-
-	@Override
-	public Resource loadAsResource(String uuid, String... path) {
-		Path file = load(uuid, path);
-		try {
-			Resource resource = new UrlResource(file.toUri());
-			if (resource.exists() || resource.isReadable()) {
-				return resource;
-			}
-			else {
-				throw new StorageFileNotFoundException("Could not read file: " + file);
-			}
-		} catch (MalformedURLException e) {
-			throw new StorageFileNotFoundException("Could not read file: " + file, e);
-		}
-	}
-
-	@Override
-	public void delete(String uuid) {
-		Path path = load(uuid);
-		try {
-			Files.walk(path)
-				.sorted(Comparator.reverseOrder())
-				.map(java.nio.file.Path::toFile)
-				.forEach(File::delete);
-		} catch (IOException e) {
-			throw new StorageFileNotFoundException("Could not delete: " + path);
-		}
-	}
-	
-	@Override
-	public void deleteAll() {
-		loadAll().forEach(path -> {
-			try {
-				FileSystemUtils.deleteRecursively(path);
-			} catch (IOException e) {
-				LOG.error("Could not delete: {}", path, e);
-			}
-		});
-	}
-
-	@Override
-	public String copyExample(String filename, Resource[] exampleResources) {
-		try {
-			Path path = Files.createTempDirectory(TEMP_DIR_PREFIX);
-			
-			// extract uuid from pathname
-			String uuid = path.toString().substring(path.toString().lastIndexOf('_') + 1);
-			
-			copyExample(path, filename, exampleResources);
-			
-			return uuid;
-		} catch (IOException e) {
-			throw new StorageException("Failed to copy example file " + filename, e);
-		}
-	}
-	
-	@Override
-	public void copyExample(String uuid, String filename, Resource[] exampleResources) {
-		Path path = this.tempLocation.resolve(TEMP_DIR_PREFIX + uuid);
-		copyExample(path, filename, exampleResources);
-	}
-	
-	private void copyExample(Path path, String filename, Resource[] exampleResources) {
-		try {
-			Resource resource = Arrays.stream(exampleResources)
-					.filter(res -> filename.equals(res.getFilename()))
-					.findFirst()
-					.orElse(null);
-			if (resource != null) {
-				Files.copy(resource.getInputStream(), path.resolve(filename));
-			} else {
-				throw new StorageException("Could not access example file " + filename);
-			}
-		} catch (IOException e) {
-			throw new StorageException("Failed to copy example file " + filename, e);
-		}
-	}
-	
-	@Override
-	public Resource getResultArchive(String uuid) throws IOException {
-		Path folder = load(uuid);
-		zipResult(folder);
-		return loadAsResource(uuid, "result.zip");
-	}
-	
-	public static void zipResult(Path folder) throws IOException {
-		Path outputFileName = folder.resolve("result.zip");
-	    if (Files.exists(outputFileName)) {
-			Files.delete(outputFileName);
-	    }
-	    Path zipFile = Files.createFile(outputFileName);
-		try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile))) {
-	        Files.walk(folder)
-	        	.filter(path -> !Files.isDirectory(path))
-	        	.filter(path -> !path.getFileName().startsWith("."))
-	        	.filter(path -> !path.getFileName().endsWith("result.zip"))
-	        	.forEach(path -> {
-	        		try {
-	        			ZipEntry zipEntry = new ZipEntry(folder.relativize(path).toString());
-	        			zipOutputStream.putNextEntry(zipEntry);
-	        			Files.copy(path, zipOutputStream);
-	        			zipOutputStream.closeEntry();
-	        		} catch (IOException e) {
-	        			LOG.error("Failed to include {} to zip archive", path.getFileName(), e);
-	        		}
-		       });
-		} catch (Exception e){
-			LOG.error("Failed to produce result zip archive", e);
-		}
-	}
-
-}
diff --git a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageService.java b/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageService.java
deleted file mode 100644
index 09465fd..0000000
--- a/manager/src/main/java/org/eclipse/app4mc/cloud/manager/storage/StorageService.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*********************************************************************************
- * 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.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.stream.Stream;
-
-import org.springframework.core.io.Resource;
-import org.springframework.web.multipart.MultipartFile;
-
-public interface StorageService {
-
-	String store(MultipartFile file);
-	
-	void store(String uuid, MultipartFile file);
-
-	Stream<Path> loadAll();
-
-	Path load(String uuid, String... path);
-
-	Resource loadAsResource(String uuid, String... path);
-
-	void delete(String uuid);
-	
-	void deleteAll();
-
-	String copyExample(String filename, Resource[] exampleResources);
-	
-	void copyExample(String uuid, String filename, Resource[] exampleResources);
-	
-	Resource getResultArchive(String uuid) throws IOException;
-}