Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/MOXy/Web Services"

m
m (Replacing page with ''''Warning This page is obsolete. Please see ''[http://www.eclipse.org/eclipselink/documentation/ Developing JAXB Applications Using EclipseLink M...')
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{EclipseLink_UserGuide
+
'''[[Image:Elug_draft_icon.png|Warning]] This page is obsolete. Please see ''[http://www.eclipse.org/eclipselink/documentation/ Developing JAXB Applications Using EclipseLink MOXy]'' for current information.'''
|info=y
+
|toc=n
+
|eclipselink=y
+
|eclipselinktype=MOXy
+
}}
+
 
+
=Web Services=
+
 
+
When an instance of a class is used with a Web Service, the JAX-WS implementation can choose to handle fields/properties that hold binary data as SOAP attachment.  An attachment is a means to send the data outside of the XML message, this is done as an optimization since binary data encoded as a xs:base64Binary string could be quite large.  JAXB offers a couple of annotations to control this behaviour:
+
* @XmlInlineBinaryData <br />This specifies that the binary data for this field/property must be written to the XML document as xs:base64Binary and not sent as an attachment.
+
* @XmlMimeType <br />  For properties of type java.awt.Image or javax.xml.transform.Source, this annotation allows the mime type to be specified that will be used for encoding the data as bytes.
+
 
+
 
+
==Java Model==
+
 
+
The following class will be used as the domain model for this example.  It has various properties for representing binary data.  Property "c" has been annotated with @XmlInlineBinaryData to prevent that data from being treated as an attachment, and property "d" has been annotated with @XmlMimeType to specify that the Image should be encoded as a JPEG.
+
 
+
<source lang="java">
+
 
+
package blog.attachments;
+
+
import java.awt.Image;
+
+
import javax.xml.bind.annotation.XmlInlineBinaryData;
+
import javax.xml.bind.annotation.XmlMimeType;
+
import javax.xml.bind.annotation.XmlRootElement;
+
+
@XmlRootElement
+
public class Root {
+
+
    private byte[] a;
+
    private byte[] b;
+
    private byte[] c;
+
    private Image d;
+
+
    public byte[] getA() {
+
        return a;
+
    }
+
+
    public void setA(byte[] foo) {
+
        this.a = foo;
+
    }
+
+
    public byte[] getB() {
+
        return b;
+
    }
+
+
    public void setB(byte[] bar) {
+
        this.b = bar;
+
    }
+
+
    @XmlInlineBinaryData
+
    public byte[] getC() {
+
        return c;
+
    }
+
+
    public void setC(byte[] c) {
+
        this.c = c;
+
    }
+
+
    @XmlMimeType("image/jpeg")
+
    public Image getD() {
+
        return d;
+
    }
+
+
    public void setD(Image d) {
+
        this.d = d;
+
    }
+
+
}
+
</source>
+
 
+
==Web Service Layer==
+
 
+
When a JAX-WS implementation leverages attachments the XML payload will look similar to the following.  Some data will be marshalled as xs:base64Binary and other data will be marshalled as an identifier that will serve as a reference to the attachment.
+
 
+
<source lang="xml">
+
 
+
<root>
+
    <a>
+
        <xop:Include
+
            href="cid:1"
+
            xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
+
    </a>
+
    <b>QkFS</b>
+
    <c>SEVMTE8gV09STEQ=</c>
+
    <d>
+
        <xop:Include
+
            href="cid:2"
+
            xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
+
    </d>
+
</root>
+
</source>
+
 
+
A JAX-WS provider achieves this by leveraging JAXB's AttachmentMarshaller and AttachmentUnmarshaller mechanisms.  You do not need to write any code to make this happen.  The following code is provided to give you a behind the scenes look.
+
 
+
===Example AttachmentMarshaller===
+
 
+
A JAX-WS provider that wants to leverage attachments registers an implementation of javax.xml.bind.attachment.AttachmentMarshaller on the JAXB Marshaller.  The implementation is specific to the JAX-WS provider, but below is a sample of how it might look.  A JAX-WS provider can choose when to handle binary data as an attachment, in the implementation below any candidate byte[] of size greater than 10 will be treated as an attachment.
+
<source lang="java">
+
+
package blog.attachments;
+
+
import java.util.ArrayList;
+
import java.util.List;
+
+
import javax.activation.DataHandler;
+
import javax.xml.bind.attachment.AttachmentMarshaller;
+
+
public class ExampleAttachmentMarshaller extends AttachmentMarshaller {
+
+
    private static final int THRESHOLD = 10;
+
+
    private List<Attachment> attachments = new ArrayList<Attachment>();
+
+
    public List<Attachment> getAttachments() {
+
        return attachments;
+
    }
+
+
    @Override
+
    public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
+
        return null;
+
    }
+
+
    @Override
+
    public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
+
        if(data.length < THRESHOLD) {
+
            return null;
+
        }
+
        int id = attachments.size() + 1;
+
        attachments.add(new Attachment(data, offset, length));
+
        return "cid:" + String.valueOf(id);
+
    }
+
+
    @Override
+
    public String addSwaRefAttachment(DataHandler data) {
+
        return null;
+
    }
+
+
    @Override
+
    public boolean isXOPPackage() {
+
        return true;
+
    }
+
+
    public static class Attachment {
+
+
        private byte[] data;
+
        private int offset;
+
        private int length;
+
+
        public Attachment(byte[] data, int offset, int length) {
+
            this.data = data;
+
            this.offset = offset;
+
            this.length = length;
+
        }
+
+
        public byte[] getData() {
+
            return data;
+
        }
+
+
        public int getOffset() {
+
            return offset;
+
        }
+
+
        public int getLength() {
+
            return length;
+
        }
+
+
    }
+
+
}
+
 
+
</source>
+
 
+
===Example AttachmentUnmarshaller===
+
 
+
If a JAX-WS provider is leveraging attachments, then an implementation of javax.xml.bind.attachment.AttachmentUnmarshaller must be specified on the JAXB Unmarshaller.  Again the implementations is specific to the JAX-WS provider.  A sample implementation is shown below:
+
 
+
<source lang="java">
+
+
package blog.attachments;
+
+
import java.io.ByteArrayInputStream;
+
import java.io.IOException;
+
import java.io.InputStream;
+
import java.io.OutputStream;
+
import java.util.HashMap;
+
import java.util.Map;
+
+
import javax.activation.DataHandler;
+
import javax.activation.DataSource;
+
import javax.xml.bind.attachment.AttachmentUnmarshaller;
+
+
public class ExampleAttachmentUnmarshaller extends AttachmentUnmarshaller {
+
+
    private Map<String, byte[]> attachments = new HashMap<String, byte[]>();
+
+
    public Map<String, byte[]> getAttachments() {
+
        return attachments;
+
    }
+
+
    @Override
+
    public DataHandler getAttachmentAsDataHandler(String cid) {
+
        byte[] bytes = attachments.get(cid);
+
        return new DataHandler(new ByteArrayDataSource(bytes));
+
    }
+
+
    @Override
+
    public byte[] getAttachmentAsByteArray(String cid) {
+
        return attachments.get(cid);
+
    }
+
+
    @Override
+
    public boolean isXOPPackage() {
+
        return true;
+
    }
+
+
    private static class ByteArrayDataSource implements DataSource {
+
+
        private byte[] bytes;
+
+
        public ByteArrayDataSource(byte[] bytes) {
+
            this.bytes = bytes;
+
        }
+
+
        public String getContentType() {
+
            return  "application/octet-stream";
+
        }
+
+
        public InputStream getInputStream() throws IOException {
+
            return new ByteArrayInputStream(bytes);
+
        }
+
+
        public String getName() {
+
            return null;
+
        }
+
+
        public OutputStream getOutputStream() throws IOException {
+
            return null;
+
        }
+
+
    }
+
+
}
+
 
+
</source>
+
 
+
==Demo Code==
+
 
+
The following example was inspired by an answer I gave on Stack Overflow (feel free to up vote).  It covers how to leverage JAXB's AttachmentMarshaller & AttachmentUnmarshaller to produce a message in the following format:
+
 
+
[xml_length][xml][attach1_length][attach1]...[attachN_length][attachN]
+
 
+
 
+
While this example is unique to a particular use case, it does demonstrate JAXB's attachment mechanism without requiring a JAX-WS provider.
+
 
+
<source lang="java">
+
+
package blog.attachments;
+
+
import java.awt.image.BufferedImage;
+
import java.io.FileInputStream;
+
import java.io.FileOutputStream;
+
import javax.xml.bind.JAXBContext;
+
+
public class Demo {
+
+
    public static void main(String[] args) throws Exception {
+
        JAXBContext jc = JAXBContext.newInstance(Root.class);
+
+
        Root root = new Root();
+
        root.setA("HELLO WORLD".getBytes());
+
        root.setB("BAR".getBytes());
+
        root.setC("HELLO WORLD".getBytes());
+
        root.setD(new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB));
+
+
        MessageWriter writer = new MessageWriter(jc);
+
        FileOutputStream outStream = new FileOutputStream("message.xml");
+
        writer.write(root, outStream);
+
        outStream.close();
+
+
        MessageReader reader = new MessageReader(jc);
+
        FileInputStream inStream = new FileInputStream("message.xml");
+
        Root root2 = (Root) reader.read(inStream);
+
        inStream.close();
+
        System.out.println(new String(root2.getA()));
+
        System.out.println(new String(root2.getB()));
+
        System.out.println(new String(root2.getC()));
+
        System.out.println(root2.getD());
+
    }
+
+
}
+
</source>
+
 
+
 
+
===MessageWriter===
+
<source lang="java">
+
+
package blog.attachments;
+
+
import java.io.ByteArrayOutputStream;
+
import java.io.ObjectOutputStream;
+
import java.io.OutputStream;
+
+
import javax.xml.bind.JAXBContext;
+
import javax.xml.bind.Marshaller;
+
+
import blog.attachments.ExampleAttachmentMarshaller.Attachment;
+
+
public class MessageWriter {
+
+
    private JAXBContext jaxbContext;
+
+
    public MessageWriter(JAXBContext jaxbContext) {
+
        this.jaxbContext = jaxbContext;
+
    }
+
+
    /**
+
    * Write the message in the following format:
+
    * [xml_length][xml][attach1_length][attach1]...[attachN_length][attachN]
+
    */
+
    public void write(Object object, OutputStream stream) {
+
        try {
+
            Marshaller marshaller = jaxbContext.createMarshaller();
+
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
+
            ExampleAttachmentMarshaller attachmentMarshaller = new ExampleAttachmentMarshaller();
+
            marshaller.setAttachmentMarshaller(attachmentMarshaller);
+
            ByteArrayOutputStream xmlStream = new ByteArrayOutputStream();
+
            marshaller.marshal(object, xmlStream);
+
            byte[] xml = xmlStream.toByteArray();
+
            xmlStream.close();
+
+
            ObjectOutputStream messageStream = new ObjectOutputStream(stream);
+
+
            messageStream.writeInt(xml.length); //[xml_length]
+
            messageStream.write(xml); // [xml]
+
+
            for(Attachment attachment : attachmentMarshaller.getAttachments()) {
+
                messageStream.writeInt(attachment.getLength()); // [attachX_length]
+
                messageStream.write(attachment.getData(), attachment.getOffset(), attachment.getLength());  // [attachX]
+
            }
+
+
            messageStream.flush();
+
        } catch(Exception e) {
+
            throw new RuntimeException(e);
+
        }
+
    }
+
+
}
+
</source>
+
 
+
===MessageReader===
+
 
+
<source lang="java">
+
+
package blog.attachments;
+
+
import java.io.ByteArrayInputStream;
+
import java.io.InputStream;
+
import java.io.ObjectInputStream;
+
+
import javax.xml.bind.JAXBContext;
+
import javax.xml.bind.Unmarshaller;
+
+
public class MessageReader {
+
+
    private JAXBContext jaxbContext;
+
+
    public MessageReader(JAXBContext jaxbContext) {
+
        this.jaxbContext = jaxbContext;
+
    }
+
+
    /**
+
    * Read the message from the following format:
+
    * [xml_length][xml][attach1_length][attach1]...[attachN_length][attachN]
+
    */
+
    public Object read(InputStream stream) {
+
        try {
+
            ObjectInputStream inputStream = new ObjectInputStream(stream);
+
            int xmlLength = inputStream.readInt();  // [xml_length]
+
+
            byte[] xmlIn = new byte[xmlLength];
+
            inputStream.read(xmlIn);  // [xml]
+
+
            ExampleAttachmentUnmarshaller attachmentUnmarshaller = new ExampleAttachmentUnmarshaller();
+
            int id = 1;
+
            while(inputStream.available() > 0) {
+
                int length = inputStream.readInt();  // [attachX_length]
+
                byte[] data = new byte[length];  // [attachX]
+
                inputStream.read(data);
+
                attachmentUnmarshaller.getAttachments().put("cid:" + String.valueOf(id++), data);
+
            }
+
+
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+
            unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);
+
            ByteArrayInputStream byteInputStream = new ByteArrayInputStream(xmlIn);
+
            Object object = unmarshaller.unmarshal(byteInputStream);
+
            byteInputStream.close();
+
            inputStream.close();
+
            return object;
+
        } catch(Exception e) {
+
            throw new RuntimeException(e);
+
        }
+
    }
+
+
}
+
</source>
+
 
+
{{EclipseLink_MOXy
+
|up=    [[EclipseLink/UserGuide/MOXy|MOXy User Guide]]
+
|previous=[[EclipseLink/UserGuide/MOXy/Mapping_JPA_Entities_to_XML|Mapping JPA Entities to XML]]
+
|next=n
+
|version=2.2.0 Draft}}
+

Latest revision as of 12:13, 30 January 2013

Warning This page is obsolete. Please see Developing JAXB Applications Using EclipseLink MOXy for current information.