Main-Class: org.argeo.init.Service
Class-Path: org.eclipse.osgi.jar
-Bundle-Activator: org.argeo.init.osgi.Activator
+Bundle-Activator: org.argeo.init.osgi.InitActivator
Import-Package: \
org.osgi.*;version=0.0.0,\
import java.io.IOException;
import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
public void closeRuntime(String relPath, boolean async);
+ default void startRuntime(String relPath, String props) {
+ Properties properties = new Properties();
+ try (Reader reader = new StringReader(props)) {
+ properties.load(reader);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Cannot load properties", e);
+ }
+ startRuntime(relPath, (config) -> {
+ for (Object key : properties.keySet()) {
+ config.put(key.toString(), properties.getProperty(key.toString()));
+ }
+ });
+ }
+
/**
* Load configs recursively starting with the parent directories, until a
* jvm.args file is found.
* Dynamically configures and launches multiple runtimes, coordinated by a main
* one.
*/
-public class RuntimeManagerMain implements RuntimeManager {
+public class RuntimeManagerMain {
private final static Logger logger = System.getLogger(RuntimeManagerMain.class.getName());
private final static String ENV_STATE_DIRECTORY = "STATE_DIRECTORY";
private Path baseWritableArea;
private Map<String, String> configuration = new HashMap<>();
- private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
-
RuntimeManagerMain(Path configArea, Path stateArea) {
RuntimeManager.loadConfig(configArea, configuration);
- configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.resolve(STATE).toUri().toString());
+
+ // 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());
// 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(DATA).toUri().toString());
+ configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA, stateArea.resolve(RuntimeManager.DATA).toUri().toString());
this.baseConfigArea = configArea.getParent();
this.baseWritableArea = stateArea.getParent();
// shutdown on exit
Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdown(), "Runtime shutdown"));
- BundleContext bc = managerRuntimeContext.getFramework().getBundleContext();
- // uninstall init as a bundle since it will be available via OSGi system
- OsgiBoot.uninstallBundles(bc, SYMBOLIC_NAME_INIT);
- bc.registerService(RuntimeManager.class, this, new Hashtable<>(configuration));
+// BundleContext bc = managerRuntimeContext.getFramework().getBundleContext();
+// // uninstall init as a bundle since it will be available via OSGi system
+// OsgiBoot.uninstallBundles(bc, SYMBOLIC_NAME_INIT);
+// bc.registerService(RuntimeManager.class, this, new Hashtable<>(configuration));
logger.log(Level.DEBUG, "Registered runtime manager");
managerRuntimeContext.waitForStop(0);
}
protected void shutdown() {
- // shutdowm runtimes
- Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
- for (String id : new HashSet<>(runtimeContexts.keySet())) {
- logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
- closeRuntime(id, true);
- }
- for (String id : shutdowning.keySet())
- try {
- RuntimeContext runtimeContext = shutdowning.get(id);
- runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
- } catch (InterruptedException e) {
- // silent
- } catch (Exception e) {
- logger.log(Logger.Level.DEBUG, "Cannot wait for " + id + " to shutdown", e);
- }
// shutdown manager runtime
try {
InternalState.getMainRuntimeContext().close();
}
}
- OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
- closeRuntime(relPath, false);
- Path writableArea = baseWritableArea.resolve(relPath);
- Path configArea = baseConfigArea.resolve(relPath);
- Map<String, String> config = new HashMap<>();
- RuntimeManager.loadConfig(configArea, config);
- config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
-
- if (configCallback != null)
- configCallback.accept(config);
-
- // use config area if instance area is not set
- if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
- config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
-
- OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(config);
- runtimeContexts.put(relPath, runtimeContext);
- return runtimeContext;
- }
-
- public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
- OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
- runtimeContext.run();
- Framework framework = runtimeContext.getFramework();
- if (framework != null) {// in case the framework has closed very quickly after run
- framework.getBundleContext().addFrameworkListener((e) -> {
- if (e.getType() >= FrameworkEvent.STOPPED) {
- logger.log(Level.DEBUG, "Externally stopped runtime " + relPath + ". Unregistering...", e);
- runtimeContexts.remove(relPath);
- }
- });
- } else {
- closeRuntime(relPath, false);
- }
- }
-
- public void closeRuntime(String relPath, boolean async) {
- if (!runtimeContexts.containsKey(relPath))
- return;
- RuntimeContext runtimeContext = runtimeContexts.get(relPath);
- try {
- runtimeContext.close();
- if (!async) {
- runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
- System.gc();
- }
- } catch (Exception e) {
- logger.log(Level.ERROR, "Cannot close runtime context " + relPath, e);
- } finally {
- runtimeContexts.remove(relPath);
- }
-
- }
-
public static void main(String[] args) {
ThinLoggerFinder.reloadConfiguration();
logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + ProcessHandle.current().pid());
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.util.Objects;
-
-import org.argeo.init.ServiceMain;
-import org.argeo.init.logging.ThinLoggerFinder;
-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 {
- static {
- // must be called first
- ThinLoggerFinder.lazyInit();
- }
- private final static Logger logger = System.getLogger(Activator.class.getName());
-
- private Long checkpoint = null;
-
- private boolean argeoInit = false;
- /** Not null if we created it ourselves. */
- private OsgiRuntimeContext runtimeContext;
-
- public void start(final BundleContext bundleContext) throws Exception {
- // The OSGi runtime was created by us, and therefore already initialized
- argeoInit = Boolean.parseBoolean(bundleContext.getProperty(ServiceMain.PROP_ARGEO_INIT_MAIN));
- if (!argeoInit) {
- if (runtimeContext == null) {
- runtimeContext = new OsgiRuntimeContext(bundleContext);
- logger.log(Level.DEBUG, () -> "Argeo init via OSGi activator");
- }
-
- // admin thread
-// Thread adminThread = new AdminThread(bundleContext);
-// adminThread.start();
-
- // bootstrap
-// OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
- if (checkpoint == null) {
-// osgiBoot.bootstrap();
- checkpoint = System.currentTimeMillis();
- } else {
- runtimeContext.update();
- checkpoint = System.currentTimeMillis();
- }
- }
- }
-
- public void stop(BundleContext context) throws Exception {
- if (!argeoInit) {
- Objects.nonNull(runtimeContext);
- runtimeContext.stop(context);
- runtimeContext = null;
- }
- }
-
-}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+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;
+
+/**
+ * An OSGi configurator. See
+ * <a href="http://wiki.eclipse.org/Configurator">http:
+ * //wiki.eclipse.org/Configurator</a>
+ */
+public class InitActivator implements BundleActivator {
+ static {
+ // must be called first
+ ThinLoggerFinder.lazyInit();
+ }
+ private final static Logger logger = System.getLogger(InitActivator.class.getName());
+
+ private Long checkpoint = null;
+
+ private boolean argeoInit = false;
+ /** Not null if we created it ourselves. */
+ private OsgiRuntimeContext runtimeContext;
+
+ private static OsgiRuntimeManager runtimeManager;
+
+ public void start(final BundleContext bundleContext) throws Exception {
+ // The OSGi runtime was created by us, and therefore already initialized
+ argeoInit = Boolean.parseBoolean(bundleContext.getProperty(ServiceMain.PROP_ARGEO_INIT_MAIN));
+ if (!argeoInit) {
+ if (runtimeContext == null) {
+ runtimeContext = new OsgiRuntimeContext(bundleContext);
+ logger.log(Level.DEBUG, () -> "Argeo init via OSGi activator");
+ }
+
+ // admin thread
+// Thread adminThread = new AdminThread(bundleContext);
+// adminThread.start();
+
+ // bootstrap
+// OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+ if (checkpoint == null) {
+// osgiBoot.bootstrap();
+ checkpoint = System.currentTimeMillis();
+ } else {
+ runtimeContext.update();
+ checkpoint = System.currentTimeMillis();
+ }
+ }
+
+ if (runtimeManager != null)
+ throw new IllegalArgumentException("Runtime manager is already set");
+ runtimeManager = new OsgiRuntimeManager(bundleContext);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ if (!argeoInit) {
+ Objects.nonNull(runtimeContext);
+ runtimeContext.stop(context);
+ runtimeContext = null;
+ }
+ runtimeManager = null;
+ }
+
+ public static RuntimeManager getRuntimeManager() {
+ return runtimeManager;
+ }
+
+}
--- /dev/null
+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;
+import java.util.HashMap;
+import java.util.HashSet;
+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.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;
+
+/**
+ * Dynamically configures and launches multiple runtimes.
+ */
+class OsgiRuntimeManager implements RuntimeManager {
+ private final static Logger logger = System.getLogger(OsgiRuntimeManager.class.getName());
+
+ 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<>();
+
+ private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
+
+ 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);
+ }
+
+ this.baseConfigArea = Paths
+ .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA)))
+ .getParent();
+ this.baseWritableArea = Paths
+ .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA))).getParent()
+ .getParent();
+
+ logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
+
+// System.out.println("java.library.path=" + System.getProperty("java.library.path"));
+ }
+
+ protected void shutdown() {
+ // shutdowm runtimes
+ Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
+ for (String id : new HashSet<>(runtimeContexts.keySet())) {
+ logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
+ closeRuntime(id, true);
+ }
+ for (String id : shutdowning.keySet())
+ try {
+ RuntimeContext runtimeContext = shutdowning.get(id);
+ runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+ } catch (InterruptedException e) {
+ // silent
+ } catch (Exception e) {
+ logger.log(Logger.Level.DEBUG, "Cannot wait for " + id + " to shutdown", e);
+ }
+ // shutdown manager runtime
+ try {
+ InternalState.getMainRuntimeContext().close();
+ InternalState.getMainRuntimeContext().waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+// logger.log(Logger.Level.INFO, "Argeo Init stopped with PID " + ProcessHandle.current().pid());
+ System.out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ Runtime.getRuntime().halt(1);
+ }
+ }
+
+ OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
+ closeRuntime(relPath, false);
+ Path writableArea = baseWritableArea.resolve(relPath);
+ Path configArea = baseConfigArea.resolve(relPath);
+ Map<String, String> config = new HashMap<>();
+ RuntimeManager.loadConfig(configArea, config);
+ config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
+
+ if (configCallback != null)
+ configCallback.accept(config);
+
+ // use config area if instance area is not set
+ if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
+ 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());
+ runtimeContexts.put(relPath, runtimeContext);
+ return runtimeContext;
+ }
+
+ public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
+ OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
+ //runtimeContext.run();
+ Framework framework = runtimeContext.getFramework();
+ if (framework != null) {// in case the framework has closed very quickly after run
+ framework.getBundleContext().addFrameworkListener((e) -> {
+ if (e.getType() >= FrameworkEvent.STOPPED) {
+ logger.log(Level.DEBUG, "Externally stopped runtime " + relPath + ". Unregistering...", e);
+ runtimeContexts.remove(relPath);
+ }
+ });
+ } else {
+ closeRuntime(relPath, false);
+ }
+ }
+
+ public void closeRuntime(String relPath, boolean async) {
+ if (!runtimeContexts.containsKey(relPath))
+ return;
+ RuntimeContext runtimeContext = runtimeContexts.get(relPath);
+ try {
+ runtimeContext.close();
+ if (!async) {
+ runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+ System.gc();
+ }
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot close runtime context " + relPath, e);
+ } finally {
+ runtimeContexts.remove(relPath);
+ }
+
+ }
+}
.getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
frameworkFactory = frameworkFactoryClass.getConstructor().newInstance();
- boolean test = true;
+ boolean test = false;
if (test)
new Thread() {