diff --git a/core-tiger/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java b/core-tiger/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java
index 7fba0fc2..4faab66b 100644
--- a/core-tiger/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java
+++ b/core-tiger/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java
@@ -24,7 +24,7 @@ import org.springframework.util.StringUtils;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
-import org.springframework.ws.server.endpoint.mapping.support.PayloadRootUtils;
+import org.springframework.ws.server.endpoint.support.PayloadRootUtils;
/**
* Implementation of the {@link EndpointMapping} interface that uses the {@link PayloadRoot} annotation to map methods
diff --git a/core/src/main/java/org/springframework/ws/mime/MimeMessage.java b/core/src/main/java/org/springframework/ws/mime/MimeMessage.java
index e58d372f..854e72b5 100644
--- a/core/src/main/java/org/springframework/ws/mime/MimeMessage.java
+++ b/core/src/main/java/org/springframework/ws/mime/MimeMessage.java
@@ -25,6 +25,14 @@ public interface MimeMessage extends WebServiceMessage {
*/
boolean isXopPackage();
+ /**
+ * Turns this message into a XOP package.
+ *
+ * @return true when the message is a XOP package
+ * @see XOP Packages
+ */
+ boolean convertToXopPackage();
+
/**
* Returns the Attachment with the specified content Id.
*
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
index be26cd92..3d1700e7 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractMarshallingPayloadEndpoint.java
@@ -17,21 +17,16 @@
package org.springframework.ws.server.endpoint;
import java.io.IOException;
-import javax.activation.DataHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
-import org.springframework.oxm.mime.MimeContainer;
-import org.springframework.oxm.mime.MimeMarshaller;
-import org.springframework.oxm.mime.MimeUnmarshaller;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
-import org.springframework.ws.mime.Attachment;
-import org.springframework.ws.mime.MimeMessage;
+import org.springframework.ws.server.endpoint.support.MarshallingUtils;
/**
* Endpoint that unmarshals the request payload, and marshals the response object. This endpoint needs a
@@ -84,8 +79,8 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
"AbstractMarshallingPayloadEndpoint(Marshaller, Unmarshaller) constructor.");
}
else {
- this.marshaller = marshaller;
- this.unmarshaller = (Unmarshaller) marshaller;
+ this.setMarshaller(marshaller);
+ this.setUnmarshaller((Unmarshaller) marshaller);
}
}
@@ -98,12 +93,12 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
protected AbstractMarshallingPayloadEndpoint(Marshaller marshaller, Unmarshaller unmarshaller) {
Assert.notNull(marshaller, "marshaller must not be null");
Assert.notNull(unmarshaller, "unmarshaller must not be null");
- this.marshaller = marshaller;
- this.unmarshaller = unmarshaller;
+ this.setMarshaller(marshaller);
+ this.setUnmarshaller(unmarshaller);
}
/** Returns the marshaller used for transforming objects into XML. */
- public final Marshaller getMarshaller() {
+ public Marshaller getMarshaller() {
return marshaller;
}
@@ -113,7 +108,7 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
}
/** Returns the unmarshaller used for transforming XML into objects. */
- public final Unmarshaller getUnmarshaller() {
+ public Unmarshaller getUnmarshaller() {
return unmarshaller;
}
@@ -123,8 +118,8 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
}
public final 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");
afterMarshallerSet();
}
@@ -139,15 +134,7 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
}
private Object unmarshalRequest(WebServiceMessage request) throws IOException {
- Object requestObject;
- if (unmarshaller instanceof MimeUnmarshaller && request instanceof MimeMessage) {
- MimeUnmarshaller mimeUnmarshaller = (MimeUnmarshaller) unmarshaller;
- MimeMessageContainer container = new MimeMessageContainer((MimeMessage) request);
- requestObject = mimeUnmarshaller.unmarshal(request.getPayloadSource(), container);
- }
- else {
- requestObject = unmarshaller.unmarshal(request.getPayloadSource());
- }
+ Object requestObject = MarshallingUtils.unmarshal(getUnmarshaller(), request);
if (logger.isDebugEnabled()) {
logger.debug("Unmarshalled payload request to [" + requestObject + "]");
}
@@ -158,14 +145,7 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
if (logger.isDebugEnabled()) {
logger.debug("Marshalling [" + responseObject + "] to response payload");
}
- if (marshaller instanceof MimeMarshaller && response instanceof MimeMessage) {
- MimeMarshaller mimeMarshaller = (MimeMarshaller) marshaller;
- MimeMessageContainer container = new MimeMessageContainer((MimeMessage) response);
- mimeMarshaller.marshal(responseObject, response.getPayloadResult(), container);
- }
- else {
- marshaller.marshal(responseObject, response.getPayloadResult());
- }
+ MarshallingUtils.marshal(getMarshaller(), responseObject, response);
}
/**
@@ -179,33 +159,11 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo
/**
* Template method that subclasses must implement to process a request.
*
null.
+ * The unmarshaled request object is passed as a parameter, and the returned object is marshalled to a response. If
+ * no response is required, return null.
*
* @param requestObject the unnmarshalled message payload as an object
* @return the object to be marshalled as response, or null if a response is not required
*/
protected abstract Object invokeInternal(Object requestObject) throws Exception;
-
- private static class MimeMessageContainer implements MimeContainer {
-
- private final MimeMessage mimeMessage;
-
- public MimeMessageContainer(MimeMessage mimeMessage) {
- this.mimeMessage = mimeMessage;
- }
-
- public boolean isXopPackage() {
- return mimeMessage.isXopPackage();
- }
-
- public void addAttachment(String contentId, DataHandler dataHandler) {
- mimeMessage.addAttachment(contentId, dataHandler);
- }
-
- public DataHandler getAttachment(String contentId) {
- Attachment attachment = mimeMessage.getAttachment(contentId);
- return attachment.getDataHandler();
- }
- }
}
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapter.java b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapter.java
index 6e9be87c..348ac3bd 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapter.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/adapter/MarshallingMethodEndpointAdapter.java
@@ -16,6 +16,7 @@
package org.springframework.ws.server.endpoint.adapter;
+import java.io.IOException;
import java.lang.reflect.Method;
import org.springframework.beans.factory.InitializingBean;
@@ -26,6 +27,7 @@ import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.endpoint.MethodEndpoint;
+import org.springframework.ws.server.endpoint.support.MarshallingUtils;
/**
* Adapter that supports endpoint methods that use marshalling. Supports methods with the following signature:
@@ -62,8 +64,8 @@ public class MarshallingMethodEndpointAdapter extends AbstractMethodEndpointAdap
}
/**
- * Creates a new MarshallingMethodEndpointAdapter. The {@link Marshaller} and {@link Unmarshaller}
- * must be injected using properties.
+ * Creates a new MarshallingMethodEndpointAdapter. The {@link Marshaller} and {@link Unmarshaller} must
+ * be injected using properties.
*
* @see #setMarshaller(org.springframework.oxm.Marshaller)
* @see #setUnmarshaller(org.springframework.oxm.Unmarshaller)
@@ -129,17 +131,28 @@ public class MarshallingMethodEndpointAdapter extends AbstractMethodEndpointAdap
protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception {
WebServiceMessage request = messageContext.getRequest();
- Object requestObject = unmarshaller.unmarshal(request.getPayloadSource());
+ 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(unmarshaller, request);
if (logger.isDebugEnabled()) {
logger.debug("Unmarshalled payload request to [" + requestObject + "]");
}
- Object responseObject = methodEndpoint.invoke(new Object[]{requestObject});
- if (responseObject != null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Marshalling [" + responseObject + "] to response payload");
- }
- WebServiceMessage response = messageContext.getResponse();
- marshaller.marshal(responseObject, response.getPayloadResult());
- }
+ 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/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameEndpointMapping.java b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameEndpointMapping.java
index fc14c122..ea3d1409 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameEndpointMapping.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootQNameEndpointMapping.java
@@ -21,7 +21,7 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import org.springframework.ws.context.MessageContext;
-import org.springframework.ws.server.endpoint.mapping.support.PayloadRootUtils;
+import org.springframework.ws.server.endpoint.support.PayloadRootUtils;
/**
* Implementation of the EndpointMapping interface to map from the qualified name of the request payload
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java
index 18c7a19f..756f3454 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/SimpleMethodEndpointMapping.java
@@ -25,7 +25,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
-import org.springframework.ws.server.endpoint.mapping.support.PayloadRootUtils;
+import org.springframework.ws.server.endpoint.support.PayloadRootUtils;
/**
* Simple subclass of {@link AbstractMethodEndpointMapping} that maps from the local name of the request payload to
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/package.html b/core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/package.html
deleted file mode 100644
index 47b2bec2..00000000
--- a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-Provides helper classes for EndpointMapping implementations.
-
-
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/support/MarshallingUtils.java b/core/src/main/java/org/springframework/ws/server/endpoint/support/MarshallingUtils.java
new file mode 100644
index 00000000..9ac87a5d
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/support/MarshallingUtils.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ws.server.endpoint.support;
+
+import java.io.IOException;
+import javax.activation.DataHandler;
+
+import org.springframework.oxm.Marshaller;
+import org.springframework.oxm.Unmarshaller;
+import org.springframework.oxm.mime.MimeContainer;
+import org.springframework.oxm.mime.MimeMarshaller;
+import org.springframework.oxm.mime.MimeUnmarshaller;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.mime.Attachment;
+import org.springframework.ws.mime.MimeMessage;
+
+/**
+ * Helper class for endpoints and endpoint mappings that use marshalling.
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class MarshallingUtils {
+
+ private MarshallingUtils() {
+ }
+
+ /**
+ * Unmarshals the payload of the given message using the provided {@link Unmarshaller}.
+ *
+ * @param unmarshaller the unmarshaller
+ * @param message the message of which the payload is to be unmarshalled
+ * @return the unmarshalled object
+ * @throws IOException in case of I/O errors
+ */
+ public static Object unmarshal(Unmarshaller unmarshaller, WebServiceMessage message) throws IOException {
+ if (unmarshaller instanceof MimeUnmarshaller && message instanceof MimeMessage) {
+ MimeUnmarshaller mimeUnmarshaller = (MimeUnmarshaller) unmarshaller;
+ MimeMessageContainer container = new MimeMessageContainer((MimeMessage) message);
+ return mimeUnmarshaller.unmarshal(message.getPayloadSource(), container);
+ }
+ else {
+ return unmarshaller.unmarshal(message.getPayloadSource());
+ }
+ }
+
+ /**
+ * Marshals the given object to the payload of the given message using the provided {@link Marshaller}.
+ *
+ * @param marshaller the marshaller
+ * @param graph the root of the object graph to marshal
+ * @param message the message of which the payload is to be unmarshalled
+ * @throws IOException in case of I/O errors
+ */
+ public static void marshal(Marshaller marshaller, Object graph, WebServiceMessage message) throws IOException {
+ if (marshaller instanceof MimeMarshaller && message instanceof MimeMessage) {
+ MimeMarshaller mimeMarshaller = (MimeMarshaller) marshaller;
+ MimeMessageContainer container = new MimeMessageContainer((MimeMessage) message);
+ mimeMarshaller.marshal(graph, message.getPayloadResult(), container);
+ }
+ else {
+ marshaller.marshal(graph, message.getPayloadResult());
+ }
+ }
+
+ private static class MimeMessageContainer implements MimeContainer {
+
+ private final MimeMessage mimeMessage;
+
+ public MimeMessageContainer(MimeMessage mimeMessage) {
+ this.mimeMessage = mimeMessage;
+ }
+
+ public boolean isXopPackage() {
+ return mimeMessage.isXopPackage();
+ }
+
+ public boolean convertToXopPackage() {
+ return mimeMessage.convertToXopPackage();
+ }
+
+ public void addAttachment(String contentId, DataHandler dataHandler) {
+ mimeMessage.addAttachment(contentId, dataHandler);
+ }
+
+ public DataHandler getAttachment(String contentId) {
+ Attachment attachment = mimeMessage.getAttachment(contentId);
+ return attachment != null ? attachment.getDataHandler() : null;
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtils.java b/core/src/main/java/org/springframework/ws/server/endpoint/support/PayloadRootUtils.java
similarity index 97%
rename from core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtils.java
rename to core/src/main/java/org/springframework/ws/server/endpoint/support/PayloadRootUtils.java
index b47f1cb4..e454bd6d 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtils.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/support/PayloadRootUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ws.server.endpoint.mapping.support;
+package org.springframework.ws.server.endpoint.support;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
@@ -38,6 +38,10 @@ import org.w3c.dom.Node;
*/
public abstract class PayloadRootUtils {
+ private PayloadRootUtils() {
+
+ }
+
/**
* Returns the root qualified name of the given source, transforming it if necessary.
*
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/support/package.html b/core/src/main/java/org/springframework/ws/server/endpoint/support/package.html
new file mode 100644
index 00000000..73afc656
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/support/package.html
@@ -0,0 +1,6 @@
+
+
+Provides helper classes for EndpointAdapter, EndpointInteceptor, and
+EndpointMapping implementations.
+
+
diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessage.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessage.java
index 404f6ff5..970d23ce 100644
--- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessage.java
+++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessage.java
@@ -23,9 +23,11 @@ import javax.activation.DataHandler;
import javax.xml.stream.XMLStreamException;
import org.apache.axiom.attachments.Attachments;
+import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.impl.MTOMConstants;
+import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPMessage;
@@ -81,11 +83,7 @@ public class AxiomSoapMessage extends AbstractSoapMessage {
* @param payloadCaching whether the contents of the SOAP body should be cached or not
*/
public AxiomSoapMessage(SOAPMessage soapMessage, String soapAction, boolean payloadCaching) {
- axiomMessage = soapMessage;
- axiomFactory = (SOAPFactory) soapMessage.getSOAPEnvelope().getOMFactory();
- this.attachments = new Attachments();
- this.soapAction = soapAction;
- this.payloadCaching = payloadCaching;
+ this(soapMessage, new Attachments(), soapAction, payloadCaching);
}
/**
@@ -100,6 +98,8 @@ public class AxiomSoapMessage extends AbstractSoapMessage {
Attachments attachments,
String soapAction,
boolean payloadCaching) {
+ Assert.notNull(soapMessage, "'soapMessage' must not be null");
+ Assert.notNull(attachments, "'attachments' must not be null");
axiomMessage = soapMessage;
axiomFactory = (SOAPFactory) soapMessage.getSOAPEnvelope().getOMFactory();
this.attachments = attachments;
@@ -147,7 +147,15 @@ public class AxiomSoapMessage extends AbstractSoapMessage {
}
}
+ public boolean convertToXopPackage() {
+ return false;
+ }
+
public Attachment getAttachment(String contentId) {
+ Assert.hasLength(contentId, "contentId must not be empty");
+ if (contentId.startsWith("<") && contentId.endsWith(">")) {
+ contentId = contentId.substring(1, contentId.length() - 1);
+ }
DataHandler dataHandler = attachments.getDataHandler(contentId);
return dataHandler != null ? new AxiomAttachment(contentId, dataHandler) : null;
}
@@ -191,6 +199,27 @@ public class AxiomSoapMessage extends AbstractSoapMessage {
}
}
+ public String toString() {
+ StringBuffer buffer = new StringBuffer("AxiomSoapMessage");
+ try {
+ SOAPEnvelope envelope = axiomMessage.getSOAPEnvelope();
+ if (envelope != null) {
+ SOAPBody body = envelope.getBody();
+ if (body != null) {
+ OMElement bodyElement = body.getFirstElement();
+ if (bodyElement != null) {
+ buffer.append(' ');
+ buffer.append(bodyElement.getQName());
+ }
+ }
+ }
+ }
+ catch (OMException ex) {
+ // ignore
+ }
+ return buffer.toString();
+ }
+
private class AxiomAttachmentIterator implements Iterator {
private final Iterator iterator;
diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java
index 376fd4db..ca3b90f9 100644
--- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java
+++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessage.java
@@ -37,6 +37,7 @@ import org.springframework.ws.soap.AbstractSoapMessage;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.saaj.support.SaajUtils;
+import org.springframework.ws.transport.TransportConstants;
/**
* SAAJ-specific implementation of the {@link SoapMessage} interface. Created via the {@link SaajSoapMessageFactory},
@@ -47,16 +48,12 @@ import org.springframework.ws.soap.saaj.support.SaajUtils;
*/
public class SaajSoapMessage extends AbstractSoapMessage {
- private static final String MIME_HEADER_SOAP_ACTION = "SOAPAction";
-
- private static final String MIME_HEADER_CONTENT_ID = "Content-Id";
-
- private static final String MIME_HEADER_CONTENT_TYPE = "Content-Type";
-
private SOAPMessage saajMessage;
private SoapEnvelope envelope;
+ private static final String CONTENT_TYPE_XOP = "application/xop+xml";
+
/**
* Create a new SaajSoapMessage based on the given SAAJ SOAPMessage.
*
@@ -93,13 +90,13 @@ public class SaajSoapMessage extends AbstractSoapMessage {
public String getSoapAction() {
MimeHeaders mimeHeaders = getImplementation().getMimeHeaders(getSaajMessage());
- String[] values = mimeHeaders.getHeader(MIME_HEADER_SOAP_ACTION);
+ String[] values = mimeHeaders.getHeader(TransportConstants.HEADER_SOAP_ACTION);
return ObjectUtils.isEmpty(values) ? null : values[0];
}
public void setSoapAction(String soapAction) {
MimeHeaders mimeHeaders = getImplementation().getMimeHeaders(getSaajMessage());
- mimeHeaders.setHeader(MIME_HEADER_SOAP_ACTION, soapAction);
+ mimeHeaders.setHeader(TransportConstants.HEADER_SOAP_ACTION, soapAction);
}
public void writeTo(OutputStream outputStream) throws IOException {
@@ -113,16 +110,55 @@ public class SaajSoapMessage extends AbstractSoapMessage {
}
public boolean isXopPackage() {
- SOAPPart saajPart = saajMessage.getSOAPPart();
- String[] contentTypes = saajPart.getMimeHeader(MIME_HEADER_CONTENT_TYPE);
- for (int i = 0; i < contentTypes.length; i++) {
- if (contentTypes[i].indexOf("application/xop+xml") != -1) {
- return true;
+ if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) {
+ SOAPPart saajPart = saajMessage.getSOAPPart();
+ String[] contentTypes = saajPart.getMimeHeader(TransportConstants.HEADER_CONTENT_TYPE);
+ for (int i = 0; i < contentTypes.length; i++) {
+ if (contentTypes[i].indexOf(CONTENT_TYPE_XOP) != -1) {
+ return true;
+ }
}
}
return false;
}
+ public boolean convertToXopPackage() {
+ if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) {
+ convertMessageToXop();
+ convertPartToXop();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private void convertMessageToXop() {
+ MimeHeaders mimeHeaders = saajMessage.getMimeHeaders();
+ String[] oldContentTypes = mimeHeaders.getHeader(TransportConstants.HEADER_CONTENT_TYPE);
+ String oldContentType =
+ !ObjectUtils.isEmpty(oldContentTypes) ? oldContentTypes[0] : getVersion().getContentType();
+ StringBuffer buffer = new StringBuffer(CONTENT_TYPE_XOP);
+ buffer.append(";type=");
+ buffer.append('"');
+ buffer.append(oldContentType);
+ buffer.append('"');
+ mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_TYPE, buffer.toString());
+ }
+
+ private void convertPartToXop() {
+ SOAPPart saajPart = saajMessage.getSOAPPart();
+ String[] oldContentTypes = saajPart.getMimeHeader(TransportConstants.HEADER_CONTENT_TYPE);
+ String oldContentType =
+ !ObjectUtils.isEmpty(oldContentTypes) ? oldContentTypes[0] : getVersion().getContentType();
+ StringBuffer buffer = new StringBuffer(CONTENT_TYPE_XOP);
+ buffer.append(";type=");
+ buffer.append('"');
+ buffer.append(oldContentType);
+ buffer.append('"');
+ saajPart.setMimeHeader(TransportConstants.HEADER_CONTENT_TYPE, buffer.toString());
+ }
+
public Iterator getAttachments() throws AttachmentException {
Iterator iterator = getImplementation().getAttachments(getSaajMessage());
return new SaajAttachmentIterator(iterator);
@@ -131,13 +167,8 @@ public class SaajSoapMessage extends AbstractSoapMessage {
public Attachment getAttachment(String contentId) {
Assert.hasLength(contentId, "contentId must not be empty");
MimeHeaders mimeHeaders = new MimeHeaders();
- mimeHeaders.setHeader(MIME_HEADER_CONTENT_ID, contentId);
+ mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_ID, contentId);
Iterator iterator = getImplementation().getAttachment(getSaajMessage(), mimeHeaders);
- if (!iterator.hasNext()) {
- // try to prefix it with an MTOM-specific < and >
- mimeHeaders.setHeader(MIME_HEADER_CONTENT_ID, "<" + contentId + ">");
- iterator = getImplementation().getAttachment(getSaajMessage(), mimeHeaders);
- }
if (!iterator.hasNext()) {
return null;
}
diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java
index 93f3d7ba..9f6bdea0 100644
--- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java
+++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java
@@ -56,6 +56,8 @@ public class SaajSoapMessageFactory implements SoapMessageFactory, InitializingB
private String messageFactoryProtocol;
+ private static final String CONTENT_TYPE = "Content-Type";
+
/** Default, empty constructor. */
public SaajSoapMessageFactory() {
}
@@ -161,6 +163,19 @@ public class SaajSoapMessageFactory implements SoapMessageFactory, InitializingB
return new SaajSoapMessage(messageFactory.createMessage(mimeHeaders, inputStream));
}
catch (SOAPException ex) {
+ // SAAJ 1.3 RI has a issue with handling multipart XOP content types which contain "startinfo" rather than
+ // "start-info", so let's try and do something about it
+ String contentType = StringUtils.arrayToCommaDelimitedString(mimeHeaders.getHeader(CONTENT_TYPE));
+ if (contentType.indexOf("startinfo") != -1) {
+ contentType = contentType.replace("startinfo", "start-info");
+ mimeHeaders.setHeader(CONTENT_TYPE, contentType);
+ try {
+ return new SaajSoapMessage(messageFactory.createMessage(mimeHeaders, inputStream));
+ }
+ catch (SOAPException e) {
+ // fall-through
+ }
+ }
throw new SoapMessageCreationException("Could not create message from InputStream: " + ex.getMessage(), ex);
}
}
diff --git a/core/src/main/java/org/springframework/ws/transport/TransportConstants.java b/core/src/main/java/org/springframework/ws/transport/TransportConstants.java
new file mode 100644
index 00000000..abc78547
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/transport/TransportConstants.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ws.transport;
+
+/**
+ * Declares useful transport constants.
+ *
+ * @author Arjen Poutsma
+ */
+public interface TransportConstants {
+
+ /** The "Content-Id" header. */
+ String HEADER_CONTENT_ID = "Content-Id";
+
+ /** The "Content-Type" header. */
+ String HEADER_CONTENT_TYPE = "Content-Type";
+
+ /** The "Content-Length" header. */
+ String HEADER_CONTENT_LENGTH = "Content-Length";
+
+ /** The "SOAPAction" header. */
+ String HEADER_SOAP_ACTION = "SOAPAction";
+
+}
diff --git a/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java b/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java
index 82306cc1..a6d1b3af 100644
--- a/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java
+++ b/core/src/test/java/org/springframework/ws/MockWebServiceMessage.java
@@ -125,6 +125,13 @@ public class MockWebServiceMessage implements WebServiceMessage {
writer.write(content.toString());
}
+ public String toString() {
+ StringBuffer buffer = new StringBuffer("MockWebServiceMessage {");
+ buffer.append(content);
+ buffer.append('}');
+ return buffer.toString();
+ }
+
private class StringBufferWriter extends Writer {
private StringBufferWriter() {
diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtilsTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/support/PayloadRootUtilsTest.java
similarity index 98%
rename from core/src/test/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtilsTest.java
rename to core/src/test/java/org/springframework/ws/server/endpoint/support/PayloadRootUtilsTest.java
index 74307e8c..3f960e6d 100644
--- a/core/src/test/java/org/springframework/ws/server/endpoint/mapping/support/PayloadRootUtilsTest.java
+++ b/core/src/test/java/org/springframework/ws/server/endpoint/support/PayloadRootUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ws.server.endpoint.mapping.support;
+package org.springframework.ws.server.endpoint.support;
import java.io.StringReader;
import javax.xml.namespace.QName;
diff --git a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java
index f606f5d8..0c6c67c3 100644
--- a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java
+++ b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java
@@ -88,7 +88,27 @@ public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapM
Iterator iter = soapMessage.getAttachments();
assertTrue("No attachments read", iter.hasNext());
- Attachment attachment = soapMessage.getAttachment("1.urn:uuid:492264AB42E57108E01176731445504@apache.org");
+ Attachment attachment = soapMessage.getAttachment("<1.urn:uuid:492264AB42E57108E01176731445504@apache.org>");
+ assertNotNull("No attachment read", attachment);
+ }
+
+ public void testCreateSoapMessageMtomWeirdStartInfo() throws Exception {
+ InputStream is = AbstractSoap11MessageFactoryTestCase.class.getResourceAsStream("soap11-mtom.bin");
+ Properties headers = new Properties();
+ headers.setProperty("Content-Type", "multipart/related;" + "startinfo=\"text/xml\";" +
+ "type=\"application/xop+xml\";" + "start=\"<0.urn:uuid:492264AB42E57108E01176731445508@apache.org>\";" +
+ "boundary=\"MIMEBoundaryurn_uuid_492264AB42E57108E01176731445507\"");
+ TransportInputStream tis = new MockTransportInputStream(is, headers);
+
+ WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
+ assertTrue("Not a SoapMessage", message instanceof SoapMessage);
+ SoapMessage soapMessage = (SoapMessage) message;
+ assertEquals("Invalid soap version", SoapVersion.SOAP_11, soapMessage.getVersion());
+ assertTrue("Message not a XOP pacakge", soapMessage.isXopPackage());
+ Iterator iter = soapMessage.getAttachments();
+ assertTrue("No attachments read", iter.hasNext());
+
+ Attachment attachment = soapMessage.getAttachment("<1.urn:uuid:492264AB42E57108E01176731445504@apache.org>");
assertNotNull("No attachment read", attachment);
}
diff --git a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java
index f5ecb3a4..3aa93aa6 100644
--- a/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java
+++ b/core/src/test/java/org/springframework/ws/soap/soap12/AbstractSoap12MessageFactoryTestCase.java
@@ -47,7 +47,7 @@ public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapM
assertTrue("Not a SoapMessage", message instanceof SoapMessage);
SoapMessage soapMessage = (SoapMessage) message;
assertEquals("Invalid soap version", SoapVersion.SOAP_12, soapMessage.getVersion());
- assertFalse("Message a XOP pacakge", soapMessage.isXopPackage());
+ assertFalse("Message is a XOP pacakge", soapMessage.isXopPackage());
}
public void testCreateSoapMessageSwA() throws Exception {
@@ -61,7 +61,7 @@ public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapM
assertTrue("Not a SoapMessage", message instanceof SoapMessage);
SoapMessage soapMessage = (SoapMessage) message;
assertEquals("Invalid soap version", SoapVersion.SOAP_12, soapMessage.getVersion());
- assertFalse("Message a XOP pacakge", soapMessage.isXopPackage());
+ assertFalse("Message is a XOP package", soapMessage.isXopPackage());
Attachment attachment = soapMessage.getAttachment("interface21");
assertNotNull("No attachment read", attachment);
}
@@ -78,11 +78,11 @@ public abstract class AbstractSoap12MessageFactoryTestCase extends AbstractSoapM
assertTrue("Not a SoapMessage", message instanceof SoapMessage);
SoapMessage soapMessage = (SoapMessage) message;
assertEquals("Invalid soap version", SoapVersion.SOAP_12, soapMessage.getVersion());
- assertTrue("Message not a XOP pacakge", soapMessage.isXopPackage());
+ assertTrue("Message is not a XOP pacakge", soapMessage.isXopPackage());
Iterator iter = soapMessage.getAttachments();
assertTrue("No attachments read", iter.hasNext());
- Attachment attachment = soapMessage.getAttachment("1.urn:uuid:40864869929B855F971176851454452@apache.org");
+ Attachment attachment = soapMessage.getAttachment("<1.urn:uuid:40864869929B855F971176851454452@apache.org>");
assertNotNull("No attachment read", attachment);
}
diff --git a/core/src/test/resources/log4j.properties b/core/src/test/resources/log4j.properties
index 92e896c8..53e0c020 100644
--- a/core/src/test/resources/log4j.properties
+++ b/core/src/test/resources/log4j.properties
@@ -1,5 +1,6 @@
log4j.rootCategory=INFO, stdout
log4j.logger.org.springframework.ws=DEBUG
+log4j.logger.httpclient.wire.header=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
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 6220b975..383770d7 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
@@ -314,8 +314,8 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
}
/*
- * Inner classes
- */
+ * Inner classes
+ */
private static class Jaxb2AttachmentMarshaller extends AttachmentMarshaller {
@@ -337,7 +337,7 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
public String addMtomAttachment(DataHandler dataHandler, String elementNamespace, String elementLocalName) {
String contentId = UUID.randomUUID() + "@" + elementNamespace;
- mimeContainer.addAttachment(contentId, dataHandler);
+ mimeContainer.addAttachment("<" + contentId + ">", dataHandler);
return "cid:" + contentId;
}
@@ -349,7 +349,7 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
@Override
public boolean isXOPPackage() {
- return mimeContainer.isXopPackage();
+ return mimeContainer.convertToXopPackage();
}
}
@@ -367,12 +367,15 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarsh
return FileCopyUtils.copyToByteArray(dataHandler.getInputStream());
}
catch (IOException ex) {
- return null;
+ throw new JaxbUnmarshallingFailureException(ex);
}
}
- public DataHandler getAttachmentAsDataHandler(String cid) {
- return mimeContainer.getAttachment(cid);
+ public DataHandler getAttachmentAsDataHandler(String contentId) {
+ if (contentId.startsWith("cid:")) {
+ contentId = '<' + contentId.substring("cid:".length()) + '>';
+ }
+ return mimeContainer.getAttachment(contentId);
}
@Override
diff --git a/oxm/src/main/java/org/springframework/oxm/jaxb/AbstractJaxbMarshaller.java b/oxm/src/main/java/org/springframework/oxm/jaxb/AbstractJaxbMarshaller.java
index efe63a01..e9c8649a 100644
--- a/oxm/src/main/java/org/springframework/oxm/jaxb/AbstractJaxbMarshaller.java
+++ b/oxm/src/main/java/org/springframework/oxm/jaxb/AbstractJaxbMarshaller.java
@@ -18,7 +18,6 @@ package org.springframework.oxm.jaxb;
import java.util.Iterator;
import java.util.Map;
-
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
@@ -27,7 +26,6 @@ import javax.xml.bind.ValidationEventHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.XmlMappingException;
@@ -45,9 +43,7 @@ import org.springframework.oxm.XmlMappingException;
public abstract class AbstractJaxbMarshaller
implements org.springframework.oxm.Marshaller, org.springframework.oxm.Unmarshaller, InitializingBean {
- /**
- * Logger available to subclasses.
- */
+ /** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
private String contextPath;
@@ -60,16 +56,12 @@ public abstract class AbstractJaxbMarshaller
private ValidationEventHandler validationEventHandler;
- /**
- * Returns the JAXB Context path.
- */
+ /** Returns the JAXB Context path. */
protected String getContextPath() {
return contextPath;
}
- /**
- * Sets the JAXB Context path.
- */
+ /** Sets the JAXB Context path. */
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
@@ -79,7 +71,7 @@ public abstract class AbstractJaxbMarshaller
* Marshaller, and allow for features such as indentation.
*
* @param properties the properties
- * @see javax.xml.bind.Marshaller#setProperty(String, Object)
+ * @see javax.xml.bind.Marshaller#setProperty(String,Object)
* @see javax.xml.bind.Marshaller#JAXB_ENCODING
* @see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT
* @see javax.xml.bind.Marshaller#JAXB_NO_NAMESPACE_SCHEMA_LOCATION
@@ -94,7 +86,7 @@ public abstract class AbstractJaxbMarshaller
* Unmarshaller.
*
* @param properties the properties
- * @see javax.xml.bind.Unmarshaller#setProperty(String, Object)
+ * @see javax.xml.bind.Unmarshaller#setProperty(String,Object)
*/
public void setUnmarshallerProperties(Map properties) {
this.unmarshallerProperties = properties;
@@ -110,6 +102,11 @@ public abstract class AbstractJaxbMarshaller
this.validationEventHandler = validationEventHandler;
}
+ /** Returns the {@link JAXBContext} created in {@link #afterPropertiesSet()}. */
+ public JAXBContext getJaxbContext() {
+ return jaxbContext;
+ }
+
public final void afterPropertiesSet() throws Exception {
try {
jaxbContext = createJaxbContext();
@@ -133,9 +130,7 @@ public abstract class AbstractJaxbMarshaller
return JaxbUtils.convertJaxbException(ex);
}
- /**
- * Returns a newly created JAXB marshaller. JAXB marshallers are not necessarily thread safe.
- */
+ /** Returns a newly created JAXB marshaller. JAXB marshallers are not necessarily thread safe. */
protected Marshaller createMarshaller() {
try {
Marshaller marshaller = jaxbContext.createMarshaller();
@@ -156,9 +151,7 @@ public abstract class AbstractJaxbMarshaller
}
}
- /**
- * Returns a newly created JAXB unmarshaller. JAXB unmarshallers are not necessarily thread safe.
- */
+ /** Returns a newly created JAXB unmarshaller. JAXB unmarshallers are not necessarily thread safe. */
protected Unmarshaller createUnmarshaller() {
try {
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
@@ -197,8 +190,6 @@ public abstract class AbstractJaxbMarshaller
protected void initJaxbUnmarshaller(Unmarshaller unmarshaller) throws JAXBException {
}
- /**
- * Template method that returns a newly created JAXB context. Called from afterPropertiesSet().
- */
+ /** Template method that returns a newly created JAXB context. Called from afterPropertiesSet(). */
protected abstract JAXBContext createJaxbContext() throws Exception;
}
diff --git a/oxm/src/main/java/org/springframework/oxm/jaxb/JaxbUnmarshallingFailureException.java b/oxm/src/main/java/org/springframework/oxm/jaxb/JaxbUnmarshallingFailureException.java
index b43a0f1a..8b0c6a00 100644
--- a/oxm/src/main/java/org/springframework/oxm/jaxb/JaxbUnmarshallingFailureException.java
+++ b/oxm/src/main/java/org/springframework/oxm/jaxb/JaxbUnmarshallingFailureException.java
@@ -15,6 +15,7 @@
*/
package org.springframework.oxm.jaxb;
+import java.io.IOException;
import javax.xml.bind.UnmarshalException;
import org.springframework.oxm.UnmarshallingFailureException;
@@ -30,4 +31,7 @@ public class JaxbUnmarshallingFailureException extends UnmarshallingFailureExcep
super("JAXB unmarshalling exception: " + ex.getMessage(), ex);
}
+ public JaxbUnmarshallingFailureException(IOException ex) {
+ super("JAXB unmarshalling exception: " + ex.getMessage(), ex);
+ }
}
diff --git a/oxm/src/main/java/org/springframework/oxm/mime/MimeContainer.java b/oxm/src/main/java/org/springframework/oxm/mime/MimeContainer.java
index 248d2890..12c2cef8 100644
--- a/oxm/src/main/java/org/springframework/oxm/mime/MimeContainer.java
+++ b/oxm/src/main/java/org/springframework/oxm/mime/MimeContainer.java
@@ -35,6 +35,14 @@ public interface MimeContainer {
*/
boolean isXopPackage();
+ /**
+ * Turns this message into a XOP package.
+ *
+ * @return true when the message is a XOP package
+ * @see XOP Packages
+ */
+ boolean convertToXopPackage();
+
/**
* Adds the given data handler as an attachment to this container.
*
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 8fe7e89c..6f83eeb0 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -6,6 +6,7 @@