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.osgi.framework.Bundle;
+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.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 {
+ 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 ConnectFrameworkFactory frameworkFactory;
private Map<String, String> config;
private Framework framework;
- private OsgiBoot osgiBoot;
-
- @SuppressWarnings("rawtypes")
- private ServiceRegistration<Consumer> loggingConfigurationSr;
- @SuppressWarnings("rawtypes")
- private ServiceRegistration<Flow.Publisher> logEntryPublisherSr;
+// private OsgiBoot osgiBoot;
/**
* 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;
}
@Override
public void run() {
- 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);
+ if (framework != null && framework.getState() >= Framework.STARTING)
+ 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);
+ 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);
}
}
- public void start(BundleContext bundleContext) {
- // Make sure LoggerFinder has been searched for, since it is lazily loaded
- LoggerFinder.getLoggerFinder();
+ protected void start(BundleContext bundleContext) {
+ // preferences
+// SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences();
+// bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>());
- // 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")));
+ // 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<Map<String, Object>> consumer = (Consumer<Map<String, Object>>) loggerFinder;
+ // ThinLoggerFinder.getConfigurationConsumer()
+ // ThinLoggerFinder.getLogEntryPublisher()
+
+ @SuppressWarnings("unchecked")
+ Supplier<Flow.Publisher<Map<String, Serializable>>> supplier = (Supplier<Flow.Publisher<Map<String, Serializable>>>) 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
+// }
+ }
- osgiBoot = new OsgiBoot(bundleContext);
- osgiBoot.bootstrap(config);
+ public void 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 update() {
- Objects.requireNonNull(osgiBoot);
- osgiBoot.update();
+ protected void stop() {
+ if (framework == null)
+ return;
+ stop(framework.getBundleContext());
+ try {
+ framework.stop();
+ } catch (BundleException e) {
+ throw new IllegalStateException("Cannot stop OSGi framework", e);
+ }
}
- public void stop(BundleContext bundleContext) {
+ protected void stop(BundleContext bundleContext) {
// if (loggingConfigurationSr != null)
// try {
// loggingConfigurationSr.unregister();
@Override
public void waitForStop(long timeout) throws InterruptedException {
if (framework == null)
- throw new IllegalStateException("Framework is not initialised");
+ return;
framework.waitForStop(timeout);
}
public void close() throws Exception {
+ if (framework == null)
+ return;
// TODO make shutdown of dynamic service more robust
- Bundle scrBundle = osgiBoot.getBundlesBySymbolicName().get("org.apache.felix.scr");
- if (scrBundle != null) {
- scrBundle.stop();
- while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
- Thread.sleep(500);
- }
- Thread.sleep(1000);
- }
-
- stop(framework.getBundleContext());
- if (framework != null)
- framework.stop();
+// 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;
}
+ /**
+ * 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);
+ }
}