/** * 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.scope; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.xml.Marshaller; import org.orbeon.dom.Document; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.externalcontext.ExternalContext; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.processor.*; import org.orbeon.oxf.processor.impl.DigestState; import org.orbeon.oxf.processor.impl.DigestTransformerOutputImpl; import org.orbeon.oxf.xml.*; import org.orbeon.oxf.xml.dom4j.Dom4jUtils; import org.orbeon.oxf.xml.dom4j.LocationSAXWriter; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.ParserAdapter; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import java.io.IOException; import java.io.StringReader; public class ScopeGenerator extends ScopeProcessorBase { private static SAXStore nullDocumentSAXStore; public static final String INPUT_MAPPING = "mapping"; public ScopeGenerator() { addInputInfo(new ProcessorInputOutputInfo(INPUT_CONFIG, ScopeProcessorBase.ScopeConfigNamespaceUri())); addInputInfo(new ProcessorInputOutputInfo(INPUT_MAPPING)); // optional addOutputInfo(new ProcessorInputOutputInfo(OUTPUT_DATA)); } public ProcessorOutput createOutput(String name) { final ProcessorOutput output = new DigestTransformerOutputImpl(ScopeGenerator.this, name) { public void readImpl(PipelineContext pipelineContext, final XMLReceiver xmlReceiver) { try { State state = (State) getFilledOutState(pipelineContext); state.saxStore.replay(xmlReceiver); } catch (SAXException e) { throw new OXFException(e); } } protected byte[] computeDigest(PipelineContext pipelineContext, DigestState digestState) { if (digestState.digest == null) { fillOutState(pipelineContext, digestState); } return digestState.digest; } protected boolean fillOutState(PipelineContext pipelineContext, DigestState digestState) { try { State state = (State) digestState; if (state.saxStore == null) { ScopeProcessorBase.ContextConfig config = readConfig(pipelineContext); // Get value from context ExternalContext externalContext = (ExternalContext) pipelineContext.getAttribute(PipelineContext.EXTERNAL_CONTEXT); if (externalContext == null) throw new OXFException("Missing external context"); Object value = config.javaIsRequestScope() ? externalContext.getRequest().getAttributesMap().get(config.key()) : config.javaIsSessionScope() ? ((config.sessionScope() == null) ? externalContext.getSession(true).javaGetAttribute(config.key()) : null // XXX TODO externalContext.getSession(true).javaGetAttribute(config.key(), config.sessionScope()) ) : config.javaIsApplicationScope() ? externalContext.getWebAppContext().getAttributesMap().get(config.key()) : null; if (value != null) { if (value instanceof ScopeStore) { // Use the stored key/validity as internal key/validity final ScopeStore contextStore = (ScopeStore) value; if (!config.testIgnoreStoredKeyValidity()) { // Regular case state.key = contextStore.getKey(); state.validity = contextStore.getValidity(); } else { // Special test mode (will use digest) state.key = null; state.validity = null; } // Just get SAXStore from ScopeStore state.saxStore = contextStore.getSaxStore(); } else { // Get mappings if present final Mapping mapping; if (getConnectedInputs().get(INPUT_MAPPING) == null) { mapping = new Mapping(); mapping.loadMapping(new InputSource(new StringReader("<mapping/>"))); } else { mapping = readMapping(pipelineContext); } state.saxStore = getSAXStore(value, mapping, config.isTextPlain() ? ScopeProcessorBase.TextPlain() : null, config.key()); } } else { // Store empty document if (nullDocumentSAXStore == null) { nullDocumentSAXStore = new SAXStore(); SAXUtils.streamNullDocument(nullDocumentSAXStore); } state.saxStore = nullDocumentSAXStore; } // Compute digest of the SAX Store DigestContentHandler digester = new DigestContentHandler(); state.saxStore.replay(digester); state.digest = digester.getResult(); } return true; } catch (Exception e) { throw new OXFException(e); } } }; addOutput(OUTPUT_DATA, output); return output; } public static SAXStore getSAXStore(Object value, Mapping mapping, String contentType, String key) throws SAXException, TransformerException, IOException, MappingException { if (ScopeProcessorBase.TextPlain().equals(contentType)) { final SAXStore result = new SAXStore(); if (value instanceof String) { // Creating a stream from the String! Better to extend the ProcessorUtils class to support String or StringReader or something... BinaryTextSupport.readText((String) value, result, contentType, System.currentTimeMillis()); } else { logger.error("Content-type: " + ScopeProcessorBase.TextPlain() + " not applicable for key: " + key); SAXUtils.streamNullDocument(result); } return result; } else { return getSAXStore(value, mapping); } } public static SAXStore getSAXStore(Object value, Mapping mapping) throws SAXException, TransformerException, IOException, MappingException { final SAXStore resultStore; if (value instanceof ScopeStore) { final ScopeStore contextStore = (ScopeStore) value; resultStore = contextStore.getSaxStore(); } else { if (value instanceof SAXStore) { resultStore = (SAXStore) value; } else { // Write "foreign" object to new SAX store resultStore = new SAXStore(); if (value instanceof Document) { // dom4j document final LocationSAXWriter saxWriter = new LocationSAXWriter(); saxWriter.setContentHandler(resultStore); saxWriter.setLexicalHandler(resultStore); saxWriter.write((Document) value); } else if (value instanceof org.w3c.dom.Document) { // W3C DOM document TransformerUtils.sourceToSAX(new DOMSource((org.w3c.dom.Document) value), resultStore); } else if (value instanceof String) { // Consider the String containing a document to parse XMLParsing.stringToSAX((String) value, "", resultStore, XMLParsing.ParserConfiguration.PLAIN, true); } else { // Consider the object a JavaBean readBean(value, mapping, resultStore); } } } return resultStore; } protected static void readBean(Object bean, Mapping mapping, ContentHandler contentHandler) { try { contentHandler.startDocument(); // Initialize Castor ParserAdapter adapter = new ParserAdapter(XMLParsing.newSAXParser(XMLParsing.ParserConfiguration.PLAIN).getParser()); adapter.setContentHandler(contentHandler); Marshaller marshaller = new Marshaller(adapter); marshaller.setMarshalAsDocument(false); marshaller.setMapping(mapping); // Serialize with Castor marshaller.marshal(bean); contentHandler.endDocument(); } catch (Exception e) { throw new OXFException(e); } } protected Mapping readMapping(PipelineContext pipelineContext) { return readCacheInputAsObject(pipelineContext, getInputByName(INPUT_MAPPING), new CacheableInputReader<Mapping>() { public Mapping read(PipelineContext context, ProcessorInput input) { try { Document mappingDocument = readInputAsOrbeonDom(context, input); Mapping mapping = new Mapping(); mapping.loadMapping(new InputSource(new StringReader(Dom4jUtils.domToString(mappingDocument)))); return mapping; } catch (Exception e) { throw new OXFException(e); } } }); } public void reset(PipelineContext context) { setState(context, new State()); } private static class State extends DigestState { public SAXStore saxStore; } }