Introduce DetachedLauncher based on osgiboot
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 22 Jul 2009 15:23:18 +0000 (15:23 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 22 Jul 2009 15:23:18 +0000 (15:23 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@2723 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/JvmProcess.java
runtime/org.argeo.slc.detached/pom.xml
runtime/org.argeo.slc.lib.detached/src/main/java/org/argeo/slc/lib/detached/DetachedLauncher.java [new file with mode: 0644]
runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/Activator.java
runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/Launcher.java [new file with mode: 0644]
runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/OsgiBoot.java

index d8429d192d1d9675516d47a6365e0e199426f887..b2b613494c5c5d90be063d418b0290236447db90 100644 (file)
@@ -86,7 +86,7 @@ public class DefaultExecutionFlowDescriptorConverter implements
                        Assert.notNull(executionSpec.getName());
 
                        Map<String, Object> values = new TreeMap<String, Object>();
-                       attrs: for (String key : executionSpec.getAttributes().keySet()) {
+                       for (String key : executionSpec.getAttributes().keySet()) {
                                ExecutionSpecAttribute attribute = executionSpec
                                                .getAttributes().get(key);
 
index ba1d67eb558ac0296e46d9dba4ddbb0ec7a979b6..8c0bfebe95a58393d6cf17bd70606b59cc1d3542 100644 (file)
@@ -1,6 +1,8 @@
 package org.argeo.slc.core.execution.tasks;
 
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -8,10 +10,15 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.commons.exec.CommandLine;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.slc.SlcException;
 import org.springframework.core.io.Resource;
 
 public class JvmProcess extends SystemCall {
+       private final static Log log = LogFactory.getLog(JvmProcess.class);
+
        private Properties systemProperties = new Properties();
        private List<Resource> classpath = new ArrayList<Resource>();
        private List<Resource> pBootClasspath = new ArrayList<Resource>();
@@ -29,10 +36,9 @@ public class JvmProcess extends SystemCall {
                        cl = new CommandLine("java");
 
                if (pBootClasspath.size() > 0) {
-                       StringBuffer buf = new StringBuffer("-Xbootclasspath/p:");
+                       StringBuffer buf = new StringBuffer("-Xbootclasspath/p");
                        for (Resource res : pBootClasspath) {
-                               if (buf.length() != 0)
-                                       buf.append(File.pathSeparatorChar);
+                               buf.append(File.pathSeparatorChar);
                                buf.append(asFile(res));
                        }
                        cl.addArgument(buf.toString());
@@ -62,19 +68,38 @@ public class JvmProcess extends SystemCall {
                        cl.addArgument(arg);
                }
 
+               if (log.isDebugEnabled())
+                       log.debug("Command line:\n" + cl.toString() + "\n");
+
                return cl;
        }
 
        protected File asFile(Resource res) {
-               // TODO: implements local copy
                try {
                        return res.getFile().getCanonicalFile();
+               } catch (FileNotFoundException e) {
+                       return copyToTempFile(res);
                } catch (IOException e) {
                        throw new SlcException("Cannot convert resource to file", e);
                }
 
        }
 
+       protected File copyToTempFile(Resource res) {
+               File tempFile;
+               FileOutputStream fos;
+               try {
+                       tempFile = File.createTempFile("slcJvmProcess-", res.getFilename());
+                       tempFile.deleteOnExit();
+                       fos = new FileOutputStream(tempFile);
+                       IOUtils.copy(res.getInputStream(), fos);
+               } catch (IOException e) {
+                       throw new SlcException("Cannot copy " + res + " to temp file.", e);
+               }
+               IOUtils.closeQuietly(fos);
+               return tempFile;
+       }
+
        public Properties getSystemProperties() {
                return systemProperties;
        }
index 33dafd5848cc96c31699d40d75b919412509b3d8..9a307e017dfd4b924476c1fe58497f646437fc23 100644 (file)
                                                <Import-Package>
                                                        org.w3c.dom;version="0.0.0",
                                                        javax.xml.*;version="0.0.0",
-                                                       org.apache.xerces.jaxp,
+                                                       *
+                                               </Import-Package>
+                                               <!--
+                                                       <Import-Package> org.w3c.dom;version="0.0.0",
+                                                       javax.xml.*;version="0.0.0", org.apache.xerces.jaxp,
                                                        org.springframework.beans.factory;version="2.0",
                                                        org.springframework.beans.factory.support;version="2.0",
                                                        org.springframework.beans.factory.xml;version="2.0",
                                                        org.springframework.context;version="2.0",
                                                        org.springframework.context.support;version="2.0",
-                                                       org.springframework.core.io;version="2.0",
-                                                       *
-                                               </Import-Package>
-                                               <Spring-Context>*;create-asynchronously:=false</Spring-Context>
+                                                       org.springframework.core.io;version="2.0", * </Import-Package>
+                                                       <Spring-Context>*;create-asynchronously:=false</Spring-Context>
+                                               -->
                                        </instructions>
                                </configuration>
                        </plugin>
diff --git a/runtime/org.argeo.slc.lib.detached/src/main/java/org/argeo/slc/lib/detached/DetachedLauncher.java b/runtime/org.argeo.slc.lib.detached/src/main/java/org/argeo/slc/lib/detached/DetachedLauncher.java
new file mode 100644 (file)
index 0000000..505fa66
--- /dev/null
@@ -0,0 +1,123 @@
+package org.argeo.slc.lib.detached;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.core.execution.tasks.JvmProcess;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class DetachedLauncher extends JvmProcess implements BundleContextAware,
+               InitializingBean, ResourceLoaderAware {
+       private final static Log log = LogFactory.getLog(DetachedLauncher.class);
+
+       private BundleContext bundleContext = null;
+       private ResourceLoader resourceLoader = null;
+
+       private Resource osgiFramework = null;
+       private String osgibootBundleName = "org.argeo.slc.osgiboot";
+       private String equinoxBundleName = "org.eclipse.osgi";
+       private String xmlapisBundleName = "com.springsource.org.apache.xmlcommons";
+       private String xercesBundleName = "com.springsource.org.apache.xerces";
+
+       /**
+        * Required by Spring for JDK 1.4. see
+        * http://forum.springsource.org/showthread.php?t=74555
+        */
+       private Boolean prependXmlJars = false;
+
+       public DetachedLauncher() {
+               // Override defaults
+               setSynchronous(false);
+               setMainClass("org.argeo.slc.osgiboot.Launcher");
+       }
+
+       public void afterPropertiesSet() throws Exception {
+               if (bundleContext == null)
+                       throw new SlcException("An OSGi bundle context is required.");
+
+               // Equinox jar
+               if (osgiFramework == null)
+                       getClasspath()
+                                       .add(asResource(System.getProperty("osgi.framework")));
+               else
+                       getClasspath().add(osgiFramework);
+
+               StringBuffer osgiLocations = new StringBuffer("");
+               bundles: for (Bundle bundle : bundleContext.getBundles()) {
+                       String name = bundle.getSymbolicName();
+
+                       // Special bundles
+                       if (osgibootBundleName.equals(name))
+                               getClasspath().add(findOsgiboot(bundle));
+                       else if (equinoxBundleName.equals(name))
+                               continue bundles;// skip framework
+                       else if (xmlapisBundleName.equals(name) && prependXmlJars)
+                               getPBootClasspath().add(asResource(bundle.getLocation()));
+                       else if (xercesBundleName.equals(name) && prependXmlJars)
+                               getPBootClasspath().add(asResource(bundle.getLocation()));
+
+                       if (osgiLocations.length() != 0)
+                               osgiLocations.append(',');
+                       osgiLocations.append(bundle.getLocation());
+               }
+
+               getSystemProperties().setProperty("osgi.bundles",
+                               osgiLocations.toString());
+       }
+
+       protected Resource findOsgiboot(Bundle bundle) {
+               String location = bundle.getLocation();
+               if (location.startsWith("initial@reference:file:"))
+                       location = System.getProperty("osgi.install.area") + "/../"
+                                       + location.substring("initial@reference:file:".length());
+               if (location.charAt(location.length() - 1) == '/')
+                       location.substring(0, location.length() - 1);
+               return asResource(location);
+       }
+
+       protected Resource asResource(String location) {
+               Resource res = resourceLoader.getResource(location);
+               if (log.isDebugEnabled())
+                       log.debug("Converted " + location + " to " + res);
+               return res;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       public void setResourceLoader(ResourceLoader resourceLoader) {
+               this.resourceLoader = resourceLoader;
+       }
+
+       public void setOsgibootBundleName(String osgibootBundleName) {
+               this.osgibootBundleName = osgibootBundleName;
+       }
+
+       public void setXmlapisBundleName(String xmlapisBundleName) {
+               this.xmlapisBundleName = xmlapisBundleName;
+       }
+
+       public void setXercesBundleName(String xercesBundleName) {
+               this.xercesBundleName = xercesBundleName;
+       }
+
+       public void setOsgiFramework(Resource osgiFramework) {
+               this.osgiFramework = osgiFramework;
+       }
+
+       public void setEquinoxBundleName(String equinoxBundleName) {
+               this.equinoxBundleName = equinoxBundleName;
+       }
+
+       public void setPrependXmlJars(Boolean prependXmlJars) {
+               this.prependXmlJars = prependXmlJars;
+       }
+
+}
index 0945ca7197f5ea2c19904219e70a0a2aef3c9b29..80ec7b7f218e306c121f74832770229abc8c5d10 100644 (file)
@@ -3,21 +3,27 @@ package org.argeo.slc.osgiboot;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 
+/**
+ * An OSGi configurator. See <a
+ * href="http://wiki.eclipse.org/Configurator">http:
+ * //wiki.eclipse.org/Configurator</a>
+ */
 public class Activator implements BundleActivator {
 
        public void start(BundleContext bundleContext) throws Exception {
-               try {
-                       OsgiBoot.info("SLC OSGi bootstrap starting...");
-                       OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
-                       osgiBoot.installUrls(osgiBoot.getBundlesUrls());
-                       osgiBoot.installUrls(osgiBoot.getLocationsUrls());
-                       osgiBoot.installUrls(osgiBoot.getModulesUrls());
-                       osgiBoot.startBundles();
-                       OsgiBoot.info("SLC OSGi bootstrap completed");
-               } catch (Exception e) {
-                       e.printStackTrace();
-                       throw e;
-               }
+               OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+               osgiBoot.bootstrap();
+//             try {
+//                     OsgiBoot.info("SLC OSGi bootstrap starting...");
+//                     osgiBoot.installUrls(osgiBoot.getBundlesUrls());
+//                     osgiBoot.installUrls(osgiBoot.getLocationsUrls());
+//                     osgiBoot.installUrls(osgiBoot.getModulesUrls());
+//                     osgiBoot.startBundles();
+//                     OsgiBoot.info("SLC OSGi bootstrap completed");
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//                     throw e;
+//             }
        }
 
        public void stop(BundleContext context) throws Exception {
diff --git a/runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/Launcher.java b/runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/Launcher.java
new file mode 100644 (file)
index 0000000..8116b71
--- /dev/null
@@ -0,0 +1,111 @@
+package org.argeo.slc.osgiboot;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.osgi.framework.BundleContext;
+
+public class Launcher {
+
+       public static void main(String[] args) {
+               startMainClass();
+
+               BundleContext bundleContext = null;
+               try {
+                       bundleContext = EclipseStarter.startup(args, null);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot start Equinox.", e);
+               }
+
+               OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+               osgiBoot.bootstrap();
+       }
+//
+//     protected static void startEquinox(Properties config) throws Exception {
+//             info("java.home=" + System.getProperty("java.home"));
+//             info("java.class.path=" + System.getProperty("java.class.path"));
+//
+//             File baseDir = new File(System.getProperty("user.dir"))
+//                             .getCanonicalFile();
+//             String equinoxConfigurationPath = baseDir.getPath() + File.separator
+//                             + "slc-detached" + File.separator + "equinoxConfiguration";
+//
+//             String equinoxArgsLineDefault = "-console -noExit -clean -debug -configuration "
+//                             + equinoxConfigurationPath;
+//             String equinoxArgsLine = config.getProperty(PROP_SLC_OSGI_EQUINOX_ARGS,
+//                             equinoxArgsLineDefault);
+//             // String[] equinoxArgs = { "-console", "-noExit", "-clean", "-debug",
+//             // "-configuration", equinoxConfigurationPath };
+//             String[] equinoxArgs = equinoxArgsLine.split(" ");
+//
+//             BundleContext context = EclipseStarter.startup(equinoxArgs, null);
+//     }
+
+       protected static void startMainClass() {
+               Properties config = System.getProperties();
+               String className = config.getProperty("slc.detached.appclass");
+               String[] uiArgs = readArgumentsFromLine(config.getProperty(
+                               "slc.detached.appargs", ""));
+               try {
+                       // Launch main method using reflection
+                       Class clss = Class.forName(className);
+                       Class[] mainArgsClasses = new Class[] { uiArgs.getClass() };
+                       Object[] mainArgs = { uiArgs };
+                       Method mainMethod = clss.getMethod("main", mainArgsClasses);
+                       mainMethod.invoke(null, mainArgs);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot start main class.", e);
+               }
+
+       }
+
+       /**
+        * Transform a line into an array of arguments, taking "" as single
+        * arguments. (nested \" are not supported)
+        */
+       private static String[] readArgumentsFromLine(String lineOrig) {
+
+               String line = lineOrig.trim();// remove trailing spaces
+               // System.out.println("line=" + line);
+               List args = new Vector();
+               StringBuffer curr = new StringBuffer("");
+               boolean inQuote = false;
+               char[] arr = line.toCharArray();
+               for (int i = 0; i < arr.length; i++) {
+                       char c = arr[i];
+                       switch (c) {
+                       case '\"':
+                               inQuote = !inQuote;
+                               break;
+                       case ' ':
+                               if (!inQuote) {// otherwise, no break: goes to default
+                                       if (curr.length() > 0) {
+                                               args.add(curr.toString());
+                                               curr = new StringBuffer("");
+                                       }
+                                       break;
+                               }
+                       default:
+                               curr.append(c);
+                               break;
+                       }
+               }
+
+               // Add last arg
+               if (curr.length() > 0) {
+                       args.add(curr.toString());
+                       curr = null;
+               }
+
+               String[] res = new String[args.size()];
+               for (int i = 0; i < args.size(); i++) {
+                       res[i] = args.get(i).toString();
+                       // System.out.println("res[i]=" + res[i]);
+               }
+               return res;
+       }
+
+}
index dd2b1b691930f011d3a8f329f1d764a9d7e75b33..fcefc8345e974986d70255920605af9619a01d46 100644 (file)
@@ -45,6 +45,15 @@ public class OsgiBoot {
                this.bundleContext = bundleContext;
        }
 
+       public void bootstrap() {
+               info("SLC OSGi bootstrap starting...");
+               installUrls(getBundlesUrls());
+               installUrls(getLocationsUrls());
+               installUrls(getModulesUrls());
+               startBundles();
+               info("SLC OSGi bootstrap completed");
+       }
+
        public void installUrls(List urls) {
                Map installedBundles = getInstalledBundles();
                for (int i = 0; i < urls.size(); i++) {
@@ -109,12 +118,12 @@ public class OsgiBoot {
 
        }
 
-       public void startBundles() throws Exception {
+       public void startBundles() {
                String bundlesToStart = getProperty(PROP_SLC_OSGI_START);
                startBundles(bundlesToStart);
        }
 
-       public void startBundles(String bundlesToStartStr) throws Exception {
+       public void startBundles(String bundlesToStartStr) {
                if (bundlesToStartStr == null)
                        return;
 
@@ -127,7 +136,7 @@ public class OsgiBoot {
                startBundles(bundlesToStart);
        }
 
-       public void startBundles(List bundlesToStart) throws Exception {
+       public void startBundles(List bundlesToStart) {
                if (bundlesToStart.size() == 0)
                        return;
 
@@ -299,7 +308,7 @@ public class OsgiBoot {
                StringTokenizer st = new StringTokenizer(bundleLocations,
                                File.pathSeparator);
                while (st.hasMoreTokens()) {
-                       urls.add(baseUrl + st.nextToken().trim());
+                       urls.add(locationToUrl(baseUrl, st.nextToken().trim()));
                }
                return urls;
        }
@@ -349,7 +358,7 @@ public class OsgiBoot {
                for (int i = 0; i < included.size(); i++) {
                        String fullPath = (String) included.get(i);
                        if (!excluded.contains(fullPath))
-                               urls.add(baseUrl + fullPath);
+                               urls.add(locationToUrl(baseUrl, fullPath));
                }
 
                return urls;
@@ -419,6 +428,18 @@ public class OsgiBoot {
                }
        }
 
+       protected String locationToUrl(String baseUrl, String location) {
+               int extInd = location.lastIndexOf('.');
+               String ext = null;
+               if (extInd > 0)
+                       ext = location.substring(extInd);
+
+               if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
+                       return "file:" + location;
+               else
+                       return baseUrl + location;
+       }
+
        /** Transforms a relative path in a full system path. */
        protected String relativeToFullPath(String basePath, String relativePath) {
                return (basePath + '/' + relativePath).replace('/', File.separatorChar);