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