]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.core/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java
Merge JCR, OSGi and Ant support into SLC Core
[gpl/argeo-slc.git] / org.argeo.slc.core / src / org / argeo / slc / jcr / execution / JcrExecutionModulesListener.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
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 package org.argeo.slc.jcr.execution;
17
18 import java.util.Arrays;
19 import java.util.Iterator;
20 import java.util.List;
21
22 import javax.jcr.Node;
23 import javax.jcr.NodeIterator;
24 import javax.jcr.Property;
25 import javax.jcr.Repository;
26 import javax.jcr.RepositoryException;
27 import javax.jcr.Session;
28 import javax.jcr.nodetype.NodeType;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.argeo.jcr.JcrUtils;
33 import org.argeo.slc.SlcException;
34 import org.argeo.slc.core.execution.PrimitiveSpecAttribute;
35 import org.argeo.slc.core.execution.PrimitiveValue;
36 import org.argeo.slc.core.execution.RefSpecAttribute;
37 import org.argeo.slc.core.execution.RefValueChoice;
38 import org.argeo.slc.deploy.ModuleDescriptor;
39 import org.argeo.slc.execution.ExecutionFlowDescriptor;
40 import org.argeo.slc.execution.ExecutionModuleDescriptor;
41 import org.argeo.slc.execution.ExecutionModulesListener;
42 import org.argeo.slc.execution.ExecutionModulesManager;
43 import org.argeo.slc.execution.ExecutionSpec;
44 import org.argeo.slc.execution.ExecutionSpecAttribute;
45 import org.argeo.slc.jcr.SlcJcrUtils;
46 import org.argeo.slc.jcr.SlcNames;
47 import org.argeo.slc.jcr.SlcTypes;
48
49 /**
50 * Synchronizes the local execution runtime with a JCR repository. For the time
51 * being the state is completely reset from one start to another.
52 */
53 public class JcrExecutionModulesListener implements ExecutionModulesListener,
54 SlcNames {
55 private final static String SLC_EXECUTION_MODULES_PROPERTY = "slc.executionModules";
56
57 private final static Log log = LogFactory
58 .getLog(JcrExecutionModulesListener.class);
59 private JcrAgent agent;
60
61 private ExecutionModulesManager modulesManager;
62
63 private Repository repository;
64 /**
65 * We don't use a thread bound session because many different threads will
66 * call this critical component and we don't want to login each time. We
67 * therefore rather protect access to this session via synchronized.
68 */
69 private Session session;
70
71 /*
72 * LIFECYCLE
73 */
74 public void init() {
75 try {
76 session = repository.login();
77 clearAgent();
78 if (modulesManager != null) {
79 Node agentNode = session.getNode(agent.getNodePath());
80
81 List<ModuleDescriptor> moduleDescriptors = modulesManager
82 .listModules();
83
84 // scan SLC-ExecutionModule metadata
85 for (ModuleDescriptor md : moduleDescriptors) {
86 if (md.getMetadata().containsKey(
87 ExecutionModuleDescriptor.SLC_EXECUTION_MODULE)) {
88 String moduleNodeName = SlcJcrUtils
89 .getModuleNodeName(md);
90 Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode
91 .getNode(moduleNodeName) : agentNode
92 .addNode(moduleNodeName);
93 moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE);
94 moduleNode.setProperty(SLC_NAME, md.getName());
95 moduleNode.setProperty(SLC_VERSION, md.getVersion());
96 moduleNode.setProperty(Property.JCR_TITLE,
97 md.getTitle());
98 moduleNode.setProperty(Property.JCR_DESCRIPTION,
99 md.getDescription());
100 moduleNode.setProperty(SLC_STARTED, md.getStarted());
101 }
102 }
103
104 // scan execution modules property
105 String executionModules = System
106 .getProperty(SLC_EXECUTION_MODULES_PROPERTY);
107 if (executionModules != null) {
108 for (String executionModule : executionModules.split(",")) {
109 allModules: for (ModuleDescriptor md : moduleDescriptors) {
110 String moduleNodeName = SlcJcrUtils
111 .getModuleNodeName(md);
112 if (md.getName().equals(executionModule)) {
113 Node moduleNode = agentNode
114 .hasNode(moduleNodeName) ? agentNode
115 .getNode(moduleNodeName) : agentNode
116 .addNode(moduleNodeName);
117 moduleNode
118 .addMixin(SlcTypes.SLC_EXECUTION_MODULE);
119 moduleNode.setProperty(SLC_NAME, md.getName());
120 moduleNode.setProperty(SLC_VERSION,
121 md.getVersion());
122 moduleNode.setProperty(Property.JCR_TITLE,
123 md.getTitle());
124 moduleNode.setProperty(
125 Property.JCR_DESCRIPTION,
126 md.getDescription());
127 moduleNode.setProperty(SLC_STARTED,
128 md.getStarted());
129 break allModules;
130 }
131 }
132 }
133
134 // save if needed
135 if (session.hasPendingChanges())
136 session.save();
137 }
138 }
139 } catch (RepositoryException e) {
140 JcrUtils.discardQuietly(session);
141 JcrUtils.logoutQuietly(session);
142 throw new SlcException("Cannot initialize modules", e);
143 }
144 }
145
146 public void destroy() {
147 clearAgent();
148 JcrUtils.logoutQuietly(session);
149 }
150
151 protected synchronized void clearAgent() {
152 try {
153 Node agentNode = session.getNode(agent.getNodePath());
154 for (NodeIterator nit = agentNode.getNodes(); nit.hasNext();)
155 nit.nextNode().remove();
156 session.save();
157 } catch (RepositoryException e) {
158 JcrUtils.discardQuietly(session);
159 throw new SlcException("Cannot clear agent " + agent, e);
160 }
161 }
162
163 /*
164 * EXECUTION MODULES LISTENER
165 */
166
167 public synchronized void executionModuleAdded(
168 ModuleDescriptor moduleDescriptor) {
169 syncExecutionModule(moduleDescriptor);
170 }
171
172 protected void syncExecutionModule(ModuleDescriptor moduleDescriptor) {
173 try {
174 Node agentNode = session.getNode(agent.getNodePath());
175 String moduleNodeName = SlcJcrUtils
176 .getModuleNodeName(moduleDescriptor);
177 Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode
178 .getNode(moduleNodeName) : agentNode
179 .addNode(moduleNodeName);
180 moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE);
181 moduleNode.setProperty(SLC_NAME, moduleDescriptor.getName());
182 moduleNode.setProperty(SLC_VERSION, moduleDescriptor.getVersion());
183 moduleNode.setProperty(Property.JCR_TITLE,
184 moduleDescriptor.getTitle());
185 moduleNode.setProperty(Property.JCR_DESCRIPTION,
186 moduleDescriptor.getDescription());
187 moduleNode.setProperty(SLC_STARTED, moduleDescriptor.getStarted());
188 session.save();
189 } catch (RepositoryException e) {
190 JcrUtils.discardQuietly(session);
191 throw new SlcException("Cannot sync module " + moduleDescriptor, e);
192 }
193 }
194
195 public synchronized void executionModuleRemoved(
196 ModuleDescriptor moduleDescriptor) {
197 try {
198 String moduleName = SlcJcrUtils.getModuleNodeName(moduleDescriptor);
199 Node agentNode = session.getNode(agent.getNodePath());
200 if (agentNode.hasNode(moduleName)) {
201 Node moduleNode = agentNode.getNode(moduleName);
202 for (NodeIterator nit = moduleNode.getNodes(); nit.hasNext();) {
203 nit.nextNode().remove();
204 }
205 moduleNode.setProperty(SLC_STARTED, false);
206 }
207 session.save();
208 } catch (RepositoryException e) {
209 JcrUtils.discardQuietly(session);
210 throw new SlcException("Cannot remove module " + moduleDescriptor,
211 e);
212 }
213 }
214
215 public synchronized void executionFlowAdded(ModuleDescriptor module,
216 ExecutionFlowDescriptor efd) {
217 try {
218 Node agentNode = session.getNode(agent.getNodePath());
219 Node moduleNode = agentNode.getNode(SlcJcrUtils
220 .getModuleNodeName(module));
221 String relativePath = getExecutionFlowRelativePath(efd);
222 @SuppressWarnings("unused")
223 Node flowNode = null;
224 if (!moduleNode.hasNode(relativePath)) {
225 flowNode = createExecutionFlowNode(moduleNode, relativePath,
226 efd);
227 session.save();
228 } else {
229 flowNode = moduleNode.getNode(relativePath);
230 }
231
232 if (log.isTraceEnabled())
233 log.trace("Flow " + efd + " added to JCR");
234 } catch (RepositoryException e) {
235 JcrUtils.discardQuietly(session);
236 throw new SlcException("Cannot add flow " + efd + " from module "
237 + module, e);
238 }
239
240 }
241
242 protected Node createExecutionFlowNode(Node moduleNode,
243 String relativePath, ExecutionFlowDescriptor efd)
244 throws RepositoryException {
245 Node flowNode = null;
246 List<String> pathTokens = Arrays.asList(relativePath.split("/"));
247
248 Iterator<String> names = pathTokens.iterator();
249 // create intermediary paths
250 Node currNode = moduleNode;
251 while (names.hasNext()) {
252 String name = names.next();
253 if (currNode.hasNode(name))
254 currNode = currNode.getNode(name);
255 else {
256 if (names.hasNext())
257 currNode = currNode.addNode(name);
258 else
259 flowNode = currNode.addNode(name,
260 SlcTypes.SLC_EXECUTION_FLOW);
261 }
262 }
263
264 // name, description
265 flowNode.setProperty(SLC_NAME, efd.getName());
266 String endName = pathTokens.get(pathTokens.size() - 1);
267 flowNode.setProperty(Property.JCR_TITLE, endName);
268 if (efd.getDescription() != null
269 && !efd.getDescription().trim().equals("")) {
270 flowNode.setProperty(Property.JCR_DESCRIPTION, efd.getDescription());
271 } else {
272 flowNode.setProperty(Property.JCR_DESCRIPTION, endName);
273 }
274
275 // execution spec
276 ExecutionSpec executionSpec = efd.getExecutionSpec();
277 String esName = executionSpec.getName();
278 if (esName == null || esName.equals(ExecutionSpec.INTERNAL_NAME)
279 || esName.contains("#")/* automatically generated bean name */) {
280 // internal spec node
281 mapExecutionSpec(flowNode, executionSpec);
282 } else {
283 // reference spec node
284 Node executionSpecsNode = moduleNode.hasNode(SLC_EXECUTION_SPECS) ? moduleNode
285 .getNode(SLC_EXECUTION_SPECS) : moduleNode
286 .addNode(SLC_EXECUTION_SPECS);
287 Node executionSpecNode = executionSpecsNode.addNode(esName,
288 SlcTypes.SLC_EXECUTION_SPEC);
289 executionSpecNode.setProperty(SLC_NAME, esName);
290 executionSpecNode.setProperty(Property.JCR_TITLE, esName);
291 if (executionSpec.getDescription() != null
292 && !executionSpec.getDescription().trim().equals(""))
293 executionSpecNode.setProperty(Property.JCR_DESCRIPTION,
294 executionSpec.getDescription());
295 mapExecutionSpec(executionSpecNode, executionSpec);
296 flowNode.setProperty(SLC_SPEC, executionSpecNode);
297 }
298
299 // flow values
300 for (String attr : efd.getValues().keySet()) {
301 ExecutionSpecAttribute esa = executionSpec.getAttributes()
302 .get(attr);
303 if (esa instanceof PrimitiveSpecAttribute) {
304 PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa;
305 // if spec reference there will be no node at this stage
306 Node valueNode = JcrUtils.getOrAdd(flowNode, attr);
307 valueNode.setProperty(SLC_TYPE, psa.getType());
308 SlcJcrUtils.setPrimitiveAsProperty(valueNode, SLC_VALUE,
309 (PrimitiveValue) efd.getValues().get(attr));
310 }
311 }
312
313 return flowNode;
314 }
315
316 /**
317 * Base can be either an execution spec node, or an execution flow node (in
318 * case the execution spec is internal)
319 */
320 protected void mapExecutionSpec(Node baseNode, ExecutionSpec executionSpec)
321 throws RepositoryException {
322 for (String attrName : executionSpec.getAttributes().keySet()) {
323 ExecutionSpecAttribute esa = executionSpec.getAttributes().get(
324 attrName);
325 Node attrNode = baseNode.addNode(attrName);
326 // booleans
327 attrNode.addMixin(SlcTypes.SLC_EXECUTION_SPEC_ATTRIBUTE);
328 attrNode.setProperty(SLC_IS_IMMUTABLE, esa.getIsImmutable());
329 attrNode.setProperty(SLC_IS_CONSTANT, esa.getIsConstant());
330 attrNode.setProperty(SLC_IS_HIDDEN, esa.getIsHidden());
331
332 if (esa instanceof PrimitiveSpecAttribute) {
333 attrNode.addMixin(SlcTypes.SLC_PRIMITIVE_SPEC_ATTRIBUTE);
334 PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa;
335 SlcJcrUtils.setPrimitiveAsProperty(attrNode, SLC_VALUE, psa);
336 attrNode.setProperty(SLC_TYPE, psa.getType());
337 } else if (esa instanceof RefSpecAttribute) {
338 attrNode.addMixin(SlcTypes.SLC_REF_SPEC_ATTRIBUTE);
339 RefSpecAttribute rsa = (RefSpecAttribute) esa;
340 attrNode.setProperty(SLC_TYPE, rsa.getTargetClassName());
341 Object value = rsa.getValue();
342 if (rsa.getChoices() != null) {
343 Integer index = null;
344 int count = 0;
345 for (RefValueChoice choice : rsa.getChoices()) {
346 String name = choice.getName();
347 if (value != null && name.equals(value.toString()))
348 index = count;
349 Node choiceNode = attrNode.addNode(choice.getName());
350 choiceNode.addMixin(NodeType.MIX_TITLE);
351 choiceNode.setProperty(Property.JCR_TITLE,
352 choice.getName());
353 if (choice.getDescription() != null
354 && !choice.getDescription().trim().equals(""))
355 choiceNode.setProperty(Property.JCR_DESCRIPTION,
356 choice.getDescription());
357 count++;
358 }
359
360 if (index != null)
361 attrNode.setProperty(SLC_VALUE, index);
362 }
363 }
364 }
365 }
366
367 public synchronized void executionFlowRemoved(ModuleDescriptor module,
368 ExecutionFlowDescriptor executionFlow) {
369 try {
370 Node agentNode = session.getNode(agent.getNodePath());
371 Node moduleNode = agentNode.getNode(SlcJcrUtils
372 .getModuleNodeName(module));
373 String relativePath = getExecutionFlowRelativePath(executionFlow);
374 if (moduleNode.hasNode(relativePath))
375 moduleNode.getNode(relativePath).remove();
376 agentNode.getSession().save();
377 } catch (RepositoryException e) {
378 throw new SlcException("Cannot remove flow " + executionFlow
379 + " from module " + module, e);
380 }
381 }
382
383 /*
384 * UTILITIES
385 */
386 /** @return the relative path, never starts with '/' */
387 @SuppressWarnings("deprecation")
388 protected String getExecutionFlowRelativePath(
389 ExecutionFlowDescriptor executionFlow) {
390 String relativePath = executionFlow.getPath() == null ? executionFlow
391 .getName() : executionFlow.getPath() + '/'
392 + executionFlow.getName();
393 // we assume that it is more than one char long
394 if (relativePath.charAt(0) == '/')
395 relativePath = relativePath.substring(1);
396 // FIXME quick hack to avoid duplicate '/'
397 relativePath = relativePath.replaceAll("//", "/");
398 return relativePath;
399 }
400
401 /*
402 * BEAN
403 */
404 public void setAgent(JcrAgent agent) {
405 this.agent = agent;
406 }
407
408 public void setRepository(Repository repository) {
409 this.repository = repository;
410 }
411
412 public void setModulesManager(ExecutionModulesManager modulesManager) {
413 this.modulesManager = modulesManager;
414 }
415
416 }