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