/** * 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); } } }