1 package org
.argeo
.slc
.osgi
;
3 import org
.apache
.commons
.logging
.Log
;
4 import org
.apache
.commons
.logging
.LogFactory
;
5 import org
.argeo
.slc
.SlcException
;
6 import org
.osgi
.framework
.Bundle
;
7 import org
.osgi
.framework
.BundleContext
;
8 import org
.osgi
.framework
.BundleException
;
9 import org
.osgi
.framework
.Constants
;
10 import org
.osgi
.framework
.FrameworkEvent
;
11 import org
.osgi
.framework
.FrameworkListener
;
12 import org
.osgi
.framework
.InvalidSyntaxException
;
13 import org
.osgi
.framework
.ServiceReference
;
14 import org
.osgi
.service
.packageadmin
.PackageAdmin
;
15 import org
.osgi
.util
.tracker
.ServiceTracker
;
16 import org
.springframework
.beans
.factory
.DisposableBean
;
17 import org
.springframework
.beans
.factory
.InitializingBean
;
18 import org
.springframework
.context
.ApplicationContext
;
19 import org
.springframework
.osgi
.context
.BundleContextAware
;
20 import org
.springframework
.osgi
.context
.event
.OsgiBundleApplicationContextEvent
;
21 import org
.springframework
.osgi
.context
.event
.OsgiBundleApplicationContextListener
;
22 import org
.springframework
.osgi
.context
.event
.OsgiBundleContextClosedEvent
;
23 import org
.springframework
.osgi
.context
.event
.OsgiBundleContextFailedEvent
;
24 import org
.springframework
.osgi
.context
.event
.OsgiBundleContextRefreshedEvent
;
25 import org
.springframework
.osgi
.util
.OsgiBundleUtils
;
26 import org
.springframework
.osgi
.util
.OsgiFilterUtils
;
27 import org
.springframework
.util
.Assert
;
29 /** Wraps low-level access to a {@link BundleContext} */
30 public class BundlesManager
implements BundleContextAware
, FrameworkListener
,
31 InitializingBean
, DisposableBean
, OsgiBundleApplicationContextListener
{
32 private final static Log log
= LogFactory
.getLog(BundlesManager
.class);
34 private BundleContext bundleContext
;
36 private Long defaultTimeout
= 10000l;
37 private Long pollingPeriod
= 100l;
39 // Refresh sync objects
40 private final Object refreshedPackageSem
= new Object();
41 private Boolean packagesRefreshed
= false;
44 * Stop the module, update it, refresh it and restart it. All synchronously.
46 public void upgradeSynchronous(OsgiBundle osgiBundle
) {
48 Bundle bundle
= findRelatedBundle(osgiBundle
);
50 long begin
= System
.currentTimeMillis();
53 stopSynchronous(bundle
);
55 long bUpdate
= System
.currentTimeMillis();
56 updateSynchronous(bundle
);
58 // Refresh in case there are fragments
59 long bRefresh
= System
.currentTimeMillis();
60 refreshSynchronous(bundle
);
62 long bStart
= System
.currentTimeMillis();
63 startSynchronous(bundle
);
65 long aStart
= System
.currentTimeMillis();
66 if (log
.isTraceEnabled()) {
67 log
.debug("OSGi upgrade performed in " + (aStart
- begin
)
68 + "ms for bundle " + osgiBundle
);
69 log
.debug(" stop \t: " + (bUpdate
- bStop
) + "ms");
70 log
.debug(" update\t: " + (bRefresh
- bUpdate
) + "ms");
71 log
.debug(" refresh\t: " + (bStart
- bRefresh
) + "ms");
72 log
.debug(" start\t: " + (aStart
- bStart
) + "ms");
73 log
.debug(" TOTAL\t: " + (aStart
- begin
) + "ms");
76 long bAppContext
= System
.currentTimeMillis();
77 String filter
= "(Bundle-SymbolicName=" + bundle
.getSymbolicName()
79 // Wait for application context to be ready
80 // TODO: use service tracker
81 ServiceReference
[] srs
= getServiceRefSynchronous(
82 ApplicationContext
.class.getName(), filter
);
83 ServiceReference sr
= srs
[0];
84 long aAppContext
= System
.currentTimeMillis();
85 long end
= aAppContext
;
87 if (log
.isTraceEnabled()) {
88 log
.debug("Application context refresh performed in "
89 + (aAppContext
- bAppContext
) + "ms for bundle "
93 if (log
.isDebugEnabled())
94 log
.debug("Bundle '" + bundle
.getSymbolicName()
95 + "' upgraded and ready " + " (upgrade performed in "
96 + (end
- begin
) + "ms).");
98 if (log
.isTraceEnabled()) {
99 ApplicationContext applicationContext
= (ApplicationContext
) bundleContext
101 int beanDefCount
= applicationContext
.getBeanDefinitionCount();
102 log
.debug(" " + beanDefCount
+ " beans in app context of "
103 + bundle
.getSymbolicName()
104 + ", average init time per bean=" + (end
- begin
)
105 / beanDefCount
+ "ms");
108 bundleContext
.ungetService(sr
);
110 } catch (Exception e
) {
111 throw new SlcException("Cannot update bundle " + osgiBundle
, e
);
115 /** Updates bundle synchronously. */
116 protected void updateSynchronous(Bundle bundle
) throws BundleException
{
118 boolean waiting
= true;
120 long begin
= System
.currentTimeMillis();
122 int state
= bundle
.getState();
123 if (state
== Bundle
.INSTALLED
|| state
== Bundle
.ACTIVE
124 || state
== Bundle
.RESOLVED
)
128 checkTimeout(begin
, "Update of bundle " + bundle
.getSymbolicName()
129 + " timed out. Bundle state = " + bundle
.getState());
132 if (log
.isTraceEnabled())
133 log
.debug("Bundle " + bundle
.getSymbolicName() + " updated.");
136 /** Starts bundle synchronously. Does nothing if already started. */
137 protected void startSynchronous(Bundle bundle
) throws BundleException
{
138 int originalState
= bundle
.getState();
139 if (originalState
== Bundle
.ACTIVE
)
143 boolean waiting
= true;
145 long begin
= System
.currentTimeMillis();
147 if (bundle
.getState() == Bundle
.ACTIVE
)
151 checkTimeout(begin
, "Start of bundle " + bundle
.getSymbolicName()
152 + " timed out. Bundle state = " + bundle
.getState());
155 if (log
.isTraceEnabled())
156 log
.debug("Bundle " + bundle
.getSymbolicName() + " started.");
159 /** Stops bundle synchronously. Does nothing if already started. */
160 protected void stopSynchronous(Bundle bundle
) throws BundleException
{
161 int originalState
= bundle
.getState();
162 if (originalState
!= Bundle
.ACTIVE
)
166 boolean waiting
= true;
168 long begin
= System
.currentTimeMillis();
170 if (bundle
.getState() != Bundle
.ACTIVE
171 && bundle
.getState() != Bundle
.STOPPING
)
175 checkTimeout(begin
, "Stop of bundle " + bundle
.getSymbolicName()
176 + " timed out. Bundle state = " + bundle
.getState());
179 if (log
.isTraceEnabled())
180 log
.debug("Bundle " + bundle
.getSymbolicName() + " stopped.");
183 /** Refresh bundle synchronously. Does nothing if already started. */
184 protected void refreshSynchronous(Bundle bundle
) throws BundleException
{
185 ServiceReference packageAdminRef
= bundleContext
186 .getServiceReference(PackageAdmin
.class.getName());
187 PackageAdmin packageAdmin
= (PackageAdmin
) bundleContext
188 .getService(packageAdminRef
);
189 Bundle
[] bundles
= { bundle
};
191 long begin
= System
.currentTimeMillis();
192 synchronized (refreshedPackageSem
) {
193 packagesRefreshed
= false;
194 packageAdmin
.refreshPackages(bundles
);
196 refreshedPackageSem
.wait(defaultTimeout
);
197 } catch (InterruptedException e
) {
200 if (!packagesRefreshed
) {
201 long now
= System
.currentTimeMillis();
202 throw new SlcException("Packages not refreshed after "
203 + (now
- begin
) + "ms");
205 packagesRefreshed
= false;
209 if (log
.isTraceEnabled())
210 log
.debug("Bundle " + bundle
.getSymbolicName() + " refreshed.");
213 public void frameworkEvent(FrameworkEvent event
) {
214 if (event
.getType() == FrameworkEvent
.PACKAGES_REFRESHED
) {
215 synchronized (refreshedPackageSem
) {
216 packagesRefreshed
= true;
217 refreshedPackageSem
.notifyAll();
222 public ServiceReference
[] getServiceRefSynchronous(String clss
,
223 String filter
) throws InvalidSyntaxException
{
224 if (log
.isTraceEnabled())
225 log
.debug("Filter: '" + filter
+ "'");
226 ServiceReference
[] sfs
= null;
227 boolean waiting
= true;
228 long begin
= System
.currentTimeMillis();
230 sfs
= bundleContext
.getServiceReferences(clss
, filter
);
236 checkTimeout(begin
, "Search of services " + clss
+ " with filter "
237 + filter
+ " timed out.");
243 protected void checkTimeout(long begin
, String msg
) {
244 long now
= System
.currentTimeMillis();
245 if (now
- begin
> defaultTimeout
)
246 throw new SlcException(msg
+ " (timeout after " + (now
- begin
)
251 protected void sleepWhenPolling() {
253 Thread
.sleep(pollingPeriod
);
254 } catch (InterruptedException e
) {
259 /** Creates and open a new service tracker. */
260 public ServiceTracker
newTracker(Class
<?
> clss
) {
261 ServiceTracker st
= new ServiceTracker(bundleContext
, clss
.getName(),
267 @SuppressWarnings(value
= { "unchecked" })
268 public <T
> T
getSingleService(Class
<T
> clss
, String filter
) {
269 Assert
.isTrue(OsgiFilterUtils
.isValidFilter(filter
), "valid filter");
270 ServiceReference
[] sfs
;
272 sfs
= bundleContext
.getServiceReferences(clss
.getName(), filter
);
273 } catch (InvalidSyntaxException e
) {
274 throw new SlcException("Cannot retrieve service reference for "
278 if (sfs
== null || sfs
.length
== 0)
280 else if (sfs
.length
> 1)
281 throw new SlcException("More than one execution flow found for "
283 return (T
) bundleContext
.getService(sfs
[0]);
286 public <T
> T
getSingleServiceStrict(Class
<T
> clss
, String filter
) {
287 T service
= getSingleService(clss
, filter
);
289 throw new SlcException("No execution flow found for " + filter
);
294 /** @return the related bundle or null if not found */
295 public Bundle
findRelatedBundle(OsgiBundle osgiBundle
) {
296 Bundle bundle
= null;
297 if (osgiBundle
.getInternalBundleId() != null) {
298 bundle
= bundleContext
.getBundle(osgiBundle
.getInternalBundleId());
300 osgiBundle
.getName().equals(bundle
.getSymbolicName()),
301 "symbolic name consistent");
302 if (osgiBundle
.getVersion() != null)
303 Assert
.isTrue(osgiBundle
.getVersion().equals(
304 bundle
.getHeaders().get(Constants
.BUNDLE_VERSION
)),
305 "version consistent");
306 } else if (osgiBundle
.getVersion() == null) {
307 bundle
= OsgiBundleUtils
.findBundleBySymbolicName(bundleContext
,
308 osgiBundle
.getName());
309 } else {// scan all bundles
310 bundles
: for (Bundle b
: bundleContext
.getBundles()) {
311 if (b
.getSymbolicName() == null) {
312 log
.warn("Bundle " + b
+ " has no symbolic name defined.");
316 if (b
.getSymbolicName().equals(osgiBundle
.getName())) {
317 if (osgiBundle
.getVersion() == null) {
322 if (b
.getHeaders().get(Constants
.BUNDLE_VERSION
).equals(
323 osgiBundle
.getVersion())) {
325 osgiBundle
.setInternalBundleId(b
.getBundleId());
334 /** Find a single bundle based on a symbolic name pattern. */
335 public OsgiBundle
findFromPattern(String pattern
) {
336 OsgiBundle osgiBundle
= null;
337 for (Bundle b
: bundleContext
.getBundles()) {
338 if (b
.getSymbolicName().contains(pattern
)) {
339 osgiBundle
= new OsgiBundle(b
);
346 public OsgiBundle
getBundle(Long bundleId
) {
347 Bundle bundle
= bundleContext
.getBundle(bundleId
);
348 return new OsgiBundle(bundle
);
351 public void setBundleContext(BundleContext bundleContext
) {
352 this.bundleContext
= bundleContext
;
355 public void afterPropertiesSet() throws Exception
{
356 bundleContext
.addFrameworkListener(this);
359 public void destroy() throws Exception
{
360 bundleContext
.removeFrameworkListener(this);
363 public void setDefaultTimeout(Long defaultTimeout
) {
364 this.defaultTimeout
= defaultTimeout
;
367 /** Temporary internal access for {@link OsgiExecutionModulesManager} */
368 BundleContext
getBundleContext() {
369 return bundleContext
;
372 public void setPollingPeriod(Long pollingPeriod
) {
373 this.pollingPeriod
= pollingPeriod
;
376 public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event
) {
377 if (event
instanceof OsgiBundleContextRefreshedEvent
) {
378 log
.debug("App context refreshed: " + event
);
379 } else if (event
instanceof OsgiBundleContextFailedEvent
) {
380 log
.debug("App context failed: " + event
);
382 if (event
instanceof OsgiBundleContextClosedEvent
) {
383 log
.debug("App context closed: " + event
);