2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.osgi
.boot
;
18 import java
.io
.BufferedReader
;
20 import java
.io
.IOException
;
21 import java
.io
.InputStreamReader
;
23 import java
.util
.ArrayList
;
24 import java
.util
.HashMap
;
25 import java
.util
.Iterator
;
26 import java
.util
.List
;
28 import java
.util
.Properties
;
30 import java
.util
.SortedMap
;
31 import java
.util
.StringTokenizer
;
32 import java
.util
.TreeMap
;
33 import java
.util
.TreeSet
;
35 import org
.argeo
.osgi
.boot
.internal
.springutil
.AntPathMatcher
;
36 import org
.argeo
.osgi
.boot
.internal
.springutil
.PathMatcher
;
37 import org
.argeo
.osgi
.boot
.internal
.springutil
.SystemPropertyUtils
;
38 import org
.osgi
.framework
.Bundle
;
39 import org
.osgi
.framework
.BundleContext
;
40 import org
.osgi
.framework
.BundleException
;
41 import org
.osgi
.framework
.Constants
;
42 import org
.osgi
.framework
.ServiceReference
;
43 import org
.osgi
.service
.packageadmin
.ExportedPackage
;
44 import org
.osgi
.service
.packageadmin
.PackageAdmin
;
47 * Basic provisioning of an OSGi runtime via file path patterns and system
48 * properties. Java 1.4 compatible.<br>
49 * The approach is to generate list of URLs based on various methods, configured
50 * via system properties.
52 @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
53 public class OsgiBoot
{
54 public final static String SYMBOLIC_NAME_OSGI_BOOT
= "org.argeo.osgi.boot";
55 public final static String SYMBOLIC_NAME_EQUINOX
= "org.eclipse.osgi";
57 public final static String PROP_OSGI_STARTLEVEL
= "osgi.startLevel";
58 public final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
= "osgi.bundles.defaultStartLevel";
60 public final static String PROP_ARGEO_OSGI_DATA_DIR
= "argeo.osgi.data.dir";
62 public final static String PROP_ARGEO_OSGI_START
= "argeo.osgi.start";
63 public final static String PROP_ARGEO_OSGI_BUNDLES
= "argeo.osgi.bundles";
64 public final static String PROP_ARGEO_OSGI_LOCATIONS
= "argeo.osgi.locations";
65 public final static String PROP_ARGEO_OSGI_BASE_URL
= "argeo.osgi.baseUrl";
67 public final static String PROP_ARGEO_OSGI_MODULES_URL
= "argeo.osgi.modulesUrl";
68 public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL
= "argeo.osgi.distributionUrl";
71 public final static String PROP_ARGEO_OSGI_BOOT_DEBUG
= "argeo.osgi.boot.debug";
72 public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN
= "argeo.osgi.boot.excludeSvn";
73 public final static String PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER
= "argeo.osgi.boot.installInLexicographicOrder";
75 public final static String PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
= "argeo.osgi.boot.defaultTimeout";
76 public final static String PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR
= "argeo.osgi.boot.modulesUrlSeparator";
77 public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE
= "argeo.osgi.boot.systemPropertiesFile";
78 public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS
= "argeo.osgi.boot.appclass";
79 public final static String PROP_ARGEO_OSGI_BOOT_APPARGS
= "argeo.osgi.boot.appargs";
81 public final static String DEFAULT_BASE_URL
= "reference:file:";
82 public final static String EXCLUDES_SVN_PATTERN
= "**/.svn/**";
84 // OSGi system properties
85 public final static String INSTANCE_AREA_PROP
= "osgi.instance.area";
86 public final static String INSTANCE_AREA_DEFAULT_PROP
= "osgi.instance.area.default";
88 private boolean debug
= Boolean
.valueOf(
89 System
.getProperty(PROP_ARGEO_OSGI_BOOT_DEBUG
, "false"))
91 /** Exclude svn metadata implicitely(a bit costly) */
92 private boolean excludeSvn
= Boolean
.valueOf(
93 System
.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN
, "false"))
97 * The {@link #installUrls(List)} methods won't follow the list order but
98 * order the urls according to the alphabetical order of the file names
99 * (last part of the URL). The goal is to stay closer from Eclipse PDE way
100 * of installing target platform bundles.
102 private boolean installInLexicographicOrder
= Boolean
105 PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER
,
106 "true")).booleanValue();;
108 /** Default is 10s (set in constructor) */
109 private long defaultTimeout
;
111 /** Default is ',' (set in constructor) */
112 private String modulesUrlSeparator
= ",";
114 private final BundleContext bundleContext
;
120 public OsgiBoot(BundleContext bundleContext
) {
121 this.bundleContext
= bundleContext
;
122 defaultTimeout
= Long
.parseLong(OsgiBootUtils
.getProperty(
123 PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
, "10000"));
124 modulesUrlSeparator
= OsgiBootUtils
.getProperty(
125 PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR
, ",");
126 initSystemProperties();
130 * Set additional system properties, especially ${argeo.osgi.data.dir} as an
131 * OS file path (and not a file:// URL)
133 protected void initSystemProperties() {
134 String osgiInstanceArea
= System
.getProperty(INSTANCE_AREA_PROP
);
135 String osgiInstanceAreaDefault
= System
136 .getProperty(INSTANCE_AREA_DEFAULT_PROP
);
137 String tempDir
= System
.getProperty("java.io.tmpdir");
140 if (osgiInstanceArea
!= null) {
141 // within OSGi with -data specified
142 osgiInstanceArea
= removeFilePrefix(osgiInstanceArea
);
143 dataDir
= new File(osgiInstanceArea
);
144 } else if (osgiInstanceAreaDefault
!= null) {
145 // within OSGi without -data specified
146 osgiInstanceAreaDefault
= removeFilePrefix(osgiInstanceAreaDefault
);
147 dataDir
= new File(osgiInstanceAreaDefault
);
148 } else {// outside OSGi
149 dataDir
= new File(tempDir
+ File
.separator
+ "argeoOsgiData");
151 System
.setProperty(PROP_ARGEO_OSGI_DATA_DIR
, dataDir
.getAbsolutePath());
157 /** Bootstraps the OSGi runtime */
158 public void bootstrap() {
159 long begin
= System
.currentTimeMillis();
160 System
.out
.println();
161 OsgiBootUtils
.info("OSGi bootstrap starting...");
162 OsgiBootUtils
.info("Writable data directory : "
163 + System
.getProperty(PROP_ARGEO_OSGI_DATA_DIR
)
164 + " (set as system property " + PROP_ARGEO_OSGI_DATA_DIR
+ ")");
165 installUrls(getBundlesUrls());
166 installUrls(getLocationsUrls());
167 installUrls(getModulesUrls());
168 installUrls(getDistributionUrls());
171 long duration
= System
.currentTimeMillis() - begin
;
172 OsgiBootUtils
.info("OSGi bootstrap completed in "
173 + Math
.round(((double) duration
) / 1000) + "s (" + duration
174 + "ms), " + bundleContext
.getBundles().length
+ " bundles");
176 // display packages exported twice
178 Map
/* <String,Set<String>> */duplicatePackages
= findPackagesExportedTwice();
179 if (duplicatePackages
.size() > 0) {
180 OsgiBootUtils
.info("Packages exported twice:");
181 Iterator it
= duplicatePackages
.keySet().iterator();
182 while (it
.hasNext()) {
183 String pkgName
= it
.next().toString();
184 OsgiBootUtils
.info(pkgName
);
185 Set bdles
= (Set
) duplicatePackages
.get(pkgName
);
186 Iterator bdlesIt
= bdles
.iterator();
187 while (bdlesIt
.hasNext())
188 OsgiBootUtils
.info(" " + bdlesIt
.next());
193 System
.out
.println();
199 /** Install a single url. Convenience method. */
200 public Bundle
installUrl(String url
) {
201 List urls
= new ArrayList();
204 return (Bundle
) getBundlesByLocation().get(url
);
207 /** Install the bundles at this URL list. */
208 public void installUrls(List urls
) {
209 Map installedBundles
= getBundlesByLocation();
211 if (installInLexicographicOrder
) {
212 SortedMap map
= new TreeMap();
214 for (int i
= 0; i
< urls
.size(); i
++) {
215 String url
= (String
) urls
.get(i
);
216 int index
= url
.lastIndexOf('/');
219 fileName
= url
.substring(index
+ 1);
222 map
.put(fileName
, url
);
226 Iterator keys
= map
.keySet().iterator();
227 while (keys
.hasNext()) {
228 Object key
= keys
.next();
229 String url
= map
.get(key
).toString();
230 installUrl(url
, installedBundles
);
233 for (int i
= 0; i
< urls
.size(); i
++) {
234 String url
= (String
) urls
.get(i
);
235 installUrl(url
, installedBundles
);
241 /** Actually install the provided URL */
242 protected void installUrl(String url
, Map installedBundles
) {
244 if (installedBundles
.containsKey(url
)) {
245 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
248 debug("Bundle " + bundle
.getSymbolicName()
249 + " already installed from " + url
);
251 Bundle bundle
= bundleContext
.installBundle(url
);
253 debug("Installed bundle " + bundle
.getSymbolicName()
256 } catch (BundleException e
) {
257 String message
= e
.getMessage();
258 if ((message
.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT
+ "\"") || message
259 .contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX
+ "\""))
260 && message
.contains("has already been installed")) {
261 // silent, in order to avoid warnings: we know that both
262 // have already been installed...
264 OsgiBootUtils
.warn("Could not install bundle from " + url
275 public void startBundles() {
276 // default and active start levels from System properties
277 Integer defaultStartLevel
= new Integer(Integer
.parseInt(OsgiBootUtils
278 .getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
, "4")));
279 Integer activeStartLevel
= new Integer(OsgiBootUtils
.getProperty(
280 PROP_OSGI_STARTLEVEL
, "6"));
282 SortedMap
/* <Integer, List<String>> */startLevels
= new TreeMap();
283 computeStartLevels(startLevels
, System
.getProperties(),
286 Iterator
/* <Integer> */levels
= startLevels
.keySet().iterator();
287 while (levels
.hasNext()) {
288 Integer level
= (Integer
) levels
.next();
289 boolean allStarted
= startBundles((List
) startLevels
.get(level
));
292 .warn("Not all bundles started for level " + level
);
293 if (level
.equals(activeStartLevel
))
294 break;// active start level reached
299 public static void computeStartLevels(
300 SortedMap
/* <Integer, List<String>> */startLevels
,
301 Properties properties
, Integer defaultStartLevel
) {
303 // default (and previously, only behaviour)
304 appendToStartLevels(startLevels
, defaultStartLevel
,
305 properties
.getProperty(PROP_ARGEO_OSGI_START
, ""));
307 // list argeo.osgi.start.* system properties
308 Iterator
/* <String> */keys
= properties
.keySet().iterator();
309 final String prefix
= PROP_ARGEO_OSGI_START
+ ".";
310 while (keys
.hasNext()) {
311 String key
= (String
) keys
.next();
312 if (key
.startsWith(prefix
)) {
314 String suffix
= key
.substring(prefix
.length());
315 String
[] tokens
= suffix
.split("\\.");
316 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
318 // first token is start level
319 startLevel
= new Integer(tokens
[0]);
320 } catch (NumberFormatException e
) {
321 startLevel
= defaultStartLevel
;
324 startLevel
= defaultStartLevel
;
326 // append bundle names
327 String bundleNames
= properties
.getProperty(key
);
328 appendToStartLevels(startLevels
, startLevel
, bundleNames
);
333 /** Append a comma-separated list of bundles to the start levels. */
334 private static void appendToStartLevels(
335 SortedMap
/* <Integer, List<String>> */startLevels
,
336 Integer startLevel
, String str
) {
337 if (str
== null || str
.trim().equals(""))
340 if (!startLevels
.containsKey(startLevel
))
341 startLevels
.put(startLevel
, new ArrayList());
342 String
[] bundleNames
= str
.split(",");
343 for (int i
= 0; i
< bundleNames
.length
; i
++) {
344 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
345 ((List
) startLevels
.get(startLevel
)).add(bundleNames
[i
]);
350 * Convenience method accepting a comma-separated list of bundle to start
354 public void startBundles(String bundlesToStartStr
) {
355 if (bundlesToStartStr
== null)
358 StringTokenizer st
= new StringTokenizer(bundlesToStartStr
, ",");
359 List bundlesToStart
= new ArrayList();
360 while (st
.hasMoreTokens()) {
361 String name
= st
.nextToken().trim();
362 bundlesToStart
.add(name
);
364 startBundles(bundlesToStart
);
368 * Start the provided list of bundles
370 * @return whether all bundlesa are now in active state
372 public boolean startBundles(List bundlesToStart
) {
373 if (bundlesToStart
.size() == 0)
376 // used to monitor ACTIVE states
377 List
/* <Bundle> */startedBundles
= new ArrayList();
378 // used to log the bundles not found
379 List
/* <String> */notFoundBundles
= new ArrayList(bundlesToStart
);
381 Bundle
[] bundles
= bundleContext
.getBundles();
382 long startBegin
= System
.currentTimeMillis();
383 for (int i
= 0; i
< bundles
.length
; i
++) {
384 Bundle bundle
= bundles
[i
];
385 String symbolicName
= bundle
.getSymbolicName();
386 if (bundlesToStart
.contains(symbolicName
))
391 debug("Bundle " + symbolicName
+ " started");
392 } catch (Exception e
) {
393 OsgiBootUtils
.warn("Start of bundle " + symbolicName
394 + " failed because of " + e
395 + ", maybe bundle is not yet resolved,"
396 + " waiting and trying again.");
397 waitForBundleResolvedOrActive(startBegin
, bundle
);
399 startedBundles
.add(bundle
);
401 notFoundBundles
.remove(symbolicName
);
402 } catch (Exception e
) {
403 OsgiBootUtils
.warn("Bundle " + symbolicName
404 + " cannot be started: " + e
.getMessage());
407 // was found even if start failed
408 notFoundBundles
.remove(symbolicName
);
412 for (int i
= 0; i
< notFoundBundles
.size(); i
++)
413 OsgiBootUtils
.warn("Bundle '" + notFoundBundles
.get(i
)
414 + "' not started because it was not found.");
416 // monitors that all bundles are started
417 long beginMonitor
= System
.currentTimeMillis();
418 boolean allStarted
= !(startedBundles
.size() > 0);
419 List
/* <String> */notStarted
= new ArrayList();
421 && (System
.currentTimeMillis() - beginMonitor
) < defaultTimeout
) {
422 notStarted
= new ArrayList();
424 for (int i
= 0; i
< startedBundles
.size(); i
++) {
425 Bundle bundle
= (Bundle
) startedBundles
.get(i
);
426 // TODO check behaviour of lazs bundles
427 if (bundle
.getState() != Bundle
.ACTIVE
) {
429 notStarted
.add(bundle
.getSymbolicName());
434 } catch (InterruptedException e
) {
438 long duration
= System
.currentTimeMillis() - beginMonitor
;
441 for (int i
= 0; i
< notStarted
.size(); i
++)
442 OsgiBootUtils
.warn("Bundle '" + notStarted
.get(i
)
443 + "' not ACTIVE after " + (duration
/ 1000) + "s");
451 /** Check unresolved bundles */
452 protected void checkUnresolved() {
454 ServiceReference packageAdminRef
= bundleContext
455 .getServiceReference(PackageAdmin
.class.getName());
456 PackageAdmin packageAdmin
= (PackageAdmin
) bundleContext
457 .getService(packageAdminRef
);
458 packageAdmin
.resolveBundles(null);
460 Bundle
[] bundles
= bundleContext
.getBundles();
461 List
/* Bundle */unresolvedBundles
= new ArrayList();
462 for (int i
= 0; i
< bundles
.length
; i
++) {
463 int bundleState
= bundles
[i
].getState();
464 if (!(bundleState
== Bundle
.ACTIVE
465 || bundleState
== Bundle
.RESOLVED
|| bundleState
== Bundle
.STARTING
))
466 unresolvedBundles
.add(bundles
[i
]);
469 if (unresolvedBundles
.size() != 0) {
470 OsgiBootUtils
.warn("Unresolved bundles " + unresolvedBundles
);
474 /** List packages exported twice. */
475 public Map
findPackagesExportedTwice() {
476 ServiceReference paSr
= bundleContext
477 .getServiceReference(PackageAdmin
.class.getName());
478 PackageAdmin packageAdmin
= (PackageAdmin
) bundleContext
481 // find packages exported twice
482 Bundle
[] bundles
= bundleContext
.getBundles();
483 Map
/* <String,Set<String>> */exportedPackages
= new TreeMap();
484 for (int i
= 0; i
< bundles
.length
; i
++) {
485 Bundle bundle
= bundles
[i
];
486 ExportedPackage
[] pkgs
= packageAdmin
.getExportedPackages(bundle
);
488 for (int j
= 0; j
< pkgs
.length
; j
++) {
489 String pkgName
= pkgs
[j
].getName();
490 if (!exportedPackages
.containsKey(pkgName
)) {
491 exportedPackages
.put(pkgName
, new TreeSet());
493 ((Set
) exportedPackages
.get(pkgName
)).add(bundle
494 .getSymbolicName() + "_" + bundle
.getVersion());
497 Map
/* <String,Set<String>> */duplicatePackages
= new TreeMap();
498 Iterator it
= exportedPackages
.keySet().iterator();
499 while (it
.hasNext()) {
500 String pkgName
= it
.next().toString();
501 Set bdles
= (Set
) exportedPackages
.get(pkgName
);
502 if (bdles
.size() > 1)
503 duplicatePackages
.put(pkgName
, bdles
);
505 return duplicatePackages
;
508 /** Waits for a bundle to become active or resolved */
509 protected void waitForBundleResolvedOrActive(long startBegin
, Bundle bundle
)
511 int originalState
= bundle
.getState();
512 if ((originalState
== Bundle
.RESOLVED
)
513 || (originalState
== Bundle
.ACTIVE
))
516 String originalStateStr
= OsgiBootUtils
.stateAsString(originalState
);
518 int currentState
= bundle
.getState();
519 while (!(currentState
== Bundle
.RESOLVED
|| currentState
== Bundle
.ACTIVE
)) {
520 long now
= System
.currentTimeMillis();
521 if ((now
- startBegin
) > defaultTimeout
* 10)
522 throw new Exception("Bundle " + bundle
.getSymbolicName()
523 + " was not RESOLVED or ACTIVE after "
524 + (now
- startBegin
) + "ms (originalState="
525 + originalStateStr
+ ", currentState="
526 + OsgiBootUtils
.stateAsString(currentState
) + ")");
530 } catch (InterruptedException e
) {
533 currentState
= bundle
.getState();
538 * EXPLICIT LOCATIONS INSTALLATION
540 /** Gets the list of resolved explicit URL locations. */
541 public List
getLocationsUrls() {
542 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
,
544 String bundleLocations
= OsgiBootUtils
545 .getProperty(PROP_ARGEO_OSGI_LOCATIONS
);
546 return getLocationsUrls(baseUrl
, bundleLocations
);
550 * Gets a list of URLs based on explicit locations, resolving placeholder
551 * ${...} containing system properties, e.g. ${user.home}.
553 public List
getLocationsUrls(String baseUrl
, String bundleLocations
) {
554 List urls
= new ArrayList();
556 if (bundleLocations
== null)
558 bundleLocations
= SystemPropertyUtils
559 .resolvePlaceholders(bundleLocations
);
561 debug(PROP_ARGEO_OSGI_LOCATIONS
+ "=" + bundleLocations
);
563 StringTokenizer st
= new StringTokenizer(bundleLocations
,
565 while (st
.hasMoreTokens()) {
566 urls
.add(locationToUrl(baseUrl
, st
.nextToken().trim()));
572 * BUNDLE PATTERNS INSTALLATION
575 * Computes a list of URLs based on Ant-like incluide/exclude patterns
576 * defined by ${argeo.osgi.bundles} with the following format:<br>
577 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
578 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
579 * without a new line causes a '.' to be appended with unexpected side
582 public List
getBundlesUrls() {
583 String bundlePatterns
= OsgiBootUtils
584 .getProperty(PROP_ARGEO_OSGI_BUNDLES
);
585 return getBundlesUrls(bundlePatterns
);
589 * Compute alist of URLs to install based on the provided patterns, with
592 public List
getBundlesUrls(String bundlePatterns
) {
593 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
,
595 return getBundlesUrls(baseUrl
, bundlePatterns
);
598 /** Implements the path matching logic */
599 List
getBundlesUrls(String baseUrl
, String bundlePatterns
) {
600 List urls
= new ArrayList();
601 if (bundlePatterns
== null)
604 bundlePatterns
= SystemPropertyUtils
605 .resolvePlaceholders(bundlePatterns
);
607 debug(PROP_ARGEO_OSGI_BUNDLES
+ "=" + bundlePatterns
608 + " (excludeSvn=" + excludeSvn
+ ")");
610 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
611 List bundlesSets
= new ArrayList();
612 while (st
.hasMoreTokens()) {
613 bundlesSets
.add(new BundlesSet(st
.nextToken()));
617 List included
= new ArrayList();
618 PathMatcher matcher
= new AntPathMatcher();
619 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
620 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
621 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
622 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
623 match(matcher
, included
, bundlesSet
.getDir(), null, pattern
);
628 List excluded
= new ArrayList();
629 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
630 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
631 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
632 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
633 match(matcher
, excluded
, bundlesSet
.getDir(), null, pattern
);
638 for (int i
= 0; i
< included
.size(); i
++) {
639 String fullPath
= (String
) included
.get(i
);
640 if (!excluded
.contains(fullPath
))
641 urls
.add(locationToUrl(baseUrl
, fullPath
));
648 * DISTRIBUTION JAR INSTALLATION
650 public List
getDistributionUrls() {
651 List urls
= new ArrayList();
652 String distributionUrl
= OsgiBootUtils
653 .getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL
);
654 if (distributionUrl
== null)
656 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
);
658 DistributionBundle distributionBundle
;
660 && !(distributionUrl
.startsWith("http") || distributionUrl
661 .startsWith("file"))) {
663 distributionBundle
= new DistributionBundle(baseUrl
,
666 distributionBundle
= new DistributionBundle(distributionUrl
);
668 distributionBundle
.setBaseUrl(baseUrl
);
671 distributionBundle
.processUrl();
672 return distributionBundle
.listUrls();
676 * MODULES LIST INSTALLATION (${argeo.osgi.modulesUrl})
679 * Downloads a list of URLs in CSV format from ${argeo.osgi.modulesUrl}:<br>
680 * <code>Bundle-SymbolicName,Bundle-Version,url</code>)<br>
681 * If ${argeo.osgi.baseUrl} is set, URLs will be considered relative paths
682 * and be concatenated with the base URL, typically the root of a Maven
687 public List
getModulesUrls() {
688 List urls
= new ArrayList();
689 String modulesUrlStr
= OsgiBootUtils
690 .getProperty(PROP_ARGEO_OSGI_MODULES_URL
);
691 if (modulesUrlStr
== null)
694 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
);
696 Map installedBundles
= getBundlesBySymbolicName();
698 BufferedReader reader
= null;
700 URL modulesUrl
= new URL(modulesUrlStr
);
701 reader
= new BufferedReader(new InputStreamReader(
702 modulesUrl
.openStream()));
704 while ((line
= reader
.readLine()) != null) {
705 StringTokenizer st
= new StringTokenizer(line
,
706 modulesUrlSeparator
);
707 String moduleName
= st
.nextToken();
708 String moduleVersion
= st
.nextToken();
709 String url
= st
.nextToken();
713 if (installedBundles
.containsKey(moduleName
)) {
714 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
715 String bundleVersion
= bundle
.getHeaders()
716 .get(Constants
.BUNDLE_VERSION
).toString();
717 int comp
= OsgiBootUtils
.compareVersions(bundleVersion
,
720 OsgiBootUtils
.warn("Installed version " + bundleVersion
721 + " of bundle " + moduleName
722 + " is newer than provided version "
724 } else if (comp
< 0) {
726 OsgiBootUtils
.info("Updated bundle " + moduleName
727 + " with version " + moduleVersion
728 + " (old version was " + bundleVersion
+ ")");
736 } catch (Exception e1
) {
737 throw new RuntimeException("Cannot read url " + modulesUrlStr
, e1
);
742 } catch (IOException e
) {
750 * HIGH LEVEL UTILITIES
752 /** Actually performs the matching logic. */
753 protected void match(PathMatcher matcher
, List matched
, String base
,
754 String currentPath
, String pattern
) {
755 if (currentPath
== null) {
757 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
758 File
[] files
= baseDir
.listFiles();
762 OsgiBootUtils
.warn("Base dir " + baseDir
763 + " has no children, exists=" + baseDir
.exists()
764 + ", isDirectory=" + baseDir
.isDirectory());
768 for (int i
= 0; i
< files
.length
; i
++)
769 match(matcher
, matched
, base
, files
[i
].getName(), pattern
);
771 String fullPath
= base
+ '/' + currentPath
;
772 if (matched
.contains(fullPath
))
773 return;// don't try deeper if already matched
775 boolean ok
= matcher
.match(pattern
, currentPath
);
777 // debug(currentPath + " " + (ok ? "" : " not ")
778 // + " matched with " + pattern);
780 matched
.add(fullPath
);
783 String newFullPath
= relativeToFullPath(base
, currentPath
);
784 File newFile
= new File(newFullPath
);
785 File
[] files
= newFile
.listFiles();
787 for (int i
= 0; i
< files
.length
; i
++) {
788 String newCurrentPath
= currentPath
+ '/'
789 + files
[i
].getName();
790 if (files
[i
].isDirectory()) {
791 if (matcher
.matchStart(pattern
, newCurrentPath
)) {
792 // recurse only if start matches
793 match(matcher
, matched
, base
, newCurrentPath
,
798 + " does not start match with "
803 boolean nonDirectoryOk
= matcher
.match(pattern
,
806 debug(currentPath
+ " " + (ok ?
"" : " not ")
807 + " matched with " + pattern
);
809 matched
.add(relativeToFullPath(base
,
818 protected void matchFile() {
823 * LOW LEVEL UTILITIES
826 * The bundles already installed. Key is location (String) , value is a
829 public Map
getBundlesByLocation() {
830 Map installedBundles
= new HashMap();
831 Bundle
[] bundles
= bundleContext
.getBundles();
832 for (int i
= 0; i
< bundles
.length
; i
++) {
833 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
835 return installedBundles
;
839 * The bundles already installed. Key is symbolic name (String) , value is a
842 public Map
getBundlesBySymbolicName() {
843 Map namedBundles
= new HashMap();
844 Bundle
[] bundles
= bundleContext
.getBundles();
845 for (int i
= 0; i
< bundles
.length
; i
++) {
846 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
851 /** Creates an URL from a location */
852 protected String
locationToUrl(String baseUrl
, String location
) {
853 // int extInd = location.lastIndexOf('.');
854 // String ext = null;
856 // ext = location.substring(extInd);
858 // if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
859 // return "file:" + location;
861 return baseUrl
+ location
;
864 /** Transforms a relative path in a full system path. */
865 protected String
relativeToFullPath(String basePath
, String relativePath
) {
866 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
869 private String
removeFilePrefix(String url
) {
870 if (url
.startsWith("file:"))
871 return url
.substring("file:".length());
872 else if (url
.startsWith("reference:file:"))
873 return url
.substring("reference:file:".length());
879 * Convenience method to avoid cluttering the code with
880 * OsgiBootUtils.debug()
882 protected void debug(Object obj
) {
883 OsgiBootUtils
.debug(obj
);
890 public boolean getDebug() {
894 public void setDebug(boolean debug
) {
898 public BundleContext
getBundleContext() {
899 return bundleContext
;
902 public void setInstallInLexicographicOrder(
903 boolean installInAlphabeticalOrder
) {
904 this.installInLexicographicOrder
= installInAlphabeticalOrder
;
907 public boolean isInstallInLexicographicOrder() {
908 return installInLexicographicOrder
;
911 public void setDefaultTimeout(long defaultTimeout
) {
912 this.defaultTimeout
= defaultTimeout
;
915 public void setModulesUrlSeparator(String modulesUrlSeparator
) {
916 this.modulesUrlSeparator
= modulesUrlSeparator
;
919 public boolean isExcludeSvn() {
923 public void setExcludeSvn(boolean excludeSvn
) {
924 this.excludeSvn
= excludeSvn
;
931 /** Intermediary structure used by path matching */
932 protected class BundlesSet
{
933 private String baseUrl
= "reference:file";// not used yet
934 private final String dir
;
935 private List includes
= new ArrayList();
936 private List excludes
= new ArrayList();
938 public BundlesSet(String def
) {
939 StringTokenizer st
= new StringTokenizer(def
, ";");
941 if (!st
.hasMoreTokens())
942 throw new RuntimeException("Base dir not defined.");
944 String dirPath
= st
.nextToken();
946 if (dirPath
.startsWith("file:"))
947 dirPath
= dirPath
.substring("file:".length());
949 dir
= new File(dirPath
.replace('/', File
.separatorChar
))
952 debug("Base dir: " + dir
);
953 } catch (IOException e
) {
954 throw new RuntimeException("Cannot convert to absolute path", e
);
957 while (st
.hasMoreTokens()) {
958 String tk
= st
.nextToken();
959 StringTokenizer stEq
= new StringTokenizer(tk
, "=");
960 String type
= stEq
.nextToken();
961 String pattern
= stEq
.nextToken();
962 if ("in".equals(type
) || "include".equals(type
)) {
963 includes
.add(pattern
);
964 } else if ("ex".equals(type
) || "exclude".equals(type
)) {
965 excludes
.add(pattern
);
966 } else if ("baseUrl".equals(type
)) {
969 System
.err
.println("Unkown bundles pattern type " + type
);
973 if (excludeSvn
&& !excludes
.contains(EXCLUDES_SVN_PATTERN
)) {
974 excludes
.add(EXCLUDES_SVN_PATTERN
);
978 public String
getDir() {
982 public List
getIncludes() {
986 public List
getExcludes() {
990 public String
getBaseUrl() {
996 /* @deprecated Doesn't seem to be used anymore. */
997 // public void installOrUpdateUrls(Map urls) {
998 // Map installedBundles = getBundles();
1000 // for (Iterator modules = urls.keySet().iterator(); modules.hasNext();) {
1001 // String moduleName = (String) modules.next();
1002 // String urlStr = (String) urls.get(moduleName);
1003 // if (installedBundles.containsKey(moduleName)) {
1004 // Bundle bundle = (Bundle) installedBundles.get(moduleName);
1007 // URL url = new URL(urlStr);
1008 // in = url.openStream();
1009 // bundle.update(in);
1010 // OsgiBootUtils.info("Updated bundle " + moduleName
1011 // + " from " + urlStr);
1012 // } catch (Exception e) {
1013 // throw new RuntimeException("Cannot update " + moduleName
1014 // + " from " + urlStr);
1019 // } catch (IOException e) {
1020 // e.printStackTrace();
1024 // Bundle bundle = bundleContext.installBundle(urlStr);
1026 // debug("Installed bundle " + bundle.getSymbolicName()
1027 // + " from " + urlStr);
1028 // } catch (BundleException e) {
1029 // OsgiBootUtils.warn("Could not install bundle from "
1030 // + urlStr + ": " + e.getMessage());