]> git.argeo.org Git - lgpl/argeo-commons.git/blob - argeo/init/osgi/OsgiRuntimeContext.java
Prepare next development cycle
[lgpl/argeo-commons.git] / argeo / init / osgi / OsgiRuntimeContext.java
1 package org.argeo.init.osgi;
2
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.Collections;
8 import java.util.Hashtable;
9 import java.util.Map;
10 import java.util.Optional;
11 import java.util.ServiceLoader;
12 import java.util.concurrent.Flow;
13 import java.util.function.Consumer;
14 import java.util.function.Supplier;
15
16 import org.argeo.api.init.RuntimeContext;
17 import org.osgi.framework.Bundle;
18 import org.osgi.framework.BundleContext;
19 import org.osgi.framework.BundleException;
20 import org.osgi.framework.Constants;
21 import org.osgi.framework.launch.Framework;
22 import org.osgi.framework.launch.FrameworkFactory;
23
24 /** An OSGi runtime context. */
25 public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
26 private final static Logger logger = System.getLogger(OsgiRuntimeContext.class.getName());
27
28 private final static long STOP_FOR_UPDATE_TIMEOUT = 60 * 1000;
29 private final static long CLOSE_TIMEOUT = 60 * 1000;
30
31 private final static String SYMBOLIC_NAME_FELIX_SCR = "org.apache.felix.scr";
32
33 private Map<String, String> config;
34 private Framework framework;
35 // private OsgiBoot osgiBoot;
36
37 /**
38 * Constructor to use when the runtime context will create the OSGi
39 * {@link Framework}.
40 */
41 public OsgiRuntimeContext(Map<String, String> config) {
42 this.config = config;
43 }
44
45 /**
46 * Constructor to use when the OSGi {@link Framework} has been created by other
47 * means.
48 */
49 OsgiRuntimeContext(BundleContext bundleContext) {
50 start(bundleContext);
51 }
52
53 @Override
54 public void run() {
55 if (framework != null && framework.getState() >= Framework.STARTING)
56 throw new IllegalStateException("OSGi framework is already started");
57
58 if (framework == null) {
59 ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
60 Optional<FrameworkFactory> opt = sl.findFirst();
61 if (opt.isEmpty())
62 throw new IllegalStateException("Cannot find OSGi framework");
63 framework = opt.get().newFramework(config);
64 }
65
66 try {
67 framework.start();
68 BundleContext bundleContext = framework.getBundleContext();
69 start(bundleContext);
70 } catch (BundleException e) {
71 throw new IllegalStateException("Cannot start OSGi framework", e);
72 }
73 }
74
75 protected void start(BundleContext bundleContext) {
76 // preferences
77 // SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences();
78 // bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>());
79
80 // Make sure LoggerFinder has been searched for, since it is lazily loaded
81 LoggerFinder loggerFinder = LoggerFinder.getLoggerFinder();
82
83 if (loggerFinder instanceof Consumer<?> && loggerFinder instanceof Supplier<?>) {
84 @SuppressWarnings("unchecked")
85 Consumer<Map<String, Object>> consumer = (Consumer<Map<String, Object>>) loggerFinder;
86 // ThinLoggerFinder.getConfigurationConsumer()
87 // ThinLoggerFinder.getLogEntryPublisher()
88
89 @SuppressWarnings("unchecked")
90 Supplier<Flow.Publisher<Map<String, Serializable>>> supplier = (Supplier<Flow.Publisher<Map<String, Serializable>>>) loggerFinder;
91 // logging
92 bundleContext.registerService(Consumer.class, consumer,
93 new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration")));
94 bundleContext.registerService(Flow.Publisher.class, supplier.get(),
95 new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher")));
96 }
97 OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
98 String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
99
100 // separate thread in order to improve logging
101 Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) {
102 @Override
103 public void run() {
104 osgiBoot.bootstrap(config);
105 }
106 };
107 osgiBootThread.start();
108 // TODO return a completable stage so that inits can run in parallel
109 // try {
110 // osgiBootThread.join(60 * 1000);
111 // } catch (InterruptedException e) {
112 // // silent
113 // }
114 }
115
116 public void update() {
117 stop();
118 try {
119 waitForStop(STOP_FOR_UPDATE_TIMEOUT);
120 } catch (InterruptedException e) {
121 logger.log(Level.TRACE, "Wait for stop interrupted", e);
122 }
123 run();
124
125 // TODO Optimise with OSGi mechanisms (e.g. framework.update())
126 // if (osgiBoot != null) {
127 // Objects.requireNonNull(osgiBoot);
128 // osgiBoot.update();
129 // }
130 }
131
132 protected void stop() {
133 if (framework == null)
134 return;
135 stop(framework.getBundleContext());
136 try {
137 framework.stop();
138 } catch (BundleException e) {
139 throw new IllegalStateException("Cannot stop OSGi framework", e);
140 }
141 }
142
143 protected void stop(BundleContext bundleContext) {
144 // if (loggingConfigurationSr != null)
145 // try {
146 // loggingConfigurationSr.unregister();
147 // } catch (Exception e) {
148 // // silent
149 // }
150 // if (logEntryPublisherSr != null)
151 // try {
152 // logEntryPublisherSr.unregister();
153 // } catch (Exception e) {
154 // // silent
155 // }
156 }
157
158 @Override
159 public void waitForStop(long timeout) throws InterruptedException {
160 if (framework == null)
161 return;
162
163 framework.waitForStop(timeout);
164 }
165
166 public void close() throws Exception {
167 if (framework == null)
168 return;
169 // TODO make shutdown of dynamic service more robust
170 // for (Bundle scrBundle : framework.getBundleContext().getBundles()) {
171 // if (scrBundle.getSymbolicName().equals(SYMBOLIC_NAME_FELIX_SCR)) {
172 // if (scrBundle.getState() > Bundle.RESOLVED) {
173 // scrBundle.stop();
174 // while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
175 // Thread.sleep(100);
176 // }
177 // Thread.sleep(500);
178 // }
179 // }
180 // }
181
182 stop();
183 waitForStop(CLOSE_TIMEOUT);
184 framework = null;
185 // osgiBoot = null;
186 config.clear();
187 }
188
189 public Framework getFramework() {
190 return framework;
191 }
192
193 }