Fixed SWS-107: Support JAXB's MTOM/XOP/MIME support

This commit is contained in:
Arjen Poutsma
2007-05-13 00:37:24 +00:00
parent 0fbf4d0b68
commit 0a07669a15
10 changed files with 593 additions and 121 deletions

View File

@@ -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.
*
* <p>The default implementation does nothing.
* <p/>
* The default implementation does nothing.
*/
public void afterMarshallerSet() throws Exception {
}
/**
* Template method that subclasses must implement to process a request.
*
* <p>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 <code>null</code>.
* <p/>
* 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 <code>null</code>.
*
* @param requestObject the unnmarshalled message payload as 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,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 <code>Marshaller</code> 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";
}
}
}

View File

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

View File

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

View File

@@ -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 = "<binaryObject xmlns='http://springframework.org/spring-ws'>" + "<bytes>" +
"<xop:Include href='cid:6b76528d-7a9c-4def-8e13-095ab89e9bb7@http://springframework.org/spring-ws' xmlns:xop='http://www.w3.org/2004/08/xop/include'/>" +
"</bytes>" + "<dataHandler>" +
"<xop:Include href='cid:99bd1592-0521-41a2-9688-a8bfb40192fb@http://springframework.org/spring-ws' xmlns:xop='http://www.w3.org/2004/08/xop/include'/>" +
"</dataHandler>" +
"<swaDataHandler>696cfb9a-4d2d-402f-bb5c-59fa69e7f0b3@spring-ws.png</swaDataHandler>" +
"</binaryObject>";
Source source = new StringSource(content);
Object result = unmarshaller.unmarshal(source, mimeContainer);
assertTrue("Result is not a BinaryObject", result instanceof BinaryObject);
verify(mimeContainer);
BinaryObject object = (BinaryObject) result;
assertNotNull("bytes property not set", object.getBytes());
assertTrue("bytes property not set", object.getBytes().length > 0);
assertNotNull("datahandler property not set", object.getSwaDataHandler());
}
private void testFlights(Object o) {
Flights flights = (Flights) o;
assertNotNull("Flights is null", flights);

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,53 @@
/*
* 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 javax.activation.DataHandler;
/**
* Represents a container for MIME attachments. Concrete implementations might adapt a SOAPMesage, or an email message.
*
* @author Arjen Poutsma
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/">XML-binary Optimized Packaging</a>
*/
public interface MimeContainer {
/**
* Indicates whether this container is a XOP package.
*
* @return <code>true</code> when the constraints specified in <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/#identifying_xop_documents">Identifying
* XOP Documents</a> are met.
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/#xop_packages">XOP Packages</a>
*/
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 <code>null</code> if not found.
*
* @param contentId the content id
* @return the attachment, as a data handler
*/
DataHandler getAttachment(String contentId);
}

View File

@@ -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 <a href="http://www.w3.org/TR/2004/WD-soap12-mtom-20040608/">SOAP Message Transmission Optimization
* Mechanism</a>
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/">XML-binary Optimized Packaging</a>
*/
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;
}

View File

@@ -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 <a href="http://www.w3.org/TR/2004/WD-soap12-mtom-20040608/">SOAP Message Transmission Optimization
* Mechanism</a>
* @see <a href="http://www.w3.org/TR/2005/REC-xop10-20050125/">XML-binary Optimized Packaging</a>
*/
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;
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Contains (un)marshallers optimized to store binary data in MIME attachments.
</body>
</html>