X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.slc.jcr%2Fsrc%2Forg%2Fargeo%2Fslc%2Fjcr%2Fexecution%2FJcrExecutionModulesListener.java;fp=org.argeo.slc.jcr%2Fsrc%2Forg%2Fargeo%2Fslc%2Fjcr%2Fexecution%2FJcrExecutionModulesListener.java;h=58f31251b8f1b07a7766f19703a7f50c841946b1;hb=2754797eb6cbee85ba2ff0b59e3be974114a49b1;hp=0000000000000000000000000000000000000000;hpb=a81b61a88cef01d294cd8956c215604d2b10ab58;p=gpl%2Fargeo-jcr.git diff --git a/org.argeo.slc.jcr/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java b/org.argeo.slc.jcr/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java new file mode 100644 index 0000000..58f3125 --- /dev/null +++ b/org.argeo.slc.jcr/src/org/argeo/slc/jcr/execution/JcrExecutionModulesListener.java @@ -0,0 +1,352 @@ +package org.argeo.slc.jcr.execution; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsLog; +import org.argeo.jcr.JcrUtils; +import org.argeo.slc.SlcException; +import org.argeo.slc.SlcNames; +import org.argeo.slc.SlcTypes; +import org.argeo.slc.deploy.ModuleDescriptor; +import org.argeo.slc.execution.ExecutionFlowDescriptor; +import org.argeo.slc.execution.ExecutionModuleDescriptor; +import org.argeo.slc.execution.ExecutionModulesListener; +import org.argeo.slc.execution.ExecutionModulesManager; +import org.argeo.slc.execution.ExecutionSpec; +import org.argeo.slc.execution.ExecutionSpecAttribute; +import org.argeo.slc.execution.RefSpecAttribute; +import org.argeo.slc.execution.RefValueChoice; +import org.argeo.slc.jcr.SlcJcrUtils; +import org.argeo.slc.primitive.PrimitiveSpecAttribute; +import org.argeo.slc.primitive.PrimitiveValue; + +/** + * Synchronizes the local execution runtime with a JCR repository. For the time + * being the state is completely reset from one start to another. + */ +public class JcrExecutionModulesListener implements ExecutionModulesListener, SlcNames { + private final static String SLC_EXECUTION_MODULES_PROPERTY = "slc.executionModules"; + + private final static CmsLog log = CmsLog.getLog(JcrExecutionModulesListener.class); + private JcrAgent agent; + + private ExecutionModulesManager modulesManager; + + private Repository repository; + /** + * We don't use a thread bound session because many different threads will call + * this critical component and we don't want to login each time. We therefore + * rather protect access to this session via synchronized. + */ + private Session session; + + /* + * LIFECYCLE + */ + public void init() { + try { + session = repository.login(); + clearAgent(); + if (modulesManager != null) { + Node agentNode = session.getNode(agent.getNodePath()); + + List moduleDescriptors = modulesManager.listModules(); + + // scan SLC-ExecutionModule metadata + for (ModuleDescriptor md : moduleDescriptors) { + if (md.getMetadata().containsKey(ExecutionModuleDescriptor.SLC_EXECUTION_MODULE)) { + String moduleNodeName = SlcJcrUtils.getModuleNodeName(md); + Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode.getNode(moduleNodeName) + : agentNode.addNode(moduleNodeName); + moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, md.getName()); + moduleNode.setProperty(SLC_VERSION, md.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, md.getTitle()); + moduleNode.setProperty(Property.JCR_DESCRIPTION, md.getDescription()); + moduleNode.setProperty(SLC_STARTED, md.getStarted()); + } + } + + // scan execution modules property + String executionModules = System.getProperty(SLC_EXECUTION_MODULES_PROPERTY); + if (executionModules != null) { + for (String executionModule : executionModules.split(",")) { + allModules: for (ModuleDescriptor md : moduleDescriptors) { + String moduleNodeName = SlcJcrUtils.getModuleNodeName(md); + if (md.getName().equals(executionModule)) { + Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode.getNode(moduleNodeName) + : agentNode.addNode(moduleNodeName); + moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, md.getName()); + moduleNode.setProperty(SLC_VERSION, md.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, md.getTitle()); + moduleNode.setProperty(Property.JCR_DESCRIPTION, md.getDescription()); + moduleNode.setProperty(SLC_STARTED, md.getStarted()); + break allModules; + } + } + } + + // save if needed + if (session.hasPendingChanges()) + session.save(); + } + } + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + JcrUtils.logoutQuietly(session); + throw new SlcException("Cannot initialize modules", e); + } + } + + public void destroy() { + clearAgent(); + JcrUtils.logoutQuietly(session); + } + + protected synchronized void clearAgent() { + try { + Node agentNode = session.getNode(agent.getNodePath()); + for (NodeIterator nit = agentNode.getNodes(); nit.hasNext();) + nit.nextNode().remove(); + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot clear agent " + agent, e); + } + } + + /* + * EXECUTION MODULES LISTENER + */ + + public synchronized void executionModuleAdded(ModuleDescriptor moduleDescriptor) { + syncExecutionModule(moduleDescriptor); + } + + protected void syncExecutionModule(ModuleDescriptor moduleDescriptor) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + String moduleNodeName = SlcJcrUtils.getModuleNodeName(moduleDescriptor); + Node moduleNode = agentNode.hasNode(moduleNodeName) ? agentNode.getNode(moduleNodeName) + : agentNode.addNode(moduleNodeName); + moduleNode.addMixin(SlcTypes.SLC_EXECUTION_MODULE); + moduleNode.setProperty(SLC_NAME, moduleDescriptor.getName()); + moduleNode.setProperty(SLC_VERSION, moduleDescriptor.getVersion()); + moduleNode.setProperty(Property.JCR_TITLE, moduleDescriptor.getTitle()); + moduleNode.setProperty(Property.JCR_DESCRIPTION, moduleDescriptor.getDescription()); + moduleNode.setProperty(SLC_STARTED, moduleDescriptor.getStarted()); + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot sync module " + moduleDescriptor, e); + } + } + + public synchronized void executionModuleRemoved(ModuleDescriptor moduleDescriptor) { + try { + String moduleName = SlcJcrUtils.getModuleNodeName(moduleDescriptor); + Node agentNode = session.getNode(agent.getNodePath()); + if (agentNode.hasNode(moduleName)) { + Node moduleNode = agentNode.getNode(moduleName); + for (NodeIterator nit = moduleNode.getNodes(); nit.hasNext();) { + nit.nextNode().remove(); + } + moduleNode.setProperty(SLC_STARTED, false); + } + session.save(); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot remove module " + moduleDescriptor, e); + } + } + + public synchronized void executionFlowAdded(ModuleDescriptor module, ExecutionFlowDescriptor efd) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + Node moduleNode = agentNode.getNode(SlcJcrUtils.getModuleNodeName(module)); + String relativePath = getExecutionFlowRelativePath(efd); + @SuppressWarnings("unused") + Node flowNode = null; + if (!moduleNode.hasNode(relativePath)) { + flowNode = createExecutionFlowNode(moduleNode, relativePath, efd); + session.save(); + } else { + flowNode = moduleNode.getNode(relativePath); + } + + if (log.isTraceEnabled()) + log.trace("Flow " + efd + " added to JCR"); + } catch (RepositoryException e) { + JcrUtils.discardQuietly(session); + throw new SlcException("Cannot add flow " + efd + " from module " + module, e); + } + + } + + protected Node createExecutionFlowNode(Node moduleNode, String relativePath, ExecutionFlowDescriptor efd) + throws RepositoryException { + Node flowNode = null; + List pathTokens = Arrays.asList(relativePath.split("/")); + + Iterator names = pathTokens.iterator(); + // create intermediary paths + Node currNode = moduleNode; + while (names.hasNext()) { + String name = names.next(); + if (currNode.hasNode(name)) + currNode = currNode.getNode(name); + else { + if (names.hasNext()) + currNode = currNode.addNode(name); + else + flowNode = currNode.addNode(name, SlcTypes.SLC_EXECUTION_FLOW); + } + } + + // name, description + flowNode.setProperty(SLC_NAME, efd.getName()); + String endName = pathTokens.get(pathTokens.size() - 1); + flowNode.setProperty(Property.JCR_TITLE, endName); + if (efd.getDescription() != null && !efd.getDescription().trim().equals("")) { + flowNode.setProperty(Property.JCR_DESCRIPTION, efd.getDescription()); + } else { + flowNode.setProperty(Property.JCR_DESCRIPTION, endName); + } + + // execution spec + ExecutionSpec executionSpec = efd.getExecutionSpec(); + String esName = executionSpec.getName(); + if (esName == null || esName.equals(ExecutionSpec.INTERNAL_NAME) + || esName.contains("#")/* automatically generated bean name */) { + // internal spec node + mapExecutionSpec(flowNode, executionSpec); + } else { + // reference spec node + Node executionSpecsNode = moduleNode.hasNode(SLC_EXECUTION_SPECS) ? moduleNode.getNode(SLC_EXECUTION_SPECS) + : moduleNode.addNode(SLC_EXECUTION_SPECS); + Node executionSpecNode = executionSpecsNode.addNode(esName, SlcTypes.SLC_EXECUTION_SPEC); + executionSpecNode.setProperty(SLC_NAME, esName); + executionSpecNode.setProperty(Property.JCR_TITLE, esName); + if (executionSpec.getDescription() != null && !executionSpec.getDescription().trim().equals("")) + executionSpecNode.setProperty(Property.JCR_DESCRIPTION, executionSpec.getDescription()); + mapExecutionSpec(executionSpecNode, executionSpec); + flowNode.setProperty(SLC_SPEC, executionSpecNode); + } + + // flow values + for (String attr : efd.getValues().keySet()) { + ExecutionSpecAttribute esa = executionSpec.getAttributes().get(attr); + if (esa instanceof PrimitiveSpecAttribute) { + PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa; + // if spec reference there will be no node at this stage + Node valueNode = JcrUtils.getOrAdd(flowNode, attr); + valueNode.setProperty(SLC_TYPE, psa.getType()); + SlcJcrUtils.setPrimitiveAsProperty(valueNode, SLC_VALUE, (PrimitiveValue) efd.getValues().get(attr)); + } + } + + return flowNode; + } + + /** + * Base can be either an execution spec node, or an execution flow node (in case + * the execution spec is internal) + */ + protected void mapExecutionSpec(Node baseNode, ExecutionSpec executionSpec) throws RepositoryException { + for (String attrName : executionSpec.getAttributes().keySet()) { + ExecutionSpecAttribute esa = executionSpec.getAttributes().get(attrName); + Node attrNode = baseNode.addNode(attrName); + // booleans + attrNode.addMixin(SlcTypes.SLC_EXECUTION_SPEC_ATTRIBUTE); + attrNode.setProperty(SLC_IS_IMMUTABLE, esa.getIsImmutable()); + attrNode.setProperty(SLC_IS_CONSTANT, esa.getIsConstant()); + attrNode.setProperty(SLC_IS_HIDDEN, esa.getIsHidden()); + + if (esa instanceof PrimitiveSpecAttribute) { + attrNode.addMixin(SlcTypes.SLC_PRIMITIVE_SPEC_ATTRIBUTE); + PrimitiveSpecAttribute psa = (PrimitiveSpecAttribute) esa; + SlcJcrUtils.setPrimitiveAsProperty(attrNode, SLC_VALUE, psa); + attrNode.setProperty(SLC_TYPE, psa.getType()); + } else if (esa instanceof RefSpecAttribute) { + attrNode.addMixin(SlcTypes.SLC_REF_SPEC_ATTRIBUTE); + RefSpecAttribute rsa = (RefSpecAttribute) esa; + attrNode.setProperty(SLC_TYPE, rsa.getTargetClassName()); + Object value = rsa.getValue(); + if (rsa.getChoices() != null) { + Integer index = null; + int count = 0; + for (RefValueChoice choice : rsa.getChoices()) { + String name = choice.getName(); + if (value != null && name.equals(value.toString())) + index = count; + Node choiceNode = attrNode.addNode(choice.getName()); + choiceNode.addMixin(NodeType.MIX_TITLE); + choiceNode.setProperty(Property.JCR_TITLE, choice.getName()); + if (choice.getDescription() != null && !choice.getDescription().trim().equals("")) + choiceNode.setProperty(Property.JCR_DESCRIPTION, choice.getDescription()); + count++; + } + + if (index != null) + attrNode.setProperty(SLC_VALUE, index); + } + } + } + } + + public synchronized void executionFlowRemoved(ModuleDescriptor module, ExecutionFlowDescriptor executionFlow) { + try { + Node agentNode = session.getNode(agent.getNodePath()); + Node moduleNode = agentNode.getNode(SlcJcrUtils.getModuleNodeName(module)); + String relativePath = getExecutionFlowRelativePath(executionFlow); + if (moduleNode.hasNode(relativePath)) + moduleNode.getNode(relativePath).remove(); + agentNode.getSession().save(); + } catch (RepositoryException e) { + throw new SlcException("Cannot remove flow " + executionFlow + " from module " + module, e); + } + } + + /* + * UTILITIES + */ + /** @return the relative path, never starts with '/' */ + @SuppressWarnings("deprecation") + protected String getExecutionFlowRelativePath(ExecutionFlowDescriptor executionFlow) { + String relativePath = executionFlow.getPath() == null ? executionFlow.getName() + : executionFlow.getPath() + '/' + executionFlow.getName(); + // we assume that it is more than one char long + if (relativePath.charAt(0) == '/') + relativePath = relativePath.substring(1); + // FIXME quick hack to avoid duplicate '/' + relativePath = relativePath.replaceAll("//", "/"); + return relativePath; + } + + /* + * BEAN + */ + public void setAgent(JcrAgent agent) { + this.agent = agent; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setModulesManager(ExecutionModulesManager modulesManager) { + this.modulesManager = modulesManager; + } + +}