From 14d78f16b17e152d0f34021fc4771606b12f1604 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 4 Mar 2024 19:23:15 +0100 Subject: [PATCH] Introduce runtime manager --- .../src/org/argeo/init/RuntimeManager.java | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 org.argeo.init/src/org/argeo/init/RuntimeManager.java diff --git a/org.argeo.init/src/org/argeo/init/RuntimeManager.java b/org.argeo.init/src/org/argeo/init/RuntimeManager.java new file mode 100644 index 000000000..a5f48d446 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/RuntimeManager.java @@ -0,0 +1,150 @@ +package org.argeo.init; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; +import java.util.function.Consumer; + +import org.argeo.init.logging.ThinLoggerFinder; +import org.argeo.init.osgi.OsgiBoot; +import org.argeo.init.osgi.OsgiRuntimeContext; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +public class RuntimeManager { + private final static Logger logger = System.getLogger(RuntimeManager.class.getName()); + + private final static String ENV_STATE_DIRECTORY = "STATE_DIRECTORY"; +// private final static String ENV_CONFIGURATION_DIRECTORY = "CONFIGURATION_DIRECTORY"; +// private final static String ENV_CACHE_DIRECTORY = "CACHE_DIRECTORY"; + + private final static String JVM_ARGS = "jvm.args"; + + private Path baseConfigArea; + private Path baseStateArea; + private Map configuration = new HashMap<>(); + + private Map runtimeContexts = new TreeMap<>(); + + RuntimeManager(Path configArea, Path stateArea) { + loadConfig(configArea, configuration); + configuration.put(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA, stateArea.toUri().toString()); + this.baseConfigArea = configArea.getParent(); + this.baseStateArea = stateArea.getParent(); + + logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration); + + } + + public void run() { + logger.log(Level.DEBUG, "Start OSGi"); + try (OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(configuration)) { + runtimeContext.run(); + + BundleContext bc = runtimeContext.getFramework().getBundleContext(); + // uninstall init as a bundle since it will be available via OSGi system + for (Bundle b : bc.getBundles()) { + if (b.getSymbolicName().equals("org.argeo.init")) { + b.uninstall(); + } + } + bc.registerService(RuntimeManager.class, this, new Hashtable<>(configuration)); + logger.log(Level.DEBUG, "Registered runtime manager"); + + runtimeContext.waitForStop(0); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + + } + + public static void loadConfig(Path dir, Map config) { + try { + System.out.println("Load from "+dir); + Path jvmArgsPath = dir.resolve(JVM_ARGS); + if (!Files.exists(jvmArgsPath)) { + // load from parent directory + loadConfig(dir.getParent(), config); + } + + if (Files.exists(dir)) + for (Path p : Files.newDirectoryStream(dir, "*.ini")) { + Properties props = new Properties(); + try (InputStream in = Files.newInputStream(p)) { + props.load(in); + } + for (Object key : props.keySet()) { + config.put(key.toString(), props.getProperty(key.toString())); + } + } + } catch (IOException e) { + throw new UncheckedIOException("Cannot load configuration from " + dir, e); + } + } + + OsgiRuntimeContext loadRuntime(String relPath, Consumer> configCallback) { + stopRuntime(relPath); + Path stateArea = baseStateArea.resolve(relPath); + Path configArea = baseConfigArea.resolve(relPath); + Map config = new HashMap<>(); + loadConfig(configArea, config); + config.put(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA, stateArea.toUri().toString()); + + if (configCallback != null) + configCallback.accept(config); + OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(config); + runtimeContexts.put(relPath, runtimeContext); + return runtimeContext; + } + + public void startRuntime(String relPath, Consumer> configCallback) { + OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback); + runtimeContext.run(); + } + + public void stopRuntime(String relPath) { + if (!runtimeContexts.containsKey(relPath)) + return; + RuntimeContext runtimeContext = runtimeContexts.get(relPath); + try { + runtimeContext.close(); + runtimeContext.waitForStop(60 * 1000); + } 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(); + Map env = System.getenv(); +// for (String envName : new TreeSet<>(env.keySet())) { +// System.out.format("%s=%s%n", envName, env.get(envName)); +// } + if (args.length < 1) + throw new IllegalArgumentException("A relative configuration directory must be specified"); + Path configArea = Paths.get(System.getProperty("user.dir"), args[0]); + + // System.out.println("## Start with PID " + ProcessHandle.current().pid()); + // System.out.println("user.dir=" + System.getProperty("user.dir")); + + Path stateArea = Paths.get(env.get(ENV_STATE_DIRECTORY)); + + RuntimeManager runtimeManager = new RuntimeManager(configArea, stateArea); + runtimeManager.run(); + } + +} -- 2.30.2