X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.init%2Fsrc%2Forg%2Fargeo%2Finit%2Fosgi%2FOsgiRuntimeContext.java;h=2914be38a75eac6af5e787a6fcca3820ff226b19;hb=9a3cf6732b7b42d19e113bce32c799b283b8f79b;hp=7f44f6b2746dccc914312180b493d56e7328ce3c;hpb=1010bff4f1ffadbf3f7988fbd3eba70f5d672c88;p=lgpl%2Fargeo-commons.git 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 7f44f6b27..2914be38a 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java @@ -1,49 +1,67 @@ package org.argeo.init.osgi; +import java.io.Serializable; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.lang.System.LoggerFinder; import java.util.Collections; import java.util.Hashtable; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import java.util.concurrent.Flow; import java.util.function.Consumer; +import java.util.function.Supplier; -import org.argeo.init.RuntimeContext; -import org.argeo.init.logging.ThinLoggerFinder; +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.ServiceRegistration; import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; /** An OSGi runtime context. */ -public class OsgiRuntimeContext implements RuntimeContext { +public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable { + private final static Logger logger = System.getLogger(OsgiRuntimeContext.class.getName()); + + private final static long STOP_FOR_UPDATE_TIMEOUT = 60 * 1000; + private final static long CLOSE_TIMEOUT = 60 * 1000; + + // private final static String SYMBOLIC_NAME_FELIX_SCR = "org.apache.felix.scr"; + private Map config; private Framework framework; - private OsgiBoot osgiBoot; - - @SuppressWarnings("rawtypes") - private ServiceRegistration loggingConfigurationSr; - @SuppressWarnings("rawtypes") - private ServiceRegistration logEntryPublisherSr; +// private OsgiBoot osgiBoot; + /** + * Constructor to use when the runtime context will create the OSGi + * {@link Framework}. + */ public OsgiRuntimeContext(Map config) { this.config = config; } - public OsgiRuntimeContext(BundleContext bundleContext) { + /** + * Constructor to use when the OSGi {@link Framework} has been created by other + * means. + */ + OsgiRuntimeContext(BundleContext bundleContext) { start(bundleContext); } @Override public void run() { - 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); + if (framework != null && framework.getState() >= Framework.STARTING) + 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); + } + try { framework.start(); BundleContext bundleContext = framework.getBundleContext(); @@ -53,45 +71,122 @@ public class OsgiRuntimeContext implements RuntimeContext { } } - public void start(BundleContext bundleContext) { - // logging - loggingConfigurationSr = bundleContext.registerService(Consumer.class, - ThinLoggerFinder.getConfigurationConsumer(), - new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration"))); - logEntryPublisherSr = bundleContext.registerService(Flow.Publisher.class, - ThinLoggerFinder.getLogEntryPublisher(), - new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher"))); - - osgiBoot = new OsgiBoot(bundleContext); - osgiBoot.bootstrap(); - + protected void start(BundleContext bundleContext) { + // preferences +// SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences(); +// bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>()); + + // Make sure LoggerFinder has been searched for, since it is lazily loaded + LoggerFinder loggerFinder = LoggerFinder.getLoggerFinder(); + + if (loggerFinder instanceof Consumer && loggerFinder instanceof Supplier) { + @SuppressWarnings("unchecked") + Consumer> consumer = (Consumer>) loggerFinder; + // ThinLoggerFinder.getConfigurationConsumer() + // ThinLoggerFinder.getLogEntryPublisher() + + @SuppressWarnings("unchecked") + Supplier>> supplier = (Supplier>>) loggerFinder; + // logging + bundleContext.registerService(Consumer.class, consumer, + new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration"))); + bundleContext.registerService(Flow.Publisher.class, supplier.get(), + new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher"))); + } + OsgiBoot osgiBoot = new OsgiBoot(bundleContext); + String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID); + + // separate thread in order to improve logging + Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) { + @Override + public void run() { + osgiBoot.bootstrap(); + } + }; + osgiBootThread.start(); + // TODO return a completable stage so that inits can run in parallel +// try { +// osgiBootThread.join(60 * 1000); +// } catch (InterruptedException e) { +// // silent +// } } public void update() { - Objects.requireNonNull(osgiBoot); - osgiBoot.update(); + stop(); + try { + waitForStop(STOP_FOR_UPDATE_TIMEOUT); + } catch (InterruptedException e) { + logger.log(Level.TRACE, "Wait for stop interrupted", e); + } + run(); + + // TODO Optimise with OSGi mechanisms (e.g. framework.update()) +// if (osgiBoot != null) { +// Objects.requireNonNull(osgiBoot); +// osgiBoot.update(); +// } } - public void stop(BundleContext bundleContext) { - if (loggingConfigurationSr != null) - loggingConfigurationSr.unregister(); - if (logEntryPublisherSr != null) - logEntryPublisherSr.unregister(); + protected void stop() { + if (framework == null) + return; + stop(framework.getBundleContext()); + try { + framework.stop(); + } catch (BundleException e) { + throw new IllegalStateException("Cannot stop OSGi framework", e); + } + } + protected void stop(BundleContext bundleContext) { +// if (loggingConfigurationSr != null) +// try { +// loggingConfigurationSr.unregister(); +// } catch (Exception e) { +// // silent +// } +// if (logEntryPublisherSr != null) +// try { +// logEntryPublisherSr.unregister(); +// } catch (Exception e) { +// // silent +// } } @Override public void waitForStop(long timeout) throws InterruptedException { if (framework == null) - throw new IllegalStateException("Framework is not initialised"); - stop(framework.getBundleContext()); + return; + framework.waitForStop(timeout); } - @Override public void close() throws Exception { - if (framework != null) - framework.stop(); + if (framework == null) + return; + // TODO make shutdown of dynamic service more robust +// for (Bundle scrBundle : framework.getBundleContext().getBundles()) { +// if (scrBundle.getSymbolicName().equals(SYMBOLIC_NAME_FELIX_SCR)) { +// if (scrBundle.getState() > Bundle.RESOLVED) { +// scrBundle.stop(); +// while (!(scrBundle.getState() <= Bundle.RESOLVED)) { +// Thread.sleep(100); +// } +// Thread.sleep(500); +// } +// } +// } + + stop(); + waitForStop(CLOSE_TIMEOUT); + framework = null; +// osgiBoot = null; + config.clear(); + } + + public Framework getFramework() { + return framework; } }