diff --git a/bnd.karaf.demo/.classpath b/bnd.karaf.demo/.classpath
new file mode 100644
index 0000000..023968b
--- /dev/null
+++ b/bnd.karaf.demo/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/bnd.karaf.demo/.project b/bnd.karaf.demo/.project
new file mode 100644
index 0000000..a617c44
--- /dev/null
+++ b/bnd.karaf.demo/.project
@@ -0,0 +1,23 @@
+
+
+ bnd.karaf.demo
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ bndtools.core.bndbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ bndtools.core.bndnature
+
+
diff --git a/bnd.karaf.demo/.settings/org.eclipse.jdt.core.prefs b/bnd.karaf.demo/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..8000cd6
--- /dev/null
+++ b/bnd.karaf.demo/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bnd.karaf.demo/bnd.bnd b/bnd.karaf.demo/bnd.bnd
new file mode 100644
index 0000000..d19ea21
--- /dev/null
+++ b/bnd.karaf.demo/bnd.bnd
@@ -0,0 +1,6 @@
+-runproperties: karaf.home=/Users/developer/Downloads/apache-karaf-2.2.6
+-runpath: bnd.karaf.launcher;version=1.0.0
+Private-Package: demo
+-buildpath: osgi.core;version=4.2.0.200908310645
+Bundle-Activator: demo.Activator
+-runfw: org.eclipse.osgi;version='[3.6.2.R36x_v20110210,3.6.2.R36x_v20110210]'
\ No newline at end of file
diff --git a/bnd.karaf.demo/build.xml b/bnd.karaf.demo/build.xml
new file mode 100644
index 0000000..23063e2
--- /dev/null
+++ b/bnd.karaf.demo/build.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/bnd.karaf.demo/src/demo/Activator.java b/bnd.karaf.demo/src/demo/Activator.java
new file mode 100644
index 0000000..961f13c
--- /dev/null
+++ b/bnd.karaf.demo/src/demo/Activator.java
@@ -0,0 +1,18 @@
+package demo;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ System.out.println("Start Demo Bundle.");
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ System.out.println("Stop Demo Bundle.");
+ }
+
+}
diff --git a/bnd.karaf.launcher/.classpath b/bnd.karaf.launcher/.classpath
new file mode 100644
index 0000000..023968b
--- /dev/null
+++ b/bnd.karaf.launcher/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/bnd.karaf.launcher/.project b/bnd.karaf.launcher/.project
new file mode 100644
index 0000000..0738e1e
--- /dev/null
+++ b/bnd.karaf.launcher/.project
@@ -0,0 +1,23 @@
+
+
+ bnd.karaf.launcher
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ bndtools.core.bndbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ bndtools.core.bndnature
+
+
diff --git a/bnd.karaf.launcher/.settings/org.eclipse.jdt.core.prefs b/bnd.karaf.launcher/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..8000cd6
--- /dev/null
+++ b/bnd.karaf.launcher/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/bnd.karaf.launcher/bnd.bnd b/bnd.karaf.launcher/bnd.bnd
new file mode 100644
index 0000000..1b27217
--- /dev/null
+++ b/bnd.karaf.launcher/bnd.bnd
@@ -0,0 +1,8 @@
+Bundle-SymbolicName: bnd.karaf.launcher
+Bundle-Version: 1.0.1
+Launcher-Plugin: bnd.karaf.launcher.plugin.KarafProjectLauncher
+-buildpath: biz.aQute.bndlib;version=1.51.0,\
+ org.apache.karaf.main;version=2.2.6
+Private-Package: bnd.karaf.launcher.plugin,\
+ bnd.karaf.launcher,\
+ org.apache.karaf.main
\ No newline at end of file
diff --git a/bnd.karaf.launcher/build.xml b/bnd.karaf.launcher/build.xml
new file mode 100644
index 0000000..23063e2
--- /dev/null
+++ b/bnd.karaf.launcher/build.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/bnd.karaf.launcher/src/bnd/karaf/launcher/Launcher.java b/bnd.karaf.launcher/src/bnd/karaf/launcher/Launcher.java
new file mode 100644
index 0000000..d2678b6
--- /dev/null
+++ b/bnd.karaf.launcher/src/bnd/karaf/launcher/Launcher.java
@@ -0,0 +1,789 @@
+package bnd.karaf.launcher;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.Vector;
+
+import org.apache.karaf.main.Main;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class Launcher implements ServiceListener {
+
+ private PrintStream out;
+ private LauncherProperties parms;
+ private Framework systemBundle;
+ private final Properties properties;
+ private boolean security;
+ private Runnable mainThread;
+ private PackageAdmin padmin;
+ private static File propertiesFile;
+ private final Timer timer = new Timer();
+ private final List embedded = new ArrayList();
+ private TimerTask watchdog = null;
+ private final Map errors = new HashMap();
+ private final Map installedBundles = new LinkedHashMap();
+
+ public static void main(String[] args) {
+ try {
+ String path = System
+ .getProperty(LauncherConstants.LAUNCHER_PROPERTIES);
+ assert path != null;
+ propertiesFile = new File(path).getAbsoluteFile();
+ propertiesFile.deleteOnExit();
+ FileInputStream in = new FileInputStream(propertiesFile);
+ Properties properties = new Properties();
+ try {
+ properties.load(in);
+ } finally {
+ in.close();
+ }
+ Launcher target = new Launcher(properties);
+ target.run(args);
+ } catch (Throwable t) {
+ // Last resort ... errors should be handled lower
+ t.printStackTrace(System.err);
+ }
+ }
+
+ public Launcher(Properties properties) throws Exception {
+ this.properties = properties;
+ System.getProperties().putAll(properties);
+ this.parms = new LauncherProperties(properties);
+
+ out = System.err;
+ trace("inited runbundles=%s activators=%s timeout=%s",
+ parms.runbundles, parms.activators, parms.timeout);
+ watchdog = new TimerTask() {
+ long begin = propertiesFile.lastModified();
+
+ public void run() {
+ if (begin < propertiesFile.lastModified()) {
+ try {
+ FileInputStream in = new FileInputStream(propertiesFile);
+ Properties properties = new Properties();
+ try {
+ properties.load(in);
+ } finally {
+ in.close();
+ }
+ parms = new LauncherProperties(properties);
+ update();
+ } catch (Exception e) {
+ error("Error in updating the framework from the properties: %s",
+ e);
+ }
+ begin = propertiesFile.lastModified();
+ }
+ }
+ };
+ timer.scheduleAtFixedRate(watchdog, 5000, 1000);
+ }
+
+ private void run(String args[]) throws Throwable {
+ try {
+ int status = activate();
+ if (status != 0) {
+ report(out);
+ System.exit(status);
+ }
+
+ trace("framework=" + systemBundle);
+
+ // Register the command line with ourselves as the
+ // service.
+ if (parms.services) { // Does not work for our dummy framework
+ Hashtable argprops = new Hashtable();
+ argprops.put(LauncherConstants.LAUNCHER_ARGUMENTS, args);
+ argprops.put(LauncherConstants.LAUNCHER_READY, "true");
+ systemBundle.getBundleContext().registerService(
+ Launcher.class.getName(), this, argprops);
+ trace("registered launcher with arguments for syncing");
+ }
+
+ // Wait until a Runnable is registered with main.thread=true.
+ // not that this will never happen when we're running on the mini fw
+ // but the test case normally exits.
+ synchronized (this) {
+ while (mainThread == null) {
+ trace("will wait for a registered Runnable");
+ wait();
+ }
+ }
+ trace("Will run %s as main thread", mainThread);
+ mainThread.run();
+ } catch (Throwable e) {
+ error("Unexpected error in the run body: %s", e);
+ throw e;
+ } finally {
+ systemBundle.stop();
+ trace("stopped system bundle due to leaving run body");
+ }
+ }
+
+ private List split(String value, String separator) {
+ List list = new ArrayList();
+ if (value == null)
+ return list;
+
+ StringTokenizer st = new StringTokenizer(value, separator);
+ while (st.hasMoreTokens())
+ list.add(st.nextToken());
+
+ return list;
+ }
+
+ public int activate() throws Exception {
+ Policy.setPolicy(new AllPolicy());
+
+ systemBundle = createFramework();
+ if (systemBundle == null)
+ return LauncherConstants.ERROR;
+
+ doTimeoutHandler();
+
+ // Initialize this framework so it becomes STARTING
+ systemBundle.start();
+ trace("system bundle started ok");
+
+ BundleContext systemContext = systemBundle.getBundleContext();
+ ServiceReference ref = systemContext
+ .getServiceReference(PackageAdmin.class.getName());
+ if (ref != null) {
+ padmin = (PackageAdmin) systemContext.getService(ref);
+ } else
+ trace("could not get package admin");
+
+ systemContext.addServiceListener(this,
+ "(&(objectclass=java.lang.Runnable)(main.thread=true))");
+
+ update();
+
+ if (parms.trace) {
+ report(out);
+ }
+
+ // Start embedded activators
+ trace("start embedded activators");
+ if (parms.activators != null) {
+ ClassLoader loader = getClass().getClassLoader();
+ for (Object token : parms.activators) {
+ try {
+ Class> clazz = loader.loadClass((String) token);
+ BundleActivator activator = (BundleActivator) clazz
+ .newInstance();
+ embedded.add(activator);
+ trace("adding activator %s", activator);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Embedded Bundle Activator incorrect: " + token
+ + ", " + e);
+ }
+ }
+ }
+ int result = LauncherConstants.OK;
+ for (BundleActivator activator : embedded)
+ try {
+ trace("starting activator %s", activator);
+ activator.start(systemContext);
+ } catch (Exception e) {
+ error("Starting activator %s : %s", activator, e);
+ result = LauncherConstants.ERROR;
+ }
+
+ return result;
+ }
+
+ /**
+ * @param systemContext
+ * @throws MalformedURLException
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private void update() throws Exception {
+ trace("Updating framework with %s", parms.runbundles);
+
+ // Turn the bundle location paths into files
+ List desired = new ArrayList();
+ for (Object o : parms.runbundles) {
+ File file = new File((String) o).getAbsoluteFile();
+ if (!file.exists())
+ error("Bundle files does not exist: " + file);
+ else
+ desired.add(file);
+ }
+
+ // deleted = old - new
+ List tobedeleted = new ArrayList(installedBundles.keySet());
+ tobedeleted.removeAll(desired);
+
+ // updated = old /\ new
+ List tobeupdated = new ArrayList(installedBundles.keySet());
+ tobeupdated.retainAll(desired);
+
+ // install = new - old
+ List tobeinstalled = new ArrayList(desired);
+ tobeinstalled.removeAll(installedBundles.keySet());
+
+ List tobestarted = new ArrayList();
+
+ for (File f : tobedeleted)
+ try {
+ trace("uninstalling %s", f);
+ installedBundles.get(f).uninstall();
+ installedBundles.remove(f);
+ } catch (Exception e) {
+ error("Failed to uninstall bundle %s, exception %s", f, e);
+ }
+
+ for (File f : tobeinstalled)
+ try {
+ trace("installing %s", f);
+ Bundle b = install(f);
+ installedBundles.put(f, b);
+ tobestarted.add(b);
+ } catch (Exception e) {
+ error("Failed to uninstall bundle %s, exception %s", f, e);
+ }
+
+ for (File f : tobeupdated)
+ try {
+ Bundle b = installedBundles.get(f);
+ if (b.getLastModified() < f.lastModified()) {
+ trace("updating %s", f);
+ if (b.getState() == Bundle.ACTIVE) {
+ tobestarted.add(b);
+ b.stop();
+ }
+ b.update();
+ } else
+ trace("bundle is still current according to timestamp %s",
+ f);
+ } catch (Exception e) {
+ error("Failed to update bundle %s, exception %s", f, e);
+ }
+
+ if (padmin != null)
+ padmin.refreshPackages(null);
+
+ trace("bundles administered %s", installedBundles.keySet());
+
+ // From now on, the bundles are on their own. They have
+ // by default AllPermission, but if they install bundles
+ // they will not automatically get AllPermission anymore
+
+ // Get the resolved status
+ if (padmin != null && padmin.resolveBundles(null) == false) {
+ error("could not resolve the bundles");
+ // return LauncherConstants.RESOLVE_ERROR;
+ }
+
+ // Now start all the installed bundles in the same order
+ // (unless they're a fragment)
+
+ trace("Will start bundles: %s", tobestarted);
+ for (Bundle b : tobestarted) {
+ try {
+ trace("starting %s", b.getSymbolicName());
+ if (!isFragment(b))
+ b.start(Bundle.START_ACTIVATION_POLICY);
+ trace("started %s", b.getSymbolicName());
+ } catch (BundleException e) {
+ error("Failed to start bundle %s-%s, exception %s",
+ b.getSymbolicName(), b.getVersion(), e);
+ }
+ }
+
+ }
+
+ Bundle install(File f) throws Exception {
+ BundleContext context = systemBundle.getBundleContext();
+ try {
+ trace("will install %s with reference", f.getAbsolutePath());
+ String reference = "reference:"
+ + f.toURI().toURL().toExternalForm();
+ Bundle b = context.installBundle(reference);
+ if (b.getLastModified() < f.lastModified()) {
+ b.update();
+ }
+ return b;
+ } catch (BundleException e) {
+ trace("failed reference, will try to install %s with input stream",
+ f.getAbsolutePath());
+ String reference = f.toURI().toURL().toExternalForm();
+ InputStream in = new FileInputStream(f);
+ try {
+ return context.installBundle(reference, in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
+ private void doTimeoutHandler() {
+ // Ensure we properly close in a separate thread so that
+ // we can leverage the main thread, which is required for macs
+ Thread wait = new Thread("FrameworkWaiter") {
+ public void run() {
+ try {
+ FrameworkEvent result = systemBundle
+ .waitForStop(parms.timeout);
+ trace("framework event " + result + " " + result.getType());
+ Thread.sleep(1000);
+ switch (result.getType()) {
+ case FrameworkEvent.STOPPED:
+ System.exit(LauncherConstants.OK);
+ break;
+ case FrameworkEvent.WAIT_TIMEDOUT:
+ System.exit(LauncherConstants.TIMEDOUT);
+ break;
+
+ case FrameworkEvent.ERROR:
+ System.exit(LauncherConstants.ERROR);
+ break;
+
+ case FrameworkEvent.WARNING:
+ System.exit(LauncherConstants.WARNING);
+ break;
+
+ case FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED:
+ case FrameworkEvent.STOPPED_UPDATE:
+ System.exit(LauncherConstants.UPDATE_NEEDED);
+ break;
+ }
+ } catch (InterruptedException e) {
+ System.exit(LauncherConstants.CANCELED);
+ } finally {
+ System.err.println("System exiting due to timeout of "
+ + parms.timeout);
+ }
+ }
+ };
+ wait.start();
+ }
+
+ private boolean isFragment(Bundle b) {
+ return padmin != null
+ && padmin.getBundleType(b) == PackageAdmin.BUNDLE_TYPE_FRAGMENT;
+ }
+
+ public void deactivate() throws Exception {
+ if (systemBundle != null) {
+ systemBundle.stop();
+ systemBundle.waitForStop(parms.timeout);
+ }
+ }
+
+ public void addSystemPackage(String packageName) {
+ parms.systemPackages = concat(parms.systemPackages, packageName);
+ }
+
+ private String concat(String a, String b) {
+ if (a == null)
+ return b;
+ else if (b == null)
+ return a;
+
+ return a + "," + b;
+ }
+
+ private Framework createFramework() throws Exception {
+ String karafHomeFolder = parms.runProperties.get("karaf.home");
+ if (karafHomeFolder == null) {
+ error("Karaf home folder is not set, "
+ + "please setup run property karaf.home "
+ + "to your Karaf installation folder.");
+ return null;
+ }
+ System.setProperty("karaf.home", karafHomeFolder);
+ System.setProperty("karaf.base", karafHomeFolder);
+ System.setProperty("karaf.data", karafHomeFolder + "/data");
+ System.setProperty("karaf.history", karafHomeFolder
+ + "/data/history.txt");
+ System.setProperty("karaf.instances", karafHomeFolder + "/instances");
+ System.setProperty("karaf.startLocalConsole", "true");
+ System.setProperty("karaf.startRemoteShell", "false");
+ System.setProperty("karaf.lock", "false");
+ Main main = new Main(new String[0]);
+ main.launch();
+
+ systemBundle = main.getFramework();
+ return systemBundle;
+ }
+
+ /**
+ * Try to get the stupid service interface ...
+ *
+ * @param loader
+ * @param string
+ * @return
+ * @throws IOException
+ */
+ private List getMetaInfServices(ClassLoader loader, String factory)
+ throws IOException {
+ if (loader == null)
+ loader = getClass().getClassLoader();
+
+ Enumeration e = loader
+ .getResources("META-INF/services/" + factory);
+ List factories = new ArrayList();
+
+ while (e.hasMoreElements()) {
+ URL url = (URL) e.nextElement();
+ trace("found META-INF/services in %s", url);
+
+ InputStream in = url.openStream();
+ try {
+ BufferedReader rdr = new BufferedReader(new InputStreamReader(
+ in, "UTF-8"));
+ String line;
+ while ((line = rdr.readLine()) != null) {
+ trace(line);
+ line = line.trim();
+ if (!line.startsWith("#") && line.length() > 0) {
+ factories.add(line);
+ }
+ }
+ } finally {
+ in.close();
+ }
+ }
+ return factories;
+ }
+
+ public void addBundle(File resource) {
+ parms.runbundles.add(resource.getAbsolutePath());
+ }
+
+ private void delete(File f) {
+ String path = f.getAbsolutePath();
+ char first = path.charAt(0);
+ if (path.equals("/")
+ || (first >= 'A' && first <= 'Z' && path.substring(1).equals(
+ ":\\")))
+ throw new IllegalArgumentException(
+ "You can not make the root the storage area because it will be deleted");
+ if (f.isDirectory()) {
+ File fs[] = f.listFiles();
+ for (int i = 0; i < fs.length; i++)
+ delete(fs[i]);
+ }
+ f.delete();
+ }
+
+ public void report(PrintStream out) {
+ try {
+ out.println("------------------------------- REPORT --------------------------");
+ out.println();
+ row(out, "Framework",
+ systemBundle == null ? "<>" : systemBundle.getClass());
+ row(out, "Framework type", parms.services ? "META-INF/services"
+ : "mini framework");
+ row(out, "Storage", parms.storageDir);
+ row(out, "Keep", parms.keep);
+ row(out, "Security", security);
+ list(out, fill("Run bundles", 40), parms.runbundles);
+ list(out,
+ fill("Classpath", 40),
+ split(System.getProperty("java.class.path"),
+ File.pathSeparator));
+ list(out, fill("System Packages", 40),
+ split(parms.systemPackages, ","));
+ row(out, "Properties");
+ for (java.util.Map.Entry