Improve init launch
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 6 May 2024 06:45:09 +0000 (08:45 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 6 May 2024 06:45:09 +0000 (08:45 +0200)
org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java
org.argeo.init/src/org/argeo/init/ServiceMain.java
org.argeo.init/src/org/argeo/init/osgi/InitActivator.java
org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java
org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java

index d092242f7d6e41736df802c11b0bef411930b398..41102243c476cfdb7855ef8dd7d149e96afe9c30 100644 (file)
@@ -1,28 +1,17 @@
 package org.argeo.init;
 
-import static org.argeo.api.init.InitConstants.SYMBOLIC_NAME_INIT;
-
 import java.lang.System.Logger;
 import java.lang.System.Logger.Level;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
 import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Consumer;
 
 import org.argeo.api.init.InitConstants;
-import org.argeo.api.init.RuntimeContext;
 import org.argeo.api.init.RuntimeManager;
 import org.argeo.init.logging.ThinLoggerFinder;
-import org.argeo.init.osgi.OsgiBoot;
 import org.argeo.init.osgi.OsgiRuntimeContext;
 import org.argeo.internal.init.InternalState;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.launch.Framework;
 
 /**
  * Dynamically configures and launches multiple runtimes, coordinated by a main
@@ -37,23 +26,21 @@ public class RuntimeManagerMain {
 
        private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
 
-       private Path baseConfigArea;
-       private Path baseWritableArea;
        private Map<String, String> configuration = new HashMap<>();
 
        RuntimeManagerMain(Path configArea, Path stateArea) {
                RuntimeManager.loadConfig(configArea, configuration);
-               
+
                // integration with OSGi runtime; this will be read by the init bundle
                configuration.put(ServiceMain.PROP_ARGEO_INIT_MAIN, "true");
                configuration.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea.toUri().toString());
-               
-               configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.resolve(RuntimeManager.STATE).toUri().toString());
+
+               configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA,
+                               stateArea.resolve(RuntimeManager.STATE).toUri().toString());
                // use config area if instance area is not set
                if (!configuration.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
-                       configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA, stateArea.resolve(RuntimeManager.DATA).toUri().toString());
-               this.baseConfigArea = configArea.getParent();
-               this.baseWritableArea = stateArea.getParent();
+                       configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA,
+                                       stateArea.resolve(RuntimeManager.DATA).toUri().toString());
 
                logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
 
@@ -61,7 +48,8 @@ public class RuntimeManagerMain {
        }
 
        public void run() {
-               OsgiRuntimeContext managerRuntimeContext = new OsgiRuntimeContext(configuration);
+               OsgiRuntimeContext managerRuntimeContext = new OsgiRuntimeContext(OsgiRuntimeContext.loadFrameworkFactory(),
+                               configuration);
                try {
                        managerRuntimeContext.run();
                        InternalState.setMainRuntimeContext(managerRuntimeContext);
index ca8ba3a7f71191626f684ff2fd29879242fd0755..4dd30a9da2522d25c37eda9faf42c5883171b061 100644 (file)
@@ -27,6 +27,7 @@ public class ServiceMain {
 
        final static String FILE_SYSTEM_PROPERTIES = "system.properties";
 
+       @Deprecated
        public final static String PROP_ARGEO_INIT_MAIN = "argeo.init.main";
 
 //     private static RuntimeContext runtimeContext = null;
@@ -121,7 +122,8 @@ public class ServiceMain {
                                        config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, dataArea);
                                // config.put(OsgiBoot.PROP_OSGI_USE_SYSTEM_PROPERTIES, "true");
 
-                               OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(config);
+                               OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(
+                                               OsgiRuntimeContext.loadFrameworkFactory(), config);
                                osgiRuntimeContext.run();
                                InternalState.setMainRuntimeContext(osgiRuntimeContext);
                                for (Runnable run : postStart) {
index 310bce70edd00dcba5fd00b2befdb7e8ac3a8532..ae0388f35607636bbca95c350032a7a343203dde 100644 (file)
@@ -2,13 +2,16 @@ package org.argeo.init.osgi;
 
 import java.lang.System.Logger;
 import java.lang.System.Logger.Level;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Objects;
 
 import org.argeo.api.init.RuntimeManager;
-import org.argeo.init.ServiceMain;
 import org.argeo.init.logging.ThinLoggerFinder;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.connect.ConnectFrameworkFactory;
+import org.osgi.framework.launch.Framework;
 
 /**
  * An OSGi configurator. See
@@ -24,16 +27,26 @@ public class InitActivator implements BundleActivator {
 
        private Long checkpoint = null;
 
-       private boolean argeoInit = false;
+       // TODO use framework factory SR
+//     @Deprecated
+//     private boolean argeoInit = false;
        /** Not null if we created it ourselves. */
        private OsgiRuntimeContext runtimeContext;
+       private ServiceRegistration<ConnectFrameworkFactory> frameworkFactorySr = null;
 
        private static OsgiRuntimeManager runtimeManager;
 
        public void start(final BundleContext bundleContext) throws Exception {
+               ConnectFrameworkFactory frameworkFactory = OsgiRuntimeContext.getFrameworkFactory(bundleContext);
+               if (frameworkFactory == null) {
+//                     argeoInit = false;
+                       frameworkFactory = newFrameworkFactory();
+                       frameworkFactorySr = bundleContext.registerService(ConnectFrameworkFactory.class, frameworkFactory, null);
+               }
+
                // The OSGi runtime was created by us, and therefore already initialized
-               argeoInit = Boolean.parseBoolean(bundleContext.getProperty(ServiceMain.PROP_ARGEO_INIT_MAIN));
-               if (!argeoInit) {
+//             argeoInit = Boolean.parseBoolean(bundleContext.getProperty(ServiceMain.PROP_ARGEO_INIT_MAIN));
+               if (!isArgeoInit()) {
                        if (runtimeContext == null) {
                                runtimeContext = new OsgiRuntimeContext(bundleContext);
                                logger.log(Level.DEBUG, () -> "Argeo init via OSGi activator");
@@ -52,15 +65,17 @@ public class InitActivator implements BundleActivator {
                                runtimeContext.update();
                                checkpoint = System.currentTimeMillis();
                        }
-               }
+               } else {
 
-               if (runtimeManager != null)
-                       throw new IllegalArgumentException("Runtime manager is already set");
-               runtimeManager = new OsgiRuntimeManager(bundleContext);
+                       if (runtimeManager != null)
+                               throw new IllegalArgumentException("Runtime manager is already set");
+                       runtimeManager = new OsgiRuntimeManager(bundleContext);
+               }
        }
 
        public void stop(BundleContext context) throws Exception {
-               if (!argeoInit) {
+               if (!isArgeoInit()) {
+                       frameworkFactorySr.unregister();
                        Objects.nonNull(runtimeContext);
                        runtimeContext.stop(context);
                        runtimeContext = null;
@@ -68,8 +83,29 @@ public class InitActivator implements BundleActivator {
                runtimeManager = null;
        }
 
+       /** Whether it wa sinitialised by an Argeo Init main class. */
+       private boolean isArgeoInit() {
+               return frameworkFactorySr == null;
+       }
+
        public static RuntimeManager getRuntimeManager() {
                return runtimeManager;
        }
 
+       /**
+        * Workaround to explicitly instantiate an Equinox
+        * {@link ConnectFrameworkFactory} when running in a pure OSGi runtime.
+        */
+       private ConnectFrameworkFactory newFrameworkFactory() {
+               final String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory";
+               try {
+                       @SuppressWarnings("unchecked")
+                       Class<? extends ConnectFrameworkFactory> frameworkFactoryClass = (Class<? extends ConnectFrameworkFactory>) Framework.class
+                                       .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
+                       return frameworkFactoryClass.getConstructor().newInstance();
+               } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
+                               | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+                       throw new IllegalStateException("Cannot create OSGi framework factory", e);
+               }
+       }
 }
index 2914be38a75eac6af5e787a6fcca3820ff226b19..2e8c1042ca7d20e21d749fecd7eb418833f9f07b 100644 (file)
@@ -17,8 +17,9 @@ import org.argeo.api.init.RuntimeContext;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.connect.ConnectFrameworkFactory;
 import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
 
 /** An OSGi runtime context. */
 public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
@@ -29,6 +30,7 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
 
        // private final static String SYMBOLIC_NAME_FELIX_SCR = "org.apache.felix.scr";
 
+       private ConnectFrameworkFactory frameworkFactory;
        private Map<String, String> config;
        private Framework framework;
 //     private OsgiBoot osgiBoot;
@@ -37,7 +39,8 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
         * Constructor to use when the runtime context will create the OSGi
         * {@link Framework}.
         */
-       public OsgiRuntimeContext(Map<String, String> config) {
+       public OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory, Map<String, String> config) {
+               this.frameworkFactory = frameworkFactory;
                this.config = config;
        }
 
@@ -55,16 +58,18 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
                        throw new IllegalStateException("OSGi framework is already started");
 
                if (framework == null) {
-                       ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
-                       Optional<FrameworkFactory> opt = sl.findFirst();
-                       if (opt.isEmpty())
-                               throw new IllegalStateException("Cannot find OSGi framework");
-                       framework = opt.get().newFramework(config);
+//                     ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
+//                     Optional<FrameworkFactory> opt = sl.findFirst();
+//                     if (opt.isEmpty())
+//                             throw new IllegalStateException("Cannot find OSGi framework");
+//                     framework = opt.get().newFramework(config);
+                       framework = frameworkFactory.newFramework(config, null);
                }
 
                try {
                        framework.start();
                        BundleContext bundleContext = framework.getBundleContext();
+                       bundleContext.registerService(ConnectFrameworkFactory.class, frameworkFactory, null);
                        start(bundleContext);
                } catch (BundleException e) {
                        throw new IllegalStateException("Cannot start OSGi framework", e);
@@ -189,4 +194,23 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
                return framework;
        }
 
+       /**
+        * Load {@link ConnectFrameworkFactory} from Java service loader. This will not
+        * work within a pure OSGi runtime, so the reference should be passed to child
+        * runtimes as an OSGi service.
+        */
+       public static ConnectFrameworkFactory loadFrameworkFactory() {
+               ServiceLoader<ConnectFrameworkFactory> sl = ServiceLoader.load(ConnectFrameworkFactory.class);
+               Optional<ConnectFrameworkFactory> opt = sl.findFirst();
+               if (opt.isEmpty())
+                       throw new IllegalStateException("Cannot find OSGi framework factory");
+               return opt.get();
+       }
+
+       public static ConnectFrameworkFactory getFrameworkFactory(BundleContext bundleContext) {
+               ServiceReference<ConnectFrameworkFactory> sr = bundleContext.getServiceReference(ConnectFrameworkFactory.class);
+               if (sr == null)
+                       return null;
+               return bundleContext.getService(sr);
+       }
 }
index 3fce54df1763289f4ffe8aba63d6b15e54617fd7..b8ee1cd2ca9dc20efddccdd147b613f5ddf99fd1 100644 (file)
@@ -2,7 +2,6 @@ package org.argeo.init.osgi;
 
 import java.lang.System.Logger;
 import java.lang.System.Logger.Level;
-import java.lang.reflect.InvocationTargetException;
 import java.net.URI;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -17,7 +16,6 @@ import org.argeo.api.init.RuntimeContext;
 import org.argeo.api.init.RuntimeManager;
 import org.argeo.internal.init.InternalState;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.connect.ConnectFrameworkFactory;
 import org.osgi.framework.launch.Framework;
@@ -30,8 +28,6 @@ class OsgiRuntimeManager implements RuntimeManager {
 
        private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
 
-       private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory";
-
        private Path baseConfigArea;
        private Path baseWritableArea;
        private Map<String, String> configuration = new HashMap<>();
@@ -41,16 +37,7 @@ class OsgiRuntimeManager implements RuntimeManager {
        private ConnectFrameworkFactory frameworkFactory;
 
        OsgiRuntimeManager(BundleContext bundleContext) {
-               try {
-                       @SuppressWarnings("unchecked")
-                       Class<? extends ConnectFrameworkFactory> frameworkFactoryClass = (Class<? extends ConnectFrameworkFactory>) Framework.class
-                                       .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
-                       frameworkFactory = frameworkFactoryClass.getConstructor().newInstance();
-               } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
-                               | InvocationTargetException | NoSuchMethodException | SecurityException e) {
-                       throw new IllegalStateException("Cannot create OSGi framework factory", e);
-               }
-
+               frameworkFactory = OsgiRuntimeContext.getFrameworkFactory(bundleContext);
                this.baseConfigArea = Paths
                                .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA)))
                                .getParent();
@@ -107,20 +94,20 @@ class OsgiRuntimeManager implements RuntimeManager {
                        config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
 
                // create framework
-               Framework framework = frameworkFactory.newFramework(config, null);
-               try {
-                       framework.start();
-               } catch (BundleException e) {
-                       throw new IllegalStateException("Cannot initialise framework", e);
-               }
-               OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(framework.getBundleContext());
+//             Framework framework = frameworkFactory.newFramework(config, null);
+//             try {
+//                     framework.start();
+//             } catch (BundleException e) {
+//                     throw new IllegalStateException("Cannot initialise framework", e);
+//             }
+               OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(frameworkFactory, config);
                runtimeContexts.put(relPath, runtimeContext);
                return runtimeContext;
        }
 
        public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
                OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
-               //runtimeContext.run();
+               runtimeContext.run();
                Framework framework = runtimeContext.getFramework();
                if (framework != null) {// in case the framework has closed very quickly after run
                        framework.getBundleContext().addFrameworkListener((e) -> {