/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.processor.test;
import org.orbeon.dom.Document;
import org.orbeon.dom.Element;
import org.orbeon.oxf.cache.ObjectCache;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.common.ValidationException;
import org.orbeon.oxf.externalcontext.TestExternalContext;
import org.orbeon.oxf.externalcontext.ExternalContext;
import org.orbeon.oxf.pipeline.api.PipelineContext;
import org.orbeon.oxf.processor.*;
import org.orbeon.oxf.util.PipelineUtils;
import org.orbeon.oxf.xml.Dom4j;
import org.orbeon.oxf.xml.XMLReceiverAdapter;
import org.orbeon.oxf.xml.XPathUtils;
import org.orbeon.oxf.xml.dom4j.Dom4jUtils;
import org.orbeon.oxf.xml.dom4j.LocationData;
import java.io.File;
import java.util.Iterator;
import java.util.Map;
public class TestScriptProcessor extends ProcessorImpl {
public static final String TEST_NAMESPACE_URI = "http://www.orbeon.org/oxf/xml/schemas/test-processor";
public TestScriptProcessor() {
addInputInfo(new ProcessorInputOutputInfo(INPUT_CONFIG, TEST_NAMESPACE_URI));
}
private static class ExecutionContext {
public Processor mainProcessor;
public OutputProcessor outputProcessor;
public ExternalContext externalContext;
}
@Override
public void start(PipelineContext pipelineContext) {
// Read configuration script
Document config = readInputAsOrbeonDom(pipelineContext, INPUT_CONFIG);
// Create execution context
ExecutionContext executionContext = new ExecutionContext();
// Iterate through actions
for (Iterator i = XPathUtils.selectNodeIterator(config, "/*/*"); i.hasNext();) {
Element commandElement = (Element) i.next();
String commandName = commandElement.getName();
try {
if (commandName.equals("processor")) {
handleProcessorCommand(executionContext, commandElement);
} else if (commandName.equals("cache-value")) {
handleCacheValueCommand(executionContext, commandElement);
} else if (commandName.equals("assert")) {
handleAssertCommand(executionContext, commandElement);
} else if (commandName.equals("touch")) {
handleTouchCommand(executionContext, commandElement);
} else if (commandName.equals("wait")) {
handleWaitCommand(executionContext, commandElement);
} else if (commandName.equals("read")) {
handleReadCommand(executionContext, commandElement);
} else if (commandName.equals("run-processor")) {
handleRunCommand(executionContext, commandElement);
} else if (commandName.equals("set-request")) {
handleSetRequestCommand(executionContext, commandElement, pipelineContext);
}
} catch (Exception e) {
throw new ValidationException(e, (LocationData) commandElement.getData());
}
}
}
private void handleProcessorCommand(ExecutionContext executionContext, Element commandElement) {
Processor mainProcessor = ProcessorUtils.createProcessorWithInputs(commandElement);
mainProcessor.setLocationData((LocationData) commandElement.getData());
mainProcessor.setId("Main Test Processor");
executionContext.mainProcessor = mainProcessor;
executionContext.outputProcessor = null;
}
private void handleCacheValueCommand(ExecutionContext executionContext, Element commandElement) {
final String outputName = commandElement.attributeValue("output-name");
final String value = commandElement.attributeValue("value");
// Make sure output to read is connected
ensureOutputConnected(executionContext, outputName);
// Tell output processor to read and cache value
// Candidate for Scala withPipelineContext
final PipelineContext pipelineContext = new PipelineContext();
boolean success = false;
try {
if (executionContext.externalContext != null)
pipelineContext.setAttribute(PipelineContext.EXTERNAL_CONTEXT, executionContext.externalContext);
executionContext.mainProcessor.reset(pipelineContext);
executionContext.outputProcessor.readCacheInputWithValue(pipelineContext, outputName, value);
success = true;
} finally {
pipelineContext.destroy(success);
}
}
private void ensureOutputConnected(ExecutionContext executionContext, String outputName) {
// Create output processor if needed
if (executionContext.outputProcessor == null)
executionContext.outputProcessor = new OutputProcessor();
// Connect output if needed
Map connectedOutputs = executionContext.mainProcessor.getConnectedOutputs();
if (connectedOutputs.get(outputName) == null)
PipelineUtils.connect(executionContext.mainProcessor, outputName, executionContext.outputProcessor, outputName);
}
private void handleAssertCommand(ExecutionContext executionContext, Element commandElement) {
final String outputName = commandElement.attributeValue("output-name");
final String condition = commandElement.attributeValue("condition");
final String value = commandElement.attributeValue("value");
// Make sure output to read is connected
if (outputName != null)
ensureOutputConnected(executionContext, outputName);
// Candidate for Scala withPipelineContext
final PipelineContext pipelineContext = new PipelineContext();
boolean success = false;
try {
if (executionContext.externalContext != null)
pipelineContext.setAttribute(PipelineContext.EXTERNAL_CONTEXT, executionContext.externalContext);
executionContext.mainProcessor.reset(pipelineContext);
if (condition.equals("output-cached")) {
if (!executionContext.outputProcessor.isInputInCache(pipelineContext, outputName))
throw new OXFException("Assertion failed: output '" + outputName + "' is not cached, but was expected to be.");
} else if (condition.equals("output-not-cached")) {
if (executionContext.outputProcessor.isInputInCache(pipelineContext, outputName))
throw new OXFException("Assertion failed: output '" + outputName + "' is cached, but was expected not to be.");
} else if (condition.equals("output-cacheable")) {
if (executionContext.outputProcessor.getInputKeyValidity(pipelineContext, outputName) == null)
throw new OXFException("Assertion failed: output '" + outputName + "' is not cacheable, but was expected to be.");
} else if (condition.equals("output-not-cacheable")) {
if (executionContext.outputProcessor.getInputKeyValidity(pipelineContext, outputName) != null)
throw new OXFException("Assertion failed: output '" + outputName + "' is cacheable, but was expected not to be.");
} else if (condition.equals("cached-value-equal")) {
Object result = executionContext.outputProcessor.getCachedValue(pipelineContext, outputName);
if (!value.equals(result))
throw new OXFException("Assertion failed: output '" + outputName + "' caches '" + result + " ', but expected '" + value + "'.");
} else if (condition.equals("cached-value-not-equal")) {
Object result = executionContext.outputProcessor.getCachedValue(pipelineContext, outputName);
if (value.equals(result))
throw new OXFException("Assertion failed: output '" + outputName + "' caches '" + result + " ', but was expected to be different.");
} else if (condition.equals("output-equals")) {
final Document actualDocument = executionContext.outputProcessor.readInputAsOrbeonDom(pipelineContext, outputName);
final Document expectedDocument = ProcessorUtils.createDocumentFromEmbeddedOrHref(commandElement, XPathUtils.selectStringValue(commandElement, "@href"));
if (!Dom4j.compareDocumentsIgnoreNamespacesInScopeCollapse(actualDocument, expectedDocument))
throw new OXFException("Assertion failed: output '" + outputName + "' got '" + Dom4jUtils.domToCompactString(actualDocument)
+ " ', but expected '" + Dom4jUtils.domToCompactString(expectedDocument) + "'.");
} else {
throw new IllegalArgumentException("Not implemented yet.");
}
success = true;
} finally {
pipelineContext.destroy(success);
}
}
private void handleTouchCommand(ExecutionContext executionContext, Element commandElement) {
String urlAttributeValue = commandElement.attributeValue("url");
File file = JavaProcessor.getFileFromURL(urlAttributeValue, (LocationData) commandElement.getData());
boolean result = file.setLastModified(System.currentTimeMillis());
if (!result)
throw new OXFException("Setting last modified date on file '" + file.toString() + "' failed.");
}
private void handleWaitCommand(ExecutionContext executionContext, Element commandElement) {
String delay = commandElement.attributeValue("delay");
try {
Thread.sleep(Long.parseLong(delay));
} catch (InterruptedException e) {
throw new OXFException(e);
}
}
private void handleReadCommand(ExecutionContext executionContext, Element commandElement) {
throw new IllegalArgumentException("Not implemented yet.");
}
private void handleRunCommand(ExecutionContext executionContext, Element commandElement) {
// Candidate for Scala withPipelineContext
final PipelineContext pipelineContext = new PipelineContext();
boolean success = false;
try {
if (executionContext.externalContext != null)
pipelineContext.setAttribute(PipelineContext.EXTERNAL_CONTEXT, executionContext.externalContext);
executionContext.mainProcessor.reset(pipelineContext);
executionContext.mainProcessor.start(pipelineContext);
success = true;
} finally {
pipelineContext.destroy(success);
}
}
private void handleSetRequestCommand(ExecutionContext executionContext, Element commandElement, PipelineContext pipelineContext) {
// Build request document
final Document requestDocument = ProcessorUtils.createDocumentFromEmbeddedOrHref(commandElement, XPathUtils.selectStringValue(commandElement, "@href"));
// Create external context
executionContext.externalContext = new TestExternalContext(pipelineContext, requestDocument);
}
private static class OutputProcessor extends ProcessorImpl {
@Override
public void start(PipelineContext pipelineContext) {}
public void readCacheInputWithValue(PipelineContext pipelineContext, String inputName, final String value) {
final ProcessorInput input = getInputByName(inputName);
// As for KeyValidity as does ProcessorImpl.readCacheInputAsObject()
KeyValidity keyValidity = getInputKeyValidity(pipelineContext, input);
// Read input in every case (we ignore the content)
readInputAsSAX(pipelineContext, input, new XMLReceiverAdapter());
// Cache result if possible, asking again for KeyValidity if needed
if (keyValidity == null)
keyValidity = getInputKeyValidity(pipelineContext, input);
if (keyValidity == null)
throw new OXFException("Cannot cache value '" + value + "' from output '" + inputName + "'.");
ObjectCache.instance().add(keyValidity.key, keyValidity.validity, value);
}
@Override
public boolean isInputInCache(PipelineContext pipelineContext, String inputName) {
return super.isInputInCache(pipelineContext, inputName);
}
@Override
public KeyValidity getInputKeyValidity(PipelineContext context, String inputName) {
return super.getInputKeyValidity(context, inputName);
}
public Object getCachedValue(PipelineContext pipelineContext, String inputName) {
ProcessorInput input = getInputByName(inputName);
KeyValidity keyValidity = getInputKeyValidity(pipelineContext, input);
if (keyValidity == null)
return null;
return ObjectCache.instance().findValid(keyValidity.key, keyValidity.validity);
}
@Override
public Document readInputAsOrbeonDom(PipelineContext context, String inputName) {
return super.readInputAsOrbeonDom(context, inputName);
}
}
}