1 package org
.argeo
.init
.osgi
;
3 import static java
.lang
.System
.Logger
.Level
.DEBUG
;
4 import static java
.lang
.System
.Logger
.Level
.ERROR
;
5 import static java
.lang
.System
.Logger
.Level
.TRACE
;
6 import static java
.lang
.System
.Logger
.Level
.WARNING
;
9 import java
.lang
.System
.Logger
;
10 import java
.lang
.System
.Logger
.Level
;
11 import java
.nio
.file
.FileSystems
;
12 import java
.nio
.file
.Files
;
13 import java
.nio
.file
.Path
;
14 import java
.nio
.file
.PathMatcher
;
15 import java
.nio
.file
.Paths
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Arrays
;
18 import java
.util
.HashMap
;
19 import java
.util
.Iterator
;
20 import java
.util
.List
;
22 import java
.util
.Optional
;
23 import java
.util
.ServiceLoader
;
25 import java
.util
.SortedMap
;
26 import java
.util
.StringTokenizer
;
27 import java
.util
.TreeMap
;
29 import org
.argeo
.api
.a2
.A2Source
;
30 import org
.argeo
.api
.a2
.ProvisioningManager
;
31 import org
.argeo
.api
.init
.InitConstants
;
32 import org
.osgi
.framework
.Bundle
;
33 import org
.osgi
.framework
.BundleContext
;
34 import org
.osgi
.framework
.BundleException
;
35 import org
.osgi
.framework
.FrameworkEvent
;
36 import org
.osgi
.framework
.Version
;
37 import org
.osgi
.framework
.launch
.Framework
;
38 import org
.osgi
.framework
.launch
.FrameworkFactory
;
39 import org
.osgi
.framework
.startlevel
.BundleStartLevel
;
40 import org
.osgi
.framework
.startlevel
.FrameworkStartLevel
;
41 import org
.osgi
.framework
.wiring
.FrameworkWiring
;
44 * Basic provisioning of an OSGi runtime via file path patterns and system
45 * properties. The approach is to generate list of URLs based on various
46 * methods, configured via properties.
48 public class OsgiBoot
{
49 private final static Logger logger
= System
.getLogger(OsgiBoot
.class.getName());
52 final static String PROP_ARGEO_OSGI_BUNDLES
= "argeo.osgi.bundles";
53 final static String PROP_ARGEO_OSGI_BASE_URL
= "argeo.osgi.baseUrl";
54 final static String PROP_ARGEO_OSGI_LOCAL_CACHE
= "argeo.osgi.localCache";
55 final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL
= "argeo.osgi.distributionUrl";
59 final static String PROP_ARGEO_OSGI_BOOT_DEBUG
= "argeo.osgi.boot.debug";
61 final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE
= "argeo.osgi.boot.systemPropertiesFile";
62 final static String PROP_ARGEO_OSGI_BOOT_APPCLASS
= "argeo.osgi.boot.appclass";
63 final static String PROP_ARGEO_OSGI_BOOT_APPARGS
= "argeo.osgi.boot.appargs";
66 public final static String DEFAULT_BASE_URL
= "reference:file:";
67 final static String DEFAULT_MAX_START_LEVEL
= "32";
69 private final BundleContext bundleContext
;
70 private final String localCache
;
71 private final ProvisioningManager provisioningManager
;
77 public OsgiBoot(BundleContext bundleContext
) {
78 this.bundleContext
= bundleContext
;
79 Path homePath
= Paths
.get(System
.getProperty("user.home")).toAbsolutePath();
80 String homeUri
= homePath
.toUri().toString();
81 localCache
= getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE
, homeUri
+ ".m2/repository/");
83 provisioningManager
= new ProvisioningManager(bundleContext
);
84 String sources
= getProperty(InitConstants
.PROP_ARGEO_OSGI_SOURCES
);
85 if (sources
== null) {
86 provisioningManager
.registerDefaultSource();
88 // OsgiBootUtils.debug("Found sources " + sources);
89 for (String source
: sources
.split(",")) {
90 int qmIndex
= source
.lastIndexOf('?');
91 String queryPart
= "";
93 queryPart
= source
.substring(qmIndex
);
94 source
= source
.substring(0, qmIndex
);
96 // TODO centralise in A" package?
97 if (source
.trim().equals(A2Source
.DEFAULT_A2_URI
)) {
98 if (Files
.exists(homePath
))
99 provisioningManager
.registerSource(
100 A2Source
.SCHEME_A2
+ "://" + homePath
.toString() + "/.local/share/a2" + queryPart
);
101 provisioningManager
.registerSource(A2Source
.SCHEME_A2
+ ":///usr/local/share/a2" + queryPart
);
102 // provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/lib/a2" + queryPart);
103 provisioningManager
.registerSource(A2Source
.SCHEME_A2
+ ":///usr/share/a2" + queryPart
);
104 // provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/lib/a2" + queryPart);
105 } else if (source
.trim().equals(A2Source
.DEFAULT_A2_REFERENCE_URI
)) {
106 if (Files
.exists(homePath
))
107 provisioningManager
.registerSource(A2Source
.SCHEME_A2_REFERENCE
+ "://" + homePath
.toString()
108 + "/.local/share/a2" + queryPart
);
110 .registerSource(A2Source
.SCHEME_A2_REFERENCE
+ ":///usr/local/share/a2" + queryPart
);
111 // provisioningManager
112 // .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/lib/a2" + queryPart);
113 provisioningManager
.registerSource(A2Source
.SCHEME_A2_REFERENCE
+ ":///usr/share/a2" + queryPart
);
114 // provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
116 provisioningManager
.registerSource(source
+ queryPart
);
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() {
136 long begin
= System
.currentTimeMillis();
139 // Make sure fragments are properly considered by refreshing
143 long duration
= System
.currentTimeMillis() - begin
;
144 logger
.log(DEBUG
, () -> "OSGi bootstrap completed in " + Math
.round(((double) duration
) / 1000) + "s ("
145 + duration
+ "ms), " + bundleContext
.getBundles().length
+ " bundles");
146 } catch (RuntimeException e
) {
147 logger
.log(ERROR
, "OSGi bootstrap FAILED", e
);
152 if (logger
.isLoggable(TRACE
)) {
153 OsgiBootDiagnostics diagnostics
= new OsgiBootDiagnostics(bundleContext
);
154 diagnostics
.checkUnresolved();
155 Map
<String
, Set
<String
>> duplicatePackages
= diagnostics
.findPackagesExportedTwice();
156 if (duplicatePackages
.size() > 0) {
157 logger
.log(TRACE
, "Packages exported twice:");
158 Iterator
<String
> it
= duplicatePackages
.keySet().iterator();
159 while (it
.hasNext()) {
160 String pkgName
= it
.next();
161 logger
.log(TRACE
, pkgName
);
162 Set
<String
> bdles
= duplicatePackages
.get(pkgName
);
163 Iterator
<String
> bdlesIt
= bdles
.iterator();
164 while (bdlesIt
.hasNext())
165 logger
.log(TRACE
, " " + bdlesIt
.next());
169 System
.out
.println();
172 public void install() {
173 String osgiInstancePath
= getProperty(InitConstants
.PROP_OSGI_INSTANCE_AREA
);
174 String osgiConfigurationPath
= getProperty(InitConstants
.PROP_OSGI_CONFIGURATION_AREA
);
175 String osgiSharedConfigurationPath
= getProperty(InitConstants
.PROP_OSGI_CONFIGURATION_AREA
);
176 logger
.log(DEBUG
, () -> "OSGi bootstrap starting" //
177 + (osgiInstancePath
!= null ?
" data: " + osgiInstancePath
+ "" : "") //
178 + (osgiConfigurationPath
!= null ?
" state: " + osgiConfigurationPath
+ "" : "") //
179 + (osgiSharedConfigurationPath
!= null ?
" config: " + osgiSharedConfigurationPath
+ "" : "") //
182 // legacy install bundles
183 installUrls(getBundlesUrls());
184 installUrls(getDistributionUrls());
186 // A2 install bundles
187 provisioningManager
.install(null);
191 public void update() {
192 provisioningManager
.update();
198 /** Install a single url. Convenience method. */
199 public Bundle
installUrl(String url
) {
200 List
<String
> urls
= new ArrayList
<String
>();
203 return (Bundle
) getBundlesByLocation().get(url
);
206 /** Install the bundles at this URL list. */
207 public void installUrls(List
<String
> urls
) {
208 Map
<String
, Bundle
> installedBundles
= getBundlesByLocation();
209 for (int i
= 0; i
< urls
.size(); i
++) {
210 String url
= (String
) urls
.get(i
);
211 installUrl(url
, installedBundles
);
213 // refreshFramework();
216 /** Actually install the provided URL */
217 protected void installUrl(String url
, Map
<String
, Bundle
> installedBundles
) {
219 if (installedBundles
.containsKey(url
)) {
220 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
221 logger
.log(TRACE
, () -> "Bundle " + bundle
.getSymbolicName() + " already installed from " + url
);
222 } else if (url
.contains("/" + InitConstants
.SYMBOLIC_NAME_EQUINOX
+ "/")
223 || url
.contains("/" + InitConstants
.SYMBOLIC_NAME_INIT
+ "/")) {
224 if (logger
.isLoggable(TRACE
))
225 logger
.log(WARNING
, "Skip " + url
);
228 Bundle bundle
= bundleContext
.installBundle(url
);
229 if (url
.startsWith("http"))
231 () -> "Installed " + bundle
.getSymbolicName() + "-" + bundle
.getVersion() + " from " + url
);
234 () -> "Installed " + bundle
.getSymbolicName() + "-" + bundle
.getVersion() + " from " + url
);
235 assert bundle
.getSymbolicName() != null;
236 // uninstall previous versions
237 bundles
: for (Bundle b
: bundleContext
.getBundles()) {
238 if (b
.getSymbolicName() == null)
240 if (bundle
.getSymbolicName().equals(b
.getSymbolicName())) {
241 Version bundleV
= bundle
.getVersion();
242 Version bV
= b
.getVersion();
245 if (bundleV
.getMajor() == bV
.getMajor() && bundleV
.getMinor() == bV
.getMinor()) {
246 if (bundleV
.getMicro() > bV
.getMicro()) {
247 // uninstall older bundles
249 logger
.log(TRACE
, () -> "Uninstalled " + b
);
250 } else if (bundleV
.getMicro() < bV
.getMicro()) {
251 // uninstall just installed bundle if newer
253 logger
.log(TRACE
, () -> "Uninstalled " + bundle
);
256 // uninstall any other with same major/minor
257 if (!bundleV
.getQualifier().equals(bV
.getQualifier())) {
259 logger
.log(TRACE
, () -> "Uninstalled " + b
);
266 } catch (BundleException e
) {
267 final String ALREADY_INSTALLED
= "is already installed";
268 String message
= e
.getMessage();
269 if ((message
.contains("Bundle \"" + InitConstants
.SYMBOLIC_NAME_INIT
+ "\"")
270 || message
.contains("Bundle \"" + InitConstants
.SYMBOLIC_NAME_EQUINOX
+ "\""))
271 && message
.contains(ALREADY_INSTALLED
)) {
272 // silent, in order to avoid warnings: we know that both
273 // have already been installed...
275 if (message
.contains(ALREADY_INSTALLED
)) {
276 if (logger
.isLoggable(TRACE
))
277 logger
.log(WARNING
, "Duplicate install from " + url
+ ": " + message
);
279 logger
.log(WARNING
, "Could not install bundle from " + url
+ ": " + message
);
281 if (logger
.isLoggable(TRACE
) && !message
.contains(ALREADY_INSTALLED
))
291 * Start bundles based on these properties.
293 * @see OsgiBoot#doStartBundles(Map)
295 public void startBundles() {
296 Map
<String
, String
> map
= new TreeMap
<>();
297 // first use properties
298 // if (properties != null) {
299 // for (String key : properties.keySet()) {
300 // String property = key;
301 // if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
302 // map.put(property, properties.get(property));
306 // then try all start level until a maximum
307 int maxStartLevel
= Integer
308 .parseInt(getProperty(InitConstants
.PROP_ARGEO_OSGI_MAX_START_LEVEL
, DEFAULT_MAX_START_LEVEL
));
309 for (int i
= 1; i
<= maxStartLevel
; i
++) {
310 String key
= InitConstants
.PROP_ARGEO_OSGI_START
+ "." + i
;
311 String value
= getProperty(key
);
316 // finally, override with system properties
317 // for (Object key : System.getProperties().keySet()) {
318 // if (key.toString().startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
319 // map.put(key.toString(), System.getProperty(key.toString()));
326 // void startBundles(Properties properties) {
327 // Map<String, String> map = new TreeMap<>();
328 // // first use properties
329 // if (properties != null) {
330 // for (Object key : properties.keySet()) {
331 // String property = key.toString();
332 // if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
333 // map.put(property, properties.get(property).toString());
337 // startBundles(map);
341 * Start bundle based on keys starting with
342 * {@link InitConstants#PROP_ARGEO_OSGI_START}.
344 protected void doStartBundles(Map
<String
, String
> properties
) {
345 FrameworkStartLevel frameworkStartLevel
= bundleContext
.getBundle(0).adapt(FrameworkStartLevel
.class);
347 // default and active start levels from System properties
348 int initialStartLevel
= frameworkStartLevel
.getInitialBundleStartLevel();
349 int defaultStartLevel
= Integer
.parseInt(getProperty(InitConstants
.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
, "4"));
350 int activeStartLevel
= Integer
.parseInt(getProperty(InitConstants
.PROP_OSGI_STARTLEVEL
, "6"));
351 if (logger
.isLoggable(TRACE
)) {
353 "OSGi default start level: "
354 + getProperty(InitConstants
.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
, "<not set>") + ", using "
355 + defaultStartLevel
);
356 logger
.log(TRACE
, "OSGi active start level: " + getProperty(InitConstants
.PROP_OSGI_STARTLEVEL
, "<not set>")
357 + ", using " + activeStartLevel
);
358 logger
.log(TRACE
, "Framework start level: " + frameworkStartLevel
.getStartLevel() + " (initial: "
359 + initialStartLevel
+ ")");
362 SortedMap
<Integer
, List
<String
>> startLevels
= new TreeMap
<Integer
, List
<String
>>();
363 computeStartLevels(startLevels
, properties
, defaultStartLevel
);
364 // inverts the map for the time being, TODO optimise
365 Map
<String
, Integer
> bundleStartLevels
= new HashMap
<>();
366 for (Integer level
: startLevels
.keySet()) {
367 for (String bsn
: startLevels
.get(level
))
368 bundleStartLevels
.put(bsn
, level
);
371 // keep only bundles with the highest version
372 Map
<String
, Bundle
> startableBundles
= new HashMap
<>();
373 bundles
: for (Bundle bundle
: bundleContext
.getBundles()) {
374 if (bundle
.getVersion() == null)
376 String bsn
= bundle
.getSymbolicName();
377 if (!startableBundles
.containsKey(bsn
)) {
378 startableBundles
.put(bsn
, bundle
);
380 if (bundle
.getVersion().compareTo(startableBundles
.get(bsn
).getVersion()) > 0) {
381 startableBundles
.put(bsn
, bundle
);
386 for (Bundle bundle
: startableBundles
.values()) {
387 String bsn
= bundle
.getSymbolicName();
388 if (bundleStartLevels
.containsKey(bsn
)) {
389 BundleStartLevel bundleStartLevel
= bundle
.adapt(BundleStartLevel
.class);
390 Integer level
= bundleStartLevels
.get(bsn
);
391 if (bundleStartLevel
.getStartLevel() != level
|| !bundleStartLevel
.isPersistentlyStarted()) {
392 bundleStartLevel
.setStartLevel(level
);
395 } catch (BundleException e
) {
396 logger
.log(ERROR
, "Cannot mark " + bsn
+ " as started", e
);
398 logger
.log(TRACE
, () -> bsn
+ " v" + bundle
.getVersion() + " starts at level " + level
);
403 logger
.log(TRACE
, () -> "About to set framework start level to " + activeStartLevel
+ " ...");
405 frameworkStartLevel
.setStartLevel(activeStartLevel
, (FrameworkEvent event
) -> {
406 if (event
.getType() == FrameworkEvent
.ERROR
) {
407 logger
.log(ERROR
, "Start sequence failed", event
.getThrowable());
409 logger
.log(TRACE
, () -> "Framework started at level " + frameworkStartLevel
.getStartLevel());
413 // // Start the framework level after level
414 // int currentStartLevel = frameworkStartLevel.getStartLevel();
415 // stages: for (int stage = currentStartLevel + 1; stage <= activeStartLevel; stage++) {
416 // if (OsgiBootUtils.isDebug())
417 // OsgiBootUtils.debug("Starting stage " + stage + "...");
418 // final int nextStage = stage;
419 // final CompletableFuture<FrameworkEvent> stageCompleted = new CompletableFuture<>();
420 // frameworkStartLevel.setStartLevel(nextStage, (FrameworkEvent event) -> {
421 // stageCompleted.complete(event);
423 // FrameworkEvent event;
425 // event = stageCompleted.get();
426 // } catch (InterruptedException | ExecutionException e) {
427 // throw new IllegalStateException("Cannot continue start", e);
429 // if (event.getThrowable() != null) {
430 // OsgiBootUtils.error("Stage " + nextStage + " failed, aborting start.", event.getThrowable());
436 private static void computeStartLevels(SortedMap
<Integer
, List
<String
>> startLevels
, Map
<String
, String
> properties
,
437 Integer defaultStartLevel
) {
439 // default (and previously, only behaviour)
440 appendToStartLevels(startLevels
, defaultStartLevel
,
441 properties
.getOrDefault(InitConstants
.PROP_ARGEO_OSGI_START
, ""));
443 // list argeo.osgi.start.* system properties
444 Iterator
<String
> keys
= properties
.keySet().iterator();
445 final String prefix
= InitConstants
.PROP_ARGEO_OSGI_START
+ ".";
446 while (keys
.hasNext()) {
447 String key
= keys
.next();
448 if (key
.startsWith(prefix
)) {
450 String suffix
= key
.substring(prefix
.length());
451 String
[] tokens
= suffix
.split("\\.");
452 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
454 // first token is start level
455 startLevel
= Integer
.parseInt(tokens
[0]);
456 } catch (NumberFormatException e
) {
457 startLevel
= defaultStartLevel
;
460 startLevel
= defaultStartLevel
;
462 // append bundle names
463 String bundleNames
= properties
.get(key
);
464 appendToStartLevels(startLevels
, startLevel
, bundleNames
);
469 /** Append a comma-separated list of bundles to the start levels. */
470 private static void appendToStartLevels(SortedMap
<Integer
, List
<String
>> startLevels
, Integer startLevel
,
472 if (str
== null || str
.trim().equals(""))
475 if (!startLevels
.containsKey(startLevel
))
476 startLevels
.put(startLevel
, new ArrayList
<String
>());
477 String
[] bundleNames
= str
.split(",");
478 for (int i
= 0; i
< bundleNames
.length
; i
++) {
479 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
480 (startLevels
.get(startLevel
)).add(bundleNames
[i
]);
485 * BUNDLE PATTERNS INSTALLATION
488 * Computes a list of URLs based on Ant-like include/exclude patterns defined by
489 * ${argeo.osgi.bundles} with the following format:<br>
490 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
491 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
492 * without a new line causes a '.' to be appended with unexpected side effects.
494 public List
<String
> getBundlesUrls() {
495 String bundlePatterns
= getProperty(PROP_ARGEO_OSGI_BUNDLES
);
496 return getBundlesUrls(bundlePatterns
);
500 * Compute a list of URLs to install based on the provided patterns, with
503 public List
<String
> getBundlesUrls(String bundlePatterns
) {
504 String baseUrl
= getProperty(PROP_ARGEO_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
505 return getBundlesUrls(baseUrl
, bundlePatterns
);
508 /** Implements the path matching logic */
510 public List
<String
> getBundlesUrls(String baseUrl
, String bundlePatterns
) {
511 List
<String
> urls
= new ArrayList
<String
>();
512 if (bundlePatterns
== null)
515 // bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
516 logger
.log(TRACE
, () -> PROP_ARGEO_OSGI_BUNDLES
+ "=" + bundlePatterns
);
518 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
519 List
<BundlesSet
> bundlesSets
= new ArrayList
<BundlesSet
>();
520 while (st
.hasMoreTokens()) {
521 String token
= st
.nextToken();
522 if (new File(token
).exists()) {
523 String url
= locationToUrl(baseUrl
, token
);
526 bundlesSets
.add(new BundlesSet(token
));
530 List
<String
> included
= new ArrayList
<String
>();
531 // PathMatcher matcher = new AntPathMatcher();
532 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
533 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
534 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
535 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
536 match(included
, bundlesSet
.getDir(), null, pattern
);
541 List
<String
> excluded
= new ArrayList
<String
>();
542 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
543 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
544 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
545 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
546 match(excluded
, bundlesSet
.getDir(), null, pattern
);
551 for (int i
= 0; i
< included
.size(); i
++) {
552 String fullPath
= (String
) included
.get(i
);
553 if (!excluded
.contains(fullPath
))
554 urls
.add(locationToUrl(baseUrl
, fullPath
));
561 * DISTRIBUTION JAR INSTALLATION
563 public List
<String
> getDistributionUrls() {
564 String distributionUrl
= getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL
);
565 String baseUrl
= getProperty(PROP_ARGEO_OSGI_BASE_URL
);
566 return getDistributionUrls(distributionUrl
, baseUrl
);
569 public List
<String
> getDistributionUrls(String distributionUrl
, String baseUrl
) {
570 List
<String
> urls
= new ArrayList
<String
>();
571 if (distributionUrl
== null)
574 DistributionBundle distributionBundle
;
575 if (distributionUrl
.startsWith("http") || distributionUrl
.startsWith("file")) {
576 distributionBundle
= new DistributionBundle(distributionUrl
);
578 distributionBundle
.setBaseUrl(baseUrl
);
581 if (baseUrl
== null) {
582 baseUrl
= localCache
;
585 if (distributionUrl
.contains(":")) {
586 // TODO make it safer
587 String
[] parts
= distributionUrl
.trim().split(":");
588 String
[] categoryParts
= parts
[0].split("\\.");
589 String artifactId
= parts
[1];
590 String version
= parts
[2];
591 StringBuilder sb
= new StringBuilder();
592 for (String categoryPart
: categoryParts
) {
593 sb
.append(categoryPart
).append('/');
595 sb
.append(artifactId
).append('/');
596 sb
.append(version
).append('/');
597 sb
.append(artifactId
).append('-').append(version
).append(".jar");
598 distributionUrl
= sb
.toString();
601 distributionBundle
= new DistributionBundle(baseUrl
, distributionUrl
, localCache
);
603 distributionBundle
.processUrl();
604 return distributionBundle
.listUrls();
608 * HIGH LEVEL UTILITIES
610 /** Actually performs the matching logic. */
611 protected void match(List
<String
> matched
, String base
, String currentPath
, String pattern
) {
612 if (currentPath
== null) {
614 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
615 File
[] files
= baseDir
.listFiles();
618 if (logger
.isLoggable(TRACE
))
619 logger
.log(Level
.WARNING
, "Base dir " + baseDir
+ " has no children, exists=" + baseDir
.exists()
620 + ", isDirectory=" + baseDir
.isDirectory());
624 for (int i
= 0; i
< files
.length
; i
++)
625 match(matched
, base
, files
[i
].getName(), pattern
);
627 PathMatcher matcher
= FileSystems
.getDefault().getPathMatcher(pattern
);
628 String fullPath
= base
+ '/' + currentPath
;
629 if (matched
.contains(fullPath
))
630 return;// don't try deeper if already matched
632 boolean ok
= matcher
.matches(Paths
.get(currentPath
));
634 // debug(currentPath + " " + (ok ? "" : " not ")
635 // + " matched with " + pattern);
637 matched
.add(fullPath
);
640 String newFullPath
= relativeToFullPath(base
, currentPath
);
641 File newFile
= new File(newFullPath
);
642 File
[] files
= newFile
.listFiles();
644 for (int i
= 0; i
< files
.length
; i
++) {
645 String newCurrentPath
= currentPath
+ '/' + files
[i
].getName();
646 if (files
[i
].isDirectory()) {
647 // if (matcher.matchStart(pattern, newCurrentPath)) {
648 // FIXME recurse only if start matches ?
649 match(matched
, base
, newCurrentPath
, pattern
);
651 // if (OsgiBootUtils.isDebug())
652 // debug(newCurrentPath + " does not start match with " + pattern);
656 boolean nonDirectoryOk
= matcher
.matches(Paths
.get(newCurrentPath
));
658 () -> currentPath
+ " " + (ok ?
"" : " not ") + " matched with " + pattern
);
660 matched
.add(relativeToFullPath(base
, newCurrentPath
));
669 * LOW LEVEL UTILITIES
672 * The bundles already installed. Key is location (String) , value is a
675 public Map
<String
, Bundle
> getBundlesByLocation() {
676 Map
<String
, Bundle
> installedBundles
= new HashMap
<String
, Bundle
>();
677 Bundle
[] bundles
= bundleContext
.getBundles();
678 for (int i
= 0; i
< bundles
.length
; i
++) {
679 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
681 return installedBundles
;
685 * The bundles already installed. Key is symbolic name (String) , value is a
688 public Map
<String
, Bundle
> getBundlesBySymbolicName() {
689 Map
<String
, Bundle
> namedBundles
= new HashMap
<String
, Bundle
>();
690 Bundle
[] bundles
= bundleContext
.getBundles();
691 for (int i
= 0; i
< bundles
.length
; i
++) {
692 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
697 /** Creates an URL from a location */
698 protected String
locationToUrl(String baseUrl
, String location
) {
699 return baseUrl
+ location
;
702 /** Transforms a relative path in a full system path. */
703 protected String
relativeToFullPath(String basePath
, String relativePath
) {
704 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
707 public void refresh() {
708 Bundle systemBundle
= bundleContext
.getBundle(0);
709 FrameworkWiring frameworkWiring
= systemBundle
.adapt(FrameworkWiring
.class);
710 // TODO deal with refresh breaking native loading (e.g SWT)
711 frameworkWiring
.refreshBundles(null);
715 * Gets a property value
717 * @return null when defaultValue is ""
719 public String
getProperty(String name
, String defaultValue
) {
720 String value
= bundleContext
.getProperty(name
);
722 return defaultValue
; // may be null
727 public String
getProperty(String name
) {
728 return getProperty(name
, null);
735 public BundleContext
getBundleContext() {
736 return bundleContext
;
740 * PLAIN OSGI LAUNCHER
742 /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
743 public static Framework
defaultOsgiLaunch(Map
<String
, String
> configuration
) {
744 Optional
<FrameworkFactory
> frameworkFactory
= ServiceLoader
.load(FrameworkFactory
.class).findFirst();
745 if (frameworkFactory
.isEmpty())
746 throw new IllegalStateException("No framework factory found");
747 return defaultOsgiLaunch(frameworkFactory
.get(), configuration
);
750 /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
751 public static Framework
defaultOsgiLaunch(FrameworkFactory frameworkFactory
, Map
<String
, String
> configuration
) {
753 Framework framework
= frameworkFactory
.newFramework(configuration
);
756 } catch (BundleException e
) {
757 throw new IllegalStateException("Cannot start OSGi framework", e
);
765 /** Uninstall all bundles with these symbolic names */
766 public static void uninstallBundles(BundleContext bc
, String
... symbolicNames
) {
767 List
<String
> lst
= Arrays
.asList(symbolicNames
);
768 for (Bundle b
: bc
.getBundles()) {
769 String sn
= b
.getSymbolicName();
772 if (lst
.contains(sn
)) {
775 } catch (BundleException e
) {
776 logger
.log(ERROR
, "Cannot uninstall " + sn
, e
);