]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.support.osgi/src/main/java/org/argeo/slc/osgi/OsgiExecutionModulesManager.java
Improve Javadocs
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.osgi / src / main / java / org / argeo / slc / osgi / OsgiExecutionModulesManager.java
1 /*
2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 package org.argeo.slc.osgi;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Properties;
27 import java.util.Set;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.argeo.slc.SlcException;
32 import org.argeo.slc.build.BasicNameVersion;
33 import org.argeo.slc.build.NameVersion;
34 import org.argeo.slc.core.execution.AbstractExecutionModulesManager;
35 import org.argeo.slc.core.execution.DefaultExecutionFlowDescriptorConverter;
36 import org.argeo.slc.deploy.ModuleDescriptor;
37 import org.argeo.slc.execution.ExecutionContext;
38 import org.argeo.slc.execution.ExecutionFlow;
39 import org.argeo.slc.execution.ExecutionFlowDescriptor;
40 import org.argeo.slc.execution.ExecutionFlowDescriptorConverter;
41 import org.argeo.slc.execution.ExecutionModuleDescriptor;
42 import org.argeo.slc.execution.ExecutionModulesListener;
43 import org.argeo.slc.process.RealizedFlow;
44 import org.osgi.framework.Bundle;
45 import org.osgi.framework.Constants;
46 import org.osgi.framework.InvalidSyntaxException;
47 import org.osgi.framework.ServiceReference;
48 import org.osgi.util.tracker.ServiceTracker;
49 import org.springframework.beans.factory.DisposableBean;
50 import org.springframework.beans.factory.InitializingBean;
51 import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener;
52
53 public class OsgiExecutionModulesManager extends
54 AbstractExecutionModulesManager implements InitializingBean,
55 DisposableBean, OsgiServiceLifecycleListener {
56
57 // static {
58 // // Force usage of vanilla Xalan when in OSGi
59 // // We would like to do it in a cleaner way
60 // // but the integration of Xalan and Xerces in the JRE
61 // // makes it very difficult
62 // // Suggestions welcome!
63 // Properties systemProperties = System.getProperties();
64 // // if (!systemProperties
65 // // .containsKey("javax.xml.parsers.DocumentBuilderFactory"))
66 // // System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
67 // // "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
68 // // if
69 // // (!systemProperties.containsKey("javax.xml.parsers.SAXParserFactory"))
70 // // System.setProperty("javax.xml.parsers.SAXParserFactory",
71 // // "org.apache.xerces.jaxp.SAXParserFactoryImpl");
72 // if (!systemProperties
73 // .containsKey("javax.xml.transform.TransformerFactory"))
74 // System.setProperty("javax.xml.transform.TransformerFactory",
75 // "org.apache.xalan.processor.TransformerFactoryImpl");
76 // }
77
78 private final static String PROPERTY_CACHE_SERVICES = "slc.osgi.execution.cacheServices";
79
80 private final static Log log = LogFactory
81 .getLog(OsgiExecutionModulesManager.class);
82
83 private BundlesManager bundlesManager;
84 private ServiceTracker executionContextsTracker;
85 private Map<OsgiBundle, ExecutionContext> executionContexts = new HashMap<OsgiBundle, ExecutionContext>();
86 private Map<OsgiBundle, ExecutionFlowDescriptorConverter> executionFlowDescriptorConverters = new HashMap<OsgiBundle, ExecutionFlowDescriptorConverter>();
87 private Map<OsgiBundle, Set<ExecutionFlow>> executionFlows = new HashMap<OsgiBundle, Set<ExecutionFlow>>();
88 private ExecutionFlowDescriptorConverter defaultDescriptorConverter = new DefaultExecutionFlowDescriptorConverter();
89
90 private Boolean useCachedServices = Boolean.parseBoolean(System
91 .getProperty(PROPERTY_CACHE_SERVICES, "true"));
92
93 public synchronized ExecutionModuleDescriptor getExecutionModuleDescriptor(
94 String moduleName, String version) {
95 ExecutionModuleDescriptor md = new ExecutionModuleDescriptor();
96 if (useCachedServices) {
97 OsgiBundle osgiBundle = null;
98 BasicNameVersion nameVersion = new BasicNameVersion(moduleName,
99 version);
100 bundles: for (Iterator<OsgiBundle> iterator = executionContexts
101 .keySet().iterator(); iterator.hasNext();) {
102 OsgiBundle ob = iterator.next();
103 if (ob.equals(nameVersion)) {
104 osgiBundle = ob;
105 break bundles;
106 }
107 }
108 if (osgiBundle == null)
109 throw new SlcException("No execution module registered for "
110 + nameVersion);
111 md.setName(osgiBundle.getName());
112 md.setVersion(osgiBundle.getVersion());
113 md.setLabel(osgiBundle.getLabel());
114 md.setDescription(osgiBundle.getDescription());
115 } else {
116 md.setName(moduleName);
117 md.setVersion(version);
118 setMetadataFromBundle(md, null);
119 }
120 ExecutionFlowDescriptorConverter executionFlowDescriptorConverter = getExecutionFlowDescriptorConverter(
121 moduleName, version);
122 if (executionFlowDescriptorConverter == null)
123 throw new SlcException("No flow converter found.");
124 executionFlowDescriptorConverter.addFlowsToDescriptor(md, listFlows(
125 moduleName, version));
126 return md;
127 }
128
129 public synchronized List<ExecutionModuleDescriptor> listExecutionModules() {
130 List<ExecutionModuleDescriptor> descriptors = new ArrayList<ExecutionModuleDescriptor>();
131
132 if (useCachedServices) {
133 for (Iterator<OsgiBundle> iterator = executionContexts.keySet()
134 .iterator(); iterator.hasNext();) {
135 OsgiBundle osgiBundle = iterator.next();
136 ExecutionModuleDescriptor md = new ExecutionModuleDescriptor();
137 setMetadataFromBundle(md, bundlesManager
138 .findRelatedBundle(osgiBundle));
139 descriptors.add(md);
140 }
141 } else {
142 ServiceReference[] arr = executionContextsTracker
143 .getServiceReferences();
144 if (arr == null) {
145 log.error("Tracker returned null.");
146 return descriptors;
147 }
148
149 List<ServiceReference> srs = Arrays.asList(arr);
150 // ServiceReference[] srs =
151 // executionContexts.getServiceReferences();
152 for (ServiceReference sr : srs) {
153 ExecutionModuleDescriptor md = new ExecutionModuleDescriptor();
154 setMetadataFromBundle(md, sr.getBundle());
155 descriptors.add(md);
156 }
157 }
158 return descriptors;
159 }
160
161 protected synchronized Map<String, ExecutionFlow> listFlows(
162 String moduleName, String moduleVersion) {
163
164 Map<String, ExecutionFlow> flows = new HashMap<String, ExecutionFlow>();
165 if (useCachedServices) {
166 OsgiBundle key = new OsgiBundle(
167 moduleName, moduleVersion);
168 if(!executionFlows.containsKey(key))
169 return flows;
170 Set<ExecutionFlow> flowsT = executionFlows.get(key);
171 for (ExecutionFlow flow : flowsT)
172 flows.put(flow.getName(), flow);
173 } else {
174
175 // TODO: use service trackers?
176 // String filter = OsgiFilterUtils.unifyFilter(ExecutionFlow.class,
177 // null);
178
179 String filter = "(Bundle-SymbolicName=" + moduleName + ")";
180 ServiceReference[] sfs;
181 try {
182 sfs = bundlesManager.getBundleContext().getServiceReferences(
183 ExecutionFlow.class.getName(), filter);
184 } catch (InvalidSyntaxException e) {
185 throw new SlcException(
186 "Cannot retrieve service reference for flow " + filter,
187 e);
188 }
189
190 for (ServiceReference sf : sfs) {
191 ExecutionFlow flow = (ExecutionFlow) bundlesManager
192 .getBundleContext().getService(sf);
193 flows.put(flow.getName(), flow);
194 }
195 }
196 return flows;
197 }
198
199 protected ExecutionFlow findExecutionFlow(String moduleName,
200 String moduleVersion, String flowName) {
201 String filter = "(&(Bundle-SymbolicName=" + moduleName
202 + ")(org.springframework.osgi.bean.name=" + flowName + "))";
203 return bundlesManager.getSingleServiceStrict(ExecutionFlow.class,
204 filter);
205 }
206
207 protected ExecutionContext findExecutionContext(String moduleName,
208 String moduleVersion) {
209 String filter = "(&(Bundle-SymbolicName=" + moduleName
210 + ")(Bundle-Version=" + moduleVersion + "))";
211 return bundlesManager.getSingleServiceStrict(ExecutionContext.class,
212 filter);
213 }
214
215 protected ExecutionFlowDescriptorConverter findExecutionFlowDescriptorConverter(
216 String moduleName, String moduleVersion) {
217
218 String filter = "(&(Bundle-SymbolicName=" + moduleName
219 + ")(Bundle-Version=" + moduleVersion + "))";
220 return bundlesManager.getSingleService(
221 ExecutionFlowDescriptorConverter.class, filter);
222 }
223
224 public void setBundlesManager(BundlesManager bundlesManager) {
225 this.bundlesManager = bundlesManager;
226 }
227
228 public void afterPropertiesSet() throws Exception {
229 if (!useCachedServices)
230 executionContextsTracker = bundlesManager
231 .newTracker(ExecutionContext.class);
232 }
233
234 public void destroy() throws Exception {
235 if (executionContextsTracker != null)
236 executionContextsTracker.close();
237 }
238
239 /**
240 * Builds a minimal realized flow, based on the provided information
241 * (typically from the command line).
242 *
243 * @param module
244 * a bundle id, or a pattern contained in a bundle symbolic name
245 * @param module
246 * the execution flow name
247 * @return a minimal realized flow, to be used in an execution
248 */
249 public RealizedFlow findRealizedFlow(String module, String executionName) {
250 // First check whether we have a bundleId
251 Long bundleId = null;
252 try {
253 bundleId = Long.parseLong(module);
254 } catch (NumberFormatException e) {
255 // silent
256 }
257
258 // Look for bundle names containing pattern
259 OsgiBundle bundle = null;
260 if (bundleId != null) {
261 bundle = bundlesManager.getBundle(bundleId);
262 } else {
263 bundle = bundlesManager.findFromPattern(module);
264 }
265
266 if (bundle != null) {
267 RealizedFlow launch = new RealizedFlow();
268 launch.setModuleName(bundle.getName());
269 launch.setModuleVersion(bundle.getVersion());
270 ExecutionFlowDescriptor descriptor = new ExecutionFlowDescriptor();
271 descriptor.setName(executionName);
272 launch.setFlowDescriptor(descriptor);
273 return launch;
274 } else {
275 log
276 .warn("Could not find any execution module matching these requirements.");
277 return null;
278 }
279 }
280
281 public void upgrade(NameVersion nameVersion) {
282 OsgiBundle osgiBundle = new OsgiBundle(nameVersion);
283 bundlesManager.upgradeSynchronous(osgiBundle);
284 }
285
286 protected synchronized ExecutionFlowDescriptorConverter getExecutionFlowDescriptorConverter(
287 String moduleName, String moduleVersion) {
288 if (useCachedServices) {
289 OsgiBundle osgiBundle = new OsgiBundle(moduleName, moduleVersion);
290 if (executionFlowDescriptorConverters.containsKey(osgiBundle))
291 return executionFlowDescriptorConverters.get(osgiBundle);
292 else
293 return defaultDescriptorConverter;
294 } else {
295 // Check whether a descriptor converter is published by this module
296 ExecutionFlowDescriptorConverter descriptorConverter = findExecutionFlowDescriptorConverter(
297 moduleName, moduleVersion);
298 if (descriptorConverter == null)
299 return defaultDescriptorConverter;
300 else
301 return descriptorConverter;
302 }
303 }
304
305 public ModuleDescriptor getModuleDescriptor(String moduleName,
306 String version) {
307 return getExecutionModuleDescriptor(moduleName, version);
308 }
309
310 public List<ModuleDescriptor> listModules() {
311 Bundle[] bundles = bundlesManager.getBundleContext().getBundles();
312 List<ModuleDescriptor> lst = new ArrayList<ModuleDescriptor>();
313 for (Bundle bundle : bundles) {
314 ModuleDescriptor moduleDescriptor = new ModuleDescriptor();
315 setMetadataFromBundle(moduleDescriptor, bundle);
316 lst.add(moduleDescriptor);
317 }
318 return lst;
319 }
320
321 protected void setMetadataFromBundle(ModuleDescriptor md, Bundle bundle) {
322 Bundle bdl = bundle;
323 if (bdl == null) {
324 if (md.getName() == null || md.getVersion() == null)
325 throw new SlcException("Name and version not available.");
326
327 Bundle[] bundles = bundlesManager.getBundleContext().getBundles();
328 for (Bundle b : bundles) {
329 if (b.getSymbolicName().equals(md.getName())
330 && md.getVersion().equals(
331 getHeaderSafe(b, Constants.BUNDLE_VERSION))) {
332 bdl = b;
333 break;
334 }
335 }
336
337 }
338
339 if (bdl == null)
340 throw new SlcException("Cannot find bundle.");
341
342 md.setName(bdl.getSymbolicName());
343 md.setVersion(getHeaderSafe(bdl, Constants.BUNDLE_VERSION));
344 md.setLabel(getHeaderSafe(bdl, Constants.BUNDLE_NAME));
345 md.setDescription(getHeaderSafe(bdl, Constants.BUNDLE_DESCRIPTION));
346 }
347
348 private String getHeaderSafe(Bundle bundle, Object key) {
349 Object obj = bundle.getHeaders().get(key);
350 if (obj == null)
351 return null;
352 else
353 return obj.toString();
354 }
355
356 @SuppressWarnings("unchecked")
357 public synchronized void bind(Object service, Map properties)
358 throws Exception {
359 if (service instanceof ExecutionContext) {
360 ExecutionContext executionContext = (ExecutionContext) service;
361 OsgiBundle osgiBundle = asOsgiBundle(properties);
362 Bundle bundle = bundlesManager.findRelatedBundle(osgiBundle);
363 osgiBundle.setLabel(getHeaderSafe(bundle, Constants.BUNDLE_NAME));
364 osgiBundle.setDescription(getHeaderSafe(bundle,
365 Constants.BUNDLE_DESCRIPTION));
366 executionContexts.put(osgiBundle, executionContext);
367 if (log.isTraceEnabled())
368 log.debug("Registered execution context from " + osgiBundle);
369 // Notify
370 for (ExecutionModulesListener listener : getExecutionModulesListeners())
371 listener.executionModuleAdded(osgiBundle, executionContext);
372
373 } else if (service instanceof ExecutionFlow) {
374 ExecutionFlow executionFlow = (ExecutionFlow) service;
375 OsgiBundle osgiBundle = asOsgiBundle(properties);
376 if (!executionFlows.containsKey(osgiBundle)) {
377 executionFlows.put(osgiBundle, new HashSet());
378 }
379 executionFlows.get(osgiBundle).add(executionFlow);
380 if (log.isTraceEnabled())
381 log
382 .debug("Registered " + executionFlow + " from "
383 + osgiBundle);
384 for (ExecutionModulesListener listener : getExecutionModulesListeners())
385 listener.executionFlowAdded(osgiBundle, executionFlow);
386
387 } else if (service instanceof ExecutionFlowDescriptorConverter) {
388 ExecutionFlowDescriptorConverter executionFlowDescriptorConverter = (ExecutionFlowDescriptorConverter) service;
389 OsgiBundle osgiBundle = asOsgiBundle(properties);
390 executionFlowDescriptorConverters.put(osgiBundle,
391 executionFlowDescriptorConverter);
392 if (log.isTraceEnabled())
393 log
394 .debug("Registered execution flow descriptor converter from "
395 + osgiBundle);
396 } else {
397 // ignore
398 }
399 }
400
401 @SuppressWarnings("unchecked")
402 public synchronized void unbind(Object service, Map properties)
403 throws Exception {
404 if (service instanceof ExecutionContext) {
405 OsgiBundle osgiBundle = asOsgiBundle(properties);
406 if (executionContexts.containsKey(osgiBundle)) {
407 ExecutionContext executionContext = executionContexts
408 .remove(osgiBundle);
409 if (log.isTraceEnabled())
410 log.debug("Removed execution context from " + osgiBundle);
411 // Notify
412 for (ExecutionModulesListener listener : getExecutionModulesListeners())
413 listener.executionModuleRemoved(osgiBundle,
414 executionContext);
415 }
416 } else if (service instanceof ExecutionFlow) {
417 ExecutionFlow executionFlow = (ExecutionFlow) service;
418 OsgiBundle osgiBundle = asOsgiBundle(properties);
419 if (executionFlows.containsKey(osgiBundle)) {
420 Set flows = executionFlows.get(osgiBundle);
421 flows.remove(executionFlow);
422 if (log.isTraceEnabled())
423 log.debug("Removed " + executionFlow + " from "
424 + osgiBundle);
425 if (flows.size() == 0) {
426 executionFlows.remove(osgiBundle);
427 if (log.isTraceEnabled())
428 log.debug("Removed flows set from " + osgiBundle);
429 }
430 for (ExecutionModulesListener listener : getExecutionModulesListeners())
431 listener.executionFlowRemoved(osgiBundle, executionFlow);
432 }
433 } else if (service instanceof ExecutionFlowDescriptorConverter) {
434 OsgiBundle osgiBundle = asOsgiBundle(properties);
435 if (executionFlowDescriptorConverters.containsKey(osgiBundle)) {
436 executionFlowDescriptorConverters.remove(osgiBundle);
437 if (log.isTraceEnabled())
438 log
439 .debug("Removed execution flow descriptor converter from "
440 + osgiBundle);
441 }
442 } else {
443 // ignore
444 }
445 }
446
447 @SuppressWarnings("unchecked")
448 private OsgiBundle asOsgiBundle(Map properties) {
449 String bundleSymbolicName = checkAndGet(Constants.BUNDLE_SYMBOLICNAME,
450 properties);
451 String bundleVersion = checkAndGet(Constants.BUNDLE_VERSION, properties);
452 return new OsgiBundle(bundleSymbolicName, bundleVersion);
453 }
454
455 @SuppressWarnings("unchecked")
456 private String checkAndGet(Object key, Map properties) {
457 if (!properties.containsKey(key) || properties.get(key) == null)
458 throw new SlcException(key + " not set in " + properties);
459 else
460 return properties.get(key).toString();
461 }
462
463 public void setDefaultDescriptorConverter(
464 ExecutionFlowDescriptorConverter defaultDescriptorConverter) {
465 this.defaultDescriptorConverter = defaultDescriptorConverter;
466 }
467 }