1 package org
.argeo
.init
.osgi
;
3 import java
.lang
.System
.Logger
;
4 import java
.lang
.System
.Logger
.Level
;
5 import java
.lang
.reflect
.InvocationTargetException
;
7 import java
.nio
.file
.Path
;
8 import java
.nio
.file
.Paths
;
9 import java
.util
.HashMap
;
10 import java
.util
.HashSet
;
12 import java
.util
.TreeMap
;
13 import java
.util
.function
.Consumer
;
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
;
26 * Dynamically configures and launches multiple runtimes.
28 class OsgiRuntimeManager
implements RuntimeManager
{
29 private final static Logger logger
= System
.getLogger(OsgiRuntimeManager
.class.getName());
31 private final static long RUNTIME_SHUTDOWN_TIMEOUT
= 60 * 1000;
33 private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS
= "org.eclipse.osgi.launch.EquinoxFactory";
35 private Path baseConfigArea
;
36 private Path baseWritableArea
;
37 private Map
<String
, String
> configuration
= new HashMap
<>();
39 private Map
<String
, OsgiRuntimeContext
> runtimeContexts
= new TreeMap
<>();
41 private ConnectFrameworkFactory frameworkFactory
;
43 OsgiRuntimeManager(BundleContext bundleContext
) {
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
);
54 this.baseConfigArea
= Paths
55 .get(URI
.create(bundleContext
.getProperty(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA
)))
57 this.baseWritableArea
= Paths
58 .get(URI
.create(bundleContext
.getProperty(InitConstants
.PROP_OSGI_CONFIGURATION_AREA
))).getParent()
61 logger
.log(Level
.TRACE
, () -> "Runtime manager configuration: " + configuration
);
63 // System.out.println("java.library.path=" + System.getProperty("java.library.path"));
66 protected void shutdown() {
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);
73 for (String id
: shutdowning
.keySet())
75 RuntimeContext runtimeContext
= shutdowning
.get(id
);
76 runtimeContext
.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT
);
77 } catch (InterruptedException e
) {
79 } catch (Exception e
) {
80 logger
.log(Logger
.Level
.DEBUG
, "Cannot wait for " + id
+ " to shutdown", e
);
82 // shutdown manager runtime
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());
88 } catch (Exception e
) {
90 Runtime
.getRuntime().halt(1);
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());
102 if (configCallback
!= null)
103 configCallback
.accept(config
);
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());
110 Framework framework
= frameworkFactory
.newFramework(config
, null);
113 } catch (BundleException e
) {
114 throw new IllegalStateException("Cannot initialise framework", e
);
116 OsgiRuntimeContext runtimeContext
= new OsgiRuntimeContext(framework
.getBundleContext());
117 runtimeContexts
.put(relPath
, runtimeContext
);
118 return runtimeContext
;
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
);
133 closeRuntime(relPath
, false);
137 public void closeRuntime(String relPath
, boolean async
) {
138 if (!runtimeContexts
.containsKey(relPath
))
140 RuntimeContext runtimeContext
= runtimeContexts
.get(relPath
);
142 runtimeContext
.close();
144 runtimeContext
.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT
);
147 } catch (Exception e
) {
148 logger
.log(Level
.ERROR
, "Cannot close runtime context " + relPath
, e
);
150 runtimeContexts
.remove(relPath
);