1 package org
.argeo
.init
.osgi
;
3 import java
.io
.Serializable
;
4 import java
.lang
.System
.Logger
;
5 import java
.lang
.System
.Logger
.Level
;
6 import java
.lang
.System
.LoggerFinder
;
7 import java
.util
.Arrays
;
8 import java
.util
.Collections
;
9 import java
.util
.Hashtable
;
10 import java
.util
.List
;
12 import java
.util
.Objects
;
13 import java
.util
.Optional
;
14 import java
.util
.ServiceLoader
;
15 import java
.util
.concurrent
.Flow
;
16 import java
.util
.function
.Consumer
;
17 import java
.util
.function
.Supplier
;
19 import org
.argeo
.api
.init
.InitConstants
;
20 import org
.argeo
.api
.init
.RuntimeContext
;
21 import org
.osgi
.framework
.BundleContext
;
22 import org
.osgi
.framework
.BundleException
;
23 import org
.osgi
.framework
.Constants
;
24 import org
.osgi
.framework
.ServiceReference
;
25 import org
.osgi
.framework
.connect
.ConnectFrameworkFactory
;
26 import org
.osgi
.framework
.launch
.Framework
;
28 /** An OSGi runtime context. */
29 public class OsgiRuntimeContext
implements RuntimeContext
, AutoCloseable
{
30 private final static Logger logger
= System
.getLogger(OsgiRuntimeContext
.class.getName());
32 private final static long STOP_FOR_UPDATE_TIMEOUT
= 60 * 1000;
33 private final static long CLOSE_TIMEOUT
= 60 * 1000;
35 private final ConnectFrameworkFactory frameworkFactory
;
36 private Map
<String
, String
> config
;
37 private Framework framework
;
40 // private final BundleContext foreignBundleContext;
41 // private final List<String> foreignCategories;
43 private final ForeignModuleConnector moduleConnector
;
46 * Constructor to use when the runtime context will create the OSGi
49 public OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory
, Map
<String
, String
> config
) {
50 this(frameworkFactory
, config
, null, null);
54 * Constructor to use when the runtime context will create the OSGi
55 * {@link Framework} and will potentially have a parent runtime.
57 OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory
, Map
<String
, String
> config
,
58 BundleContext foreignBundleContext
, List
<String
> foreignCategories
) {
59 this.frameworkFactory
= frameworkFactory
;
61 if (foreignCategories
!= null) {
62 // String parentCategories = config.get(InitConstants.PROP_ARGEO_OSGI_PARENT_CATEGORIES);
63 // if (parentCategories != null) {
64 // Objects.requireNonNull(foreignBundleContext, "Foreign bundle context");
65 // List<String> foreignCategories = Arrays.asList(parentCategories.trim().split(","));
66 // foreignCategories.removeIf((s) -> "".equals(s));
67 this.moduleConnector
= new ForeignModuleConnector(foreignBundleContext
, foreignCategories
);
69 this.moduleConnector
= null;
74 * Constructor to use when the OSGi {@link Framework} has been created by other
77 OsgiRuntimeContext(BundleContext bundleContext
) {
78 this.frameworkFactory
= null;
79 // TODO try nesting within Eclipse PDE
80 this.moduleConnector
= null;
86 if (framework
!= null && framework
.getState() >= Framework
.STARTING
)
87 throw new IllegalStateException("OSGi framework is already started");
89 if (framework
== null) {
90 // ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
91 // Optional<FrameworkFactory> opt = sl.findFirst();
93 // throw new IllegalStateException("Cannot find OSGi framework");
94 // framework = opt.get().newFramework(config);
95 framework
= frameworkFactory
.newFramework(config
, moduleConnector
);
100 BundleContext bundleContext
= framework
.getBundleContext();
101 bundleContext
.registerService(ConnectFrameworkFactory
.class, frameworkFactory
, null);
102 start(bundleContext
);
103 } catch (BundleException e
) {
104 throw new IllegalStateException("Cannot start OSGi framework", e
);
108 protected void start(BundleContext bundleContext
) {
110 // SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences();
111 // bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>());
113 // Make sure LoggerFinder has been searched for, since it is lazily loaded
114 LoggerFinder loggerFinder
= LoggerFinder
.getLoggerFinder();
116 if (loggerFinder
instanceof Consumer
<?
> && loggerFinder
instanceof Supplier
<?
>) {
117 @SuppressWarnings("unchecked")
118 Consumer
<Map
<String
, Object
>> consumer
= (Consumer
<Map
<String
, Object
>>) loggerFinder
;
119 // ThinLoggerFinder.getConfigurationConsumer()
120 // ThinLoggerFinder.getLogEntryPublisher()
122 @SuppressWarnings("unchecked")
123 Supplier
<Flow
.Publisher
<Map
<String
, Serializable
>>> supplier
= (Supplier
<Flow
.Publisher
<Map
<String
, Serializable
>>>) loggerFinder
;
125 bundleContext
.registerService(Consumer
.class, consumer
,
126 new Hashtable
<>(Collections
.singletonMap(Constants
.SERVICE_PID
, "argeo.logging.configuration")));
127 bundleContext
.registerService(Flow
.Publisher
.class, supplier
.get(),
128 new Hashtable
<>(Collections
.singletonMap(Constants
.SERVICE_PID
, "argeo.logging.publisher")));
130 OsgiBoot osgiBoot
= new OsgiBoot(bundleContext
);
131 String frameworkUuuid
= bundleContext
.getProperty(Constants
.FRAMEWORK_UUID
);
133 // osgiBoot.bootstrap();
135 // separate thread in order to improve logging
136 Thread osgiBootThread
= new Thread("OSGi boot framework " + frameworkUuuid
) {
139 osgiBoot
.bootstrap();
142 osgiBootThread
.start();
143 // TODO return a completable stage so that inits can run in parallel
145 // osgiBootThread.join(60 * 1000);
146 // } catch (InterruptedException e) {
151 public void update() {
154 waitForStop(STOP_FOR_UPDATE_TIMEOUT
);
155 } catch (InterruptedException e
) {
156 logger
.log(Level
.TRACE
, "Wait for stop interrupted", e
);
160 // TODO Optimise with OSGi mechanisms (e.g. framework.update())
161 // if (osgiBoot != null) {
162 // Objects.requireNonNull(osgiBoot);
163 // osgiBoot.update();
167 protected void stop() {
168 if (framework
== null)
170 stop(framework
.getBundleContext());
173 } catch (BundleException e
) {
174 throw new IllegalStateException("Cannot stop OSGi framework", e
);
178 protected void stop(BundleContext bundleContext
) {
179 // if (loggingConfigurationSr != null)
181 // loggingConfigurationSr.unregister();
182 // } catch (Exception e) {
185 // if (logEntryPublisherSr != null)
187 // logEntryPublisherSr.unregister();
188 // } catch (Exception e) {
194 public void waitForStop(long timeout
) throws InterruptedException
{
195 if (framework
== null)
198 framework
.waitForStop(timeout
);
201 public void close() throws Exception
{
202 if (framework
== null)
204 // TODO make shutdown of dynamic service more robust
205 // for (Bundle scrBundle : framework.getBundleContext().getBundles()) {
206 // if (scrBundle.getSymbolicName().equals(SYMBOLIC_NAME_FELIX_SCR)) {
207 // if (scrBundle.getState() > Bundle.RESOLVED) {
209 // while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
210 // Thread.sleep(100);
212 // Thread.sleep(500);
218 waitForStop(CLOSE_TIMEOUT
);
224 public Framework
getFramework() {
229 * Load {@link ConnectFrameworkFactory} from Java service loader. This will not
230 * work within a pure OSGi runtime, so the reference should be passed to child
231 * runtimes as an OSGi service.
233 public static ConnectFrameworkFactory
loadFrameworkFactory() {
234 ServiceLoader
<ConnectFrameworkFactory
> sl
= ServiceLoader
.load(ConnectFrameworkFactory
.class);
235 Optional
<ConnectFrameworkFactory
> opt
= sl
.findFirst();
237 throw new IllegalStateException("Cannot find OSGi framework factory");
241 public static ConnectFrameworkFactory
getFrameworkFactory(BundleContext bundleContext
) {
242 ServiceReference
<ConnectFrameworkFactory
> sr
= bundleContext
.getServiceReference(ConnectFrameworkFactory
.class);
245 return bundleContext
.getService(sr
);