FELIX-1262: add local Bnd source to apply temporary patches

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@793527 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java b/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java
new file mode 100644
index 0000000..dd3f117
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/test/ProjectLauncher.java
@@ -0,0 +1,336 @@
+package aQute.bnd.test;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.build.*;
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+
+public class ProjectLauncher extends Processor {
+    final Project project;
+    static File   runtime;
+    String        report;
+
+    public ProjectLauncher(Project project) {
+        super(project);
+        this.project = project;
+    }
+
+    /**
+     * Calculate the classpath. We include our own runtime.jar which includes
+     * the test framework and we include the first of the test frameworks
+     * specified.
+     */
+    public String[] getClasspath() {
+        try {
+            List<String> classpath = new ArrayList<String>();
+            classpath.add(getRuntime().getAbsolutePath());
+
+            for (Container c : project.getRunpath()) {
+                if (c.getType() != Container.TYPE.ERROR) {
+                    if (!c.getFile().getName().startsWith("ee."))
+                        classpath.add(c.getFile().getAbsolutePath());
+                } else {
+                    error("Invalid entry on the " + Constants.RUNPATH + ": "
+                            + c);
+                }
+            }
+            return classpath.toArray(new String[classpath.size()]);
+        } catch (Exception e) {
+            error("Calculating class path", e);
+        }
+        return null;
+    }
+
+    /**
+     * Extract the runtime on the file system so we can refer to it. in the
+     * remote VM.
+     * 
+     * @return
+     */
+    public static File getRuntime() {
+        if (runtime == null) {
+            try {
+                URL url = ProjectLauncher.class
+                        .getResource("aQute.runtime.jar");
+                if (url == null)
+                    throw new IllegalStateException(
+                            "Can not find my runtime.jar");
+
+                runtime = File.createTempFile("aQute.runtime", ".jar");
+                // runtime.deleteOnExit();
+                FileOutputStream out = new FileOutputStream(runtime);
+                InputStream in = url.openStream();
+                copy(in, out);
+                out.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return runtime;
+    }
+
+    public void doRunbundles(List<String> programArguments) throws Exception {
+        // we are going to use this for running, so we want to
+        // use the "best" version. For compile, we should
+        // use the lowest version.
+        Collection<Container> testbundles = project.getRunbundles();
+
+        for (Container c : testbundles) {
+            if (c.getError() != null)
+                error("Invalid bundle on " + Constants.RUNBUNDLES + " "
+                        + c.getError());
+            else {
+                // Do we need to build any sub projects?
+                if (c.getVersion() != null && c.getVersion().equals("project")) {
+                    Project sub = c.getProject();
+                    sub.clear();
+                    File[] outputs = sub.build(false);
+                    for (File f : outputs) {
+                        programArguments.add("-bundle");
+                        programArguments.add(f.getAbsolutePath());
+                    }
+                    getInfo(sub);
+                } else {
+                    programArguments.add("-bundle");
+                    programArguments.add(c.getFile().getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    private void doRunpath(List<String> programArguments) throws Exception {
+        Collection<Container> testpath = project.getRunpath();
+        Container found = null;
+        for (Container c : testpath) {
+            if (c.getAttributes().containsKey("framework")) {
+                if (found != null) {
+                    warning("Specifying multiple framework classes on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("framework"));
+                found = c;
+            }
+            if (c.getAttributes().containsKey("factory")) {
+                if (found != null) {
+                    warning("Specifying multiple framework factories on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("factory"));
+                found = c;
+            }
+            String exports = c.getAttributes().get("export");
+            if (exports != null) {
+                String parts[] = exports.split("\\s*,\\s*");
+                for (String p : parts) {
+                    programArguments.add("-export");
+                    programArguments.add(p);
+                }
+            }
+        }
+
+        doSystemPackages(programArguments);
+        doRunProperties(programArguments);
+    }
+
+    private void doRunProperties(List<String> programArguments) {
+        Map<String, String> properties = OSGiHeader
+                .parseProperties(getProperty(RUNPROPERTIES));
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            programArguments.add("-set");
+            programArguments.add(entry.getKey());
+            programArguments.add(entry.getValue());
+        }
+    }
+
+    private void doSystemPackages(List<String> programArguments) {
+        Map<String, Map<String, String>> systemPackages = parseHeader(getProperty(RUNSYSTEMPACKAGES));
+        for (Map.Entry<String, Map<String, String>> entry : systemPackages
+                .entrySet()) {
+            programArguments.add("-export");
+            StringBuffer sb = new StringBuffer();
+            sb.append(entry.getKey());
+            printClause(entry.getValue(), null, sb);
+            programArguments.add(sb.toString());
+        }
+    }
+
+    private void doStorage(List<String> programArguments) throws Exception {
+        File tmp = new File(project.getTarget(), "fwtmp");
+        tmp.mkdirs();
+        tmp.deleteOnExit();
+
+        programArguments.add("-storage");
+        programArguments.add(tmp.getAbsolutePath());
+    }
+
+    /**
+     * Utility to copy a file from a resource.
+     * 
+     * @param in
+     * @param out
+     * @throws IOException
+     */
+    private static void copy(InputStream in, FileOutputStream out)
+            throws IOException {
+        byte buf[] = new byte[8192];
+        int size = in.read(buf);
+        while (size > 0) {
+            out.write(buf, 0, size);
+            size = in.read(buf);
+        }
+        in.close();
+    }
+
+    private Process launch(File[] targets) throws Exception {
+        List<String> arguments = newList();
+        List<String> vmArguments = newList();
+
+        vmArguments.add(getProperty("java", "java"));
+        doClasspath(vmArguments);
+
+        getArguments(targets, vmArguments, arguments);
+
+        vmArguments.add("aQute.junit.runtime.Target");
+        arguments.add("-report");
+        arguments.add(getTestreport());
+
+        List<String> all = newList();
+        all.addAll(vmArguments);
+        all.addAll(arguments);
+
+        System.out.println("Cmd: " + all);
+
+        String[] cmdarray = all.toArray(new String[all.size()]);
+        if (getErrors().size() > 0)
+            return null;
+
+        return Runtime.getRuntime().exec(cmdarray, null, project.getBase());
+    }
+/*
+    static Pattern ARGUMENT = Pattern.compile("[-a-zA-Z0-9\\._]+");
+    private void doVMArguments(List<String> arguments) {
+        Map<String,String> map = OSGiHeader.parseProperties( getProperty(RUNVM));
+        for ( String key : map.keySet() ) {
+            if ( ARGUMENT.matcher(key).matches())
+                if ( key.startsWith("-"))
+                    arguments.add(key);
+                else
+                    arguments.add("-D" + key.trim() + "=" + map.get(key));
+            else
+                warning("VM Argument is not a proper property key: " + key );
+        }
+    }
+*/
+    private void doVMArguments(List<String> arguments) {
+        Map<String, String> map = OSGiHeader
+                .parseProperties(getProperty(RUNVM));
+        for (String key : map.keySet()) {
+            if (key.startsWith("-"))
+                arguments.add(key);
+            else
+                arguments.add("-D" + key.trim() + "=" + map.get(key));
+        }
+    }
+    private void doClasspath(List<String> arguments) {
+        Collection<String> cp = Arrays.asList(getClasspath());
+        if (!cp.isEmpty()) {
+            arguments.add("-classpath");
+            arguments.add(join(cp, File.pathSeparator));
+        }
+    }
+
+    public int run(File f) throws Exception {
+        final Process process = launch(new File[] { f });
+
+        Thread killer = new Thread() {
+            public void run() {
+                process.destroy();
+            }
+        };
+        Runtime.getRuntime().addShutdownHook(killer);
+        Streamer sin = new Streamer(process.getInputStream(), System.out);
+        Streamer serr = new Streamer(process.getErrorStream(), System.out);
+        try {
+            sin.start();
+            serr.start();
+            return process.waitFor();
+        } finally {
+            Runtime.getRuntime().removeShutdownHook(killer);
+            sin.join();
+            serr.join();
+        }
+    }
+
+    static class Streamer extends Thread {
+        final InputStream  in;
+        final OutputStream out;
+
+        Streamer(InputStream in, OutputStream out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        public void run() {
+            try {
+                int c;
+                while ((c = in.read()) > 0) {
+                    this.out.write(c);
+                }
+            } catch (IOException ioe) {
+                // Ignore
+            }
+        };
+
+    };
+
+    public String getTestreport() {
+        if (report != null)
+            return report;
+        return report = getProperty(Constants.TESTREPORT,
+                "${target}/test-report.xml");
+
+    }
+
+    public void getArguments(List<String> vmArguments,
+            List<String> programArguments, boolean undertest) throws Exception {
+        File files[] = project.build(undertest);
+        getInfo(project);
+        if (files == null)
+            return;
+
+        getArguments(files, vmArguments, programArguments);
+    }
+
+    public void getArguments(File files[], List<String> vmArguments,
+            List<String> programArguments) throws Exception {
+        doVMArguments(vmArguments);
+        doStorage(programArguments);
+        doRunpath(programArguments);
+        doRunbundles(programArguments);
+        for (File file : files) {
+            programArguments.add("-target");
+            programArguments.add(file.getAbsolutePath());
+        }
+    }
+
+    public String getReport() {
+        return report;
+    }
+
+    public void setReport(String report) {
+        this.report = report;
+    }
+
+}