]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeManager.java
Simplify multi-runtime
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / osgi / OsgiRuntimeManager.java
1 package org.argeo.init.osgi;
2
3 import java.lang.System.Logger;
4 import java.lang.System.Logger.Level;
5 import java.lang.reflect.InvocationTargetException;
6 import java.net.URI;
7 import java.nio.file.Path;
8 import java.nio.file.Paths;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.Map;
12 import java.util.TreeMap;
13 import java.util.function.Consumer;
14
15 import org.argeo.api.init.InitConstants;
16 import org.argeo.api.init.RuntimeContext;
17 import org.argeo.api.init.RuntimeManager;
18 import org.argeo.internal.init.InternalState;
19 import org.osgi.framework.BundleContext;
20 import org.osgi.framework.BundleException;
21 import org.osgi.framework.FrameworkEvent;
22 import org.osgi.framework.connect.ConnectFrameworkFactory;
23 import org.osgi.framework.launch.Framework;
24
25 /**
26 * Dynamically configures and launches multiple runtimes.
27 */
28 class OsgiRuntimeManager implements RuntimeManager {
29 private final static Logger logger = System.getLogger(OsgiRuntimeManager.class.getName());
30
31 private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
32
33 private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory";
34
35 private Path baseConfigArea;
36 private Path baseWritableArea;
37 private Map<String, String> configuration = new HashMap<>();
38
39 private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
40
41 private ConnectFrameworkFactory frameworkFactory;
42
43 OsgiRuntimeManager(BundleContext bundleContext) {
44 try {
45 @SuppressWarnings("unchecked")
46 Class<? extends ConnectFrameworkFactory> frameworkFactoryClass = (Class<? extends ConnectFrameworkFactory>) Framework.class
47 .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
48 frameworkFactory = frameworkFactoryClass.getConstructor().newInstance();
49 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
50 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
51 throw new IllegalStateException("Cannot create OSGi framework factory", e);
52 }
53
54 this.baseConfigArea = Paths
55 .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA)))
56 .getParent();
57 this.baseWritableArea = Paths
58 .get(URI.create(bundleContext.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA))).getParent()
59 .getParent();
60
61 logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
62
63 // System.out.println("java.library.path=" + System.getProperty("java.library.path"));
64 }
65
66 protected void shutdown() {
67 // shutdowm runtimes
68 Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
69 for (String id : new HashSet<>(runtimeContexts.keySet())) {
70 logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
71 closeRuntime(id, true);
72 }
73 for (String id : shutdowning.keySet())
74 try {
75 RuntimeContext runtimeContext = shutdowning.get(id);
76 runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
77 } catch (InterruptedException e) {
78 // silent
79 } catch (Exception e) {
80 logger.log(Logger.Level.DEBUG, "Cannot wait for " + id + " to shutdown", e);
81 }
82 // shutdown manager runtime
83 try {
84 InternalState.getMainRuntimeContext().close();
85 InternalState.getMainRuntimeContext().waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
86 // logger.log(Logger.Level.INFO, "Argeo Init stopped with PID " + ProcessHandle.current().pid());
87 System.out.flush();
88 } catch (Exception e) {
89 e.printStackTrace();
90 Runtime.getRuntime().halt(1);
91 }
92 }
93
94 OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
95 closeRuntime(relPath, false);
96 Path writableArea = baseWritableArea.resolve(relPath);
97 Path configArea = baseConfigArea.resolve(relPath);
98 Map<String, String> config = new HashMap<>();
99 RuntimeManager.loadConfig(configArea, config);
100 config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
101
102 if (configCallback != null)
103 configCallback.accept(config);
104
105 // use config area if instance area is not set
106 if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
107 config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
108
109 // create framework
110 Framework framework = frameworkFactory.newFramework(config, null);
111 try {
112 framework.start();
113 } catch (BundleException e) {
114 throw new IllegalStateException("Cannot initialise framework", e);
115 }
116 OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(framework.getBundleContext());
117 runtimeContexts.put(relPath, runtimeContext);
118 return runtimeContext;
119 }
120
121 public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
122 OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
123 //runtimeContext.run();
124 Framework framework = runtimeContext.getFramework();
125 if (framework != null) {// in case the framework has closed very quickly after run
126 framework.getBundleContext().addFrameworkListener((e) -> {
127 if (e.getType() >= FrameworkEvent.STOPPED) {
128 logger.log(Level.DEBUG, "Externally stopped runtime " + relPath + ". Unregistering...", e);
129 runtimeContexts.remove(relPath);
130 }
131 });
132 } else {
133 closeRuntime(relPath, false);
134 }
135 }
136
137 public void closeRuntime(String relPath, boolean async) {
138 if (!runtimeContexts.containsKey(relPath))
139 return;
140 RuntimeContext runtimeContext = runtimeContexts.get(relPath);
141 try {
142 runtimeContext.close();
143 if (!async) {
144 runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
145 System.gc();
146 }
147 } catch (Exception e) {
148 logger.log(Level.ERROR, "Cannot close runtime context " + relPath, e);
149 } finally {
150 runtimeContexts.remove(relPath);
151 }
152
153 }
154 }