Skip to content

Commit

Permalink
CAMEL-9097: untested XSLT Aggregation Strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
stravag committed Aug 21, 2015
1 parent f7d6de0 commit 159563c
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -17,4 +17,4 @@ components/camel-solr/data
*.epoch
.factorypath
.pmd

.metadata
@@ -0,0 +1,177 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.camel.util.toolbox;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.builder.xml.XsltBuilder;
import org.apache.camel.builder.xml.XsltUriResolver;
import org.apache.camel.component.xslt.XsltOutput;
import org.apache.camel.processor.aggregate.AggregationStrategy;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import java.io.IOException;

/**
* The XSLT Aggregation Strategy enables you to use XSL stylesheets to aggregate messages.
*
* The already aggregated payload (oldExchange) will be the input XML, the message to aggregate (newExchange)
* will be passed in a exchange property. The exchange property name defaults to <i>xmlToAggregate</i> but can be
* changed through the a header called <i>CamelPropertyName</i>.
*
* Some code bits have been copied from the {@link org.apache.camel.component.xslt.XsltEndpoint}.
*
*/
public class XsltAggregationStrategy implements AggregationStrategy {

private static final Logger LOG = LoggerFactory.getLogger(XsltAggregationStrategy.class);

private static final String PROPERTY_NAME_HEADER = "CamelPropertyName";
private static final String DEFAULT_PROPERTY_NAME = "xmlToAggregate";

private volatile XsltBuilder xslt;
private volatile URIResolver uriResolver;

private String xslFile;
private String transformerFactoryClass;
private XsltOutput output = XsltOutput.string;

/**
* Constructor.
*
* @param xslFile to use in the aggregation.
*/
public XsltAggregationStrategy(String xslFile) {
this.xslFile = xslFile;
}

@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {

// Guard against empty new exchanges
if (newExchange == null) {
return oldExchange;
}

// first aggregation
if (oldExchange == null) {
return newExchange;
}

try {

if (xslt == null) {
initializeDefaultXsltBuilder(oldExchange.getContext());
}

String propertyName = newExchange.getIn().getHeader(PROPERTY_NAME_HEADER, String.class);
propertyName = propertyName == null ? DEFAULT_PROPERTY_NAME : propertyName;

oldExchange.setProperty(propertyName, newExchange.getIn().getBody(Document.class));
xslt.process(oldExchange);

return oldExchange;

} catch (Exception e) {
oldExchange.setException(e);
}

return oldExchange;
}

public void setOutput(XsltOutput output) {
this.output = output;
}

public void setXslt(XsltBuilder xslt) {
this.xslt = xslt;
}

public void setUriResolver(URIResolver uriResolver) {
this.uriResolver = uriResolver;
}

public void setTransformerFactoryClass(String transformerFactoryClass) {
this.transformerFactoryClass = transformerFactoryClass;
}

protected void initializeDefaultXsltBuilder(CamelContext context) throws Exception {

this.xslt = context.getInjector().newInstance(XsltBuilder.class);

if (transformerFactoryClass != null) {
Class<?> factoryClass = context.getClassResolver().resolveMandatoryClass(transformerFactoryClass, XsltAggregationStrategy.class.getClassLoader());
TransformerFactory factory = (TransformerFactory) context.getInjector().newInstance(factoryClass);
xslt.getConverter().setTransformerFactory(factory);
}

if (uriResolver == null) {
uriResolver = new XsltUriResolver(context.getClassResolver(), xslFile);
}
xslt.setUriResolver(uriResolver);

xslt.setFailOnNullBody(true);
xslt.transformerCacheSize(0);
xslt.setAllowStAX(true);
xslt.setCamelContext(context);

configureOutput(xslt, output.name());
loadResource(xslFile);
}

protected void configureOutput(XsltBuilder xslt, String output) throws Exception {
if (ObjectHelper.isEmpty(output)) {
return;
}

if ("string".equalsIgnoreCase(output)) {
xslt.outputString();
} else if ("bytes".equalsIgnoreCase(output)) {
xslt.outputBytes();
} else if ("DOM".equalsIgnoreCase(output)) {
xslt.outputDOM();
} else if ("file".equalsIgnoreCase(output)) {
xslt.outputFile();
} else {
throw new IllegalArgumentException("Unknown output type: " + output);
}
}

/**
* Loads the resource.
*
* @param resourceUri the resource to load
* @throws TransformerException is thrown if error loading resource
* @throws IOException is thrown if error loading resource
*/
protected void loadResource(String resourceUri) throws TransformerException, IOException {
LOG.trace("{} loading schema resource: {}", this, resourceUri);
Source source = xslt.getUriResolver().resolve(resourceUri, null);
if (source == null) {
throw new IOException("Cannot load schema resource " + resourceUri);
} else {
xslt.setTransformerSource(source);
}
}
}
@@ -0,0 +1,53 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.camel.util.toolbox;

import org.apache.camel.ContextTestSupport;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.junit.Test;

/**
* Unit test for the {@link XsltAggregationStrategy}.
*/
public class XsltAggregationStrategyTest extends ContextTestSupport {

@Test
public void testXsltAggregation() throws InterruptedException {

MockEndpoint mock = getMockEndpoint("mock:transformed");
mock.expectedMessageCount(1);
mock.expectedBodiesReceived("<?xml version=\"1.0\" encoding=\"UTF-8\"?><item>ABC</item>");

assertMockEndpointsSatisfied();
}


protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("file:src/test/resources/org/apache/camel/util/toolbox?noop=true&antInclude=*.xml")
.aggregate(new XsltAggregationStrategy("org/apache/camel/util/toolbox/aggregate.xsl"))
.constant(true)
.completionFromBatchConsumer()
.log("after aggregate body: ${body}")
.to("mock:transformed");
}
};
}
}
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="no"/>
<xsl:strip-space elements="*"/>

<xsl:param name="xmlToAggregate"/>

<xsl:template match="/">
<item>
<xsl:value-of select="."/>
<xsl:value-of select="$xmlToAggregate"/>
</item>
</xsl:template>

</xsl:stylesheet>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>A</item>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>B</item>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>C</item>

0 comments on commit 159563c

Please sign in to comment.