1 package org
.argeo
.init
.osgi
;
3 import java
.lang
.System
.Logger
;
4 import java
.lang
.System
.Logger
.Level
;
6 import java
.nio
.file
.Files
;
7 import java
.nio
.file
.Path
;
8 import java
.nio
.file
.Paths
;
9 import java
.util
.ArrayList
;
10 import java
.util
.Arrays
;
11 import java
.util
.HashMap
;
12 import java
.util
.HashSet
;
13 import java
.util
.List
;
15 import java
.util
.Objects
;
16 import java
.util
.TreeMap
;
17 import java
.util
.function
.Consumer
;
19 import org
.argeo
.api
.init
.InitConstants
;
20 import org
.argeo
.api
.init
.RuntimeContext
;
21 import org
.argeo
.api
.init
.RuntimeManager
;
22 import org
.argeo
.internal
.init
.InternalState
;
23 import org
.osgi
.framework
.BundleContext
;
24 import org
.osgi
.framework
.FrameworkEvent
;
25 import org
.osgi
.framework
.connect
.ConnectFrameworkFactory
;
26 import org
.osgi
.framework
.launch
.Framework
;
29 * Dynamically configures and launches multiple runtimes.
31 class OsgiRuntimeManager
implements RuntimeManager
{
32 private final static Logger logger
= System
.getLogger(OsgiRuntimeManager
.class.getName());
34 private final static long RUNTIME_SHUTDOWN_TIMEOUT
= 60 * 1000;
36 // private Path ownConfigArea;
38 private Path baseConfigArea
;
39 private Path baseWritableArea
;
40 // private Map<String, String> configuration = new HashMap<>();
42 private ConnectFrameworkFactory frameworkFactory
;
44 private final BundleContext bundleContext
;
46 private Map
<String
, OsgiRuntimeContext
> runtimeContexts
= new TreeMap
<>();
48 private boolean useForeignRuntime
= Boolean
49 .parseBoolean(System
.getProperty(InitConstants
.PROP_ARGEO_OSGI_EXPORT_ENABLED
, "true"));
51 OsgiRuntimeManager(BundleContext bundleContext
) {
52 Objects
.requireNonNull(bundleContext
);
53 this.bundleContext
= bundleContext
;
54 frameworkFactory
= OsgiRuntimeContext
.getFrameworkFactory(bundleContext
);
55 this.baseConfigArea
= Paths
56 .get(URI
.create(bundleContext
.getProperty(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA
)));
57 // this.baseConfigArea = ownConfigArea.getParent();
58 this.baseWritableArea
= Paths
59 .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);
97 BundleContext foreignBundleContext
= null;
98 if (useForeignRuntime
) {
99 foreignBundleContext
= bundleContext
;
101 Path parentRelPath
= Paths
.get(relPath
).getParent();
102 if (parentRelPath
!= null && Files
.exists(baseConfigArea
.resolve(parentRelPath
))) {
103 if (!runtimeContexts
.containsKey(parentRelPath
.toString())) {
105 String exportCategories
= bundleContext
106 .getProperty(InitConstants
.PROP_ARGEO_OSGI_EXPORT_CATEGORIES
);
107 List
<String
> foreignCategories
= exportCategories
== null ?
new ArrayList
<>()
108 : Arrays
.asList(exportCategories
.trim().split(","));
109 Path writableArea
= baseWritableArea
.resolve(parentRelPath
);
110 Path configArea
= baseConfigArea
.resolve(parentRelPath
);
111 Map
<String
, String
> config
= new HashMap
<>();
112 RuntimeManager
.loadDefaults(config
);
113 config
.put(InitConstants
.PROP_OSGI_USE_SYSTEM_PROPERTIES
, "false");
114 config
.put(InitConstants
.PROP_OSGI_CONFIGURATION_AREA
,
115 writableArea
.resolve(STATE
).toUri().toString());
116 config
.put(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA
, configArea
.toUri().toString());
117 config
.put(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA_RO
, "true");
118 OsgiRuntimeContext runtimeContext
= new OsgiRuntimeContext(frameworkFactory
, config
,
119 foreignBundleContext
, foreignCategories
);
120 runtimeContexts
.put(parentRelPath
.toString(), runtimeContext
);
121 runtimeContext
.run();
122 // FIXME properly stage installation
125 } catch (InterruptedException e
) {
129 OsgiRuntimeContext parentRuntimeContext
= runtimeContexts
.get(parentRelPath
.toString());
130 foreignBundleContext
= parentRuntimeContext
.getFramework().getBundleContext();
133 Path writableArea
= baseWritableArea
.resolve(relPath
);
134 Path configArea
= baseConfigArea
.resolve(relPath
).getParent().resolve(SHARED
);
135 Map
<String
, String
> config
= new HashMap
<>();
136 // RuntimeManager.loadConfig(configArea, config);
137 RuntimeManager
.loadDefaults(config
);
138 config
.put(InitConstants
.PROP_OSGI_USE_SYSTEM_PROPERTIES
, "false");
139 config
.put(InitConstants
.PROP_OSGI_CONFIGURATION_AREA
, writableArea
.resolve(STATE
).toUri().toString());
140 config
.put(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA
, configArea
.toUri().toString());
141 config
.put(InitConstants
.PROP_OSGI_SHARED_CONFIGURATION_AREA_RO
, "true");
143 if (configCallback
!= null)
144 configCallback
.accept(config
);
146 // use config area if instance area is not set
147 if (!config
.containsKey(InitConstants
.PROP_OSGI_INSTANCE_AREA
))
148 config
.put(InitConstants
.PROP_OSGI_INSTANCE_AREA
, writableArea
.resolve(DATA
).toUri().toString());
151 OsgiRuntimeContext runtimeContext
;
152 if (useForeignRuntime
) {
153 String exportCategories
= bundleContext
.getProperty(InitConstants
.PROP_ARGEO_OSGI_EXPORT_CATEGORIES
);
154 List
<String
> foreignCategories
= exportCategories
== null ?
new ArrayList
<>()
155 : Arrays
.asList(exportCategories
.trim().split(","));
156 runtimeContext
= new OsgiRuntimeContext(frameworkFactory
, config
, foreignBundleContext
, foreignCategories
);
158 runtimeContext
= new OsgiRuntimeContext(frameworkFactory
, config
);
160 runtimeContexts
.put(relPath
, runtimeContext
);
161 return runtimeContext
;
164 public void startRuntime(String relPath
, Consumer
<Map
<String
, String
>> configCallback
) {
165 OsgiRuntimeContext runtimeContext
= loadRuntime(relPath
, configCallback
);
166 runtimeContext
.run();
167 Framework framework
= runtimeContext
.getFramework();
168 if (framework
!= null) {// in case the framework has closed very quickly after run
169 framework
.getBundleContext().addFrameworkListener((e
) -> {
170 if (e
.getType() >= FrameworkEvent
.STOPPED
) {
171 logger
.log(Level
.DEBUG
, "Externally stopped runtime " + relPath
+ ". Unregistering...", e
);
172 runtimeContexts
.remove(relPath
);
176 closeRuntime(relPath
, false);
180 public void closeRuntime(String relPath
, boolean async
) {
181 if (!runtimeContexts
.containsKey(relPath
))
183 RuntimeContext runtimeContext
= runtimeContexts
.get(relPath
);
185 runtimeContext
.close();
187 runtimeContext
.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT
);
190 } catch (Exception e
) {
191 logger
.log(Level
.ERROR
, "Cannot close runtime context " + relPath
, e
);
193 runtimeContexts
.remove(relPath
);