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 e178b667..8340a4e2 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 @@ -16,14 +16,22 @@ 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; /** * Endpoint that unmarshals the request payload, and marshals the response object. This endpoint needs a @@ -39,39 +47,29 @@ import org.springframework.ws.context.MessageContext; */ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpoint, InitializingBean { - /** - * Logger available to subclasses. - */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); private Marshaller marshaller; private Unmarshaller unmarshaller; - /** - * Returns the marshaller used for transforming objects into XML. - */ + /** Returns the marshaller used for transforming objects into XML. */ public final Marshaller getMarshaller() { return marshaller; } - /** - * Sets the marshaller used for transforming objects into XML. - */ + /** 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. - */ + /** Returns the unmarshaller used for transforming XML into objects. */ public final Unmarshaller getUnmarshaller() { return unmarshaller; } - /** - * Sets the unmarshaller used for transforming XML into objects. - */ + /** Sets the unmarshaller used for transforming XML into objects. */ public final void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } @@ -84,36 +82,82 @@ public abstract class AbstractMarshallingPayloadEndpoint implements MessageEndpo public final void invoke(MessageContext messageContext) throws Exception { WebServiceMessage request = messageContext.getRequest(); - Object requestObject = unmarshaller.unmarshal(request.getPayloadSource()); + Object requestObject = unmarshalRequest(request); + Object responseObject = invokeInternal(requestObject); + if (responseObject != null) { + WebServiceMessage response = messageContext.getResponse(); + marshalResponse(responseObject, response); + } + } + + 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()); + } if (logger.isDebugEnabled()) { logger.debug("Unmarshalled payload request to [" + requestObject + "]"); } - Object responseObject = invokeInternal(requestObject); - if (responseObject != null) { - if (logger.isDebugEnabled()) { - logger.debug("Marshalling [" + responseObject + "] to response payload"); - } - WebServiceMessage response = messageContext.getResponse(); + return requestObject; + } + + private void marshalResponse(Object responseObject, WebServiceMessage response) throws IOException { + 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()); } } /** * Template method that gets called after the marshaller and unmarshaller have been set. - * - *
The default implementation does nothing. + *
+ * The default implementation does nothing. */ public void afterMarshallerSet() throws Exception { } /** * Template method that subclasses must implement to process a request. - * - *The unmarshaled request object is passed as a parameter, and an the returned object is marshalled to a
- * response. If no response is required, return null.
+ *
null.
*
* @param requestObject the unnmarshalled message payload as 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/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/oxm-tiger/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java
index ea2798aa..6220b975 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
@@ -16,8 +16,14 @@
package org.springframework.oxm.jaxb;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Map;
+import java.util.UUID;
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
@@ -26,18 +32,24 @@ import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.attachment.AttachmentMarshaller;
+import javax.xml.bind.attachment.AttachmentUnmarshaller;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import org.springframework.core.io.Resource;
+import org.springframework.oxm.XmlMappingException;
+import org.springframework.oxm.mime.MimeContainer;
+import org.springframework.oxm.mime.MimeMarshaller;
+import org.springframework.oxm.mime.MimeUnmarshaller;
import org.springframework.util.ClassUtils;
+import org.springframework.util.FileCopyUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.xml.transform.StaxResult;
import org.springframework.xml.transform.StaxSource;
import org.springframework.xml.validation.SchemaLoaderUtils;
-import org.xml.sax.SAXException;
/**
* Implementation of the Marshaller interface for JAXB 2.0.
@@ -58,7 +70,7 @@ import org.xml.sax.SAXException;
* @see #setUnmarshallerListener(javax.xml.bind.Unmarshaller.Listener)
* @see #setAdapters(javax.xml.bind.annotation.adapters.XmlAdapter[])
*/
-public class Jaxb2Marshaller extends AbstractJaxbMarshaller {
+public class Jaxb2Marshaller extends AbstractJaxbMarshaller implements MimeMarshaller, MimeUnmarshaller {
private Resource[] schemaResources;
@@ -136,61 +148,9 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller {
return clazz.getAnnotation(XmlRootElement.class) != null || JAXBElement.class.isAssignableFrom(clazz);
}
- public void marshal(Object graph, Result result) {
- try {
- if (result instanceof StaxResult) {
- marshalStaxResult(graph, (StaxResult) result);
- }
- else {
- createMarshaller().marshal(graph, result);
- }
- }
- catch (JAXBException ex) {
- throw convertJaxbException(ex);
- }
- }
-
- public Object unmarshal(Source source) {
- try {
- if (source instanceof StaxSource) {
- return unmarshalStaxSource((StaxSource) source);
- }
- else {
- return createUnmarshaller().unmarshal(source);
- }
- }
- catch (JAXBException ex) {
- throw convertJaxbException(ex);
- }
- }
-
- protected void initJaxbMarshaller(Marshaller marshaller) throws JAXBException {
- if (schema != null) {
- marshaller.setSchema(schema);
- }
- if (marshallerListener != null) {
- marshaller.setListener(marshallerListener);
- }
- if (adapters != null) {
- for (int i = 0; i < adapters.length; i++) {
- marshaller.setAdapter(adapters[i]);
- }
- }
- }
-
- protected void initJaxbUnmarshaller(Unmarshaller unmarshaller) throws JAXBException {
- if (schema != null) {
- unmarshaller.setSchema(schema);
- }
- if (unmarshallerListener != null) {
- unmarshaller.setListener(unmarshallerListener);
- }
- if (adapters != null) {
- for (int i = 0; i < adapters.length; i++) {
- unmarshaller.setAdapter(adapters[i]);
- }
- }
- }
+ /*
+ * JAXBContext
+ */
protected JAXBContext createJaxbContext() throws Exception {
if (JaxbUtils.getJaxbVersion() < JaxbUtils.JAXB_2) {
@@ -200,7 +160,13 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller {
if (StringUtils.hasLength(getContextPath()) && !ObjectUtils.isEmpty(classesToBeBound)) {
throw new IllegalArgumentException("specify either contextPath or classesToBeBound property; not both");
}
- loadSchema();
+ if (!ObjectUtils.isEmpty(schemaResources)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ "Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(schemaResources));
+ }
+ schema = SchemaLoaderUtils.loadSchema(schemaResources, schemaLanguage);
+ }
if (StringUtils.hasLength(getContextPath())) {
return createJaxbContextFromContextPath();
}
@@ -238,37 +204,219 @@ public class Jaxb2Marshaller extends AbstractJaxbMarshaller {
}
}
- private void loadSchema() throws IOException, SAXException {
- if (!ObjectUtils.isEmpty(schemaResources)) {
- if (logger.isDebugEnabled()) {
- logger.debug(
- "Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(schemaResources));
+ /*
+ * Marshaller/Unmarshaller
+ */
+
+ protected void initJaxbMarshaller(Marshaller marshaller) throws JAXBException {
+ if (schema != null) {
+ marshaller.setSchema(schema);
+ }
+ if (marshallerListener != null) {
+ marshaller.setListener(marshallerListener);
+ }
+ if (adapters != null) {
+ for (int i = 0; i < adapters.length; i++) {
+ marshaller.setAdapter(adapters[i]);
}
- schema = SchemaLoaderUtils.loadSchema(schemaResources, schemaLanguage);
}
}
- private void marshalStaxResult(Object graph, StaxResult staxResult) throws JAXBException {
+ protected void initJaxbUnmarshaller(Unmarshaller unmarshaller) throws JAXBException {
+ if (schema != null) {
+ unmarshaller.setSchema(schema);
+ }
+ if (unmarshallerListener != null) {
+ unmarshaller.setListener(unmarshallerListener);
+ }
+ if (adapters != null) {
+ for (int i = 0; i < adapters.length; i++) {
+ unmarshaller.setAdapter(adapters[i]);
+ }
+ }
+ }
+
+ /*
+ * Marshalling
+ */
+
+ public void marshal(Object graph, Result result) throws XmlMappingException {
+ marshal(graph, result, null);
+ }
+
+ public void marshal(Object graph, Result result, MimeContainer mimeContainer) throws XmlMappingException {
+ try {
+ Marshaller marshaller = createMarshaller();
+ if (mimeContainer != null) {
+ marshaller.setAttachmentMarshaller(new Jaxb2AttachmentMarshaller(mimeContainer));
+ }
+ if (result instanceof StaxResult) {
+ marshalStaxResult(marshaller, graph, (StaxResult) result);
+ }
+ else {
+ marshaller.marshal(graph, result);
+ }
+ }
+ catch (JAXBException ex) {
+ throw convertJaxbException(ex);
+ }
+ }
+
+ private void marshalStaxResult(Marshaller jaxbMarshaller, Object graph, StaxResult staxResult)
+ throws JAXBException {
if (staxResult.getXMLStreamWriter() != null) {
- createMarshaller().marshal(graph, staxResult.getXMLStreamWriter());
+ jaxbMarshaller.marshal(graph, staxResult.getXMLStreamWriter());
}
else if (staxResult.getXMLEventWriter() != null) {
- createMarshaller().marshal(graph, staxResult.getXMLEventWriter());
+ jaxbMarshaller.marshal(graph, staxResult.getXMLEventWriter());
}
else {
throw new IllegalArgumentException("StaxResult contains neither XMLStreamWriter nor XMLEventConsumer");
}
}
- private Object unmarshalStaxSource(StaxSource staxSource) throws JAXBException {
+ /*
+ * Unmarshalling
+ */
+
+ public Object unmarshal(Source source) throws XmlMappingException {
+ return unmarshal(source, null);
+ }
+
+ public Object unmarshal(Source source, MimeContainer mimeContainer) throws XmlMappingException {
+ try {
+ Unmarshaller unmarshaller = createUnmarshaller();
+ if (mimeContainer != null) {
+ unmarshaller.setAttachmentUnmarshaller(new Jaxb2AttachmentUnmarshaller(mimeContainer));
+ }
+ if (source instanceof StaxSource) {
+ return unmarshalStaxSource(unmarshaller, (StaxSource) source);
+ }
+ else {
+ return unmarshaller.unmarshal(source);
+ }
+ }
+ catch (JAXBException ex) {
+ throw convertJaxbException(ex);
+ }
+ }
+
+ private Object unmarshalStaxSource(Unmarshaller jaxbUnmarshaller, StaxSource staxSource) throws JAXBException {
if (staxSource.getXMLStreamReader() != null) {
- return createUnmarshaller().unmarshal(staxSource.getXMLStreamReader());
+ return jaxbUnmarshaller.unmarshal(staxSource.getXMLStreamReader());
}
else if (staxSource.getXMLEventReader() != null) {
- return createUnmarshaller().unmarshal(staxSource.getXMLEventReader());
+ return jaxbUnmarshaller.unmarshal(staxSource.getXMLEventReader());
}
else {
throw new IllegalArgumentException("StaxSource contains neither XMLStreamReader nor XMLEventReader");
}
}
+
+ /*
+ * Inner classes
+ */
+
+ private static class Jaxb2AttachmentMarshaller extends AttachmentMarshaller {
+
+ private final MimeContainer mimeContainer;
+
+ public Jaxb2AttachmentMarshaller(MimeContainer mimeContainer) {
+ this.mimeContainer = mimeContainer;
+ }
+
+ public String addMtomAttachment(byte[] data,
+ int offset,
+ int length,
+ String mimeType,
+ String elementNamespace,
+ String elementLocalName) {
+ ByteArrayDataSource dataSource = new ByteArrayDataSource(mimeType, data, offset, length);
+ return addMtomAttachment(new DataHandler(dataSource), elementNamespace, elementLocalName);
+ }
+
+ public String addMtomAttachment(DataHandler dataHandler, String elementNamespace, String elementLocalName) {
+ String contentId = UUID.randomUUID() + "@" + elementNamespace;
+ mimeContainer.addAttachment(contentId, dataHandler);
+ return "cid:" + contentId;
+ }
+
+ public String addSwaRefAttachment(DataHandler dataHandler) {
+ String contentId = UUID.randomUUID() + "@" + dataHandler.getName();
+ mimeContainer.addAttachment(contentId, dataHandler);
+ return contentId;
+ }
+
+ @Override
+ public boolean isXOPPackage() {
+ return mimeContainer.isXopPackage();
+ }
+ }
+
+ private static class Jaxb2AttachmentUnmarshaller extends AttachmentUnmarshaller {
+
+ private final MimeContainer mimeContainer;
+
+ public Jaxb2AttachmentUnmarshaller(MimeContainer mimeContainer) {
+ this.mimeContainer = mimeContainer;
+ }
+
+ public byte[] getAttachmentAsByteArray(String cid) {
+ try {
+ DataHandler dataHandler = getAttachmentAsDataHandler(cid);
+ return FileCopyUtils.copyToByteArray(dataHandler.getInputStream());
+ }
+ catch (IOException ex) {
+ return null;
+ }
+ }
+
+ public DataHandler getAttachmentAsDataHandler(String cid) {
+ return mimeContainer.getAttachment(cid);
+ }
+
+ @Override
+ public boolean isXOPPackage() {
+ return mimeContainer.isXopPackage();
+ }
+ }
+
+ /*
+ * DataSource that wraps around a byte array
+ */
+ private static class ByteArrayDataSource implements DataSource {
+
+ private byte[] data;
+
+ private String contentType;
+
+ private int offset;
+
+ private int length;
+
+ public ByteArrayDataSource(String contentType, byte[] data, int offset, int length) {
+ this.contentType = contentType;
+ this.data = data;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(data, offset, length);
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public String getName() {
+ return "ByteArrayDataSource";
+ }
+ }
+
}
+
diff --git a/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/BinaryObject.java b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/BinaryObject.java
new file mode 100644
index 00000000..7f15d041
--- /dev/null
+++ b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/BinaryObject.java
@@ -0,0 +1,57 @@
+/*
+ * 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.jaxb;
+
+import javax.activation.DataHandler;
+import javax.xml.bind.annotation.XmlAttachmentRef;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(namespace = "http://springframework.org/spring-ws")
+public class BinaryObject {
+
+ @XmlElement(namespace = "http://springframework.org/spring-ws")
+ private byte[] bytes;
+
+ @XmlElement(namespace = "http://springframework.org/spring-ws")
+ private DataHandler dataHandler;
+
+ @XmlElement(namespace = "http://springframework.org/spring-ws")
+ @XmlAttachmentRef
+ private DataHandler swaDataHandler;
+
+ public BinaryObject() {
+ }
+
+ public BinaryObject(byte[] bytes, DataHandler dataHandler) {
+ this.bytes = bytes;
+ this.dataHandler = dataHandler;
+ swaDataHandler = dataHandler;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ public DataHandler getDataHandler() {
+ return dataHandler;
+ }
+
+ public DataHandler getSwaDataHandler() {
+ return swaDataHandler;
+ }
+}
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 c4095708..9850a7d9 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
@@ -19,6 +19,8 @@ package org.springframework.oxm.jaxb;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.util.Collections;
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
import javax.xml.bind.JAXBElement;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -31,15 +33,22 @@ import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import org.custommonkey.xmlunit.XMLTestCase;
-import org.easymock.MockControl;
+import static org.easymock.EasyMock.*;
+import org.springframework.core.io.ClassPathResource;
+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.mime.MimeContainer;
+import org.springframework.util.FileCopyUtils;
import org.springframework.xml.transform.StaxResult;
+import org.springframework.xml.transform.StringResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
+import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
public class Jaxb2MarshallerTest extends XMLTestCase {
@@ -159,34 +168,53 @@ public class Jaxb2MarshallerTest extends XMLTestCase {
}
public void testMarshalSaxResult() throws Exception {
- MockControl handlerControl = MockControl.createStrictControl(ContentHandler.class);
- ContentHandler handlerMock = (ContentHandler) handlerControl.getMock();
- handlerMock.setDocumentLocator(null);
- handlerControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ ContentHandler handlerMock = createStrictMock(ContentHandler.class);
+ handlerMock.setDocumentLocator(isA(Locator.class));
handlerMock.startDocument();
handlerMock.startPrefixMapping("", "http://samples.springframework.org/flight");
- handlerMock.startElement("http://samples.springframework.org/flight", "flights", "flights", null);
- handlerControl.setMatcher(MockControl.ALWAYS_MATCHER);
- handlerMock.startElement("http://samples.springframework.org/flight", "flight", "flight", null);
- handlerControl.setMatcher(MockControl.ALWAYS_MATCHER);
- handlerMock.startElement("http://samples.springframework.org/flight", "number", "number", null);
- handlerControl.setMatcher(MockControl.ALWAYS_MATCHER);
- handlerMock.characters(new char[]{'4', '2'}, 0, 2);
- handlerControl.setMatcher(MockControl.ALWAYS_MATCHER);
+ handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("flights"), eq("flights"),
+ isA(Attributes.class));
+ handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("flight"), eq("flight"),
+ isA(Attributes.class));
+ handlerMock.startElement(eq("http://samples.springframework.org/flight"), eq("number"), eq("number"),
+ isA(Attributes.class));
+ handlerMock.characters(isA(char[].class), eq(0), eq(2));
handlerMock.endElement("http://samples.springframework.org/flight", "number", "number");
handlerMock.endElement("http://samples.springframework.org/flight", "flight", "flight");
handlerMock.endElement("http://samples.springframework.org/flight", "flights", "flights");
handlerMock.endPrefixMapping("");
handlerMock.endDocument();
+ replay(handlerMock);
- handlerControl.replay();
SAXResult result = new SAXResult(handlerMock);
marshaller.marshal(flights, result);
- handlerControl.verify();
+ verify(handlerMock);
}
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));
}
+
+ public void testMarshalAttachments() throws Exception {
+ marshaller = new Jaxb2Marshaller();
+ marshaller.setClassesToBeBound(new Class[]{BinaryObject.class});
+ marshaller.afterPropertiesSet();
+ MimeContainer mimeContainer = createMock(MimeContainer.class);
+
+ Resource logo = new ClassPathResource("spring-ws.png", getClass());
+ DataHandler dataHandler = new DataHandler(new FileDataSource(logo.getFile()));
+
+ expect(mimeContainer.isXopPackage()).andReturn(true);
+ mimeContainer.addAttachment(isA(String.class), isA(DataHandler.class));
+ expectLastCall().times(3);
+
+ replay(mimeContainer);
+ byte[] bytes = FileCopyUtils.copyToByteArray(logo.getInputStream());
+ BinaryObject object = new BinaryObject(bytes, dataHandler);
+ Result result = new StringResult();
+ marshaller.marshal(object, result, mimeContainer);
+ verify(mimeContainer);
+ assertTrue("No XML written", result.toString().length() > 0);
+ }
}
diff --git a/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2UnmarshallerTest.java b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2UnmarshallerTest.java
index 63bbd874..2eb722b5 100644
--- a/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2UnmarshallerTest.java
+++ b/oxm-tiger/src/test/java/org/springframework/oxm/jaxb/Jaxb2UnmarshallerTest.java
@@ -16,30 +16,34 @@
package org.springframework.oxm.jaxb;
-import java.io.StringReader;
import java.io.ByteArrayInputStream;
-
+import java.io.StringReader;
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.transform.sax.SAXSource;
+import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
-import javax.xml.stream.XMLEventReader;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
import junit.framework.TestCase;
-
+import static org.easymock.EasyMock.*;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
import org.springframework.oxm.jaxb2.FlightType;
import org.springframework.oxm.jaxb2.Flights;
+import org.springframework.oxm.mime.MimeContainer;
import org.springframework.xml.transform.StaxSource;
-
+import org.springframework.xml.transform.StringSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
-import org.xml.sax.XMLReader;
import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class Jaxb2UnmarshallerTest extends TestCase {
@@ -107,6 +111,43 @@ public class Jaxb2UnmarshallerTest extends TestCase {
testFlights(flights);
}
+ public void testMarshalAttachments() throws Exception {
+ unmarshaller = new Jaxb2Marshaller();
+ unmarshaller.setClassesToBeBound(new Class[]{BinaryObject.class});
+ unmarshaller.afterPropertiesSet();
+ MimeContainer mimeContainer = createMock(MimeContainer.class);
+
+ Resource logo = new ClassPathResource("spring-ws.png", getClass());
+ DataHandler dataHandler = new DataHandler(new FileDataSource(logo.getFile()));
+
+ expect(mimeContainer.isXopPackage()).andReturn(true);
+ expect(mimeContainer.getAttachment(
+ "cid:6b76528d-7a9c-4def-8e13-095ab89e9bb7@http://springframework.org/spring-ws"))
+ .andReturn(dataHandler);
+ expect(mimeContainer.getAttachment(
+ "cid:99bd1592-0521-41a2-9688-a8bfb40192fb@http://springframework.org/spring-ws"))
+ .andReturn(dataHandler);
+ expect(mimeContainer.getAttachment("696cfb9a-4d2d-402f-bb5c-59fa69e7f0b3@spring-ws.png"))
+ .andReturn(dataHandler);
+ replay(mimeContainer);
+ String content = "true when the constraints specified in Identifying
+ * XOP Documents are met.
+ * @see XOP Packages
+ */
+ boolean isXopPackage();
+
+ /**
+ * Adds the given data handler as an attachment to this container.
+ *
+ * @param contentId the content id of the attachment
+ * @param dataHandler the data handler containing the data of the attachment
+ */
+ void addAttachment(String contentId, DataHandler dataHandler);
+
+ /**
+ * Returns the attachment with the given content id, or null if not found.
+ *
+ * @param contentId the content id
+ * @return the attachment, as a data handler
+ */
+ DataHandler getAttachment(String contentId);
+}
diff --git a/oxm/src/main/java/org/springframework/oxm/mime/MimeMarshaller.java b/oxm/src/main/java/org/springframework/oxm/mime/MimeMarshaller.java
new file mode 100644
index 00000000..71ee95c6
--- /dev/null
+++ b/oxm/src/main/java/org/springframework/oxm/mime/MimeMarshaller.java
@@ -0,0 +1,49 @@
+/*
+ * 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.mime;
+
+import java.io.IOException;
+import javax.xml.transform.Result;
+
+import org.springframework.oxm.Marshaller;
+import org.springframework.oxm.XmlMappingException;
+
+/**
+ * Subinterface of {@link Marshaller} that can use MIME attachments to optimize storage of binary data. Attachments can
+ * be added as MTOM, XOP, or SwA.
+ *
+ * @author Arjen Poutsma
+ * @see SOAP Message Transmission Optimization
+ * Mechanism
+ * @see XML-binary Optimized Packaging
+ */
+public interface MimeMarshaller extends Marshaller {
+
+ /**
+ * Marshals the object graph with the given root into the provided {@link Result}, writing binary data to a {@link
+ * MimeContainer}.
+ *
+ * @param graph the root of the object graph to marshal
+ * @param result the result to marshal to
+ * @param mimeContainer the MIME container to write extracted binary content to
+ * @throws XmlMappingException if the given object cannot be marshalled to the result
+ * @throws IOException if an I/O exception occurs
+ */
+ void marshal(Object graph, Result result, MimeContainer mimeContainer) throws XmlMappingException, IOException;
+
+
+}
diff --git a/oxm/src/main/java/org/springframework/oxm/mime/MimeUnmarshaller.java b/oxm/src/main/java/org/springframework/oxm/mime/MimeUnmarshaller.java
new file mode 100644
index 00000000..c19c4a9a
--- /dev/null
+++ b/oxm/src/main/java/org/springframework/oxm/mime/MimeUnmarshaller.java
@@ -0,0 +1,47 @@
+/*
+ * 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.mime;
+
+import java.io.IOException;
+import javax.xml.transform.Source;
+
+import org.springframework.oxm.Unmarshaller;
+import org.springframework.oxm.XmlMappingException;
+
+/**
+ * Subinterface of {@link org.springframework.oxm.Marshaller} that can use MIME attachments to optimize storage of
+ * binary data. Attachments can be added as MTOM, XOP, or SwA.
+ *
+ * @author Arjen Poutsma
+ * @see SOAP Message Transmission Optimization
+ * Mechanism
+ * @see XML-binary Optimized Packaging
+ */
+public interface MimeUnmarshaller extends Unmarshaller {
+
+ /**
+ * Unmarshals the given provided {@link Source} into an object graph, reading binary attachments from a {@link
+ * MimeContainer}.
+ *
+ * @param source the source to marshal from
+ * @param mimeContainer the MIME container to read extracted binary content from
+ * @return the object graph
+ * @throws XmlMappingException if the given source cannot be mapped to an object
+ * @throws IOException if an I/O Exception occurs
+ */
+ Object unmarshal(Source source, MimeContainer mimeContainer) throws XmlMappingException, IOException;
+}
diff --git a/oxm/src/main/java/org/springframework/oxm/mime/package.html b/oxm/src/main/java/org/springframework/oxm/mime/package.html
new file mode 100644
index 00000000..c5563d6e
--- /dev/null
+++ b/oxm/src/main/java/org/springframework/oxm/mime/package.html
@@ -0,0 +1,5 @@
+
+
+Contains (un)marshallers optimized to store binary data in MIME attachments.
+
+
\ No newline at end of file