zoukankan      html  css  js  c++  java
  • JAXB, Web Services, and Binary Data

    JAXB, Web Services, and Binary Data
    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
    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
    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.

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

    }

    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. 

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

    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.

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

        }

    }

    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:

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

        }

    }

    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.

    Demo

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

    }

    MessageWriter

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

    }

    MessageReader

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

    }

    文章转自:http://blog.bdoughan.com/2011/03/jaxb-web-services-and-binary-data.html

  • 相关阅读:
    oracle数据库sql中文乱码问题,字符编码环境变量
    oracle数据库序列自增id
    上线注意事项
    impdp 导出导入注意事项
    linux 上不同用户权限问题
    plsql 工具命令窗口执行sql脚本问题
    oracle创建用户
    Redis会遇到的15个坑
    JavaScript的标准库
    JavaScript的运算符
  • 原文地址:https://www.cnblogs.com/likehua/p/2295819.html
Copyright © 2011-2022 走看看