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