]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java
Make icon sizes configurable
[lgpl/argeo-commons.git] / org.argeo.init / src / org / argeo / init / osgi / OsgiBoot.java
1 package org.argeo.init.osgi;
2
3 import static org.argeo.init.osgi.OsgiBootUtils.debug;
4 import static org.argeo.init.osgi.OsgiBootUtils.warn;
5
6 import java.io.File;
7 import java.nio.file.FileSystems;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10 import java.nio.file.PathMatcher;
11 import java.nio.file.Paths;
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Properties;
18 import java.util.Set;
19 import java.util.SortedMap;
20 import java.util.StringTokenizer;
21 import java.util.TreeMap;
22
23 import org.argeo.init.a2.A2Source;
24 import org.argeo.init.a2.ProvisioningManager;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleContext;
27 import org.osgi.framework.BundleException;
28 import org.osgi.framework.FrameworkEvent;
29 import org.osgi.framework.Version;
30 import org.osgi.framework.startlevel.BundleStartLevel;
31 import org.osgi.framework.startlevel.FrameworkStartLevel;
32 import org.osgi.framework.wiring.FrameworkWiring;
33
34 /**
35 * Basic provisioning of an OSGi runtime via file path patterns and system
36 * properties. The approach is to generate list of URLs based on various
37 * methods, configured via properties.
38 */
39 public class OsgiBoot implements OsgiBootConstants {
40 public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
41 public final static String PROP_ARGEO_OSGI_MAX_START_LEVEL = "argeo.osgi.maxStartLevel";
42 public final static String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
43
44 @Deprecated
45 final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
46 final static String PROP_ARGEO_OSGI_BASE_URL = "argeo.osgi.baseUrl";
47 final static String PROP_ARGEO_OSGI_LOCAL_CACHE = "argeo.osgi.localCache";
48 final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL = "argeo.osgi.distributionUrl";
49
50 // booleans
51 @Deprecated
52 final static String PROP_ARGEO_OSGI_BOOT_DEBUG = "argeo.osgi.boot.debug";
53
54 final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE = "argeo.osgi.boot.systemPropertiesFile";
55 final static String PROP_ARGEO_OSGI_BOOT_APPCLASS = "argeo.osgi.boot.appclass";
56 final static String PROP_ARGEO_OSGI_BOOT_APPARGS = "argeo.osgi.boot.appargs";
57
58 @Deprecated
59 public final static String DEFAULT_BASE_URL = "reference:file:";
60 final static String DEFAULT_MAX_START_LEVEL = "32";
61
62 // OSGi standard properties
63 final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
64 final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
65 public final static String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";
66 public final static String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
67 public final static String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area";
68 public final static String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
69
70 // Symbolic names
71 final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.init";
72 final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
73
74 private final BundleContext bundleContext;
75 private final String localCache;
76 private final ProvisioningManager provisioningManager;
77
78 /*
79 * INITIALISATION
80 */
81 /** Constructor */
82 public OsgiBoot(BundleContext bundleContext) {
83 this.bundleContext = bundleContext;
84 Path homePath = Paths.get(System.getProperty("user.home")).toAbsolutePath();
85 String homeUri = homePath.toUri().toString();
86 localCache = getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE, homeUri + ".m2/repository/");
87
88 provisioningManager = new ProvisioningManager(bundleContext);
89 String sources = getProperty(PROP_ARGEO_OSGI_SOURCES);
90 if (sources == null) {
91 provisioningManager.registerDefaultSource();
92 } else {
93 // OsgiBootUtils.debug("Found sources " + sources);
94 for (String source : sources.split(",")) {
95 int qmIndex = source.lastIndexOf('?');
96 String queryPart = "";
97 if (qmIndex >= 0) {
98 queryPart = source.substring(qmIndex);
99 source = source.substring(0, qmIndex);
100 }
101 if (source.trim().equals(A2Source.DEFAULT_A2_URI)) {
102 if (Files.exists(homePath))
103 provisioningManager.registerSource(
104 A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/a2" + queryPart);
105 provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/a2" + queryPart);
106 provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/a2" + queryPart);
107 } else {
108 provisioningManager.registerSource(source + queryPart);
109 }
110 }
111 }
112 }
113
114 ProvisioningManager getProvisioningManager() {
115 return provisioningManager;
116 }
117
118 /*
119 * HIGH-LEVEL METHODS
120 */
121 /**
122 * Bootstraps the OSGi runtime using these properties, which MUST be consistent
123 * with {@link BundleContext#getProperty(String)}. If these properties are
124 * <code>null</code>, system properties are used instead.
125 */
126 public void bootstrap(Map<String, String> properties) {
127 try {
128 long begin = System.currentTimeMillis();
129 // check properties
130 // if (properties != null) {
131 // for (String property : properties.keySet()) {
132 // String value = properties.get(property);
133 // String bcValue = bundleContext.getProperty(property);
134 // if (PROP_OSGI_CONFIGURATION_AREA.equals(property) || PROP_OSGI_INSTANCE_AREA.equals(property)) {
135 // try {
136 // if (value.startsWith("/"))
137 // value = "file://" + value;
138 // URL uri = new URL(value);
139 // URL bcUri = new URL(bcValue);
140 // if (!uri.equals(bcUri))
141 // throw new IllegalArgumentException("Property " + property + "=" + uri
142 // + " is inconsistent with bundle context : " + bcUri);
143 // } catch (MalformedURLException e) {
144 // throw new IllegalArgumentException("Malformed property " + property, e);
145 // }
146 //
147 // } else {
148 // if (!value.equals(bcValue))
149 // throw new IllegalArgumentException("Property " + property + "=" + value
150 // + " is inconsistent with bundle context : " + bcValue);
151 // }
152 // }
153 // } else {
154 // String useSystemProperties = bundleContext.getProperty(PROP_OSGI_USE_SYSTEM_PROPERTIES);
155 // if (useSystemProperties == null || !useSystemProperties.equals("true")) {
156 // OsgiBootUtils.warn("No properties passed but " + PROP_OSGI_USE_SYSTEM_PROPERTIES + " is not set.");
157 // }
158 // }
159
160 // notify start
161 String osgiInstancePath = getProperty(PROP_OSGI_INSTANCE_AREA);
162 String osgiConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
163 String osgiSharedConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
164 OsgiBootUtils.info("OSGi bootstrap starting" //
165 + (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
166 + (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
167 + (osgiSharedConfigurationPath != null ? " config: " + osgiSharedConfigurationPath + "" : "") //
168 );
169
170 // legacy install bundles
171 installUrls(getBundlesUrls());
172 installUrls(getDistributionUrls());
173
174 // A2 install bundles
175 provisioningManager.install(null);
176
177 // start bundles
178 // if (properties != null && !Boolean.parseBoolean(properties.get(PROP_OSGI_USE_SYSTEM_PROPERTIES)))
179 startBundles(properties);
180 // else
181 // startBundles();
182
183 // complete
184 long duration = System.currentTimeMillis() - begin;
185 OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
186 + duration + "ms), " + bundleContext.getBundles().length + " bundles");
187 } catch (RuntimeException e) {
188 OsgiBootUtils.error("OSGi bootstrap FAILED", e);
189 throw e;
190 }
191
192 // diagnostics
193 if (OsgiBootUtils.isDebug()) {
194 OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
195 diagnostics.checkUnresolved();
196 Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
197 if (duplicatePackages.size() > 0) {
198 OsgiBootUtils.info("Packages exported twice:");
199 Iterator<String> it = duplicatePackages.keySet().iterator();
200 while (it.hasNext()) {
201 String pkgName = it.next();
202 OsgiBootUtils.info(pkgName);
203 Set<String> bdles = duplicatePackages.get(pkgName);
204 Iterator<String> bdlesIt = bdles.iterator();
205 while (bdlesIt.hasNext())
206 OsgiBootUtils.info(" " + bdlesIt.next());
207 }
208 }
209 }
210 System.out.println();
211 }
212
213 /**
214 * Calls {@link #bootstrap(Map)} with <code>null</code>.
215 *
216 * @see #bootstrap(Map)
217 */
218 @Deprecated
219 public void bootstrap() {
220 bootstrap(null);
221 }
222
223 public void update() {
224 provisioningManager.update();
225 }
226
227 /*
228 * INSTALLATION
229 */
230 /** Install a single url. Convenience method. */
231 public Bundle installUrl(String url) {
232 List<String> urls = new ArrayList<String>();
233 urls.add(url);
234 installUrls(urls);
235 return (Bundle) getBundlesByLocation().get(url);
236 }
237
238 /** Install the bundles at this URL list. */
239 public void installUrls(List<String> urls) {
240 Map<String, Bundle> installedBundles = getBundlesByLocation();
241 for (int i = 0; i < urls.size(); i++) {
242 String url = (String) urls.get(i);
243 installUrl(url, installedBundles);
244 }
245 refreshFramework();
246 }
247
248 /** Actually install the provided URL */
249 protected void installUrl(String url, Map<String, Bundle> installedBundles) {
250 try {
251 if (installedBundles.containsKey(url)) {
252 Bundle bundle = (Bundle) installedBundles.get(url);
253 if (OsgiBootUtils.isDebug())
254 debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
255 } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
256 || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
257 if (OsgiBootUtils.isDebug())
258 warn("Skip " + url);
259 return;
260 } else {
261 Bundle bundle = bundleContext.installBundle(url);
262 if (url.startsWith("http"))
263 OsgiBootUtils
264 .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
265 else if (OsgiBootUtils.isDebug())
266 OsgiBootUtils.debug(
267 "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
268 assert bundle.getSymbolicName() != null;
269 // uninstall previous versions
270 bundles: for (Bundle b : bundleContext.getBundles()) {
271 if (b.getSymbolicName() == null)
272 continue bundles;
273 if (bundle.getSymbolicName().equals(b.getSymbolicName())) {
274 Version bundleV = bundle.getVersion();
275 Version bV = b.getVersion();
276 if (bV == null)
277 continue bundles;
278 if (bundleV.getMajor() == bV.getMajor() && bundleV.getMinor() == bV.getMinor()) {
279 if (bundleV.getMicro() > bV.getMicro()) {
280 // uninstall older bundles
281 b.uninstall();
282 OsgiBootUtils.debug("Uninstalled " + b);
283 } else if (bundleV.getMicro() < bV.getMicro()) {
284 // uninstall just installed bundle if newer
285 bundle.uninstall();
286 OsgiBootUtils.debug("Uninstalled " + bundle);
287 break bundles;
288 } else {
289 // uninstall any other with same major/minor
290 if (!bundleV.getQualifier().equals(bV.getQualifier())) {
291 b.uninstall();
292 OsgiBootUtils.debug("Uninstalled " + b);
293 }
294 }
295 }
296 }
297 }
298 }
299 } catch (BundleException e) {
300 final String ALREADY_INSTALLED = "is already installed";
301 String message = e.getMessage();
302 if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
303 || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
304 && message.contains(ALREADY_INSTALLED)) {
305 // silent, in order to avoid warnings: we know that both
306 // have already been installed...
307 } else {
308 if (message.contains(ALREADY_INSTALLED)) {
309 if (OsgiBootUtils.isDebug())
310 OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
311 } else
312 OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
313 }
314 if (OsgiBootUtils.isDebug() && !message.contains(ALREADY_INSTALLED))
315 e.printStackTrace();
316 }
317 }
318
319 /*
320 * START
321 */
322 // /**
323 // * Start bundles based on system properties.
324 // *
325 // * @see OsgiBoot#doStartBundles(Map)
326 // */
327 // public void startBundles() {
328 // Properties properties = System.getProperties();
329 // startBundles(properties);
330 // }
331
332 /**
333 * Start bundles based on these properties.
334 *
335 * @see OsgiBoot#doStartBundles(Map)
336 */
337 public void startBundles(Map<String, String> properties) {
338 Map<String, String> map = new TreeMap<>();
339 // first use properties
340 if (properties != null) {
341 for (String key : properties.keySet()) {
342 String property = key;
343 if (property.startsWith(PROP_ARGEO_OSGI_START)) {
344 map.put(property, properties.get(property));
345 }
346 }
347 }
348 // then try all start level until a maximum
349 int maxStartLevel = Integer.parseInt(getProperty(PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
350 for (int i = 1; i <= maxStartLevel; i++) {
351 String key = PROP_ARGEO_OSGI_START + "." + i;
352 String value = getProperty(key);
353 if (value != null)
354 map.put(key, value);
355
356 }
357 // finally, override with system properties
358 for (Object key : System.getProperties().keySet()) {
359 if (key.toString().startsWith(PROP_ARGEO_OSGI_START)) {
360 map.put(key.toString(), System.getProperty(key.toString()));
361 }
362 }
363 // start
364 doStartBundles(map);
365 }
366
367 @Deprecated
368 public void startBundles(Properties properties) {
369 Map<String, String> map = new TreeMap<>();
370 // first use properties
371 if (properties != null) {
372 for (Object key : properties.keySet()) {
373 String property = key.toString();
374 if (property.startsWith(PROP_ARGEO_OSGI_START)) {
375 map.put(property, properties.get(property).toString());
376 }
377 }
378 }
379 startBundles(map);
380 }
381
382 /** Start bundle based on keys starting with {@link #PROP_ARGEO_OSGI_START}. */
383 protected void doStartBundles(Map<String, String> properties) {
384 FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
385
386 // default and active start levels from System properties
387 Integer defaultStartLevel = Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
388 Integer activeStartLevel = Integer.parseInt(getProperty(PROP_OSGI_STARTLEVEL, "6"));
389
390 SortedMap<Integer, List<String>> startLevels = new TreeMap<Integer, List<String>>();
391 computeStartLevels(startLevels, properties, defaultStartLevel);
392 // inverts the map for the time being, TODO optimise
393 Map<String, Integer> bundleStartLevels = new HashMap<>();
394 for (Integer level : startLevels.keySet()) {
395 for (String bsn : startLevels.get(level))
396 bundleStartLevels.put(bsn, level);
397 }
398 for (Bundle bundle : bundleContext.getBundles()) {
399 String bsn = bundle.getSymbolicName();
400 if (bundleStartLevels.containsKey(bsn)) {
401 BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
402 Integer level = bundleStartLevels.get(bsn);
403 if (bundleStartLevel.getStartLevel() != level || !bundleStartLevel.isPersistentlyStarted()) {
404 bundleStartLevel.setStartLevel(level);
405 try {
406 bundle.start();
407 } catch (BundleException e) {
408 OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
409 }
410 if (OsgiBootUtils.isDebug())
411 OsgiBootUtils.debug(bsn + " starts at level " + level);
412 }
413 }
414 }
415 frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
416 if (OsgiBootUtils.isDebug())
417 OsgiBootUtils.debug("Framework event: " + event);
418 int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
419 int startLevel = frameworkStartLevel.getStartLevel();
420 OsgiBootUtils.debug("Framework start level: " + startLevel + " (initial: " + initialStartLevel + ")");
421 });
422 }
423
424 private static void computeStartLevels(SortedMap<Integer, List<String>> startLevels, Map<String, String> properties,
425 Integer defaultStartLevel) {
426
427 // default (and previously, only behaviour)
428 appendToStartLevels(startLevels, defaultStartLevel, properties.getOrDefault(PROP_ARGEO_OSGI_START, ""));
429
430 // list argeo.osgi.start.* system properties
431 Iterator<String> keys = properties.keySet().iterator();
432 final String prefix = PROP_ARGEO_OSGI_START + ".";
433 while (keys.hasNext()) {
434 String key = keys.next();
435 if (key.startsWith(prefix)) {
436 Integer startLevel;
437 String suffix = key.substring(prefix.length());
438 String[] tokens = suffix.split("\\.");
439 if (tokens.length > 0 && !tokens[0].trim().equals(""))
440 try {
441 // first token is start level
442 startLevel = Integer.parseInt(tokens[0]);
443 } catch (NumberFormatException e) {
444 startLevel = defaultStartLevel;
445 }
446 else
447 startLevel = defaultStartLevel;
448
449 // append bundle names
450 String bundleNames = properties.get(key);
451 appendToStartLevels(startLevels, startLevel, bundleNames);
452 }
453 }
454 }
455
456 /** Append a comma-separated list of bundles to the start levels. */
457 private static void appendToStartLevels(SortedMap<Integer, List<String>> startLevels, Integer startLevel,
458 String str) {
459 if (str == null || str.trim().equals(""))
460 return;
461
462 if (!startLevels.containsKey(startLevel))
463 startLevels.put(startLevel, new ArrayList<String>());
464 String[] bundleNames = str.split(",");
465 for (int i = 0; i < bundleNames.length; i++) {
466 if (bundleNames[i] != null && !bundleNames[i].trim().equals(""))
467 (startLevels.get(startLevel)).add(bundleNames[i]);
468 }
469 }
470
471 // /**
472 // * Start the provided list of bundles
473 // *
474 // * @return whether all bundles are now in active state
475 // * @deprecated
476 // */
477 // @Deprecated
478 // public boolean startBundles(List<String> bundlesToStart) {
479 // if (bundlesToStart.size() == 0)
480 // return true;
481 //
482 // // used to monitor ACTIVE states
483 // List<Bundle> startedBundles = new ArrayList<Bundle>();
484 // // used to log the bundles not found
485 // List<String> notFoundBundles = new ArrayList<String>(bundlesToStart);
486 //
487 // Bundle[] bundles = bundleContext.getBundles();
488 // long startBegin = System.currentTimeMillis();
489 // for (int i = 0; i < bundles.length; i++) {
490 // Bundle bundle = bundles[i];
491 // String symbolicName = bundle.getSymbolicName();
492 // if (bundlesToStart.contains(symbolicName))
493 // try {
494 // try {
495 // bundle.start();
496 // if (OsgiBootUtils.isDebug())
497 // debug("Bundle " + symbolicName + " started");
498 // } catch (Exception e) {
499 // OsgiBootUtils.warn("Start of bundle " + symbolicName + " failed because of " + e
500 // + ", maybe bundle is not yet resolved," + " waiting and trying again.");
501 // waitForBundleResolvedOrActive(startBegin, bundle);
502 // bundle.start();
503 // startedBundles.add(bundle);
504 // }
505 // notFoundBundles.remove(symbolicName);
506 // } catch (Exception e) {
507 // OsgiBootUtils.warn("Bundle " + symbolicName + " cannot be started: " + e.getMessage());
508 // if (OsgiBootUtils.isDebug())
509 // e.printStackTrace();
510 // // was found even if start failed
511 // notFoundBundles.remove(symbolicName);
512 // }
513 // }
514 //
515 // for (int i = 0; i < notFoundBundles.size(); i++)
516 // OsgiBootUtils.warn("Bundle '" + notFoundBundles.get(i) + "' not started because it was not found.");
517 //
518 // // monitors that all bundles are started
519 // long beginMonitor = System.currentTimeMillis();
520 // boolean allStarted = !(startedBundles.size() > 0);
521 // List<String> notStarted = new ArrayList<String>();
522 // while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) {
523 // notStarted = new ArrayList<String>();
524 // allStarted = true;
525 // for (int i = 0; i < startedBundles.size(); i++) {
526 // Bundle bundle = (Bundle) startedBundles.get(i);
527 // // TODO check behaviour of lazs bundles
528 // if (bundle.getState() != Bundle.ACTIVE) {
529 // allStarted = false;
530 // notStarted.add(bundle.getSymbolicName());
531 // }
532 // }
533 // try {
534 // Thread.sleep(100);
535 // } catch (InterruptedException e) {
536 // // silent
537 // }
538 // }
539 // long duration = System.currentTimeMillis() - beginMonitor;
540 //
541 // if (!allStarted)
542 // for (int i = 0; i < notStarted.size(); i++)
543 // OsgiBootUtils.warn("Bundle '" + notStarted.get(i) + "' not ACTIVE after " + (duration / 1000) + "s");
544 //
545 // return allStarted;
546 // }
547
548 // /** Waits for a bundle to become active or resolved */
549 // @Deprecated
550 // private void waitForBundleResolvedOrActive(long startBegin, Bundle bundle) throws Exception {
551 // int originalState = bundle.getState();
552 // if ((originalState == Bundle.RESOLVED) || (originalState == Bundle.ACTIVE))
553 // return;
554 //
555 // String originalStateStr = OsgiBootUtils.stateAsString(originalState);
556 //
557 // int currentState = bundle.getState();
558 // while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
559 // long now = System.currentTimeMillis();
560 // if ((now - startBegin) > defaultTimeout * 10)
561 // throw new Exception("Bundle " + bundle.getSymbolicName() + " was not RESOLVED or ACTIVE after "
562 // + (now - startBegin) + "ms (originalState=" + originalStateStr + ", currentState="
563 // + OsgiBootUtils.stateAsString(currentState) + ")");
564 //
565 // try {
566 // Thread.sleep(100l);
567 // } catch (InterruptedException e) {
568 // // silent
569 // }
570 // currentState = bundle.getState();
571 // }
572 // }
573
574 /*
575 * BUNDLE PATTERNS INSTALLATION
576 */
577 /**
578 * Computes a list of URLs based on Ant-like include/exclude patterns defined by
579 * ${argeo.osgi.bundles} with the following format:<br>
580 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
581 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
582 * without a new line causes a '.' to be appended with unexpected side effects.
583 */
584 public List<String> getBundlesUrls() {
585 String bundlePatterns = getProperty(PROP_ARGEO_OSGI_BUNDLES);
586 return getBundlesUrls(bundlePatterns);
587 }
588
589 /**
590 * Compute a list of URLs to install based on the provided patterns, with
591 * default base url
592 */
593 public List<String> getBundlesUrls(String bundlePatterns) {
594 String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL, DEFAULT_BASE_URL);
595 return getBundlesUrls(baseUrl, bundlePatterns);
596 }
597
598 /** Implements the path matching logic */
599 @Deprecated
600 public List<String> getBundlesUrls(String baseUrl, String bundlePatterns) {
601 List<String> urls = new ArrayList<String>();
602 if (bundlePatterns == null)
603 return urls;
604
605 // bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
606 if (OsgiBootUtils.isDebug())
607 debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
608
609 StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
610 List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
611 while (st.hasMoreTokens()) {
612 String token = st.nextToken();
613 if (new File(token).exists()) {
614 String url = locationToUrl(baseUrl, token);
615 urls.add(url);
616 } else
617 bundlesSets.add(new BundlesSet(token));
618 }
619
620 // find included
621 List<String> included = new ArrayList<String>();
622 // PathMatcher matcher = new AntPathMatcher();
623 for (int i = 0; i < bundlesSets.size(); i++) {
624 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
625 for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
626 String pattern = (String) bundlesSet.getIncludes().get(j);
627 match(included, bundlesSet.getDir(), null, pattern);
628 }
629 }
630
631 // find excluded
632 List<String> excluded = new ArrayList<String>();
633 for (int i = 0; i < bundlesSets.size(); i++) {
634 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
635 for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
636 String pattern = (String) bundlesSet.getExcludes().get(j);
637 match(excluded, bundlesSet.getDir(), null, pattern);
638 }
639 }
640
641 // construct list
642 for (int i = 0; i < included.size(); i++) {
643 String fullPath = (String) included.get(i);
644 if (!excluded.contains(fullPath))
645 urls.add(locationToUrl(baseUrl, fullPath));
646 }
647
648 return urls;
649 }
650
651 /*
652 * DISTRIBUTION JAR INSTALLATION
653 */
654 public List<String> getDistributionUrls() {
655 String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL);
656 String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL);
657 return getDistributionUrls(distributionUrl, baseUrl);
658 }
659
660 public List<String> getDistributionUrls(String distributionUrl, String baseUrl) {
661 List<String> urls = new ArrayList<String>();
662 if (distributionUrl == null)
663 return urls;
664
665 DistributionBundle distributionBundle;
666 if (distributionUrl.startsWith("http") || distributionUrl.startsWith("file")) {
667 distributionBundle = new DistributionBundle(distributionUrl);
668 if (baseUrl != null)
669 distributionBundle.setBaseUrl(baseUrl);
670 } else {
671 // relative url
672 if (baseUrl == null) {
673 baseUrl = localCache;
674 }
675
676 if (distributionUrl.contains(":")) {
677 // TODO make it safer
678 String[] parts = distributionUrl.trim().split(":");
679 String[] categoryParts = parts[0].split("\\.");
680 String artifactId = parts[1];
681 String version = parts[2];
682 StringBuilder sb = new StringBuilder();
683 for (String categoryPart : categoryParts) {
684 sb.append(categoryPart).append('/');
685 }
686 sb.append(artifactId).append('/');
687 sb.append(version).append('/');
688 sb.append(artifactId).append('-').append(version).append(".jar");
689 distributionUrl = sb.toString();
690 }
691
692 distributionBundle = new DistributionBundle(baseUrl, distributionUrl, localCache);
693 }
694 // if (baseUrl != null && !(distributionUrl.startsWith("http") ||
695 // distributionUrl.startsWith("file"))) {
696 // // relative url
697 // distributionBundle = new DistributionBundle(baseUrl, distributionUrl,
698 // localCache);
699 // } else {
700 // distributionBundle = new DistributionBundle(distributionUrl);
701 // if (baseUrl != null)
702 // distributionBundle.setBaseUrl(baseUrl);
703 // }
704 distributionBundle.processUrl();
705 return distributionBundle.listUrls();
706 }
707
708 /*
709 * HIGH LEVEL UTILITIES
710 */
711 /** Actually performs the matching logic. */
712 protected void match(List<String> matched, String base, String currentPath, String pattern) {
713 if (currentPath == null) {
714 // Init
715 File baseDir = new File(base.replace('/', File.separatorChar));
716 File[] files = baseDir.listFiles();
717
718 if (files == null) {
719 if (OsgiBootUtils.isDebug())
720 OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
721 + ", isDirectory=" + baseDir.isDirectory());
722 return;
723 }
724
725 for (int i = 0; i < files.length; i++)
726 match(matched, base, files[i].getName(), pattern);
727 } else {
728 PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);
729 String fullPath = base + '/' + currentPath;
730 if (matched.contains(fullPath))
731 return;// don't try deeper if already matched
732
733 boolean ok = matcher.matches(Paths.get(currentPath));
734 // if (debug)
735 // debug(currentPath + " " + (ok ? "" : " not ")
736 // + " matched with " + pattern);
737 if (ok) {
738 matched.add(fullPath);
739 return;
740 } else {
741 String newFullPath = relativeToFullPath(base, currentPath);
742 File newFile = new File(newFullPath);
743 File[] files = newFile.listFiles();
744 if (files != null) {
745 for (int i = 0; i < files.length; i++) {
746 String newCurrentPath = currentPath + '/' + files[i].getName();
747 if (files[i].isDirectory()) {
748 // if (matcher.matchStart(pattern, newCurrentPath)) {
749 // FIXME recurse only if start matches ?
750 match(matched, base, newCurrentPath, pattern);
751 // } else {
752 // if (OsgiBootUtils.isDebug())
753 // debug(newCurrentPath + " does not start match with " + pattern);
754 //
755 // }
756 } else {
757 boolean nonDirectoryOk = matcher.matches(Paths.get(newCurrentPath));
758 if (OsgiBootUtils.isDebug())
759 debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
760 if (nonDirectoryOk)
761 matched.add(relativeToFullPath(base, newCurrentPath));
762 }
763 }
764 }
765 }
766 }
767 }
768
769 /*
770 * LOW LEVEL UTILITIES
771 */
772 /**
773 * The bundles already installed. Key is location (String) , value is a
774 * {@link Bundle}
775 */
776 public Map<String, Bundle> getBundlesByLocation() {
777 Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
778 Bundle[] bundles = bundleContext.getBundles();
779 for (int i = 0; i < bundles.length; i++) {
780 installedBundles.put(bundles[i].getLocation(), bundles[i]);
781 }
782 return installedBundles;
783 }
784
785 /**
786 * The bundles already installed. Key is symbolic name (String) , value is a
787 * {@link Bundle}
788 */
789 public Map<String, Bundle> getBundlesBySymbolicName() {
790 Map<String, Bundle> namedBundles = new HashMap<String, Bundle>();
791 Bundle[] bundles = bundleContext.getBundles();
792 for (int i = 0; i < bundles.length; i++) {
793 namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
794 }
795 return namedBundles;
796 }
797
798 /** Creates an URL from a location */
799 protected String locationToUrl(String baseUrl, String location) {
800 return baseUrl + location;
801 }
802
803 /** Transforms a relative path in a full system path. */
804 protected String relativeToFullPath(String basePath, String relativePath) {
805 return (basePath + '/' + relativePath).replace('/', File.separatorChar);
806 }
807
808 private void refreshFramework() {
809 Bundle systemBundle = bundleContext.getBundle(0);
810 FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
811 frameworkWiring.refreshBundles(null);
812 }
813
814 /**
815 * Gets a property value
816 *
817 * @return null when defaultValue is ""
818 */
819 public String getProperty(String name, String defaultValue) {
820 String value = bundleContext.getProperty(name);
821 if (value == null)
822 return defaultValue; // may be null
823 else
824 return value;
825 }
826
827 public String getProperty(String name) {
828 return getProperty(name, null);
829 }
830
831 /*
832 * BEAN METHODS
833 */
834
835 // public boolean getDebug() {
836 // return OsgiBootUtils.debug;
837 // }
838
839 // public void setDebug(boolean debug) {
840 // this.debug = debug;
841 // }
842
843 public BundleContext getBundleContext() {
844 return bundleContext;
845 }
846
847 // public String getLocalCache() {
848 // return localCache;
849 // }
850
851 // public void setDefaultTimeout(long defaultTimeout) {
852 // this.defaultTimeout = defaultTimeout;
853 // }
854
855 // public boolean isExcludeSvn() {
856 // return excludeSvn;
857 // }
858 //
859 // public void setExcludeSvn(boolean excludeSvn) {
860 // this.excludeSvn = excludeSvn;
861 // }
862
863 /*
864 * INTERNAL CLASSES
865 */
866
867 }