2 * Copyright (C) 2007-2012 Argeo GmbH
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.slc
.osgi
;
18 import java
.util
.Collection
;
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
;
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);
53 private BundleContext bundleContext
;
55 private Long defaultTimeout
= 60 * 1000l;
56 private Long pollingPeriod
= 200l;
58 // Refresh sync objects
59 private final Object refreshedPackageSem
= new Object();
60 private Boolean packagesRefreshed
= false;
62 public BundlesManager() {
65 public BundlesManager(BundleContext bundleContext
) {
66 this.bundleContext
= bundleContext
;
70 * Stop the module, update it, refresh it and restart it. All synchronously.
72 public void upgradeSynchronous(OsgiBundle osgiBundle
) {
74 Bundle bundle
= findRelatedBundle(osgiBundle
);
76 long begin
= System
.currentTimeMillis();
79 stopSynchronous(bundle
);
81 long bUpdate
= System
.currentTimeMillis();
82 updateSynchronous(bundle
);
84 // Refresh in case there are fragments
85 long bRefresh
= System
.currentTimeMillis();
86 refreshSynchronous(bundle
);
88 long bStart
= System
.currentTimeMillis();
89 startSynchronous(bundle
);
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");
102 long bAppContext
= System
.currentTimeMillis();
103 String filter
= "(Bundle-SymbolicName=" + bundle
.getSymbolicName()
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
;
113 if (log
.isTraceEnabled()) {
114 log
.debug("Application context refresh performed in "
115 + (aAppContext
- bAppContext
) + "ms for bundle "
119 if (log
.isDebugEnabled())
120 log
.debug("Bundle '" + bundle
.getSymbolicName()
121 + "' upgraded and ready " + " (upgrade performed in "
122 + (end
- begin
) + "ms).");
124 if (log
.isTraceEnabled()) {
125 ApplicationContext applicationContext
= (ApplicationContext
) bundleContext
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");
134 bundleContext
.ungetService(sr
);
136 } catch (Exception e
) {
137 throw new SlcException("Cannot update bundle " + osgiBundle
, e
);
141 /** Updates bundle synchronously. */
142 protected void updateSynchronous(Bundle bundle
) throws BundleException
{
144 boolean waiting
= true;
146 long begin
= System
.currentTimeMillis();
148 int state
= bundle
.getState();
149 if (state
== Bundle
.INSTALLED
|| state
== Bundle
.ACTIVE
150 || state
== Bundle
.RESOLVED
)
154 checkTimeout(begin
, "Update of bundle " + bundle
.getSymbolicName()
155 + " timed out. Bundle state = " + bundle
.getState());
158 if (log
.isTraceEnabled())
159 log
.debug("Bundle " + bundle
.getSymbolicName() + " updated.");
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
)
169 boolean waiting
= true;
171 long begin
= System
.currentTimeMillis();
173 if (bundle
.getState() == Bundle
.ACTIVE
)
177 checkTimeout(begin
, "Start of bundle " + bundle
.getSymbolicName()
178 + " timed out. Bundle state = " + bundle
.getState());
181 if (log
.isTraceEnabled())
182 log
.debug("Bundle " + bundle
.getSymbolicName() + " started.");
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
)
192 boolean waiting
= true;
194 long begin
= System
.currentTimeMillis();
196 if (bundle
.getState() != Bundle
.ACTIVE
197 && bundle
.getState() != Bundle
.STOPPING
)
201 checkTimeout(begin
, "Stop of bundle " + bundle
.getSymbolicName()
202 + " timed out. Bundle state = " + bundle
.getState());
205 if (log
.isTraceEnabled())
206 log
.debug("Bundle " + bundle
.getSymbolicName() + " stopped.");
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
};
217 long begin
= System
.currentTimeMillis();
218 synchronized (refreshedPackageSem
) {
219 packagesRefreshed
= false;
220 packageAdmin
.refreshPackages(bundles
);
222 refreshedPackageSem
.wait(defaultTimeout
);
223 } catch (InterruptedException e
) {
226 if (!packagesRefreshed
) {
227 long now
= System
.currentTimeMillis();
228 throw new SlcException("Packages not refreshed after "
229 + (now
- begin
) + "ms");
231 packagesRefreshed
= false;
235 if (log
.isTraceEnabled())
236 log
.debug("Bundle " + bundle
.getSymbolicName() + " refreshed.");
239 public void frameworkEvent(FrameworkEvent event
) {
240 if (event
.getType() == FrameworkEvent
.PACKAGES_REFRESHED
) {
241 synchronized (refreshedPackageSem
) {
242 packagesRefreshed
= true;
243 refreshedPackageSem
.notifyAll();
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();
256 sfs
= bundleContext
.getServiceReferences(clss
, filter
);
262 checkTimeout(begin
, "Search of services " + clss
+ " with filter "
263 + filter
+ " timed out.");
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
)
277 protected void sleepWhenPolling() {
279 Thread
.sleep(pollingPeriod
);
280 } catch (InterruptedException e
) {
281 throw new SlcException("Polling interrupted");
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
,
293 public <T
> T
getSingleService(Class
<T
> clss
, String filter
,
294 Boolean synchronous
) {
296 Assert
.isTrue(OsgiFilterUtils
.isValidFilter(filter
), "valid filter");
297 Collection
<ServiceReference
<T
>> sfs
;
300 sfs
= getServiceRefSynchronous(clss
, filter
);
302 sfs
= bundleContext
.getServiceReferences(clss
, filter
);
303 } catch (InvalidSyntaxException e
) {
304 throw new SlcException("Cannot retrieve service reference for "
308 if (sfs
== null || sfs
.size() == 0)
310 else if (sfs
.size() > 1)
311 throw new SlcException("More than one execution flow found for "
313 return (T
) bundleContext
.getService(sfs
.iterator().next());
316 public <T
> T
getSingleServiceStrict(Class
<T
> clss
, String filter
,
317 Boolean synchronous
) {
318 T service
= getSingleService(clss
, filter
, synchronous
);
320 throw new SlcException("No execution flow found for " + filter
);
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
);
337 * @return the related bundle or null if not found
338 * @throws SlcException
339 * if osgiBundle argument is null
341 public Bundle
findRelatedBundle(OsgiBundle osgiBundle
) {
342 if (osgiBundle
== null)
343 throw new SlcException("OSGi bundle cannot be null");
345 Bundle bundle
= null;
346 if (osgiBundle
.getInternalBundleId() != null) {
347 bundle
= bundleContext
.getBundle(osgiBundle
.getInternalBundleId());
349 osgiBundle
.getName().equals(bundle
.getSymbolicName()),
350 "symbolic name consistent");
351 if (osgiBundle
.getVersion() != null)
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.");
368 if (b
.getSymbolicName().equals(osgiBundle
.getName())) {
369 if (osgiBundle
.getVersion() == null) {
374 if (b
.getHeaders().get(Constants
.BUNDLE_VERSION
)
375 .equals(osgiBundle
.getVersion())) {
377 osgiBundle
.setInternalBundleId(b
.getBundleId());
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
);
398 public OsgiBundle
getBundle(Long bundleId
) {
399 Bundle bundle
= bundleContext
.getBundle(bundleId
);
400 return new OsgiBundle(bundle
);
403 public void setBundleContext(BundleContext bundleContext
) {
404 this.bundleContext
= bundleContext
;
407 public void afterPropertiesSet() throws Exception
{
408 bundleContext
.addFrameworkListener(this);
411 public void destroy() throws Exception
{
412 bundleContext
.removeFrameworkListener(this);
415 public void setDefaultTimeout(Long defaultTimeout
) {
416 this.defaultTimeout
= defaultTimeout
;
420 * Use with caution since it may interfer with some cached information
423 public BundleContext
getBundleContext() {
424 return bundleContext
;
427 public void setPollingPeriod(Long pollingPeriod
) {
428 this.pollingPeriod
= pollingPeriod
;
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
);
437 if (event
instanceof OsgiBundleContextClosedEvent
) {
438 log
.debug("App context closed: " + event
);