]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.osgiboot/src/main/java/org/argeo/slc/osgiboot/OsgiBoot.java
remove empty packages
[gpl/argeo-slc.git] / runtime / org.argeo.slc.osgiboot / src / main / java / org / argeo / slc / osgiboot / 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.slc.osgiboot;
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.StringTokenizer;
31
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;
39
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";
46
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";
51
52 public final static String DEFAULT_BASE_URL = "reference:file:";
53 public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
54
55 private boolean debug = Boolean.valueOf(
56 System.getProperty(PROP_SLC_OSGIBOOT_DEBUG, "false"))
57 .booleanValue();
58 /** Default is 10s (set in constructor) */
59 private long defaultTimeout;
60
61 private boolean excludeSvn = true;
62 /** Default is ',' (set in constructor) */
63 private String modulesUrlSeparator = ",";
64
65 private final BundleContext bundleContext;
66
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, ",");
73
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("############################################################################################################");
78 }
79
80 public void bootstrap() {
81 info("SLC OSGi bootstrap starting...");
82 installUrls(getBundlesUrls());
83 installUrls(getLocationsUrls());
84 installUrls(getModulesUrls());
85 startBundles();
86 info("SLC OSGi bootstrap completed");
87 }
88
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);
93 try {
94 if (installedBundles.containsKey(url)) {
95 Bundle bundle = (Bundle) installedBundles.get(url);
96 // bundle.update();
97 if (debug)
98 debug("Bundle " + bundle.getSymbolicName()
99 + " already installed from " + url);
100 } else {
101 Bundle bundle = bundleContext.installBundle(url);
102 if (debug)
103 debug("Installed bundle " + bundle.getSymbolicName()
104 + " from " + url);
105 }
106 } catch (BundleException e) {
107 warn("Could not install bundle from " + url + ": "
108 + e.getMessage());
109 if (debug)
110 e.printStackTrace();
111 }
112 }
113
114 }
115
116 public void installOrUpdateUrls(Map urls) {
117 Map installedBundles = getBundles();
118
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);
124 InputStream in;
125 try {
126 URL url = new URL(urlStr);
127 in = url.openStream();
128 bundle.update(in);
129 info("Updated bundle " + moduleName + " from " + urlStr);
130 } catch (Exception e) {
131 throw new RuntimeException("Cannot update " + moduleName
132 + " from " + urlStr);
133 }
134 if (in != null)
135 try {
136 in.close();
137 } catch (IOException e) {
138 e.printStackTrace();
139 }
140 } else {
141 try {
142 Bundle bundle = bundleContext.installBundle(urlStr);
143 if (debug)
144 debug("Installed bundle " + bundle.getSymbolicName()
145 + " from " + urlStr);
146 } catch (BundleException e) {
147 warn("Could not install bundle from " + urlStr + ": "
148 + e.getMessage());
149 }
150 }
151 }
152
153 }
154
155 public void startBundles() {
156 String bundlesToStart = getProperty(PROP_SLC_OSGI_START);
157 startBundles(bundlesToStart);
158 }
159
160 public void startBundles(String bundlesToStartStr) {
161 if (bundlesToStartStr == null)
162 return;
163
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);
169 }
170 startBundles(bundlesToStart);
171 }
172
173 public void startBundles(List bundlesToStart) {
174 if (bundlesToStart.size() == 0)
175 return;
176
177 // used to log the bundles not found
178 List notFoundBundles = new ArrayList(bundlesToStart);
179
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))
186 try {
187 try {
188 bundle.start();
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);
195 bundle.start();
196 }
197 notFoundBundles.remove(symbolicName);
198 } catch (Exception e) {
199 warn("Bundle " + symbolicName + " cannot be started: "
200 + e.getMessage());
201 if (debug)
202 e.printStackTrace();
203 // was found even if start failed
204 notFoundBundles.remove(symbolicName);
205 }
206 }
207
208 for (int i = 0; i < notFoundBundles.size(); i++)
209 warn("Bundle " + notFoundBundles.get(i)
210 + " not started because it was not found.");
211 }
212
213 protected void waitForBundleResolvedOrActive(long startBegin, Bundle bundle)
214 throws Exception {
215 int originalState = bundle.getState();
216 if ((originalState == Bundle.RESOLVED)
217 || (originalState == Bundle.ACTIVE))
218 return;
219
220 String originalStateStr = stateAsString(originalState);
221
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) + ")");
231
232 try {
233 Thread.sleep(100l);
234 } catch (InterruptedException e) {
235 // silent
236 }
237 currentState = bundle.getState();
238 }
239 }
240
241 public static String stateAsString(int state) {
242 switch (state) {
243 case Bundle.UNINSTALLED:
244 return "UNINSTALLED";
245 case Bundle.INSTALLED:
246 return "INSTALLED";
247 case Bundle.RESOLVED:
248 return "RESOLVED";
249 case Bundle.STARTING:
250 return "STARTING";
251 case Bundle.ACTIVE:
252 return "ACTIVE";
253 case Bundle.STOPPING:
254 return "STOPPING";
255 default:
256 return Integer.toString(state);
257 }
258 }
259
260 /** Key is location */
261 public Map getInstalledBundles() {
262 Map installedBundles = new HashMap();
263
264 Bundle[] bundles = bundleContext.getBundles();
265 for (int i = 0; i < bundles.length; i++) {
266 installedBundles.put(bundles[i].getLocation(), bundles[i]);
267 }
268 return installedBundles;
269 }
270
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]);
277 }
278 return namedBundles;
279 }
280
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);
285 }
286
287 public List getModulesUrls() {
288 List urls = new ArrayList();
289 String modulesUrlStr = getProperty(PROP_SLC_OSGI_MODULES_URL);
290 if (modulesUrlStr == null)
291 return urls;
292
293 String baseUrl = getProperty(PROP_SLC_OSGI_BASE_URL);
294
295 Map installedBundles = getBundles();
296
297 BufferedReader reader = null;
298 try {
299 URL modulesUrl = new URL(modulesUrlStr);
300 reader = new BufferedReader(new InputStreamReader(modulesUrl
301 .openStream()));
302 String line = null;
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();
309 if (baseUrl != null)
310 url = baseUrl + url;
311
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);
317 if (comp > 0) {
318 warn("Installed version " + bundleVersion
319 + " of bundle " + moduleName
320 + " is newer than provided version "
321 + moduleVersion);
322 } else if (comp < 0) {
323 urls.add(url);
324 info("Updated bundle " + moduleName + " with version "
325 + moduleVersion + " (old version was "
326 + bundleVersion + ")");
327 } else {
328 // do nothing
329 }
330 } else {
331 urls.add(url);
332 }
333 }
334 } catch (Exception e1) {
335 throw new RuntimeException("Cannot read url " + modulesUrlStr, e1);
336 } finally {
337 if (reader != null)
338 try {
339 reader.close();
340 } catch (IOException e) {
341 e.printStackTrace();
342 }
343 }
344 return urls;
345 }
346
347 /**
348 * @return ==0: versions are identical, <0: tested version is newer, >0:
349 * currentVersion is newer.
350 */
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());
360
361 int comp = 0;
362 comp: for (int i = 0; i < cToks.size(); i++) {
363 if (tToks.size() <= i) {
364 // equals until then, tested shorter
365 comp = 1;
366 break comp;
367 }
368
369 String c = (String) cToks.get(i);
370 String t = (String) tToks.get(i);
371
372 try {
373 int cInt = Integer.parseInt(c);
374 int tInt = Integer.parseInt(t);
375 if (cInt == tInt)
376 continue comp;
377 else {
378 comp = (cInt - tInt);
379 break comp;
380 }
381 } catch (NumberFormatException e) {
382 if (c.equals(t))
383 continue comp;
384 else {
385 comp = c.compareTo(t);
386 break comp;
387 }
388 }
389 }
390
391 if (comp == 0 && tToks.size() > cToks.size()) {
392 // equals until then, current shorter
393 comp = -1;
394 }
395
396 return comp;
397 }
398
399 public List getLocationsUrls(String baseUrl, String bundleLocations) {
400 List urls = new ArrayList();
401
402 if (bundleLocations == null)
403 return urls;
404 bundleLocations = SystemPropertyUtils
405 .resolvePlaceholders(bundleLocations);
406 if (debug)
407 debug(PROP_SLC_OSGI_LOCATIONS + "=" + bundleLocations);
408
409 StringTokenizer st = new StringTokenizer(bundleLocations,
410 File.pathSeparator);
411 while (st.hasMoreTokens()) {
412 urls.add(locationToUrl(baseUrl, st.nextToken().trim()));
413 }
414 return urls;
415 }
416
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);
421 }
422
423 public List getBundlesUrls(String baseUrl, String bundlePatterns) {
424 List urls = new ArrayList();
425
426 List bundlesSets = new ArrayList();
427 if (bundlePatterns == null)
428 return urls;
429 bundlePatterns = SystemPropertyUtils
430 .resolvePlaceholders(bundlePatterns);
431 if (debug)
432 debug(PROP_SLC_OSGI_BUNDLES + "=" + bundlePatterns
433 + " (excludeSvn=" + excludeSvn + ")");
434
435 StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
436 while (st.hasMoreTokens()) {
437 bundlesSets.add(new BundlesSet(st.nextToken()));
438 }
439
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);
447 }
448 }
449
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);
456 }
457 }
458
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));
463 }
464
465 return urls;
466 }
467
468 protected void match(PathMatcher matcher, List matched, String base,
469 String currentPath, String pattern) {
470 if (currentPath == null) {
471 // Init
472 File baseDir = new File(base.replace('/', File.separatorChar));
473 File[] files = baseDir.listFiles();
474
475 if (files == null) {
476 warn("Base dir " + baseDir + " has no children, exists="
477 + baseDir.exists() + ", isDirectory="
478 + baseDir.isDirectory());
479 return;
480 }
481
482 for (int i = 0; i < files.length; i++)
483 match(matcher, matched, base, files[i].getName(), pattern);
484 } else {
485 String fullPath = base + '/' + currentPath;
486 if (matched.contains(fullPath))
487 return;// don't try deeper if already matched
488
489 boolean ok = matcher.match(pattern, currentPath);
490 if (debug)
491 debug(currentPath + " " + (ok ? "" : " not ")
492 + " matched with " + pattern);
493 if (ok) {
494 matched.add(fullPath);
495 return;
496 } else {
497 String newFullPath = relativeToFullPath(base, currentPath);
498 File newFile = new File(newFullPath);
499 File[] files = newFile.listFiles();
500 if (files != null) {
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,
508 pattern);
509 } else {
510 if (debug)
511 debug(newCurrentPath
512 + " does not start match with "
513 + pattern);
514
515 }
516 } else {
517 boolean nonDirectoryOk = matcher.match(pattern,
518 newCurrentPath);
519 if (debug)
520 debug(currentPath + " " + (ok ? "" : " not ")
521 + " matched with " + pattern);
522 if (nonDirectoryOk)
523 matched.add(relativeToFullPath(base,
524 newCurrentPath));
525 }
526 }
527 }
528 }
529 }
530 }
531
532 protected String locationToUrl(String baseUrl, String location) {
533 int extInd = location.lastIndexOf('.');
534 String ext = null;
535 if (extInd > 0)
536 ext = location.substring(extInd);
537
538 if (baseUrl.startsWith("reference:") && ".jar".equals(ext))
539 return "file:" + location;
540 else
541 return baseUrl + location;
542 }
543
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);
547 }
548
549 protected static void info(Object obj) {
550 System.out.println("#OSGiBOOT# " + obj);
551 }
552
553 protected void debug(Object obj) {
554 if (debug)
555 System.out.println("#OSGiBOOT DEBUG# " + obj);
556 }
557
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);
563 // else
564 // System.err.println("# WARN " + obj);
565 }
566
567 protected String getProperty(String name, String defaultValue) {
568 final String value;
569 if (defaultValue != null)
570 value = System.getProperty(name, defaultValue);
571 else
572 value = System.getProperty(name);
573
574 if (value == null || value.equals(""))
575 return null;
576 else
577 return value;
578 }
579
580 protected String getProperty(String name) {
581 return getProperty(name, null);
582 }
583
584 public boolean getDebug() {
585 return debug;
586 }
587
588 public void setDebug(boolean debug) {
589 this.debug = debug;
590 }
591
592 public BundleContext getBundleContext() {
593 return bundleContext;
594 }
595
596 /** Whether to exclude Subversion directories (true by default) */
597 public boolean isExcludeSvn() {
598 return excludeSvn;
599 }
600
601 public void setExcludeSvn(boolean excludeSvn) {
602 this.excludeSvn = excludeSvn;
603 }
604
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();
610
611 public BundlesSet(String def) {
612 StringTokenizer st = new StringTokenizer(def, ";");
613
614 if (!st.hasMoreTokens())
615 throw new RuntimeException("Base dir not defined.");
616 try {
617 String dirPath = st.nextToken();
618
619 if (dirPath.startsWith("file:"))
620 dirPath = dirPath.substring("file:".length());
621
622 dir = new File(dirPath.replace('/', File.separatorChar))
623 .getCanonicalPath();
624 if (debug)
625 debug("Base dir: " + dir);
626 } catch (IOException e) {
627 throw new RuntimeException("Cannot convert to absolute path", e);
628 }
629
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)) {
640 baseUrl = pattern;
641 } else {
642 System.err.println("Unkown bundles pattern type " + type);
643 }
644 }
645
646 if (excludeSvn && !excludes.contains(EXCLUDES_SVN_PATTERN)) {
647 excludes.add(EXCLUDES_SVN_PATTERN);
648 }
649 }
650
651 public String getDir() {
652 return dir;
653 }
654
655 public List getIncludes() {
656 return includes;
657 }
658
659 public List getExcludes() {
660 return excludes;
661 }
662
663 public String getBaseUrl() {
664 return baseUrl;
665 }
666
667 }
668
669 public void setDefaultTimeout(long defaultTimeout) {
670 this.defaultTimeout = defaultTimeout;
671 }
672
673 public void setModulesUrlSeparator(String modulesUrlSeparator) {
674 this.modulesUrlSeparator = modulesUrlSeparator;
675 }
676
677 }