/*
* JBoss, Home of Professional Open Source Copyright 2009, Red Hat Middleware
* LLC, and individual contributors by the @authors tag. See the copyright.txt
* in the distribution for a full listing of individual contributors.
*
* This 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 software 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* site: http://www.fsf.org.
*/
package org.jboss.soa.esb.actions.transformation.xslt;
import static org.jboss.soa.esb.listeners.ListenerTagNames.MAX_THREADS_TAG;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.AbstractActionPipelineProcessor;
import org.jboss.soa.esb.actions.ActionLifecycleException;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.transformation.xslt.ResultFactory.ResultType;
import org.jboss.soa.esb.actions.transformation.xslt.TransformerFactoryConfig.Builder;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.util.ClassUtil;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
/**
* ESB Action that performs xslt transformation.
* <p/>
* Example configuration:
* <pre>{@code
* <action name="xslt-transform" class="org.jboss.soa.esb.actions.transformation.xslt.XsltAction">
* <property name="templateFile" value="/sample.xsl"/>
* <property name="failOnWarning" value="true"/>
* <property name="validation" value="true"/>
* <property name="schemaFile" value="/sample.xsd"/>
* <property name="schemaLanguage" value="http://www.w3.org/2001/XMLSchema"/>
* <property name="resultType" value="STRING"/>
* </action>
* }<pre>
*
* <h3>Configuration Properties</h3>
* <ul>
* <li><i>templateFile</i>:
* The path to the xsl template to be used. Mandatory</li>
*
* <li><i>resultType</i>:
* This property controls the output result of the transformation.
* The following values are currently available:
* {@link ResultType.STRING} - will produce a string.
* {@link ResultType.BYTES} - will produce a byte[].
* {@link ResultType.DOM} - will produce a {@link DOMResult}.
* {@link ResultType.SAX} - will produce a {@link SAXResult}.
* </li>
* If the above does not suite your needs then you have the option of specifying both the
* Source and Result by creating {@link SourceResult} object instance. This is a simple
* object that holds both a {@link Source} and a {@link Result}.
* You need to create this object prior to calling this action and the type of {@link Result}
* returned will be the type that was used to create the {@link SourceResult}.
*
* <li><i>failOnWarning</i>:
* If true will cause a transformation warning to cause an exception to be thrown.
* If false the failure will be logged. Default is true if not specified.</li>
*
* <li><i>validation</i>:
* If true will cause an invalid source document to cause an exception to be thrown.
* If false validation will not occur, although well-formed documents are enforced.
* Default is false if not specified.<br/>
* Please refer to <b>Validation Configuration</b> chart below.</li>
*
* <li><i>schemaFile</i>:
* The input schema file (xsd) to use, located on the classpath.
* Default is null (unset) if not specified.<br/>
* Please refer to <b>Validation Configuration</b> chart below.</li>
*
* <li><i>schemaLanguage</i>:
* The input schema language to use.
* Default is null (unset) if not specified.<br/>
* Please refer to <b>Validation Configuration</b> chart below.</li>
*
* <li><i>uriResolver</i>:
* Fully qualified class name of a class that implements {@link URIResolver}.
* This will be set on the transformation factory. Optional</li>
*
* <li><i>factory.feature.*</i>:
* Factory features that will be set for the transformation factory. Optional.
* The feature name, which are fully qualified URIs will should be specified
* after the 'factory.feature.' prefix. For example:
* factory.feature.http://javax.xml.XMLConstants/feature/secure-processing</li>
*
* <li><i>factory.attribute.*</i>:
* Factory attributes that will be set for the transformation factory. Optional.
* The attribute name should be specified after the 'factory.attribute.' prefix.
* For example:
* factory.attribute.someVendorAttributename</li>
* </ul>
*
* <table border="1" cellpadding="5" cellspacing="0">
* <thead>
* <tr valign="top">
* <th>Validation Configuration</th>
* <th>XML Input</th>
* <th>Processing Outcome</th>
* </tr>
* </thead>
* <tbody>
* <tr valign="top">
* <td rowspan="3">
* <u>Default</u><p/>
* <i>nothing</i><p/>or
* <pre> <property name="validation" value="false"/></pre>
* </td>
* <td>
* <ul>
* <li><b>mal</b>formed</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li><i>pipeline continues</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li><b>in</b>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li><b>transformation may fail</b></li>
* <li><i>pipeline continues</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td rowspan="3">
* <u>DTD</u><br/>
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value="http://www.w3.org/TR/REC-xml"/></pre>
* or
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value=""/></pre>
* </td>
* <td>
* <ul>
* <li><b>mal</b>formed</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li><i>pipeline continues</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li><b>in</b>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td rowspan="3">
* <u>W3C XML Schema</u> or <u>RELAX NG</u><br/>
* <pre> <property name="validation" value="true"/></pre>
* or
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value="http://www.w3.org/2001/XMLSchema"/></pre>
* or
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value="http://relaxng.org/ns/structure/1.0"/></pre>
* </td>
* <td>
* <ul>
* <li><b>mal</b>formed</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li><i>pipeline continues</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li><b>in</b>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td rowspan="3">
* <u>W3C XML Schema</u> or <u>RELAX NG</u> <i>with included schemaFile</i><br/>
* <pre> <property name="validation" value="true"/>
* <property name="schemaFile" value="/example.xsd"/>
* </pre>
* or
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value="http://www.w3.org/2001/XMLSchema"/>
* <property name="schemaFile" value="/example.xsd"/>
* </pre>
* or
* <pre> <property name="validation" value="true"/>
* <property name="schemaLanguage" value="http://relaxng.org/ns/structure/1.0"/>
* <property name="schemaFile" value="/example.rng"/>
* </pre>
* </td>
* <td>
* <ul>
* <li><b>mal</b>formed</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li><i>pipeline continues</i></li>
* </ul>
* </td>
* </tr>
* <tr valign="top">
* <td>
* <ul>
* <li>well-formed</li>
* <li><b>in</b>valid</li>
* </ul>
* </td>
* <td>
* <ul>
* <li>error logged</li>
* <li><pre>SAXParseException causes ActionProcessingException</pre></li>
* <li><i>pipeline stops</i></li>
* </ul>
* </td>
* </tr>
* </tbody>
* </table>
*
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
* @author dward at jboss.org
* @since 4.6
*/
public class XsltAction extends AbstractActionPipelineProcessor
{
/**
* Logger instance.
*/
private static Logger log = Logger.getLogger(XsltAction.class);
/**
* Config object that holds transformation factory config options.
*/
private TransformerFactoryConfig transformerConfig;
/**
* Can be used to control whether a warning should be reported
* as an exception during transformation.
*/
private boolean failOnWarning;
/**
* Used for validation on the XML InputSource during transformation.
*/
private boolean validation;
/**
* Used for validation on the XML InputSource during transformation.
*/
private SAXParserFactory validationFactory;
/**
* Used for validation on the XML InputSource during transformation.
*/
private String schemaFile;
/**
* Used for validation on the XML InputSource during transformation.
*/
private String schemaLanguage;
/**
* The templates object.
*/
private Templates xslTemplate;
/**
* The {@link MessagePayloadProxy}.
*/
private MessagePayloadProxy payloadProxy;
/**
* The number of transformer instances to create upon initialization.
* This will be the value of the {@link ListenerTagNames#MAX_THREADS_TAG} value.
*/
private int transformerPoolSize;
/**
* A queue of Transformer instance which will be populated upon initialization.
* The size of this queue will be that of {@link #transformerPoolSize}.
*/
private ArrayBlockingQueue<Transformer> transformers;
/**
* Sole constructor that parses the passed-in {@link ConfigTree} for
* mandatory attributes and sets the fields of this instance.
*
* @param config The {@link ConfigTree} instance.
*
* @throws ConfigurationException if the mandatory attribute 'templateFile' has not been set.
*/
public XsltAction(final ConfigTree config) throws ConfigurationException
{
transformerConfig = createConfig(config);
failOnWarning = config.getBooleanAttribute("failOnWarning", true);
validation = config.getBooleanAttribute("validation", false);
schemaFile = config.getAttribute("schemaFile");
schemaLanguage = config.getAttribute("schemaLanguage");
payloadProxy = new MessagePayloadProxy(config);
transformerPoolSize = getMaxThreadsFromParentConfigTree(config);
}
/**
* Will try to extract and parse a configuration attribute named {@link ListenerTagNames#MAX_THREADS_TAG}
* from the parent of the passed-in ConfigTree instance. If no parent exists (that is if the ConfigTree
* instance passed into this method is the root), or if the property is not defined on the parent, then
* the same attribute is extracted and parsed into an integer but from the config element.
*
* @param config The {@link ConfigTree} instance.
* @return int The parsed value the {@link ListenerTagNames#MAX_THREADS_TAG} configuration attribute
* @throws ConfigurationException If it was not possible to parse the {@link ListenerTagNames#MAX_THREADS_TAG} attribute into a
* positive integer value.
*/
int getMaxThreadsFromParentConfigTree(final ConfigTree config) throws ConfigurationException
{
try
{
final ConfigTree parent = config.getParent();
String maxThreadsStr = null;
if (parent != null) {
maxThreadsStr = parent.getAttribute(MAX_THREADS_TAG);
}
if (maxThreadsStr == null) {
maxThreadsStr = config.getAttribute(MAX_THREADS_TAG, "1");
}
int maxThreads = Integer.parseInt(maxThreadsStr);
if (maxThreads <= 0)
throw new ConfigurationException(MAX_THREADS_TAG + " must be a positive integer. Was [" + maxThreads +"]");
return maxThreads;
}
catch (final NumberFormatException e)
{
throw new ConfigurationException("Could not parse " + MAX_THREADS_TAG + " to an int.", e);
}
}
/**
* Performs the xsl transformation of the message payload.
*
* @param message The ESB {@link Message} object whose payload should be transformed.
* The payload is extracted and set using contract specified by {@link MessagePayloadProxy}.
*
* @return {@link Message} The same ESB Message instance passed-in but with it payload transformed.
* @throws ActionProcessingException if an error occurs while trying to perform the transformation.
*/
public Message process(final Message message) throws ActionProcessingException
{
AssertArgument.isNotNull(message, "message");
try
{
final Object payload = getPayload(message);
final ValidationHandler validationHandler;
final Source source;
final Result result;
// If the payload is a SourceResult than use its source and result.
if (payload instanceof SourceResult)
{
validationHandler = null;
final SourceResult sourceResult = (SourceResult)payload;
source = sourceResult.getSource();
result = sourceResult.getResult();
}
else
{
validationHandler = new ValidationHandler(failOnWarning);
source = SourceFactory.getInstance().createSource(payload, validationFactory, validationHandler);
result = ResultFactory.getInstance().createResult(transformerConfig.getResultType());
}
// Perform the transformation.
transform(source, result);
// Check for validation errors.
if (validationHandler != null)
{
validationHandler.check();
}
// Get the result and set on the message object
final Object object = ResultFactory.getInstance().extractResult(result, transformerConfig.getResultType());
return setPayload(message, object);
}
catch (final TransformerConfigurationException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
catch (SAXException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
catch (ParserConfigurationException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
catch (TransformerException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
}
private void transform (final Source source, final Result result) throws TransformerException, ActionProcessingException
{
Transformer transformer = null;
try
{
transformer = transformers.take();
}
catch (final InterruptedException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
try
{
transformer.transform(source, result);
}
finally
{
try
{
transformer.reset();
transformers.put(transformer);
}
catch (final InterruptedException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
}
}
/**
* Creates the XSLTemplate.
*
* @throws ActionLifecycleException if the {@link Templates} could not be created.
*
*/
@Override
public void initialise() throws ActionLifecycleException
{
try
{
final TransformerFactory factory = TransformerFactory.newInstance();
addFeatures(transformerConfig.getFeatures(), factory);
addAttributes(transformerConfig.getAttributes(), factory);
setResolver(transformerConfig.getUriResolver(), factory);
setErrorListener(new TransformerListener(failOnWarning), factory);
createValidationFactory(factory);
xslTemplate = createTemplate(transformerConfig.getTemplateFile(), factory);
transformers = new ArrayBlockingQueue<Transformer>(transformerPoolSize);
for (int i = transformerPoolSize; --i>=0 ;)
{
transformers.add(xslTemplate.newTransformer());
}
}
catch (final TransformerConfigurationException e)
{
throw new ActionLifecycleException(e.getMessage(), e);
}
}
private void addFeatures(final Map<String, Boolean> features, final TransformerFactory factory) throws TransformerConfigurationException
{
for (Entry<String, Boolean> entry : features.entrySet())
{
factory.setFeature(entry.getKey(), entry.getValue());
}
}
private void addAttributes(final Map<String, Object> attributes, final TransformerFactory factory) throws TransformerConfigurationException
{
for (Entry<String, Object> entry : attributes.entrySet())
{
factory.setAttribute(entry.getKey(), entry.getValue());
}
}
private void setResolver(final URIResolver uriResolver, final TransformerFactory factory)
{
if (uriResolver != null)
{
factory.setURIResolver(uriResolver);
}
}
private void setErrorListener(final ErrorListener errorListener, final TransformerFactory factory)
{
factory.setErrorListener(errorListener);
}
private void createValidationFactory(final TransformerFactory factory) throws ActionLifecycleException
{
if (factory.getFeature(SAXSource.FEATURE))
{
validationFactory = SAXParserFactory.newInstance();
validationFactory.setNamespaceAware(true);
// The code above enforces well-formed XML input, as described in JBESB-3036.
// The code below conditionally enforces valid XML input, as described in JBESB-3068.
if (validation)
{
validationFactory.setValidating(true);
if (!XMLConstants.XML_DTD_NS_URI.equals(schemaLanguage) && !XMLConstants.NULL_NS_URI.equals(schemaLanguage))
{
// W3C XML Schema or RELAX NG validation
for (String name : new String[]{"http://xml.org/sax/features/validation/schema", "http://apache.org/xml/features/validation/schema"})
{
try
{
validationFactory.setFeature(name, true);
}
catch (ParserConfigurationException ignored) {}
catch (SAXNotRecognizedException ignored) {}
catch (SAXNotSupportedException ignored) {}
}
if (schemaFile != null)
{
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage != null ? schemaLanguage : XMLConstants.W3C_XML_SCHEMA_NS_URI);
try
{
Schema schema = schemaFactory.newSchema(new StreamSource(ClassUtil.getResourceAsStream(schemaFile, getClass())));
validationFactory.setSchema(schema);
}
catch (SAXException e)
{
throw new ActionLifecycleException("problem creating schema '" + schemaFile + "'", e);
}
}
}
// else { DTD validation }
}
}
else
{
log.warn("TransformerFactory does not support " + SAXSource.FEATURE);
}
}
private Templates createTemplate(final String templateFile, final TransformerFactory factory) throws ActionLifecycleException, TransformerConfigurationException
{
InputStream stream = null;
try
{
stream = StreamUtils.getResource(templateFile);
return factory.newTemplates(new StreamSource(stream));
}
catch (final ConfigurationException e)
{
throw new ActionLifecycleException(e.getMessage(), e);
}
finally
{
if (stream != null)
{
try { stream.close(); } catch (final IOException ignore) { log.error("Exception while closing stream", ignore); }
}
}
}
/**
* Parses the passed-in ESB {@link ConfigTree} and populates a {@link TransformerFactoryConfig}.
*
* @param config The ESB {@link ConfigTree}.
* @return {@link TransformerFactoryConfig}.
* @throws ConfigurationException
*/
private TransformerFactoryConfig createConfig(final ConfigTree config) throws ConfigurationException
{
final Builder builder = new TransformerFactoryConfig.Builder(config.getRequiredAttribute("templateFile"));
extractFeatures(config, builder);
extractAttributes(config, builder);
createUrlResolver(config, builder);
builder.resultType(ResultType.valueOf(config.getAttribute("resultType", ResultType.STRING.name())));
return builder.build();
}
/**
* Extracts the factory attributes and adds them to the builder.
*
* @param config The ESB {@link ConfigTree}.
* @param builder The {@link TransformerFactoryConfig.Builder}.
* @return Builder To support method chaining.
*/
void extractAttributes(final ConfigTree config, final Builder builder)
{
for (final String attrName : config.getAttributeNames())
{
int idx = attrName.indexOf("factory.attribute.");
if (idx != -1)
{
final Object value = config.getAttribute(attrName);
final String name = attrName.substring(idx + "factory.attribute.".length());
builder.attribute(name, value);
}
}
}
/**
* Extracts the 'uriResolver' attribute from the ESB {@link ConfigTree} and instantiates a class
* of that type. This class will be set on the passed-in builder.
* @param config The ESB {@link ConfigTree}.
* @param builder The {@link TransformerFactoryConfig.Builder}.
* @throws ConfigurationException If the class could not be created.
*/
void createUrlResolver(final ConfigTree config, final Builder builder) throws ConfigurationException
{
final String className = config.getAttribute("uriResolver");
if (className != null)
{
try
{
final Class<?> resolver = ClassUtil.forName(className, getClass());
URIResolver uriResolver = (URIResolver) resolver.newInstance();
builder.uriResolver(uriResolver);
}
catch (final ClassNotFoundException e)
{
throw new ConfigurationException(e.getMessage(), e);
}
catch (InstantiationException e)
{
throw new ConfigurationException(e.getMessage(), e);
}
catch (IllegalAccessException e)
{
throw new ConfigurationException(e.getMessage(), e);
}
}
}
/**
* Extracts the factory features and adds them to the builder.
*
* @param config The ESB {@link ConfigTree}.
* @param builder The {@link TransformerFactoryConfig.Builder}.
*/
void extractFeatures(final ConfigTree config, Builder builder)
{
for (final String attrName : config.getAttributeNames())
{
int idx = attrName.indexOf("factory.feature.");
if (idx != -1)
{
final String value = config.getAttribute(attrName);
final String name = attrName.substring(idx + "factory.feature.".length());
builder.feature(name, Boolean.valueOf(value));
}
}
}
private Object getPayload(final Message message) throws ActionProcessingException
{
try
{
return payloadProxy.getPayload(message);
}
catch (MessageDeliverException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
}
private Message setPayload(final Message message, final Object payload) throws ActionProcessingException
{
try
{
payloadProxy.setPayload(message, payload);
}
catch (MessageDeliverException e)
{
throw new ActionProcessingException(e.getMessage(), e);
}
return message;
}
public TransformerFactoryConfig getTranformerConfig()
{
return transformerConfig;
}
@Override
public String toString()
{
return String.format("%s templateFile=%s, validation=%b, schemaFile=%s, schemaLanguage=%s, failOnWarning=%b, features=%s, attributes=%s", getClass().getSimpleName(), transformerConfig.getTemplateFile(), validation, schemaFile, schemaLanguage, failOnWarning, transformerConfig.getFeatures(), transformerConfig.getAttributes());
}
private static class TransformerListener implements ErrorListener
{
private final boolean failOnWarning;
public TransformerListener(boolean failOnWarning)
{
this.failOnWarning = failOnWarning;
}
public void warning(TransformerException exception) throws TransformerException
{
if (failOnWarning)
{
throw exception;
}
else
{
log.warn("Transformation Warning.", exception);
}
}
public void error(TransformerException exception) throws TransformerException
{
throw exception;
}
public void fatalError(TransformerException exception) throws TransformerException
{
throw exception;
}
}
private static class ValidationHandler implements ErrorHandler
{
private final boolean failOnWarning;
private SAXParseException exception = null;
public ValidationHandler(boolean failOnWarning)
{
this.failOnWarning = failOnWarning;
}
private void check() throws SAXParseException
{
if (exception != null)
{
throw exception;
}
}
public void warning(SAXParseException exception) throws SAXException
{
if (failOnWarning)
{
this.exception = exception;
throw exception;
}
else
{
log.warn("Validation Warning.", exception);
}
}
public void error(SAXParseException exception) throws SAXException
{
this.exception = exception;
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException
{
this.exception = exception;
throw exception;
}
}
public int getNumberOfPooledTransfomers ()
{
if (transformers == null)
throw new IllegalStateException("The transformers have not been initialized yet. Please make sure that initialize has bee called prior to calling this method.");
return transformers.size();
}
}