/** * Copyright 2011 meltmedia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xchain.namespaces.sax; import java.util.LinkedList; import javax.xml.transform.Result; import javax.xml.transform.URIResolver; import javax.xml.transform.ErrorListener; import javax.xml.transform.TransformerException; import javax.xml.transform.SourceLocator; import org.apache.commons.jxpath.JXPathContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.XMLReader; import org.xml.sax.InputSource; import org.xml.sax.EntityResolver; import org.xml.sax.helpers.XMLReaderFactory; import org.xchain.impl.ChainImpl; import org.xchain.framework.lifecycle.XmlFactoryLifecycle; import org.xchain.framework.sax.Pipeline; import org.xchain.framework.sax.CompositeStage; import org.xchain.framework.sax.UrlFactoryEntityResolver; import org.xchain.framework.net.UrlFactoryUriResolver; import org.xchain.framework.net.DependencyTracker; import org.xchain.annotations.Attribute; import org.xchain.annotations.AttributeType; import org.xchain.annotations.Element; /** * <p>The <sax:pipeline> command creates a sax pipeline, and then executes it. A pipeline should start with * a source, then be followed by zero or more transformations, followed by a result.</p> * * <code class="source"> * <sax:pipeline xmlns:sax="http://www.xchain.org/sax/1.0"> * ... * </sax:pipeline> * </code> * * @author Mike Moulton * @author Christian Trimble * @author Devon Tackett * @author Josh Kennedy */ @Element(localName="pipeline") public abstract class PipelineCommand extends ChainImpl { /** The log for this class. */ private static Logger log = LoggerFactory.getLogger( PipelineCommand.class ); /** * <p>The ThreadLocal stack of composite stages. The current pipeline being built is on the top of the stack.</p> */ private static ThreadLocal<LinkedList<PipelineConfig>> pipelineConfigStackThreadLocal = new ThreadLocal<LinkedList<PipelineConfig>>(); /** * <p>Returns the composite stage stack for this thread.</p> * @returns the current stack of pipeline configuration objects for this thread. */ public static LinkedList<PipelineConfig> getPipelineConfigStack() { LinkedList<PipelineConfig> pipelineConfigStack = pipelineConfigStackThreadLocal.get(); if( pipelineConfigStack == null ) { pipelineConfigStack = new LinkedList<PipelineConfig>(); pipelineConfigStackThreadLocal.set(pipelineConfigStack); } return pipelineConfigStack; } /** * <p>Pushes a pipeline configuration object on the stack.</p> * @param pipelineConfig the pipeline configuration object to push. */ public static void pushPipelineConfig( PipelineConfig pipelineConfig ) { getPipelineConfigStack().addFirst(pipelineConfig); } /** * <p>Pops a top pipeline configuration object off of the stack.</p> * @return the pipeline configuration object popped off of the stack. */ public static PipelineConfig popPipelineConfig() { return getPipelineConfigStack().removeFirst(); } /** * <p>Returns the top item on the pipeline configuration stack, without removing it from the stack.</p> * @return the pipeline configuration object current on the top of the stack. */ public static PipelineConfig getPipelineConfig() { return getPipelineConfigStack().getFirst(); } /** * <p>The uri resolver to use when building this pipeline.</p> * @param context the JXPathContext to evaluate against. */ @Attribute(localName="uri-resolver", type=AttributeType.JXPATH_VALUE) public abstract URIResolver getUriResolver( JXPathContext context ); public abstract boolean hasUriResolver(); public URIResolver getUriResolverSafe( JXPathContext context ) { URIResolver uriResolver = hasUriResolver()?getUriResolver(context):null; if( uriResolver == null ) { if (log.isDebugEnabled()) { log.debug("The uri resolver could not not be found in context. The default uri resolver will be created."); } uriResolver = DependencyTracker.getInstance().createDependencyUriResolver(new UrlFactoryUriResolver()); } return uriResolver; } /** * Constructs a new PipelineCommand object. */ public PipelineCommand() { } /** * <p>Constructs a new Pipeline and places it into the context at path. Nested commands can be used to * add transforms to the pipeline.</p> * @param context the JXPathContext to evaluate against. */ public boolean execute( JXPathContext context ) throws Exception { PipelineConfig config = new PipelineConfig(); // create the dependency uri resolver. try { pushPipelineConfig(config); config.setCompositeStage(new CompositeStage()); config.setUriResolver(getUriResolverSafe(context)); config.setEntityResolver(new UrlFactoryEntityResolver()); config.setErrorListener(new ErrorListener() { public void error( TransformerException e ) { SourceLocator sourceLocator = e.getLocator(); StringBuffer message = new StringBuffer(); if( sourceLocator != null ) { message.append(sourceLocator.getSystemId()+":"+sourceLocator.getLineNumber()+":"+sourceLocator.getColumnNumber()+" - "); } message.append(e.getMessage()); log.error(message.toString(), e); throw new RuntimeException(message.toString(), e); } public void fatalError( TransformerException e ) { SourceLocator sourceLocator = e.getLocator(); StringBuffer message = new StringBuffer(); if( sourceLocator != null ) { message.append(sourceLocator.getSystemId()+":"+sourceLocator.getLineNumber()+":"+sourceLocator.getColumnNumber()+" - "); } message.append(e.getMessage()); log.error(message.toString(), e); throw new RuntimeException(message.toString(), e); } public void warning( TransformerException e ) { log.warn("SAX Parser Warning.", e); } }); // call all of the child commands. super.execute(context); // put a pipeline in the context to add to. Pipeline pipeline = new Pipeline(); // set the composite stage. pipeline.setCompositeStage(config.getCompositeStage()); if( config.getXmlReader() == null ) { // set the xml reader on the pipeline. pipeline.setXmlReader(XmlFactoryLifecycle.newXmlReader()); } else { pipeline.setXmlReader(config.getXmlReader()); } if( config.getEntityResolver() != null ) { pipeline.getXmlReader().setEntityResolver(config.getEntityResolver()); } // set the source on the pipeline. pipeline.setSource(config.getSource()); // execute the pipeline. pipeline.execute(); } finally { // reset the stage list. popPipelineConfig(); // DependencyUriResolverFactory.destroyDependencyUriResolver( uriResolver ); } // the pipeline is built and executed. return false; } /** * <p>The configuration for a pipeline that is being assembled.<p> */ public static class PipelineConfig { protected URIResolver uriResolver = null; protected ErrorListener errorListener = null; protected CompositeStage compositeStage = new CompositeStage(); protected XMLReader reader = null; protected InputSource source = null; protected Result result = null; protected EntityResolver entityResolver = null; public URIResolver getUriResolver() { return uriResolver; } public void setUriResolver( URIResolver uriResolver ) { this.uriResolver = uriResolver; } public ErrorListener getErrorListener() { return this.errorListener; } public void setErrorListener( ErrorListener errorListener ) { this.errorListener = errorListener; } public CompositeStage getCompositeStage() { return this.compositeStage; } public void setCompositeStage( CompositeStage compositeStage ) { this.compositeStage = compositeStage; } public XMLReader getXmlReader() { return this.reader; } public void setXmlReader(XMLReader reader) { this.reader = reader; } public InputSource getSource() { return this.source; } public void setSource( InputSource source ) { this.source = source; } public EntityResolver getEntityResolver() { return this.entityResolver; } public void setEntityResolver( EntityResolver entityResolver ) { this.entityResolver = entityResolver; } } }