Temporarily include bndlib 1.47 for testing purposes (not yet on central)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/ProjectLauncher.java b/bundleplugin/src/main/java/aQute/bnd/build/ProjectLauncher.java
new file mode 100644
index 0000000..2b78943
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/build/ProjectLauncher.java
@@ -0,0 +1,327 @@
+package aQute.bnd.build;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.jar.*;
+
+import aQute.bnd.service.RepositoryPlugin.Strategy;
+import aQute.lib.osgi.*;
+import aQute.libg.command.*;
+import aQute.libg.generics.*;
+
+/**
+ * A Project Launcher is a base class to be extended by launchers. Launchers are
+ * JARs that launch a framework and install a number of bundles and then run the
+ * framework. A launcher jar must specify a Launcher-Class manifest header. This
+ * class is instantiated and cast to a LauncherPlugin. This plug in is then
+ * asked to provide a ProjectLauncher. This project launcher is then used by the
+ * project to run the code. Launchers must extend this class.
+ * 
+ */
+public abstract class ProjectLauncher {
+	private final Project						project;
+	private long								timeout				= 0;
+	private final Collection<String>			classpath			= new ArrayList<String>();
+	private List<String>						runbundles			= Create.list();
+	private final List<String>					runvm				= new ArrayList<String>();
+	private Map<String, String>					runproperties;
+	private Command								java;
+	private Map<String, Map<String, String>>	runsystempackages;
+	private final List<String>					activators			= Create.list();
+	private File								storageDir;
+
+	private boolean								trace;
+	private boolean								keep;
+	private int									framework;
+
+	public final static int						SERVICES			= 10111;
+	public final static int						NONE				= 20123;
+
+	// MUST BE ALIGNED WITH LAUNCHER
+	public final static int						OK					= 0;
+	public final static int						WARNING				= -1;
+	public final static int						ERROR				= -2;
+	public final static int						TIMEDOUT			= -3;
+	public final static int						UPDATE_NEEDED		= -4;
+	public final static int						CANCELED			= -5;
+	public final static int						DUPLICATE_BUNDLE	= -6;
+	public final static int						RESOLVE_ERROR		= -7;
+	public final static int						ACTIVATOR_ERROR		= -8;
+	public final static int						CUSTOM_LAUNCHER		= -128;
+
+	public final static String					EMBEDDED_ACTIVATOR	= "Embedded-Activator";
+
+	public ProjectLauncher(Project project) throws Exception {
+		this.project = project;
+
+		updateFromProject();
+	}
+
+	/**
+	 * Collect all the aspect from the project and set the local fields from
+	 * them. Should be called
+	 * 
+	 * @throws Exception
+	 */
+	protected void updateFromProject() throws Exception {
+		// pkr: could not use this because this is killing the runtests.
+		// project.refresh();
+		runbundles.clear();
+		Collection<Container> run = project.getRunbundles();
+		
+		for (Container container : run) {
+			File file = container.getFile();
+			if (file != null && (file.isFile() || file.isDirectory()))
+				runbundles.add(file.getAbsolutePath());
+		}
+
+		if (project.getRunBuilds()) {
+			File[] builds = project.build();
+			if (builds != null)
+				for (File file : builds)
+					runbundles.add(file.getAbsolutePath());
+		}
+
+		Collection<Container> runpath = project.getRunpath();
+		runsystempackages = project.parseHeader(project.getProperty(Constants.RUNSYSTEMPACKAGES));
+		framework = getRunframework(project.getProperty(Constants.RUNFRAMEWORK));
+		trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
+
+		timeout = Processor.getDuration(project.getProperty(Constants.RUNTIMEOUT), 0);
+		trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
+
+		// For backward compatibility with bndtools launcher
+		List<Container> fws = project.getBundles(Strategy.HIGHEST, project.getProperty("-runfw"), "-runfw");
+		runpath.addAll(fws);
+
+		for (Container c : runpath) {
+			addClasspath(c);
+		}
+
+		runvm.addAll(project.getRunVM());
+		runproperties = project.getRunProperties();
+
+		storageDir = project.getRunStorage();
+		if (storageDir == null) {
+			storageDir = new File(project.getTarget(), "fw");
+		}
+	}
+
+	private int getRunframework(String property) {
+		if (Constants.RUNFRAMEWORK_NONE.equalsIgnoreCase(property))
+			return NONE;
+		else if (Constants.RUNFRAMEWORK_SERVICES.equalsIgnoreCase(property))
+			return SERVICES;
+
+		return SERVICES;
+	}
+
+	public void addClasspath(Container container) throws Exception {
+		if (container.getError() != null) {
+			project.error("Cannot launch because %s has reported %s", container.getProject(),
+					container.getError());
+		} else {
+			Collection<Container> members = container.getMembers();
+			for (Container m : members) {
+				String path = m.getFile().getAbsolutePath();
+				if (!classpath.contains(path)) {
+					classpath.add(path);
+
+					Manifest manifest = m.getManifest();
+					if (manifest != null) {
+						Map<String, Map<String, String>> exports = project.parseHeader(manifest
+								.getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
+						for (Map.Entry<String, Map<String, String>> e : exports.entrySet()) {
+							if (!runsystempackages.containsKey(e.getKey()))
+								runsystempackages.put(e.getKey(), e.getValue());
+						}
+
+						// Allow activators on the runpath. They are called
+						// after
+						// the framework is completely initialized wit the
+						// system
+						// context.
+						String activator = manifest.getMainAttributes()
+								.getValue(EMBEDDED_ACTIVATOR);
+						if (activator != null)
+							activators.add(activator);
+					}
+				}
+			}
+		}
+	}
+
+	public void addRunBundle(String f) {
+		runbundles.add(f);
+	}
+
+	public Collection<String> getRunBundles() {
+		return runbundles;
+	}
+
+	public void addRunVM(String arg) {
+		runvm.add(arg);
+	}
+
+	public Collection<String> getRunpath() {
+		return classpath;
+	}
+
+	public Collection<String> getClasspath() {
+		return classpath;
+	}
+
+	public Collection<String> getRunVM() {
+		return runvm;
+	}
+
+	public Collection<String> getArguments() {
+		return Collections.emptySet();
+	}
+
+	public Map<String, String> getRunProperties() {
+		return runproperties;
+	}
+	
+	public File getStorageDir() {
+		return storageDir;
+	}
+
+	public abstract String getMainTypeName();
+
+	public abstract void update() throws Exception;
+
+	public int launch() throws Exception {
+		prepare();
+		java = new Command();
+		java.add(project.getProperty("java", "java"));
+		java.add("-cp");
+		java.add(Processor.join(getClasspath(), File.pathSeparator));
+		java.addAll(getRunVM());
+		java.add(getMainTypeName());
+		java.addAll(getArguments());
+		if (timeout != 0)
+			java.setTimeout(timeout + 1000, TimeUnit.MILLISECONDS);
+
+		int result = java.execute(System.in, System.out, System.err);
+		if (result == Integer.MIN_VALUE)
+			return TIMEDOUT;
+
+		reportResult(result);
+		return result;
+	}
+
+	protected void reportResult(int result) {
+		switch (result) {
+		case OK:
+			project.trace("Command terminated normal %s", java);
+			break;
+		case TIMEDOUT:
+			project.error("Launch timedout: %s", java);
+			break;
+
+		case ERROR:
+			project.error("Launch errored: %s", java);
+			break;
+
+		case WARNING:
+			project.warning("Launch had a warning %s", java);
+			break;
+		default:
+			project.warning("Unknown code %d from launcher: %s", result, java);
+			break;
+		}
+	}
+
+	public void setTimeout(long timeout, TimeUnit unit) {
+		this.timeout = unit.convert(timeout, TimeUnit.MILLISECONDS);
+	}
+
+	public long getTimeout() {
+		return this.timeout;
+	}
+
+	public void cancel() {
+		java.cancel();
+	}
+
+	public Map<String, Map<String, String>> getSystemPackages() {
+		return runsystempackages;
+	}
+
+	public void setKeep(boolean keep) {
+		this.keep = keep;
+	}
+
+	public boolean isKeep() {
+		return keep;
+	}
+
+	public void setTrace(boolean level) {
+		this.trace = level;
+	}
+
+	public boolean getTrace() {
+		return this.trace;
+	}
+
+	/**
+	 * Should be called when all the changes to the launchers are set. Will
+	 * calculate whatever is necessary for the launcher.
+	 * 
+	 * @throws Exception
+	 */
+	public abstract void prepare() throws Exception;
+
+	public Project getProject() {
+		return project;
+	}
+
+	public boolean addActivator(String e) {
+		return activators.add(e);
+	}
+
+	public Collection<String> getActivators() {
+		return Collections.unmodifiableCollection(activators);
+	}
+
+	/**
+	 * Either NONE or SERVICES to indicate how the remote end launches. NONE
+	 * means it should not use the classpath to run a framework. This likely
+	 * requires some dummy framework support. SERVICES means it should load the
+	 * framework from the claspath.
+	 * 
+	 * @return
+	 */
+	public int getRunFramework() {
+		return framework;
+	}
+
+	public void setRunFramework(int n) {
+		assert n == NONE || n == SERVICES;
+		this.framework = n;
+	}
+
+	/**
+	 * Add the specification for a set of bundles the runpath if it does not
+	 * already is included. This can be used by subclasses to ensure the proper
+	 * jars are on the classpath.
+	 * 
+	 * @param defaultSpec
+	 *            The default spec for default jars
+	 */
+	public void addDefault(String defaultSpec) throws Exception {
+		Collection<Container> deflts = project.getBundles(Strategy.HIGHEST, defaultSpec, null);
+		for (Container c : deflts)
+			addClasspath(c);
+	}
+
+	/**
+	 * Create a self executable.
+	 */
+
+	public Jar executable() throws Exception {
+		throw new UnsupportedOperationException();
+	}
+}