Fixed SWS-107: Support JAXB's MTOM/XOP/MIME support
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains (un)marshallers optimized to store binary data in MIME attachments.
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user