]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.support.osgi/src/main/java/org/argeo/slc/osgi/BundlesManager.java
Improve executions and system calls
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.osgi / src / main / java / org / argeo / slc / osgi / BundlesManager.java
1 package org.argeo.slc.osgi;
2
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;
28
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);
33
34 private BundleContext bundleContext;
35
36 private Long defaultTimeout = 10000l;
37 private Long pollingPeriod = 100l;
38
39 // Refresh sync objects
40 private final Object refreshedPackageSem = new Object();
41 private Boolean packagesRefreshed = false;
42
43 /**
44 * Stop the module, update it, refresh it and restart it. All synchronously.
45 */
46 public void upgradeSynchronous(OsgiBundle osgiBundle) {
47 try {
48 Bundle bundle = findRelatedBundle(osgiBundle);
49
50 long begin = System.currentTimeMillis();
51
52 long bStop = begin;
53 stopSynchronous(bundle);
54
55 long bUpdate = System.currentTimeMillis();
56 updateSynchronous(bundle);
57
58 // Refresh in case there are fragments
59 long bRefresh = System.currentTimeMillis();
60 refreshSynchronous(bundle);
61
62 long bStart = System.currentTimeMillis();
63 startSynchronous(bundle);
64
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");
74 }
75
76 long bAppContext = System.currentTimeMillis();
77 String filter = "(Bundle-SymbolicName=" + bundle.getSymbolicName()
78 + ")";
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;
86
87 if (log.isTraceEnabled()) {
88 log.debug("Application context refresh performed in "
89 + (aAppContext - bAppContext) + "ms for bundle "
90 + osgiBundle);
91 }
92
93 if (log.isDebugEnabled())
94 log.debug("Bundle '" + bundle.getSymbolicName()
95 + "' upgraded and ready " + " (upgrade performed in "
96 + (end - begin) + "ms).");
97
98 if (log.isTraceEnabled()) {
99 ApplicationContext applicationContext = (ApplicationContext) bundleContext
100 .getService(sr);
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");
106 }
107
108 bundleContext.ungetService(sr);
109
110 } catch (Exception e) {
111 throw new SlcException("Cannot update bundle " + osgiBundle, e);
112 }
113 }
114
115 /** Updates bundle synchronously. */
116 protected void updateSynchronous(Bundle bundle) throws BundleException {
117 bundle.update();
118 boolean waiting = true;
119
120 long begin = System.currentTimeMillis();
121 do {
122 int state = bundle.getState();
123 if (state == Bundle.INSTALLED || state == Bundle.ACTIVE
124 || state == Bundle.RESOLVED)
125 waiting = false;
126
127 sleepWhenPolling();
128 checkTimeout(begin, "Update of bundle " + bundle.getSymbolicName()
129 + " timed out. Bundle state = " + bundle.getState());
130 } while (waiting);
131
132 if (log.isTraceEnabled())
133 log.debug("Bundle " + bundle.getSymbolicName() + " updated.");
134 }
135
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)
140 return;
141
142 bundle.start();
143 boolean waiting = true;
144
145 long begin = System.currentTimeMillis();
146 do {
147 if (bundle.getState() == Bundle.ACTIVE)
148 waiting = false;
149
150 sleepWhenPolling();
151 checkTimeout(begin, "Start of bundle " + bundle.getSymbolicName()
152 + " timed out. Bundle state = " + bundle.getState());
153 } while (waiting);
154
155 if (log.isTraceEnabled())
156 log.debug("Bundle " + bundle.getSymbolicName() + " started.");
157 }
158
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)
163 return;
164
165 bundle.stop();
166 boolean waiting = true;
167
168 long begin = System.currentTimeMillis();
169 do {
170 if (bundle.getState() != Bundle.ACTIVE
171 && bundle.getState() != Bundle.STOPPING)
172 waiting = false;
173
174 sleepWhenPolling();
175 checkTimeout(begin, "Stop of bundle " + bundle.getSymbolicName()
176 + " timed out. Bundle state = " + bundle.getState());
177 } while (waiting);
178
179 if (log.isTraceEnabled())
180 log.debug("Bundle " + bundle.getSymbolicName() + " stopped.");
181 }
182
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 };
190
191 long begin = System.currentTimeMillis();
192 synchronized (refreshedPackageSem) {
193 packagesRefreshed = false;
194 packageAdmin.refreshPackages(bundles);
195 try {
196 refreshedPackageSem.wait(defaultTimeout);
197 } catch (InterruptedException e) {
198 // silent
199 }
200 if (!packagesRefreshed) {
201 long now = System.currentTimeMillis();
202 throw new SlcException("Packages not refreshed after "
203 + (now - begin) + "ms");
204 } else {
205 packagesRefreshed = false;
206 }
207 }
208
209 if (log.isTraceEnabled())
210 log.debug("Bundle " + bundle.getSymbolicName() + " refreshed.");
211 }
212
213 public void frameworkEvent(FrameworkEvent event) {
214 if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
215 synchronized (refreshedPackageSem) {
216 packagesRefreshed = true;
217 refreshedPackageSem.notifyAll();
218 }
219 }
220 }
221
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();
229 do {
230 sfs = bundleContext.getServiceReferences(clss, filter);
231
232 if (sfs != null)
233 waiting = false;
234
235 sleepWhenPolling();
236 checkTimeout(begin, "Search of services " + clss + " with filter "
237 + filter + " timed out.");
238 } while (waiting);
239
240 return sfs;
241 }
242
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)
247 + "ms)");
248
249 }
250
251 protected void sleepWhenPolling() {
252 try {
253 Thread.sleep(pollingPeriod);
254 } catch (InterruptedException e) {
255 // silent
256 }
257 }
258
259 /** Creates and open a new service tracker. */
260 public ServiceTracker newTracker(Class<?> clss) {
261 ServiceTracker st = new ServiceTracker(bundleContext, clss.getName(),
262 null);
263 st.open();
264 return st;
265 }
266
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;
271 try {
272 sfs = bundleContext.getServiceReferences(clss.getName(), filter);
273 } catch (InvalidSyntaxException e) {
274 throw new SlcException("Cannot retrieve service reference for "
275 + filter, e);
276 }
277
278 if (sfs == null || sfs.length == 0)
279 return null;
280 else if (sfs.length > 1)
281 throw new SlcException("More than one execution flow found for "
282 + filter);
283 return (T) bundleContext.getService(sfs[0]);
284 }
285
286 public <T> T getSingleServiceStrict(Class<T> clss, String filter) {
287 T service = getSingleService(clss, filter);
288 if (service == null)
289 throw new SlcException("No execution flow found for " + filter);
290 else
291 return service;
292 }
293
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());
299 Assert.isTrue(
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.");
313 continue bundles;
314 }
315
316 if (b.getSymbolicName().equals(osgiBundle.getName())) {
317 if (osgiBundle.getVersion() == null) {
318 bundle = b;
319 break bundles;
320 }
321
322 if (b.getHeaders().get(Constants.BUNDLE_VERSION).equals(
323 osgiBundle.getVersion())) {
324 bundle = b;
325 osgiBundle.setInternalBundleId(b.getBundleId());
326 break bundles;
327 }
328 }
329 }
330 }
331 return bundle;
332 }
333
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);
340 break;
341 }
342 }
343 return osgiBundle;
344 }
345
346 public OsgiBundle getBundle(Long bundleId) {
347 Bundle bundle = bundleContext.getBundle(bundleId);
348 return new OsgiBundle(bundle);
349 }
350
351 public void setBundleContext(BundleContext bundleContext) {
352 this.bundleContext = bundleContext;
353 }
354
355 public void afterPropertiesSet() throws Exception {
356 bundleContext.addFrameworkListener(this);
357 }
358
359 public void destroy() throws Exception {
360 bundleContext.removeFrameworkListener(this);
361 }
362
363 public void setDefaultTimeout(Long defaultTimeout) {
364 this.defaultTimeout = defaultTimeout;
365 }
366
367 /** Temporary internal access for {@link OsgiExecutionModulesManager} */
368 BundleContext getBundleContext() {
369 return bundleContext;
370 }
371
372 public void setPollingPeriod(Long pollingPeriod) {
373 this.pollingPeriod = pollingPeriod;
374 }
375
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);
381 }
382 if (event instanceof OsgiBundleContextClosedEvent) {
383 log.debug("App context closed: " + event);
384 }
385
386 }
387
388 }