Improved XOM handling. Now (optionally) using XOMSource and XOMResult, with increased performance.

This commit is contained in:
Arjen Poutsma
2007-04-18 12:22:21 +00:00
parent 4ef7773527
commit f99573afe7
3 changed files with 186 additions and 9 deletions

View File

@@ -17,7 +17,14 @@
package org.springframework.ws.server.endpoint;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
@@ -25,9 +32,12 @@ import javax.xml.transform.stream.StreamSource;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.NodeFactory;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.converters.DOMConverter;
import org.springframework.xml.transform.StringSource;
import org.springframework.util.ClassUtils;
import org.springframework.xml.transform.TransformerObjectSupport;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
@@ -37,13 +47,96 @@ import org.xml.sax.InputSource;
* <p/>
* An <code>AbstractXomPayloadEndpoint</code> only accept one payload element. Multiple payload elements are not in
* accordance with WS-I.
* <p/>
* This class tries to use Java reflection to access some of the non-public classes of XOM
* (<code>nu.xom.xslt.XOMResult</code> and <code>nu.xom.xslt.XOMSource</code>). If these classes cannot be accessed
* because of security restrictions, a slower approach is used. You can specify whether you want to use the faster, but
* non-public reflection-based approach by calling {@link #AbstractXomPayloadEndpoint(boolean)}.
*
* @author Arjen Poutsma
* @see Element
*/
public abstract class AbstractXomPayloadEndpoint implements PayloadEndpoint {
public abstract class AbstractXomPayloadEndpoint extends TransformerObjectSupport implements PayloadEndpoint {
private Constructor xomResultConstructor;
private Method xomResultGetResultMethod;
private Constructor xomSourceConstructor;
private boolean useReflection = true;
private DocumentBuilderFactory documentBuilderFactory;
/**
* Creates a new instance of <code>AbstractXomPayloadEndpoint</code> using reflection to access faster, but
* non-public XOM classes.
*/
protected AbstractXomPayloadEndpoint() {
this(true);
}
/**
* Creates a new instance of <code>AbstractXomPayloadEndpoint</code>.
*
* @param useReflection specifies whether to use faster, but non-public XOM classes (<code>true</code>); or to use a
* converting approach (<code>false</code>)
*/
protected AbstractXomPayloadEndpoint(boolean useReflection) {
this.useReflection = useReflection;
if (useReflection) {
try {
Class xomResultClass = ClassUtils.forName("nu.xom.xslt.XOMResult");
xomResultConstructor = xomResultClass.getDeclaredConstructor(new Class[]{NodeFactory.class});
xomResultConstructor.setAccessible(true);
xomResultGetResultMethod = xomResultClass.getDeclaredMethod("getResult", new Class[0]);
xomResultGetResultMethod.setAccessible(true);
Class xomSourceClass = ClassUtils.forName("nu.xom.xslt.XOMSource");
xomSourceConstructor = xomSourceClass.getDeclaredConstructor(new Class[]{Nodes.class});
xomSourceConstructor.setAccessible(true);
}
catch (Exception e) {
this.useReflection = false;
createDocumentBuilderFactory();
}
}
}
public final Source invoke(Source request) throws Exception {
if (useReflection) {
return invokeUsingReflection(request);
}
else {
return invokeUsingTransformation(request);
}
}
private Source invokeUsingReflection(Source request) throws Exception {
try {
Transformer transformer = createTransformer();
Result xomResult = createXomResult();
transformer.transform(request, xomResult);
Element requestElement = getRequestElement(xomResult);
Element responseElement = invokeInternal(requestElement);
return responseElement != null ? createXomSource(responseElement) : null;
}
catch (IllegalAccessException ex) {
useReflection = false;
throw ex;
}
catch (InvocationTargetException ex) {
useReflection = false;
throw ex;
}
catch (InstantiationException ex) {
useReflection = false;
throw ex;
}
}
private Source invokeUsingTransformation(Source request) throws Exception {
logger.debug("Using transformations");
Element requestElement = null;
if (request instanceof DOMSource) {
requestElement = handleDomSource(request);
@@ -59,7 +152,40 @@ public abstract class AbstractXomPayloadEndpoint implements PayloadEndpoint {
"Source [" + request.getClass().getName() + "] is neither SAXSource, DOMSource, nor StreamSource");
}
Element responseElement = invokeInternal(requestElement);
return responseElement != null ? new StringSource(responseElement.toXML()) : null;
if (responseElement != null) {
if (documentBuilderFactory == null) {
createDocumentBuilderFactory();
}
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document responseDocument = new Document(responseElement);
org.w3c.dom.Document w3cDocument =
DOMConverter.convert(responseDocument, documentBuilder.getDOMImplementation());
return new DOMSource(w3cDocument);
}
else {
return null;
}
}
private Result createXomResult() throws IllegalAccessException, InvocationTargetException, InstantiationException {
return (Result) xomResultConstructor.newInstance(new Object[]{new NodeFactory()});
}
private Element getRequestElement(Result xomResult) throws IllegalAccessException, InvocationTargetException {
Nodes result = (Nodes) xomResultGetResultMethod.invoke(xomResult, new Object[0]);
if (result.size() == 0) {
return null;
}
else {
return (Element) result.get(0);
}
}
private Source createXomSource(Element responseElement)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
Nodes nodes = new Nodes(responseElement);
return (Source) xomSourceConstructor.newInstance(new Object[]{nodes});
}
private Element handleStreamSource(Source request) throws ParsingException, IOException {
@@ -109,6 +235,11 @@ public abstract class AbstractXomPayloadEndpoint implements PayloadEndpoint {
return DOMConverter.convert(w3cElement);
}
private void createDocumentBuilderFactory() {
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
}
/**
* Template method. Subclasses must implement this. Offers the request payload as a XOM <code>Element</code>, and
* allows subclasses to return a response <code>Element</code>.
@@ -118,4 +249,5 @@ public abstract class AbstractXomPayloadEndpoint implements PayloadEndpoint {
*/
protected abstract Element invokeInternal(Element requestElement) throws Exception;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006 the original author or authors.
* Copyright 2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,10 @@ package org.springframework.ws.server.endpoint;
import nu.xom.Element;
public class XomPayloadEndpointTest extends AbstractPayloadEndpointTestCase {
public class NonReflectiveXomPayloadEndpointTest extends AbstractPayloadEndpointTestCase {
protected PayloadEndpoint createNoResponseEndpoint() throws Exception {
return new AbstractXomPayloadEndpoint() {
return new AbstractXomPayloadEndpoint(false) {
protected Element invokeInternal(Element requestElement) throws Exception {
return null;
@@ -30,7 +30,7 @@ public class XomPayloadEndpointTest extends AbstractPayloadEndpointTestCase {
}
protected PayloadEndpoint createResponseEndpoint() throws Exception {
return new AbstractXomPayloadEndpoint() {
return new AbstractXomPayloadEndpoint(false) {
protected Element invokeInternal(Element requestElement) throws Exception {
assertNotNull("No requestElement passed", requestElement);
@@ -42,10 +42,10 @@ public class XomPayloadEndpointTest extends AbstractPayloadEndpointTestCase {
}
public void testStaxSourceEventReader() throws Exception {
// Unfortutately, XOM does not support these, hence the override here
// overriden, because XOM doesn not support it
}
public void testStaxSourceStreamReader() throws Exception {
// Unfortutately, XOM does not support these, hence the override here
// overriden, because XOM doesn not support it
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2006 the original author or authors.
*
* 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.springframework.ws.server.endpoint;
import nu.xom.Element;
public class ReflectiveXomPayloadEndpointTest extends AbstractPayloadEndpointTestCase {
protected PayloadEndpoint createNoResponseEndpoint() throws Exception {
return new AbstractXomPayloadEndpoint(true) {
protected Element invokeInternal(Element requestElement) throws Exception {
return null;
}
};
}
protected PayloadEndpoint createResponseEndpoint() throws Exception {
return new AbstractXomPayloadEndpoint(true) {
protected Element invokeInternal(Element requestElement) throws Exception {
assertNotNull("No requestElement passed", requestElement);
assertEquals("Invalid request element", REQUEST_ELEMENT, requestElement.getLocalName());
assertEquals("Invalid request element", NAMESPACE_URI, requestElement.getNamespaceURI());
return new Element(RESPONSE_ELEMENT, NAMESPACE_URI);
}
};
}
}