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;
27
28import com.sun.istack.internal.NotNull;
29import com.sun.xml.internal.bind.api.Bridge;
30import com.sun.xml.internal.ws.api.SOAPVersion;
31import com.sun.xml.internal.ws.api.message.Header;
32import com.sun.xml.internal.ws.api.message.Message;
33import com.sun.xml.internal.ws.api.message.MessageHeaders;
34import com.sun.xml.internal.ws.api.message.MessageWritable;
35import com.sun.xml.internal.ws.api.message.Packet;
36import com.sun.xml.internal.ws.api.message.saaj.SAAJFactory;
37import com.sun.xml.internal.ws.encoding.TagInfoset;
38import com.sun.xml.internal.ws.message.saaj.SAAJMessage;
39import com.sun.xml.internal.ws.spi.db.XMLBridge;
40import java.util.ArrayList;
41import java.util.Collections;
42import org.xml.sax.ContentHandler;
43import org.xml.sax.ErrorHandler;
44import org.xml.sax.SAXException;
45import org.xml.sax.helpers.AttributesImpl;
46import org.xml.sax.helpers.LocatorImpl;
47
48import javax.xml.bind.JAXBException;
49import javax.xml.bind.Unmarshaller;
50import javax.xml.soap.SOAPException;
51import javax.xml.soap.SOAPMessage;
52import javax.xml.stream.XMLStreamException;
53import javax.xml.stream.XMLStreamWriter;
54import javax.xml.transform.Source;
55import javax.xml.transform.sax.SAXSource;
56
57import java.util.List;
58import java.util.Map;
59
60/**
61 * Partial {@link Message} implementation.
62 *
63 * <p>
64 * This class implements some of the {@link Message} methods.
65 * The idea is that those implementations may be non-optimal but
66 * it may save effort in implementing {@link Message} and reduce
67 * the code size.
68 *
69 * <p>
70 * {@link Message} classes that are used more commonly should
71 * examine carefully which method can be implemented faster,
72 * and override them accordingly.
73 *
74 * @author Kohsuke Kawaguchi
75 */
76public abstract class AbstractMessageImpl extends Message {
77    /**
78     * SOAP version of this message.
79     * Used to implement some of the methods, but nothing more than that.
80     *
81     * <p>
82     * So if you aren't using those methods that use this field,
83     * this can be null.
84     */
85    protected final SOAPVersion soapVersion;
86
87    protected @NotNull TagInfoset envelopeTag;
88    protected @NotNull TagInfoset headerTag;
89    protected @NotNull TagInfoset bodyTag;
90
91    protected static final AttributesImpl EMPTY_ATTS;
92    protected static final LocatorImpl NULL_LOCATOR = new LocatorImpl();
93    protected static final List<TagInfoset> DEFAULT_TAGS;
94
95    static void create(SOAPVersion v, List c) {
96        int base = v.ordinal()*3;
97        c.add(base, new TagInfoset(v.nsUri, "Envelope", "S", EMPTY_ATTS,"S", v.nsUri));
98        c.add(base+1, new TagInfoset(v.nsUri, "Header", "S", EMPTY_ATTS));
99        c.add(base+2, new TagInfoset(v.nsUri, "Body", "S", EMPTY_ATTS));
100    }
101
102    static {
103        EMPTY_ATTS = new AttributesImpl();
104        List<TagInfoset> tagList = new ArrayList<TagInfoset>();
105        create(SOAPVersion.SOAP_11, tagList);
106        create(SOAPVersion.SOAP_12, tagList);
107        DEFAULT_TAGS = Collections.unmodifiableList(tagList);
108    }
109
110    protected AbstractMessageImpl(SOAPVersion soapVersion) {
111        this.soapVersion = soapVersion;
112    }
113
114    @Override
115    public SOAPVersion getSOAPVersion() {
116        return soapVersion;
117    }
118    /**
119     * Copy constructor.
120     */
121    protected AbstractMessageImpl(AbstractMessageImpl that) {
122        this.soapVersion = that.soapVersion;
123        this.copyFrom(that);
124    }
125
126    @Override
127    public Source readEnvelopeAsSource() {
128        return new SAXSource(new XMLReaderImpl(this), XMLReaderImpl.THE_SOURCE);
129    }
130
131    @Override
132    public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
133        if(hasAttachments())
134            unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshallerImpl(getAttachments()));
135        try {
136            return (T) unmarshaller.unmarshal(readPayloadAsSource());
137        } finally{
138            unmarshaller.setAttachmentUnmarshaller(null);
139        }
140    }
141    /** @deprecated */
142    @Override
143    public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
144        return bridge.unmarshal(readPayloadAsSource(),
145            hasAttachments()? new AttachmentUnmarshallerImpl(getAttachments()) : null );
146    }
147
148    @Override
149    public <T> T readPayloadAsJAXB(XMLBridge<T> bridge) throws JAXBException {
150        return bridge.unmarshal(readPayloadAsSource(),
151            hasAttachments()? new AttachmentUnmarshallerImpl(getAttachments()) : null );
152    }
153
154    public void writeToBodyStart(XMLStreamWriter w) throws XMLStreamException {
155        String soapNsUri = soapVersion.nsUri;
156        w.writeStartDocument();
157        w.writeStartElement("S","Envelope",soapNsUri);
158        w.writeNamespace("S",soapNsUri);
159        if(hasHeaders()) {
160            w.writeStartElement("S","Header",soapNsUri);
161            MessageHeaders headers = getHeaders();
162            for (Header h : headers.asList()) {
163                h.writeTo(w);
164            }
165            w.writeEndElement();
166        }
167        // write the body
168        w.writeStartElement("S","Body",soapNsUri);
169    }
170
171    /**
172     * Default implementation that relies on {@link #writePayloadTo(XMLStreamWriter)}
173     */
174    @Override
175    public void writeTo(XMLStreamWriter w) throws XMLStreamException {
176        writeToBodyStart(w);
177        writePayloadTo(w);
178
179        w.writeEndElement();
180        w.writeEndElement();
181        w.writeEndDocument();
182    }
183
184    /**
185     * Writes the whole envelope as SAX events.
186     */
187    @Override
188    public void writeTo( ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException {
189        String soapNsUri = soapVersion.nsUri;
190
191        contentHandler.setDocumentLocator(NULL_LOCATOR);
192        contentHandler.startDocument();
193        contentHandler.startPrefixMapping("S",soapNsUri);
194        contentHandler.startElement(soapNsUri,"Envelope","S:Envelope",EMPTY_ATTS);
195        if(hasHeaders()) {
196            contentHandler.startElement(soapNsUri,"Header","S:Header",EMPTY_ATTS);
197            MessageHeaders headers = getHeaders();
198            for (Header h : headers.asList()) {
199                h.writeTo(contentHandler,errorHandler);
200            }
201            contentHandler.endElement(soapNsUri,"Header","S:Header");
202        }
203        // write the body
204        contentHandler.startElement(soapNsUri,"Body","S:Body",EMPTY_ATTS);
205        writePayloadTo(contentHandler,errorHandler, true);
206        contentHandler.endElement(soapNsUri,"Body","S:Body");
207        contentHandler.endElement(soapNsUri,"Envelope","S:Envelope");
208    }
209
210    /**
211     * Writes the payload to SAX events.
212     *
213     * @param fragment
214     *      if true, this method will fire SAX events without start/endDocument events,
215     *      suitable for embedding this into a bigger SAX event sequence.
216     *      if false, this method generaets a completely SAX event sequence on its own.
217     */
218    protected abstract void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException;
219
220    public Message toSAAJ(Packet p, Boolean inbound) throws SOAPException {
221        SAAJMessage message = SAAJFactory.read(p);
222        if (message instanceof MessageWritable)
223            ((MessageWritable) message)
224                    .setMTOMConfiguration(p.getMtomFeature());
225        if (inbound != null) transportHeaders(p, inbound, message.readAsSOAPMessage());
226        return message;
227    }
228
229    /**
230     * Default implementation that uses {@link #writeTo(ContentHandler, ErrorHandler)}
231     */
232    @Override
233    public SOAPMessage readAsSOAPMessage() throws SOAPException {
234        return SAAJFactory.read(soapVersion, this);
235    }
236
237    @Override
238    public SOAPMessage readAsSOAPMessage(Packet packet, boolean inbound) throws SOAPException {
239        SOAPMessage msg = SAAJFactory.read(soapVersion, this, packet);
240        transportHeaders(packet, inbound, msg);
241        return msg;
242    }
243
244    private void transportHeaders(Packet packet, boolean inbound, SOAPMessage msg) throws SOAPException {
245        Map<String, List<String>> headers = getTransportHeaders(packet, inbound);
246        if (headers != null) {
247            addSOAPMimeHeaders(msg.getMimeHeaders(), headers);
248        }
249        if (msg.saveRequired()) msg.saveChanges();
250    }
251}
252