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_SLC_OSGI_START
= "slc.osgi.start";
26 public final static String PROP_SLC_OSGI_BUNDLES
= "slc.osgi.bundles";
27 public final static String PROP_SLC_OSGI_LOCATIONS
= "slc.osgi.locations";
28 public final static String PROP_SLC_OSGI_BASE_URL
= "slc.osgi.baseUrl";
29 public final static String PROP_SLC_OSGI_MODULES_URL
= "slc.osgi.modulesUrl";
31 public final static String PROP_SLC_OSGIBOOT_DEBUG
= "slc.osgiboot.debug";
32 public final static String PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
= "slc.osgiboot.defaultTimeout";
33 public final static String PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
= "slc.osgiboot.modulesUrlSeparator";
35 public final static String DEFAULT_BASE_URL
= "reference:file:";
36 public final static String EXCLUDES_SVN_PATTERN
= "**/.svn/**";
38 private boolean debug
= Boolean
.valueOf(
39 System
.getProperty(PROP_SLC_OSGIBOOT_DEBUG
, "false"))
41 /** Default is 10s (set in constructor) */
42 private long defaultTimeout
;
44 private boolean excludeSvn
= true;
45 /** Default is ',' (set in constructor) */
46 private String modulesUrlSeparator
= ",";
48 private final BundleContext bundleContext
;
50 public OsgiBoot(BundleContext bundleContext
) {
51 this.bundleContext
= bundleContext
;
52 defaultTimeout
= Long
.parseLong(getProperty(
53 PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
, "10000"));
54 modulesUrlSeparator
= getProperty(
55 PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
, ",");
58 public void bootstrap() {
59 info("SLC OSGi bootstrap starting...");
60 installUrls(getBundlesUrls());
61 installUrls(getLocationsUrls());
62 installUrls(getModulesUrls());
64 info("SLC OSGi bootstrap completed");
67 public void installUrls(List urls
) {
68 Map installedBundles
= getInstalledBundles();
69 for (int i
= 0; i
< urls
.size(); i
++) {
70 String url
= (String
) urls
.get(i
);
72 if (installedBundles
.containsKey(url
)) {
73 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
76 debug("Bundle " + bundle
.getSymbolicName()
77 + " already installed from " + url
);
79 Bundle bundle
= bundleContext
.installBundle(url
);
81 debug("Installed bundle " + bundle
.getSymbolicName()
84 } catch (BundleException e
) {
85 warn("Could not install bundle from " + url
+ ": "
92 public void installOrUpdateUrls(Map urls
) {
93 Map installedBundles
= getBundles();
95 for (Iterator modules
= urls
.keySet().iterator(); modules
.hasNext();) {
96 String moduleName
= (String
) modules
.next();
97 String urlStr
= (String
) urls
.get(moduleName
);
98 if (installedBundles
.containsKey(moduleName
)) {
99 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
102 URL url
= new URL(urlStr
);
103 in
= url
.openStream();
105 info("Updated bundle " + moduleName
+ " from " + urlStr
);
106 } catch (Exception e
) {
107 throw new RuntimeException("Cannot update " + moduleName
108 + " from " + urlStr
);
113 } catch (IOException e
) {
118 Bundle bundle
= bundleContext
.installBundle(urlStr
);
120 debug("Installed bundle " + bundle
.getSymbolicName()
121 + " from " + urlStr
);
122 } catch (BundleException e
) {
123 warn("Could not install bundle from " + urlStr
+ ": "
131 public void startBundles() {
132 String bundlesToStart
= getProperty(PROP_SLC_OSGI_START
);
133 startBundles(bundlesToStart
);
136 public void startBundles(String bundlesToStartStr
) {
137 if (bundlesToStartStr
== null)
140 StringTokenizer st
= new StringTokenizer(bundlesToStartStr
, ",");
141 List bundlesToStart
= new ArrayList();
142 while (st
.hasMoreTokens()) {
143 String name
= st
.nextToken().trim();
144 bundlesToStart
.add(name
);
146 startBundles(bundlesToStart
);
149 public void startBundles(List bundlesToStart
) {
150 if (bundlesToStart
.size() == 0)
153 // used to log the bundles not found
154 List notFoundBundles
= new ArrayList(bundlesToStart
);
156 Bundle
[] bundles
= bundleContext
.getBundles();
157 long startBegin
= System
.currentTimeMillis();
158 for (int i
= 0; i
< bundles
.length
; i
++) {
159 Bundle bundle
= bundles
[i
];
160 String symbolicName
= bundle
.getSymbolicName();
161 if (bundlesToStart
.contains(symbolicName
))
165 } catch (Exception e
) {
166 warn("Start of bundle " + symbolicName
167 + " failed because of " + e
168 + ", maybe bundle is not yet resolved,"
169 + " waiting and trying again.");
170 waitForBundleResolvedOrActive(startBegin
, bundle
);
173 notFoundBundles
.remove(symbolicName
);
174 } catch (Exception e
) {
175 warn("Bundle " + symbolicName
+ " cannot be started: "
177 // was found even if start failed
178 notFoundBundles
.remove(symbolicName
);
182 for (int i
= 0; i
< notFoundBundles
.size(); i
++)
183 warn("Bundle " + notFoundBundles
.get(i
)
184 + " not started because it was not found.");
187 protected void waitForBundleResolvedOrActive(long startBegin
, Bundle bundle
)
189 int originalState
= bundle
.getState();
190 if ((originalState
== Bundle
.RESOLVED
)
191 || (originalState
== Bundle
.ACTIVE
))
194 String originalStateStr
= stateAsString(originalState
);
196 int currentState
= bundle
.getState();
197 while (!(currentState
== Bundle
.RESOLVED
|| currentState
== Bundle
.ACTIVE
)) {
198 long now
= System
.currentTimeMillis();
199 if ((now
- startBegin
) > defaultTimeout
)
200 throw new Exception("Bundle " + bundle
.getSymbolicName()
201 + " was not RESOLVED or ACTIVE after "
202 + (now
- startBegin
) + "ms (originalState="
203 + originalStateStr
+ ", currentState="
204 + stateAsString(currentState
) + ")");
208 } catch (InterruptedException e
) {
211 currentState
= bundle
.getState();
215 public static String
stateAsString(int state
) {
217 case Bundle
.UNINSTALLED
:
218 return "UNINSTALLED";
219 case Bundle
.INSTALLED
:
221 case Bundle
.RESOLVED
:
223 case Bundle
.STARTING
:
227 case Bundle
.STOPPING
:
230 return Integer
.toString(state
);
234 /** Key is location */
235 public Map
getInstalledBundles() {
236 Map installedBundles
= new HashMap();
238 Bundle
[] bundles
= bundleContext
.getBundles();
239 for (int i
= 0; i
< bundles
.length
; i
++) {
240 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
242 return installedBundles
;
245 /** Key is symbolic name */
246 public Map
getBundles() {
247 Map namedBundles
= new HashMap();
248 Bundle
[] bundles
= bundleContext
.getBundles();
249 for (int i
= 0; i
< bundles
.length
; i
++) {
250 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
255 public List
getLocationsUrls() {
256 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
257 String bundleLocations
= getProperty(PROP_SLC_OSGI_LOCATIONS
);
258 return getLocationsUrls(baseUrl
, bundleLocations
);
261 public List
getModulesUrls() {
262 List urls
= new ArrayList();
263 String modulesUrlStr
= getProperty(PROP_SLC_OSGI_MODULES_URL
);
264 if (modulesUrlStr
== null)
267 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
);
269 Map installedBundles
= getBundles();
271 BufferedReader reader
= null;
273 URL modulesUrl
= new URL(modulesUrlStr
);
274 reader
= new BufferedReader(new InputStreamReader(modulesUrl
277 while ((line
= reader
.readLine()) != null) {
278 StringTokenizer st
= new StringTokenizer(line
,
279 modulesUrlSeparator
);
280 String moduleName
= st
.nextToken();
281 String moduleVersion
= st
.nextToken();
282 String url
= st
.nextToken();
286 if (installedBundles
.containsKey(moduleName
)) {
287 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
288 String bundleVersion
= bundle
.getHeaders().get(
289 Constants
.BUNDLE_VERSION
).toString();
290 int comp
= compareVersions(bundleVersion
, moduleVersion
);
292 warn("Installed version " + bundleVersion
293 + " of bundle " + moduleName
294 + " is newer than provided version "
296 } else if (comp
< 0) {
298 info("Updated bundle " + moduleName
+ " with version "
299 + moduleVersion
+ " (old version was "
300 + bundleVersion
+ ")");
308 } catch (Exception e1
) {
309 throw new RuntimeException("Cannot read url " + modulesUrlStr
, e1
);
314 } catch (IOException e
) {
322 * @return ==0: versions are identical, <0: tested version is newer, >0:
323 * currentVersion is newer.
325 protected int compareVersions(String currentVersion
, String testedVersion
) {
326 List cToks
= new ArrayList();
327 StringTokenizer cSt
= new StringTokenizer(currentVersion
, ".");
328 while (cSt
.hasMoreTokens())
329 cToks
.add(cSt
.nextToken());
330 List tToks
= new ArrayList();
331 StringTokenizer tSt
= new StringTokenizer(currentVersion
, ".");
332 while (tSt
.hasMoreTokens())
333 tToks
.add(tSt
.nextToken());
336 comp
: for (int i
= 0; i
< cToks
.size(); i
++) {
337 if (tToks
.size() <= i
) {
338 // equals until then, tested shorter
343 String c
= (String
) cToks
.get(i
);
344 String t
= (String
) tToks
.get(i
);
347 int cInt
= Integer
.parseInt(c
);
348 int tInt
= Integer
.parseInt(t
);
352 comp
= (cInt
- tInt
);
355 } catch (NumberFormatException e
) {
359 comp
= c
.compareTo(t
);
365 if (comp
== 0 && tToks
.size() > cToks
.size()) {
366 // equals until then, current shorter
373 public List
getLocationsUrls(String baseUrl
, String bundleLocations
) {
374 List urls
= new ArrayList();
376 if (bundleLocations
== null)
378 bundleLocations
= SystemPropertyUtils
379 .resolvePlaceholders(bundleLocations
);
381 debug(PROP_SLC_OSGI_LOCATIONS
+ "=" + bundleLocations
);
383 StringTokenizer st
= new StringTokenizer(bundleLocations
,
385 while (st
.hasMoreTokens()) {
386 urls
.add(locationToUrl(baseUrl
, st
.nextToken().trim()));
391 public List
getBundlesUrls() {
392 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
393 String bundlePatterns
= getProperty(PROP_SLC_OSGI_BUNDLES
);
394 return getBundlesUrls(baseUrl
, bundlePatterns
);
397 public List
getBundlesUrls(String baseUrl
, String bundlePatterns
) {
398 List urls
= new ArrayList();
400 List bundlesSets
= new ArrayList();
401 if (bundlePatterns
== null)
403 bundlePatterns
= SystemPropertyUtils
404 .resolvePlaceholders(bundlePatterns
);
406 debug(PROP_SLC_OSGI_BUNDLES
+ "=" + bundlePatterns
407 + " (excludeSvn=" + excludeSvn
+ ")");
409 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
410 while (st
.hasMoreTokens()) {
411 bundlesSets
.add(new BundlesSet(st
.nextToken()));
414 List included
= new ArrayList();
415 PathMatcher matcher
= new AntPathMatcher();
416 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
417 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
418 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
419 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
420 match(matcher
, included
, bundlesSet
.getDir(), null, pattern
);
424 List excluded
= new ArrayList();
425 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
426 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
427 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
428 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
429 match(matcher
, excluded
, bundlesSet
.getDir(), null, pattern
);
433 for (int i
= 0; i
< included
.size(); i
++) {
434 String fullPath
= (String
) included
.get(i
);
435 if (!excluded
.contains(fullPath
))
436 urls
.add(locationToUrl(baseUrl
, fullPath
));
442 protected void match(PathMatcher matcher
, List matched
, String base
,
443 String currentPath
, String pattern
) {
444 if (currentPath
== null) {
446 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
447 File
[] files
= baseDir
.listFiles();
450 warn("Base dir " + baseDir
+ " has no children, exists="
451 + baseDir
.exists() + ", isDirectory="
452 + baseDir
.isDirectory());
456 for (int i
= 0; i
< files
.length
; i
++)
457 match(matcher
, matched
, base
, files
[i
].getName(), pattern
);
459 String fullPath
= base
+ '/' + currentPath
;
460 if (matched
.contains(fullPath
))
461 return;// don't try deeper if already matched
463 boolean ok
= matcher
.match(pattern
, currentPath
);
465 debug(currentPath
+ " " + (ok ?
"" : " not ")
466 + " matched with " + pattern
);
468 matched
.add(fullPath
);
471 String newFullPath
= relativeToFullPath(base
, currentPath
);
472 File newFile
= new File(newFullPath
);
473 File
[] files
= newFile
.listFiles();
475 for (int i
= 0; i
< files
.length
; i
++) {
476 String newCurrentPath
= currentPath
+ '/'
477 + files
[i
].getName();
478 if (files
[i
].isDirectory()) {
479 if (matcher
.matchStart(pattern
, newCurrentPath
)) {
480 // recurse only if start matches
481 match(matcher
, matched
, base
, newCurrentPath
,
486 + " does not start match with "
491 boolean nonDirectoryOk
= matcher
.match(pattern
,
494 debug(currentPath
+ " " + (ok ?
"" : " not ")
495 + " matched with " + pattern
);
497 matched
.add(relativeToFullPath(base
,
506 protected String
locationToUrl(String baseUrl
, String location
) {
507 int extInd
= location
.lastIndexOf('.');
510 ext
= location
.substring(extInd
);
512 if (baseUrl
.startsWith("reference:") && ".jar".equals(ext
))
513 return "file:" + location
;
515 return baseUrl
+ location
;
518 /** Transforms a relative path in a full system path. */
519 protected String
relativeToFullPath(String basePath
, String relativePath
) {
520 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
523 protected static void info(Object obj
) {
524 System
.out
.println("#OSGiBOOT# " + obj
);
527 protected void debug(Object obj
) {
529 System
.out
.println("#OSGiBOOT DEBUG# " + obj
);
532 protected void warn(Object obj
) {
533 System
.out
.println("# WARN " + obj
);
534 // Because of a weird bug under Windows when starting it in a forked VM
535 // if (System.getProperty("os.name").contains("Windows"))
536 // System.out.println("# WARN " + obj);
538 // System.err.println("# WARN " + obj);
541 protected String
getProperty(String name
, String defaultValue
) {
543 if (defaultValue
!= null)
544 value
= System
.getProperty(name
, defaultValue
);
546 value
= System
.getProperty(name
);
548 if (value
== null || value
.equals(""))
554 protected String
getProperty(String name
) {
555 return getProperty(name
, null);
558 public boolean getDebug() {
562 public void setDebug(boolean debug
) {
566 public BundleContext
getBundleContext() {
567 return bundleContext
;
570 /** Whether to exclude Subversion directories (true by default) */
571 public boolean isExcludeSvn() {
575 public void setExcludeSvn(boolean excludeSvn
) {
576 this.excludeSvn
= excludeSvn
;
579 protected class BundlesSet
{
580 private String baseUrl
= "reference:file";// not used yet
581 private final String dir
;
582 private List includes
= new ArrayList();
583 private List excludes
= new ArrayList();
585 public BundlesSet(String def
) {
586 StringTokenizer st
= new StringTokenizer(def
, ";");
588 if (!st
.hasMoreTokens())
589 throw new RuntimeException("Base dir not defined.");
591 String dirPath
= st
.nextToken();
593 if (dirPath
.startsWith("file:"))
594 dirPath
= dirPath
.substring("file:".length());
596 dir
= new File(dirPath
.replace('/', File
.separatorChar
))
599 debug("Base dir: " + dir
);
600 } catch (IOException e
) {
601 throw new RuntimeException("Cannot convert to absolute path", e
);
604 while (st
.hasMoreTokens()) {
605 String tk
= st
.nextToken();
606 StringTokenizer stEq
= new StringTokenizer(tk
, "=");
607 String type
= stEq
.nextToken();
608 String pattern
= stEq
.nextToken();
609 if ("in".equals(type
) || "include".equals(type
)) {
610 includes
.add(pattern
);
611 } else if ("ex".equals(type
) || "exclude".equals(type
)) {
612 excludes
.add(pattern
);
613 } else if ("baseUrl".equals(type
)) {
616 System
.err
.println("Unkown bundles pattern type " + type
);
620 if (excludeSvn
&& !excludes
.contains(EXCLUDES_SVN_PATTERN
)) {
621 excludes
.add(EXCLUDES_SVN_PATTERN
);
625 public String
getDir() {
629 public List
getIncludes() {
633 public List
getExcludes() {
637 public String
getBaseUrl() {
643 public void setDefaultTimeout(long defaultTimeout
) {
644 this.defaultTimeout
= defaultTimeout
;
647 public void setModulesUrlSeparator(String modulesUrlSeparator
) {
648 this.modulesUrlSeparator
= modulesUrlSeparator
;