From cd4a08330e72b84239b85bc9bf313145c3505042 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 6 May 2010 11:38:55 +0000 Subject: [PATCH] SWS-351 - Arbitrary parameter injection for @Endpoints --- .../adapter/DefaultMethodEndpointAdapter.java | 137 +++++++-- .../StaxPayloadMethodArgumentResolver.java | 9 +- .../Dom4jPayloadMethodProcessor.java | 5 +- .../{ => dom}/DomPayloadMethodProcessor.java | 5 +- .../{ => dom}/JDomPayloadMethodProcessor.java | 5 +- .../{ => dom}/XomPayloadMethodProcessor.java | 5 +- .../endpoint/adapter/method/dom/package.html | 6 + .../AbstractJaxb2PayloadMethodProcessor.java | 266 ++++++++++++++++++ .../JaxbElementPayloadMethodProcessor.java | 55 ++++ .../XmlRootElementPayloadMethodProcessor.java | 62 ++++ .../endpoint/adapter/method/jaxb/package.html | 6 + .../endpoint/adapter/method/package.html | 6 + .../DefaultMethodEndpointAdapter.properties | 16 -- .../DefaultMethodEndpointAdapterTest.java | 10 + .../Dom4jPayloadMethodProcessorTest.java | 4 +- .../DomPayloadMethodProcessorTest.java | 4 +- .../JDomPayloadMethodProcessorTest.java | 4 +- .../XomPayloadMethodProcessorTest.java | 4 +- ...JaxbElementPayloadMethodProcessorTest.java | 115 ++++++++ ...RootElementPayloadMethodProcessorTest.java | 143 ++++++++++ core/template.mf | 1 + .../xml/transform/TraxUtils.java | 32 +-- 22 files changed, 825 insertions(+), 75 deletions(-) rename core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/Dom4jPayloadMethodProcessor.java (86%) rename core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/DomPayloadMethodProcessor.java (88%) rename core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/JDomPayloadMethodProcessor.java (87%) rename core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/XomPayloadMethodProcessor.java (91%) create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/package.html create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/package.html create mode 100644 core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/package.html delete mode 100644 core/src/main/resources/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.properties rename core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/Dom4jPayloadMethodProcessorTest.java (89%) rename core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/DomPayloadMethodProcessorTest.java (91%) rename core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/JDomPayloadMethodProcessorTest.java (89%) rename core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/{ => dom}/XomPayloadMethodProcessorTest.java (89%) create mode 100644 core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java create mode 100644 core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java index ad390f29..6584966d 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java @@ -16,19 +16,29 @@ package org.springframework.ws.server.endpoint.adapter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.InitializingBean; import org.springframework.core.MethodParameter; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.endpoint.MethodEndpoint; +import org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver; import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver; import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler; -import org.springframework.ws.support.DefaultStrategiesHelper; +import org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver; +import org.springframework.ws.server.endpoint.adapter.method.dom.Dom4jPayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.dom.JDomPayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.dom.XomPayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor; +import org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor; /** * Default extension of {@link AbstractMethodEndpointAdapter} with support for pluggable {@linkplain @@ -37,19 +47,31 @@ import org.springframework.ws.support.DefaultStrategiesHelper; * @author Arjen Poutsma * @since 2.0 */ -public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter { +public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter + implements BeanClassLoaderAware, InitializingBean { + + private static final String DOM4J_CLASS_NAME = "org.dom4j.Element"; + + private static final String JAXB2_CLASS_NAME = "javax.xml.bind.Binder"; + + private static final String JDOM_CLASS_NAME = "org.jdom.Element"; + + private static final String STAX_CLASS_NAME = "javax.xml.stream.XMLInputFactory"; + + private static final String XOM_CLASS_NAME = "nu.xom.Element"; + + private static final String SOAP_METHOD_ARGUMENT_RESOLVER_CLASS_NAME = + "org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver"; private List methodArgumentResolvers; private List methodReturnValueHandlers; - /** - * Initializes a {@code DefaultMethodEndpointAdapter} with the default strategies. - * - * @see #initDefaultStrategies() - */ - public DefaultMethodEndpointAdapter() { - initDefaultStrategies(); + private ClassLoader classLoader; + + /** Returns the list of {@code MethodArgumentResolver}s to use. */ + public List getMethodArgumentResolvers() { + return methodArgumentResolvers; } /** Sets the list of {@code MethodArgumentResolver}s to use. */ @@ -57,29 +79,102 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter this.methodArgumentResolvers = methodArgumentResolvers; } + /** Returns the list of {@code MethodReturnValueHandler}s to use. */ + public List getMethodReturnValueHandlers() { + return methodReturnValueHandlers; + } + /** Sets the list of {@code MethodReturnValueHandler}s to use. */ public void setMethodReturnValueHandlers(List methodReturnValueHandlers) { this.methodReturnValueHandlers = methodReturnValueHandlers; } - /** Initialize the default implementations for the adapter's strategies */ + private ClassLoader getClassLoader() { + return this.classLoader != null ? this.classLoader : DefaultMethodEndpointAdapter.class.getClassLoader(); + } + + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public void afterPropertiesSet() throws Exception { + initDefaultStrategies(); + } + + /** Initialize the default implementations for the adapter's strategies. */ protected void initDefaultStrategies() { - Resource resource = - new ClassPathResource(ClassUtils.getShortName(DefaultMethodEndpointAdapter.class) + ".properties", - DefaultMethodEndpointAdapter.class); - DefaultStrategiesHelper strategiesHelper = new DefaultStrategiesHelper(resource); + initMethodArgumentResolvers(); + initMethodReturnValueHandlers(); + } + + @SuppressWarnings("unchecked") + private void initMethodArgumentResolvers() { if (CollectionUtils.isEmpty(methodArgumentResolvers)) { - List methodArgumentResolvers = - strategiesHelper.getDefaultStrategies(MethodArgumentResolver.class); + List methodArgumentResolvers = new ArrayList(); + methodArgumentResolvers.add(new DomPayloadMethodProcessor()); + methodArgumentResolvers.add(new MessageContextMethodArgumentResolver()); + methodArgumentResolvers.add(new SourcePayloadMethodProcessor()); + try { + Class soapMethodArgumentResolverClass = + (Class) ClassUtils + .forName(SOAP_METHOD_ARGUMENT_RESOLVER_CLASS_NAME, getClassLoader()); + methodArgumentResolvers.add(BeanUtils.instantiate(soapMethodArgumentResolverClass)); + } + catch (ClassNotFoundException e) { + logger.warn("Could not find \"" + SOAP_METHOD_ARGUMENT_RESOLVER_CLASS_NAME + "\" on the classpath"); + } + if (isPresent(DOM4J_CLASS_NAME)) { + methodArgumentResolvers.add(new Dom4jPayloadMethodProcessor()); + } + if (isPresent(JAXB2_CLASS_NAME)) { + methodArgumentResolvers.add(new XmlRootElementPayloadMethodProcessor()); + methodArgumentResolvers.add(new JaxbElementPayloadMethodProcessor()); + } + if (isPresent(JDOM_CLASS_NAME)) { + methodArgumentResolvers.add(new JDomPayloadMethodProcessor()); + } + if (isPresent(STAX_CLASS_NAME)) { + methodArgumentResolvers.add(new StaxPayloadMethodArgumentResolver()); + } + if (isPresent(XOM_CLASS_NAME)) { + methodArgumentResolvers.add(new XomPayloadMethodProcessor()); + } + if (logger.isDebugEnabled()) { + logger.debug("No MethodArgumentResolvers set, using defaults: " + methodArgumentResolvers); + } setMethodArgumentResolvers(methodArgumentResolvers); } + } + + private void initMethodReturnValueHandlers() { if (CollectionUtils.isEmpty(methodReturnValueHandlers)) { - List methodReturnValueHandlers = - strategiesHelper.getDefaultStrategies(MethodReturnValueHandler.class); + List methodReturnValueHandlers = new ArrayList(); + methodReturnValueHandlers.add(new DomPayloadMethodProcessor()); + methodReturnValueHandlers.add(new SourcePayloadMethodProcessor()); + if (isPresent(DOM4J_CLASS_NAME)) { + methodReturnValueHandlers.add(new Dom4jPayloadMethodProcessor()); + } + if (isPresent(JAXB2_CLASS_NAME)) { + methodReturnValueHandlers.add(new XmlRootElementPayloadMethodProcessor()); + methodReturnValueHandlers.add(new JaxbElementPayloadMethodProcessor()); + } + if (isPresent(JDOM_CLASS_NAME)) { + methodReturnValueHandlers.add(new JDomPayloadMethodProcessor()); + } + if (isPresent(XOM_CLASS_NAME)) { + methodArgumentResolvers.add(new XomPayloadMethodProcessor()); + } + if (logger.isDebugEnabled()) { + logger.debug("No MethodReturnValueHandlers set, using defaults: " + methodReturnValueHandlers); + } setMethodReturnValueHandlers(methodReturnValueHandlers); } } + private boolean isPresent(String className) { + return ClassUtils.isPresent(className, getClassLoader()); + } + @Override protected boolean supportsInternal(MethodEndpoint methodEndpoint) { return supportsParameters(methodEndpoint.getMethodParameters()) && @@ -170,7 +265,7 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter /** * Handle the return value for the given method endpoint. *

- * This implementation iterates over the set {@linkplain #setMethodReturnValueHandler(java.util.List) return value + * This implementation iterates over the set {@linkplain #setMethodReturnValueHandlers(java.util.List)} return value * handlers} to resolve the return value. * * @param messageContext the current message context diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/StaxPayloadMethodArgumentResolver.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/StaxPayloadMethodArgumentResolver.java index 8b38d879..00cb78b8 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/StaxPayloadMethodArgumentResolver.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/StaxPayloadMethodArgumentResolver.java @@ -155,14 +155,7 @@ public class StaxPayloadMethodArgumentResolver extends TransformerObjectSupport return XMLInputFactory.newInstance(); } - /** - * Converts the given source to a byte array input stream. - * - * @param source the source to convert - * @return the input stream - * @throws TransformerException in case of transformation errors - */ - protected ByteArrayInputStream convertToByteArrayInputStream(Source source) throws TransformerException { + private ByteArrayInputStream convertToByteArrayInputStream(Source source) throws TransformerException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); transform(source, new StreamResult(bos)); return new ByteArrayInputStream(bos.toByteArray()); diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessor.java similarity index 86% rename from core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessor.java rename to core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessor.java index 2e9bc537..3a4b3bd2 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessor.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.dom4j.Document; import org.dom4j.Element; @@ -29,7 +30,7 @@ import org.dom4j.io.DocumentResult; import org.dom4j.io.DocumentSource; /** - * Implementation of {@link MethodArgumentResolver} and {@link MethodReturnValueHandler} that supports dom4j + * Implementation of {@link org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver} and {@link org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler} that supports dom4j * {@linkplain Element elements}. * * @author Arjen Poutsma diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessor.java similarity index 88% rename from core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessor.java rename to core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessor.java index d4231e77..e08a85ce 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessor.java @@ -14,20 +14,21 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** - * Implementation of {@link MethodArgumentResolver} and {@link MethodReturnValueHandler} that supports W3C DOM + * Implementation of {@link org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver} and {@link org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler} that supports W3C DOM * {@linkplain Element elements}. * * @author Arjen Poutsma diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessor.java similarity index 87% rename from core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessor.java rename to core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessor.java index 0a512d2e..7a23eb7f 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessor.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.jdom.Document; import org.jdom.Element; @@ -29,7 +30,7 @@ import org.jdom.transform.JDOMSource; import org.w3c.dom.Node; /** - * Implementation of {@link MethodArgumentResolver} and {@link MethodReturnValueHandler} that supports JDOM + * Implementation of {@link org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver} and {@link org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler} that supports JDOM * {@linkplain Element elements}. * * @author Arjen Poutsma 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/dom/XomPayloadMethodProcessor.java similarity index 91% rename from core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessor.java rename to core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/XomPayloadMethodProcessor.java index a8d00cd9..27ef0863 100644 --- 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/dom/XomPayloadMethodProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -26,6 +26,7 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import nu.xom.Builder; import nu.xom.Document; @@ -35,7 +36,7 @@ import nu.xom.converters.DOMConverter; import org.w3c.dom.DOMImplementation; /** - * Implementation of {@link MethodArgumentResolver} and {@link MethodReturnValueHandler} that supports XOM {@linkplain + * Implementation of {@link org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver} and {@link org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler} that supports XOM {@linkplain * Element elements}. * * @author Arjen Poutsma diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/package.html b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/package.html new file mode 100644 index 00000000..29182587 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/dom/package.html @@ -0,0 +1,6 @@ + + +Provides DOM-based implementations of the the MethodArgumentResolver and +MethodReturnValueHandler interfaces. + + diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java new file mode 100644 index 00000000..bec076e3 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java @@ -0,0 +1,266 @@ +/* + * 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.jaxb; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamSource; + +import org.springframework.util.Assert; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadMethodProcessor; +import org.springframework.xml.transform.TraxUtils; + +import org.w3c.dom.Node; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.LexicalHandler; + +/** + * @author Arjen Poutsma + * @since 2.0 + */ +public abstract class AbstractJaxb2PayloadMethodProcessor extends AbstractPayloadMethodProcessor { + + private final ConcurrentMap jaxbContexts = new ConcurrentHashMap(); + + protected void marshalToResponse(MessageContext messageContext, Class clazz, Object jaxbElement) + throws JAXBException { + if (logger.isDebugEnabled()) { + logger.debug("Marshalling [" + jaxbElement + "] to response payload"); + } + Result responsePayload = getResponsePayload(messageContext); + try { + Jaxb2ResultCallback callback = new Jaxb2ResultCallback(clazz, jaxbElement); + TraxUtils.doWithResult(responsePayload, callback); + } + catch (Exception ex) { + throw convertToJaxbException(ex); + } + } + + private Result getResponsePayload(MessageContext messageContext) { + WebServiceMessage response = messageContext.getResponse(); + return response != null ? response.getPayloadResult() : null; + } + + protected Object unmarshalFromRequest(MessageContext messageContext, Class clazz) throws JAXBException { + Source requestPayload = getRequestPayload(messageContext); + if (requestPayload == null) { + return null; + } + try { + Jaxb2SourceCallback callback = new Jaxb2SourceCallback(clazz); + TraxUtils.doWithSource(requestPayload, callback); + if (logger.isDebugEnabled()) { + logger.debug("Unmarshalled payload request to [" + callback.result + "]"); + } + return callback.result; + } + catch (Exception ex) { + throw convertToJaxbException(ex); + } + } + + protected JAXBElement unmarshalElementFromRequest(MessageContext messageContext, Class clazz) + throws JAXBException { + Source requestPayload = getRequestPayload(messageContext); + if (requestPayload == null) { + return null; + } + try { + JaxbElementSourceCallback callback = new JaxbElementSourceCallback(clazz); + TraxUtils.doWithSource(requestPayload, callback); + if (logger.isDebugEnabled()) { + logger.debug("Unmarshalled payload request to [" + callback.result + "]"); + } + return callback.result; + } + catch (Exception ex) { + throw convertToJaxbException(ex); + } + } + + private Source getRequestPayload(MessageContext messageContext) { + WebServiceMessage request = messageContext.getRequest(); + return request != null ? request.getPayloadSource() : null; + } + + private JAXBException convertToJaxbException(Exception ex) { + if (ex instanceof JAXBException) { + return (JAXBException) ex; + } + else { + return new JAXBException(ex); + } + } + + private Marshaller createMarshaller(Class clazz) throws JAXBException { + JAXBContext jaxbContext = getJaxbContext(clazz); + return jaxbContext.createMarshaller(); + } + + private Unmarshaller createUnmarshaller(Class clazz) throws JAXBException { + JAXBContext jaxbContext = getJaxbContext(clazz); + return jaxbContext.createUnmarshaller(); + } + + private JAXBContext getJaxbContext(Class clazz) throws JAXBException { + Assert.notNull(clazz, "'clazz' must not be null"); + JAXBContext jaxbContext = jaxbContexts.get(clazz); + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(clazz); + jaxbContexts.putIfAbsent(clazz, jaxbContext); + } + return jaxbContext; + } + + // Callbacks + + @SuppressWarnings("Since15") + private class Jaxb2SourceCallback implements TraxUtils.SourceCallback { + + private final Unmarshaller unmarshaller; + + private Object result; + + public Jaxb2SourceCallback(Class clazz) throws JAXBException { + this.unmarshaller = createUnmarshaller(clazz); + } + + public void domSource(Node node) throws JAXBException { + result = unmarshaller.unmarshal(node); + } + + public void saxSource(XMLReader reader, InputSource inputSource) throws JAXBException { + result = unmarshaller.unmarshal(inputSource); + } + + public void staxSource(XMLEventReader eventReader) throws JAXBException { + result = unmarshaller.unmarshal(eventReader); + } + + public void staxSource(XMLStreamReader streamReader) throws JAXBException { + result = unmarshaller.unmarshal(streamReader); + } + + public void streamSource(InputStream inputStream) throws IOException, JAXBException { + result = unmarshaller.unmarshal(inputStream); + } + + public void streamSource(Reader reader) throws IOException, JAXBException { + result = unmarshaller.unmarshal(reader); + } + } + + @SuppressWarnings("Since15") + private class JaxbElementSourceCallback implements TraxUtils.SourceCallback { + + private final Unmarshaller unmarshaller; + + private final Class declaredType; + + private JAXBElement result; + + public JaxbElementSourceCallback(Class declaredType) throws JAXBException { + this.unmarshaller = createUnmarshaller(declaredType); + this.declaredType = declaredType; + } + + public void domSource(Node node) throws JAXBException { + result = unmarshaller.unmarshal(node, declaredType); + } + + public void saxSource(XMLReader reader, InputSource inputSource) throws JAXBException { + result = unmarshaller.unmarshal(new SAXSource(reader, inputSource), declaredType); + } + + public void staxSource(XMLEventReader eventReader) throws JAXBException { + result = unmarshaller.unmarshal(eventReader, declaredType); + } + + public void staxSource(XMLStreamReader streamReader) throws JAXBException { + result = unmarshaller.unmarshal(streamReader, declaredType); + } + + public void streamSource(InputStream inputStream) throws IOException, JAXBException { + result = unmarshaller.unmarshal(new StreamSource(inputStream), declaredType); + } + + public void streamSource(Reader reader) throws IOException, JAXBException { + result = unmarshaller.unmarshal(new StreamSource(reader), declaredType); + } + } + + @SuppressWarnings("Since15") + private class Jaxb2ResultCallback implements TraxUtils.ResultCallback { + + private final Marshaller marshaller; + + private final Object jaxbElement; + + private Jaxb2ResultCallback(Class clazz, Object jaxbElement) throws JAXBException { + this.marshaller = createMarshaller(clazz); + this.jaxbElement = jaxbElement; + } + + public void domResult(Node node) throws JAXBException { + marshaller.marshal(jaxbElement, node); + } + + public void saxResult(ContentHandler contentHandler, LexicalHandler lexicalHandler) throws JAXBException { + marshaller.marshal(jaxbElement, contentHandler); + } + + public void staxResult(XMLEventWriter eventWriter) throws JAXBException { + marshaller.marshal(jaxbElement, eventWriter); + } + + public void staxResult(XMLStreamWriter streamWriter) throws JAXBException { + marshaller.marshal(jaxbElement, streamWriter); + } + + public void streamResult(OutputStream outputStream) throws JAXBException { + marshaller.marshal(jaxbElement, outputStream); + } + + public void streamResult(Writer writer) throws JAXBException { + marshaller.marshal(jaxbElement, writer); + } + } + + +} \ No newline at end of file diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java new file mode 100644 index 00000000..6e38c7eb --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java @@ -0,0 +1,55 @@ +/* + * 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.jaxb; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; + +import org.springframework.core.MethodParameter; +import org.springframework.ws.context.MessageContext; + +/** @author Arjen Poutsma */ +public class JaxbElementPayloadMethodProcessor extends AbstractJaxb2PayloadMethodProcessor { + + @Override + protected boolean supportsRequestPayloadParameter(MethodParameter parameter) { + Class parameterType = parameter.getParameterType(); + Type genericType = parameter.getGenericParameterType(); + return JAXBElement.class.equals(parameterType) && genericType instanceof ParameterizedType; + } + + public JAXBElement resolveArgument(MessageContext messageContext, MethodParameter parameter) + throws JAXBException { + ParameterizedType parameterizedType = (ParameterizedType) parameter.getGenericParameterType(); + Class clazz = (Class) parameterizedType.getActualTypeArguments()[0]; + return unmarshalElementFromRequest(messageContext, clazz); + } + + @Override + protected boolean supportsResponsePayloadReturnType(MethodParameter returnType) { + Class parameterType = returnType.getParameterType(); + return JAXBElement.class.isAssignableFrom(parameterType); + } + + public void handleReturnValue(MessageContext messageContext, MethodParameter returnType, Object returnValue) + throws JAXBException { + JAXBElement element = (JAXBElement) returnValue; + marshalToResponse(messageContext, element.getDeclaredType(), element); + } +} diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java new file mode 100644 index 00000000..3e7783a8 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.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.jaxb; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.springframework.core.MethodParameter; +import org.springframework.ws.context.MessageContext; + +/** @author Arjen Poutsma */ +public class XmlRootElementPayloadMethodProcessor extends AbstractJaxb2PayloadMethodProcessor { + + @Override + protected boolean supportsRequestPayloadParameter(MethodParameter parameter) { + Class parameterType = parameter.getParameterType(); + return parameterType.isAnnotationPresent(XmlRootElement.class) || + parameterType.isAnnotationPresent(XmlType.class); + } + + public Object resolveArgument(MessageContext messageContext, MethodParameter parameter) throws JAXBException { + Class parameterType = parameter.getParameterType(); + + if (parameterType.isAnnotationPresent(XmlRootElement.class)) { + return unmarshalFromRequest(messageContext, parameterType); + } + else { + JAXBElement element = unmarshalElementFromRequest(messageContext, parameterType); + return element != null ? element.getValue() : null; + } + } + + @Override + protected boolean supportsResponsePayloadReturnType(MethodParameter returnType) { + Class parameterType = returnType.getParameterType(); + return parameterType.isAnnotationPresent(XmlRootElement.class); + } + + public void handleReturnValue(MessageContext messageContext, MethodParameter returnType, Object returnValue) + throws JAXBException { + Class parameterType = returnType.getParameterType(); + marshalToResponse(messageContext, parameterType, returnValue); + } + + +} diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/package.html b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/package.html new file mode 100644 index 00000000..762be767 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/package.html @@ -0,0 +1,6 @@ + + +Provides JAXB2-based implementations of the the MethodArgumentResolver and +MethodReturnValueHandler interfaces. + + diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/package.html b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/package.html new file mode 100644 index 00000000..2bdc3627 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/package.html @@ -0,0 +1,6 @@ + + +Provides the MethodArgumentResolver and MethodReturnValueHandler abstractions, and various +implementations thereof. + + diff --git a/core/src/main/resources/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.properties b/core/src/main/resources/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.properties deleted file mode 100644 index 8c89cb51..00000000 --- a/core/src/main/resources/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.properties +++ /dev/null @@ -1,16 +0,0 @@ -# Default implementation classes for DefaultMethodEndpointAdapter's strategy interfaces. -# Used as fallback when no matching beans are configured. -# Not meant to be customized by application developers. - -org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver=org.springframework.ws.server.endpoint.adapter.method.Dom4jPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.DomPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.JDomPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver,\ - org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver,\ - org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver - -org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler=org.springframework.ws.server.endpoint.adapter.method.Dom4jPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.DomPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.JDomPayloadMethodProcessor,\ - org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor \ No newline at end of file diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java index 2f884da6..5855722c 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java @@ -67,6 +67,16 @@ public class DefaultMethodEndpointAdapterTest { exceptionEndpoint = new MethodEndpoint(this, "exception", String.class); } + @Test + public void initDefaultStrategies() throws Exception { + adapter = new DefaultMethodEndpointAdapter(); + adapter.setBeanClassLoader(DefaultMethodEndpointAdapterTest.class.getClassLoader()); + adapter.afterPropertiesSet(); + + assertFalse("No default MethodArgumentResolvers loaded", adapter.getMethodArgumentResolvers().isEmpty()); + assertFalse("No default MethodReturnValueHandlers loaded", adapter.getMethodReturnValueHandlers().isEmpty()); + } + @Test public void supportsSupported() throws Exception { expect(argumentResolver1.supportsParameter(isA(MethodParameter.class))).andReturn(true); diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessorTest.java similarity index 89% rename from core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessorTest.java rename to core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessorTest.java index 5522e656..82b9c35a 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/Dom4jPayloadMethodProcessorTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/Dom4jPayloadMethodProcessorTest.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadMethodProcessorTestCase; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessorTest.java similarity index 91% rename from core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessorTest.java rename to core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessorTest.java index b69de6c2..316cd588 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/DomPayloadMethodProcessorTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/DomPayloadMethodProcessorTest.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadMethodProcessorTestCase; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessorTest.java similarity index 89% rename from core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessorTest.java rename to core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessorTest.java index 7ae3654c..65928bae 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/JDomPayloadMethodProcessorTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/JDomPayloadMethodProcessorTest.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadMethodProcessorTestCase; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; 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/dom/XomPayloadMethodProcessorTest.java similarity index 89% rename from core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/XomPayloadMethodProcessorTest.java rename to core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/dom/XomPayloadMethodProcessorTest.java index 8b4a3e2f..e78d757c 100644 --- 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/dom/XomPayloadMethodProcessorTest.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package org.springframework.ws.server.endpoint.adapter.method; +package org.springframework.ws.server.endpoint.adapter.method.dom; import org.springframework.core.MethodParameter; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadMethodProcessorTestCase; +import org.springframework.ws.server.endpoint.adapter.method.AbstractPayloadSourceMethodProcessor; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java new file mode 100644 index 00000000..d4a5bcb7 --- /dev/null +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java @@ -0,0 +1,115 @@ +/* + * 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.jaxb; + +import java.io.IOException; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlType; +import javax.xml.namespace.QName; + +import org.springframework.core.MethodParameter; +import org.springframework.ws.MockWebServiceMessage; +import org.springframework.ws.MockWebServiceMessageFactory; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.DefaultMessageContext; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.annotation.RequestPayload; +import org.springframework.ws.server.endpoint.annotation.ResponsePayload; + +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.SAXException; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** @author Arjen Poutsma */ +public class JaxbElementPayloadMethodProcessorTest { + + private JaxbElementPayloadMethodProcessor processor; + + private MethodParameter supportedParameter; + + private MethodParameter supportedReturnType; + + @Before + public void setUp() throws Exception { + processor = new JaxbElementPayloadMethodProcessor(); + supportedParameter = new MethodParameter(getClass().getMethod("supported", JAXBElement.class), 0); + supportedReturnType = new MethodParameter(getClass().getMethod("supported", JAXBElement.class), -1); + } + + @Test + public void supportsParameter() { + assertTrue("processor does not support @JAXBElement parameter", + processor.supportsParameter(supportedParameter)); + } + + @Test + public void supportsReturnType() { + assertTrue("processor does not support @JAXBElement return type", + processor.supportsReturnType(supportedReturnType)); + } + + @Test + public void resolveArgument() throws JAXBException { + WebServiceMessage request = new MockWebServiceMessage("Foo"); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + + JAXBElement result = processor.resolveArgument(messageContext, supportedParameter); + assertTrue("result not a MyType", result.getValue() instanceof MyType); + MyType type = (MyType) result.getValue(); + assertEquals("invalid result", "Foo", type.getString()); + } + + @Test + public void handleReturnValue() throws JAXBException, IOException, SAXException { + MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); + + MyType type = new MyType(); + type.setString("Foo"); + JAXBElement element = new JAXBElement(new QName("type"), MyType.class, type); + processor.handleReturnValue(messageContext, supportedReturnType, element); + assertTrue("context has no response", messageContext.hasResponse()); + MockWebServiceMessage response = (MockWebServiceMessage) messageContext.getResponse(); + assertXMLEqual("Foo", response.getPayloadAsString()); + } + + + + @ResponsePayload + public JAXBElement supported(@RequestPayload JAXBElement element) { + return element; + } + + @XmlType + public static class MyType { + + private String string; + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + +} diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java new file mode 100644 index 00000000..158829cd --- /dev/null +++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java @@ -0,0 +1,143 @@ +/* + * 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.jaxb; + +import java.io.IOException; +import javax.xml.bind.JAXBException; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.springframework.core.MethodParameter; +import org.springframework.ws.MockWebServiceMessage; +import org.springframework.ws.MockWebServiceMessageFactory; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.DefaultMessageContext; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.annotation.RequestPayload; +import org.springframework.ws.server.endpoint.annotation.ResponsePayload; + +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.SAXException; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class XmlRootElementPayloadMethodProcessorTest { + + private XmlRootElementPayloadMethodProcessor processor; + + private MethodParameter rootElementParameter; + + private MethodParameter typeParameter; + + private MethodParameter rootElementReturnType; + + @Before + public void setUp() throws Exception { + processor = new XmlRootElementPayloadMethodProcessor(); + rootElementParameter = new MethodParameter(getClass().getMethod("rootElement", MyRootElement.class), 0); + typeParameter = new MethodParameter(getClass().getMethod("type", MyType.class), 0); + rootElementReturnType = new MethodParameter(getClass().getMethod("rootElement", MyRootElement.class), -1); + } + + @Test + public void supportsParameter() { + assertTrue("processor does not support @XmlRootElement parameter", + processor.supportsParameter(rootElementParameter)); + assertTrue("processor does not support @XmlType parameter", processor.supportsParameter(typeParameter)); + } + + @Test + public void supportsReturnType() { + assertTrue("processor does not support @XmlRootElement return type", + processor.supportsReturnType(rootElementReturnType)); + } + + @Test + public void resolveArgumentRootElement() throws JAXBException { + WebServiceMessage request = new MockWebServiceMessage("Foo"); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + + Object result = processor.resolveArgument(messageContext, rootElementParameter); + assertTrue("result not a MyRootElement", result instanceof MyRootElement); + MyRootElement rootElement = (MyRootElement) result; + assertEquals("invalid result", "Foo", rootElement.getString()); + } + + @Test + public void resolveArgumentType() throws JAXBException { + WebServiceMessage request = new MockWebServiceMessage("Foo"); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + + Object result = processor.resolveArgument(messageContext, typeParameter); + assertTrue("result not a MyType", result instanceof MyType); + MyType type = (MyType) result; + assertEquals("invalid result", "Foo", type.getString()); + } + + @Test + public void handleReturnValue() throws JAXBException, IOException, SAXException { + MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); + + MyRootElement rootElement = new MyRootElement(); + rootElement.setString("Foo"); + processor.handleReturnValue(messageContext, rootElementReturnType, rootElement); + assertTrue("context has no response", messageContext.hasResponse()); + MockWebServiceMessage response = (MockWebServiceMessage) messageContext.getResponse(); + assertXMLEqual("Foo", response.getPayloadAsString()); + } + + @ResponsePayload + public MyRootElement rootElement(@RequestPayload MyRootElement rootElement) { + return rootElement; + } + + public void type(@RequestPayload MyType type) { + } + + @XmlRootElement + public static class MyRootElement { + + private String string; + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + + @XmlType + public static class MyType { + + private String string; + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + } + + +} diff --git a/core/template.mf b/core/template.mf index 4a14dfb9..0fbe5dc9 100644 --- a/core/template.mf +++ b/core/template.mf @@ -7,6 +7,7 @@ Import-Template: javax.activation.*;version="[1.1.0, 2.0.0)", javax.servlet.*;version="[2.4.0, 3.0.0)", javax.wsdl.*;version="[1.6.1, 2.0.0)";resolution:=optional, + javax.xml.bind.*;version="0";resolution:=optional, javax.xml.namespace.*;version="0", javax.xml.parsers.*;version="0", javax.xml.transform.*;version="0", diff --git a/xml/src/main/java/org/springframework/xml/transform/TraxUtils.java b/xml/src/main/java/org/springframework/xml/transform/TraxUtils.java index c8350a22..b154b851 100644 --- a/xml/src/main/java/org/springframework/xml/transform/TraxUtils.java +++ b/xml/src/main/java/org/springframework/xml/transform/TraxUtils.java @@ -16,7 +16,6 @@ package org.springframework.xml.transform; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -44,7 +43,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; -import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.ext.LexicalHandler; @@ -232,8 +230,7 @@ public abstract class TraxUtils { * @param source source to look at * @param callback the callback to invoke for each kind of source */ - public static void doWithSource(Source source, SourceCallback callback) - throws XMLStreamException, IOException, SAXException { + public static void doWithSource(Source source, SourceCallback callback) throws Exception { if (source instanceof DOMSource) { callback.domSource(((DOMSource) source).getNode()); } @@ -281,8 +278,7 @@ public abstract class TraxUtils { * @param result result to look at * @param callback the callback to invoke for each kind of result */ - public static void doWithResult(Result result, ResultCallback callback) - throws XMLStreamException, IOException, SAXException { + public static void doWithResult(Result result, ResultCallback callback) throws Exception{ if (result instanceof DOMResult) { callback.domResult(((DOMResult) result).getNode()); } @@ -335,7 +331,7 @@ public abstract class TraxUtils { * * @param node the node */ - void domSource(Node node); + void domSource(Node node) throws Exception; /** * Perform an operation on the {@code XMLReader} and {@code InputSource} contained in a {@link SAXSource}. @@ -343,7 +339,7 @@ public abstract class TraxUtils { * @param reader the reader, can be {@code null} * @param inputSource the input source, can be {@code null} */ - void saxSource(XMLReader reader, InputSource inputSource) throws IOException, SAXException; + void saxSource(XMLReader reader, InputSource inputSource) throws Exception; /** * Perform an operation on the {@code XMLEventReader} contained in a JAXP 1.4 {@link StAXSource} or Spring @@ -351,7 +347,7 @@ public abstract class TraxUtils { * * @param eventReader the reader */ - void staxSource(XMLEventReader eventReader) throws XMLStreamException; + void staxSource(XMLEventReader eventReader) throws Exception; /** * Perform an operation on the {@code XMLStreamReader} contained in a JAXP 1.4 {@link StAXSource} or Spring @@ -359,21 +355,21 @@ public abstract class TraxUtils { * * @param streamReader the reader */ - void staxSource(XMLStreamReader streamReader) throws XMLStreamException; + void staxSource(XMLStreamReader streamReader) throws Exception; /** * Perform an operation on the {@code InputStream} contained in a {@link StreamSource}. * * @param inputStream the input stream */ - void streamSource(InputStream inputStream) throws IOException; + void streamSource(InputStream inputStream) throws Exception; /** * Perform an operation on the {@code Reader} contained in a {@link StreamSource}. * * @param reader the reader */ - void streamSource(Reader reader) throws IOException; + void streamSource(Reader reader) throws Exception; } /** @@ -388,7 +384,7 @@ public abstract class TraxUtils { * * @param node the node */ - void domResult(Node node); + void domResult(Node node) throws Exception; /** * Perform an operation on the {@code ContentHandler} and {@code LexicalHandler} contained in a {@link @@ -397,7 +393,7 @@ public abstract class TraxUtils { * @param contentHandler the content handler * @param lexicalHandler the lexicalHandler, can be {@code null} */ - void saxResult(ContentHandler contentHandler, LexicalHandler lexicalHandler) throws IOException, SAXException; + void saxResult(ContentHandler contentHandler, LexicalHandler lexicalHandler) throws Exception; /** * Perform an operation on the {@code XMLEventWriter} contained in a JAXP 1.4 {@link StAXResult} or Spring @@ -405,7 +401,7 @@ public abstract class TraxUtils { * * @param eventWriter the writer */ - void staxResult(XMLEventWriter eventWriter) throws XMLStreamException; + void staxResult(XMLEventWriter eventWriter) throws Exception; /** * Perform an operation on the {@code XMLStreamWriter} contained in a JAXP 1.4 {@link StAXResult} or Spring @@ -413,21 +409,21 @@ public abstract class TraxUtils { * * @param streamWriter the writer */ - void staxResult(XMLStreamWriter streamWriter) throws XMLStreamException; + void staxResult(XMLStreamWriter streamWriter) throws Exception; /** * Perform an operation on the {@code OutputStream} contained in a {@link StreamResult}. * * @param outputStream the output stream */ - void streamResult(OutputStream outputStream) throws IOException; + void streamResult(OutputStream outputStream) throws Exception; /** * Perform an operation on the {@code Writer} contained in a {@link StreamResult}. * * @param writer the writer */ - void streamResult(Writer writer) throws IOException; + void streamResult(Writer writer) throws Exception; } /** Inner class to avoid a static JAXP 1.4 dependency. */