]> git.argeo.org Git - lgpl/argeo-commons.git/blob - osgi/runtime/org.argeo.osgi.boot/src/main/java/org/argeo/osgi/boot/OsgiBoot.java
[maven-release-plugin] prepare for next development iteration
[lgpl/argeo-commons.git] / osgi / runtime / org.argeo.osgi.boot / src / main / java / org / argeo / osgi / boot / OsgiBoot.java
1 /*
2 * Copyright (C) 2007-2012 Mathieu Baudier
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16 package org.argeo.osgi.boot;
17
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStreamReader;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.SortedMap;
30 import java.util.StringTokenizer;
31 import java.util.TreeMap;
32 import java.util.TreeSet;
33
34 import org.argeo.osgi.boot.internal.springutil.AntPathMatcher;
35 import org.argeo.osgi.boot.internal.springutil.PathMatcher;
36 import org.argeo.osgi.boot.internal.springutil.SystemPropertyUtils;
37 import org.osgi.framework.Bundle;
38 import org.osgi.framework.BundleContext;
39 import org.osgi.framework.BundleException;
40 import org.osgi.framework.Constants;
41 import org.osgi.framework.ServiceReference;
42 import org.osgi.service.packageadmin.ExportedPackage;
43 import org.osgi.service.packageadmin.PackageAdmin;
44
45 /**
46 * Basic provisioning of an OSGi runtime via file path patterns and system
47 * properties. Java 1.4 compatible.<br>
48 * The approach is to generate list of URLs based on various methods, configured
49 * via system properties.
50 */
51 public class OsgiBoot {
52 public final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.osgi.boot";
53 public final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
54
55 public final static String PROP_ARGEO_OSGI_DATA_DIR = "argeo.osgi.data.dir";
56
57 public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
58 public final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
59 public final static String PROP_ARGEO_OSGI_LOCATIONS = "argeo.osgi.locations";
60 public final static String PROP_ARGEO_OSGI_BASE_URL = "argeo.osgi.baseUrl";
61 /** Use org.argeo.osgi */
62 public final static String PROP_ARGEO_OSGI_MODULES_URL = "argeo.osgi.modulesUrl";
63
64 // booleans
65 public final static String PROP_ARGEO_OSGI_BOOT_DEBUG = "argeo.osgi.boot.debug";
66 public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN = "argeo.osgi.boot.excludeSvn";
67 public final static String PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER = "argeo.osgi.boot.installInLexicographicOrder";
68
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 = "argeo.osgi.boot.modulesUrlSeparator";
71 public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE = "argeo.osgi.boot.systemPropertiesFile";
72 public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS = "argeo.osgi.boot.appclass";
73 public final static String PROP_ARGEO_OSGI_BOOT_APPARGS = "argeo.osgi.boot.appargs";
74
75 public final static String DEFAULT_BASE_URL = "reference:file:";
76 public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
77
78 // OSGi system properties
79 public final static String INSTANCE_AREA_PROP = "osgi.instance.area";
80 public final static String INSTANCE_AREA_DEFAULT_PROP = "osgi.instance.area.default";
81
82 private boolean debug = Boolean.valueOf(
83 System.getProperty(PROP_ARGEO_OSGI_BOOT_DEBUG, "false"))
84 .booleanValue();
85 /** Exclude svn metadata implicitely(a bit costly) */
86 private boolean excludeSvn = Boolean.valueOf(
87 System.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN, "false"))
88 .booleanValue();
89
90 /**
91 * The {@link #installUrls(List)} methods won't follow the list order but
92 * order the urls according to the alphabetical order of the file names
93 * (last part of the URL). The goal is to stay closer from Eclipse PDE way
94 * of installing target platform bundles.
95 */
96 private boolean installInLexicographicOrder = Boolean
97 .valueOf(
98 System.getProperty(
99 PROP_ARGEO_OSGI_BOOT_INSTALL_IN_LEXICOGRAPHIC_ORDER,
100 "true")).booleanValue();;
101
102 /** Default is 10s (set in constructor) */
103 private long defaultTimeout;
104
105 /** Default is ',' (set in constructor) */
106 private String modulesUrlSeparator = ",";
107
108 private final BundleContext bundleContext;
109
110 /*
111 * INITIALIZATION
112 */
113 /** Constructor */
114 public OsgiBoot(BundleContext bundleContext) {
115 this.bundleContext = bundleContext;
116 defaultTimeout = Long.parseLong(OsgiBootUtils.getProperty(
117 PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT, "10000"));
118 modulesUrlSeparator = OsgiBootUtils.getProperty(
119 PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR, ",");
120 initSystemProperties();
121 }
122
123 /**
124 * Set additional system properties, especially ${argeo.osgi.data.dir} as an
125 * OS file path (and not a file:// URL)
126 */
127 protected void initSystemProperties() {
128 String osgiInstanceArea = System.getProperty(INSTANCE_AREA_PROP);
129 String osgiInstanceAreaDefault = System
130 .getProperty(INSTANCE_AREA_DEFAULT_PROP);
131 String tempDir = System.getProperty("java.io.tmpdir");
132
133 File dataDir = null;
134 if (osgiInstanceArea != null) {
135 // within OSGi with -data specified
136 osgiInstanceArea = removeFilePrefix(osgiInstanceArea);
137 dataDir = new File(osgiInstanceArea);
138 } else if (osgiInstanceAreaDefault != null) {
139 // within OSGi without -data specified
140 osgiInstanceAreaDefault = removeFilePrefix(osgiInstanceAreaDefault);
141 dataDir = new File(osgiInstanceAreaDefault);
142 } else {// outside OSGi
143 dataDir = new File(tempDir + File.separator + "argeoOsgiData");
144 }
145 System.setProperty(PROP_ARGEO_OSGI_DATA_DIR, dataDir.getAbsolutePath());
146 }
147
148 /*
149 * HIGH-LEVEL METHODS
150 */
151 /** Bootstraps the OSGi runtime */
152 public void bootstrap() {
153 long begin = System.currentTimeMillis();
154 System.out.println();
155 OsgiBootUtils.info("OSGi bootstrap starting...");
156 OsgiBootUtils.info("Writable data directory : "
157 + System.getProperty(PROP_ARGEO_OSGI_DATA_DIR)
158 + " (set as system property " + PROP_ARGEO_OSGI_DATA_DIR + ")");
159 installUrls(getBundlesUrls());
160 installUrls(getLocationsUrls());
161 installUrls(getModulesUrls());
162 checkUnresolved();
163 startBundles();
164 long duration = System.currentTimeMillis() - begin;
165 OsgiBootUtils.info("OSGi bootstrap completed in "
166 + Math.round(((double) duration) / 1000) + "s (" + duration
167 + "ms), " + bundleContext.getBundles().length + " bundles");
168
169 // display packages exported twice
170 if (debug) {
171 Map /* <String,Set<String>> */duplicatePackages = findPackagesExportedTwice();
172 if (duplicatePackages.size() > 0) {
173 OsgiBootUtils.info("Packages exported twice:");
174 Iterator it = duplicatePackages.keySet().iterator();
175 while (it.hasNext()) {
176 String pkgName = it.next().toString();
177 OsgiBootUtils.info(pkgName);
178 Set bdles = (Set) duplicatePackages.get(pkgName);
179 Iterator bdlesIt = bdles.iterator();
180 while (bdlesIt.hasNext())
181 OsgiBootUtils.info(" " + bdlesIt.next());
182 }
183 }
184 }
185
186 System.out.println();
187 }
188
189 /*
190 * INSTALLATION
191 */
192 /** Install the bundles at this URL list. */
193 public void installUrls(List urls) {
194 Map installedBundles = getBundlesByLocation();
195
196 if (installInLexicographicOrder) {
197 SortedMap map = new TreeMap();
198 // reorder
199 for (int i = 0; i < urls.size(); i++) {
200 String url = (String) urls.get(i);
201 int index = url.lastIndexOf('/');
202 String fileName;
203 if (index >= 0)
204 fileName = url.substring(index + 1);
205 else
206 fileName = url;
207 map.put(fileName, url);
208 }
209
210 // install
211 Iterator keys = map.keySet().iterator();
212 while (keys.hasNext()) {
213 Object key = keys.next();
214 String url = map.get(key).toString();
215 installUrl(url, installedBundles);
216 }
217 } else {
218 for (int i = 0; i < urls.size(); i++) {
219 String url = (String) urls.get(i);
220 installUrl(url, installedBundles);
221 }
222 }
223
224 }
225
226 /** Actually install the provided URL */
227 protected void installUrl(String url, Map installedBundles) {
228 try {
229 if (installedBundles.containsKey(url)) {
230 Bundle bundle = (Bundle) installedBundles.get(url);
231 // bundle.update();
232 if (debug)
233 debug("Bundle " + bundle.getSymbolicName()
234 + " already installed from " + url);
235 } else {
236 Bundle bundle = bundleContext.installBundle(url);
237 if (debug)
238 debug("Installed bundle " + bundle.getSymbolicName()
239 + " from " + url);
240 }
241 } catch (BundleException e) {
242 String message = e.getMessage();
243 if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"") || message
244 .contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
245 && message.contains("has already been installed")) {
246 // silent, in order to avoid warnings: we know that both
247 // have already been installed...
248 } else {
249 OsgiBootUtils.warn("Could not install bundle from " + url
250 + ": " + message);
251 }
252 if (debug)
253 e.printStackTrace();
254 }
255 }
256
257 /* @deprecated Doesn't seem to be used anymore. */
258 // public void installOrUpdateUrls(Map urls) {
259 // Map installedBundles = getBundles();
260 //
261 // for (Iterator modules = urls.keySet().iterator(); modules.hasNext();) {
262 // String moduleName = (String) modules.next();
263 // String urlStr = (String) urls.get(moduleName);
264 // if (installedBundles.containsKey(moduleName)) {
265 // Bundle bundle = (Bundle) installedBundles.get(moduleName);
266 // InputStream in;
267 // try {
268 // URL url = new URL(urlStr);
269 // in = url.openStream();
270 // bundle.update(in);
271 // OsgiBootUtils.info("Updated bundle " + moduleName
272 // + " from " + urlStr);
273 // } catch (Exception e) {
274 // throw new RuntimeException("Cannot update " + moduleName
275 // + " from " + urlStr);
276 // }
277 // if (in != null)
278 // try {
279 // in.close();
280 // } catch (IOException e) {
281 // e.printStackTrace();
282 // }
283 // } else {
284 // try {
285 // Bundle bundle = bundleContext.installBundle(urlStr);
286 // if (debug)
287 // debug("Installed bundle " + bundle.getSymbolicName()
288 // + " from " + urlStr);
289 // } catch (BundleException e) {
290 // OsgiBootUtils.warn("Could not install bundle from "
291 // + urlStr + ": " + e.getMessage());
292 // }
293 // }
294 // }
295 //
296 // }
297
298 /*
299 * START
300 */
301 public void startBundles() {
302 String bundlesToStart = OsgiBootUtils
303 .getProperty(PROP_ARGEO_OSGI_START);
304 startBundles(bundlesToStart);
305 }
306
307 /** Convenience method accepting a comma-separated list of bundle to start */
308 public void startBundles(String bundlesToStartStr) {
309 if (bundlesToStartStr == null)
310 return;
311
312 StringTokenizer st = new StringTokenizer(bundlesToStartStr, ",");
313 List bundlesToStart = new ArrayList();
314 while (st.hasMoreTokens()) {
315 String name = st.nextToken().trim();
316 bundlesToStart.add(name);
317 }
318 startBundles(bundlesToStart);
319 }
320
321 /** Start the provided list of bundles */
322 public void startBundles(List bundlesToStart) {
323 if (bundlesToStart.size() == 0)
324 return;
325
326 // used to log the bundles not found
327 List notFoundBundles = new ArrayList(bundlesToStart);
328
329 Bundle[] bundles = bundleContext.getBundles();
330 long startBegin = System.currentTimeMillis();
331 for (int i = 0; i < bundles.length; i++) {
332 Bundle bundle = bundles[i];
333 String symbolicName = bundle.getSymbolicName();
334 if (bundlesToStart.contains(symbolicName))
335 try {
336 try {
337 bundle.start();
338 if (debug)
339 debug("Bundle " + symbolicName + " started");
340 } catch (Exception e) {
341 OsgiBootUtils.warn("Start of bundle " + symbolicName
342 + " failed because of " + e
343 + ", maybe bundle is not yet resolved,"
344 + " waiting and trying again.");
345 waitForBundleResolvedOrActive(startBegin, bundle);
346 bundle.start();
347 }
348 notFoundBundles.remove(symbolicName);
349 } catch (Exception e) {
350 OsgiBootUtils.warn("Bundle " + symbolicName
351 + " cannot be started: " + e.getMessage());
352 if (debug)
353 e.printStackTrace();
354 // was found even if start failed
355 notFoundBundles.remove(symbolicName);
356 }
357 }
358
359 for (int i = 0; i < notFoundBundles.size(); i++)
360 OsgiBootUtils.warn("Bundle " + notFoundBundles.get(i)
361 + " not started because it was not found.");
362 }
363
364 /*
365 * DIAGNOSTICS
366 */
367 /** Check unresolved bundles */
368 protected void checkUnresolved() {
369 // Refresh
370 ServiceReference packageAdminRef = bundleContext
371 .getServiceReference(PackageAdmin.class.getName());
372 PackageAdmin packageAdmin = (PackageAdmin) bundleContext
373 .getService(packageAdminRef);
374 packageAdmin.resolveBundles(null);
375
376 Bundle[] bundles = bundleContext.getBundles();
377 List /* Bundle */unresolvedBundles = new ArrayList();
378 for (int i = 0; i < bundles.length; i++) {
379 int bundleState = bundles[i].getState();
380 if (!(bundleState == Bundle.ACTIVE
381 || bundleState == Bundle.RESOLVED || bundleState == Bundle.STARTING))
382 unresolvedBundles.add(bundles[i]);
383 }
384
385 if (unresolvedBundles.size() != 0) {
386 OsgiBootUtils.warn("Unresolved bundles " + unresolvedBundles);
387 }
388 }
389
390 /** List packages exported twice. */
391 public Map findPackagesExportedTwice() {
392 ServiceReference paSr = bundleContext
393 .getServiceReference(PackageAdmin.class.getName());
394 PackageAdmin packageAdmin = (PackageAdmin) bundleContext
395 .getService(paSr);
396
397 // find packages exported twice
398 Bundle[] bundles = bundleContext.getBundles();
399 Map /* <String,Set<String>> */exportedPackages = new TreeMap();
400 for (int i = 0; i < bundles.length; i++) {
401 Bundle bundle = bundles[i];
402 ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bundle);
403 if (pkgs != null)
404 for (int j = 0; j < pkgs.length; j++) {
405 String pkgName = pkgs[j].getName();
406 if (!exportedPackages.containsKey(pkgName)) {
407 exportedPackages.put(pkgName, new TreeSet());
408 }
409 ((Set) exportedPackages.get(pkgName)).add(bundle
410 .getSymbolicName() + "_" + bundle.getVersion());
411 }
412 }
413 Map /* <String,Set<String>> */duplicatePackages = new TreeMap();
414 Iterator it = exportedPackages.keySet().iterator();
415 while (it.hasNext()) {
416 String pkgName = it.next().toString();
417 Set bdles = (Set) exportedPackages.get(pkgName);
418 if (bdles.size() > 1)
419 duplicatePackages.put(pkgName, bdles);
420 }
421 return duplicatePackages;
422 }
423
424 /** Waits for a bundle to become active or resolved */
425 protected void waitForBundleResolvedOrActive(long startBegin, Bundle bundle)
426 throws Exception {
427 int originalState = bundle.getState();
428 if ((originalState == Bundle.RESOLVED)
429 || (originalState == Bundle.ACTIVE))
430 return;
431
432 String originalStateStr = OsgiBootUtils.stateAsString(originalState);
433
434 int currentState = bundle.getState();
435 while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
436 long now = System.currentTimeMillis();
437 if ((now - startBegin) > defaultTimeout)
438 throw new Exception("Bundle " + bundle.getSymbolicName()
439 + " was not RESOLVED or ACTIVE after "
440 + (now - startBegin) + "ms (originalState="
441 + originalStateStr + ", currentState="
442 + OsgiBootUtils.stateAsString(currentState) + ")");
443
444 try {
445 Thread.sleep(100l);
446 } catch (InterruptedException e) {
447 // silent
448 }
449 currentState = bundle.getState();
450 }
451 }
452
453 /*
454 * EXPLICIT LOCATIONS INSTALLATION
455 */
456 /** Gets the list of resolved explicit URL locations. */
457 public List getLocationsUrls() {
458 String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL,
459 DEFAULT_BASE_URL);
460 String bundleLocations = OsgiBootUtils
461 .getProperty(PROP_ARGEO_OSGI_LOCATIONS);
462 return getLocationsUrls(baseUrl, bundleLocations);
463 }
464
465 /**
466 * Gets a list of URLs based on explicit locations, resolving placeholder
467 * ${...} containing system properties, e.g. ${user.home}.
468 */
469 public List getLocationsUrls(String baseUrl, String bundleLocations) {
470 List urls = new ArrayList();
471
472 if (bundleLocations == null)
473 return urls;
474 bundleLocations = SystemPropertyUtils
475 .resolvePlaceholders(bundleLocations);
476 if (debug)
477 debug(PROP_ARGEO_OSGI_LOCATIONS + "=" + bundleLocations);
478
479 StringTokenizer st = new StringTokenizer(bundleLocations,
480 File.pathSeparator);
481 while (st.hasMoreTokens()) {
482 urls.add(locationToUrl(baseUrl, st.nextToken().trim()));
483 }
484 return urls;
485 }
486
487 /*
488 * BUNDLE PATTERNS INSTALLATION
489 */
490 /**
491 * Computes a list of URLs based on Ant-like incluide/exclude patterns
492 * defined by ${argeo.osgi.bundles} with the following format:<br>
493 * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
494 * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
495 * without a new line causes a '.' to be appended with unexpected side
496 * effects.
497 */
498 public List getBundlesUrls() {
499 String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL,
500 DEFAULT_BASE_URL);
501 String bundlePatterns = OsgiBootUtils
502 .getProperty(PROP_ARGEO_OSGI_BUNDLES);
503 return getBundlesUrls(baseUrl, bundlePatterns);
504 }
505
506 /** Implements the path matching logic */
507 public List getBundlesUrls(String baseUrl, String bundlePatterns) {
508 List urls = new ArrayList();
509 if (bundlePatterns == null)
510 return urls;
511
512 bundlePatterns = SystemPropertyUtils
513 .resolvePlaceholders(bundlePatterns);
514 if (debug)
515 debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns
516 + " (excludeSvn=" + excludeSvn + ")");
517
518 StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
519 List bundlesSets = new ArrayList();
520 while (st.hasMoreTokens()) {
521 bundlesSets.add(new BundlesSet(st.nextToken()));
522 }
523
524 // find included
525 List included = new ArrayList();
526 PathMatcher matcher = new AntPathMatcher();
527 for (int i = 0; i < bundlesSets.size(); i++) {
528 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
529 for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
530 String pattern = (String) bundlesSet.getIncludes().get(j);
531 match(matcher, included, bundlesSet.getDir(), null, pattern);
532 }
533 }
534
535 // find excluded
536 List excluded = new ArrayList();
537 for (int i = 0; i < bundlesSets.size(); i++) {
538 BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
539 for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
540 String pattern = (String) bundlesSet.getExcludes().get(j);
541 match(matcher, excluded, bundlesSet.getDir(), null, pattern);
542 }
543 }
544
545 // construct list
546 for (int i = 0; i < included.size(); i++) {
547 String fullPath = (String) included.get(i);
548 if (!excluded.contains(fullPath))
549 urls.add(locationToUrl(baseUrl, fullPath));
550 }
551
552 return urls;
553 }
554
555 /*
556 * MODULES LIST INSTALLATION (${argeo.osgi.modulesUrl})
557 */
558 /**
559 * Downloads a list of URLs in CSV format from ${argeo.osgi.modulesUrl}:<br>
560 * <code>Bundle-SymbolicName,Bundle-Version,url</code>)<br>
561 * If ${argeo.osgi.baseUrl} is set, URLs will be considered relative paths
562 * and be concatenated with the base URL, typically the root of a Maven
563 * repository.
564 */
565 public List getModulesUrls() {
566 List urls = new ArrayList();
567 String modulesUrlStr = OsgiBootUtils
568 .getProperty(PROP_ARGEO_OSGI_MODULES_URL);
569 if (modulesUrlStr == null)
570 return urls;
571
572 String baseUrl = OsgiBootUtils.getProperty(PROP_ARGEO_OSGI_BASE_URL);
573
574 Map installedBundles = getBundlesBySymbolicName();
575
576 BufferedReader reader = null;
577 try {
578 URL modulesUrl = new URL(modulesUrlStr);
579 reader = new BufferedReader(new InputStreamReader(
580 modulesUrl.openStream()));
581 String line = null;
582 while ((line = reader.readLine()) != null) {
583 StringTokenizer st = new StringTokenizer(line,
584 modulesUrlSeparator);
585 String moduleName = st.nextToken();
586 String moduleVersion = st.nextToken();
587 String url = st.nextToken();
588 if (baseUrl != null)
589 url = baseUrl + url;
590
591 if (installedBundles.containsKey(moduleName)) {
592 Bundle bundle = (Bundle) installedBundles.get(moduleName);
593 String bundleVersion = bundle.getHeaders()
594 .get(Constants.BUNDLE_VERSION).toString();
595 int comp = OsgiBootUtils.compareVersions(bundleVersion,
596 moduleVersion);
597 if (comp > 0) {
598 OsgiBootUtils.warn("Installed version " + bundleVersion
599 + " of bundle " + moduleName
600 + " is newer than provided version "
601 + moduleVersion);
602 } else if (comp < 0) {
603 urls.add(url);
604 OsgiBootUtils.info("Updated bundle " + moduleName
605 + " with version " + moduleVersion
606 + " (old version was " + bundleVersion + ")");
607 } else {
608 // do nothing
609 }
610 } else {
611 urls.add(url);
612 }
613 }
614 } catch (Exception e1) {
615 throw new RuntimeException("Cannot read url " + modulesUrlStr, e1);
616 } finally {
617 if (reader != null)
618 try {
619 reader.close();
620 } catch (IOException e) {
621 e.printStackTrace();
622 }
623 }
624 return urls;
625 }
626
627 /*
628 * HIGH LEVEL UTILITIES
629 */
630 /** Actually performs the matching logic. */
631 protected void match(PathMatcher matcher, List matched, String base,
632 String currentPath, String pattern) {
633 if (currentPath == null) {
634 // Init
635 File baseDir = new File(base.replace('/', File.separatorChar));
636 File[] files = baseDir.listFiles();
637
638 if (files == null) {
639 if (debug)
640 OsgiBootUtils.warn("Base dir " + baseDir
641 + " has no children, exists=" + baseDir.exists()
642 + ", isDirectory=" + baseDir.isDirectory());
643 return;
644 }
645
646 for (int i = 0; i < files.length; i++)
647 match(matcher, matched, base, files[i].getName(), pattern);
648 } else {
649 String fullPath = base + '/' + currentPath;
650 if (matched.contains(fullPath))
651 return;// don't try deeper if already matched
652
653 boolean ok = matcher.match(pattern, currentPath);
654 // if (debug)
655 // debug(currentPath + " " + (ok ? "" : " not ")
656 // + " matched with " + pattern);
657 if (ok) {
658 matched.add(fullPath);
659 return;
660 } else {
661 String newFullPath = relativeToFullPath(base, currentPath);
662 File newFile = new File(newFullPath);
663 File[] files = newFile.listFiles();
664 if (files != null) {
665 for (int i = 0; i < files.length; i++) {
666 String newCurrentPath = currentPath + '/'
667 + files[i].getName();
668 if (files[i].isDirectory()) {
669 if (matcher.matchStart(pattern, newCurrentPath)) {
670 // recurse only if start matches
671 match(matcher, matched, base, newCurrentPath,
672 pattern);
673 } else {
674 if (debug)
675 debug(newCurrentPath
676 + " does not start match with "
677 + pattern);
678
679 }
680 } else {
681 boolean nonDirectoryOk = matcher.match(pattern,
682 newCurrentPath);
683 if (debug)
684 debug(currentPath + " " + (ok ? "" : " not ")
685 + " matched with " + pattern);
686 if (nonDirectoryOk)
687 matched.add(relativeToFullPath(base,
688 newCurrentPath));
689 }
690 }
691 }
692 }
693 }
694 }
695
696 protected void matchFile() {
697
698 }
699
700 /*
701 * LOW LEVEL UTILITIES
702 */
703 /**
704 * The bundles already installed. Key is location (String) , value is a
705 * {@link Bundle}
706 */
707 public Map getBundlesByLocation() {
708 Map installedBundles = new HashMap();
709 Bundle[] bundles = bundleContext.getBundles();
710 for (int i = 0; i < bundles.length; i++) {
711 installedBundles.put(bundles[i].getLocation(), bundles[i]);
712 }
713 return installedBundles;
714 }
715
716 /**
717 * The bundles already installed. Key is symbolic name (String) , value is a
718 * {@link Bundle}
719 */
720 public Map getBundlesBySymbolicName() {
721 Map namedBundles = new HashMap();
722 Bundle[] bundles = bundleContext.getBundles();
723 for (int i = 0; i < bundles.length; i++) {
724 namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
725 }
726 return namedBundles;
727 }
728
729 /** Creates an URL from a location */
730 protected String locationToUrl(String baseUrl, String location) {
731 int extInd = location.lastIndexOf('.');
732 String ext = null;
733 if (extInd > 0)
734 ext = location.substring(extInd);
735
736 if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
737 return "file:" + location;
738 else
739 return baseUrl + location;
740 }
741
742 /** Transforms a relative path in a full system path. */
743 protected String relativeToFullPath(String basePath, String relativePath) {
744 return (basePath + '/' + relativePath).replace('/', File.separatorChar);
745 }
746
747 private String removeFilePrefix(String url) {
748 if (url.startsWith("file:"))
749 return url.substring("file:".length());
750 else if (url.startsWith("reference:file:"))
751 return url.substring("reference:file:".length());
752 else
753 return url;
754 }
755
756 /**
757 * Convenience method to avoid cluttering the code with
758 * OsgiBootUtils.debug()
759 */
760 protected void debug(Object obj) {
761 OsgiBootUtils.debug(obj);
762 }
763
764 /*
765 * BEAN METHODS
766 */
767
768 public boolean getDebug() {
769 return debug;
770 }
771
772 public void setDebug(boolean debug) {
773 this.debug = debug;
774 }
775
776 public BundleContext getBundleContext() {
777 return bundleContext;
778 }
779
780 public void setInstallInLexicographicOrder(
781 boolean installInAlphabeticalOrder) {
782 this.installInLexicographicOrder = installInAlphabeticalOrder;
783 }
784
785 public boolean isInstallInLexicographicOrder() {
786 return installInLexicographicOrder;
787 }
788
789 public void setDefaultTimeout(long defaultTimeout) {
790 this.defaultTimeout = defaultTimeout;
791 }
792
793 public void setModulesUrlSeparator(String modulesUrlSeparator) {
794 this.modulesUrlSeparator = modulesUrlSeparator;
795 }
796
797 public boolean isExcludeSvn() {
798 return excludeSvn;
799 }
800
801 public void setExcludeSvn(boolean excludeSvn) {
802 this.excludeSvn = excludeSvn;
803 }
804
805 /*
806 * INTERNAL CLASSES
807 */
808
809 /** Intermediary structure used by path matching */
810 protected class BundlesSet {
811 private String baseUrl = "reference:file";// not used yet
812 private final String dir;
813 private List includes = new ArrayList();
814 private List excludes = new ArrayList();
815
816 public BundlesSet(String def) {
817 StringTokenizer st = new StringTokenizer(def, ";");
818
819 if (!st.hasMoreTokens())
820 throw new RuntimeException("Base dir not defined.");
821 try {
822 String dirPath = st.nextToken();
823
824 if (dirPath.startsWith("file:"))
825 dirPath = dirPath.substring("file:".length());
826
827 dir = new File(dirPath.replace('/', File.separatorChar))
828 .getCanonicalPath();
829 if (debug)
830 debug("Base dir: " + dir);
831 } catch (IOException e) {
832 throw new RuntimeException("Cannot convert to absolute path", e);
833 }
834
835 while (st.hasMoreTokens()) {
836 String tk = st.nextToken();
837 StringTokenizer stEq = new StringTokenizer(tk, "=");
838 String type = stEq.nextToken();
839 String pattern = stEq.nextToken();
840 if ("in".equals(type) || "include".equals(type)) {
841 includes.add(pattern);
842 } else if ("ex".equals(type) || "exclude".equals(type)) {
843 excludes.add(pattern);
844 } else if ("baseUrl".equals(type)) {
845 baseUrl = pattern;
846 } else {
847 System.err.println("Unkown bundles pattern type " + type);
848 }
849 }
850
851 if (excludeSvn && !excludes.contains(EXCLUDES_SVN_PATTERN)) {
852 excludes.add(EXCLUDES_SVN_PATTERN);
853 }
854 }
855
856 public String getDir() {
857 return dir;
858 }
859
860 public List getIncludes() {
861 return includes;
862 }
863
864 public List getExcludes() {
865 return excludes;
866 }
867
868 public String getBaseUrl() {
869 return baseUrl;
870 }
871
872 }
873
874 }