]> git.argeo.org Git - gpl/argeo-slc.git/blob - legacy/org.argeo.slc.spring/src/org/argeo/slc/osgi/BundlesManager.java
Adapt to changes in Argeo Commons
[gpl/argeo-slc.git] / legacy / org.argeo.slc.spring / src / org / argeo / slc / osgi / BundlesManager.java
1 package org.argeo.slc.osgi;
2
3 import java.util.Collection;
4
5 import org.apache.commons.logging.Log;
6 import org.apache.commons.logging.LogFactory;
7 import org.argeo.slc.SlcException;
8 import org.eclipse.gemini.blueprint.context.BundleContextAware;
9 import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextEvent;
10 import org.eclipse.gemini.blueprint.context.event.OsgiBundleApplicationContextListener;
11 import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextClosedEvent;
12 import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextFailedEvent;
13 import org.eclipse.gemini.blueprint.context.event.OsgiBundleContextRefreshedEvent;
14 import org.eclipse.gemini.blueprint.util.OsgiBundleUtils;
15 import org.eclipse.gemini.blueprint.util.OsgiFilterUtils;
16 import org.osgi.framework.Bundle;
17 import org.osgi.framework.BundleContext;
18 import org.osgi.framework.BundleException;
19 import org.osgi.framework.Constants;
20 import org.osgi.framework.FrameworkEvent;
21 import org.osgi.framework.FrameworkListener;
22 import org.osgi.framework.InvalidSyntaxException;
23 import org.osgi.framework.ServiceReference;
24 import org.osgi.service.packageadmin.PackageAdmin;
25 import org.osgi.util.tracker.ServiceTracker;
26 import org.springframework.beans.factory.DisposableBean;
27 import org.springframework.beans.factory.InitializingBean;
28 import org.springframework.context.ApplicationContext;
29 import org.springframework.util.Assert;
30
31 /** Wraps low-level access to a {@link BundleContext} */
32 @SuppressWarnings("deprecation")
33 public class BundlesManager implements BundleContextAware, FrameworkListener,
34 InitializingBean, DisposableBean,
35 OsgiBundleApplicationContextListener<OsgiBundleApplicationContextEvent> {
36 private final static Log log = LogFactory.getLog(BundlesManager.class);
37
38 private BundleContext bundleContext;
39
40 private Long defaultTimeout = 60 * 1000l;
41 private Long pollingPeriod = 200l;
42
43 // Refresh sync objects
44 private final Object refreshedPackageSem = new Object();
45 private Boolean packagesRefreshed = false;
46
47 public BundlesManager() {
48 }
49
50 public BundlesManager(BundleContext bundleContext) {
51 this.bundleContext = bundleContext;
52 }
53
54 /**
55 * Stop the module, update it, refresh it and restart it. All synchronously.
56 */
57 public void upgradeSynchronous(OsgiBundle osgiBundle) {
58 try {
59 Bundle bundle = findRelatedBundle(osgiBundle);
60
61 long begin = System.currentTimeMillis();
62
63 long bStop = begin;
64 stopSynchronous(bundle);
65
66 long bUpdate = System.currentTimeMillis();
67 updateSynchronous(bundle);
68
69 // Refresh in case there are fragments
70 long bRefresh = System.currentTimeMillis();
71 refreshSynchronous(bundle);
72
73 long bStart = System.currentTimeMillis();
74 startSynchronous(bundle);
75
76 long aStart = System.currentTimeMillis();
77 if (log.isTraceEnabled()) {
78 log.debug("OSGi upgrade performed in " + (aStart - begin)
79 + "ms for bundle " + osgiBundle);
80 log.debug(" stop \t: " + (bUpdate - bStop) + "ms");
81 log.debug(" update\t: " + (bRefresh - bUpdate) + "ms");
82 log.debug(" refresh\t: " + (bStart - bRefresh) + "ms");
83 log.debug(" start\t: " + (aStart - bStart) + "ms");
84 log.debug(" TOTAL\t: " + (aStart - begin) + "ms");
85 }
86
87 long bAppContext = System.currentTimeMillis();
88 String filter = "(Bundle-SymbolicName=" + bundle.getSymbolicName()
89 + ")";
90 // Wait for application context to be ready
91 // TODO: use service tracker
92 Collection<ServiceReference<ApplicationContext>> srs = getServiceRefSynchronous(
93 ApplicationContext.class, filter);
94 ServiceReference<ApplicationContext> sr = srs.iterator().next();
95 long aAppContext = System.currentTimeMillis();
96 long end = aAppContext;
97
98 if (log.isTraceEnabled()) {
99 log.debug("Application context refresh performed in "
100 + (aAppContext - bAppContext) + "ms for bundle "
101 + osgiBundle);
102 }
103
104 if (log.isDebugEnabled())
105 log.debug("Bundle '" + bundle.getSymbolicName()
106 + "' upgraded and ready " + " (upgrade performed in "
107 + (end - begin) + "ms).");
108
109 if (log.isTraceEnabled()) {
110 ApplicationContext applicationContext = (ApplicationContext) bundleContext
111 .getService(sr);
112 int beanDefCount = applicationContext.getBeanDefinitionCount();
113 log.debug(" " + beanDefCount + " beans in app context of "
114 + bundle.getSymbolicName()
115 + ", average init time per bean=" + (end - begin)
116 / beanDefCount + "ms");
117 }
118
119 bundleContext.ungetService(sr);
120
121 } catch (Exception e) {
122 throw new SlcException("Cannot update bundle " + osgiBundle, e);
123 }
124 }
125
126 /** Updates bundle synchronously. */
127 protected void updateSynchronous(Bundle bundle) throws BundleException {
128 bundle.update();
129 boolean waiting = true;
130
131 long begin = System.currentTimeMillis();
132 do {
133 int state = bundle.getState();
134 if (state == Bundle.INSTALLED || state == Bundle.ACTIVE
135 || state == Bundle.RESOLVED)
136 waiting = false;
137
138 sleepWhenPolling();
139 checkTimeout(begin, "Update of bundle " + bundle.getSymbolicName()
140 + " timed out. Bundle state = " + bundle.getState());
141 } while (waiting);
142
143 if (log.isTraceEnabled())
144 log.debug("Bundle " + bundle.getSymbolicName() + " updated.");
145 }
146
147 /** Starts bundle synchronously. Does nothing if already started. */
148 protected void startSynchronous(Bundle bundle) throws BundleException {
149 int originalState = bundle.getState();
150 if (originalState == Bundle.ACTIVE)
151 return;
152
153 bundle.start();
154 boolean waiting = true;
155
156 long begin = System.currentTimeMillis();
157 do {
158 if (bundle.getState() == Bundle.ACTIVE)
159 waiting = false;
160
161 sleepWhenPolling();
162 checkTimeout(begin, "Start of bundle " + bundle.getSymbolicName()
163 + " timed out. Bundle state = " + bundle.getState());
164 } while (waiting);
165
166 if (log.isTraceEnabled())
167 log.debug("Bundle " + bundle.getSymbolicName() + " started.");
168 }
169
170 /** Stops bundle synchronously. Does nothing if already started. */
171 protected void stopSynchronous(Bundle bundle) throws BundleException {
172 int originalState = bundle.getState();
173 if (originalState != Bundle.ACTIVE)
174 return;
175
176 bundle.stop();
177 boolean waiting = true;
178
179 long begin = System.currentTimeMillis();
180 do {
181 if (bundle.getState() != Bundle.ACTIVE
182 && bundle.getState() != Bundle.STOPPING)
183 waiting = false;
184
185 sleepWhenPolling();
186 checkTimeout(begin, "Stop of bundle " + bundle.getSymbolicName()
187 + " timed out. Bundle state = " + bundle.getState());
188 } while (waiting);
189
190 if (log.isTraceEnabled())
191 log.debug("Bundle " + bundle.getSymbolicName() + " stopped.");
192 }
193
194 /** Refresh bundle synchronously. Does nothing if already started. */
195 protected void refreshSynchronous(Bundle bundle) throws BundleException {
196 ServiceReference<PackageAdmin> packageAdminRef = bundleContext
197 .getServiceReference(PackageAdmin.class);
198 PackageAdmin packageAdmin = (PackageAdmin) bundleContext
199 .getService(packageAdminRef);
200 Bundle[] bundles = { bundle };
201
202 long begin = System.currentTimeMillis();
203 synchronized (refreshedPackageSem) {
204 packagesRefreshed = false;
205 packageAdmin.refreshPackages(bundles);
206 try {
207 refreshedPackageSem.wait(defaultTimeout);
208 } catch (InterruptedException e) {
209 // silent
210 }
211 if (!packagesRefreshed) {
212 long now = System.currentTimeMillis();
213 throw new SlcException("Packages not refreshed after "
214 + (now - begin) + "ms");
215 } else {
216 packagesRefreshed = false;
217 }
218 }
219
220 if (log.isTraceEnabled())
221 log.debug("Bundle " + bundle.getSymbolicName() + " refreshed.");
222 }
223
224 public void frameworkEvent(FrameworkEvent event) {
225 if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
226 synchronized (refreshedPackageSem) {
227 packagesRefreshed = true;
228 refreshedPackageSem.notifyAll();
229 }
230 }
231 }
232
233 public <S> Collection<ServiceReference<S>> getServiceRefSynchronous(
234 Class<S> clss, String filter) throws InvalidSyntaxException {
235 if (log.isTraceEnabled())
236 log.debug("Filter: '" + filter + "'");
237 Collection<ServiceReference<S>> sfs = null;
238 boolean waiting = true;
239 long begin = System.currentTimeMillis();
240 do {
241 sfs = bundleContext.getServiceReferences(clss, filter);
242
243 if (sfs != null)
244 waiting = false;
245
246 sleepWhenPolling();
247 checkTimeout(begin, "Search of services " + clss + " with filter "
248 + filter + " timed out.");
249 } while (waiting);
250
251 return sfs;
252 }
253
254 protected void checkTimeout(long begin, String msg) {
255 long now = System.currentTimeMillis();
256 if (now - begin > defaultTimeout)
257 throw new SlcException(msg + " (timeout after " + (now - begin)
258 + "ms)");
259
260 }
261
262 protected void sleepWhenPolling() {
263 try {
264 Thread.sleep(pollingPeriod);
265 } catch (InterruptedException e) {
266 throw new SlcException("Polling interrupted");
267 }
268 }
269
270 /** Creates and open a new service tracker. */
271 public <S> ServiceTracker<S, S> newTracker(Class<S> clss) {
272 ServiceTracker<S, S> st = new ServiceTracker<S, S>(bundleContext, clss,
273 null);
274 st.open();
275 return st;
276 }
277
278 public <T> T getSingleService(Class<T> clss, String filter,
279 Boolean synchronous) {
280 if (filter != null)
281 Assert.isTrue(OsgiFilterUtils.isValidFilter(filter), "valid filter");
282 Collection<ServiceReference<T>> sfs;
283 try {
284 if (synchronous)
285 sfs = getServiceRefSynchronous(clss, filter);
286 else
287 sfs = bundleContext.getServiceReferences(clss, filter);
288 } catch (InvalidSyntaxException e) {
289 throw new SlcException("Cannot retrieve service reference for "
290 + filter, e);
291 }
292
293 if (sfs == null || sfs.size() == 0)
294 return null;
295 else if (sfs.size() > 1)
296 throw new SlcException("More than one execution flow found for "
297 + filter);
298 return (T) bundleContext.getService(sfs.iterator().next());
299 }
300
301 public <T> T getSingleServiceStrict(Class<T> clss, String filter,
302 Boolean synchronous) {
303 T service = getSingleService(clss, filter, synchronous);
304 if (service == null)
305 throw new SlcException("No execution flow found for " + filter);
306 else
307 return service;
308 }
309
310 public OsgiBundle findRelatedBundle(String moduleName, String moduleVersion) {
311 OsgiBundle osgiBundle = new OsgiBundle(moduleName, moduleVersion);
312 if (osgiBundle.getVersion() == null) {
313 Bundle bundle = findRelatedBundle(osgiBundle);
314 osgiBundle = new OsgiBundle(bundle);
315 }
316 return osgiBundle;
317 }
318
319 /**
320 * @param osgiBundle
321 * cannot be null
322 * @return the related bundle or null if not found
323 * @throws SlcException
324 * if osgiBundle argument is null
325 */
326 public Bundle findRelatedBundle(OsgiBundle osgiBundle) {
327 if (osgiBundle == null)
328 throw new SlcException("OSGi bundle cannot be null");
329
330 Bundle bundle = null;
331 if (osgiBundle.getInternalBundleId() != null) {
332 bundle = bundleContext.getBundle(osgiBundle.getInternalBundleId());
333 Assert.isTrue(
334 osgiBundle.getName().equals(bundle.getSymbolicName()),
335 "symbolic name consistent");
336 if (osgiBundle.getVersion() != null)
337 Assert.isTrue(
338 osgiBundle.getVersion().equals(
339 bundle.getHeaders().get(
340 Constants.BUNDLE_VERSION)),
341 "version consistent");
342 } else if (osgiBundle.getVersion() == null
343 || osgiBundle.getVersion().equals("0.0.0")) {
344 bundle = OsgiBundleUtils.findBundleBySymbolicName(bundleContext,
345 osgiBundle.getName());
346 } else {// scan all bundles
347 bundles: for (Bundle b : bundleContext.getBundles()) {
348 if (b.getSymbolicName() == null) {
349 log.warn("Bundle " + b + " has no symbolic name defined.");
350 continue bundles;
351 }
352
353 if (b.getSymbolicName().equals(osgiBundle.getName())) {
354 if (osgiBundle.getVersion() == null) {
355 bundle = b;
356 break bundles;
357 }
358
359 if (b.getHeaders().get(Constants.BUNDLE_VERSION)
360 .equals(osgiBundle.getVersion())) {
361 bundle = b;
362 osgiBundle.setInternalBundleId(b.getBundleId());
363 break bundles;
364 }
365 }
366 }
367 }
368 return bundle;
369 }
370
371 /** Find a single bundle based on a symbolic name pattern. */
372 public OsgiBundle findFromPattern(String pattern) {
373 OsgiBundle osgiBundle = null;
374 for (Bundle b : bundleContext.getBundles()) {
375 if (b.getSymbolicName().contains(pattern)) {
376 osgiBundle = new OsgiBundle(b);
377 break;
378 }
379 }
380 return osgiBundle;
381 }
382
383 public OsgiBundle getBundle(Long bundleId) {
384 Bundle bundle = bundleContext.getBundle(bundleId);
385 return new OsgiBundle(bundle);
386 }
387
388 public void setBundleContext(BundleContext bundleContext) {
389 this.bundleContext = bundleContext;
390 }
391
392 public void afterPropertiesSet() throws Exception {
393 bundleContext.addFrameworkListener(this);
394 }
395
396 public void destroy() throws Exception {
397 bundleContext.removeFrameworkListener(this);
398 }
399
400 public void setDefaultTimeout(Long defaultTimeout) {
401 this.defaultTimeout = defaultTimeout;
402 }
403
404 /**
405 * Use with caution since it may interfer with some cached information
406 * within this object
407 */
408 public BundleContext getBundleContext() {
409 return bundleContext;
410 }
411
412 public void setPollingPeriod(Long pollingPeriod) {
413 this.pollingPeriod = pollingPeriod;
414 }
415
416 public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) {
417 if (event instanceof OsgiBundleContextRefreshedEvent) {
418 log.debug("App context refreshed: " + event);
419 } else if (event instanceof OsgiBundleContextFailedEvent) {
420 log.debug("App context failed: " + event);
421 }
422 if (event instanceof OsgiBundleContextClosedEvent) {
423 log.debug("App context closed: " + event);
424 }
425
426 }
427
428 }