Fixed various MTOM issues.

This commit is contained in:
Arjen Poutsma
2007-06-06 02:41:49 +00:00
parent 3b06729ec5
commit 4f843e76a9
24 changed files with 371 additions and 134 deletions

View File

@@ -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

View File

@@ -25,6 +25,14 @@ public interface MimeMessage extends WebServiceMessage {
*/
boolean isXopPackage();
/**
* Turns this message into a XOP package.
*
* @return <code>true</code> when the message is a XOP package
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/#xop_packages">XOP Packages</a>
*/
boolean convertToXopPackage();
/**
* Returns the <code>Attachment</code> with the specified content Id.
*

View File

@@ -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.
* <p/>
* The unmarshaled request object is passed as a parameter, and the returned object is marshalled to a response.
* If no response is required, return <code>null</code>.
* The unmarshaled request object is passed as a parameter, and the returned object is marshalled to a response. If
* no response is required, return <code>null</code>.
*
* @param requestObject the unnmarshalled message payload as an object
* @return the object to be marshalled as response, or <code>null</code> 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();
}
}
}

View File

@@ -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 <code>MarshallingMethodEndpointAdapter</code>. The {@link Marshaller} and {@link Unmarshaller}
* must be injected using properties.
* Creates a new <code>MarshallingMethodEndpointAdapter</code>. 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);
}
}

View File

@@ -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 <code>EndpointMapping</code> interface to map from the qualified name of the request payload

View File

@@ -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

View File

@@ -1,5 +0,0 @@
<html>
<body>
Provides helper classes for <code>EndpointMapping</code> implementations.
</body>
</html>

View File

@@ -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;
}
}
}

View File

@@ -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.
*

View File

@@ -0,0 +1,6 @@
<html>
<body>
Provides helper classes for <code>EndpointAdapter</code>, <code>EndpointInteceptor</code>, and
<code>EndpointMapping</code> implementations.
</body>
</html>

View File

@@ -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;

View File

@@ -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 <code>SaajSoapMessage</code> based on the given SAAJ <code>SOAPMessage</code>.
*
@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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";
}

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
* <code>Marshaller</code>, 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
* <code>Unmarshaller</code>.
*
* @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 <code>afterPropertiesSet()</code>.
*/
/** Template method that returns a newly created JAXB context. Called from <code>afterPropertiesSet()</code>. */
protected abstract JAXBContext createJaxbContext() throws Exception;
}

View File

@@ -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);
}
}

View File

@@ -35,6 +35,14 @@ public interface MimeContainer {
*/
boolean isXopPackage();
/**
* Turns this message into a XOP package.
*
* @return <code>true</code> when the message is a XOP package
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/#xop_packages">XOP Packages</a>
*/
boolean convertToXopPackage();
/**
* Adds the given data handler as an attachment to this container.
*

View File

@@ -6,6 +6,7 @@
</properties>
<body>
<release version="1.0-rc2">
<action dev="poutsma" type="fix">Fixed various MTOM issues, added an MTOM sample</action>
<action dev="poutsma" type="add" issue="SWS-135">Implement client-side TransportContext</action>
<action dev="poutsma" type="add" issue="SWS-133">Added ignoreExtraAttributes and ignoreExtraElements
properties to CastorMarshaller