2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org
.argeo
.slc
.osgiboot
;
19 import java
.io
.BufferedReader
;
21 import java
.io
.IOException
;
22 import java
.io
.InputStream
;
23 import java
.io
.InputStreamReader
;
25 import java
.util
.ArrayList
;
26 import java
.util
.HashMap
;
27 import java
.util
.Iterator
;
28 import java
.util
.List
;
30 import java
.util
.StringTokenizer
;
32 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.AntPathMatcher
;
33 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.PathMatcher
;
34 import org
.argeo
.slc
.osgiboot
.internal
.springutil
.SystemPropertyUtils
;
35 import org
.osgi
.framework
.Bundle
;
36 import org
.osgi
.framework
.BundleContext
;
37 import org
.osgi
.framework
.BundleException
;
38 import org
.osgi
.framework
.Constants
;
40 public class OsgiBoot
{
41 public final static String PROP_SLC_OSGI_START
= "slc.osgi.start";
42 public final static String PROP_SLC_OSGI_BUNDLES
= "slc.osgi.bundles";
43 public final static String PROP_SLC_OSGI_LOCATIONS
= "slc.osgi.locations";
44 public final static String PROP_SLC_OSGI_BASE_URL
= "slc.osgi.baseUrl";
45 public final static String PROP_SLC_OSGI_MODULES_URL
= "slc.osgi.modulesUrl";
47 public final static String PROP_SLC_OSGIBOOT_DEBUG
= "slc.osgiboot.debug";
48 public final static String PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
= "slc.osgiboot.defaultTimeout";
49 public final static String PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
= "slc.osgiboot.modulesUrlSeparator";
50 public final static String PROP_SLC_OSGIBOOT_SYSTEM_PROPERTIES_FILE
= "slc.osgiboot.systemPropertiesFile";
52 public final static String DEFAULT_BASE_URL
= "reference:file:";
53 public final static String EXCLUDES_SVN_PATTERN
= "**/.svn/**";
55 private boolean debug
= Boolean
.valueOf(
56 System
.getProperty(PROP_SLC_OSGIBOOT_DEBUG
, "false"))
58 /** Default is 10s (set in constructor) */
59 private long defaultTimeout
;
61 private boolean excludeSvn
= true;
62 /** Default is ',' (set in constructor) */
63 private String modulesUrlSeparator
= ",";
65 private final BundleContext bundleContext
;
67 public OsgiBoot(BundleContext bundleContext
) {
68 this.bundleContext
= bundleContext
;
69 defaultTimeout
= Long
.parseLong(getProperty(
70 PROP_SLC_OSGIBOOT_DEFAULT_TIMEOUT
, "10000"));
71 modulesUrlSeparator
= getProperty(
72 PROP_SLC_OSGIBOOT_MODULES_URL_SEPARATOR
, ",");
74 warn("############################################################################################################");
75 warn("########################## THIS COMPONENT IS DEPRECATED AND WILL BE REMOVED SOON ##########################");
76 warn("########################## USE org.argeo.commons.osgi:org.argeo.osgi.boot INSTEAD ##########################");
77 warn("############################################################################################################");
80 public void bootstrap() {
81 info("SLC OSGi bootstrap starting...");
82 installUrls(getBundlesUrls());
83 installUrls(getLocationsUrls());
84 installUrls(getModulesUrls());
86 info("SLC OSGi bootstrap completed");
89 public void installUrls(List urls
) {
90 Map installedBundles
= getInstalledBundles();
91 for (int i
= 0; i
< urls
.size(); i
++) {
92 String url
= (String
) urls
.get(i
);
94 if (installedBundles
.containsKey(url
)) {
95 Bundle bundle
= (Bundle
) installedBundles
.get(url
);
98 debug("Bundle " + bundle
.getSymbolicName()
99 + " already installed from " + url
);
101 Bundle bundle
= bundleContext
.installBundle(url
);
103 debug("Installed bundle " + bundle
.getSymbolicName()
106 } catch (BundleException e
) {
107 warn("Could not install bundle from " + url
+ ": "
116 public void installOrUpdateUrls(Map urls
) {
117 Map installedBundles
= getBundles();
119 for (Iterator modules
= urls
.keySet().iterator(); modules
.hasNext();) {
120 String moduleName
= (String
) modules
.next();
121 String urlStr
= (String
) urls
.get(moduleName
);
122 if (installedBundles
.containsKey(moduleName
)) {
123 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
126 URL url
= new URL(urlStr
);
127 in
= url
.openStream();
129 info("Updated bundle " + moduleName
+ " from " + urlStr
);
130 } catch (Exception e
) {
131 throw new RuntimeException("Cannot update " + moduleName
132 + " from " + urlStr
);
137 } catch (IOException e
) {
142 Bundle bundle
= bundleContext
.installBundle(urlStr
);
144 debug("Installed bundle " + bundle
.getSymbolicName()
145 + " from " + urlStr
);
146 } catch (BundleException e
) {
147 warn("Could not install bundle from " + urlStr
+ ": "
155 public void startBundles() {
156 String bundlesToStart
= getProperty(PROP_SLC_OSGI_START
);
157 startBundles(bundlesToStart
);
160 public void startBundles(String bundlesToStartStr
) {
161 if (bundlesToStartStr
== null)
164 StringTokenizer st
= new StringTokenizer(bundlesToStartStr
, ",");
165 List bundlesToStart
= new ArrayList();
166 while (st
.hasMoreTokens()) {
167 String name
= st
.nextToken().trim();
168 bundlesToStart
.add(name
);
170 startBundles(bundlesToStart
);
173 public void startBundles(List bundlesToStart
) {
174 if (bundlesToStart
.size() == 0)
177 // used to log the bundles not found
178 List notFoundBundles
= new ArrayList(bundlesToStart
);
180 Bundle
[] bundles
= bundleContext
.getBundles();
181 long startBegin
= System
.currentTimeMillis();
182 for (int i
= 0; i
< bundles
.length
; i
++) {
183 Bundle bundle
= bundles
[i
];
184 String symbolicName
= bundle
.getSymbolicName();
185 if (bundlesToStart
.contains(symbolicName
))
189 } catch (Exception e
) {
190 warn("Start of bundle " + symbolicName
191 + " failed because of " + e
192 + ", maybe bundle is not yet resolved,"
193 + " waiting and trying again.");
194 waitForBundleResolvedOrActive(startBegin
, bundle
);
197 notFoundBundles
.remove(symbolicName
);
198 } catch (Exception e
) {
199 warn("Bundle " + symbolicName
+ " cannot be started: "
203 // was found even if start failed
204 notFoundBundles
.remove(symbolicName
);
208 for (int i
= 0; i
< notFoundBundles
.size(); i
++)
209 warn("Bundle " + notFoundBundles
.get(i
)
210 + " not started because it was not found.");
213 protected void waitForBundleResolvedOrActive(long startBegin
, Bundle bundle
)
215 int originalState
= bundle
.getState();
216 if ((originalState
== Bundle
.RESOLVED
)
217 || (originalState
== Bundle
.ACTIVE
))
220 String originalStateStr
= stateAsString(originalState
);
222 int currentState
= bundle
.getState();
223 while (!(currentState
== Bundle
.RESOLVED
|| currentState
== Bundle
.ACTIVE
)) {
224 long now
= System
.currentTimeMillis();
225 if ((now
- startBegin
) > defaultTimeout
)
226 throw new Exception("Bundle " + bundle
.getSymbolicName()
227 + " was not RESOLVED or ACTIVE after "
228 + (now
- startBegin
) + "ms (originalState="
229 + originalStateStr
+ ", currentState="
230 + stateAsString(currentState
) + ")");
234 } catch (InterruptedException e
) {
237 currentState
= bundle
.getState();
241 public static String
stateAsString(int state
) {
243 case Bundle
.UNINSTALLED
:
244 return "UNINSTALLED";
245 case Bundle
.INSTALLED
:
247 case Bundle
.RESOLVED
:
249 case Bundle
.STARTING
:
253 case Bundle
.STOPPING
:
256 return Integer
.toString(state
);
260 /** Key is location */
261 public Map
getInstalledBundles() {
262 Map installedBundles
= new HashMap();
264 Bundle
[] bundles
= bundleContext
.getBundles();
265 for (int i
= 0; i
< bundles
.length
; i
++) {
266 installedBundles
.put(bundles
[i
].getLocation(), bundles
[i
]);
268 return installedBundles
;
271 /** Key is symbolic name */
272 public Map
getBundles() {
273 Map namedBundles
= new HashMap();
274 Bundle
[] bundles
= bundleContext
.getBundles();
275 for (int i
= 0; i
< bundles
.length
; i
++) {
276 namedBundles
.put(bundles
[i
].getSymbolicName(), bundles
[i
]);
281 public List
getLocationsUrls() {
282 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
283 String bundleLocations
= getProperty(PROP_SLC_OSGI_LOCATIONS
);
284 return getLocationsUrls(baseUrl
, bundleLocations
);
287 public List
getModulesUrls() {
288 List urls
= new ArrayList();
289 String modulesUrlStr
= getProperty(PROP_SLC_OSGI_MODULES_URL
);
290 if (modulesUrlStr
== null)
293 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
);
295 Map installedBundles
= getBundles();
297 BufferedReader reader
= null;
299 URL modulesUrl
= new URL(modulesUrlStr
);
300 reader
= new BufferedReader(new InputStreamReader(modulesUrl
303 while ((line
= reader
.readLine()) != null) {
304 StringTokenizer st
= new StringTokenizer(line
,
305 modulesUrlSeparator
);
306 String moduleName
= st
.nextToken();
307 String moduleVersion
= st
.nextToken();
308 String url
= st
.nextToken();
312 if (installedBundles
.containsKey(moduleName
)) {
313 Bundle bundle
= (Bundle
) installedBundles
.get(moduleName
);
314 String bundleVersion
= bundle
.getHeaders().get(
315 Constants
.BUNDLE_VERSION
).toString();
316 int comp
= compareVersions(bundleVersion
, moduleVersion
);
318 warn("Installed version " + bundleVersion
319 + " of bundle " + moduleName
320 + " is newer than provided version "
322 } else if (comp
< 0) {
324 info("Updated bundle " + moduleName
+ " with version "
325 + moduleVersion
+ " (old version was "
326 + bundleVersion
+ ")");
334 } catch (Exception e1
) {
335 throw new RuntimeException("Cannot read url " + modulesUrlStr
, e1
);
340 } catch (IOException e
) {
348 * @return ==0: versions are identical, <0: tested version is newer, >0:
349 * currentVersion is newer.
351 protected int compareVersions(String currentVersion
, String testedVersion
) {
352 List cToks
= new ArrayList();
353 StringTokenizer cSt
= new StringTokenizer(currentVersion
, ".");
354 while (cSt
.hasMoreTokens())
355 cToks
.add(cSt
.nextToken());
356 List tToks
= new ArrayList();
357 StringTokenizer tSt
= new StringTokenizer(currentVersion
, ".");
358 while (tSt
.hasMoreTokens())
359 tToks
.add(tSt
.nextToken());
362 comp
: for (int i
= 0; i
< cToks
.size(); i
++) {
363 if (tToks
.size() <= i
) {
364 // equals until then, tested shorter
369 String c
= (String
) cToks
.get(i
);
370 String t
= (String
) tToks
.get(i
);
373 int cInt
= Integer
.parseInt(c
);
374 int tInt
= Integer
.parseInt(t
);
378 comp
= (cInt
- tInt
);
381 } catch (NumberFormatException e
) {
385 comp
= c
.compareTo(t
);
391 if (comp
== 0 && tToks
.size() > cToks
.size()) {
392 // equals until then, current shorter
399 public List
getLocationsUrls(String baseUrl
, String bundleLocations
) {
400 List urls
= new ArrayList();
402 if (bundleLocations
== null)
404 bundleLocations
= SystemPropertyUtils
405 .resolvePlaceholders(bundleLocations
);
407 debug(PROP_SLC_OSGI_LOCATIONS
+ "=" + bundleLocations
);
409 StringTokenizer st
= new StringTokenizer(bundleLocations
,
411 while (st
.hasMoreTokens()) {
412 urls
.add(locationToUrl(baseUrl
, st
.nextToken().trim()));
417 public List
getBundlesUrls() {
418 String baseUrl
= getProperty(PROP_SLC_OSGI_BASE_URL
, DEFAULT_BASE_URL
);
419 String bundlePatterns
= getProperty(PROP_SLC_OSGI_BUNDLES
);
420 return getBundlesUrls(baseUrl
, bundlePatterns
);
423 public List
getBundlesUrls(String baseUrl
, String bundlePatterns
) {
424 List urls
= new ArrayList();
426 List bundlesSets
= new ArrayList();
427 if (bundlePatterns
== null)
429 bundlePatterns
= SystemPropertyUtils
430 .resolvePlaceholders(bundlePatterns
);
432 debug(PROP_SLC_OSGI_BUNDLES
+ "=" + bundlePatterns
433 + " (excludeSvn=" + excludeSvn
+ ")");
435 StringTokenizer st
= new StringTokenizer(bundlePatterns
, ",");
436 while (st
.hasMoreTokens()) {
437 bundlesSets
.add(new BundlesSet(st
.nextToken()));
440 List included
= new ArrayList();
441 PathMatcher matcher
= new AntPathMatcher();
442 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
443 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
444 for (int j
= 0; j
< bundlesSet
.getIncludes().size(); j
++) {
445 String pattern
= (String
) bundlesSet
.getIncludes().get(j
);
446 match(matcher
, included
, bundlesSet
.getDir(), null, pattern
);
450 List excluded
= new ArrayList();
451 for (int i
= 0; i
< bundlesSets
.size(); i
++) {
452 BundlesSet bundlesSet
= (BundlesSet
) bundlesSets
.get(i
);
453 for (int j
= 0; j
< bundlesSet
.getExcludes().size(); j
++) {
454 String pattern
= (String
) bundlesSet
.getExcludes().get(j
);
455 match(matcher
, excluded
, bundlesSet
.getDir(), null, pattern
);
459 for (int i
= 0; i
< included
.size(); i
++) {
460 String fullPath
= (String
) included
.get(i
);
461 if (!excluded
.contains(fullPath
))
462 urls
.add(locationToUrl(baseUrl
, fullPath
));
468 protected void match(PathMatcher matcher
, List matched
, String base
,
469 String currentPath
, String pattern
) {
470 if (currentPath
== null) {
472 File baseDir
= new File(base
.replace('/', File
.separatorChar
));
473 File
[] files
= baseDir
.listFiles();
476 warn("Base dir " + baseDir
+ " has no children, exists="
477 + baseDir
.exists() + ", isDirectory="
478 + baseDir
.isDirectory());
482 for (int i
= 0; i
< files
.length
; i
++)
483 match(matcher
, matched
, base
, files
[i
].getName(), pattern
);
485 String fullPath
= base
+ '/' + currentPath
;
486 if (matched
.contains(fullPath
))
487 return;// don't try deeper if already matched
489 boolean ok
= matcher
.match(pattern
, currentPath
);
491 debug(currentPath
+ " " + (ok ?
"" : " not ")
492 + " matched with " + pattern
);
494 matched
.add(fullPath
);
497 String newFullPath
= relativeToFullPath(base
, currentPath
);
498 File newFile
= new File(newFullPath
);
499 File
[] files
= newFile
.listFiles();
501 for (int i
= 0; i
< files
.length
; i
++) {
502 String newCurrentPath
= currentPath
+ '/'
503 + files
[i
].getName();
504 if (files
[i
].isDirectory()) {
505 if (matcher
.matchStart(pattern
, newCurrentPath
)) {
506 // recurse only if start matches
507 match(matcher
, matched
, base
, newCurrentPath
,
512 + " does not start match with "
517 boolean nonDirectoryOk
= matcher
.match(pattern
,
520 debug(currentPath
+ " " + (ok ?
"" : " not ")
521 + " matched with " + pattern
);
523 matched
.add(relativeToFullPath(base
,
532 protected String
locationToUrl(String baseUrl
, String location
) {
533 int extInd
= location
.lastIndexOf('.');
536 ext
= location
.substring(extInd
);
538 if (baseUrl
.startsWith("reference:") && ".jar".equals(ext
))
539 return "file:" + location
;
541 return baseUrl
+ location
;
544 /** Transforms a relative path in a full system path. */
545 protected String
relativeToFullPath(String basePath
, String relativePath
) {
546 return (basePath
+ '/' + relativePath
).replace('/', File
.separatorChar
);
549 protected static void info(Object obj
) {
550 System
.out
.println("#OSGiBOOT# " + obj
);
553 protected void debug(Object obj
) {
555 System
.out
.println("#OSGiBOOT DEBUG# " + obj
);
558 protected void warn(Object obj
) {
559 System
.out
.println("# WARN " + obj
);
560 // Because of a weird bug under Windows when starting it in a forked VM
561 // if (System.getProperty("os.name").contains("Windows"))
562 // System.out.println("# WARN " + obj);
564 // System.err.println("# WARN " + obj);
567 protected String
getProperty(String name
, String defaultValue
) {
569 if (defaultValue
!= null)
570 value
= System
.getProperty(name
, defaultValue
);
572 value
= System
.getProperty(name
);
574 if (value
== null || value
.equals(""))
580 protected String
getProperty(String name
) {
581 return getProperty(name
, null);
584 public boolean getDebug() {
588 public void setDebug(boolean debug
) {
592 public BundleContext
getBundleContext() {
593 return bundleContext
;
596 /** Whether to exclude Subversion directories (true by default) */
597 public boolean isExcludeSvn() {
601 public void setExcludeSvn(boolean excludeSvn
) {
602 this.excludeSvn
= excludeSvn
;
605 protected class BundlesSet
{
606 private String baseUrl
= "reference:file";// not used yet
607 private final String dir
;
608 private List includes
= new ArrayList();
609 private List excludes
= new ArrayList();
611 public BundlesSet(String def
) {
612 StringTokenizer st
= new StringTokenizer(def
, ";");
614 if (!st
.hasMoreTokens())
615 throw new RuntimeException("Base dir not defined.");
617 String dirPath
= st
.nextToken();
619 if (dirPath
.startsWith("file:"))
620 dirPath
= dirPath
.substring("file:".length());
622 dir
= new File(dirPath
.replace('/', File
.separatorChar
))
625 debug("Base dir: " + dir
);
626 } catch (IOException e
) {
627 throw new RuntimeException("Cannot convert to absolute path", e
);
630 while (st
.hasMoreTokens()) {
631 String tk
= st
.nextToken();
632 StringTokenizer stEq
= new StringTokenizer(tk
, "=");
633 String type
= stEq
.nextToken();
634 String pattern
= stEq
.nextToken();
635 if ("in".equals(type
) || "include".equals(type
)) {
636 includes
.add(pattern
);
637 } else if ("ex".equals(type
) || "exclude".equals(type
)) {
638 excludes
.add(pattern
);
639 } else if ("baseUrl".equals(type
)) {
642 System
.err
.println("Unkown bundles pattern type " + type
);
646 if (excludeSvn
&& !excludes
.contains(EXCLUDES_SVN_PATTERN
)) {
647 excludes
.add(EXCLUDES_SVN_PATTERN
);
651 public String
getDir() {
655 public List
getIncludes() {
659 public List
getExcludes() {
663 public String
getBaseUrl() {
669 public void setDefaultTimeout(long defaultTimeout
) {
670 this.defaultTimeout
= defaultTimeout
;
673 public void setModulesUrlSeparator(String modulesUrlSeparator
) {
674 this.modulesUrlSeparator
= modulesUrlSeparator
;