private void fireSequencingEvent( AbstractJcrNode sequencedNode, List<AbstractJcrNode> outputNodes, JcrSession outputSession, String sequencerName) throws RepositoryException { RecordingChanges sequencingChanges = new RecordingChanges( outputSession.context().getProcessId(), outputSession.getRepository().repositoryKey(), outputSession.workspaceName()); for (AbstractJcrNode outputNode : outputNodes) { sequencingChanges.nodeSequenced( sequencedNode.key(), sequencedNode.path(), outputNode.key(), outputNode.path(), work.getOutputPath(), work.getUserId(), work.getSelectedPath(), sequencerName); } repository.changeBus().notify(sequencingChanges); }
private void fireSequencingFailureEvent( AbstractJcrNode sequencedNode, JcrSession inputSession, Throwable cause, String sequencerName) throws RepositoryException { assert sequencedNode != null; assert inputSession != null; RecordingChanges sequencingChanges = new RecordingChanges( inputSession.context().getProcessId(), inputSession.getRepository().repositoryKey(), inputSession.workspaceName()); sequencingChanges.nodeSequencingFailure( sequencedNode.key(), sequencedNode.path(), work.getOutputPath(), work.getUserId(), work.getSelectedPath(), sequencerName, cause); repository.changeBus().notify(sequencingChanges); }
private void setCreatedByIfNecessary(JcrSession outputSession, List<AbstractJcrNode> outputNodes) throws RepositoryException { // if the mix:created mixin is on any of the new nodes, we need to set the createdBy here, // otherwise it will be // set by the system session when it saves and it will default to "modeshape-worker" for (AbstractJcrNode node : outputNodes) { if (node.isNodeType(JcrMixLexicon.CREATED)) { node.setProperty( JcrLexicon.CREATED_BY, outputSession.getValueFactory().createValue(work.getUserId()), true, true); } } }
@Override public void run() { JcrSession inputSession = null; JcrSession outputSession = null; final RunningState state = repository.runningState(); final RepositoryStatistics stats = state.statistics(); Sequencer sequencer = null; String sequencerName = null; try { // Create the required session(s) ... inputSession = state.loginInternalSession(work.getInputWorkspaceName()); if (work.getOutputWorkspaceName() != null && !work.getOutputWorkspaceName().equals(work.getInputWorkspaceName())) { outputSession = state.loginInternalSession(work.getOutputWorkspaceName()); } else { outputSession = inputSession; } // Get the sequencer ... sequencer = state.sequencers().getSequencer(work.getSequencerId()); if (sequencer == null) return; sequencerName = sequencer.getName(); // Find the selected node ... AbstractJcrNode selectedNode = inputSession.getNode(work.getSelectedPath()); // Find the input that has changed and is to be sequenced ... Item inputItem = inputSession.getItem(work.getInputPath()); Property changedProperty = null; if (inputItem instanceof Property) { changedProperty = (Property) inputItem; } else { Node changedNode = (Node) inputItem; // now look for a property that was changed or added ... changedProperty = changedNode.getProperty(work.getChangedPropertyName()); } assert changedProperty != null; if (sequencer.hasAcceptedMimeTypes()) { // Get the MIME type, first by looking at the changed property's parent node // (or grand-parent node if parent is 'jcr:content') ... Node parent = changedProperty.getParent(); String mimeType = null; if (parent.hasProperty(JcrConstants.JCR_MIME_TYPE)) { // The parent node has a 'jcr:mimeType' node ... Property property = parent.getProperty(JcrConstants.JCR_MIME_TYPE); if (!property.isMultiple()) { // The standard 'jcr:mimeType' property is single valued, but we're technically not // checking if // the property has that particular property definition (only by name) ... mimeType = property.getString(); } } else if (parent.getName().equals(JcrConstants.JCR_CONTENT)) { // There is no 'jcr:mimeType' property, and since the sequenced property is on the // 'jcr:content' node, // get the parent (probably 'nt:file') node and look for the 'jcr:mimeType' property there // ... try { parent = parent.getParent(); if (parent.hasProperty(JcrConstants.JCR_MIME_TYPE)) { Property property = parent.getProperty(JcrConstants.JCR_MIME_TYPE); if (!property.isMultiple()) { // The standard 'jcr:mimeType' property is single valued, but we're technically not // checking if // the property has that particular property definition (only by name) ... mimeType = property.getString(); } } } catch (ItemNotFoundException e) { // must be the root ... } } if (mimeType == null && !changedProperty.isMultiple() && changedProperty.getType() == PropertyType.BINARY) { // Still don't know the MIME type of the property, so if it's a BINARY property we can // check it ... javax.jcr.Binary binary = changedProperty.getBinary(); if (binary instanceof org.modeshape.jcr.api.Binary) { mimeType = ((org.modeshape.jcr.api.Binary) binary).getMimeType(parent.getName()); } } // See if the sequencer accepts the MIME type ... if (mimeType != null && !sequencer.isAccepted(mimeType)) { return; // nope } } AbstractJcrNode outputNode = null; String primaryType = null; if (work.getSelectedPath().equals(work.getOutputPath())) { // The output is to go directly under the sequenced node ... outputNode = selectedNode.getName().equals(JcrConstants.JCR_CONTENT) ? selectedNode.getParent() : selectedNode; primaryType = selectedNode.getPrimaryNodeType().getName(); } else { // Find the parent of the output if it exists, or create the node(s) along the path if not // ... Node parentOfOutput = null; try { parentOfOutput = outputSession.getNode(work.getOutputPath()); } catch (PathNotFoundException e) { JcrTools tools = new JcrTools(); parentOfOutput = tools.findOrCreateNode(outputSession, work.getOutputPath()); } // Now determine the name of top node in the output, using the last segment of the selected // path ... String outputNodeName = computeOutputNodeName(selectedNode); // Remove any existing output (from a prior sequencing run on this same input) ... removeExistingOutputNodes(parentOfOutput, outputNodeName, work.getSelectedPath()); // Create the output node if (parentOfOutput.isNew() && parentOfOutput.getName().equals(outputNodeName)) { // avoid creating a duplicate path with the same name outputNode = (AbstractJcrNode) parentOfOutput; } else { outputNode = (AbstractJcrNode) parentOfOutput.addNode(outputNodeName, JcrConstants.NT_UNSTRUCTURED); } // and make sure the output node has the 'mode:derived' mixin ... outputNode.addMixin(DERIVED_NODE_TYPE_NAME); outputNode.setProperty(DERIVED_FROM_PROPERTY_NAME, work.getSelectedPath()); } // Execute the sequencer ... DateTime now = outputSession.dateFactory().create(); Sequencer.Context context = new SequencingContext( now, outputSession.getValueFactory(), outputSession.context().getMimeTypeDetector()); if (inputSession.isLive() && (inputSession == outputSession || outputSession.isLive())) { final long start = System.nanoTime(); try { if (sequencer.execute(changedProperty, outputNode, context)) { // Make sure that the sequencer did not change the primary type of the selected node .. if (selectedNode == outputNode && !selectedNode.getPrimaryNodeType().getName().equals(primaryType)) { String msg = RepositoryI18n.sequencersMayNotChangeThePrimaryTypeOfTheSelectedNode.text(); throw new RepositoryException(msg); } // find the new nodes created by the sequencing before saving, so we can properly fire // the events List<AbstractJcrNode> outputNodes = findOutputNodes(outputNode); // set the createdBy property (if it applies) to the user which triggered the // sequencing, not the context // of the saving session setCreatedByIfNecessary(outputSession, outputNodes); // outputSession outputSession.save(); // fire the sequencing event after save (hopefully by this time the transaction has been // committed) fireSequencingEvent(selectedNode, outputNodes, outputSession, sequencerName); long durationInNanos = System.nanoTime() - start; Map<String, String> payload = new HashMap<String, String>(); payload.put("sequencerName", sequencer.getClass().getName()); payload.put("sequencedPath", changedProperty.getPath()); payload.put("outputPath", outputNode.getPath()); stats.recordDuration( DurationMetric.SEQUENCER_EXECUTION_TIME, durationInNanos, TimeUnit.NANOSECONDS, payload); } } catch (Throwable t) { fireSequencingFailureEvent(selectedNode, inputSession, t, sequencerName); // let it bubble down, because we still want to log it and update the stats throw t; } } } catch (Throwable t) { Logger logger = Logger.getLogger(getClass()); if (work.getOutputWorkspaceName() != null) { logger.error( t, RepositoryI18n.errorWhileSequencingNodeIntoWorkspace, sequencerName, state.name(), work.getInputPath(), work.getInputWorkspaceName(), work.getOutputPath(), work.getOutputWorkspaceName()); } else { logger.error( t, RepositoryI18n.errorWhileSequencingNode, sequencerName, state.name(), work.getInputPath(), work.getInputWorkspaceName(), work.getOutputPath()); } } finally { stats.increment(ValueMetric.SEQUENCED_COUNT); stats.decrement(ValueMetric.SEQUENCER_QUEUE_SIZE); if (inputSession != null && inputSession.isLive()) inputSession.logout(); if (outputSession != null && outputSession != inputSession && outputSession.isLive()) outputSession.logout(); } }