]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java
Improve init launch
[lgpl/argeo-commons.git] / org.argeo.init / src / org / 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.BundleContext;
18 import org.osgi.framework.BundleException;
19 import org.osgi.framework.Constants;
20 import org.osgi.framework.ServiceReference;
21 import org.osgi.framework.connect.ConnectFrameworkFactory;
22 import org.osgi.framework.launch.Framework;
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 ConnectFrameworkFactory frameworkFactory;
34 private Map<String, String> config;
35 private Framework framework;
36 // private OsgiBoot osgiBoot;
37
38 /**
39 * Constructor to use when the runtime context will create the OSGi
40 * {@link Framework}.
41 */
42 public OsgiRuntimeContext(ConnectFrameworkFactory frameworkFactory, Map<String, String> config) {
43 this.frameworkFactory = frameworkFactory;
44 this.config = config;
45 }
46
47 /**
48 * Constructor to use when the OSGi {@link Framework} has been created by other
49 * means.
50 */
51 OsgiRuntimeContext(BundleContext bundleContext) {
52 start(bundleContext);
53 }
54
55 @Override
56 public void run() {
57 if (framework != null && framework.getState() >= Framework.STARTING)
58 throw new IllegalStateException("OSGi framework is already started");
59
60 if (framework == null) {
61 // ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
62 // Optional<FrameworkFactory> opt = sl.findFirst();
63 // if (opt.isEmpty())
64 // throw new IllegalStateException("Cannot find OSGi framework");
65 // framework = opt.get().newFramework(config);
66 framework = frameworkFactory.newFramework(config, null);
67 }
68
69 try {
70 framework.start();
71 BundleContext bundleContext = framework.getBundleContext();
72 bundleContext.registerService(ConnectFrameworkFactory.class, frameworkFactory, null);
73 start(bundleContext);
74 } catch (BundleException e) {
75 throw new IllegalStateException("Cannot start OSGi framework", e);
76 }
77 }
78
79 protected void start(BundleContext bundleContext) {
80 // preferences
81 // SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences();
82 // bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>());
83
84 // Make sure LoggerFinder has been searched for, since it is lazily loaded
85 LoggerFinder loggerFinder = LoggerFinder.getLoggerFinder();
86
87 if (loggerFinder instanceof Consumer<?> && loggerFinder instanceof Supplier<?>) {
88 @SuppressWarnings("unchecked")
89 Consumer<Map<String, Object>> consumer = (Consumer<Map<String, Object>>) loggerFinder;
90 // ThinLoggerFinder.getConfigurationConsumer()
91 // ThinLoggerFinder.getLogEntryPublisher()
92
93 @SuppressWarnings("unchecked")
94 Supplier<Flow.Publisher<Map<String, Serializable>>> supplier = (Supplier<Flow.Publisher<Map<String, Serializable>>>) loggerFinder;
95 // logging
96 bundleContext.registerService(Consumer.class, consumer,
97 new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration")));
98 bundleContext.registerService(Flow.Publisher.class, supplier.get(),
99 new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher")));
100 }
101 OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
102 String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
103
104 // separate thread in order to improve logging
105 Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) {
106 @Override
107 public void run() {
108 osgiBoot.bootstrap();
109 }
110 };
111 osgiBootThread.start();
112 // TODO return a completable stage so that inits can run in parallel
113 // try {
114 // osgiBootThread.join(60 * 1000);
115 // } catch (InterruptedException e) {
116 // // silent
117 // }
118 }
119
120 public void update() {
121 stop();
122 try {
123 waitForStop(STOP_FOR_UPDATE_TIMEOUT);
124 } catch (InterruptedException e) {
125 logger.log(Level.TRACE, "Wait for stop interrupted", e);
126 }
127 run();
128
129 // TODO Optimise with OSGi mechanisms (e.g. framework.update())
130 // if (osgiBoot != null) {
131 // Objects.requireNonNull(osgiBoot);
132 // osgiBoot.update();
133 // }
134 }
135
136 protected void stop() {
137 if (framework == null)
138 return;
139 stop(framework.getBundleContext());
140 try {
141 framework.stop();
142 } catch (BundleException e) {
143 throw new IllegalStateException("Cannot stop OSGi framework", e);
144 }
145 }
146
147 protected void stop(BundleContext bundleContext) {
148 // if (loggingConfigurationSr != null)
149 // try {
150 // loggingConfigurationSr.unregister();
151 // } catch (Exception e) {
152 // // silent
153 // }
154 // if (logEntryPublisherSr != null)
155 // try {
156 // logEntryPublisherSr.unregister();
157 // } catch (Exception e) {
158 // // silent
159 // }
160 }
161
162 @Override
163 public void waitForStop(long timeout) throws InterruptedException {
164 if (framework == null)
165 return;
166
167 framework.waitForStop(timeout);
168 }
169
170 public void close() throws Exception {
171 if (framework == null)
172 return;
173 // TODO make shutdown of dynamic service more robust
174 // for (Bundle scrBundle : framework.getBundleContext().getBundles()) {
175 // if (scrBundle.getSymbolicName().equals(SYMBOLIC_NAME_FELIX_SCR)) {
176 // if (scrBundle.getState() > Bundle.RESOLVED) {
177 // scrBundle.stop();
178 // while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
179 // Thread.sleep(100);
180 // }
181 // Thread.sleep(500);
182 // }
183 // }
184 // }
185
186 stop();
187 waitForStop(CLOSE_TIMEOUT);
188 framework = null;
189 // osgiBoot = null;
190 config.clear();
191 }
192
193 public Framework getFramework() {
194 return framework;
195 }
196
197 /**
198 * Load {@link ConnectFrameworkFactory} from Java service loader. This will not
199 * work within a pure OSGi runtime, so the reference should be passed to child
200 * runtimes as an OSGi service.
201 */
202 public static ConnectFrameworkFactory loadFrameworkFactory() {
203 ServiceLoader<ConnectFrameworkFactory> sl = ServiceLoader.load(ConnectFrameworkFactory.class);
204 Optional<ConnectFrameworkFactory> opt = sl.findFirst();
205 if (opt.isEmpty())
206 throw new IllegalStateException("Cannot find OSGi framework factory");
207 return opt.get();
208 }
209
210 public static ConnectFrameworkFactory getFrameworkFactory(BundleContext bundleContext) {
211 ServiceReference<ConnectFrameworkFactory> sr = bundleContext.getServiceReference(ConnectFrameworkFactory.class);
212 if (sr == null)
213 return null;
214 return bundleContext.getService(sr);
215 }
216 }