diff --git a/core-tiger/pom.xml b/core-tiger/pom.xml
index d91ea4b6..ec0729c7 100644
--- a/core-tiger/pom.xml
+++ b/core-tiger/pom.xml
@@ -1,4 +1,5 @@
-
GenericMarshallingMethodEndpointAdapter. The {@link Marshaller} and {@link
+ * Unmarshaller} must be injected using properties.
+ *
+ * @see #setMarshaller(org.springframework.oxm.Marshaller)
+ * @see #setUnmarshaller(org.springframework.oxm.Unmarshaller)
+ */
+ public GenericMarshallingMethodEndpointAdapter() {
+ }
+
+ /**
+ * Creates a new GenericMarshallingMethodEndpointAdapter with the given marshaller. If the given {@link
+ * Marshaller} also implements the {@link Unmarshaller} interface, it is used for both marshalling and
+ * unmarshalling. Otherwise, an exception is thrown.
+ *
+ * Note that all {@link Marshaller} implementations in Spring-WS also implement the {@link Unmarshaller} interface,
+ * so that you can safely use this constructor.
+ *
+ * @param marshaller object used as marshaller and unmarshaller
+ * @throws IllegalArgumentException when marshaller does not implement the {@link Unmarshaller}
+ * interface
+ */
+ public GenericMarshallingMethodEndpointAdapter(Marshaller marshaller) {
+ super(marshaller);
+ }
+
+ /**
+ * Creates a new GenericMarshallingMethodEndpointAdapter with the given marshaller and unmarshaller.
+ *
+ * @param marshaller the marshaller to use
+ * @param unmarshaller the unmarshaller to use
+ */
+ public GenericMarshallingMethodEndpointAdapter(Marshaller marshaller, Unmarshaller unmarshaller) {
+ super(marshaller, unmarshaller);
+ }
+
+ protected boolean supportsInternal(MethodEndpoint methodEndpoint) {
+ Method method = methodEndpoint.getMethod();
+ return supportsReturnType(method) && supportsParameters(method);
+ }
+
+ private boolean supportsReturnType(Method method) {
+ if (Void.TYPE.equals(method.getReturnType())) {
+ return true;
+ }
+ else {
+ if (getMarshaller() instanceof GenericMarshaller) {
+ return ((GenericMarshaller) getMarshaller()).supports(method.getGenericReturnType());
+ }
+ else {
+ return getMarshaller().supports(method.getReturnType());
+ }
+ }
+ }
+
+ private boolean supportsParameters(Method method) {
+ if (method.getParameterTypes().length != 1) {
+ return false;
+ }
+ else if (getUnmarshaller() instanceof GenericUnmarshaller) {
+ GenericUnmarshaller genericUnmarshaller = (GenericUnmarshaller) getUnmarshaller();
+ return genericUnmarshaller.supports(method.getGenericParameterTypes()[0]);
+ }
+ else {
+ return getUnmarshaller().supports(method.getParameterTypes()[0]);
+ }
+ }
+}
diff --git a/core-tiger/src/test/java/org/springframework/ws/server/endpoint/adapter/GenericMarshallingMethodEndpointAdapterTest.java b/core-tiger/src/test/java/org/springframework/ws/server/endpoint/adapter/GenericMarshallingMethodEndpointAdapterTest.java
new file mode 100644
index 00000000..a5ed2eaa
--- /dev/null
+++ b/core-tiger/src/test/java/org/springframework/ws/server/endpoint/adapter/GenericMarshallingMethodEndpointAdapterTest.java
@@ -0,0 +1,185 @@
+package org.springframework.ws.server.endpoint.adapter;
+
+import java.lang.reflect.Method;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+
+import junit.framework.TestCase;
+import static org.easymock.EasyMock.*;
+import org.springframework.oxm.GenericMarshaller;
+import org.springframework.oxm.GenericUnmarshaller;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.WebServiceMessageFactory;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.endpoint.MethodEndpoint;
+import org.springframework.xml.transform.StringResult;
+import org.springframework.xml.transform.StringSource;
+
+public class GenericMarshallingMethodEndpointAdapterTest extends TestCase {
+
+ private GenericMarshallingMethodEndpointAdapter adapter;
+
+ private boolean noResponseInvoked;
+
+ private GenericMarshaller marshallerMock;
+
+ private GenericUnmarshaller unmarshallerMock;
+
+ private boolean responseInvoked;
+
+ protected void setUp() throws Exception {
+ adapter = new GenericMarshallingMethodEndpointAdapter();
+ marshallerMock = createMock(GenericMarshaller.class);
+ adapter.setMarshaller(marshallerMock);
+ unmarshallerMock = createMock(GenericUnmarshaller.class);
+ adapter.setUnmarshaller(unmarshallerMock);
+ adapter.afterPropertiesSet();
+ }
+
+ public void testNoResponse() throws Exception {
+ WebServiceMessage messageMock = createMock(WebServiceMessage.class);
+ expect(messageMock.getPayloadSource()).andReturn(new StringSource("MarshallingMethodEndpointAdapter. The {@link Marshaller} and {@link Unmarshaller} must
* be injected using properties.
@@ -94,8 +86,8 @@ public class MarshallingMethodEndpointAdapter extends AbstractMethodEndpointAdap
"MarshallingMethodEndpointAdapter(Marshaller, Unmarshaller) constructor.");
}
else {
- this.marshaller = marshaller;
- this.unmarshaller = (Unmarshaller) marshaller;
+ this.setMarshaller(marshaller);
+ this.setUnmarshaller((Unmarshaller) marshaller);
}
}
@@ -108,13 +100,58 @@ public class MarshallingMethodEndpointAdapter extends AbstractMethodEndpointAdap
public MarshallingMethodEndpointAdapter(Marshaller marshaller, Unmarshaller unmarshaller) {
Assert.notNull(marshaller, "marshaller must not be null");
Assert.notNull(unmarshaller, "unmarshaller must not be null");
+ this.setMarshaller(marshaller);
+ this.setUnmarshaller(unmarshaller);
+ }
+
+ /** Returns the marshaller used for transforming objects into XML. */
+ public Marshaller getMarshaller() {
+ return marshaller;
+ }
+
+ /** Sets the marshaller used for transforming objects into XML. */
+ public final void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
+ }
+
+ /** Returns the unmarshaller used for transforming XML into objects. */
+ public Unmarshaller getUnmarshaller() {
+ return unmarshaller;
+ }
+
+ /** Sets the unmarshaller used for transforming XML into objects. */
+ public final void setUnmarshaller(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}
public void afterPropertiesSet() throws Exception {
- Assert.notNull(marshaller, "marshaller is required");
- Assert.notNull(unmarshaller, "unmarshaller is required");
+ Assert.notNull(getMarshaller(), "marshaller is required");
+ Assert.notNull(getUnmarshaller(), "unmarshaller is required");
+ }
+
+ protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception {
+ WebServiceMessage request = messageContext.getRequest();
+ Object requestObject = unmarshalRequest(request);
+ Object responseObject = methodEndpoint.invoke(new Object[]{requestObject});
+ if (responseObject != null) {
+ WebServiceMessage response = messageContext.getResponse();
+ marshalResponse(responseObject, response);
+ }
+ }
+
+ private Object unmarshalRequest(WebServiceMessage request) throws IOException {
+ Object requestObject = MarshallingUtils.unmarshal(getUnmarshaller(), request);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unmarshalled payload request to [" + requestObject + "]");
+ }
+ return requestObject;
+ }
+
+ private void marshalResponse(Object responseObject, WebServiceMessage response) throws IOException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Marshalling [" + responseObject + "] to response payload");
+ }
+ MarshallingUtils.marshal(getMarshaller(), responseObject, response);
}
/**
@@ -126,34 +163,19 @@ public class MarshallingMethodEndpointAdapter extends AbstractMethodEndpointAdap
*/
protected boolean supportsInternal(MethodEndpoint methodEndpoint) {
Method method = methodEndpoint.getMethod();
- return (Void.TYPE.isAssignableFrom(method.getReturnType()) || marshaller.supports(method.getReturnType())) &&
- method.getParameterTypes().length == 1 && unmarshaller.supports(method.getParameterTypes()[0]);
+ return supportsReturnType(method) && supportsParameters(method);
}
- protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception {
- WebServiceMessage request = messageContext.getRequest();
- Object requestObject = unmarshalRequest(request);
- Object responseObject = methodEndpoint.invoke(new Object[]{requestObject});
- if (responseObject != null) {
- WebServiceMessage response = messageContext.getResponse();
- marshalResponse(responseObject, response);
+ private boolean supportsReturnType(Method method) {
+ return (Void.TYPE.equals(method.getReturnType()) || getMarshaller().supports(method.getReturnType()));
+ }
+
+ private boolean supportsParameters(Method method) {
+ if (method.getParameterTypes().length != 1) {
+ return false;
}
-
- }
-
- private Object unmarshalRequest(WebServiceMessage request) throws IOException {
- Object requestObject = MarshallingUtils.unmarshal(unmarshaller, request);
- if (logger.isDebugEnabled()) {
- logger.debug("Unmarshalled payload request to [" + requestObject + "]");
+ else {
+ return getUnmarshaller().supports(method.getParameterTypes()[0]);
}
- return requestObject;
}
-
- private void marshalResponse(Object responseObject, WebServiceMessage response) throws IOException {
- if (logger.isDebugEnabled()) {
- logger.debug("Marshalling [" + responseObject + "] to response payload");
- }
- MarshallingUtils.marshal(marshaller, responseObject, response);
- }
-
}
diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapterTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapterTest.java
index f130d5cd..0fb24082 100644
--- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapterTest.java
+++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapterTest.java
@@ -160,7 +160,8 @@ public class MarshallingMethodEndpointAdapterTest extends TestCase {
return s;
}
- public static class MyType {
+ private static class MyType {
}
+
}
\ No newline at end of file
diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/PayloadMethodEndpointAdapterTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/PayloadMethodEndpointAdapterTest.java
index ea4aa2ef..5707de84 100644
--- a/core/src/test/java/org/springframework/ws/server/endpoint/adapter/PayloadMethodEndpointAdapterTest.java
+++ b/core/src/test/java/org/springframework/ws/server/endpoint/adapter/PayloadMethodEndpointAdapterTest.java
@@ -17,6 +17,8 @@
package org.springframework.ws.server.endpoint.adapter;
import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
import junit.framework.TestCase;
import org.springframework.ws.MockWebServiceMessage;
@@ -42,12 +44,12 @@ public class PayloadMethodEndpointAdapterTest extends TestCase {
}
public void testSupportedNoResponse() throws NoSuchMethodException {
- MethodEndpoint methodEndpoint = new MethodEndpoint(this, "noResponse", new Class[]{Source.class});
+ MethodEndpoint methodEndpoint = new MethodEndpoint(this, "noResponse", new Class[]{DOMSource.class});
assertTrue("Method unsupported", adapter.supportsInternal(methodEndpoint));
}
public void testSupportedResponse() throws NoSuchMethodException {
- MethodEndpoint methodEndpoint = new MethodEndpoint(this, "response", new Class[]{Source.class});
+ MethodEndpoint methodEndpoint = new MethodEndpoint(this, "response", new Class[]{StreamSource.class});
assertTrue("Method unsupported", adapter.supportsInternal(methodEndpoint));
}
@@ -67,7 +69,7 @@ public class PayloadMethodEndpointAdapterTest extends TestCase {
}
public void testNoResponse() throws Exception {
- MethodEndpoint methodEndpoint = new MethodEndpoint(this, "noResponse", new Class[]{Source.class});
+ MethodEndpoint methodEndpoint = new MethodEndpoint(this, "noResponse", new Class[]{DOMSource.class});
assertFalse("Method invoked", noResponseInvoked);
adapter.invoke(messageContext, methodEndpoint);
assertTrue("Method not invoked", noResponseInvoked);
@@ -76,17 +78,17 @@ public class PayloadMethodEndpointAdapterTest extends TestCase {
public void testResponse() throws Exception {
WebServiceMessage request = new MockWebServiceMessage("true if this marshaller can indeed marshal instances of the supplied type;
+ * false otherwise
+ */
+ boolean supports(Type type);
+}
diff --git a/oxm-tiger/src/main/java/org/springframework/oxm/GenericUnmarshaller.java b/oxm-tiger/src/main/java/org/springframework/oxm/GenericUnmarshaller.java
new file mode 100644
index 00000000..f9bdc677
--- /dev/null
+++ b/oxm-tiger/src/main/java/org/springframework/oxm/GenericUnmarshaller.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ * 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.oxm;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+/**
+ * Extension of the {@link Unmarshaller} interface that supports Java 5 generics. More specifically, this unmarshaller
+ * adds support for the new {@link Type} hierarchy, returned by methods such as {@link
+ * Method#getGenericParameterTypes()} and {@link Method#getGenericReturnType()}.
+ *
+ * @author Arjen Poutsma
+ * @since 1.0.2
+ */
+public interface GenericUnmarshaller extends Unmarshaller {
+
+ /**
+ * Indicates whether this unmarshaller can unmarshal instances of the supplied type.
+ *
+ * @param type the type that this unmarshaller is being asked if it can marshal
+ * @return true if this unmarshaller can indeed unmarshal to the supplied type; false
+ * otherwise
+ */
+ boolean supports(Type type);
+}
diff --git a/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
index f449ad26..254365aa 100644
--- a/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
+++ b/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
@@ -21,11 +21,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
-import java.util.HashMap;
+import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import javax.activation.DataHandler;
@@ -34,9 +36,10 @@ import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
-import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.attachment.AttachmentMarshaller;
import javax.xml.bind.attachment.AttachmentUnmarshaller;
@@ -45,6 +48,8 @@ import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import org.springframework.core.io.Resource;
+import org.springframework.oxm.GenericMarshaller;
+import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.oxm.mime.MimeMarshaller;
@@ -77,7 +82,8 @@ import org.springframework.xml.validation.SchemaLoaderUtils;
* @see #setAdapters(javax.xml.bind.annotation.adapters.XmlAdapter[])
* @since 1.0.0
*/
-public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarshaller, MimeUnmarshaller {
+public class Jaxb2Marshaller extends AbstractJaxbMarshaller
+ implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller {
private Resource[] schemaResources;
@@ -97,8 +103,6 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
private boolean mtomEnabled = false;
- private MapXmlAdapters to be registered with the JAXB Marshaller and
* Unmarshaller
@@ -163,28 +167,56 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
this.unmarshallerListener = unmarshallerListener;
}
- public boolean supports(Class clazz) {
- if (JAXBElement.class.isAssignableFrom(clazz)) {
- return true;
+ public boolean supports(Type type) {
+ if (type instanceof Class) {
+ return supportsInternal((Class) type, true);
}
- else if (!supportedClasses.containsKey(clazz)) {
- boolean supported = false;
- Object instance = null;
- try {
- instance = clazz.newInstance();
+ else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ if (JAXBElement.class.equals(parameterizedType.getRawType())) {
+ Type[] typeArguments = parameterizedType.getActualTypeArguments();
+ for (int i = 0; i < typeArguments.length; i++) {
+ if (typeArguments[i] instanceof Class) {
+ if (!supportsInternal((Class) typeArguments[i], false)) {
+ return false;
+ }
+ }
+ else if (!supports(typeArguments[i])) {
+ return false;
+ }
+ }
+ return true;
}
- catch (InstantiationException e) {
- // not supported
- }
- catch (IllegalAccessException e) {
- // not supported
- }
- JAXBIntrospector introspector = getJaxbContext().createJAXBIntrospector();
- supported = introspector.isElement(instance);
- supportedClasses.put(clazz, supported);
}
- return supportedClasses.get(clazz);
+ return false;
}
+
+ public boolean supports(Class clazz) {
+ return supportsInternal(clazz, true);
+ }
+
+ private boolean supportsInternal(Class> clazz, boolean checkForXmlRootElement) {
+ if (checkForXmlRootElement && clazz.getAnnotation(XmlRootElement.class) == null) {
+ return false;
+ }
+ if (clazz.getAnnotation(XmlType.class) == null) {
+ return false;
+ }
+ if (StringUtils.hasLength(getContextPath())) {
+ String className = ClassUtils.getQualifiedName(clazz);
+ int lastDotIndex = className.lastIndexOf('.');
+ if (lastDotIndex == -1) {
+ return false;
+ }
+ String packageName = className.substring(0, lastDotIndex);
+ return getContextPath().startsWith(packageName);
+ }
+ else if (!ObjectUtils.isEmpty(classesToBeBound)) {
+ return Arrays.asList(classesToBeBound).contains(clazz);
+ }
+ return false;
+ }
+
/*
* JAXBContext
*/
diff --git a/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTest.java b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTest.java
index 49d3e384..136b554e 100644
--- a/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTest.java
+++ b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2MarshallerTest.java
@@ -18,10 +18,12 @@ package org.springframework.oxm.jaxb;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
+import java.lang.reflect.Method;
import java.util.Collections;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLEventWriter;
@@ -39,6 +41,7 @@ import org.springframework.core.io.Resource;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.jaxb2.FlightType;
import org.springframework.oxm.jaxb2.Flights;
+import org.springframework.oxm.jaxb2.ObjectFactory;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.util.FileCopyUtils;
import org.springframework.xml.transform.StaxResult;
@@ -192,8 +195,16 @@ public class Jaxb2MarshallerTest extends XMLTestCase {
}
public void testSupports() throws Exception {
- assertTrue("Jaxb2Marshaller does not support Flights", marshaller.supports(Flights.class));
- assertTrue("Jaxb2Marshaller does not support JAXBElement", marshaller.supports(JAXBElement.class));
+ Method createFlights = ObjectFactory.class.getDeclaredMethod("createFlights");
+ assertTrue("Jaxb2Marshaller does not support Flights",
+ marshaller.supports(createFlights.getGenericReturnType()));
+ Method createFlight = ObjectFactory.class.getDeclaredMethod("createFlight", FlightType.class);
+ assertTrue("Jaxb2Marshaller does not support JAXBElement