/*
* 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 java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import junit.framework.JUnit4TestAdapter;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.internal.soa.esb.util.XMLHelper;
import org.jboss.soa.esb.ConfigurationException;
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.helpers.ConfigTree;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.format.MessageFactory;
import org.junit.Ignore;
import org.junit.Test;
import org.xml.sax.SAXException;
/**
* Unit test for {@link XsltAction}.
*
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
*/
public class XsltActionUnitTest
{
private static final String PACKAGE_PATH = "/org/jboss/soa/esb/actions/transformation/xslt";
private static final String TEST_XSL_1 = PACKAGE_PATH + "/test1.xsl";
private static final String TEST_XSL_2 = PACKAGE_PATH + "/test2.xsl";
private static final String TEST_XSD_2 = PACKAGE_PATH + "/test2.xsd";
private static final String TEST_XML_1 = PACKAGE_PATH + "/example1.xml";
private static final String MALFORMED_XML_1 = PACKAGE_PATH + "/malformed1.xml";
private static final String DTD_VALID_XML_1 = PACKAGE_PATH + "/dtd-valid1.xml";
private static final String DTD_INVALID_XML_1 = PACKAGE_PATH + "/dtd-invalid1.xml";
@Test (expected = ConfigurationException.class)
public void shouldThrowIfNoTemplateIsConfigured() throws ConfigurationException
{
new XsltAction(new ConfigTree("xslttest"));
}
@Test public void processXsltString() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().templateFile(TEST_XSL_1).resultType(ResultType.STRING).build());
action.initialise();
final String xml = StreamUtils.getResourceAsString(TEST_XML_1, "UTF-8");
final Message message = MessageFactory.getInstance().getMessage();
message.getBody().add(xml);
Message processed = action.process(message);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
@Test public void extractFeatures() throws ConfigurationException, ActionProcessingException, ActionLifecycleException, SAXException, IOException
{
ConfigBuilder confBuilder = new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1);
confBuilder.feature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
ConfigTree config = confBuilder.build();
final XsltAction action = new XsltAction(config);
action.initialise();
final Map<String, Boolean> features = action.getTranformerConfig().getFeatures();
assertNotNull(features);
assertEquals(1, features.size());
assertTrue(features.containsKey(XMLConstants.FEATURE_SECURE_PROCESSING));
assertEquals(true, features.get(XMLConstants.FEATURE_SECURE_PROCESSING));
}
@Test public void extractAttributes() throws ConfigurationException, ActionProcessingException, ActionLifecycleException, SAXException, IOException
{
ConfigBuilder confBuilder = new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1);
confBuilder.attribute("att1", "value1").attribute("att2", "value2");
ConfigTree config = confBuilder.build();
final XsltAction action = new XsltAction(config);
final Map<String, Object> attributes = action.getTranformerConfig().getAttributes();
assertNotNull(attributes);
assertEquals(2, attributes.size());
assertTrue(attributes.containsKey("att1"));
assertTrue(attributes.containsKey("att2"));
assertEquals("value1", attributes.get("att1"));
assertEquals("value2", attributes.get("att2"));
}
@Test public void createURIResolver() throws ConfigurationException, ActionProcessingException, ActionLifecycleException, SAXException, IOException
{
ConfigBuilder confBuilder = new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1);
confBuilder.uriResolver(MockUriResolver.class.getName());
ConfigTree config = confBuilder.build();
final XsltAction action = new XsltAction(config);
action.initialise();
URIResolver resolver = action.getTranformerConfig().getUriResolver();
assertNotNull(resolver);
assertEquals(MockUriResolver.class.getName(), resolver.getClass().getName());
}
@Test public void processFileSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final URL resource = getClass().getResource(TEST_XML_1);
final File xmlFile = new File(resource.getFile());
message.getBody().add(xmlFile);
final Message processed = action.process(message);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
@Test (expected = ActionProcessingException.class)
public void processMalformedFileSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final URL resource = getClass().getResource(MALFORMED_XML_1);
final File xmlFile = new File(resource.getFile());
message.getBody().add(xmlFile);
@SuppressWarnings("unused")
final Message processed = action.process(message);
fail("shouldn't reach");
}
@Test
public void processDtdValidFileSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_2).validation(true).schemaLanguage(XMLConstants.XML_DTD_NS_URI).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final URL resource = getClass().getResource(DTD_VALID_XML_1);
final File xmlFile = new File(resource.getFile());
message.getBody().add(xmlFile);
final Message processed = action.process(message);
assertEquals("Output Comparison","Title;artist#Empire Burlesque;Bob Dylan", (String) processed.getBody().get());
}
@Test (expected = ActionProcessingException.class)
public void processDtdInvalidFileSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_2).validation(true).schemaLanguage(XMLConstants.XML_DTD_NS_URI).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final URL resource = getClass().getResource(DTD_INVALID_XML_1);
final File xmlFile = new File(resource.getFile());
message.getBody().add(xmlFile);
@SuppressWarnings("unused")
final Message processed = action.process(message);
fail("shouldn't reach");
}
@Test
public void processXsdValidStringSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_2).validation(true).schemaLanguage(XMLConstants.W3C_XML_SCHEMA_NS_URI).schemaFile(TEST_XSD_2).build());
action.initialise();
final URL resource = getClass().getResource(TEST_XSD_2);
final String schemaLocation = new File(resource.getFile()).getAbsolutePath();
final Message message = MessageFactory.getInstance().getMessage();
final String xml = "<catalog xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"" + schemaLocation + "\"><cd><title>Empire Burlesque</title><artist>Bob Dylan</artist><country>USA</country><company>Columbia</company><price>10.90</price><year>1985</year></cd></catalog>";
message.getBody().add(xml);
final Message processed = action.process(message);
assertEquals("Output Comparison","Title;artist#Empire Burlesque;Bob Dylan", (String) processed.getBody().get());
}
@Test (expected = ActionProcessingException.class)
public void processXsdInvalidStringSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_2).validation(true).schemaLanguage(XMLConstants.W3C_XML_SCHEMA_NS_URI).schemaFile(TEST_XSD_2).build());
action.initialise();
final URL resource = getClass().getResource(TEST_XSD_2);
final String schemaLocation = new File(resource.getFile()).getAbsolutePath();
final Message message = MessageFactory.getInstance().getMessage();
final String xml = "<catalog xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"" + schemaLocation + "\"><tape><title>Empire Burlesque</title><artist>Bob Dylan</artist><country>USA</country><company>Columbia</company><price>10.90</price><year>1985</year></tape></catalog>";
message.getBody().add(xml);
@SuppressWarnings("unused")
final Message processed = action.process(message);
fail("shouldn't reach");
}
@Test public void processStringSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final String xml = "<a><b><c><b></b></c></b></a>";
message.getBody().add(xml);
final Message processed = action.process(message);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
@Test public void processInputStreamSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final InputStream inputStream = getClass().getResourceAsStream(TEST_XML_1);
message.getBody().add(inputStream);
final Message processed = action.process(message);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
@Test public void processReaderSource() throws Exception
{
processOneReaderSource();
}
@Test public void processMultipleReaderSource() throws Exception
{
for (int i = 0; i < 100; i++)
{
processOneReaderSource();
}
}
@Test public void getMaxThreadsFromParentConfigTree() throws Exception
{
final int expectedMaxThreads = 2;
final ConfigTree configTree = new ConfigBuilder(expectedMaxThreads).resultType(ResultType.STRING).templateFile(TEST_XSL_1).build();
final XsltAction action = new XsltAction(configTree);
final int actualMaxThreads = action.getMaxThreadsFromParentConfigTree(configTree);
assertEquals(expectedMaxThreads, actualMaxThreads);
}
@Test (expected = ConfigurationException.class)
public void shouldThrowIfMaxThreadsIsInvalid() throws Exception
{
final int expectedMaxThreads = -1;
final ConfigTree configTree = new ConfigBuilder(expectedMaxThreads).resultType(ResultType.STRING).templateFile(TEST_XSL_1).build();
final XsltAction action = new XsltAction(configTree);
action.initialise();
}
/**
* This method tries to make sure that transformers in the pool do not interfer with each other.
* Only one instance of the XsltAction is created and it will have a pool of Transformer instance.
*/
@Test public void processUsingPooledTransformers() throws Exception
{
final int poolSize = 8;
final XsltAction action = new XsltAction(new ConfigBuilder(poolSize).resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final int nrOfTransformers = action.getNumberOfPooledTransfomers();
assertEquals(poolSize, nrOfTransformers);
final String validXml = "<a><b><c><b></b></c></b></a>";
final String inValidXml = "<a>";
for (int i = 100; --i >= 0 ;)
{
new Thread(new ProcessRunnable(validXml, action)).start();
new Thread(new ProcessRunnable(inValidXml, action, true)).start();
}
}
private static class ProcessRunnable implements Runnable
{
private final String xml;
private final boolean ignoreException;
private final XsltAction action;
public ProcessRunnable (final String xml, XsltAction action)
{
this(xml, action, false);
}
public ProcessRunnable (final String xml, XsltAction action, boolean ignoreException)
{
this.xml = xml;
this.action = action;
this.ignoreException = ignoreException;
}
public void run ()
{
final Message message = MessageFactory.getInstance().getMessage();
message.getBody().add(xml);
Message processed;
try
{
processed = action.process(message);
assertTrue(XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
catch (final Exception e)
{
if (ignoreException == false)
{
e.printStackTrace();
fail(e.getMessage());
}
}
}
}
@Test
@Ignore
public void performance() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final MessageFactory messageFactory = MessageFactory.getInstance();
Message processed = null;
// warm up
for (int i = 0; i < 50000; i++)
{
final Message message = messageFactory.getMessage();
final String xml = "<a><b><c><b></b></c></b></a>";
message.getBody().add(xml);
processed = action.process(message);
}
final int iterations = 1000000;
final long start = System.nanoTime();
for (int i = 0; i < iterations; i++)
{
final Message message = messageFactory.getMessage();
final String xml = "<a><b><c><b></b></c></b></a>";
message.getBody().add(xml);
processed = action.process(message);
}
final long duration = System.nanoTime() - start;
System.out.println(iterations + " took : " + NANOSECONDS.toSeconds(duration) + "s");
assertTrue(XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
private void processOneReaderSource() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.STRING).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final String xml = "<a><b><c><b></b></c></b></a>";
final StreamSource streamSource = new StreamSource(new StringReader(xml));
message.getBody().add(streamSource);
final Message processed = action.process(message);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", (String) processed.getBody().get()));
}
@Test public void processSourceResult() throws Exception
{
final XsltAction action = new XsltAction(new ConfigBuilder().resultType(ResultType.SOURCERESULT).templateFile(TEST_XSL_1).build());
action.initialise();
final Message message = MessageFactory.getInstance().getMessage();
final String xml = "<a><b><c><b></b></c></b></a>";
final StreamSource source = new StreamSource(new StringReader(xml));
final StringWriter stringWriter = new StringWriter();
final StreamResult result = new StreamResult(stringWriter);
final SourceResult sourceResult = new SourceResult(source, result);
message.getBody().add(sourceResult);
final Message processed = action.process(message);
Object object = processed.getBody().get();
assertTrue(object instanceof StreamResult);
assertTrue("XML Comparison", XMLHelper.compareXMLContent("<xxx/>", ((StreamResult) processed.getBody().get()).getWriter().toString()));
}
public static class MockUriResolver implements URIResolver
{
public Source resolve(String href, String base) throws TransformerException
{
return null;
}
}
public static junit.framework.Test suite()
{
return new JUnit4TestAdapter(XsltActionUnitTest.class);
}
private static class ConfigBuilder
{
private ConfigTree config;
public ConfigBuilder()
{
this(1);
}
public ConfigBuilder(final int maxThreads)
{
ConfigTree parent = new ConfigTree("parent");
parent.setAttribute(ListenerTagNames.MAX_THREADS_TAG, Integer.toString(maxThreads));
config = new ConfigTree("xsltUnitTest", parent);
}
public ConfigBuilder templateFile(final String xslt)
{
config.setAttribute("templateFile", xslt);
return this;
}
public ConfigBuilder validation(final boolean validation)
{
config.setAttribute("validation", String.valueOf(validation));
return this;
}
public ConfigBuilder schemaLanguage(final String schemaLanguage)
{
config.setAttribute("schemaLanguage", schemaLanguage);
return this;
}
public ConfigBuilder schemaFile(final String schemaFile)
{
config.setAttribute("schemaFile", schemaFile);
return this;
}
public ConfigBuilder resultType(final ResultType type)
{
config.setAttribute("resultType", type.toString());
return this;
}
public ConfigBuilder feature(final String name, final Boolean value)
{
config.setAttribute("factory.feature." + name, value.toString());
return this;
}
public ConfigBuilder attribute(final String name, final String value)
{
config.setAttribute("factory.attribute." + name, value);
return this;
}
public ConfigBuilder uriResolver(final String className)
{
config.setAttribute("uriResolver", className);
return this;
}
public ConfigTree build()
{
return config;
}
}
}