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