1 package org
.argeo
.init
.osgi
;
3 import static org
.argeo
.init
.osgi
.OsgiBootUtils
.debug
;
4 import static org
.argeo
.init
.osgi
.OsgiBootUtils
.warn
;
7 import java
.net
.MalformedURLException
;
9 import java
.nio
.file
.FileSystems
;
10 import java
.nio
.file
.Files
;
11 import java
.nio
.file
.Path
;
12 import java
.nio
.file
.PathMatcher
;
13 import java
.nio
.file
.Paths
;
14 import java
.util
.ArrayList
;
15 import java
.util
.HashMap
;
16 import java
.util
.Iterator
;
17 import java
.util
.List
;
19 import java
.util
.Properties
;
21 import java
.util
.SortedMap
;
22 import java
.util
.StringTokenizer
;
23 import java
.util
.TreeMap
;
25 import org
.argeo
.init
.a2
.A2Source
;
26 import org
.argeo
.init
.a2
.ProvisioningManager
;
27 import org
.osgi
.framework
.Bundle
;
28 import org
.osgi
.framework
.BundleContext
;
29 import org
.osgi
.framework
.BundleException
;
30 import org
.osgi
.framework
.FrameworkEvent
;
31 import org
.osgi
.framework
.Version
;
32 import org
.osgi
.framework
.startlevel
.BundleStartLevel
;
33 import org
.osgi
.framework
.startlevel
.FrameworkStartLevel
;
34 import org
.osgi
.framework
.wiring
.FrameworkWiring
;
37 * Basic provisioning of an OSGi runtime via file path patterns and system
38 * properties. The approach is to generate list of URLs based on various
39 * methods, configured via properties.
41 public class OsgiBoot
implements OsgiBootConstants
{
42 public final static String PROP_ARGEO_OSGI_START
= "argeo.osgi.start";
43 public final static String PROP_ARGEO_OSGI_SOURCES
= "argeo.osgi.sources";
45 public final static String PROP_ARGEO_OSGI_BUNDLES
= "argeo.osgi.bundles";
46 public final static String PROP_ARGEO_OSGI_BASE_URL
= "argeo.osgi.baseUrl";
47 public final static String PROP_ARGEO_OSGI_LOCAL_CACHE
= "argeo.osgi.localCache";
48 public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL
= "argeo.osgi.distributionUrl";
51 public final static String PROP_ARGEO_OSGI_BOOT_DEBUG
= "argeo.osgi.boot.debug";
52 // public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN =
53 // "argeo.osgi.boot.excludeSvn";
55 public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE
= "argeo.osgi.boot.systemPropertiesFile";
56 public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS
= "argeo.osgi.boot.appclass";
57 public final static String PROP_ARGEO_OSGI_BOOT_APPARGS
= "argeo.osgi.boot.appargs";
59 public final static String DEFAULT_BASE_URL
= "reference:file:";
60 // public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
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 final static String PROP_OSGI_INSTANCE_AREA
= "osgi.instance.area";
66 final static String PROP_OSGI_CONFIGURATION_AREA
= "osgi.configuration.area";
67 final static String PROP_OSGI_USE_SYSTEM_PROPERTIES
= "osgi.framework.useSystemProperties";
70 public final static String SYMBOLIC_NAME_OSGI_BOOT
= "org.argeo.osgi.boot";
71 public final static String SYMBOLIC_NAME_EQUINOX
= "org.eclipse.osgi";
73 /** Exclude svn metadata implicitely(a bit costly) */
74 // private boolean excludeSvn =
75 // Boolean.valueOf(System.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN,
79 // /** Default is 10s */
81 // private long defaultTimeout = 10000l;
83 private final BundleContext bundleContext
;
84 private final String localCache
;
86 private final ProvisioningManager provisioningManager
;
92 public OsgiBoot(BundleContext bundleContext
) {
93 this.bundleContext
= bundleContext
;
94 Path homePath
= Paths
.get(System
.getProperty("user.home")).toAbsolutePath();
95 String homeUri
= homePath
.toUri().toString();
96 localCache
= getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE
, homeUri
+ ".m2/repository/");
98 provisioningManager
= new ProvisioningManager(bundleContext
);
99 String sources
= getProperty(PROP_ARGEO_OSGI_SOURCES
);
100 if (sources
== null) {
101 provisioningManager
.registerDefaultSource();
103 for (String source
: sources
.split(",")) {
104 if (source
.trim().equals(A2Source
.DEFAULT_A2_URI
)) {
105 int qmIndex
= source
.lastIndexOf('?');
106 String queryPart
= "";
108 queryPart
= source
.substring(qmIndex
);
110 if (Files
.exists(homePath
))
111 provisioningManager
.registerSource(
112 A2Source
.SCHEME_A2
+ "://" + homePath
.toString() + "/.local/share/a2" + queryPart
);
113 provisioningManager
.registerSource(A2Source
.SCHEME_A2
+ ":///usr/local/share/a2" + queryPart
);
114 provisioningManager
.registerSource(A2Source
.SCHEME_A2
+ ":///usr/share/a2" + queryPart
);
116 provisioningManager
.registerSource(source
);
122 ProvisioningManager
getProvisioningManager() {
123 return provisioningManager
;
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.
134 public void bootstrap(Map
<String
, String
> properties
) {
136 long begin
= System
.currentTimeMillis();
138 if (properties
!= null) {
139 for (String property
: properties
.keySet()) {
140 String value
= properties
.get(property
);
141 String bcValue
= bundleContext
.getProperty(property
);
142 if (PROP_OSGI_CONFIGURATION_AREA
.equals(property
) || PROP_OSGI_INSTANCE_AREA
.equals(property
)) {
144 URL uri
= new URL(value
);
145 URL bcUri
= new URL(bcValue
);
146 if (!uri
.equals(bcUri
))
147 throw new IllegalArgumentException("Property " + property
+ "=" + uri
148 + " is inconsistent with bundle context : " + bcUri
);
149 } catch (MalformedURLException e
) {
150 throw new IllegalArgumentException("Malformed property " + property
, e
);
154 if (!value
.equals(bcValue
))
155 throw new IllegalArgumentException("Property " + property
+ "=" + value
156 + " is inconsistent with bundle context : " + bcValue
);
160 String useSystemProperties
= bundleContext
.getProperty(PROP_OSGI_USE_SYSTEM_PROPERTIES
);
161 if (useSystemProperties
== null || !useSystemProperties
.equals("true")) {
162 OsgiBootUtils
.warn("No properties passed but " + PROP_OSGI_USE_SYSTEM_PROPERTIES
+ " is not set.");
167 System
.out
.println();
168 String osgiInstancePath
= bundleContext
.getProperty(PROP_OSGI_INSTANCE_AREA
);
170 .info("OSGi bootstrap starting" + (osgiInstancePath
!= null ?
" (" + osgiInstancePath
+ ")" : ""));
171 installUrls(getBundlesUrls());
172 installUrls(getDistributionUrls());
173 provisioningManager
.install(null);
174 if (properties
!= null)
175 startBundles(properties
);
178 long duration
= System
.currentTimeMillis() - begin
;
179 OsgiBootUtils
.info("OSGi bootstrap completed in " + Math
.round(((double) duration
) / 1000) + "s ("
180 + duration
+ "ms), " + bundleContext
.getBundles().length
+ " bundles");
181 } catch (RuntimeException e
) {
182 OsgiBootUtils
.error("OSGi bootstrap FAILED", e
);
187 if (OsgiBootUtils
.isDebug()) {
188 OsgiBootDiagnostics diagnostics
= new OsgiBootDiagnostics(bundleContext
);
189 diagnostics
.checkUnresolved();
190 Map
<String
, Set
<String
>> duplicatePackages
= diagnostics
.findPackagesExportedTwice();
191 if (duplicatePackages
.size() > 0) {
192 OsgiBootUtils
.info("Packages exported twice:");
193 Iterator
<String
> it
= duplicatePackages
.keySet().iterator();
194 while (it
.hasNext()) {
195 String pkgName
= it
.next();
196 OsgiBootUtils
.info(pkgName
);
197 Set
<String
> bdles
= duplicatePackages
.get(pkgName
);
198 Iterator
<String
> bdlesIt
= bdles
.iterator();
199 while (bdlesIt
.hasNext())
200 OsgiBootUtils
.info(" " + bdlesIt
.next());
204 System
.out
.println();
208 * Calls {@link #bootstrap(Map)} with <code>null</code>.
210 * @see #bootstrap(Map)
213 public void bootstrap() {
217 public void update() {
218 provisioningManager
.update();
224 /** Install a single url. Convenience method. */
225 public Bundle
installUrl(String url
) {
226 List
<String
> urls
= new ArrayList
<String
>();
229 return (Bundle
) getBundlesByLocation().get(url
);
232 /** Install the bundles at this URL list. */
233 public void installUrls(List
<String
> urls
) {
234 Map
<String
, Bundle
> installedBundles
= getBundlesByLocation();
235 for (int i
= 0; i
< urls
.size(); i
++) {
236 String url
= (String
) urls
.get(i
);
237 installUrl(url
, installedBundles
);
242 /** Actually install the provided URL */
243 protected void installUrl(String url
, Map
<String
, Bundle
> installedBundles
) {
245 if (installedBundles
.containsKey(url
)) {
246 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
247 if (OsgiBootUtils
.isDebug())
248 debug("Bundle " + bundle
.getSymbolicName() + " already installed from " + url
);
249 } else if (url
.contains("/" + SYMBOLIC_NAME_EQUINOX
+ "/")
250 || url
.contains("/" + SYMBOLIC_NAME_OSGI_BOOT
+ "/")) {
251 if (OsgiBootUtils
.isDebug())
255 Bundle bundle
= bundleContext
.installBundle(url
);
256 if (url
.startsWith("http"))
258 .info("Installed " + bundle
.getSymbolicName() + "-" + bundle
.getVersion() + " from " + url
);
259 else if (OsgiBootUtils
.isDebug())
261 "Installed " + bundle
.getSymbolicName() + "-" + bundle
.getVersion() + " from " + url
);
262 assert bundle
.getSymbolicName() != null;
263 // uninstall previous versions
264 bundles
: for (Bundle b
: bundleContext
.getBundles()) {
265 if (b
.getSymbolicName() == null)
267 if (bundle
.getSymbolicName().equals(b
.getSymbolicName())) {
268 Version bundleV
= bundle
.getVersion();
269 Version bV
= b
.getVersion();
272 if (bundleV
.getMajor() == bV
.getMajor() && bundleV
.getMinor() == bV
.getMinor()) {
273 if (bundleV
.getMicro() > bV
.getMicro()) {
274 // uninstall older bundles
276 OsgiBootUtils
.debug("Uninstalled " + b
);
277 } else if (bundleV
.getMicro() < bV
.getMicro()) {
278 // uninstall just installed bundle if newer
280 OsgiBootUtils
.debug("Uninstalled " + bundle
);
283 // uninstall any other with same major/minor
284 if (!bundleV
.getQualifier().equals(bV
.getQualifier())) {
286 OsgiBootUtils
.debug("Uninstalled " + b
);
293 } catch (BundleException e
) {
294 final String ALREADY_INSTALLED
= "is already installed";
295 String message
= e
.getMessage();
296 if ((message
.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT
+ "\"")
297 || message
.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX
+ "\""))
298 && message
.contains(ALREADY_INSTALLED
)) {
299 // silent, in order to avoid warnings: we know that both
300 // have already been installed...
302 if (message
.contains(ALREADY_INSTALLED
)) {
303 if (OsgiBootUtils
.isDebug())
304 OsgiBootUtils
.warn("Duplicate install from " + url
+ ": " + message
);
306 OsgiBootUtils
.warn("Could not install bundle from " + url
+ ": " + message
);
308 if (OsgiBootUtils
.isDebug() && !message
.contains(ALREADY_INSTALLED
))
317 * Start bundles based on system properties.
319 * @see OsgiBoot#startBundles(Map)
321 public void startBundles() {
322 Properties properties
= System
.getProperties();
323 startBundles(properties
);
327 * Start bundles based on these properties.
329 * @see OsgiBoot#startBundles(Map)
331 public void startBundles(Properties properties
) {
332 Map
<String
, String
> map
= new TreeMap
<>();
333 for (Object key
: properties
.keySet()) {
334 String property
= key
.toString();
335 if (property
.startsWith(PROP_ARGEO_OSGI_START
)) {
336 map
.put(property
, properties
.getProperty(property
));
342 /** Start bundle based on keys starting with {@link #PROP_ARGEO_OSGI_START}. */
343 public void startBundles(Map
<String
, String
> properties
) {
344 FrameworkStartLevel frameworkStartLevel
= bundleContext
.getBundle(0).adapt(FrameworkStartLevel
.class);
346 // default and active start levels from System properties
347 Integer defaultStartLevel
= Integer
.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
, "4"));
348 Integer activeStartLevel
= Integer
.parseInt(getProperty(PROP_OSGI_STARTLEVEL
, "6"));
350 SortedMap
<Integer
, List
<String
>> startLevels
= new TreeMap
<Integer
, List
<String
>>();
351 computeStartLevels(startLevels
, properties
, defaultStartLevel
);
352 // inverts the map for the time being, TODO optimise
353 Map
<String
, Integer
> bundleStartLevels
= new HashMap
<>();
354 for (Integer level
: startLevels
.keySet()) {
355 for (String bsn
: startLevels
.get(level
))
356 bundleStartLevels
.put(bsn
, level
);
358 for (Bundle bundle
: bundleContext
.getBundles()) {
359 String bsn
= bundle
.getSymbolicName();
360 if (bundleStartLevels
.containsKey(bsn
)) {
361 BundleStartLevel bundleStartLevel
= bundle
.adapt(BundleStartLevel
.class);
362 Integer level
= bundleStartLevels
.get(bsn
);
363 if (bundleStartLevel
.getStartLevel() != level
|| !bundleStartLevel
.isPersistentlyStarted()) {
364 bundleStartLevel
.setStartLevel(level
);
367 } catch (BundleException e
) {
368 OsgiBootUtils
.error("Cannot mark " + bsn
+ " as started", e
);
370 if (OsgiBootUtils
.isDebug())
371 OsgiBootUtils
.debug(bsn
+ " starts at level " + level
);
375 frameworkStartLevel
.setStartLevel(activeStartLevel
, (FrameworkEvent event
) -> {
376 if (OsgiBootUtils
.isDebug())
377 OsgiBootUtils
.debug("Framework event: " + event
);
378 int initialStartLevel
= frameworkStartLevel
.getInitialBundleStartLevel();
379 int startLevel
= frameworkStartLevel
.getStartLevel();
380 OsgiBootUtils
.debug("Framework start level: " + startLevel
+ " (initial: " + initialStartLevel
+ ")");
384 private static void computeStartLevels(SortedMap
<Integer
, List
<String
>> startLevels
, Map
<String
, String
> properties
,
385 Integer defaultStartLevel
) {
387 // default (and previously, only behaviour)
388 appendToStartLevels(startLevels
, defaultStartLevel
, properties
.getOrDefault(PROP_ARGEO_OSGI_START
, ""));
390 // list argeo.osgi.start.* system properties
391 Iterator
<String
> keys
= properties
.keySet().iterator();
392 final String prefix
= PROP_ARGEO_OSGI_START
+ ".";
393 while (keys
.hasNext()) {
394 String key
= keys
.next();
395 if (key
.startsWith(prefix
)) {
397 String suffix
= key
.substring(prefix
.length());
398 String
[] tokens
= suffix
.split("\\.");
399 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
401 // first token is start level
402 startLevel
= Integer
.parseInt(tokens
[0]);
403 } catch (NumberFormatException e
) {
404 startLevel
= defaultStartLevel
;
407 startLevel
= defaultStartLevel
;
409 // append bundle names
410 String bundleNames
= properties
.get(key
);
411 appendToStartLevels(startLevels
, startLevel
, bundleNames
);
416 /** Append a comma-separated list of bundles to the start levels. */
417 private static void appendToStartLevels(SortedMap
<Integer
, List
<String
>> startLevels
, Integer startLevel
,
419 if (str
== null || str
.trim().equals(""))
422 if (!startLevels
.containsKey(startLevel
))
423 startLevels
.put(startLevel
, new ArrayList
<String
>());
424 String
[] bundleNames
= str
.split(",");
425 for (int i
= 0; i
< bundleNames
.length
; i
++) {
426 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
427 (startLevels
.get(startLevel
)).add(bundleNames
[i
]);
432 // * Start the provided list of bundles
434 // * @return whether all bundles are now in active state
438 // public boolean startBundles(List<String> bundlesToStart) {
439 // if (bundlesToStart.size() == 0)
442 // // used to monitor ACTIVE states
443 // List<Bundle> startedBundles = new ArrayList<Bundle>();
444 // // used to log the bundles not found
445 // List<String> notFoundBundles = new ArrayList<String>(bundlesToStart);
447 // Bundle[] bundles = bundleContext.getBundles();
448 // long startBegin = System.currentTimeMillis();
449 // for (int i = 0; i < bundles.length; i++) {
450 // Bundle bundle = bundles[i];
451 // String symbolicName = bundle.getSymbolicName();
452 // if (bundlesToStart.contains(symbolicName))
456 // if (OsgiBootUtils.isDebug())
457 // debug("Bundle " + symbolicName + " started");
458 // } catch (Exception e) {
459 // OsgiBootUtils.warn("Start of bundle " + symbolicName + " failed because of " + e
460 // + ", maybe bundle is not yet resolved," + " waiting and trying again.");
461 // waitForBundleResolvedOrActive(startBegin, bundle);
463 // startedBundles.add(bundle);
465 // notFoundBundles.remove(symbolicName);
466 // } catch (Exception e) {
467 // OsgiBootUtils.warn("Bundle " + symbolicName + " cannot be started: " + e.getMessage());
468 // if (OsgiBootUtils.isDebug())
469 // e.printStackTrace();
470 // // was found even if start failed
471 // notFoundBundles.remove(symbolicName);
475 // for (int i = 0; i < notFoundBundles.size(); i++)
476 // OsgiBootUtils.warn("Bundle '" + notFoundBundles.get(i) + "' not started because it was not found.");
478 // // monitors that all bundles are started
479 // long beginMonitor = System.currentTimeMillis();
480 // boolean allStarted = !(startedBundles.size() > 0);
481 // List<String> notStarted = new ArrayList<String>();
482 // while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) {
483 // notStarted = new ArrayList<String>();
484 // allStarted = true;
485 // for (int i = 0; i < startedBundles.size(); i++) {
486 // Bundle bundle = (Bundle) startedBundles.get(i);
487 // // TODO check behaviour of lazs bundles
488 // if (bundle.getState() != Bundle.ACTIVE) {
489 // allStarted = false;
490 // notStarted.add(bundle.getSymbolicName());
494 // Thread.sleep(100);
495 // } catch (InterruptedException e) {
499 // long duration = System.currentTimeMillis() - beginMonitor;
502 // for (int i = 0; i < notStarted.size(); i++)
503 // OsgiBootUtils.warn("Bundle '" + notStarted.get(i) + "' not ACTIVE after " + (duration / 1000) + "s");
505 // return allStarted;
508 // /** Waits for a bundle to become active or resolved */
510 // private void waitForBundleResolvedOrActive(long startBegin, Bundle bundle) throws Exception {
511 // int originalState = bundle.getState();
512 // if ((originalState == Bundle.RESOLVED) || (originalState == Bundle.ACTIVE))
515 // String originalStateStr = OsgiBootUtils.stateAsString(originalState);
517 // int currentState = bundle.getState();
518 // while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
519 // long now = System.currentTimeMillis();
520 // if ((now - startBegin) > defaultTimeout * 10)
521 // throw new Exception("Bundle " + bundle.getSymbolicName() + " was not RESOLVED or ACTIVE after "
522 // + (now - startBegin) + "ms (originalState=" + originalStateStr + ", currentState="
523 // + OsgiBootUtils.stateAsString(currentState) + ")");
526 // Thread.sleep(100l);
527 // } catch (InterruptedException e) {
530 // currentState = bundle.getState();
535 * BUNDLE PATTERNS INSTALLATION
538 * Computes a list of URLs based on Ant-like include/exclude patterns defined by
539 * ${argeo.osgi.bundles} with the following format:<br>
540 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
541 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
542 * without a new line causes a '.' to be appended with unexpected side effects.
544 public List
<String
> getBundlesUrls() {
545 String bundlePatterns
= getProperty(PROP_ARGEO_OSGI_BUNDLES
);
546 return getBundlesUrls(bundlePatterns
);
550 * Compute a list of URLs to install based on the provided patterns, with
553 public List
<String
> getBundlesUrls(String bundlePatterns
) {
554 String baseUrl
= getProperty(PROP_ARGEO_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
555 return getBundlesUrls(baseUrl
, bundlePatterns
);
558 /** Implements the path matching logic */
559 public List
<String
> getBundlesUrls(String baseUrl
, String bundlePatterns
) {
560 List
<String
> urls
= new ArrayList
<String
>();
561 if (bundlePatterns
== null)
564 // bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
565 if (OsgiBootUtils
.isDebug())
566 debug(PROP_ARGEO_OSGI_BUNDLES
+ "=" + bundlePatterns
);
568 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
569 List
<BundlesSet
> bundlesSets
= new ArrayList
<BundlesSet
>();
570 while (st
.hasMoreTokens()) {
571 String token
= st
.nextToken();
572 if (new File(token
).exists()) {
573 String url
= locationToUrl(baseUrl
, token
);
576 bundlesSets
.add(new BundlesSet(token
));
580 List
<String
> included
= new ArrayList
<String
>();
581 // PathMatcher matcher = new AntPathMatcher();
582 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
583 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
584 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
585 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
586 match(included
, bundlesSet
.getDir(), null, pattern
);
591 List
<String
> excluded
= new ArrayList
<String
>();
592 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
593 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
594 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
595 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
596 match(excluded
, bundlesSet
.getDir(), null, pattern
);
601 for (int i
= 0; i
< included
.size(); i
++) {
602 String fullPath
= (String
) included
.get(i
);
603 if (!excluded
.contains(fullPath
))
604 urls
.add(locationToUrl(baseUrl
, fullPath
));
611 * DISTRIBUTION JAR INSTALLATION
613 public List
<String
> getDistributionUrls() {
614 String distributionUrl
= getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL
);
615 String baseUrl
= getProperty(PROP_ARGEO_OSGI_BASE_URL
);
616 return getDistributionUrls(distributionUrl
, baseUrl
);
619 public List
<String
> getDistributionUrls(String distributionUrl
, String baseUrl
) {
620 List
<String
> urls
= new ArrayList
<String
>();
621 if (distributionUrl
== null)
624 DistributionBundle distributionBundle
;
625 if (distributionUrl
.startsWith("http") || distributionUrl
.startsWith("file")) {
626 distributionBundle
= new DistributionBundle(distributionUrl
);
628 distributionBundle
.setBaseUrl(baseUrl
);
631 if (baseUrl
== null) {
632 baseUrl
= localCache
;
635 if (distributionUrl
.contains(":")) {
636 // TODO make it safer
637 String
[] parts
= distributionUrl
.trim().split(":");
638 String
[] categoryParts
= parts
[0].split("\\.");
639 String artifactId
= parts
[1];
640 String version
= parts
[2];
641 StringBuilder sb
= new StringBuilder();
642 for (String categoryPart
: categoryParts
) {
643 sb
.append(categoryPart
).append('/');
645 sb
.append(artifactId
).append('/');
646 sb
.append(version
).append('/');
647 sb
.append(artifactId
).append('-').append(version
).append(".jar");
648 distributionUrl
= sb
.toString();
651 distributionBundle
= new DistributionBundle(baseUrl
, distributionUrl
, localCache
);
653 // if (baseUrl != null && !(distributionUrl.startsWith("http") ||
654 // distributionUrl.startsWith("file"))) {
656 // distributionBundle = new DistributionBundle(baseUrl, distributionUrl,
659 // distributionBundle = new DistributionBundle(distributionUrl);
660 // if (baseUrl != null)
661 // distributionBundle.setBaseUrl(baseUrl);
663 distributionBundle
.processUrl();
664 return distributionBundle
.listUrls();
668 * HIGH LEVEL UTILITIES
670 /** Actually performs the matching logic. */
671 protected void match(List
<String
> matched
, String base
, String currentPath
, String pattern
) {
672 if (currentPath
== null) {
674 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
675 File
[] files
= baseDir
.listFiles();
678 if (OsgiBootUtils
.isDebug())
679 OsgiBootUtils
.warn("Base dir " + baseDir
+ " has no children, exists=" + baseDir
.exists()
680 + ", isDirectory=" + baseDir
.isDirectory());
684 for (int i
= 0; i
< files
.length
; i
++)
685 match(matched
, base
, files
[i
].getName(), pattern
);
687 PathMatcher matcher
= FileSystems
.getDefault().getPathMatcher(pattern
);
688 String fullPath
= base
+ '/' + currentPath
;
689 if (matched
.contains(fullPath
))
690 return;// don't try deeper if already matched
692 boolean ok
= matcher
.matches(Paths
.get(currentPath
));
694 // debug(currentPath + " " + (ok ? "" : " not ")
695 // + " matched with " + pattern);
697 matched
.add(fullPath
);
700 String newFullPath
= relativeToFullPath(base
, currentPath
);
701 File newFile
= new File(newFullPath
);
702 File
[] files
= newFile
.listFiles();
704 for (int i
= 0; i
< files
.length
; i
++) {
705 String newCurrentPath
= currentPath
+ '/' + files
[i
].getName();
706 if (files
[i
].isDirectory()) {
707 // if (matcher.matchStart(pattern, newCurrentPath)) {
708 // FIXME recurse only if start matches ?
709 match(matched
, base
, newCurrentPath
, pattern
);
711 // if (OsgiBootUtils.isDebug())
712 // debug(newCurrentPath + " does not start match with " + pattern);
716 boolean nonDirectoryOk
= matcher
.matches(Paths
.get(newCurrentPath
));
717 if (OsgiBootUtils
.isDebug())
718 debug(currentPath
+ " " + (ok ?
"" : " not ") + " matched with " + pattern
);
720 matched
.add(relativeToFullPath(base
, newCurrentPath
));
728 protected void matchFile() {
733 * LOW LEVEL UTILITIES
736 * The bundles already installed. Key is location (String) , value is a
739 public Map
<String
, Bundle
> getBundlesByLocation() {
740 Map
<String
, Bundle
> installedBundles
= new HashMap
<String
, Bundle
>();
741 Bundle
[] bundles
= bundleContext
.getBundles();
742 for (int i
= 0; i
< bundles
.length
; i
++) {
743 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
745 return installedBundles
;
749 * The bundles already installed. Key is symbolic name (String) , value is a
752 public Map
<String
, Bundle
> getBundlesBySymbolicName() {
753 Map
<String
, Bundle
> namedBundles
= new HashMap
<String
, Bundle
>();
754 Bundle
[] bundles
= bundleContext
.getBundles();
755 for (int i
= 0; i
< bundles
.length
; i
++) {
756 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
761 /** Creates an URL from a location */
762 protected String
locationToUrl(String baseUrl
, String location
) {
763 return baseUrl
+ location
;
766 /** Transforms a relative path in a full system path. */
767 protected String
relativeToFullPath(String basePath
, String relativePath
) {
768 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
771 private void refreshFramework() {
772 Bundle systemBundle
= bundleContext
.getBundle(0);
773 FrameworkWiring frameworkWiring
= systemBundle
.adapt(FrameworkWiring
.class);
774 frameworkWiring
.refreshBundles(null);
778 * Gets a property value
780 * @return null when defaultValue is ""
782 public String
getProperty(String name
, String defaultValue
) {
783 String value
= bundleContext
.getProperty(name
);
785 return defaultValue
; // may be null
790 public String
getProperty(String name
) {
791 return getProperty(name
, null);
798 // public boolean getDebug() {
799 // return OsgiBootUtils.debug;
802 // public void setDebug(boolean debug) {
803 // this.debug = debug;
806 public BundleContext
getBundleContext() {
807 return bundleContext
;
810 public String
getLocalCache() {
814 // public void setDefaultTimeout(long defaultTimeout) {
815 // this.defaultTimeout = defaultTimeout;
818 // public boolean isExcludeSvn() {
819 // return excludeSvn;
822 // public void setExcludeSvn(boolean excludeSvn) {
823 // this.excludeSvn = excludeSvn;