From faeeb8f0ba92b640942d88782ca6f5cfc332597e Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 6 May 2024 08:45:09 +0200 Subject: [PATCH] Improve init launch --- .../org/argeo/init/RuntimeManagerMain.java | 28 +++------- .../src/org/argeo/init/ServiceMain.java | 4 +- .../org/argeo/init/osgi/InitActivator.java | 54 +++++++++++++++---- .../argeo/init/osgi/OsgiRuntimeContext.java | 38 ++++++++++--- .../argeo/init/osgi/OsgiRuntimeManager.java | 31 ++++------- 5 files changed, 96 insertions(+), 59 deletions(-) diff --git a/org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java b/org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java index d092242f7..41102243c 100644 --- a/org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java +++ b/org.argeo.init/src/org/argeo/init/RuntimeManagerMain.java @@ -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 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); diff --git a/org.argeo.init/src/org/argeo/init/ServiceMain.java b/org.argeo.init/src/org/argeo/init/ServiceMain.java index ca8ba3a7f..4dd30a9da 100644 --- a/org.argeo.init/src/org/argeo/init/ServiceMain.java +++ b/org.argeo.init/src/org/argeo/init/ServiceMain.java @@ -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) { diff --git a/org.argeo.init/src/org/argeo/init/osgi/InitActivator.java b/org.argeo.init/src/org/argeo/init/osgi/InitActivator.java index 310bce70e..ae0388f35 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/InitActivator.java +++ b/org.argeo.init/src/org/argeo/init/osgi/InitActivator.java @@ -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 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 frameworkFactoryClass = (Class) 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); + } + } } diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java index 2914be38a..2e8c1042c 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java @@ -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 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 config) { + public OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory, Map 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 sl = ServiceLoader.load(FrameworkFactory.class); - Optional opt = sl.findFirst(); - if (opt.isEmpty()) - throw new IllegalStateException("Cannot find OSGi framework"); - framework = opt.get().newFramework(config); +// ServiceLoader sl = ServiceLoader.load(FrameworkFactory.class); +// Optional 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 sl = ServiceLoader.load(ConnectFrameworkFactory.class); + Optional opt = sl.findFirst(); + if (opt.isEmpty()) + throw new IllegalStateException("Cannot find OSGi framework factory"); + return opt.get(); + } + + public static ConnectFrameworkFactory getFrameworkFactory(BundleContext bundleContext) { + ServiceReference sr = bundleContext.getServiceReference(ConnectFrameworkFactory.class); + if (sr == null) + return null; + return bundleContext.getService(sr); + } } diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java index 3fce54df1..b8ee1cd2c 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java @@ -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 configuration = new HashMap<>(); @@ -41,16 +37,7 @@ class OsgiRuntimeManager implements RuntimeManager { private ConnectFrameworkFactory frameworkFactory; OsgiRuntimeManager(BundleContext bundleContext) { - try { - @SuppressWarnings("unchecked") - Class frameworkFactoryClass = (Class) 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> 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) -> { -- 2.39.2