From bd4ef351b0c4e5a1f3d1d46e9ae892664183e2de Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 28 Apr 2010 09:18:08 +0000 Subject: [PATCH] SWS-351 - Arbitrary parameter injection for @Endpoints --- .../AbstractPayloadMethodProcessor.java | 2 +- .../method/XomPayloadMethodProcessor.java | 108 ++++++++++++++++++ .../AbstractPayloadMethodProcessorTest.java | 8 ++ .../method/XomPayloadMethodProcessorTest.java | 62 ++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessor.java create mode 100644 core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessorTest.java diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessor.java index 2b6ae351..805a24b0 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessor.java @@ -135,7 +135,7 @@ public abstract class AbstractPayloadMethodProcessor extends TransformerObjectSu * @return the response payload * @throws Exception in case of errors */ - protected abstract Source createResponsePayload(MethodParameter returnType, Object returnValue); + protected abstract Source createResponsePayload(MethodParameter returnType, Object returnValue) throws Exception; /** * Converts the given source to a byte array input stream. diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessor.java new file mode 100644 index 00000000..547b9304 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessor.java @@ -0,0 +1,108 @@ +/* + * Copyright 2005-2010 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.adapter.method; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; +import javax.xml.transform.dom.DOMSource; + +import org.springframework.core.MethodParameter; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.converters.DOMConverter; +import org.w3c.dom.DOMImplementation; + +/** + * Implementation of {@link MethodArgumentResolver} and {@link MethodReturnValueHandler} that supports XOM {@linkplain + * Element elements}. + * + * @author Arjen Poutsma + * @since 2.0 + */ +public class XomPayloadMethodProcessor extends AbstractPayloadMethodProcessor { + + private DocumentBuilderFactory documentBuilderFactory = createDocumentBuilderFactory(); + + @Override + protected boolean supportsRequestPayloadParameter(MethodParameter parameter) { + return supports(parameter); + } + + @Override + protected Element resolveRequestPayloadArgument(Source requestPayload, MethodParameter parameter) + throws TransformerException, IOException, ParsingException { + if (requestPayload instanceof DOMSource) { + org.w3c.dom.Node node = ((DOMSource) requestPayload).getNode(); + if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { + return DOMConverter.convert((org.w3c.dom.Element) node); + } + else if (node.getNodeType() == org.w3c.dom.Node.DOCUMENT_NODE) { + Document document = DOMConverter.convert((org.w3c.dom.Document) node); + return document.getRootElement(); + } + } + // we have no other option than to transform + ByteArrayInputStream bis = convertToByteArrayInputStream(requestPayload); + Builder builder = new Builder(); + Document document = builder.build(bis); + return document.getRootElement(); + } + + @Override + protected boolean supportsResponsePayloadReturnType(MethodParameter returnType) { + return supports(returnType); + } + + @Override + protected Source createResponsePayload(MethodParameter returnType, Object returnValue) + throws ParserConfigurationException { + Element returnedElement = (Element) returnValue; + Document document = returnedElement.getDocument(); + if (document == null) { + document = new Document(returnedElement); + } + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + DOMImplementation domImplementation = documentBuilder.getDOMImplementation(); + org.w3c.dom.Document w3cDocument = DOMConverter.convert(document, domImplementation); + return new DOMSource(w3cDocument); + } + + private boolean supports(MethodParameter parameter) { + return Element.class.equals(parameter.getParameterType()); + } + + /** + * Create a {@code DocumentBuilderFactory} that this resolver will use to create response payloads. + *

+ * Can be overridden in subclasses, adding further initialization of the factory. The resulting factory is cached, + * so this method will only be called once. + * + * @return the created factory + */ + protected DocumentBuilderFactory createDocumentBuilderFactory() { + return DocumentBuilderFactory.newInstance(); + } + +} diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessorTest.java index 6acfbbe1..def7ad91 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessorTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/AbstractPayloadMethodProcessorTest.java @@ -16,12 +16,16 @@ package org.springframework.ws.server.endpoint.adapter.method; +import javax.xml.transform.Source; + import org.springframework.core.MethodParameter; import org.springframework.ws.context.MessageContext; +import org.springframework.xml.transform.StringResult; import org.junit.Before; import org.junit.Test; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -128,6 +132,10 @@ public abstract class AbstractPayloadMethodProcessorTest extends AbstractMethodA Object returnValue = getReturnValue(supportedReturnType); processor.handleReturnValue(messageContext, supportedReturnType, returnValue); assertTrue("No response created", messageContext.hasResponse()); + Source responsePayload = messageContext.getResponse().getPayloadSource(); + StringResult result = new StringResult(); + transform(responsePayload, result); + assertXMLEqual(XML, result.toString()); } } diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessorTest.java new file mode 100644 index 00000000..a70a9d9f --- /dev/null +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessorTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2005-2010 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.adapter.method; + +import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.annotation.RequestPayload; +import org.springframework.ws.server.endpoint.annotation.ResponsePayload; + +import nu.xom.Element; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class XomPayloadMethodProcessorTest extends AbstractPayloadMethodProcessorTest { + + @Override + protected AbstractPayloadMethodProcessor createProcessor() { + return new XomPayloadMethodProcessor(); + } + + @Override + protected MethodParameter[] createSupportedParameters() throws NoSuchMethodException { + return new MethodParameter[]{new MethodParameter(getClass().getMethod("element", Element.class), 0)}; + } + + @Override + protected MethodParameter[] createSupportedReturnTypes() throws NoSuchMethodException { + return new MethodParameter[]{new MethodParameter(getClass().getMethod("element", Element.class), -1)}; + } + + @Override + protected void testArgument(Object argument, MethodParameter parameter) { + assertTrue("argument not a element", argument instanceof Element); + Element node = (Element) argument; + assertEquals("Invalid namespace", NAMESPACE_URI, node.getNamespaceURI()); + assertEquals("Invalid local name", LOCAL_NAME, node.getLocalName()); + } + + @Override + protected Element getReturnValue(MethodParameter returnType) { + return new Element(LOCAL_NAME, NAMESPACE_URI); + } + + @ResponsePayload + public Element element(@RequestPayload Element element) { + return element; + } +} \ No newline at end of file