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