1 package org
.argeo
.slc
.osgiboot
;
3 import java
.io
.BufferedReader
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
7 import java
.io
.InputStreamReader
;
9 import java
.util
.ArrayList
;
10 import java
.util
.HashMap
;
11 import java
.util
.Iterator
;
12 import java
.util
.List
;
14 import java
.util
.StringTokenizer
;
16 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.AntPathMatcher
;
17 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.PathMatcher
;
18 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.SystemPropertyUtils
;
19 import org
.osgi
.framework
.Bundle
;
20 import org
.osgi
.framework
.BundleContext
;
21 import org
.osgi
.framework
.BundleException
;
22 import org
.osgi
.framework
.Constants
;
24 public class OsgiBoot
{
25 public final static String PROP_ARGEO_OSGI_DATA_DIR
= "argeo.osgi.data.dir";
27 public final static String PROP_ARGEO_OSGI_START
= "argeo.osgi.start";
28 public final static String PROP_ARGEO_OSGI_BUNDLES
= "argeo.osgi.bundles";
29 public final static String PROP_ARGEO_OSGI_LOCATIONS
= "argeo.osgi.locations";
30 public final static String PROP_ARGEO_OSGI_BASE_URL
= "argeo.osgi.baseUrl";
31 public final static String PROP_ARGEO_OSGI_MODULES_URL
= "argeo.osgi.modulesUrl";
33 public final static String PROP_ARGEO_OSGI_BOOT_DEBUG
= "argeo.osgi.boot.debug";
34 public final static String PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
= "argeo.osgi.boot.defaultTimeout";
35 public final static String PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR
= "argeo.osgi.boot.modulesUrlSeparator";
36 public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE
= "argeo.osgi.boot.systemPropertiesFile";
39 public final static String PROP_SLC_OSGI_START
= "slc.osgi.start";
41 public final static String PROP_SLC_OSGI_BUNDLES
= "slc.osgi.bundles";
43 public final static String PROP_SLC_OSGI_LOCATIONS
= "slc.osgi.locations";
45 public final static String PROP_SLC_OSGI_BASE_URL
= "slc.osgi.baseUrl";
47 public final static String PROP_SLC_OSGI_MODULES_URL
= "slc.osgi.modulesUrl";
50 public final static String PROP_SLC_OSGIBOOT_DEBUG
= "slc.osgiboot.debug";
52 public final static String PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
= "slc.osgiboot.defaultTimeout";
54 public final static String PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
= "slc.osgiboot.modulesUrlSeparator";
56 public final static String PROP_SLC_OSGIBOOT_SYSTEM_PROPERTIES_FILE
= "slc.osgiboot.systemPropertiesFile";
58 public final static String DEFAULT_BASE_URL
= "reference:file:";
59 public final static String EXCLUDES_SVN_PATTERN
= "**/.svn/**";
61 private boolean debug
= Boolean
.valueOf(
62 System
.getProperty(PROP_ARGEO_OSGI_BOOT_DEBUG
, System
.getProperty(
63 PROP_SLC_OSGIBOOT_DEBUG
, "false"))).booleanValue();
64 /** Default is 10s (set in constructor) */
65 private long defaultTimeout
;
67 private boolean excludeSvn
= true;
68 /** Default is ',' (set in constructor) */
69 private String modulesUrlSeparator
= ",";
71 private final BundleContext bundleContext
;
73 public OsgiBoot(BundleContext bundleContext
) {
74 this.bundleContext
= bundleContext
;
75 defaultTimeout
= Long
.parseLong(getPropertyCompat(
76 PROP_ARGEO_OSGI_BOOT_DEFAULT_TIMEOUT
,
77 PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
, "10000"));
78 modulesUrlSeparator
= getPropertyCompat(
79 PROP_ARGEO_OSGI_BOOT_MODULES_URL_SEPARATOR
,
80 PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
, ",");
81 initSystemProperties();
84 protected void initSystemProperties() {
85 String osgiInstanceArea
= System
.getProperty("osgi.instance.area");
86 String osgiInstanceAreaDefault
= System
87 .getProperty("osgi.instance.area.default");
88 String tempDir
= System
.getProperty("java.io.tmpdir");
91 if (osgiInstanceArea
!= null) {
92 // within OSGi with -data specified
93 osgiInstanceArea
= removeFilePrefix(osgiInstanceArea
);
94 dataDir
= new File(osgiInstanceArea
);
95 } else if (osgiInstanceAreaDefault
!= null) {
96 // within OSGi without -data specified
97 osgiInstanceAreaDefault
= removeFilePrefix(osgiInstanceAreaDefault
);
98 dataDir
= new File(osgiInstanceAreaDefault
);
99 } else {// outside OSGi
100 dataDir
= new File(tempDir
+ File
.separator
+ "argeoOsgiData");
103 System
.setProperty(PROP_ARGEO_OSGI_DATA_DIR
, dataDir
.getAbsolutePath());
104 info(PROP_ARGEO_OSGI_DATA_DIR
+ "=" + dataDir
.getAbsolutePath());
107 public static String
removeFilePrefix(String url
) {
108 if (url
.startsWith("file:"))
109 return url
.substring("file:".length());
110 else if (url
.startsWith("reference:file:"))
111 return url
.substring("reference:file:".length());
116 public void bootstrap() {
117 info("SLC OSGi bootstrap starting...");
118 installUrls(getBundlesUrls());
119 installUrls(getLocationsUrls());
120 installUrls(getModulesUrls());
122 info("SLC OSGi bootstrap completed");
125 public void installUrls(List urls
) {
126 Map installedBundles
= getInstalledBundles();
127 for (int i
= 0; i
< urls
.size(); i
++) {
128 String url
= (String
) urls
.get(i
);
130 if (installedBundles
.containsKey(url
)) {
131 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
134 debug("Bundle " + bundle
.getSymbolicName()
135 + " already installed from " + url
);
137 Bundle bundle
= bundleContext
.installBundle(url
);
139 debug("Installed bundle " + bundle
.getSymbolicName()
142 } catch (BundleException e
) {
143 warn("Could not install bundle from " + url
+ ": "
152 public void installOrUpdateUrls(Map urls
) {
153 Map installedBundles
= getBundles();
155 for (Iterator modules
= urls
.keySet().iterator(); modules
.hasNext();) {
156 String moduleName
= (String
) modules
.next();
157 String urlStr
= (String
) urls
.get(moduleName
);
158 if (installedBundles
.containsKey(moduleName
)) {
159 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
162 URL url
= new URL(urlStr
);
163 in
= url
.openStream();
165 info("Updated bundle " + moduleName
+ " from " + urlStr
);
166 } catch (Exception e
) {
167 throw new RuntimeException("Cannot update " + moduleName
168 + " from " + urlStr
);
173 } catch (IOException e
) {
178 Bundle bundle
= bundleContext
.installBundle(urlStr
);
180 debug("Installed bundle " + bundle
.getSymbolicName()
181 + " from " + urlStr
);
182 } catch (BundleException e
) {
183 warn("Could not install bundle from " + urlStr
+ ": "
191 public void startBundles() {
192 String bundlesToStart
= getPropertyCompat(PROP_ARGEO_OSGI_START
,
193 PROP_SLC_OSGI_START
);
194 startBundles(bundlesToStart
);
197 public void startBundles(String bundlesToStartStr
) {
198 if (bundlesToStartStr
== null)
201 StringTokenizer st
= new StringTokenizer(bundlesToStartStr
, ",");
202 List bundlesToStart
= new ArrayList();
203 while (st
.hasMoreTokens()) {
204 String name
= st
.nextToken().trim();
205 bundlesToStart
.add(name
);
207 startBundles(bundlesToStart
);
210 public void startBundles(List bundlesToStart
) {
211 if (bundlesToStart
.size() == 0)
214 // used to log the bundles not found
215 List notFoundBundles
= new ArrayList(bundlesToStart
);
217 Bundle
[] bundles
= bundleContext
.getBundles();
218 long startBegin
= System
.currentTimeMillis();
219 for (int i
= 0; i
< bundles
.length
; i
++) {
220 Bundle bundle
= bundles
[i
];
221 String symbolicName
= bundle
.getSymbolicName();
222 if (bundlesToStart
.contains(symbolicName
))
226 } catch (Exception e
) {
227 warn("Start of bundle " + symbolicName
228 + " failed because of " + e
229 + ", maybe bundle is not yet resolved,"
230 + " waiting and trying again.");
231 waitForBundleResolvedOrActive(startBegin
, bundle
);
234 notFoundBundles
.remove(symbolicName
);
235 } catch (Exception e
) {
236 warn("Bundle " + symbolicName
+ " cannot be started: "
240 // was found even if start failed
241 notFoundBundles
.remove(symbolicName
);
245 for (int i
= 0; i
< notFoundBundles
.size(); i
++)
246 warn("Bundle " + notFoundBundles
.get(i
)
247 + " not started because it was not found.");
250 protected void waitForBundleResolvedOrActive(long startBegin
, Bundle bundle
)
252 int originalState
= bundle
.getState();
253 if ((originalState
== Bundle
.RESOLVED
)
254 || (originalState
== Bundle
.ACTIVE
))
257 String originalStateStr
= stateAsString(originalState
);
259 int currentState
= bundle
.getState();
260 while (!(currentState
== Bundle
.RESOLVED
|| currentState
== Bundle
.ACTIVE
)) {
261 long now
= System
.currentTimeMillis();
262 if ((now
- startBegin
) > defaultTimeout
)
263 throw new Exception("Bundle " + bundle
.getSymbolicName()
264 + " was not RESOLVED or ACTIVE after "
265 + (now
- startBegin
) + "ms (originalState="
266 + originalStateStr
+ ", currentState="
267 + stateAsString(currentState
) + ")");
271 } catch (InterruptedException e
) {
274 currentState
= bundle
.getState();
278 public static String
stateAsString(int state
) {
280 case Bundle
.UNINSTALLED
:
281 return "UNINSTALLED";
282 case Bundle
.INSTALLED
:
284 case Bundle
.RESOLVED
:
286 case Bundle
.STARTING
:
290 case Bundle
.STOPPING
:
293 return Integer
.toString(state
);
297 /** Key is location */
298 public Map
getInstalledBundles() {
299 Map installedBundles
= new HashMap();
301 Bundle
[] bundles
= bundleContext
.getBundles();
302 for (int i
= 0; i
< bundles
.length
; i
++) {
303 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
305 return installedBundles
;
308 /** Key is symbolic name */
309 public Map
getBundles() {
310 Map namedBundles
= new HashMap();
311 Bundle
[] bundles
= bundleContext
.getBundles();
312 for (int i
= 0; i
< bundles
.length
; i
++) {
313 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
318 public List
getLocationsUrls() {
319 String baseUrl
= getPropertyCompat(PROP_ARGEO_OSGI_BASE_URL
,
320 PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
321 String bundleLocations
= getPropertyCompat(PROP_ARGEO_OSGI_LOCATIONS
,
322 PROP_SLC_OSGI_LOCATIONS
);
323 return getLocationsUrls(baseUrl
, bundleLocations
);
326 public List
getModulesUrls() {
327 List urls
= new ArrayList();
328 String modulesUrlStr
= getPropertyCompat(PROP_ARGEO_OSGI_MODULES_URL
,
329 PROP_SLC_OSGI_MODULES_URL
);
330 if (modulesUrlStr
== null)
333 String baseUrl
= getPropertyCompat(PROP_ARGEO_OSGI_BASE_URL
,
334 PROP_SLC_OSGI_BASE_URL
);
336 Map installedBundles
= getBundles();
338 BufferedReader reader
= null;
340 URL modulesUrl
= new URL(modulesUrlStr
);
341 reader
= new BufferedReader(new InputStreamReader(modulesUrl
344 while ((line
= reader
.readLine()) != null) {
345 StringTokenizer st
= new StringTokenizer(line
,
346 modulesUrlSeparator
);
347 String moduleName
= st
.nextToken();
348 String moduleVersion
= st
.nextToken();
349 String url
= st
.nextToken();
353 if (installedBundles
.containsKey(moduleName
)) {
354 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
355 String bundleVersion
= bundle
.getHeaders().get(
356 Constants
.BUNDLE_VERSION
).toString();
357 int comp
= compareVersions(bundleVersion
, moduleVersion
);
359 warn("Installed version " + bundleVersion
360 + " of bundle " + moduleName
361 + " is newer than provided version "
363 } else if (comp
< 0) {
365 info("Updated bundle " + moduleName
+ " with version "
366 + moduleVersion
+ " (old version was "
367 + bundleVersion
+ ")");
375 } catch (Exception e1
) {
376 throw new RuntimeException("Cannot read url " + modulesUrlStr
, e1
);
381 } catch (IOException e
) {
389 * @return ==0: versions are identical, <0: tested version is newer, >0:
390 * currentVersion is newer.
392 protected int compareVersions(String currentVersion
, String testedVersion
) {
393 List cToks
= new ArrayList();
394 StringTokenizer cSt
= new StringTokenizer(currentVersion
, ".");
395 while (cSt
.hasMoreTokens())
396 cToks
.add(cSt
.nextToken());
397 List tToks
= new ArrayList();
398 StringTokenizer tSt
= new StringTokenizer(currentVersion
, ".");
399 while (tSt
.hasMoreTokens())
400 tToks
.add(tSt
.nextToken());
403 comp
: for (int i
= 0; i
< cToks
.size(); i
++) {
404 if (tToks
.size() <= i
) {
405 // equals until then, tested shorter
410 String c
= (String
) cToks
.get(i
);
411 String t
= (String
) tToks
.get(i
);
414 int cInt
= Integer
.parseInt(c
);
415 int tInt
= Integer
.parseInt(t
);
419 comp
= (cInt
- tInt
);
422 } catch (NumberFormatException e
) {
426 comp
= c
.compareTo(t
);
432 if (comp
== 0 && tToks
.size() > cToks
.size()) {
433 // equals until then, current shorter
440 public List
getLocationsUrls(String baseUrl
, String bundleLocations
) {
441 List urls
= new ArrayList();
443 if (bundleLocations
== null)
445 bundleLocations
= SystemPropertyUtils
446 .resolvePlaceholders(bundleLocations
);
448 debug(PROP_ARGEO_OSGI_LOCATIONS
+ "=" + bundleLocations
);
450 StringTokenizer st
= new StringTokenizer(bundleLocations
,
452 while (st
.hasMoreTokens()) {
453 urls
.add(locationToUrl(baseUrl
, st
.nextToken().trim()));
458 public List
getBundlesUrls() {
459 String baseUrl
= getPropertyCompat(PROP_ARGEO_OSGI_BASE_URL
,
460 PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
461 String bundlePatterns
= getPropertyCompat(PROP_ARGEO_OSGI_BUNDLES
,
462 PROP_SLC_OSGI_BUNDLES
);
463 return getBundlesUrls(baseUrl
, bundlePatterns
);
466 public List
getBundlesUrls(String baseUrl
, String bundlePatterns
) {
467 List urls
= new ArrayList();
469 List bundlesSets
= new ArrayList();
470 if (bundlePatterns
== null)
472 bundlePatterns
= SystemPropertyUtils
473 .resolvePlaceholders(bundlePatterns
);
475 debug(PROP_ARGEO_OSGI_BUNDLES
+ "=" + bundlePatterns
476 + " (excludeSvn=" + excludeSvn
+ ")");
478 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
479 while (st
.hasMoreTokens()) {
480 bundlesSets
.add(new BundlesSet(st
.nextToken()));
483 List included
= new ArrayList();
484 PathMatcher matcher
= new AntPathMatcher();
485 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
486 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
487 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
488 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
489 match(matcher
, included
, bundlesSet
.getDir(), null, pattern
);
493 List excluded
= new ArrayList();
494 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
495 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
496 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
497 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
498 match(matcher
, excluded
, bundlesSet
.getDir(), null, pattern
);
502 for (int i
= 0; i
< included
.size(); i
++) {
503 String fullPath
= (String
) included
.get(i
);
504 if (!excluded
.contains(fullPath
))
505 urls
.add(locationToUrl(baseUrl
, fullPath
));
511 protected void match(PathMatcher matcher
, List matched
, String base
,
512 String currentPath
, String pattern
) {
513 if (currentPath
== null) {
515 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
516 File
[] files
= baseDir
.listFiles();
519 warn("Base dir " + baseDir
+ " has no children, exists="
520 + baseDir
.exists() + ", isDirectory="
521 + baseDir
.isDirectory());
525 for (int i
= 0; i
< files
.length
; i
++)
526 match(matcher
, matched
, base
, files
[i
].getName(), pattern
);
528 String fullPath
= base
+ '/' + currentPath
;
529 if (matched
.contains(fullPath
))
530 return;// don't try deeper if already matched
532 boolean ok
= matcher
.match(pattern
, currentPath
);
534 debug(currentPath
+ " " + (ok ?
"" : " not ")
535 + " matched with " + pattern
);
537 matched
.add(fullPath
);
540 String newFullPath
= relativeToFullPath(base
, currentPath
);
541 File newFile
= new File(newFullPath
);
542 File
[] files
= newFile
.listFiles();
544 for (int i
= 0; i
< files
.length
; i
++) {
545 String newCurrentPath
= currentPath
+ '/'
546 + files
[i
].getName();
547 if (files
[i
].isDirectory()) {
548 if (matcher
.matchStart(pattern
, newCurrentPath
)) {
549 // recurse only if start matches
550 match(matcher
, matched
, base
, newCurrentPath
,
555 + " does not start match with "
560 boolean nonDirectoryOk
= matcher
.match(pattern
,
563 debug(currentPath
+ " " + (ok ?
"" : " not ")
564 + " matched with " + pattern
);
566 matched
.add(relativeToFullPath(base
,
575 protected String
locationToUrl(String baseUrl
, String location
) {
576 int extInd
= location
.lastIndexOf('.');
579 ext
= location
.substring(extInd
);
581 if (baseUrl
.startsWith("reference:") && ".jar".equals(ext
))
582 return "file:" + location
;
584 return baseUrl
+ location
;
587 /** Transforms a relative path in a full system path. */
588 protected String
relativeToFullPath(String basePath
, String relativePath
) {
589 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
592 protected static void info(Object obj
) {
593 System
.out
.println("#OSGiBOOT# " + obj
);
596 protected void debug(Object obj
) {
598 System
.out
.println("#OSGiBOOT DEBUG# " + obj
);
601 protected void warn(Object obj
) {
602 System
.out
.println("# WARN " + obj
);
603 // Because of a weird bug under Windows when starting it in a forked VM
604 // if (System.getProperty("os.name").contains("Windows"))
605 // System.out.println("# WARN " + obj);
607 // System.err.println("# WARN " + obj);
610 protected String
getProperty(String name
, String defaultValue
) {
612 if (defaultValue
!= null)
613 value
= System
.getProperty(name
, defaultValue
);
615 value
= System
.getProperty(name
);
617 if (value
== null || value
.equals(""))
623 protected String
getProperty(String name
) {
624 return getProperty(name
, null);
627 protected String
getPropertyCompat(String name
, String oldName
) {
628 return getPropertyCompat(name
, oldName
, null);
631 protected String
getPropertyCompat(String name
, String oldName
,
632 String defaultValue
) {
635 if (defaultValue
!= null) {
636 res
= getProperty(name
, defaultValue
);
637 if (res
.equals(defaultValue
)) {
638 res
= getProperty(oldName
, defaultValue
);
639 if (!res
.equals(defaultValue
))
640 warnDeprecated(name
, oldName
);
643 res
= getProperty(name
, null);
645 res
= getProperty(oldName
, null);
647 warnDeprecated(name
, oldName
);
653 protected void warnDeprecated(String name
, String oldName
) {
654 warn("Property '" + oldName
655 + "' is deprecated and will be removed soon, use '" + name
659 public boolean getDebug() {
663 public void setDebug(boolean debug
) {
667 public BundleContext
getBundleContext() {
668 return bundleContext
;
671 /** Whether to exclude Subversion directories (true by default) */
672 public boolean isExcludeSvn() {
676 public void setExcludeSvn(boolean excludeSvn
) {
677 this.excludeSvn
= excludeSvn
;
680 protected class BundlesSet
{
681 private String baseUrl
= "reference:file";// not used yet
682 private final String dir
;
683 private List includes
= new ArrayList();
684 private List excludes
= new ArrayList();
686 public BundlesSet(String def
) {
687 StringTokenizer st
= new StringTokenizer(def
, ";");
689 if (!st
.hasMoreTokens())
690 throw new RuntimeException("Base dir not defined.");
692 String dirPath
= st
.nextToken();
694 if (dirPath
.startsWith("file:"))
695 dirPath
= dirPath
.substring("file:".length());
697 dir
= new File(dirPath
.replace('/', File
.separatorChar
))
700 debug("Base dir: " + dir
);
701 } catch (IOException e
) {
702 throw new RuntimeException("Cannot convert to absolute path", e
);
705 while (st
.hasMoreTokens()) {
706 String tk
= st
.nextToken();
707 StringTokenizer stEq
= new StringTokenizer(tk
, "=");
708 String type
= stEq
.nextToken();
709 String pattern
= stEq
.nextToken();
710 if ("in".equals(type
) || "include".equals(type
)) {
711 includes
.add(pattern
);
712 } else if ("ex".equals(type
) || "exclude".equals(type
)) {
713 excludes
.add(pattern
);
714 } else if ("baseUrl".equals(type
)) {
717 System
.err
.println("Unkown bundles pattern type " + type
);
721 if (excludeSvn
&& !excludes
.contains(EXCLUDES_SVN_PATTERN
)) {
722 excludes
.add(EXCLUDES_SVN_PATTERN
);
726 public String
getDir() {
730 public List
getIncludes() {
734 public List
getExcludes() {
738 public String
getBaseUrl() {
744 public void setDefaultTimeout(long defaultTimeout
) {
745 this.defaultTimeout
= defaultTimeout
;
748 public void setModulesUrlSeparator(String modulesUrlSeparator
) {
749 this.modulesUrlSeparator
= modulesUrlSeparator
;