From 06ff649b74e8802cc117c880a2c0f8d959594e21 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 20 Mar 2014 14:05:24 +0100 Subject: [PATCH] Support null return value for JAXB Support null return values for JAXB MethodReturnValueHandlers.\ Issue: SWS-867 --- .../adapter/DefaultMethodEndpointAdapter.java | 5 +-- .../MarshallingPayloadMethodProcessor.java | 3 ++ .../AbstractJaxb2PayloadMethodProcessor.java | 26 ++++++++--- .../JaxbElementPayloadMethodProcessor.java | 3 +- .../XmlRootElementPayloadMethodProcessor.java | 3 +- .../DefaultMethodEndpointAdapterTest.java | 45 +++++++++++++++---- ...JaxbElementPayloadMethodProcessorTest.java | 27 ++++++----- ...RootElementPayloadMethodProcessorTest.java | 30 ++++++++----- 8 files changed, 98 insertions(+), 44 deletions(-) diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java index 319961d2..73544b2c 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapter.java @@ -231,10 +231,7 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter Object[] args = getMethodArguments(messageContext, methodEndpoint); if (logger.isTraceEnabled()) { - StringBuilder builder = new StringBuilder("Invoking ["); - builder.append(methodEndpoint).append("] with arguments "); - builder.append(Arrays.asList(args)); - logger.trace(builder.toString()); + logger.trace("Invoking [" + methodEndpoint + "] with arguments " + Arrays.asList(args)); } Object returnValue = methodEndpoint.invoke(args); diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/MarshallingPayloadMethodProcessor.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/MarshallingPayloadMethodProcessor.java index 9598efe7..fb89447b 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/MarshallingPayloadMethodProcessor.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/MarshallingPayloadMethodProcessor.java @@ -151,6 +151,9 @@ public class MarshallingPayloadMethodProcessor extends AbstractPayloadMethodProc public void handleReturnValue(MessageContext messageContext, MethodParameter returnType, Object returnValue) throws Exception { + if (returnValue == null) { + return; + } Marshaller marshaller = getMarshaller(); Assert.state(marshaller != null, "marshaller must not be null"); diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java index 275b110b..e563347c 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/AbstractJaxb2PayloadMethodProcessor.java @@ -42,6 +42,13 @@ import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +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; + +import org.springframework.core.MethodParameter; import org.springframework.util.Assert; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.context.MessageContext; @@ -50,12 +57,6 @@ import org.springframework.ws.stream.StreamingPayload; import org.springframework.ws.stream.StreamingWebServiceMessage; 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; - /** * Abstract base class for {@link org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver * MethodArgumentResolver} and {@link org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler @@ -71,7 +72,18 @@ public abstract class AbstractJaxb2PayloadMethodProcessor extends AbstractPayloa private final ConcurrentMap jaxbContexts = new ConcurrentHashMap(); - /** + @Override + public final void handleReturnValue(MessageContext messageContext, + MethodParameter returnType, Object returnValue) throws Exception { + if (returnValue != null) { + handleReturnValueInternal(messageContext, returnType, returnValue); + } + } + + protected abstract void handleReturnValueInternal(MessageContext messageContext, + MethodParameter returnType, Object returnValue) throws Exception; + + /** * Marshals the given {@code jaxbElement} to the response payload of the given message context. * * @param messageContext the message context to marshal to diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java index f0135059..e83b3347 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessor.java @@ -54,7 +54,8 @@ public class JaxbElementPayloadMethodProcessor extends AbstractJaxb2PayloadMetho return JAXBElement.class.isAssignableFrom(parameterType); } - public void handleReturnValue(MessageContext messageContext, MethodParameter returnType, Object returnValue) + @Override + protected void handleReturnValueInternal(MessageContext messageContext, MethodParameter returnType, Object returnValue) throws JAXBException { JAXBElement element = (JAXBElement) returnValue; marshalToResponsePayload(messageContext, element.getDeclaredType(), element); diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java index be49fe4e..4bf5c538 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessor.java @@ -60,7 +60,8 @@ public class XmlRootElementPayloadMethodProcessor extends AbstractJaxb2PayloadMe return parameterType.isAnnotationPresent(XmlRootElement.class); } - public void handleReturnValue(MessageContext messageContext, MethodParameter returnType, Object returnValue) + @Override + protected void handleReturnValueInternal(MessageContext messageContext, MethodParameter returnType, Object returnValue) throws JAXBException { Class parameterType = returnType.getParameterType(); marshalToResponsePayload(messageContext, parameterType, returnValue); diff --git a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java index 5855722c..ff7e815b 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/DefaultMethodEndpointAdapterTest.java @@ -20,6 +20,11 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.ws.MockWebServiceMessage; import org.springframework.ws.MockWebServiceMessageFactory; @@ -29,12 +34,6 @@ import org.springframework.ws.server.endpoint.MethodEndpoint; import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver; import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler; -import org.junit.Before; -import org.junit.Test; - -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; - /** @author Arjen Poutsma */ public class DefaultMethodEndpointAdapterTest { @@ -48,21 +47,25 @@ public class DefaultMethodEndpointAdapterTest { private MethodEndpoint supportedEndpoint; + private MethodEndpoint nullReturnValue; + private MethodEndpoint unsupportedEndpoint; private MethodEndpoint exceptionEndpoint; private String supportedArgument; - @Before + @Before public void setUp() throws Exception { adapter = new DefaultMethodEndpointAdapter(); argumentResolver1 = createMock("stringResolver", MethodArgumentResolver.class); argumentResolver2 = createMock("intResolver", MethodArgumentResolver.class); returnValueHandler = createMock(MethodReturnValueHandler.class); adapter.setMethodArgumentResolvers(Arrays.asList(argumentResolver1, argumentResolver2)); - adapter.setMethodReturnValueHandlers(Collections.singletonList(returnValueHandler)); + adapter.setMethodReturnValueHandlers( + Collections.singletonList(returnValueHandler)); supportedEndpoint = new MethodEndpoint(this, "supported", String.class, Integer.class); + nullReturnValue = new MethodEndpoint(this, "nullReturnValue", String.class); unsupportedEndpoint = new MethodEndpoint(this, "unsupported", String.class); exceptionEndpoint = new MethodEndpoint(this, "exception", String.class); } @@ -146,6 +149,27 @@ public class DefaultMethodEndpointAdapterTest { verify(argumentResolver1, argumentResolver2, returnValueHandler); } + @Test + public void invokeNullReturnValue() throws Exception { + MockWebServiceMessage request = new MockWebServiceMessage(""); + MessageContext messageContext = new DefaultMessageContext(request, new MockWebServiceMessageFactory()); + + String value = "Foo"; + + expect(argumentResolver1.supportsParameter(isA(MethodParameter.class))).andReturn(true); + expect(argumentResolver1.resolveArgument(eq(messageContext), isA(MethodParameter.class))).andReturn(value); + + expect(returnValueHandler.supportsReturnType(isA(MethodParameter.class))).andReturn(true); + returnValueHandler.handleReturnValue(eq(messageContext), isA(MethodParameter.class), isNull()); + + replay(argumentResolver1, argumentResolver2, returnValueHandler); + + adapter.invoke(messageContext, nullReturnValue); + assertEquals("Invalid argument passed", value, supportedArgument); + + verify(argumentResolver1, argumentResolver2, returnValueHandler); + } + @Test public void invokeException() throws Exception { MockWebServiceMessage request = new MockWebServiceMessage(""); @@ -177,6 +201,11 @@ public class DefaultMethodEndpointAdapterTest { } + public String nullReturnValue(String s) { + supportedArgument = s; + return null; + } + public String unsupported(String s) { return s; } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java index 6a429c02..d28ad21c 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/JaxbElementPayloadMethodProcessorTest.java @@ -17,7 +17,6 @@ package org.springframework.ws.server.endpoint.adapter.method.jaxb; import java.io.ByteArrayOutputStream; -import java.io.IOException; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlElement; @@ -26,6 +25,11 @@ import javax.xml.namespace.QName; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.ws.MockWebServiceMessage; import org.springframework.ws.MockWebServiceMessageFactory; @@ -38,14 +42,6 @@ import org.springframework.ws.soap.axiom.AxiomSoapMessage; import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; import org.springframework.xml.transform.StringResult; -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 JaxbElementPayloadMethodProcessorTest { private JaxbElementPayloadMethodProcessor processor; @@ -88,7 +84,7 @@ public class JaxbElementPayloadMethodProcessorTest { } @Test - public void handleReturnValue() throws JAXBException, IOException, SAXException { + public void handleReturnValue() throws Exception { MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); MyType type = new MyType(); @@ -101,7 +97,7 @@ public class JaxbElementPayloadMethodProcessorTest { } @Test - public void handleReturnValueString() throws JAXBException, IOException, SAXException { + public void handleReturnValueString() throws Exception { MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); String s = "Foo"; @@ -112,6 +108,15 @@ public class JaxbElementPayloadMethodProcessorTest { assertXMLEqual("Foo", response.getPayloadAsString()); } + @Test + public void handleNullReturnValue() throws Exception { + MessageContext messageContext = + new DefaultMessageContext(new MockWebServiceMessageFactory()); + + processor.handleReturnValue(messageContext, stringReturnType, null); + assertFalse("context has response", messageContext.hasResponse()); + } + @Test public void handleReturnValueAxiom() throws Exception { AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory(); diff --git a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java index e643fe7c..e9783468 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/adapter/method/jaxb/XmlRootElementPayloadMethodProcessorTest.java @@ -17,7 +17,6 @@ package org.springframework.ws.server.endpoint.adapter.method.jaxb; import java.io.ByteArrayOutputStream; -import java.io.IOException; import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -25,6 +24,11 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + import org.springframework.core.MethodParameter; import org.springframework.ws.MockWebServiceMessage; import org.springframework.ws.MockWebServiceMessageFactory; @@ -37,14 +41,6 @@ import org.springframework.ws.soap.axiom.AxiomSoapMessage; import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; import org.springframework.xml.transform.StringResult; -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; @@ -67,13 +63,14 @@ public class XmlRootElementPayloadMethodProcessorTest { public void supportsParameter() { assertTrue("processor does not support @XmlRootElement parameter", processor.supportsParameter(rootElementParameter)); - assertTrue("processor does not support @XmlType parameter", processor.supportsParameter(typeParameter)); + 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)); + processor.supportsReturnType(rootElementReturnType)); } @Test @@ -99,7 +96,7 @@ public class XmlRootElementPayloadMethodProcessorTest { } @Test - public void handleReturnValue() throws JAXBException, IOException, SAXException { + public void handleReturnValue() throws Exception { MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); MyRootElement rootElement = new MyRootElement(); @@ -110,6 +107,15 @@ public class XmlRootElementPayloadMethodProcessorTest { assertXMLEqual("Foo", response.getPayloadAsString()); } + @Test + public void handleNullReturnValue() throws Exception { + MessageContext messageContext = new DefaultMessageContext(new MockWebServiceMessageFactory()); + + MyRootElement rootElement = null; + processor.handleReturnValue(messageContext, rootElementReturnType, rootElement); + assertFalse("context has response", messageContext.hasResponse()); + } + @Test public void handleReturnValueAxiom() throws Exception { AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();