]> git.argeo.org Git - lgpl/argeo-commons.git/blob - OsgiBuilder.java
28a2604a42c6c3fc6766b81ff5a94370b07d4ae8
[lgpl/argeo-commons.git] / OsgiBuilder.java
1 package org.argeo.osgi.boot;
2
3 import java.lang.reflect.Method;
4 import java.net.URI;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Properties;
12 import java.util.Set;
13 import java.util.TreeMap;
14
15 import org.eclipse.osgi.launch.EquinoxFactory;
16 import org.osgi.framework.Bundle;
17 import org.osgi.framework.BundleContext;
18 import org.osgi.framework.BundleEvent;
19 import org.osgi.framework.BundleException;
20 import org.osgi.framework.FrameworkUtil;
21 import org.osgi.framework.InvalidSyntaxException;
22 import org.osgi.framework.ServiceReference;
23 import org.osgi.framework.launch.Framework;
24 import org.osgi.framework.launch.FrameworkFactory;
25 import org.osgi.util.tracker.BundleTracker;
26 import org.osgi.util.tracker.ServiceTracker;
27
28 /** OSGi builder, focusing on ease of use for scripting. */
29 public class OsgiBuilder {
30 private final static String PROP_HTTP_PORT = "org.osgi.service.http.port";
31 private final static String PROP_HTTPS_PORT = "org.osgi.service.https.port";
32 private final static String PROP_OSGI_CLEAN = "osgi.clean";
33
34 private Map<Integer, StartLevel> startLevels = new TreeMap<>();
35 private List<String> distributionBundles = new ArrayList<>();
36
37 private Map<String, String> configuration = new HashMap<String, String>();
38 private Framework framework;
39 private String baseUrl = null;
40
41 public OsgiBuilder() {
42 // configuration.put("osgi.clean", "true");
43 configuration.put(OsgiBoot.CONFIGURATION_AREA_PROP, System.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP));
44 configuration.put(OsgiBoot.INSTANCE_AREA_PROP, System.getProperty(OsgiBoot.INSTANCE_AREA_PROP));
45 configuration.put(PROP_OSGI_CLEAN, System.getProperty(PROP_OSGI_CLEAN));
46 }
47
48 public Framework launch() {
49 // start OSGi
50 FrameworkFactory frameworkFactory = new EquinoxFactory();
51 framework = frameworkFactory.newFramework(configuration);
52 try {
53 framework.start();
54 } catch (BundleException e) {
55 throw new OsgiBootException("Cannot start OSGi framework", e);
56 }
57
58 BundleContext bc = framework.getBundleContext();
59 String osgiData = bc.getProperty(OsgiBoot.INSTANCE_AREA_PROP);
60 // String osgiConf = bc.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP);
61 String osgiConf = framework.getDataFile("").getAbsolutePath();
62 if (OsgiBootUtils.isDebug())
63 OsgiBootUtils.debug("OSGi starting - data: " + osgiData + " conf: " + osgiConf);
64
65 OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
66 // install bundles
67 for (String distributionBundle : distributionBundles) {
68 List<String> bundleUrls = osgiBoot.getDistributionUrls(distributionBundle, baseUrl);
69 osgiBoot.installUrls(bundleUrls);
70 }
71
72 // start bundles
73 osgiBoot.startBundles(startLevelsToProperties());
74
75 // if (OsgiBootUtils.isDebug())
76 // for (Bundle bundle : bc.getBundles()) {
77 // OsgiBootUtils.debug(bundle.getLocation());
78 // }
79 return framework;
80 }
81
82 public OsgiBuilder conf(String key, String value) {
83 checkNotLaunched();
84 configuration.put(key, value);
85 return this;
86 }
87
88 public OsgiBuilder install(String uri) {
89 // TODO dynamic install
90 checkNotLaunched();
91 if (!distributionBundles.contains(uri))
92 distributionBundles.add(uri);
93 return this;
94 }
95
96 public OsgiBuilder start(int startLevel, String bundle) {
97 // TODO dynamic start
98 checkNotLaunched();
99 StartLevel sl;
100 if (!startLevels.containsKey(startLevel))
101 startLevels.put(startLevel, new StartLevel());
102 sl = startLevels.get(startLevel);
103 sl.add(bundle);
104 return this;
105 }
106
107 public OsgiBuilder waitForServlet(String base) {
108 service("(&(objectClass=javax.servlet.Servlet)(osgi.http.whiteboard.servlet.pattern=" + base + "))");
109 return this;
110 }
111
112 public OsgiBuilder waitForBundle(String bundles) {
113 List<String> lst = new ArrayList<>();
114 Collections.addAll(lst, bundles.split(","));
115 BundleTracker<Object> bt = new BundleTracker<Object>(getBc(), Bundle.ACTIVE, null) {
116
117 @Override
118 public Object addingBundle(Bundle bundle, BundleEvent event) {
119 if (lst.contains(bundle.getSymbolicName())) {
120 return bundle.getSymbolicName();
121 } else {
122 return null;
123 }
124 }
125 };
126 bt.open();
127 while (bt.getTrackingCount() != lst.size()) {
128 try {
129 Thread.sleep(500l);
130 } catch (InterruptedException e) {
131 break;
132 }
133 }
134 bt.close();
135 return this;
136
137 }
138
139 public OsgiBuilder main(String clssUri, String[] args) {
140
141 // waitForBundle(bundleSymbolicName);
142 try {
143 URI uri = new URI(clssUri);
144 if (!"bundleclass".equals(uri.getScheme()))
145 throw new IllegalArgumentException("Unsupported scheme for " + clssUri);
146 String bundleSymbolicName = uri.getHost();
147 String clss = uri.getPath().substring(1);
148 Bundle bundle = null;
149 for (Bundle b : getBc().getBundles()) {
150 if (bundleSymbolicName.equals(b.getSymbolicName())) {
151 bundle = b;
152 break;
153 }
154 }
155 if (bundle == null)
156 throw new OsgiBootException("Bundle " + bundleSymbolicName + " not found");
157 Class<?> c = bundle.loadClass(clss);
158 Object[] mainArgs = { args };
159 Method mainMethod = c.getMethod("main", String[].class);
160 mainMethod.invoke(null, mainArgs);
161 } catch (Throwable e) {
162 throw new OsgiBootException("Cannot execute " + clssUri, e);
163 }
164 return this;
165 }
166
167 public Object service(String service) {
168 return service(service, 0);
169 }
170
171 public Object service(String service, long timeout) {
172 ServiceTracker<Object, Object> st;
173 if (service.contains("(")) {
174 try {
175 st = new ServiceTracker<>(getBc(), FrameworkUtil.createFilter(service), null);
176 } catch (InvalidSyntaxException e) {
177 throw new IllegalArgumentException("Badly formatted filter", e);
178 }
179 } else {
180 st = new ServiceTracker<>(getBc(), service, null);
181 }
182 st.open();
183 try {
184 return st.waitForService(timeout);
185 } catch (InterruptedException e) {
186 OsgiBootUtils.error("Interrupted", e);
187 return null;
188 } finally {
189 st.close();
190 }
191
192 }
193
194 public void shutdown() {
195 checkLaunched();
196 try {
197 framework.stop();
198 } catch (BundleException e) {
199 e.printStackTrace();
200 System.exit(1);
201 }
202 try {
203 framework.waitForStop(10 * 60 * 1000);
204 } catch (InterruptedException e) {
205 e.printStackTrace();
206 System.exit(1);
207 }
208 System.exit(0);
209 }
210
211 public void setHttpPort(Integer port) {
212 checkNotLaunched();
213 configuration.put(PROP_HTTP_PORT, Integer.toString(port));
214 }
215
216 public void setHttpsPort(Integer port) {
217 checkNotLaunched();
218 configuration.put(PROP_HTTPS_PORT, Integer.toString(port));
219 }
220
221 public void setClean(boolean clean) {
222 checkNotLaunched();
223 configuration.put(PROP_OSGI_CLEAN, Boolean.toString(clean));
224 }
225
226 public Integer getHttpPort() {
227 if (!isLaunched()) {
228 if (configuration.containsKey(PROP_HTTP_PORT))
229 return Integer.parseInt(configuration.get(PROP_HTTP_PORT));
230 else
231 return -1;
232 } else {
233 // TODO wait for service?
234 ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
235 if (sr == null)
236 return -1;
237 Object port = sr.getProperty("http.port");
238 if (port == null)
239 return -1;
240 return Integer.parseInt(port.toString());
241 }
242 }
243
244 public Integer getHttpsPort() {
245 if (!isLaunched()) {
246 if (configuration.containsKey(PROP_HTTPS_PORT))
247 return Integer.parseInt(configuration.get(PROP_HTTPS_PORT));
248 else
249 return -1;
250 } else {
251 // TODO wait for service?
252 ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
253 if (sr == null)
254 return -1;
255 Object port = sr.getProperty("https.port");
256 if (port == null)
257 return -1;
258 return Integer.parseInt(port.toString());
259 }
260 }
261
262 public Object spring(String bundle) {
263 return service("(&(Bundle-SymbolicName=" + bundle + ")"
264 + "(objectClass=org.springframework.context.ApplicationContext))");
265 }
266
267 //
268 // BEAN
269 //
270
271 public BundleContext getBc() {
272 checkLaunched();
273 return framework.getBundleContext();
274 }
275
276 public void setBaseUrl(String baseUrl) {
277 this.baseUrl = baseUrl;
278 }
279
280 //
281 // UTILITIES
282 //
283 private Properties startLevelsToProperties() {
284 Properties properties = new Properties();
285 for (Integer startLevel : startLevels.keySet()) {
286 String property = OsgiBoot.PROP_ARGEO_OSGI_START + "." + startLevel;
287 StringBuilder value = new StringBuilder();
288 for (String bundle : startLevels.get(startLevel).getBundles()) {
289 value.append(bundle);
290 value.append(',');
291 }
292 // TODO remove trailing comma
293 properties.put(property, value.toString());
294 }
295 return properties;
296 }
297
298 private void checkLaunched() {
299 if (!isLaunched())
300 throw new OsgiBootException("OSGi runtime is not launched");
301 }
302
303 private void checkNotLaunched() {
304 if (isLaunched())
305 throw new OsgiBootException("OSGi runtime already launched");
306 }
307
308 private boolean isLaunched() {
309 return framework != null;
310 }
311
312 private static class StartLevel {
313 private Set<String> bundles = new HashSet<>();
314
315 public void add(String bundle) {
316 String[] b = bundle.split(",");
317 Collections.addAll(bundles, b);
318 }
319
320 public Set<String> getBundles() {
321 return bundles;
322 }
323 }
324 }