]> git.argeo.org Git - lgpl/argeo-commons.git/blob - argeo/init/RuntimeManagerMain.java
Prepare next development cycle
[lgpl/argeo-commons.git] / argeo / init / RuntimeManagerMain.java
1 package org.argeo.init;
2
3 import static org.argeo.api.init.InitConstants.SYMBOLIC_NAME_INIT;
4
5 import java.lang.System.Logger;
6 import java.lang.System.Logger.Level;
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.Hashtable;
12 import java.util.Map;
13 import java.util.TreeMap;
14 import java.util.function.Consumer;
15
16 import org.argeo.api.init.InitConstants;
17 import org.argeo.api.init.RuntimeContext;
18 import org.argeo.api.init.RuntimeManager;
19 import org.argeo.init.logging.ThinLoggerFinder;
20 import org.argeo.init.osgi.OsgiRuntimeContext;
21 import org.argeo.internal.init.InternalState;
22 import org.osgi.framework.Bundle;
23 import org.osgi.framework.BundleContext;
24 import org.osgi.framework.BundleException;
25 import org.osgi.framework.FrameworkEvent;
26 import org.osgi.framework.launch.Framework;
27
28 /**
29 * Dynamically configures and launches multiple runtimes, coordinated by a main
30 * one.
31 */
32 public class RuntimeManagerMain implements RuntimeManager {
33 private final static Logger logger = System.getLogger(RuntimeManagerMain.class.getName());
34
35 private final static String ENV_STATE_DIRECTORY = "STATE_DIRECTORY";
36 // private final static String ENV_CONFIGURATION_DIRECTORY = "CONFIGURATION_DIRECTORY";
37 // private final static String ENV_CACHE_DIRECTORY = "CACHE_DIRECTORY";
38
39 private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
40
41 private Path baseConfigArea;
42 private Path baseWritableArea;
43 private Map<String, String> configuration = new HashMap<>();
44
45 private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
46
47 RuntimeManagerMain(Path configArea, Path stateArea) {
48 RuntimeManager.loadConfig(configArea, configuration);
49 configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.resolve(STATE).toUri().toString());
50 // use config area if instance area is not set
51 if (!configuration.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
52 configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA, stateArea.resolve(DATA).toUri().toString());
53 this.baseConfigArea = configArea.getParent();
54 this.baseWritableArea = stateArea.getParent();
55
56 logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
57
58 // System.out.println("java.library.path=" + System.getProperty("java.library.path"));
59 }
60
61 public void run() {
62 // try {
63 // for (Path p : Files.newDirectoryStream(Paths.get("/usr/local/lib/a2"), "*.so")) {
64 // try {
65 // System.load(p.toString());
66 // } catch (UnsatisfiedLinkError e) {
67 // e.printStackTrace();
68 // }
69 // }
70 // } catch (IOException e) {
71 // e.printStackTrace();
72 // }
73
74 OsgiRuntimeContext managerRuntimeContext = new OsgiRuntimeContext(configuration);
75 try {
76 managerRuntimeContext.run();
77 InternalState.setMainRuntimeContext(managerRuntimeContext);
78
79 // shutdown on exit
80 Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdown(), "Runtime shutdown"));
81
82 BundleContext bc = managerRuntimeContext.getFramework().getBundleContext();
83 // uninstall init as a bundle since it will be available via OSGi system
84 for (Bundle b : bc.getBundles()) {
85 if (b.getSymbolicName().equals(SYMBOLIC_NAME_INIT)) {
86 b.uninstall();
87 }
88 }
89 bc.registerService(RuntimeManager.class, this, new Hashtable<>(configuration));
90 logger.log(Level.DEBUG, "Registered runtime manager");
91
92 managerRuntimeContext.waitForStop(0);
93 } catch (InterruptedException | BundleException e) {
94 e.printStackTrace();
95 System.exit(1);
96 }
97
98 }
99
100 protected void shutdown() {
101 // shutdowm runtimes
102 Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
103 for (String id : new HashSet<>(runtimeContexts.keySet())) {
104 logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
105 closeRuntime(id, true);
106 }
107 for (String id : shutdowning.keySet())
108 try {
109 RuntimeContext runtimeContext = shutdowning.get(id);
110 runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
111 } catch (InterruptedException e) {
112 // silent
113 } catch (Exception e) {
114 logger.log(Logger.Level.DEBUG, "Cannot wait for " + id + " to shutdown", e);
115 }
116 // shutdown manager runtime
117 try {
118 InternalState.getMainRuntimeContext().close();
119 InternalState.getMainRuntimeContext().waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
120 // logger.log(Logger.Level.INFO, "Argeo Init stopped with PID " + ProcessHandle.current().pid());
121 System.out.flush();
122 } catch (Exception e) {
123 e.printStackTrace();
124 Runtime.getRuntime().halt(1);
125 }
126 }
127
128 OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
129 closeRuntime(relPath, false);
130 Path writableArea = baseWritableArea.resolve(relPath);
131 Path configArea = baseConfigArea.resolve(relPath);
132 Map<String, String> config = new HashMap<>();
133 RuntimeManager.loadConfig(configArea, config);
134 config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
135
136 if (configCallback != null)
137 configCallback.accept(config);
138
139 // use config area if instance area is not set
140 if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
141 config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
142
143 OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(config);
144 runtimeContexts.put(relPath, runtimeContext);
145 return runtimeContext;
146 }
147
148 public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
149 OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
150 runtimeContext.run();
151 Framework framework = runtimeContext.getFramework();
152
153 // for (Bundle b : framework.getBundleContext().getBundles()) {
154 // try {
155 //// if (b.getSymbolicName().startsWith("org.eclipse.swt.gtk")) {
156 //// b.uninstall();
157 //// }
158 //// else if (b.getSymbolicName().startsWith("org.eclipse.jface")) {
159 //// b.uninstall();
160 //// }
161 // } catch (Exception e) {
162 // // TODO Auto-generated catch block
163 // e.printStackTrace();
164 // }
165 // }
166
167 if (framework != null) {// in case the framework has closed very quickly after run
168 framework.getBundleContext().addFrameworkListener((e) -> {
169 if (e.getType() >= FrameworkEvent.STOPPED) {
170 logger.log(Level.DEBUG, "Externally stopped runtime " + relPath + ". Unregistering...", e);
171 runtimeContexts.remove(relPath);
172 }
173 });
174 } else {
175 closeRuntime(relPath, false);
176 }
177 }
178
179 public void closeRuntime(String relPath, boolean async) {
180 if (!runtimeContexts.containsKey(relPath))
181 return;
182 RuntimeContext runtimeContext = runtimeContexts.get(relPath);
183 try {
184 runtimeContext.close();
185 if (!async) {
186 runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
187 System.gc();
188 }
189 } catch (Exception e) {
190 logger.log(Level.ERROR, "Cannot close runtime context " + relPath, e);
191 } finally {
192 runtimeContexts.remove(relPath);
193 }
194
195 }
196
197 public static void main(String[] args) {
198 ThinLoggerFinder.reloadConfiguration();
199 logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + ProcessHandle.current().pid());
200 Map<String, String> env = System.getenv();
201 // for (String envName : new TreeSet<>(env.keySet())) {
202 // System.out.format("%s=%s%n", envName, env.get(envName));
203 // }
204 if (args.length < 1)
205 throw new IllegalArgumentException("A relative configuration directory must be specified");
206 Path configArea = Paths.get(System.getProperty("user.dir"), args[0]);
207
208 // System.out.println("## Start with PID " + ProcessHandle.current().pid());
209 // System.out.println("user.dir=" + System.getProperty("user.dir"));
210
211 Path stateArea = Paths.get(env.get(ENV_STATE_DIRECTORY));
212
213 RuntimeManagerMain runtimeManager = new RuntimeManagerMain(configArea, stateArea);
214 runtimeManager.run();
215 }
216
217 }