1 package org
.argeo
.slc
.osgi
;
3 import java
.util
.Collection
;
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
;
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);
38 private BundleContext bundleContext
;
40 private Long defaultTimeout
= 60 * 1000l;
41 private Long pollingPeriod
= 200l;
43 // Refresh sync objects
44 private final Object refreshedPackageSem
= new Object();
45 private Boolean packagesRefreshed
= false;
47 public BundlesManager() {
50 public BundlesManager(BundleContext bundleContext
) {
51 this.bundleContext
= bundleContext
;
55 * Stop the module, update it, refresh it and restart it. All synchronously.
57 public void upgradeSynchronous(OsgiBundle osgiBundle
) {
59 Bundle bundle
= findRelatedBundle(osgiBundle
);
61 long begin
= System
.currentTimeMillis();
64 stopSynchronous(bundle
);
66 long bUpdate
= System
.currentTimeMillis();
67 updateSynchronous(bundle
);
69 // Refresh in case there are fragments
70 long bRefresh
= System
.currentTimeMillis();
71 refreshSynchronous(bundle
);
73 long bStart
= System
.currentTimeMillis();
74 startSynchronous(bundle
);
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");
87 long bAppContext
= System
.currentTimeMillis();
88 String filter
= "(Bundle-SymbolicName=" + bundle
.getSymbolicName()
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
;
98 if (log
.isTraceEnabled()) {
99 log
.debug("Application context refresh performed in "
100 + (aAppContext
- bAppContext
) + "ms for bundle "
104 if (log
.isDebugEnabled())
105 log
.debug("Bundle '" + bundle
.getSymbolicName()
106 + "' upgraded and ready " + " (upgrade performed in "
107 + (end
- begin
) + "ms).");
109 if (log
.isTraceEnabled()) {
110 ApplicationContext applicationContext
= (ApplicationContext
) bundleContext
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");
119 bundleContext
.ungetService(sr
);
121 } catch (Exception e
) {
122 throw new SlcException("Cannot update bundle " + osgiBundle
, e
);
126 /** Updates bundle synchronously. */
127 protected void updateSynchronous(Bundle bundle
) throws BundleException
{
129 boolean waiting
= true;
131 long begin
= System
.currentTimeMillis();
133 int state
= bundle
.getState();
134 if (state
== Bundle
.INSTALLED
|| state
== Bundle
.ACTIVE
135 || state
== Bundle
.RESOLVED
)
139 checkTimeout(begin
, "Update of bundle " + bundle
.getSymbolicName()
140 + " timed out. Bundle state = " + bundle
.getState());
143 if (log
.isTraceEnabled())
144 log
.debug("Bundle " + bundle
.getSymbolicName() + " updated.");
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
)
154 boolean waiting
= true;
156 long begin
= System
.currentTimeMillis();
158 if (bundle
.getState() == Bundle
.ACTIVE
)
162 checkTimeout(begin
, "Start of bundle " + bundle
.getSymbolicName()
163 + " timed out. Bundle state = " + bundle
.getState());
166 if (log
.isTraceEnabled())
167 log
.debug("Bundle " + bundle
.getSymbolicName() + " started.");
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
)
177 boolean waiting
= true;
179 long begin
= System
.currentTimeMillis();
181 if (bundle
.getState() != Bundle
.ACTIVE
182 && bundle
.getState() != Bundle
.STOPPING
)
186 checkTimeout(begin
, "Stop of bundle " + bundle
.getSymbolicName()
187 + " timed out. Bundle state = " + bundle
.getState());
190 if (log
.isTraceEnabled())
191 log
.debug("Bundle " + bundle
.getSymbolicName() + " stopped.");
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
};
202 long begin
= System
.currentTimeMillis();
203 synchronized (refreshedPackageSem
) {
204 packagesRefreshed
= false;
205 packageAdmin
.refreshPackages(bundles
);
207 refreshedPackageSem
.wait(defaultTimeout
);
208 } catch (InterruptedException e
) {
211 if (!packagesRefreshed
) {
212 long now
= System
.currentTimeMillis();
213 throw new SlcException("Packages not refreshed after "
214 + (now
- begin
) + "ms");
216 packagesRefreshed
= false;
220 if (log
.isTraceEnabled())
221 log
.debug("Bundle " + bundle
.getSymbolicName() + " refreshed.");
224 public void frameworkEvent(FrameworkEvent event
) {
225 if (event
.getType() == FrameworkEvent
.PACKAGES_REFRESHED
) {
226 synchronized (refreshedPackageSem
) {
227 packagesRefreshed
= true;
228 refreshedPackageSem
.notifyAll();
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();
241 sfs
= bundleContext
.getServiceReferences(clss
, filter
);
247 checkTimeout(begin
, "Search of services " + clss
+ " with filter "
248 + filter
+ " timed out.");
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
)
262 protected void sleepWhenPolling() {
264 Thread
.sleep(pollingPeriod
);
265 } catch (InterruptedException e
) {
266 throw new SlcException("Polling interrupted");
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
,
278 public <T
> T
getSingleService(Class
<T
> clss
, String filter
,
279 Boolean synchronous
) {
281 Assert
.isTrue(OsgiFilterUtils
.isValidFilter(filter
), "valid filter");
282 Collection
<ServiceReference
<T
>> sfs
;
285 sfs
= getServiceRefSynchronous(clss
, filter
);
287 sfs
= bundleContext
.getServiceReferences(clss
, filter
);
288 } catch (InvalidSyntaxException e
) {
289 throw new SlcException("Cannot retrieve service reference for "
293 if (sfs
== null || sfs
.size() == 0)
295 else if (sfs
.size() > 1)
296 throw new SlcException("More than one execution flow found for "
298 return (T
) bundleContext
.getService(sfs
.iterator().next());
301 public <T
> T
getSingleServiceStrict(Class
<T
> clss
, String filter
,
302 Boolean synchronous
) {
303 T service
= getSingleService(clss
, filter
, synchronous
);
305 throw new SlcException("No execution flow found for " + filter
);
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
);
322 * @return the related bundle or null if not found
323 * @throws SlcException
324 * if osgiBundle argument is null
326 public Bundle
findRelatedBundle(OsgiBundle osgiBundle
) {
327 if (osgiBundle
== null)
328 throw new SlcException("OSGi bundle cannot be null");
330 Bundle bundle
= null;
331 if (osgiBundle
.getInternalBundleId() != null) {
332 bundle
= bundleContext
.getBundle(osgiBundle
.getInternalBundleId());
334 osgiBundle
.getName().equals(bundle
.getSymbolicName()),
335 "symbolic name consistent");
336 if (osgiBundle
.getVersion() != null)
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.");
353 if (b
.getSymbolicName().equals(osgiBundle
.getName())) {
354 if (osgiBundle
.getVersion() == null) {
359 if (b
.getHeaders().get(Constants
.BUNDLE_VERSION
)
360 .equals(osgiBundle
.getVersion())) {
362 osgiBundle
.setInternalBundleId(b
.getBundleId());
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
);
383 public OsgiBundle
getBundle(Long bundleId
) {
384 Bundle bundle
= bundleContext
.getBundle(bundleId
);
385 return new OsgiBundle(bundle
);
388 public void setBundleContext(BundleContext bundleContext
) {
389 this.bundleContext
= bundleContext
;
392 public void afterPropertiesSet() throws Exception
{
393 bundleContext
.addFrameworkListener(this);
396 public void destroy() throws Exception
{
397 bundleContext
.removeFrameworkListener(this);
400 public void setDefaultTimeout(Long defaultTimeout
) {
401 this.defaultTimeout
= defaultTimeout
;
405 * Use with caution since it may interfer with some cached information
408 public BundleContext
getBundleContext() {
409 return bundleContext
;
412 public void setPollingPeriod(Long pollingPeriod
) {
413 this.pollingPeriod
= pollingPeriod
;
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
);
422 if (event
instanceof OsgiBundleContextClosedEvent
) {
423 log
.debug("App context closed: " + event
);