Improved XOM handling. Now (optionally) using XOMSource and XOMResult, with increased performance.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user