@Override
public void run() {
JcrSession inputSession = null;
JcrSession outputSession = null;
final RepositoryStatistics stats = repository.statistics();
Sequencer sequencer = null;
String sequencerName = null;
try {
// Create the required session(s) ...
inputSession = repository.loginInternalSession(work.getInputWorkspaceName());
if (work.getOutputWorkspaceName() != null && !work.getOutputWorkspaceName().equals(work.getInputWorkspaceName())) {
outputSession = repository.loginInternalSession(work.getOutputWorkspaceName());
} else {
outputSession = inputSession;
}
// Get the sequencer ...
sequencer = repository.sequencers().getSequencer(work.getSequencerId());
if (sequencer == null) {
if (DEBUG) {
LOGGER.debug("Unable to find sequencer with ID '{0}' in repository '{1}'; skipping input '{3}:{2}' and output '{5}:{4}'",
work.getSequencerId(), repository.name(), work.getInputPath(), work.getInputWorkspaceName(),
work.getOutputPath(), work.getOutputWorkspaceName());
}
return;
}
sequencerName = sequencer.getName();
String logMsg = null;
if (TRACE || DEBUG) {
logMsg = StringUtil.createString("sequencer '{0}' in repository '{1}' with input '{3}:{2}' to produce '{5}:{4}'",
sequencerName,
repository.name(),
work.getInputPath(),
work.getInputWorkspaceName(),
work.getOutputPath(),
work.getOutputWorkspaceName() != null ? work.getOutputWorkspaceName() : work.getInputWorkspaceName());
LOGGER.debug("Running {0}", logMsg);
}
// 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') ...
String mimeType = getInputMimeType(changedProperty);
// See if the sequencer accepts the MIME type ...
if (mimeType != null && !sequencer.isAccepted(mimeType)) {
LOGGER.debug("Skipping sequencing because MIME type of input doesn't match expectations for {0}", logMsg);
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 ...
AbstractJcrNode parentOfOutput = null;
try {
parentOfOutput = outputSession.getNode(work.getOutputPath());
} catch (PathNotFoundException e) {
LOGGER.trace("Creating missing output path for {0}", logMsg);
JcrTools tools = new JcrTools();
parentOfOutput = (AbstractJcrNode)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(), logMsg);
// Create the output node
if (parentOfOutput.isNew() && parentOfOutput.getName().equals(outputNodeName)) {
// avoid creating a duplicate path with the same name
outputNode = parentOfOutput;
} else {
if (TRACE) {
LOGGER.trace("Creating output node '{0}' under parent '{1}' for {2}", outputNodeName,
parentOfOutput.getPath(), logMsg);
}
outputNode = 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());
if (inputSession.isLive() && (inputSession == outputSession || outputSession.isLive())) {
final long start = System.nanoTime();
try {
LOGGER.trace("Executing {0}", logMsg);
if (sequencer.execute(changedProperty, outputNode, context)) {
LOGGER.trace("Completed executing {0}", logMsg);
// 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
LOGGER.trace("Saving session used by {0}", logMsg);
outputSession.save();
// fire the sequencing event after save (hopefully by this time the transaction has been committed)
LOGGER.trace("Firing events resulting from {0}", logMsg);
fireSequencingEvent(selectedNode, outputNodes, outputSession, sequencerName);
long durationInNanos = Math.abs(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);
}