1 package org
.argeo
.init
;
3 import static org
.argeo
.api
.init
.InitConstants
.SYMBOLIC_NAME_INIT
;
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
;
13 import java
.util
.TreeMap
;
14 import java
.util
.function
.Consumer
;
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
;
29 * Dynamically configures and launches multiple runtimes, coordinated by a main
32 public class RuntimeManagerMain
implements RuntimeManager
{
33 private final static Logger logger
= System
.getLogger(RuntimeManagerMain
.class.getName());
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";
39 private final static long RUNTIME_SHUTDOWN_TIMEOUT
= 60 * 1000;
41 private Path baseConfigArea
;
42 private Path baseWritableArea
;
43 private Map
<String
, String
> configuration
= new HashMap
<>();
45 private Map
<String
, OsgiRuntimeContext
> runtimeContexts
= new TreeMap
<>();
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();
56 logger
.log(Level
.TRACE
, () -> "Runtime manager configuration: " + configuration
);
58 // System.out.println("java.library.path=" + System.getProperty("java.library.path"));
63 // for (Path p : Files.newDirectoryStream(Paths.get("/usr/local/lib/a2"), "*.so")) {
65 // System.load(p.toString());
66 // } catch (UnsatisfiedLinkError e) {
67 // e.printStackTrace();
70 // } catch (IOException e) {
71 // e.printStackTrace();
74 OsgiRuntimeContext managerRuntimeContext
= new OsgiRuntimeContext(configuration
);
76 managerRuntimeContext
.run();
77 InternalState
.setMainRuntimeContext(managerRuntimeContext
);
80 Runtime
.getRuntime().addShutdownHook(new Thread(() -> shutdown(), "Runtime shutdown"));
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
)) {
89 bc
.registerService(RuntimeManager
.class, this, new Hashtable
<>(configuration
));
90 logger
.log(Level
.DEBUG
, "Registered runtime manager");
92 managerRuntimeContext
.waitForStop(0);
93 } catch (InterruptedException
| BundleException e
) {
100 protected void shutdown() {
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);
107 for (String id
: shutdowning
.keySet())
109 RuntimeContext runtimeContext
= shutdowning
.get(id
);
110 runtimeContext
.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT
);
111 } catch (InterruptedException e
) {
113 } catch (Exception e
) {
114 logger
.log(Logger
.Level
.DEBUG
, "Cannot wait for " + id
+ " to shutdown", e
);
116 // shutdown manager runtime
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());
122 } catch (Exception e
) {
124 Runtime
.getRuntime().halt(1);
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());
136 if (configCallback
!= null)
137 configCallback
.accept(config
);
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());
143 OsgiRuntimeContext runtimeContext
= new OsgiRuntimeContext(config
);
144 runtimeContexts
.put(relPath
, runtimeContext
);
145 return runtimeContext
;
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();
153 // for (Bundle b : framework.getBundleContext().getBundles()) {
155 //// if (b.getSymbolicName().startsWith("org.eclipse.swt.gtk")) {
158 //// else if (b.getSymbolicName().startsWith("org.eclipse.jface")) {
161 // } catch (Exception e) {
162 // // TODO Auto-generated catch block
163 // e.printStackTrace();
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
);
175 closeRuntime(relPath
, false);
179 public void closeRuntime(String relPath
, boolean async
) {
180 if (!runtimeContexts
.containsKey(relPath
))
182 RuntimeContext runtimeContext
= runtimeContexts
.get(relPath
);
184 runtimeContext
.close();
186 runtimeContext
.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT
);
189 } catch (Exception e
) {
190 logger
.log(Level
.ERROR
, "Cannot close runtime context " + relPath
, e
);
192 runtimeContexts
.remove(relPath
);
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));
205 throw new IllegalArgumentException("A relative configuration directory must be specified");
206 Path configArea
= Paths
.get(System
.getProperty("user.dir"), args
[0]);
208 // System.out.println("## Start with PID " + ProcessHandle.current().pid());
209 // System.out.println("user.dir=" + System.getProperty("user.dir"));
211 Path stateArea
= Paths
.get(env
.get(ENV_STATE_DIRECTORY
));
213 RuntimeManagerMain runtimeManager
= new RuntimeManagerMain(configArea
, stateArea
);
214 runtimeManager
.run();