]> 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 logging and monitoring
[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.OsgiFilterUtils;
26 import org.springframework.util.Assert;
27
28 /** Wraps low-level access to a {@link BundleContext} */
29 public class BundlesManager implements BundleContextAware, FrameworkListener,
30 InitializingBean, DisposableBean, OsgiBundleApplicationContextListener {
31 private final static Log log = LogFactory.getLog(BundlesManager.class);
32
33 private BundleContext bundleContext;
34
35 private Long defaultTimeout = 10000l;
36 private Long pollingPeriod = 100l;
37
38 // Refresh sync objects
39 private final Object refreshedPackageSem = new Object();
40 private Boolean packagesRefreshed = false;
41
42 /**
43 * Stop the module, update it, refresh it and restart it. All synchronously.
44 */
45 public void upgradeSynchronous(OsgiBundle osgiBundle) {
46 try {
47 Bundle bundle = findRelatedBundle(osgiBundle);
48
49 long begin = System.currentTimeMillis();
50
51 long bStop = begin;
52 stopSynchronous(bundle);
53
54 long bUpdate = System.currentTimeMillis();
55 updateSynchronous(bundle);
56
57 // Refresh in case there are fragments
58 long bRefresh = System.currentTimeMillis();
59 refreshSynchronous(bundle);
60
61 long bStart = System.currentTimeMillis();
62 startSynchronous(bundle);
63
64 long aStart = System.currentTimeMillis();
65 if (log.isDebugEnabled()) {
66 log.debug("OSGi upgrade performed in " + (aStart - begin)
67 + "ms for bundle " + osgiBundle);
68 log.debug(" stop \t: " + (bUpdate - bStop) + "ms");
69 log.debug(" update\t: " + (bRefresh - bUpdate) + "ms");
70 log.debug(" refresh\t: " + (bStart - bRefresh) + "ms");
71 log.debug(" start\t: " + (aStart - bStart) + "ms");
72 log.debug(" TOTAL\t: " + (aStart - begin) + "ms");
73 }
74
75 long bAppContext = System.currentTimeMillis();
76 String filter = "(Bundle-SymbolicName=" + bundle.getSymbolicName()
77 + ")";
78 // Wait for application context to be ready
79 // TODO: use service tracker
80 ServiceReference[] srs = getServiceRefSynchronous(
81 ApplicationContext.class.getName(), filter);
82 ServiceReference sr = srs[0];
83 long aAppContext = System.currentTimeMillis();
84 long end = aAppContext;
85
86 if (log.isDebugEnabled()) {
87 log.debug("Application context refresh performed in "
88 + (aAppContext - bAppContext) + "ms for bundle "
89 + osgiBundle);
90 log.debug(" TOTAL\t: " + (aAppContext - bAppContext) + "ms");
91 }
92
93 if (log.isDebugEnabled()) {
94 log.debug("Bundle " + bundle.getSymbolicName()
95 + " ready to be used at latest version."
96 + " (upgrade performed in " + (end - begin) + "ms).");
97 log.debug(" TOTAL\t: " + (end - begin) + "ms");
98
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 Assert.isTrue(osgiBundle.getVersion().equals(
303 bundle.getHeaders().get(Constants.BUNDLE_VERSION)),
304 "version consistent");
305 } else {
306 for (Bundle b : bundleContext.getBundles()) {
307 if (b.getSymbolicName().equals(osgiBundle.getName())) {
308 if (b.getHeaders().get(Constants.BUNDLE_VERSION).equals(
309 osgiBundle.getVersion())) {
310 bundle = b;
311 osgiBundle.setInternalBundleId(b.getBundleId());
312 }
313 }
314 }
315 }
316 return bundle;
317 }
318
319 /** Find a single bundle based on a symbolic name pattern. */
320 public OsgiBundle findFromPattern(String pattern) {
321 OsgiBundle osgiBundle = null;
322 for (Bundle b : bundleContext.getBundles()) {
323 if (b.getSymbolicName().contains(pattern)) {
324 osgiBundle = new OsgiBundle(b);
325 break;
326 }
327 }
328 return osgiBundle;
329 }
330
331 public OsgiBundle getBundle(Long bundleId) {
332 Bundle bundle = bundleContext.getBundle(bundleId);
333 return new OsgiBundle(bundle);
334 }
335
336 public void setBundleContext(BundleContext bundleContext) {
337 this.bundleContext = bundleContext;
338 }
339
340 public void afterPropertiesSet() throws Exception {
341 bundleContext.addFrameworkListener(this);
342 }
343
344 public void destroy() throws Exception {
345 bundleContext.removeFrameworkListener(this);
346 }
347
348 public void setDefaultTimeout(Long defaultTimeout) {
349 this.defaultTimeout = defaultTimeout;
350 }
351
352 /** Temporary internal access for {@link OsgiExecutionModulesManager} */
353 BundleContext getBundleContext() {
354 return bundleContext;
355 }
356
357 public void setPollingPeriod(Long pollingPeriod) {
358 this.pollingPeriod = pollingPeriod;
359 }
360
361 public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) {
362 if (event instanceof OsgiBundleContextRefreshedEvent) {
363 log.debug("App context refreshed: " + event);
364 } else if (event instanceof OsgiBundleContextFailedEvent) {
365 log.debug("App context failed: " + event);
366 }
367 if (event instanceof OsgiBundleContextClosedEvent) {
368 log.debug("App context closed: " + event);
369 }
370
371 }
372
373 }