Introduce BackgroundTask to be able to a single tasks to another Thread

Change-Id: I00fa28fc5bb6fe9a32b92a8d9e0e541295cd8cf6
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
diff --git a/org.eclipse.tea.core/src/org/eclipse/tea/core/BackgroundTask.java b/org.eclipse.tea.core/src/org/eclipse/tea/core/BackgroundTask.java
new file mode 100644
index 0000000..88e12d9
--- /dev/null
+++ b/org.eclipse.tea.core/src/org/eclipse/tea/core/BackgroundTask.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ *  Copyright (c) 2017 SSI Schaefer IT Solutions GmbH and others.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *      SSI Schaefer IT Solutions GmbH
+ *******************************************************************************/
+package org.eclipse.tea.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.eclipse.e4.core.contexts.ContextInjectionFactory;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.tea.core.internal.model.TaskingModel;
+
+public class BackgroundTask {
+
+	private static final ExecutorService backgroundTasks = Executors
+			.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+
+	private final Object task;
+	private Future<?> future;
+
+	public BackgroundTask(Object task) {
+		Object actualTask;
+		if (task instanceof Class) {
+			try {
+				actualTask = ((Class<?>) task).newInstance();
+			} catch (Exception e) {
+				throw new IllegalStateException("Cannot create task " + task);
+			}
+		} else {
+			actualTask = task;
+		}
+
+		this.task = actualTask;
+	}
+
+	@Execute
+	public void run(IEclipseContext context) throws Exception {
+		future = backgroundTasks.submit(() -> {
+			ContextInjectionFactory.invoke(task, Execute.class, context);
+		});
+	}
+
+	public Object barrier() {
+		return new Object() {
+			@Execute
+			public Object doWait() throws Exception {
+				return future.get();
+			}
+
+			@Override
+			public String toString() {
+				return "Wait for: " + BackgroundTask.this.toString();
+			}
+		};
+	}
+
+	public static Object allBarrier(List<Object> tasks) {
+		List<BackgroundTask> toAwait = new ArrayList<>();
+		for (Object task : tasks) {
+			if (task instanceof BackgroundTask) {
+				toAwait.add((BackgroundTask) task);
+			}
+		}
+		if (toAwait.isEmpty()) {
+			return null;
+		}
+		return new Object() {
+			@Execute
+			public void doWaitAll(IEclipseContext context) throws Exception {
+				for (BackgroundTask bt : toAwait) {
+					bt.future.get();
+				}
+			}
+
+			@Override
+			public String toString() {
+				return "Await unfinished background tasks.";
+			}
+		};
+	}
+
+	@Override
+	public String toString() {
+		return TaskingModel.getTaskName(task) + " (parallel)";
+	}
+
+}
diff --git a/org.eclipse.tea.core/src/org/eclipse/tea/core/TaskExecutionContext.java b/org.eclipse.tea.core/src/org/eclipse/tea/core/TaskExecutionContext.java
index 65305f2..d33612d 100644
--- a/org.eclipse.tea.core/src/org/eclipse/tea/core/TaskExecutionContext.java
+++ b/org.eclipse.tea.core/src/org/eclipse/tea/core/TaskExecutionContext.java
@@ -108,6 +108,12 @@
 		// initialize this context
 		ContextInjectionFactory.invoke(chain, TaskChainContextInit.class, context);
 
+		// in case the context contains background-able tasks
+		Object barrierTask = BackgroundTask.allBarrier(tasks);
+		if (barrierTask != null) {
+			addTask(barrierTask);
+		}
+
 		// only notify about context creation if there is something to do. an
 		// empty context will not have any effect on anything and will, in fact,
 		// not be executed at all by the engine.
@@ -171,6 +177,10 @@
 		}
 	}
 
+	public void addTaskAt(int index, Object o) {
+		tasks.add(index, o);
+	}
+
 	/**
 	 * @param chain
 	 *            a TaskChain which should be initialized and executed lazily at
diff --git a/org.eclipse.tea.library.build.lcdsl/src/org/eclipse/tea/library/build/lcdsl/tasks/p2/AbstractProductBuild.java b/org.eclipse.tea.library.build.lcdsl/src/org/eclipse/tea/library/build/lcdsl/tasks/p2/AbstractProductBuild.java
index ab0925d..864d871 100644
--- a/org.eclipse.tea.library.build.lcdsl/src/org/eclipse/tea/library/build/lcdsl/tasks/p2/AbstractProductBuild.java
+++ b/org.eclipse.tea.library.build.lcdsl/src/org/eclipse/tea/library/build/lcdsl/tasks/p2/AbstractProductBuild.java
@@ -73,9 +73,14 @@
 	}
 
 	public TaskRunProductExport addProductTasks(TaskExecutionContext c, String updateSite, boolean zip) {
+		final TaskRunProductExport task = createProductExportTask(updateSite, zip);
+		c.addTask(task);
+		return task;
+	}
+
+	public TaskRunProductExport createProductExportTask(String updateSite, boolean zip) {
 		final TaskRunProductExport task = new TaskRunProductExport(updateSite, productBundle, productDefinition, zip);
 		task.setPlatformsToBuild(getPlatformsToBuild());
-		c.addTask(task);
 		return task;
 	}