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