]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java
Fix deployment versions
[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 if (source.trim().equals(A2Source.DEFAULT_A2_REFERENCE_URI)) {
108 if (Files.exists(homePath))
109 provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + "://" + homePath.toString()
110 + "/.local/share/a2" + queryPart);
111 provisioningManager
112 .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/share/a2" + queryPart);
113 provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/share/a2" + queryPart);
114 } else {
115 provisioningManager.registerSource(source + queryPart);
116 }
117 }
118 }
119 }
120
121 ProvisioningManager getProvisioningManager() {
122 return provisioningManager;
123 }
124
125 /*
126 * HIGH-LEVEL METHODS
127 */
128 /**
129 * Bootstraps the OSGi runtime using these properties, which MUST be consistent
130 * with {@link BundleContext#getProperty(String)}. If these properties are
131 * <code>null</code>, system properties are used instead.
132 */
133 public void bootstrap(Map<String, String> properties) {
134 try {
135 long begin = System.currentTimeMillis();
136
137 // notify start
138 String osgiInstancePath = getProperty(PROP_OSGI_INSTANCE_AREA);
139 String osgiConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
140 String osgiSharedConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
141 OsgiBootUtils.info("OSGi bootstrap starting" //
142 + (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
143 + (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
144 + (osgiSharedConfigurationPath != null ? " config: " + osgiSharedConfigurationPath + "" : "") //
145 );
146
147 // legacy install bundles
148 installUrls(getBundlesUrls());
149 installUrls(getDistributionUrls());
150
151 // A2 install bundles
152 provisioningManager.install(null);
153
154 // Make sure fragments are properly considered by refreshing
155 refreshFramework();
156
157 // start bundles
158 // if (properties != null && !Boolean.parseBoolean(properties.get(PROP_OSGI_USE_SYSTEM_PROPERTIES)))
159 startBundles(properties);
160 // else
161 // startBundles();
162
163 // complete
164 long duration = System.currentTimeMillis() - begin;
165 OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
166 + duration + "ms), " + bundleContext.getBundles().length + " bundles");
167 } catch (RuntimeException e) {
168 OsgiBootUtils.error("OSGi bootstrap FAILED", e);
169 throw e;
170 }
171
172 // diagnostics
173 if (OsgiBootUtils.isDebug()) {
174 OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
175 diagnostics.checkUnresolved();
176 Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
177 if (duplicatePackages.size() > 0) {
178 OsgiBootUtils.info("Packages exported twice:");
179 Iterator<String> it = duplicatePackages.keySet().iterator();
180 while (it.hasNext()) {
181 String pkgName = it.next();
182 OsgiBootUtils.info(pkgName);
183 Set<String> bdles = duplicatePackages.get(pkgName);
184 Iterator<String> bdlesIt = bdles.iterator();
185 while (bdlesIt.hasNext())
186 OsgiBootUtils.info(" " + bdlesIt.next());
187 }
188 }
189 }
190 System.out.println();
191 }
192
193 /**
194 * Calls {@link #bootstrap(Map)} with <code>null</code>.
195 *
196 * @see #bootstrap(Map)
197 */
198 @Deprecated
199 public void bootstrap() {
200 bootstrap(null);
201 }
202
203 public void update() {
204 provisioningManager.update();
205 }
206
207 /*
208 * INSTALLATION
209 */
210 /** Install a single url. Convenience method. */
211 public Bundle installUrl(String url) {
212 List<String> urls = new ArrayList<String>();
213 urls.add(url);
214 installUrls(urls);
215 return (Bundle) getBundlesByLocation().get(url);
216 }
217
218 /** Install the bundles at this URL list. */
219 public void installUrls(List<String> urls) {
220 Map<String, Bundle> installedBundles = getBundlesByLocation();
221 for (int i = 0; i < urls.size(); i++) {
222 String url = (String) urls.get(i);
223 installUrl(url, installedBundles);
224 }
225 // refreshFramework();
226 }
227
228 /** Actually install the provided URL */
229 protected void installUrl(String url, Map<String, Bundle> installedBundles) {
230 try {
231 if (installedBundles.containsKey(url)) {
232 Bundle bundle = (Bundle) installedBundles.get(url);
233 if (OsgiBootUtils.isDebug())
234 debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
235 } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
236 || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
237 if (OsgiBootUtils.isDebug())
238 warn("Skip " + url);
239 return;
240 } else {
241 Bundle bundle = bundleContext.installBundle(url);
242 if (url.startsWith("http"))
243 OsgiBootUtils
244 .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
245 else if (OsgiBootUtils.isDebug())
246 OsgiBootUtils.debug(
247 "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
248 assert bundle.getSymbolicName() != null;
249 // uninstall previous versions
250 bundles: for (Bundle b : bundleContext.getBundles()) {
251 if (b.getSymbolicName() == null)
252 continue bundles;
253 if (bundle.getSymbolicName().equals(b.getSymbolicName())) {
254 Version bundleV = bundle.getVersion();
255 Version bV = b.getVersion();
256 if (bV == null)
257 continue bundles;
258 if (bundleV.getMajor() == bV.getMajor() && bundleV.getMinor() == bV.getMinor()) {
259 if (bundleV.getMicro() > bV.getMicro()) {
260 // uninstall older bundles
261 b.uninstall();
262 OsgiBootUtils.debug("Uninstalled " + b);
263 } else if (bundleV.getMicro() < bV.getMicro()) {
264 // uninstall just installed bundle if newer
265 bundle.uninstall();
266 OsgiBootUtils.debug("Uninstalled " + bundle);
267 break bundles;
268 } else {
269 // uninstall any other with same major/minor
270 if (!bundleV.getQualifier().equals(bV.getQualifier())) {
271 b.uninstall();
272 OsgiBootUtils.debug("Uninstalled " + b);
273 }
274 }
275 }
276 }
277 }
278 }
279 } catch (BundleException e) {
280 final String ALREADY_INSTALLED = "is already installed";
281 String message = e.getMessage();
282 if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
283 || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
284 && message.contains(ALREADY_INSTALLED)) {
285 // silent, in order to avoid warnings: we know that both
286 // have already been installed...
287 } else {
288 if (message.contains(ALREADY_INSTALLED)) {
289 if (OsgiBootUtils.isDebug())
290 OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
291 } else
292 OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
293 }
294 if (OsgiBootUtils.isDebug() && !message.contains(ALREADY_INSTALLED))
295 e.printStackTrace();
296 }
297 }
298
299 /*
300 * START
301 */
302
303 /**
304 * Start bundles based on these properties.
305 *
306 * @see OsgiBoot#doStartBundles(Map)
307 */
308 public void startBundles(Map<String, String> properties) {
309 Map<String, String> map = new TreeMap<>();
310 // first use properties
311 if (properties != null) {
312 for (String key : properties.keySet()) {
313 String property = key;
314 if (property.startsWith(PROP_ARGEO_OSGI_START)) {
315 map.put(property, properties.get(property));
316 }
317 }
318 }
319 // then try all start level until a maximum
320 int maxStartLevel = Integer.parseInt(getProperty(PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
321 for (int i = 1; i <= maxStartLevel; i++) {
322 String key = PROP_ARGEO_OSGI_START + "." + i;
323 String value = getProperty(key);
324 if (value != null)
325 map.put(key, value);
326
327 }
328 // finally, override with system properties
329 for (Object key : System.getProperties().keySet()) {
330 if (key.toString().startsWith(PROP_ARGEO_OSGI_START)) {
331 map.put(key.toString(), System.getProperty(key.toString()));
332 }
333 }
334 // start
335 doStartBundles(map);
336 }
337
338 @Deprecated
339 public void startBundles(Properties properties) {
340 Map<String, String> map = new TreeMap<>();
341 // first use properties
342 if (properties != null) {
343 for (Object key : properties.keySet()) {
344 String property = key.toString();
345 if (property.startsWith(PROP_ARGEO_OSGI_START)) {
346 map.put(property, properties.get(property).toString());
347 }
348 }
349 }
350 startBundles(map);
351 }
352
353 /** Start bundle based on keys starting with {@link #PROP_ARGEO_OSGI_START}. */
354 protected void doStartBundles(Map<String, String> properties) {
355 FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
356
357 // default and active start levels from System properties
358 int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
359 int defaultStartLevel = Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
360 int activeStartLevel = Integer.parseInt(getProperty(PROP_OSGI_STARTLEVEL, "6"));
361 if (OsgiBootUtils.isDebug()) {
362 OsgiBootUtils.debug("OSGi default start level: "
363 + getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "<not set>") + ", using " + defaultStartLevel);
364 OsgiBootUtils.debug("OSGi active start level: " + getProperty(PROP_OSGI_STARTLEVEL, "<not set>")
365 + ", using " + activeStartLevel);
366 OsgiBootUtils.debug("Framework start level: " + frameworkStartLevel.getStartLevel() + " (initial: "
367 + initialStartLevel + ")");
368 }
369
370 SortedMap<Integer, List<String>> startLevels = new TreeMap<Integer, List<String>>();
371 computeStartLevels(startLevels, properties, defaultStartLevel);
372 // inverts the map for the time being, TODO optimise
373 Map<String, Integer> bundleStartLevels = new HashMap<>();
374 for (Integer level : startLevels.keySet()) {
375 for (String bsn : startLevels.get(level))
376 bundleStartLevels.put(bsn, level);
377 }
378 for (Bundle bundle : bundleContext.getBundles()) {
379 String bsn = bundle.getSymbolicName();
380 if (bundleStartLevels.containsKey(bsn)) {
381 BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
382 Integer level = bundleStartLevels.get(bsn);
383 if (bundleStartLevel.getStartLevel() != level || !bundleStartLevel.isPersistentlyStarted()) {
384 bundleStartLevel.setStartLevel(level);
385 try {
386 bundle.start();
387 } catch (BundleException e) {
388 OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
389 }
390 if (OsgiBootUtils.isDebug())
391 OsgiBootUtils.debug(bsn + " starts at level " + level);
392 }
393 }
394 }
395
396 if (OsgiBootUtils.isDebug())
397 OsgiBootUtils.debug("About to set framework start level to " + activeStartLevel + " ...");
398
399 frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
400 if (event.getType() == FrameworkEvent.ERROR) {
401 OsgiBootUtils.error("Start sequence failed", event.getThrowable());
402 } else {
403 if (OsgiBootUtils.isDebug())
404 OsgiBootUtils.debug("Framework started at level " + frameworkStartLevel.getStartLevel());
405 }
406 });
407
408 // // Start the framework level after level
409 // int currentStartLevel = frameworkStartLevel.getStartLevel();
410 // stages: for (int stage = currentStartLevel + 1; stage <= activeStartLevel; stage++) {
411 // if (OsgiBootUtils.isDebug())
412 // OsgiBootUtils.debug("Starting stage " + stage + "...");
413 // final int nextStage = stage;
414 // final CompletableFuture<FrameworkEvent> stageCompleted = new CompletableFuture<>();
415 // frameworkStartLevel.setStartLevel(nextStage, (FrameworkEvent event) -> {
416 // stageCompleted.complete(event);
417 // });
418 // FrameworkEvent event;
419 // try {
420 // event = stageCompleted.get();
421 // } catch (InterruptedException | ExecutionException e) {
422 // throw new IllegalStateException("Cannot continue start", e);
423 // }
424 // if (event.getThrowable() != null) {
425 // OsgiBootUtils.error("Stage " + nextStage + " failed, aborting start.", event.getThrowable());
426 // break stages;
427 // }
428 // }
429 }
430
431 private static void computeStartLevels(SortedMap<Integer, List<String>> startLevels, Map<String, String> properties,
432 Integer defaultStartLevel) {
433
434 // default (and previously, only behaviour)
435 appendToStartLevels(startLevels, defaultStartLevel, properties.getOrDefault(PROP_ARGEO_OSGI_START, ""));
436
437 // list argeo.osgi.start.* system properties
438 Iterator<String> keys = properties.keySet().iterator();
439 final String prefix = PROP_ARGEO_OSGI_START + ".";
440 while (keys.hasNext()) {
441 String key = keys.next();
442 if (key.startsWith(prefix)) {
443 Integer startLevel;
444 String suffix = key.substring(prefix.length());
445 String[] tokens = suffix.split("\\.");
446 if (tokens.length > 0 && !tokens[0].trim().equals(""))
447 try {
448 // first token is start level
449 startLevel = Integer.parseInt(tokens[0]);
450 } catch (NumberFormatException e) {
451 startLevel = defaultStartLevel;
452 }
453 else
454 startLevel = defaultStartLevel;
455
456 // append bundle names
457 String bundleNames = properties.get(key);
458 appendToStartLevels(startLevels, startLevel, bundleNames);
459 }
460 }
461 }
462
463 /** Append a comma-separated list of bundles to the start levels. */
464 private static void appendToStartLevels(SortedMap<Integer, List<String>> startLevels, Integer startLevel,
465 String str) {
466 if (str == null || str.trim().equals(""))
467 return;
468
469 if (!startLevels.containsKey(startLevel))
470 startLevels.put(startLevel, new ArrayList<String>());
471 String[] bundleNames = str.split(",");
472 for (int i = 0; i < bundleNames.length; i++) {
473 if (bundleNames[i] != null && !bundleNames[i].trim().equals(""))
474 (startLevels.get(startLevel)).add(bundleNames[i]);
475 }
476 }
477
478 /*
479 * BUNDLE PATTERNS INSTALLATION
480 */
481 /**
482 * Computes a list of URLs based on Ant-like include/exclude patterns defined by
483 * ${argeo.osgi.bundles} with the following format:<br>
484 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
485 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
486 * without a new line causes a '.' to be appended with unexpected side effects.
487 */
488 public List<String> getBundlesUrls() {
489 String bundlePatterns = getProperty(PROP_ARGEO_OSGI_BUNDLES);
490 return getBundlesUrls(bundlePatterns);
491 }
492
493 /**
494 * Compute a list of URLs to install based on the provided patterns, with
495 * default base url
496 */
497 public List<String> getBundlesUrls(String bundlePatterns) {
498 String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL, DEFAULT_BASE_URL);
499 return getBundlesUrls(baseUrl, bundlePatterns);
500 }
501
502 /** Implements the path matching logic */
503 @Deprecated
504 public List<String> getBundlesUrls(String baseUrl, String bundlePatterns) {
505 List<String> urls = new ArrayList<String>();
506 if (bundlePatterns == null)
507 return urls;
508
509 // bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
510 if (OsgiBootUtils.isDebug())
511 debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
512
513 StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
514 List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
515 while (st.hasMoreTokens()) {
516 String token = st.nextToken();
517 if (new File(token).exists()) {
518 String url = locationToUrl(baseUrl, token);
519 urls.add(url);
520 } else
521 bundlesSets.add(new BundlesSet(token));
522 }
523
524 // find included
525 List<String> included = new ArrayList<String>();
526 // PathMatcher matcher = new AntPathMatcher();
527 for (int i = 0; i < bundlesSets.size(); i++) {
528 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
529 for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
530 String pattern = (String) bundlesSet.getIncludes().get(j);
531 match(included, bundlesSet.getDir(), null, pattern);
532 }
533 }
534
535 // find excluded
536 List<String> excluded = new ArrayList<String>();
537 for (int i = 0; i < bundlesSets.size(); i++) {
538 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
539 for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
540 String pattern = (String) bundlesSet.getExcludes().get(j);
541 match(excluded, bundlesSet.getDir(), null, pattern);
542 }
543 }
544
545 // construct list
546 for (int i = 0; i < included.size(); i++) {
547 String fullPath = (String) included.get(i);
548 if (!excluded.contains(fullPath))
549 urls.add(locationToUrl(baseUrl, fullPath));
550 }
551
552 return urls;
553 }
554
555 /*
556 * DISTRIBUTION JAR INSTALLATION
557 */
558 public List<String> getDistributionUrls() {
559 String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL);
560 String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL);
561 return getDistributionUrls(distributionUrl, baseUrl);
562 }
563
564 public List<String> getDistributionUrls(String distributionUrl, String baseUrl) {
565 List<String> urls = new ArrayList<String>();
566 if (distributionUrl == null)
567 return urls;
568
569 DistributionBundle distributionBundle;
570 if (distributionUrl.startsWith("http") || distributionUrl.startsWith("file")) {
571 distributionBundle = new DistributionBundle(distributionUrl);
572 if (baseUrl != null)
573 distributionBundle.setBaseUrl(baseUrl);
574 } else {
575 // relative url
576 if (baseUrl == null) {
577 baseUrl = localCache;
578 }
579
580 if (distributionUrl.contains(":")) {
581 // TODO make it safer
582 String[] parts = distributionUrl.trim().split(":");
583 String[] categoryParts = parts[0].split("\\.");
584 String artifactId = parts[1];
585 String version = parts[2];
586 StringBuilder sb = new StringBuilder();
587 for (String categoryPart : categoryParts) {
588 sb.append(categoryPart).append('/');
589 }
590 sb.append(artifactId).append('/');
591 sb.append(version).append('/');
592 sb.append(artifactId).append('-').append(version).append(".jar");
593 distributionUrl = sb.toString();
594 }
595
596 distributionBundle = new DistributionBundle(baseUrl, distributionUrl, localCache);
597 }
598 distributionBundle.processUrl();
599 return distributionBundle.listUrls();
600 }
601
602 /*
603 * HIGH LEVEL UTILITIES
604 */
605 /** Actually performs the matching logic. */
606 protected void match(List<String> matched, String base, String currentPath, String pattern) {
607 if (currentPath == null) {
608 // Init
609 File baseDir = new File(base.replace('/', File.separatorChar));
610 File[] files = baseDir.listFiles();
611
612 if (files == null) {
613 if (OsgiBootUtils.isDebug())
614 OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
615 + ", isDirectory=" + baseDir.isDirectory());
616 return;
617 }
618
619 for (int i = 0; i < files.length; i++)
620 match(matched, base, files[i].getName(), pattern);
621 } else {
622 PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);
623 String fullPath = base + '/' + currentPath;
624 if (matched.contains(fullPath))
625 return;// don't try deeper if already matched
626
627 boolean ok = matcher.matches(Paths.get(currentPath));
628 // if (debug)
629 // debug(currentPath + " " + (ok ? "" : " not ")
630 // + " matched with " + pattern);
631 if (ok) {
632 matched.add(fullPath);
633 return;
634 } else {
635 String newFullPath = relativeToFullPath(base, currentPath);
636 File newFile = new File(newFullPath);
637 File[] files = newFile.listFiles();
638 if (files != null) {
639 for (int i = 0; i < files.length; i++) {
640 String newCurrentPath = currentPath + '/' + files[i].getName();
641 if (files[i].isDirectory()) {
642 // if (matcher.matchStart(pattern, newCurrentPath)) {
643 // FIXME recurse only if start matches ?
644 match(matched, base, newCurrentPath, pattern);
645 // } else {
646 // if (OsgiBootUtils.isDebug())
647 // debug(newCurrentPath + " does not start match with " + pattern);
648 //
649 // }
650 } else {
651 boolean nonDirectoryOk = matcher.matches(Paths.get(newCurrentPath));
652 if (OsgiBootUtils.isDebug())
653 debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
654 if (nonDirectoryOk)
655 matched.add(relativeToFullPath(base, newCurrentPath));
656 }
657 }
658 }
659 }
660 }
661 }
662
663 /*
664 * LOW LEVEL UTILITIES
665 */
666 /**
667 * The bundles already installed. Key is location (String) , value is a
668 * {@link Bundle}
669 */
670 public Map<String, Bundle> getBundlesByLocation() {
671 Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
672 Bundle[] bundles = bundleContext.getBundles();
673 for (int i = 0; i < bundles.length; i++) {
674 installedBundles.put(bundles[i].getLocation(), bundles[i]);
675 }
676 return installedBundles;
677 }
678
679 /**
680 * The bundles already installed. Key is symbolic name (String) , value is a
681 * {@link Bundle}
682 */
683 public Map<String, Bundle> getBundlesBySymbolicName() {
684 Map<String, Bundle> namedBundles = new HashMap<String, Bundle>();
685 Bundle[] bundles = bundleContext.getBundles();
686 for (int i = 0; i < bundles.length; i++) {
687 namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
688 }
689 return namedBundles;
690 }
691
692 /** Creates an URL from a location */
693 protected String locationToUrl(String baseUrl, String location) {
694 return baseUrl + location;
695 }
696
697 /** Transforms a relative path in a full system path. */
698 protected String relativeToFullPath(String basePath, String relativePath) {
699 return (basePath + '/' + relativePath).replace('/', File.separatorChar);
700 }
701
702 private void refreshFramework() {
703 Bundle systemBundle = bundleContext.getBundle(0);
704 FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
705 // TODO deal with refresh breaking native loading (e.g SWT)
706 frameworkWiring.refreshBundles(null);
707 }
708
709 /**
710 * Gets a property value
711 *
712 * @return null when defaultValue is ""
713 */
714 public String getProperty(String name, String defaultValue) {
715 String value = bundleContext.getProperty(name);
716 if (value == null)
717 return defaultValue; // may be null
718 else
719 return value;
720 }
721
722 public String getProperty(String name) {
723 return getProperty(name, null);
724 }
725
726 /*
727 * BEAN METHODS
728 */
729
730 public BundleContext getBundleContext() {
731 return bundleContext;
732 }
733
734 }