Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/WorkflowExecutor.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/WorkflowExecutor.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/WorkflowExecutor.java	(nonexistent)
@@ -1,96 +0,0 @@
-package eu.dnetlib.msro.workflows.procs;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.PostConstruct;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import eu.dnetlib.clients.is.InformationServiceClient;
-import eu.dnetlib.enabling.common.Stoppable;
-import eu.dnetlib.enabling.common.StoppableDetails;
-import eu.dnetlib.msro.exceptions.MSROException;
-import eu.dnetlib.msro.workflows.WorkflowInstance;
-import eu.dnetlib.msro.workflows.WorkflowsConstants;
-import eu.dnetlib.services.async.AsyncServerCallback;
-
-@Component
-public class WorkflowExecutor implements Stoppable {
-
-	private static final Log log = LogFactory.getLog(WorkflowExecutor.class);
-
-	@Autowired
-	private InformationServiceClient isLookup;
-	@Autowired
-	private ProcessRegistry processRegistry;
-	@Autowired
-	private ProcessFactory processFactory;
-	@Autowired
-	private ProcessEngine processEngine;
-
-	private boolean paused = false;
-
-	@PostConstruct
-	public void init() {
-
-		Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
-			if (isPaused() || (processRegistry.countRunningWfs() >= WorkflowsConstants.MAX_RUNNING_PROCS_SIZE)) { return; }
-
-			final WorkflowProcess process = processRegistry.nextProcessToStart();
-			if (process != null) {
-				processEngine.startProcess(process);
-			} else {
-				log.debug("WorkflowProcess queue is empty");
-			}
-		}, 10, 10, TimeUnit.SECONDS);
-	}
-
-	public String startWorkflow(final WorkflowInstance wf, final AsyncServerCallback callback) throws Exception {
-
-		if (isPaused()) {
-			log.warn("Wf " + wf.getProfileId() + " not launched, because WorkflowExecutor is preparing for shutdown");
-			throw new MSROException("WorkflowExecutor is preparing for shutdown");
-		}
-
-		try {
-			final WorkflowProcess process = processFactory.newProcess(wf, callback);
-			return processRegistry.registerProcess(process);
-		} catch (final Exception e) {
-			log.error("Error ppreparing workflow: " + wf.getProfileId(), e);
-			throw new MSROException("Error parsing workflow");
-		}
-	}
-
-	@Override
-	public void stop() {
-		paused = true;
-	}
-
-	@Override
-	public void resume() {
-		paused = false;
-	}
-
-	@Override
-	public StoppableDetails getStopDetails() {
-		final int count = processRegistry.countRunningWfs();
-
-		final StoppableDetails.StopStatus status =
-				isPaused() ? (count == 0 ? StoppableDetails.StopStatus.STOPPED : StoppableDetails.StopStatus.STOPPING) : StoppableDetails.StopStatus.RUNNING;
-
-		return new StoppableDetails("D-NET workflow manager", "Running workflows: " + count, status);
-	}
-
-	public boolean isPaused() {
-		return paused;
-	}
-
-	public void setPaused(final boolean paused) {
-		this.paused = paused;
-	}
-
-}
Index: modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/controllers/MsroController.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/controllers/MsroController.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/controllers/MsroController.java	(revision 46885)
@@ -1,5 +1,8 @@
 package eu.dnetlib.msro.controllers;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -18,6 +21,7 @@
 import eu.dnetlib.msro.workflows.util.WorkflowDispatcher;
 import eu.dnetlib.services.BaseService;
 import eu.dnetlib.services.async.AsyncMethodException;
+import eu.dnetlib.services.async.AsyncRunnable;
 import eu.dnetlib.services.async.AsyncServerCallback;
 import eu.dnetlib.services.async.HasAsyncMethods;
 
@@ -32,25 +36,38 @@
 	private WorkflowDispatcher dispatcher;
 
 	@Override
-	public void processMethod(final String method, final String jsonParams, final AsyncServerCallback callback) throws AsyncMethodException {
+	public AsyncRunnable prepareThread(final String method, final String jsonParams, final AsyncServerCallback callback) throws AsyncMethodException {
 		final ObjectMapper mapper = new ObjectMapper();
 
-		try {
-			switch (method) {
-			case "startWorkflow":
-				dispatcher.startWorkflow(mapper.readValue(jsonParams, Workflow.class), getBaseUrl());
-				break;
-			case "startWorkflowTemplate":
-				dispatcher.startWorkflowTemplate(mapper.readValue(jsonParams, WorkflowTemplate.class), getBaseUrl());
-				break;
-			default:
-				log.warn("Invalid method: " + method);
-				throw new AsyncMethodException("Invalid method: " + method);
+		return new AsyncRunnable() {
+
+			@Override
+			public Map<String, String> prepare() {
+				return new HashMap<>();
 			}
-		} catch (final Throwable e) {
-			log.warn("Error executing method: " + method, e);
-			throw new AsyncMethodException("Error executing method: " + method, e);
-		}
+
+			@Override
+			public void execute() throws AsyncMethodException {
+				final WorkflowRef ref;
+				try {
+					switch (method) {
+					case "startWorkflow":
+						dispatcher.startWorkflow(mapper.readValue(jsonParams, Workflow.class), getBaseUrl());
+						break;
+					case "startWorkflowTemplate":
+						dispatcher.startWorkflowTemplate(mapper.readValue(jsonParams, WorkflowTemplate.class), getBaseUrl());
+						break;
+					default:
+						log.warn("Invalid method: " + method);
+						throw new AsyncMethodException("Invalid method: " + method);
+					}
+				} catch (final Throwable e) {
+					log.warn("Error executing method: " + method, e);
+					throw new AsyncMethodException("Error executing method: " + method, e);
+				}
+			}
+		};
+
 	}
 
 	@RequestMapping("startWorkflow")
Index: modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/workflows/util/WorkflowDispatcher.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/workflows/util/WorkflowDispatcher.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-msro-application/src/main/java/eu/dnetlib/msro/workflows/util/WorkflowDispatcher.java	(revision 46885)
@@ -110,12 +110,12 @@
 		return wf;
 	}
 
-	public void startWorkflowTemplate(final WorkflowTemplate wf,
+	public WorkflowRef startWorkflowTemplate(final WorkflowTemplate wf,
 			final String localBaseUrl) throws MSROException {
-		startWorkflowTemplate(wf.getId(), wf.getParent(), wf.getName(), wf.getFamily(), wf.getDsId(), wf.getIface(), wf.getParams(), localBaseUrl);
+		return startWorkflowTemplate(wf.getId(), wf.getParent(), wf.getName(), wf.getFamily(), wf.getDsId(), wf.getIface(), wf.getParams(), localBaseUrl);
 	}
 
-	public void startWorkflowTemplate(final String profileId,
+	public WorkflowRef startWorkflowTemplate(final String profileId,
 			final String parent,
 			final String name,
 			final String family,
@@ -155,7 +155,7 @@
 			wf.setGlobalParams(globalParams);
 			wf.setParent(parent);
 
-			executeWf(wf, localBaseUrl);
+			return executeWf(wf, localBaseUrl);
 		} catch (final Exception e) {
 			log.error("Error starting workflow template: " + profileId, e);
 			throw new MSROException("Error starting workflow template", e);
@@ -169,7 +169,7 @@
 
 		log.info("Assigning wf " + wf.getProfileId() + " to worker " + worker.getBaseUrl() + ", procId: ");
 
-		worker.execute(wf, localBaseUrl, new AsyncClientCallback() {
+		return worker.execute(wf, localBaseUrl, new AsyncClientCallback() {
 
 			private ObjectMapper mapper = new ObjectMapper();
 
@@ -200,8 +200,6 @@
 				}
 			}
 		});
-
-		return new WorkflowRef(null, worker.getBaseUrl());
 	}
 
 	private void saveResponse(final WorkflowInstance wf, final boolean success, final Map<String, String> res) {
Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/controllers/MsroWorkerController.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/controllers/MsroWorkerController.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/controllers/MsroWorkerController.java	(revision 46885)
@@ -1,7 +1,11 @@
 package eu.dnetlib.msro.controllers;
 
+import java.io.IOException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
@@ -20,7 +24,7 @@
 import eu.dnetlib.clients.msro.TerminatedWfDesc;
 import eu.dnetlib.enabling.annotations.DnetService;
 import eu.dnetlib.enabling.annotations.DnetServiceType;
-import eu.dnetlib.msro.annotations.ProcessNode;
+import eu.dnetlib.msro.exceptions.MSROException;
 import eu.dnetlib.msro.workers.aggregation.collect.CollectorPluginEnumerator;
 import eu.dnetlib.msro.workers.aggregation.collect.plugins.CollectorPlugin;
 import eu.dnetlib.msro.workers.aggregation.collect.plugins.DnetCollectorParam;
@@ -28,14 +32,16 @@
 import eu.dnetlib.msro.workers.aggregation.collect.plugins.ProtocolParameterType;
 import eu.dnetlib.msro.workflows.ProcessStatus;
 import eu.dnetlib.msro.workflows.WorkflowInstance;
-import eu.dnetlib.msro.workflows.nodes.AbstractParallelProcessNode;
+import eu.dnetlib.msro.workflows.procs.ProcessExecutor;
+import eu.dnetlib.msro.workflows.procs.ProcessFactory;
 import eu.dnetlib.msro.workflows.procs.ProcessRegistry;
-import eu.dnetlib.msro.workflows.procs.WorkflowExecutor;
+import eu.dnetlib.msro.workflows.procs.WorkflowProcess;
 import eu.dnetlib.msro.workflows.util.NodeHelper;
 import eu.dnetlib.services.BaseService;
 import eu.dnetlib.services.ServiceRunningInstance;
 import eu.dnetlib.services.ServiceStatus;
 import eu.dnetlib.services.async.AsyncMethodException;
+import eu.dnetlib.services.async.AsyncRunnable;
 import eu.dnetlib.services.async.AsyncServerCallback;
 import eu.dnetlib.services.async.HasAsyncMethods;
 
@@ -49,12 +55,15 @@
 	private static final long MAX_NUMBER_OF_TERMINATED_WFS = 5;
 
 	@Autowired
-	private WorkflowExecutor workflowExecutor;
+	private ProcessFactory processFactory;
 
 	@Autowired
 	private ProcessRegistry processRegistry;
 
 	@Autowired
+	private ProcessExecutor processExecutor;
+
+	@Autowired
 	private NodeHelper nodeHelper;
 
 	@Autowired
@@ -61,8 +70,8 @@
 	private CollectorPluginEnumerator collectorPlugins;
 
 	@RequestMapping(value = "nodes", method = RequestMethod.GET)
-	private List<String> nodes() {
-		return nodeHelper.availableNodes().stream().map(AbstractParallelProcessNode::getNodeType).collect(Collectors.toList());
+	private Set<String> nodes() {
+		return nodeHelper.getValidTypes().keySet();
 	}
 
 	@RequestMapping(value = "collector/plugins", method = RequestMethod.GET)
@@ -72,13 +81,13 @@
 
 	@RequestMapping(value = "pause", method = RequestMethod.GET)
 	private ServiceRunningInstance pause() {
-		workflowExecutor.setPaused(true);
+		processExecutor.setPaused(true);
 		return getStatus();
 	}
 
 	@RequestMapping(value = "resume", method = RequestMethod.GET)
 	private ServiceRunningInstance resume() {
-		workflowExecutor.setPaused(false);
+		processExecutor.setPaused(false);
 		return getStatus();
 	}
 
@@ -87,13 +96,11 @@
 		final Element nodes = DocumentHelper.createElement("WORKFLOW_NODES");
 		final Element collPlugins = DocumentHelper.createElement("COLLECTOR_PLUGINS");
 
-		nodeHelper.availableNodes().stream()
-				.map(Object::getClass)
-				.forEach(cl -> {
-					final Element n = nodes.addElement("NODE");
-					n.addAttribute("type", cl.getAnnotation(ProcessNode.class).value());
-					n.addAttribute("class", cl.getName());
-				});
+		nodeHelper.getValidTypes().entrySet().forEach(e -> {
+			final Element n = nodes.addElement("NODE");
+			n.addAttribute("type", e.getKey());
+			n.addAttribute("class", e.getValue().getName());
+		});
 
 		collectorPlugins.getAll().stream()
 				.map(o -> o.getClass().getAnnotation(DnetCollectorPlugin.class))
@@ -120,7 +127,7 @@
 		return MsroWorkerRunningInstance.newInstance(
 				getProfileId(),
 				getBaseUrl(),
-				baseDir(), workflowExecutor.isPaused() ? ServiceStatus.PAUSED : ServiceStatus.ACTIVE,
+				baseDir(), processExecutor.isPaused() ? ServiceStatus.PAUSED : ServiceStatus.ACTIVE,
 				processRegistry.countRunningWfs(),
 				processRegistry.countQueuedWfs(),
 				processRegistry.listTerminated()
@@ -131,14 +138,40 @@
 	}
 
 	@Override
-	public void processMethod(final String method, final String jsonParams, final AsyncServerCallback callback) throws AsyncMethodException {
+	public AsyncRunnable prepareThread(final String method, final String jsonParams, final AsyncServerCallback callback) throws AsyncMethodException {
 		final ObjectMapper mapper = new ObjectMapper();
 
 		try {
 			switch (method) {
 			case "startWorkflow":
-				workflowExecutor.startWorkflow(mapper.readValue(jsonParams, WorkflowInstance.class), callback);
-				break;
+				return new AsyncRunnable() {
+
+					private WorkflowProcess proc;
+
+					@Override
+					public Map<String, String> prepare() throws AsyncMethodException {
+						try {
+							proc = processFactory.newProcess(mapper.readValue(jsonParams, WorkflowInstance.class), callback);
+
+							final Map<String, String> map = new HashMap<>();
+							map.put("procId", proc.getId());
+							return map;
+						} catch (final MSROException | IOException e) {
+							throw new AsyncMethodException(e);
+						}
+					}
+
+					@Override
+					public void execute() throws AsyncMethodException {
+						try {
+							processExecutor.execute(proc);
+						} catch (final MSROException e) {
+							throw new AsyncMethodException(e);
+						}
+					}
+
+				};
+
 			default:
 				log.warn("Invalid method: " + method);
 				throw new AsyncMethodException("Invalid method: " + method);
@@ -147,6 +180,7 @@
 			log.warn("Error executing method: " + method, e);
 			throw new AsyncMethodException("Error executing method: " + method, e);
 		}
+
 	}
 
 }
Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessEngine.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessEngine.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessEngine.java	(revision 46885)
@@ -20,7 +20,7 @@
 import eu.dnetlib.msro.workflows.GraphNode;
 import eu.dnetlib.msro.workflows.ProcessStatus;
 import eu.dnetlib.msro.workflows.WorkflowsConstants;
-import eu.dnetlib.msro.workflows.nodes.AbstractParallelProcessNode;
+import eu.dnetlib.msro.workflows.nodes.AbstractProcessNode;
 import eu.dnetlib.msro.workflows.util.NodeHelper;
 import eu.dnetlib.msro.workflows.util.NodeTokenCallback;
 import eu.dnetlib.services.async.AsyncMethodStatus;
@@ -50,7 +50,7 @@
 		try {
 			for (final GraphNode node : process.getGraph().startNodes()) {
 
-				final AbstractParallelProcessNode pNode = nodeHelper.newProcessNode(node, process, process.getEnv());
+				final AbstractProcessNode pNode = nodeHelper.newProcessNode(node, process, process.getEnv());
 				final Token token = new Token(node.getName(), newNodeTokenCallback(process, node));
 
 				token.getEnv().addAttributes(process.getEnv().getAttributes());
@@ -83,7 +83,7 @@
 					if (list.size() == process.getGraph().getNumberOfIncomingArcs(node)) {
 						final Token token = new Token(node.getName(), newNodeTokenCallback(process, node));
 						token.getEnv().addAttributes(mergeEnvParams(list.toArray(new Token[list.size()])));
-						final AbstractParallelProcessNode pNode = nodeHelper.newProcessNode(node, process, token.getEnv());
+						final AbstractProcessNode pNode = nodeHelper.newProcessNode(node, process, token.getEnv());
 
 						process.getTokens().add(token);
 						process.setLastActivityDate(DateUtils.now());
@@ -97,7 +97,7 @@
 				} else {
 					final Token token = new Token(node.getName(), newNodeTokenCallback(process, node));
 					token.getEnv().addAttributes(oldToken.getEnv().getAttributes());
-					final AbstractParallelProcessNode pNode = nodeHelper.newProcessNode(node, process, token.getEnv());
+					final AbstractProcessNode pNode = nodeHelper.newProcessNode(node, process, token.getEnv());
 
 					process.getTokens().add(token);
 					process.setLastActivityDate(DateUtils.now());
Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessRegistry.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessRegistry.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessRegistry.java	(revision 46885)
@@ -17,8 +17,8 @@
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 
+import eu.dnetlib.WorkerProperties;
 import eu.dnetlib.miscutils.datetime.DateUtils;
-import eu.dnetlib.WorkerProperties;
 import eu.dnetlib.msro.exceptions.MSROException;
 import eu.dnetlib.msro.workflows.WorkflowsConstants;
 
@@ -71,7 +71,7 @@
 		}
 	}
 
-	public String registerProcess(final WorkflowProcess process) throws MSROException {
+	public void registerProcess(final WorkflowProcess process) throws MSROException {
 		if (procs.containsValue(process) || procs.containsKey(process.getId())) {
 			log.error("Already registerd process: " + process);
 			throw new MSROException("Already registerd process: " + process);
@@ -99,8 +99,6 @@
 
 			log.info("WorkflowProcess [" + process + "] in queue, priority=" + process.getPriority());
 		}
-
-		return process.getId();
 	}
 
 	private void removeOldestProcess() {
Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessExecutor.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessExecutor.java	(nonexistent)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/procs/ProcessExecutor.java	(revision 46885)
@@ -0,0 +1,76 @@
+package eu.dnetlib.msro.workflows.procs;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import eu.dnetlib.enabling.common.Stoppable;
+import eu.dnetlib.enabling.common.StoppableDetails;
+import eu.dnetlib.msro.exceptions.MSROException;
+import eu.dnetlib.msro.workflows.WorkflowsConstants;
+
+@Component
+public class ProcessExecutor implements Stoppable {
+
+	@Autowired
+	private ProcessEngine processEngine;
+	@Autowired
+	private ProcessRegistry processRegistry;
+
+	private boolean paused = false;
+
+	private static final Log log = LogFactory.getLog(ProcessExecutor.class);
+
+	@PostConstruct
+	public void init() {
+
+		Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
+			if (isPaused() || (processRegistry.countRunningWfs() >= WorkflowsConstants.MAX_RUNNING_PROCS_SIZE)) { return; }
+
+			final WorkflowProcess process = processRegistry.nextProcessToStart();
+			if (process != null) {
+				processEngine.startProcess(process);
+			} else {
+				log.debug("WorkflowProcess queue is empty");
+			}
+		}, 10, 10, TimeUnit.SECONDS);
+	}
+
+	public void execute(final WorkflowProcess proc) throws MSROException {
+		processRegistry.registerProcess(proc);
+	}
+
+	@Override
+	public void stop() {
+		paused = true;
+	}
+
+	@Override
+	public void resume() {
+		paused = false;
+	}
+
+	@Override
+	public StoppableDetails getStopDetails() {
+		final int count = processRegistry.countRunningWfs();
+
+		final StoppableDetails.StopStatus status =
+				isPaused() ? (count == 0 ? StoppableDetails.StopStatus.STOPPED : StoppableDetails.StopStatus.STOPPING) : StoppableDetails.StopStatus.RUNNING;
+
+		return new StoppableDetails("D-NET workflow manager", "Running workflows: " + count, status);
+	}
+
+	public boolean isPaused() {
+		return paused;
+	}
+
+	public void setPaused(final boolean paused) {
+		this.paused = paused;
+	}
+}
Index: modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/util/NodeHelper.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/util/NodeHelper.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-simple-aggregation-worker/src/main/java/eu/dnetlib/msro/workflows/util/NodeHelper.java	(revision 46885)
@@ -1,12 +1,13 @@
 package eu.dnetlib.msro.workflows.util;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
+import javax.annotation.PostConstruct;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -24,7 +25,7 @@
 import eu.dnetlib.msro.exceptions.MSROException;
 import eu.dnetlib.msro.workflows.GraphNode;
 import eu.dnetlib.msro.workflows.GraphNodeParameter;
-import eu.dnetlib.msro.workflows.nodes.AbstractParallelProcessNode;
+import eu.dnetlib.msro.workflows.nodes.AbstractProcessNode;
 import eu.dnetlib.msro.workflows.nodes.DefaultJobNode;
 import eu.dnetlib.msro.workflows.nodes.SuccessNode;
 import eu.dnetlib.msro.workflows.procs.Env;
@@ -41,24 +42,38 @@
 
 	private ApplicationContext applicationContext;
 
-	private Set<String> validTypes = new HashSet<>();
+	private Map<String, Class<?>> validTypes = new HashMap<>();
 
-	public AbstractParallelProcessNode newProcessNode(final GraphNode node, final WorkflowProcess process, final Env env) throws MSROException {
+	@PostConstruct
+	public void init() {
+		validTypes = applicationContext.getBeansWithAnnotation(ProcessNode.class)
+				.values()
+				.stream()
+				.filter(bean -> bean instanceof AbstractProcessNode)
+				.map(bean -> (AbstractProcessNode) bean)
+				.collect(Collectors.toMap(
+						bean -> bean.getClass().getAnnotation(ProcessNode.class).value(),
+						Object::getClass));
+
+	}
+
+	public AbstractProcessNode newProcessNode(final GraphNode node, final WorkflowProcess process, final Env env) throws MSROException {
 		if (node.isSucessNode()) {
 			return new SuccessNode();
 		} else if (StringUtils.isBlank(node.getType())) {
 			return new DefaultJobNode(node.getName());
 		} else {
-			final AbstractParallelProcessNode pnode = applicationContext.getBeansWithAnnotation(ProcessNode.class)
+			final Optional<AbstractProcessNode> optional = applicationContext.getBeansWithAnnotation(ProcessNode.class)
 					.values()
 					.stream()
 					.filter(bean -> bean.getClass().getAnnotation(ProcessNode.class).value().equals(node.getType()))
-					.filter(bean -> bean instanceof AbstractParallelProcessNode)
-					.map(bean -> (AbstractParallelProcessNode) bean)
-					.findFirst()
-					.get();
+					.filter(bean -> bean instanceof AbstractProcessNode)
+					.map(bean -> (AbstractProcessNode) bean)
+					.findFirst();
 
-			if (pnode != null) {
+			if (optional.isPresent()) {
+				final AbstractProcessNode pnode = optional.get();
+
 				pnode.setNodeName(node.getName());
 				// I invoke the setter methods using the static params of the graph node
 				try {
@@ -71,20 +86,12 @@
 				}
 				return pnode;
 			} else {
-				log.error("cannot find node " + node.getType());
+				log.error("cannot find node " + node.getType() + ", valid types are: " + validTypes);
 				throw new MSROException("cannot find node " + node.getType());
 			}
 		}
 	}
 
-	public List<AbstractParallelProcessNode> availableNodes() {
-		return applicationContext.getBeansOfType(AbstractParallelProcessNode.class)
-				.values()
-				.stream()
-				.filter(cl -> cl.getClass().isAnnotationPresent(ProcessNode.class))
-				.collect(Collectors.toList());
-	}
-
 	public Map<String, Object> resolveParamsWithNoEnv(final GraphNode node) {
 		return resolveParams(node, null);
 	}
@@ -153,17 +160,11 @@
 	}
 
 	public synchronized boolean isValidType(final String type) {
-		if (validTypes.isEmpty()) {
-			validTypes = applicationContext.getBeansWithAnnotation(ProcessNode.class)
-					.values()
-					.stream()
-					.filter(bean -> bean instanceof AbstractParallelProcessNode)
-					.map(bean -> (AbstractParallelProcessNode) bean)
-					.map(bean -> bean.getClass().getAnnotation(ProcessNode.class).value())
-					.collect(Collectors.toSet());
-		}
+		return validTypes.containsKey(type);
+	}
 
-		return validTypes.contains(type);
+	public Map<String, Class<?>> getValidTypes() {
+		return validTypes;
 	}
 
 }
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/clients/msro/MsroWorkerClient.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/clients/msro/MsroWorkerClient.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/clients/msro/MsroWorkerClient.java	(revision 46885)
@@ -1,5 +1,7 @@
 package eu.dnetlib.clients.msro;
 
+import java.util.Map;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
@@ -9,6 +11,7 @@
 import eu.dnetlib.enabling.annotations.DnetServiceClient;
 import eu.dnetlib.enabling.annotations.DnetServiceType;
 import eu.dnetlib.msro.workflows.WorkflowInstance;
+import eu.dnetlib.msro.workflows.WorkflowRef;
 import eu.dnetlib.services.async.AsyncClientCallback;
 import eu.dnetlib.services.async.AsyncClientUtils;
 
@@ -24,8 +27,10 @@
 		return (new RestTemplate()).getForObject(getBaseUrl(), MsroWorkerRunningInstance.class);
 	}
 
-	public void execute(final WorkflowInstance wf, final String localBaseUrl, final AsyncClientCallback callback) {
-		asyncClientUtils.invokeRemoteMethod(getBaseUrl(), "startWorkflow", localBaseUrl, wf, callback);
+	public WorkflowRef execute(final WorkflowInstance wf, final String localBaseUrl, final AsyncClientCallback callback) {
+		final Map<String, String> info = asyncClientUtils.invokeRemoteMethod(getBaseUrl(), "startWorkflow", localBaseUrl, wf, callback);
+
+		return new WorkflowRef(info.get("procId"), getBaseUrl());
 	}
 
 }
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/AsyncInvocation.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/AsyncInvocation.java	(nonexistent)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/AsyncInvocation.java	(revision 46885)
@@ -0,0 +1,39 @@
+package eu.dnetlib.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AsyncInvocation {
+
+	private String id;
+	private Map<String, String> info = new HashMap<String, String>();
+
+	public AsyncInvocation() {
+
+	}
+
+	public AsyncInvocation(final String id) {
+		this.id = id;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(final String id) {
+		this.id = id;
+	}
+
+	public Map<String, String> getInfo() {
+		return info;
+	}
+
+	public void setInfo(final Map<String, String> info) {
+		this.info = info;
+	}
+
+	public void addInfoDetail(final String name, final String value) {
+		info.put(name, value);
+	}
+
+}
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/BaseService.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/BaseService.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/BaseService.java	(revision 46885)
@@ -28,9 +28,10 @@
 import eu.dnetlib.enabling.annotations.DnetService;
 import eu.dnetlib.enabling.annotations.DnetServiceType;
 import eu.dnetlib.exceptions.DnetGenericException;
-import eu.dnetlib.exceptions.DnetGenericRuntimeException;
 import eu.dnetlib.services.async.AsyncClientUtils;
 import eu.dnetlib.services.async.AsyncResponse;
+import eu.dnetlib.services.async.AsyncRunnable;
+import eu.dnetlib.services.async.AsyncServerCallback;
 import eu.dnetlib.services.async.HasAsyncMethods;
 import eu.dnetlib.services.async.ResponseAck;
 import eu.dnetlib.services.async.ResponseAck.ResponseAckStatus;
@@ -112,7 +113,7 @@
 	}
 
 	@RequestMapping(value = "async/method/{method}", method = RequestMethod.POST)
-	public final String asyncMethod(@PathVariable final String method,
+	public final AsyncInvocation asyncMethod(@PathVariable final String method,
 			@RequestParam(required = false) final String caller,
 			@RequestBody final String jsonParams)
 			throws DnetGenericException {
@@ -121,27 +122,21 @@
 
 		if (this instanceof HasAsyncMethods) {
 
-			final String id = "invocation-" + UUID.randomUUID();
+			final AsyncInvocation async = new AsyncInvocation("invocation-" + UUID.randomUUID());
 
-			new Thread(() -> {
-				try {
-					log.info("Async invocation:" + id);
+			log.info("Async invocation:" + async.getId());
 
-					if (StringUtils.isNotBlank(caller)) {
-						final String url = caller + "/async/response/" + id;
-						((HasAsyncMethods) this).processMethod(method, jsonParams, res -> sendResponse(url, res));
-					} else {
-						log.info("Caller: none");
-						((HasAsyncMethods) this).processMethod(method, jsonParams, res -> {});
-					}
-				} catch (final Exception e) {
-					log.error("Invocation of async method failed", e);
-					throw new DnetGenericRuntimeException("Invocation of async method failed", e);
-				}
-			}).start();
+			final AsyncServerCallback callback =
+					StringUtils.isNotBlank(caller) ? res -> sendResponse(caller + "/async/response/" + async.getId(), res) : res -> {};
 
-			return id;
+			final AsyncRunnable runnable = ((HasAsyncMethods) this).prepareThread(method, jsonParams, callback);
 
+			async.setInfo(runnable.prepare());
+
+			(new Thread(runnable)).start();
+
+			return async;
+
 		} else {
 			log.error("Service " + getServiceType() + " does not implement HasAsyncMethods interface");
 			throw new DnetGenericException("Service " + getServiceType() + " does not implement HasAsyncMethods interface");
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncRunnable.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncRunnable.java	(nonexistent)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncRunnable.java	(revision 46885)
@@ -0,0 +1,22 @@
+package eu.dnetlib.services.async;
+
+import java.util.Map;
+
+import eu.dnetlib.exceptions.DnetGenericRuntimeException;
+
+public abstract class AsyncRunnable implements Runnable {
+
+	abstract public Map<String, String> prepare() throws AsyncMethodException;
+
+	abstract public void execute() throws AsyncMethodException;
+
+	@Override
+	public final void run() {
+		try {
+			execute();
+		} catch (final AsyncMethodException e) {
+			throw new DnetGenericRuntimeException("Error executing async method", e);
+		}
+	}
+
+}
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncClientUtils.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncClientUtils.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/AsyncClientUtils.java	(revision 46885)
@@ -9,6 +9,8 @@
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.RestTemplate;
 
+import eu.dnetlib.services.AsyncInvocation;
+
 @Component
 public class AsyncClientUtils {
 
@@ -16,7 +18,7 @@
 
 	private static final Map<String, AsyncClientCallback> activeRemoteCalls = new HashMap<>();
 
-	public void invokeRemoteMethod(final String remoteBaseUrl,
+	public Map<String, String> invokeRemoteMethod(final String remoteBaseUrl,
 			final String remoteMethod,
 			final String localBaseUrl,
 			final Object o,
@@ -26,10 +28,12 @@
 
 		log.info("invoking async method: " + url);
 
-		final String id = (new RestTemplate()).postForObject(url, o, String.class);
+		final AsyncInvocation async = (new RestTemplate()).postForObject(url, o, AsyncInvocation.class);
 		if (callback != null) {
-			activeRemoteCalls.put(id, callback);
+			activeRemoteCalls.put(async.getId(), callback);
 		}
+
+		return async.getInfo();
 	}
 
 	public boolean processResponse(final String id, final AsyncResponse response) {
Index: modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/HasAsyncMethods.java
===================================================================
--- modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/HasAsyncMethods.java	(revision 46884)
+++ modules/dnet-springboot-apps/trunk/dnet-common-utils/src/main/java/eu/dnetlib/services/async/HasAsyncMethods.java	(revision 46885)
@@ -2,6 +2,6 @@
 
 public interface HasAsyncMethods {
 
-	void processMethod(String method, String jsonParams, AsyncServerCallback callback) throws AsyncMethodException;
+	AsyncRunnable prepareThread(String method, String jsonParams, AsyncServerCallback callback) throws AsyncMethodException;
 
 }
