From f99573afe7b94387f47cbd749d4569608ca49012 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 18 Apr 2007 12:22:21 +0000 Subject: [PATCH] Improved XOM handling. Now (optionally) using XOMSource and XOMResult, with increased performance. --- .../endpoint/AbstractXomPayloadEndpoint.java | 138 +++++++++++++++++- ... NonReflectiveXomPayloadEndpointTest.java} | 12 +- .../ReflectiveXomPayloadEndpointTest.java | 45 ++++++ 3 files changed, 186 insertions(+), 9 deletions(-) rename core/src/test/java/org/springframework/ws/server/endpoint/{XomPayloadEndpointTest.java => NonReflectiveXomPayloadEndpointTest.java} (80%) create mode 100644 core/src/test/java/org/springframework/ws/server/endpoint/ReflectiveXomPayloadEndpointTest.java diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractXomPayloadEndpoint.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractXomPayloadEndpoint.java index 43683d19..a087ed66 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractXomPayloadEndpoint.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractXomPayloadEndpoint.java @@ -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; *

* An AbstractXomPayloadEndpoint only accept one payload element. Multiple payload elements are not in * accordance with WS-I. + *

+ * This class tries to use Java reflection to access some of the non-public classes of XOM + * (nu.xom.xslt.XOMResult and nu.xom.xslt.XOMSource). 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 AbstractXomPayloadEndpoint using reflection to access faster, but + * non-public XOM classes. + */ + protected AbstractXomPayloadEndpoint() { + this(true); + } + + /** + * Creates a new instance of AbstractXomPayloadEndpoint. + * + * @param useReflection specifies whether to use faster, but non-public XOM classes (true); or to use a + * converting approach (false) + */ + 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 Element, and * allows subclasses to return a response Element. @@ -118,4 +249,5 @@ public abstract class AbstractXomPayloadEndpoint implements PayloadEndpoint { */ protected abstract Element invokeInternal(Element requestElement) throws Exception; + } diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/XomPayloadEndpointTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/NonReflectiveXomPayloadEndpointTest.java similarity index 80% rename from core/src/test/java/org/springframework/ws/server/endpoint/XomPayloadEndpointTest.java rename to core/src/test/java/org/springframework/ws/server/endpoint/NonReflectiveXomPayloadEndpointTest.java index c421cae1..700954a2 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/XomPayloadEndpointTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/NonReflectiveXomPayloadEndpointTest.java @@ -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 } } diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/ReflectiveXomPayloadEndpointTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/ReflectiveXomPayloadEndpointTest.java new file mode 100644 index 00000000..ea6b6d9e --- /dev/null +++ b/core/src/test/java/org/springframework/ws/server/endpoint/ReflectiveXomPayloadEndpointTest.java @@ -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); + } + }; + } + + +}