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