]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.support.osgi/src/main/java/org/argeo/slc/osgi/BundlesManager.java
8872844545d54e650f13fdcb2c0adbccc0aa8a4b
[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.util.OsgiFilterUtils;
21 import org.springframework.util.Assert;
22
23 /** Wraps low-level access to a {@link BundleContext} */
24 public class BundlesManager implements BundleContextAware, FrameworkListener,
25 InitializingBean, DisposableBean {
26 private final static Log log = LogFactory.getLog(BundlesManager.class);
27
28 private BundleContext bundleContext;
29
30 private Long defaultTimeout = 10000l;
31 private Long pollingPeriod = 100l;
32
33 // Refresh sync objects
34 private final Object refreshedPackageSem = new Object();
35 private Boolean packagesRefreshed = false;
36
37 /**
38 * Stop the module, update it, refresh it and restart it. All synchronously.
39 */
40 public void upgradeSynchronous(OsgiBundle osgiBundle) {
41 try {
42 Bundle bundle = findRelatedBundle(osgiBundle);
43
44 long begin = System.currentTimeMillis();
45
46 long bStop = begin;
47 stopSynchronous(bundle);
48
49 long bUpdate = System.currentTimeMillis();
50 updateSynchronous(bundle);
51
52 // Refresh in case there are fragments
53 long bRefresh = System.currentTimeMillis();
54 refreshSynchronous(bundle);
55
56 long bStart = System.currentTimeMillis();
57 startSynchronous(bundle);
58
59 long aStart = System.currentTimeMillis();
60 if (log.isDebugEnabled()) {
61 log.debug("OSGi upgrade performed in " + (aStart - begin)
62 + "ms for bundle " + osgiBundle);
63 log.debug(" stop \t: " + (bUpdate - bStop) + "ms");
64 log.debug(" update\t: " + (bRefresh - bUpdate) + "ms");
65 log.debug(" refresh\t: " + (bStart - bRefresh) + "ms");
66 log.debug(" start\t: " + (aStart - bStart) + "ms");
67 log.debug(" TOTAL\t: " + (aStart - begin) + "ms");
68 }
69
70 long bAppContext = System.currentTimeMillis();
71 String filter = "(Bundle-SymbolicName=" + bundle.getSymbolicName()
72 + ")";
73 // Wait for application context to be ready
74 // TODO: use service tracker
75 getServiceRefSynchronous(ApplicationContext.class.getName(), filter);
76 long aAppContext = System.currentTimeMillis();
77 long end = aAppContext;
78
79 if (log.isDebugEnabled()) {
80 log.debug("Application context refresh performed in "
81 + (aAppContext - bAppContext) + "ms for bundle "
82 + osgiBundle);
83 log.debug(" TOTAL\t: " + (aAppContext - bAppContext) + "ms");
84 }
85
86 if (log.isDebugEnabled())
87 log.debug("Bundle " + bundle.getSymbolicName()
88 + " ready to be used at latest version."
89 + " (upgrade performed in " + (end - begin) + "ms).");
90 log.debug(" TOTAL\t: " + (end - begin) + "ms");
91 } catch (Exception e) {
92 throw new SlcException("Cannot update bundle " + osgiBundle, e);
93 }
94 }
95
96 /** Updates bundle synchronously. */
97 protected void updateSynchronous(Bundle bundle) throws BundleException {
98 bundle.update();
99 boolean waiting = true;
100
101 long begin = System.currentTimeMillis();
102 do {
103 int state = bundle.getState();
104 if (state == Bundle.INSTALLED || state == Bundle.ACTIVE
105 || state == Bundle.RESOLVED)
106 waiting = false;
107
108 sleepWhenPolling();
109 checkTimeout(begin, "Update of bundle " + bundle.getSymbolicName()
110 + " timed out. Bundle state = " + bundle.getState());
111 } while (waiting);
112
113 if (log.isTraceEnabled())
114 log.debug("Bundle " + bundle.getSymbolicName() + " updated.");
115 }
116
117 /** Starts bundle synchronously. Does nothing if already started. */
118 protected void startSynchronous(Bundle bundle) throws BundleException {
119 int originalState = bundle.getState();
120 if (originalState == Bundle.ACTIVE)
121 return;
122
123 bundle.start();
124 boolean waiting = true;
125
126 long begin = System.currentTimeMillis();
127 do {
128 if (bundle.getState() == Bundle.ACTIVE)
129 waiting = false;
130
131 sleepWhenPolling();
132 checkTimeout(begin, "Start of bundle " + bundle.getSymbolicName()
133 + " timed out. Bundle state = " + bundle.getState());
134 } while (waiting);
135
136 if (log.isTraceEnabled())
137 log.debug("Bundle " + bundle.getSymbolicName() + " started.");
138 }
139
140 /** Stops bundle synchronously. Does nothing if already started. */
141 protected void stopSynchronous(Bundle bundle) throws BundleException {
142 int originalState = bundle.getState();
143 if (originalState != Bundle.ACTIVE)
144 return;
145
146 bundle.stop();
147 boolean waiting = true;
148
149 long begin = System.currentTimeMillis();
150 do {
151 if (bundle.getState() != Bundle.ACTIVE
152 && bundle.getState() != Bundle.STOPPING)
153 waiting = false;
154
155 sleepWhenPolling();
156 checkTimeout(begin, "Stop of bundle " + bundle.getSymbolicName()
157 + " timed out. Bundle state = " + bundle.getState());
158 } while (waiting);
159
160 if (log.isTraceEnabled())
161 log.debug("Bundle " + bundle.getSymbolicName() + " stopped.");
162 }
163
164 /** Refresh bundle synchronously. Does nothing if already started. */
165 protected void refreshSynchronous(Bundle bundle) throws BundleException {
166 ServiceReference packageAdminRef = bundleContext
167 .getServiceReference(PackageAdmin.class.getName());
168 PackageAdmin packageAdmin = (PackageAdmin) bundleContext
169 .getService(packageAdminRef);
170 Bundle[] bundles = { bundle };
171
172 long begin = System.currentTimeMillis();
173 synchronized (refreshedPackageSem) {
174 packagesRefreshed = false;
175 packageAdmin.refreshPackages(bundles);
176 try {
177 refreshedPackageSem.wait(defaultTimeout);
178 } catch (InterruptedException e) {
179 // silent
180 }
181 if (!packagesRefreshed) {
182 long now = System.currentTimeMillis();
183 throw new SlcException("Packages not refreshed after "
184 + (now - begin) + "ms");
185 } else {
186 packagesRefreshed = false;
187 }
188 }
189
190 if (log.isTraceEnabled())
191 log.debug("Bundle " + bundle.getSymbolicName() + " refreshed.");
192 }
193
194 public void frameworkEvent(FrameworkEvent event) {
195 if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
196 synchronized (refreshedPackageSem) {
197 packagesRefreshed = true;
198 refreshedPackageSem.notifyAll();
199 }
200 }
201 }
202
203 public ServiceReference[] getServiceRefSynchronous(String clss,
204 String filter) throws InvalidSyntaxException {
205 if (log.isTraceEnabled())
206 log.debug("Filter: '" + filter + "'");
207 ServiceReference[] sfs = null;
208 boolean waiting = true;
209 long begin = System.currentTimeMillis();
210 do {
211 sfs = bundleContext.getServiceReferences(clss, filter);
212
213 if (sfs != null)
214 waiting = false;
215
216 sleepWhenPolling();
217 if (System.currentTimeMillis() - begin > defaultTimeout)
218 throw new SlcException("Search of services " + clss
219 + " with filter " + filter + " timed out.");
220 } while (waiting);
221
222 return sfs;
223 }
224
225 protected void checkTimeout(long begin, String msg) {
226 long now = System.currentTimeMillis();
227 if (now - begin > defaultTimeout)
228 throw new SlcException(msg + " (timeout after " + (now - begin)
229 + "ms)");
230
231 }
232
233 protected void sleepWhenPolling() {
234 try {
235 Thread.sleep(pollingPeriod);
236 } catch (InterruptedException e) {
237 // silent
238 }
239 }
240
241 /** Creates and open a new service tracker. */
242 public ServiceTracker newTracker(Class<?> clss) {
243 ServiceTracker st = new ServiceTracker(bundleContext, clss.getName(),
244 null);
245 st.open();
246 return st;
247 }
248
249 @SuppressWarnings(value = { "unchecked" })
250 public <T> T getSingleService(Class<T> clss, String filter) {
251 Assert.isTrue(OsgiFilterUtils.isValidFilter(filter), "valid filter");
252 ServiceReference[] sfs;
253 try {
254 sfs = bundleContext.getServiceReferences(clss.getName(), filter);
255 } catch (InvalidSyntaxException e) {
256 throw new SlcException("Cannot retrieve service reference for "
257 + filter, e);
258 }
259
260 if (sfs == null || sfs.length == 0)
261 return null;
262 else if (sfs.length > 1)
263 throw new SlcException("More than one execution flow found for "
264 + filter);
265 return (T) bundleContext.getService(sfs[0]);
266 }
267
268 public <T> T getSingleServiceStrict(Class<T> clss, String filter) {
269 T service = getSingleService(clss, filter);
270 if (service == null)
271 throw new SlcException("No execution flow found for " + filter);
272 else
273 return service;
274 }
275
276 /** @return the related bundle or null if not found */
277 public Bundle findRelatedBundle(OsgiBundle osgiBundle) {
278 Bundle bundle = null;
279 if (osgiBundle.getInternalBundleId() != null) {
280 bundle = bundleContext.getBundle(osgiBundle.getInternalBundleId());
281 Assert.isTrue(
282 osgiBundle.getName().equals(bundle.getSymbolicName()),
283 "symbolic name consistent");
284 Assert.isTrue(osgiBundle.getVersion().equals(
285 bundle.getHeaders().get(Constants.BUNDLE_VERSION)),
286 "version consistent");
287 } else {
288 for (Bundle b : bundleContext.getBundles()) {
289 if (b.getSymbolicName().equals(osgiBundle.getName())) {
290 if (b.getHeaders().get(Constants.BUNDLE_VERSION).equals(
291 osgiBundle.getVersion())) {
292 bundle = b;
293 osgiBundle.setInternalBundleId(b.getBundleId());
294 }
295 }
296 }
297 }
298 return bundle;
299 }
300
301 /** Find a single bundle based on a symbolic name pattern. */
302 public OsgiBundle findFromPattern(String pattern) {
303 OsgiBundle osgiBundle = null;
304 for (Bundle b : bundleContext.getBundles()) {
305 if (b.getSymbolicName().contains(pattern)) {
306 osgiBundle = new OsgiBundle(b);
307 break;
308 }
309 }
310 return osgiBundle;
311 }
312
313 public OsgiBundle getBundle(Long bundleId) {
314 Bundle bundle = bundleContext.getBundle(bundleId);
315 return new OsgiBundle(bundle);
316 }
317
318 public void setBundleContext(BundleContext bundleContext) {
319 this.bundleContext = bundleContext;
320 }
321
322 public void afterPropertiesSet() throws Exception {
323 bundleContext.addFrameworkListener(this);
324 }
325
326 public void destroy() throws Exception {
327 bundleContext.removeFrameworkListener(this);
328 }
329
330 public void setDefaultTimeout(Long defaultTimeout) {
331 this.defaultTimeout = defaultTimeout;
332 }
333
334 /** Temporary internal access for {@link OsgiExecutionModulesManager} */
335 BundleContext getBundleContext() {
336 return bundleContext;
337 }
338
339 public void setPollingPeriod(Long pollingPeriod) {
340 this.pollingPeriod = pollingPeriod;
341 }
342
343 }