1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.ws.message.jaxb;
27
28import com.sun.xml.internal.ws.api.SOAPVersion;
29import com.sun.xml.internal.ws.api.message.Message;
30import com.sun.xml.internal.ws.api.message.MessageHeaders;
31import com.sun.xml.internal.ws.encoding.SOAPBindingCodec;
32import com.sun.xml.internal.ws.message.AbstractMessageImpl;
33import com.sun.xml.internal.ws.message.PayloadElementSniffer;
34import com.sun.xml.internal.ws.spi.db.BindingContext;
35import com.sun.xml.internal.ws.spi.db.XMLBridge;
36import com.sun.xml.internal.org.jvnet.staxex.util.MtomStreamWriter;
37import com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil;
38import org.xml.sax.ContentHandler;
39import org.xml.sax.ErrorHandler;
40import org.xml.sax.SAXException;
41
42import javax.xml.bind.JAXBContext;
43import javax.xml.bind.JAXBException;
44import javax.xml.bind.Marshaller;
45import javax.xml.bind.attachment.AttachmentMarshaller;
46import javax.xml.namespace.QName;
47import javax.xml.stream.XMLStreamException;
48import javax.xml.stream.XMLStreamReader;
49import javax.xml.stream.XMLStreamWriter;
50import javax.xml.transform.Source;
51import javax.xml.ws.WebServiceException;
52import java.io.OutputStream;
53
54/**
55 * {@link Message} backed by a JAXB bean; this implementation is used when client uses
56 * Dispatch mechanism in JAXB/MESSAGE mode; difference from {@link JAXBMessage} is
57 * that {@code jaxbObject} holds whole SOAP message including SOAP envelope;
58 * it's the client who is responsible for preparing message content.
59 *
60 * @author Miroslav Kos (miroslav.kos at oracle.com)
61 */
62public class JAXBDispatchMessage extends AbstractMessageImpl {
63
64    private final Object jaxbObject;
65
66    private final XMLBridge bridge;
67
68    /**
69     * For the use case of a user-supplied JAXB context that is not
70     * a known JAXB type, as when creating a Disaptch object with a
71     * JAXB object parameter, we will marshal and unmarshal directly with
72     * the context object, as there is no Bond available.  In this case,
73     * swaRef is not supported.
74     */
75    private final JAXBContext rawContext;
76
77    /**
78     * Lazily sniffed payload element name
79     */
80    private QName payloadQName;
81
82    /**
83     * Copy constructor.
84     */
85    private JAXBDispatchMessage(JAXBDispatchMessage that) {
86        super(that);
87        jaxbObject = that.jaxbObject;
88        rawContext = that.rawContext;
89        bridge = that.bridge;
90        copyFrom(that);
91    }
92
93    public JAXBDispatchMessage(JAXBContext rawContext, Object jaxbObject, SOAPVersion soapVersion) {
94        super(soapVersion);
95        this.bridge = null;
96        this.rawContext = rawContext;
97        this.jaxbObject = jaxbObject;
98    }
99
100    public JAXBDispatchMessage(BindingContext context, Object jaxbObject, SOAPVersion soapVersion) {
101        super(soapVersion);
102        this.bridge = context.createFragmentBridge();
103        this.rawContext = null;
104        this.jaxbObject = jaxbObject;
105    }
106
107    @Override
108    protected void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
109        throw new UnsupportedOperationException();
110    }
111
112    @Override
113    public boolean hasHeaders() {
114        return false;
115    }
116
117    @Override
118    public MessageHeaders getHeaders() {
119        return null;
120    }
121
122    @Override
123    public String getPayloadLocalPart() {
124        if (payloadQName == null) {
125            readPayloadElement();
126        }
127        return payloadQName.getLocalPart();
128    }
129
130    @Override
131    public String getPayloadNamespaceURI() {
132        if (payloadQName == null) {
133            readPayloadElement();
134        }
135        return payloadQName.getNamespaceURI();
136    }
137
138    private void readPayloadElement() {
139        PayloadElementSniffer sniffer = new PayloadElementSniffer();
140        try {
141            if (rawContext != null) {
142                Marshaller m = rawContext.createMarshaller();
143                m.setProperty("jaxb.fragment", Boolean.FALSE);
144                m.marshal(jaxbObject, sniffer);
145            } else {
146                bridge.marshal(jaxbObject, sniffer, null);
147            }
148
149        } catch (JAXBException e) {
150            // if it's due to us aborting the processing after the first element,
151            // we can safely ignore this exception.
152            //
153            // if it's due to error in the object, the same error will be reported
154            // when the readHeader() method is used, so we don't have to report
155            // an error right now.
156            payloadQName = sniffer.getPayloadQName();
157        }
158    }
159
160    @Override
161    public boolean hasPayload() {
162        return true;
163    }
164
165    @Override
166    public Source readPayloadAsSource() {
167        throw new UnsupportedOperationException();
168    }
169
170    @Override
171    public XMLStreamReader readPayload() throws XMLStreamException {
172        throw new UnsupportedOperationException();
173    }
174
175    @Override
176    public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
177        throw new UnsupportedOperationException();
178    }
179
180    @Override
181    public Message copy() {
182        return new JAXBDispatchMessage(this).copyFrom(this);
183    }
184
185    @Override
186    @SuppressWarnings("unchecked")
187    public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
188        try {
189            // MtomCodec sets its own AttachmentMarshaller
190            AttachmentMarshaller am = (sw instanceof MtomStreamWriter)
191                    ? ((MtomStreamWriter) sw).getAttachmentMarshaller()
192                    : new AttachmentMarshallerImpl(attachmentSet);
193
194            // Get the encoding of the writer
195            String encoding = XMLStreamWriterUtil.getEncoding(sw);
196
197            // Get output stream and use JAXB UTF-8 writer
198            OutputStream os = bridge.supportOutputStream() ? XMLStreamWriterUtil.getOutputStream(sw) : null;
199            if (rawContext != null) {
200                Marshaller m = rawContext.createMarshaller();
201                m.setProperty("jaxb.fragment", Boolean.FALSE);
202                m.setAttachmentMarshaller(am);
203                if (os != null) {
204                    m.marshal(jaxbObject, os);
205                } else {
206                    m.marshal(jaxbObject, sw);
207                }
208
209            } else {
210
211                if (os != null && encoding != null && encoding.equalsIgnoreCase(SOAPBindingCodec.UTF8_ENCODING)) {
212                    bridge.marshal(jaxbObject, os, sw.getNamespaceContext(), am);
213                } else {
214                    bridge.marshal(jaxbObject, sw, am);
215                }
216            }
217            //cleanup() is not needed since JAXB doesn't keep ref to AttachmentMarshaller
218        } catch (JAXBException e) {
219            // bug 6449684, spec 4.3.4
220            throw new WebServiceException(e);
221        }
222    }
223}
224