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
;
19 import java
.io
.IOException
;
20 import java
.util
.ArrayList
;
21 import java
.util
.HashMap
;
22 import java
.util
.Iterator
;
23 import java
.util
.List
;
25 import java
.util
.Properties
;
27 import java
.util
.SortedMap
;
28 import java
.util
.StringTokenizer
;
29 import java
.util
.TreeMap
;
30 import java
.util
.TreeSet
;
32 import org
.argeo
.osgi
.boot
.internal
.springutil
.AntPathMatcher
;
33 import org
.argeo
.osgi
.boot
.internal
.springutil
.PathMatcher
;
34 import org
.argeo
.osgi
.boot
.internal
.springutil
.SystemPropertyUtils
;
35 import org
.osgi
.framework
.Bundle
;
36 import org
.osgi
.framework
.BundleContext
;
37 import org
.osgi
.framework
.BundleException
;
38 import org
.osgi
.framework
.ServiceReference
;
39 import org
.osgi
.service
.packageadmin
.ExportedPackage
;
40 import org
.osgi
.service
.packageadmin
.PackageAdmin
;
43 * Basic provisioning of an OSGi runtime via file path patterns and system
44 * properties. The approach is to generate list of URLs based on various
45 * methods, configured via properties.
47 public class OsgiBoot
{
49 public final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
= "osgi.bundles.defaultStartLevel";
51 public final static String PROP_ARGEO_OSGI_DATA_DIR
= "argeo.osgi.data.dir";
53 public final static String PROP_ARGEO_OSGI_START
= "argeo.osgi.start";
54 public final static String PROP_ARGEO_OSGI_BUNDLES
= "argeo.osgi.bundles";
55 // public final static String PROP_ARGEO_OSGI_LOCATIONS =
56 // "argeo.osgi.locations";
57 public final static String PROP_ARGEO_OSGI_BASE_URL
= "argeo.osgi.baseUrl";
58 // public final static String PROP_ARGEO_OSGI_MODULES_URL =
59 // "argeo.osgi.modulesUrl";
60 public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL
= "argeo.osgi.distributionUrl";
63 public final static String PROP_ARGEO_OSGI_BOOT_DEBUG
= "argeo.osgi.boot.debug";
64 public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN
= "argeo.osgi.boot.excludeSvn";
65 // public final static String
66 // PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER =
67 // "argeo.osgi.boot.installInLexicographicOrder";
69 public final static String PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
= "argeo.osgi.boot.defaultTimeout";
70 // public final static String PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR =
71 // "argeo.osgi.boot.modulesUrlSeparator";
72 public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE
= "argeo.osgi.boot.systemPropertiesFile";
73 public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS
= "argeo.osgi.boot.appclass";
74 public final static String PROP_ARGEO_OSGI_BOOT_APPARGS
= "argeo.osgi.boot.appargs";
76 public final static String DEFAULT_BASE_URL
= "reference:file:";
77 public final static String EXCLUDES_SVN_PATTERN
= "**/.svn/**";
79 // OSGi system properties
80 public final static String PROP_OSGI_STARTLEVEL
= "osgi.startLevel";
81 public final static String INSTANCE_AREA_PROP
= "osgi.instance.area";
82 public final static String INSTANCE_AREA_DEFAULT_PROP
= "osgi.instance.area.default";
85 public final static String SYMBOLIC_NAME_OSGI_BOOT
= "org.argeo.osgi.boot";
86 public final static String SYMBOLIC_NAME_EQUINOX
= "org.eclipse.osgi";
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"))
96 /** Default is 10s (set in constructor) */
97 private long defaultTimeout
;
99 /** Default is ',' (set in constructor) */
100 // private String modulesUrlSeparator = ",";
102 private final BundleContext bundleContext
;
108 public OsgiBoot(BundleContext bundleContext
) {
109 this.bundleContext
= bundleContext
;
110 defaultTimeout
= Long
.parseLong(OsgiBootUtils
.getProperty(
111 PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
, "10000"));
112 // modulesUrlSeparator = OsgiBootUtils.getProperty(
113 // PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR, ",");
114 initSystemProperties();
118 * Set additional system properties, especially ${argeo.osgi.data.dir} as an
119 * OS file path (and not a file:// URL)
121 protected void initSystemProperties() {
122 String osgiInstanceArea
= System
.getProperty(INSTANCE_AREA_PROP
);
123 String osgiInstanceAreaDefault
= System
124 .getProperty(INSTANCE_AREA_DEFAULT_PROP
);
125 String tempDir
= System
.getProperty("java.io.tmpdir");
128 if (osgiInstanceArea
!= null) {
129 // within OSGi with -data specified
130 osgiInstanceArea
= removeFilePrefix(osgiInstanceArea
);
131 dataDir
= new File(osgiInstanceArea
);
132 } else if (osgiInstanceAreaDefault
!= null) {
133 // within OSGi without -data specified
134 osgiInstanceAreaDefault
= removeFilePrefix(osgiInstanceAreaDefault
);
135 dataDir
= new File(osgiInstanceAreaDefault
);
136 } else {// outside OSGi
137 dataDir
= new File(tempDir
+ File
.separator
+ "argeoOsgiData");
139 System
.setProperty(PROP_ARGEO_OSGI_DATA_DIR
, dataDir
.getAbsolutePath());
145 /** Bootstraps the OSGi runtime */
146 public void bootstrap() {
147 long begin
= System
.currentTimeMillis();
148 System
.out
.println();
149 OsgiBootUtils
.info("OSGi bootstrap starting...");
150 OsgiBootUtils
.info("Writable data directory : "
151 + System
.getProperty(PROP_ARGEO_OSGI_DATA_DIR
)
152 + " (set as system property " + PROP_ARGEO_OSGI_DATA_DIR
+ ")");
153 installUrls(getBundlesUrls());
154 // installUrls(getLocationsUrls());
155 // installUrls(getModulesUrls());
156 installUrls(getDistributionUrls());
159 long duration
= System
.currentTimeMillis() - begin
;
160 OsgiBootUtils
.info("OSGi bootstrap completed in "
161 + Math
.round(((double) duration
) / 1000) + "s (" + duration
162 + "ms), " + bundleContext
.getBundles().length
+ " bundles");
164 // display packages exported twice
166 Map
<String
, Set
<String
>> duplicatePackages
= findPackagesExportedTwice();
167 if (duplicatePackages
.size() > 0) {
168 OsgiBootUtils
.info("Packages exported twice:");
169 Iterator
<String
> it
= duplicatePackages
.keySet().iterator();
170 while (it
.hasNext()) {
171 String pkgName
= it
.next();
172 OsgiBootUtils
.info(pkgName
);
173 Set
<String
> bdles
= duplicatePackages
.get(pkgName
);
174 Iterator
<String
> bdlesIt
= bdles
.iterator();
175 while (bdlesIt
.hasNext())
176 OsgiBootUtils
.info(" " + bdlesIt
.next());
181 System
.out
.println();
187 /** Install a single url. Convenience method. */
188 public Bundle
installUrl(String url
) {
189 List
<String
> urls
= new ArrayList
<String
>();
192 return (Bundle
) getBundlesByLocation().get(url
);
195 /** Install the bundles at this URL list. */
196 public void installUrls(List
<String
> urls
) {
197 Map
<String
, Bundle
> installedBundles
= getBundlesByLocation();
199 // if (installInLexicographicOrder) {
200 // SortedMap map = new TreeMap();
202 // for (int i = 0; i < urls.size(); i++) {
203 // String url = (String) urls.get(i);
204 // int index = url.lastIndexOf('/');
207 // fileName = url.substring(index + 1);
210 // map.put(fileName, url);
214 // Iterator keys = map.keySet().iterator();
215 // while (keys.hasNext()) {
216 // Object key = keys.next();
217 // String url = map.get(key).toString();
218 // installUrl(url, installedBundles);
221 for (int i
= 0; i
< urls
.size(); i
++) {
222 String url
= (String
) urls
.get(i
);
223 installUrl(url
, installedBundles
);
229 /** Actually install the provided URL */
230 protected void installUrl(String url
, Map
<String
, Bundle
> installedBundles
) {
232 if (installedBundles
.containsKey(url
)) {
233 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
236 debug("Bundle " + bundle
.getSymbolicName()
237 + " already installed from " + url
);
239 Bundle bundle
= bundleContext
.installBundle(url
);
241 debug("Installed bundle " + bundle
.getSymbolicName()
244 } catch (BundleException e
) {
245 String message
= e
.getMessage();
246 if ((message
.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT
+ "\"") || message
247 .contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX
+ "\""))
248 && message
.contains("has already been installed")) {
249 // silent, in order to avoid warnings: we know that both
250 // have already been installed...
252 OsgiBootUtils
.warn("Could not install bundle from " + url
263 public void startBundles() {
264 // default and active start levels from System properties
265 Integer defaultStartLevel
= new Integer(Integer
.parseInt(OsgiBootUtils
266 .getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL
, "4")));
267 Integer activeStartLevel
= new Integer(OsgiBootUtils
.getProperty(
268 PROP_OSGI_STARTLEVEL
, "6"));
270 SortedMap
<Integer
, List
<String
>> startLevels
= new TreeMap
<Integer
, List
<String
>>();
271 computeStartLevels(startLevels
, System
.getProperties(),
274 Iterator
<Integer
> levels
= startLevels
.keySet().iterator();
275 while (levels
.hasNext()) {
276 Integer level
= (Integer
) levels
.next();
277 boolean allStarted
= startBundles(startLevels
.get(level
));
280 .warn("Not all bundles started for level " + level
);
281 if (level
.equals(activeStartLevel
))
282 break;// active start level reached
287 public static void computeStartLevels(
288 SortedMap
<Integer
, List
<String
>> startLevels
,
289 Properties properties
, Integer defaultStartLevel
) {
291 // default (and previously, only behaviour)
292 appendToStartLevels(startLevels
, defaultStartLevel
,
293 properties
.getProperty(PROP_ARGEO_OSGI_START
, ""));
295 // list argeo.osgi.start.* system properties
296 Iterator
<Object
> keys
= properties
.keySet().iterator();
297 final String prefix
= PROP_ARGEO_OSGI_START
+ ".";
298 while (keys
.hasNext()) {
299 String key
= keys
.next().toString();
300 if (key
.startsWith(prefix
)) {
302 String suffix
= key
.substring(prefix
.length());
303 String
[] tokens
= suffix
.split("\\.");
304 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
306 // first token is start level
307 startLevel
= new Integer(tokens
[0]);
308 } catch (NumberFormatException e
) {
309 startLevel
= defaultStartLevel
;
312 startLevel
= defaultStartLevel
;
314 // append bundle names
315 String bundleNames
= properties
.getProperty(key
);
316 appendToStartLevels(startLevels
, startLevel
, bundleNames
);
321 /** Append a comma-separated list of bundles to the start levels. */
322 private static void appendToStartLevels(
323 SortedMap
<Integer
, List
<String
>> startLevels
, Integer startLevel
,
325 if (str
== null || str
.trim().equals(""))
328 if (!startLevels
.containsKey(startLevel
))
329 startLevels
.put(startLevel
, new ArrayList
<String
>());
330 String
[] bundleNames
= str
.split(",");
331 for (int i
= 0; i
< bundleNames
.length
; i
++) {
332 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
333 (startLevels
.get(startLevel
)).add(bundleNames
[i
]);
338 * Convenience method accepting a comma-separated list of bundle to start
342 // public void startBundles(String bundlesToStartStr) {
343 // if (bundlesToStartStr == null)
346 // StringTokenizer st = new StringTokenizer(bundlesToStartStr, ",");
347 // List bundlesToStart = new ArrayList();
348 // while (st.hasMoreTokens()) {
349 // String name = st.nextToken().trim();
350 // bundlesToStart.add(name);
352 // startBundles(bundlesToStart);
356 * Start the provided list of bundles
358 * @return whether all bundlesa are now in active state
360 public boolean startBundles(List
<String
> bundlesToStart
) {
361 if (bundlesToStart
.size() == 0)
364 // used to monitor ACTIVE states
365 List
<Bundle
> startedBundles
= new ArrayList
<Bundle
>();
366 // used to log the bundles not found
367 List
<String
> notFoundBundles
= new ArrayList
<String
>(bundlesToStart
);
369 Bundle
[] bundles
= bundleContext
.getBundles();
370 long startBegin
= System
.currentTimeMillis();
371 for (int i
= 0; i
< bundles
.length
; i
++) {
372 Bundle bundle
= bundles
[i
];
373 String symbolicName
= bundle
.getSymbolicName();
374 if (bundlesToStart
.contains(symbolicName
))
379 debug("Bundle " + symbolicName
+ " started");
380 } catch (Exception e
) {
381 OsgiBootUtils
.warn("Start of bundle " + symbolicName
382 + " failed because of " + e
383 + ", maybe bundle is not yet resolved,"
384 + " waiting and trying again.");
385 waitForBundleResolvedOrActive(startBegin
, bundle
);
387 startedBundles
.add(bundle
);
389 notFoundBundles
.remove(symbolicName
);
390 } catch (Exception e
) {
391 OsgiBootUtils
.warn("Bundle " + symbolicName
392 + " cannot be started: " + e
.getMessage());
395 // was found even if start failed
396 notFoundBundles
.remove(symbolicName
);
400 for (int i
= 0; i
< notFoundBundles
.size(); i
++)
401 OsgiBootUtils
.warn("Bundle '" + notFoundBundles
.get(i
)
402 + "' not started because it was not found.");
404 // monitors that all bundles are started
405 long beginMonitor
= System
.currentTimeMillis();
406 boolean allStarted
= !(startedBundles
.size() > 0);
407 List
<String
> notStarted
= new ArrayList
<String
>();
409 && (System
.currentTimeMillis() - beginMonitor
) < defaultTimeout
) {
410 notStarted
= new ArrayList
<String
>();
412 for (int i
= 0; i
< startedBundles
.size(); i
++) {
413 Bundle bundle
= (Bundle
) startedBundles
.get(i
);
414 // TODO check behaviour of lazs bundles
415 if (bundle
.getState() != Bundle
.ACTIVE
) {
417 notStarted
.add(bundle
.getSymbolicName());
422 } catch (InterruptedException e
) {
426 long duration
= System
.currentTimeMillis() - beginMonitor
;
429 for (int i
= 0; i
< notStarted
.size(); i
++)
430 OsgiBootUtils
.warn("Bundle '" + notStarted
.get(i
)
431 + "' not ACTIVE after " + (duration
/ 1000) + "s");
439 /** Check unresolved bundles */
440 @SuppressWarnings("deprecation")
441 protected void checkUnresolved() {
443 ServiceReference
<PackageAdmin
> packageAdminRef
= bundleContext
444 .getServiceReference(PackageAdmin
.class);
445 PackageAdmin packageAdmin
= (PackageAdmin
) bundleContext
446 .getService(packageAdminRef
);
447 packageAdmin
.resolveBundles(null);
449 Bundle
[] bundles
= bundleContext
.getBundles();
450 List
<Bundle
> unresolvedBundles
= new ArrayList
<Bundle
>();
451 for (int i
= 0; i
< bundles
.length
; i
++) {
452 int bundleState
= bundles
[i
].getState();
453 if (!(bundleState
== Bundle
.ACTIVE
454 || bundleState
== Bundle
.RESOLVED
|| bundleState
== Bundle
.STARTING
))
455 unresolvedBundles
.add(bundles
[i
]);
458 if (unresolvedBundles
.size() != 0) {
459 OsgiBootUtils
.warn("Unresolved bundles " + unresolvedBundles
);
463 /** List packages exported twice. */
464 @SuppressWarnings("deprecation")
465 public Map
<String
, Set
<String
>> findPackagesExportedTwice() {
466 ServiceReference
<PackageAdmin
> paSr
= bundleContext
467 .getServiceReference(PackageAdmin
.class);
468 PackageAdmin packageAdmin
= (PackageAdmin
) bundleContext
471 // find packages exported twice
472 Bundle
[] bundles
= bundleContext
.getBundles();
473 Map
<String
, Set
<String
>> exportedPackages
= new TreeMap
<String
, Set
<String
>>();
474 for (int i
= 0; i
< bundles
.length
; i
++) {
475 Bundle bundle
= bundles
[i
];
476 ExportedPackage
[] pkgs
= packageAdmin
.getExportedPackages(bundle
);
478 for (int j
= 0; j
< pkgs
.length
; j
++) {
479 String pkgName
= pkgs
[j
].getName();
480 if (!exportedPackages
.containsKey(pkgName
)) {
481 exportedPackages
.put(pkgName
, new TreeSet
<String
>());
483 (exportedPackages
.get(pkgName
)).add(bundle
484 .getSymbolicName() + "_" + bundle
.getVersion());
487 Map
<String
, Set
<String
>> duplicatePackages
= new TreeMap
<String
, Set
<String
>>();
488 Iterator
<String
> it
= exportedPackages
.keySet().iterator();
489 while (it
.hasNext()) {
490 String pkgName
= it
.next().toString();
491 Set
<String
> bdles
= exportedPackages
.get(pkgName
);
492 if (bdles
.size() > 1)
493 duplicatePackages
.put(pkgName
, bdles
);
495 return duplicatePackages
;
498 /** Waits for a bundle to become active or resolved */
499 protected void waitForBundleResolvedOrActive(long startBegin
, Bundle bundle
)
501 int originalState
= bundle
.getState();
502 if ((originalState
== Bundle
.RESOLVED
)
503 || (originalState
== Bundle
.ACTIVE
))
506 String originalStateStr
= OsgiBootUtils
.stateAsString(originalState
);
508 int currentState
= bundle
.getState();
509 while (!(currentState
== Bundle
.RESOLVED
|| currentState
== Bundle
.ACTIVE
)) {
510 long now
= System
.currentTimeMillis();
511 if ((now
- startBegin
) > defaultTimeout
* 10)
512 throw new Exception("Bundle " + bundle
.getSymbolicName()
513 + " was not RESOLVED or ACTIVE after "
514 + (now
- startBegin
) + "ms (originalState="
515 + originalStateStr
+ ", currentState="
516 + OsgiBootUtils
.stateAsString(currentState
) + ")");
520 } catch (InterruptedException e
) {
523 currentState
= bundle
.getState();
528 * EXPLICIT LOCATIONS INSTALLATION
530 /** Gets the list of resolved explicit URL locations. */
531 // public List getLocationsUrls() {
532 // String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL,
533 // DEFAULT_BASE_URL);
534 // String bundleLocations = OsgiBootUtils
535 // .getProperty(PROP_ARGEO_OSGI_LOCATIONS);
536 // return getLocationsUrls(baseUrl, bundleLocations);
540 * Gets a list of URLs based on explicit locations, resolving placeholder
541 * ${...} containing system properties, e.g. ${user.home}.
543 // public List<String> getLocationsUrls(String baseUrl, String bundleLocations) {
544 // List<String> urls = new ArrayList<String>();
546 // if (bundleLocations == null)
548 // bundleLocations = SystemPropertyUtils
549 // .resolvePlaceholders(bundleLocations);
551 // // debug(PROP_ARGEO_OSGI_LOCATIONS + "=" + bundleLocations);
553 // StringTokenizer st = new StringTokenizer(bundleLocations,
554 // File.pathSeparator);
555 // while (st.hasMoreTokens()) {
556 // urls.add(locationToUrl(baseUrl, st.nextToken().trim()));
562 * BUNDLE PATTERNS INSTALLATION
565 * Computes a list of URLs based on Ant-like include/exclude patterns
566 * defined by ${argeo.osgi.bundles} with the following format:<br>
567 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
568 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
569 * without a new line causes a '.' to be appended with unexpected side
572 public List
<String
> getBundlesUrls() {
573 String bundlePatterns
= OsgiBootUtils
574 .getProperty(PROP_ARGEO_OSGI_BUNDLES
);
575 return getBundlesUrls(bundlePatterns
);
579 * Compute alist of URLs to install based on the provided patterns, with
582 public List
<String
> getBundlesUrls(String bundlePatterns
) {
583 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
,
585 return getBundlesUrls(baseUrl
, bundlePatterns
);
588 /** Implements the path matching logic */
589 List
<String
> getBundlesUrls(String baseUrl
, String bundlePatterns
) {
590 List
<String
> urls
= new ArrayList
<String
>();
591 if (bundlePatterns
== null)
594 bundlePatterns
= SystemPropertyUtils
595 .resolvePlaceholders(bundlePatterns
);
597 debug(PROP_ARGEO_OSGI_BUNDLES
+ "=" + bundlePatterns
598 + " (excludeSvn=" + excludeSvn
+ ")");
600 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
601 List
<BundlesSet
> bundlesSets
= new ArrayList
<BundlesSet
>();
602 while (st
.hasMoreTokens()) {
603 String token
= st
.nextToken();
604 if (new File(token
).exists()) {
605 String url
= locationToUrl(baseUrl
, token
);
608 bundlesSets
.add(new BundlesSet(token
));
612 List
<String
> included
= new ArrayList
<String
>();
613 PathMatcher matcher
= new AntPathMatcher();
614 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
615 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
616 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
617 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
618 match(matcher
, included
, bundlesSet
.getDir(), null, pattern
);
623 List
<String
> excluded
= new ArrayList
<String
>();
624 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
625 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
626 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
627 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
628 match(matcher
, excluded
, bundlesSet
.getDir(), null, pattern
);
633 for (int i
= 0; i
< included
.size(); i
++) {
634 String fullPath
= (String
) included
.get(i
);
635 if (!excluded
.contains(fullPath
))
636 urls
.add(locationToUrl(baseUrl
, fullPath
));
643 * DISTRIBUTION JAR INSTALLATION
645 public List
<String
> getDistributionUrls() {
646 List
<String
> urls
= new ArrayList
<String
>();
647 String distributionUrl
= OsgiBootUtils
648 .getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL
);
649 if (distributionUrl
== null)
651 String baseUrl
= OsgiBootUtils
.getProperty(PROP_ARGEO_OSGI_BASE_URL
);
653 DistributionBundle distributionBundle
;
655 && !(distributionUrl
.startsWith("http") || distributionUrl
656 .startsWith("file"))) {
658 distributionBundle
= new DistributionBundle(baseUrl
,
661 distributionBundle
= new DistributionBundle(distributionUrl
);
663 distributionBundle
.setBaseUrl(baseUrl
);
666 distributionBundle
.processUrl();
667 return distributionBundle
.listUrls();
671 * MODULES LIST INSTALLATION (${argeo.osgi.modulesUrl})
674 * Downloads a list of URLs in CSV format from ${argeo.osgi.modulesUrl}:<br>
675 * <code>Bundle-SymbolicName,Bundle-Version,url</code>)<br>
676 * If ${argeo.osgi.baseUrl} is set, URLs will be considered relative paths
677 * and be concatenated with the base URL, typically the root of a Maven
682 // public List getModulesUrls() {
683 // List urls = new ArrayList();
684 // String modulesUrlStr = OsgiBootUtils
685 // .getProperty(PROP_ARGEO_OSGI_MODULES_URL);
686 // if (modulesUrlStr == null)
689 // String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL);
691 // Map installedBundles = getBundlesBySymbolicName();
693 // BufferedReader reader = null;
695 // URL modulesUrl = new URL(modulesUrlStr);
696 // reader = new BufferedReader(new InputStreamReader(
697 // modulesUrl.openStream()));
698 // String line = null;
699 // while ((line = reader.readLine()) != null) {
700 // StringTokenizer st = new StringTokenizer(line,
701 // modulesUrlSeparator);
702 // String moduleName = st.nextToken();
703 // String moduleVersion = st.nextToken();
704 // String url = st.nextToken();
705 // if (baseUrl != null)
706 // url = baseUrl + url;
708 // if (installedBundles.containsKey(moduleName)) {
709 // Bundle bundle = (Bundle) installedBundles.get(moduleName);
710 // String bundleVersion = bundle.getHeaders()
711 // .get(Constants.BUNDLE_VERSION).toString();
712 // int comp = OsgiBootUtils.compareVersions(bundleVersion,
715 // OsgiBootUtils.warn("Installed version " + bundleVersion
716 // + " of bundle " + moduleName
717 // + " is newer than provided version "
719 // } else if (comp < 0) {
721 // OsgiBootUtils.info("Updated bundle " + moduleName
722 // + " with version " + moduleVersion
723 // + " (old version was " + bundleVersion + ")");
731 // } catch (Exception e1) {
732 // throw new RuntimeException("Cannot read url " + modulesUrlStr, e1);
734 // if (reader != null)
737 // } catch (IOException e) {
738 // e.printStackTrace();
745 * HIGH LEVEL UTILITIES
747 /** Actually performs the matching logic. */
748 protected void match(PathMatcher matcher
, List
<String
> matched
,
749 String base
, String currentPath
, String pattern
) {
750 if (currentPath
== null) {
752 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
753 File
[] files
= baseDir
.listFiles();
757 OsgiBootUtils
.warn("Base dir " + baseDir
758 + " has no children, exists=" + baseDir
.exists()
759 + ", isDirectory=" + baseDir
.isDirectory());
763 for (int i
= 0; i
< files
.length
; i
++)
764 match(matcher
, matched
, base
, files
[i
].getName(), pattern
);
766 String fullPath
= base
+ '/' + currentPath
;
767 if (matched
.contains(fullPath
))
768 return;// don't try deeper if already matched
770 boolean ok
= matcher
.match(pattern
, currentPath
);
772 // debug(currentPath + " " + (ok ? "" : " not ")
773 // + " matched with " + pattern);
775 matched
.add(fullPath
);
778 String newFullPath
= relativeToFullPath(base
, currentPath
);
779 File newFile
= new File(newFullPath
);
780 File
[] files
= newFile
.listFiles();
782 for (int i
= 0; i
< files
.length
; i
++) {
783 String newCurrentPath
= currentPath
+ '/'
784 + files
[i
].getName();
785 if (files
[i
].isDirectory()) {
786 if (matcher
.matchStart(pattern
, newCurrentPath
)) {
787 // recurse only if start matches
788 match(matcher
, matched
, base
, newCurrentPath
,
793 + " does not start match with "
798 boolean nonDirectoryOk
= matcher
.match(pattern
,
801 debug(currentPath
+ " " + (ok ?
"" : " not ")
802 + " matched with " + pattern
);
804 matched
.add(relativeToFullPath(base
,
813 protected void matchFile() {
818 * LOW LEVEL UTILITIES
821 * The bundles already installed. Key is location (String) , value is a
824 public Map
<String
, Bundle
> getBundlesByLocation() {
825 Map
<String
, Bundle
> installedBundles
= new HashMap
<String
, Bundle
>();
826 Bundle
[] bundles
= bundleContext
.getBundles();
827 for (int i
= 0; i
< bundles
.length
; i
++) {
828 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
830 return installedBundles
;
834 * The bundles already installed. Key is symbolic name (String) , value is a
837 public Map
<String
, Bundle
> getBundlesBySymbolicName() {
838 Map
<String
, Bundle
> namedBundles
= new HashMap
<String
, Bundle
>();
839 Bundle
[] bundles
= bundleContext
.getBundles();
840 for (int i
= 0; i
< bundles
.length
; i
++) {
841 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
846 /** Creates an URL from a location */
847 protected String
locationToUrl(String baseUrl
, String location
) {
848 // int extInd = location.lastIndexOf('.');
849 // String ext = null;
851 // ext = location.substring(extInd);
853 // if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
854 // return "file:" + location;
856 return baseUrl
+ location
;
859 /** Transforms a relative path in a full system path. */
860 protected String
relativeToFullPath(String basePath
, String relativePath
) {
861 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
864 private String
removeFilePrefix(String url
) {
865 if (url
.startsWith("file:"))
866 return url
.substring("file:".length());
867 else if (url
.startsWith("reference:file:"))
868 return url
.substring("reference:file:".length());
874 * Convenience method to avoid cluttering the code with
875 * OsgiBootUtils.debug()
877 protected void debug(Object obj
) {
878 OsgiBootUtils
.debug(obj
);
885 public boolean getDebug() {
889 public void setDebug(boolean debug
) {
893 public BundleContext
getBundleContext() {
894 return bundleContext
;
897 // public void setInstallInLexicographicOrder(
898 // boolean installInAlphabeticalOrder) {
899 // this.installInLexicographicOrder = installInAlphabeticalOrder;
902 // public boolean isInstallInLexicographicOrder() {
903 // return installInLexicographicOrder;
906 public void setDefaultTimeout(long defaultTimeout
) {
907 this.defaultTimeout
= defaultTimeout
;
910 // public void setModulesUrlSeparator(String modulesUrlSeparator) {
911 // this.modulesUrlSeparator = modulesUrlSeparator;
914 public boolean isExcludeSvn() {
918 public void setExcludeSvn(boolean excludeSvn
) {
919 this.excludeSvn
= excludeSvn
;
926 /** Intermediary structure used by path matching */
927 protected class BundlesSet
{
928 private String baseUrl
= "reference:file";// not used yet
929 private final String dir
;
930 private List
<String
> includes
= new ArrayList
<String
>();
931 private List
<String
> excludes
= new ArrayList
<String
>();
933 public BundlesSet(String def
) {
934 StringTokenizer st
= new StringTokenizer(def
, ";");
936 if (!st
.hasMoreTokens())
937 throw new RuntimeException("Base dir not defined.");
939 String dirPath
= st
.nextToken();
941 if (dirPath
.startsWith("file:"))
942 dirPath
= dirPath
.substring("file:".length());
944 dir
= new File(dirPath
.replace('/', File
.separatorChar
))
947 debug("Base dir: " + dir
);
948 } catch (IOException e
) {
949 throw new RuntimeException("Cannot convert to absolute path", e
);
952 while (st
.hasMoreTokens()) {
953 String tk
= st
.nextToken();
954 StringTokenizer stEq
= new StringTokenizer(tk
, "=");
955 String type
= stEq
.nextToken();
956 String pattern
= stEq
.nextToken();
957 if ("in".equals(type
) || "include".equals(type
)) {
958 includes
.add(pattern
);
959 } else if ("ex".equals(type
) || "exclude".equals(type
)) {
960 excludes
.add(pattern
);
961 } else if ("baseUrl".equals(type
)) {
964 System
.err
.println("Unkown bundles pattern type " + type
);
968 if (excludeSvn
&& !excludes
.contains(EXCLUDES_SVN_PATTERN
)) {
969 excludes
.add(EXCLUDES_SVN_PATTERN
);
973 public String
getDir() {
977 public List
<String
> getIncludes() {
981 public List
<String
> getExcludes() {
985 public String
getBaseUrl() {
991 /* @deprecated Doesn't seem to be used anymore. */
992 // public void installOrUpdateUrls(Map urls) {
993 // Map installedBundles = getBundles();
995 // for (Iterator modules = urls.keySet().iterator(); modules.hasNext();) {
996 // String moduleName = (String) modules.next();
997 // String urlStr = (String) urls.get(moduleName);
998 // if (installedBundles.containsKey(moduleName)) {
999 // Bundle bundle = (Bundle) installedBundles.get(moduleName);
1002 // URL url = new URL(urlStr);
1003 // in = url.openStream();
1004 // bundle.update(in);
1005 // OsgiBootUtils.info("Updated bundle " + moduleName
1006 // + " from " + urlStr);
1007 // } catch (Exception e) {
1008 // throw new RuntimeException("Cannot update " + moduleName
1009 // + " from " + urlStr);
1014 // } catch (IOException e) {
1015 // e.printStackTrace();
1019 // Bundle bundle = bundleContext.installBundle(urlStr);
1021 // debug("Installed bundle " + bundle.getSymbolicName()
1022 // + " from " + urlStr);
1023 // } catch (BundleException e) {
1024 // OsgiBootUtils.warn("Could not install bundle from "
1025 // + urlStr + ": " + e.getMessage());