1/*
2 * Copyright (c) 1997, 2013, 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.api.message;
27
28import com.sun.istack.internal.NotNull;
29import com.sun.istack.internal.Nullable;
30import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
31import com.sun.xml.internal.ws.api.SOAPVersion;
32import com.sun.xml.internal.ws.api.WSBinding;
33import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
34import com.sun.xml.internal.ws.api.message.saaj.SAAJFactory;
35import com.sun.xml.internal.ws.api.pipe.Tube;
36import com.sun.xml.internal.ws.api.pipe.Codecs;
37import com.sun.xml.internal.ws.fault.SOAPFaultBuilder;
38import com.sun.xml.internal.ws.message.AttachmentSetImpl;
39import com.sun.xml.internal.ws.message.DOMMessage;
40import com.sun.xml.internal.ws.message.EmptyMessageImpl;
41import com.sun.xml.internal.ws.message.ProblemActionHeader;
42import com.sun.xml.internal.ws.message.stream.PayloadStreamReaderMessage;
43import com.sun.xml.internal.ws.message.jaxb.JAXBMessage;
44import com.sun.xml.internal.ws.message.source.PayloadSourceMessage;
45import com.sun.xml.internal.ws.message.source.ProtocolSourceMessage;
46import com.sun.xml.internal.ws.spi.db.BindingContextFactory;
47import com.sun.xml.internal.ws.streaming.XMLStreamReaderException;
48import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
49import com.sun.xml.internal.ws.util.DOMUtil;
50import com.sun.xml.internal.ws.addressing.WsaTubeHelper;
51import com.sun.xml.internal.ws.addressing.model.MissingAddressingHeaderException;
52import com.sun.xml.internal.ws.resources.AddressingMessages;
53import org.w3c.dom.Element;
54import org.w3c.dom.Node;
55
56import javax.xml.bind.JAXBContext;
57import javax.xml.bind.JAXBElement;
58import javax.xml.bind.Marshaller;
59import javax.xml.bind.annotation.XmlRootElement;
60import javax.xml.namespace.QName;
61import javax.xml.soap.*;
62import javax.xml.stream.XMLStreamConstants;
63import javax.xml.stream.XMLStreamException;
64import javax.xml.stream.XMLStreamReader;
65import javax.xml.transform.Source;
66import javax.xml.transform.sax.SAXSource;
67import javax.xml.transform.stream.StreamSource;
68import javax.xml.transform.dom.DOMSource;
69import javax.xml.ws.ProtocolException;
70import javax.xml.ws.WebServiceException;
71
72/**
73 * Factory methods for various {@link Message} implementations.
74 *
75 * <p>
76 * This class provides various methods to create different
77 * flavors of {@link Message} classes that store data
78 * in different formats.
79 *
80 * <p>
81 * This is a part of the JAX-WS RI internal API so that
82 * {@link Tube} implementations can reuse the implementations
83 * done inside the JAX-WS.
84 *
85 * <p>
86 * If you find some of the useful convenience methods missing
87 * from this class, please talk to us.
88 *
89 *
90 * @author Kohsuke Kawaguchi
91 */
92public abstract class Messages {
93    private Messages() {}
94
95    /**
96     * Creates a {@link Message} backed by a JAXB bean.
97     * @deprecated
98     * @param context
99     *      The context to be used to produce infoset from the object. Must not be null.
100     * @param jaxbObject
101     *      The JAXB object that represents the payload. must not be null. This object
102     *      must be bound to an element (which means it either is a {@link JAXBElement} or
103     *      an instanceof a class with {@link XmlRootElement}).
104     * @param soapVersion
105     *      The SOAP version of the message. Must not be null.
106     */
107    public static Message create(JAXBContext context, Object jaxbObject, SOAPVersion soapVersion) {
108        return JAXBMessage.create(context,jaxbObject,soapVersion);
109    }
110
111    /**
112     * @deprecated
113     * For use when creating a Dispatch object with an unknown JAXB implementation
114     * for he JAXBContext parameter.
115     *
116     */
117    public static Message createRaw(JAXBContext context, Object jaxbObject, SOAPVersion soapVersion) {
118        return JAXBMessage.createRaw(context,jaxbObject,soapVersion);
119    }
120
121    /**
122     * @deprecated
123     *      Use {@link #create(JAXBRIContext, Object, SOAPVersion)}
124     */
125    public static Message create(Marshaller marshaller, Object jaxbObject, SOAPVersion soapVersion) {
126        return create(BindingContextFactory.getBindingContext(marshaller).getJAXBContext(),jaxbObject,soapVersion);
127    }
128
129    /**
130     * Creates a {@link Message} backed by a SAAJ {@link SOAPMessage} object.
131     *
132     * <p>
133     * If the {@link SOAPMessage} contains headers and attachments, this method
134     * does the right thing.
135     *
136     * @param saaj
137     *      The SOAP message to be represented as a {@link Message}.
138     *      Must not be null. Once this method is invoked, the created
139     *      {@link Message} will own the {@link SOAPMessage}, so it shall
140     *      never be touched directly.
141     */
142    public static Message create(SOAPMessage saaj) {
143        return SAAJFactory.create(saaj);
144    }
145
146    /**
147     * Creates a {@link Message} using {@link Source} as payload.
148     *
149     * @param payload
150     *      Source payload is {@link Message}'s payload
151     *      Must not be null. Once this method is invoked, the created
152     *      {@link Message} will own the {@link Source}, so it shall
153     *      never be touched directly.
154     *
155     * @param ver
156     *      The SOAP version of the message. Must not be null.
157     */
158    public static Message createUsingPayload(Source payload, SOAPVersion ver) {
159        if (payload instanceof DOMSource) {
160            if (((DOMSource)payload).getNode() == null) {
161                return new EmptyMessageImpl(ver);
162            }
163        } else if (payload instanceof StreamSource) {
164            StreamSource ss = (StreamSource)payload;
165            if (ss.getInputStream() == null && ss.getReader() == null && ss.getSystemId() == null) {
166                return new EmptyMessageImpl(ver);
167            }
168        } else if (payload instanceof SAXSource) {
169            SAXSource ss = (SAXSource)payload;
170            if (ss.getInputSource() == null && ss.getXMLReader() == null) {
171                return new EmptyMessageImpl(ver);
172            }
173        }
174        return new PayloadSourceMessage(payload, ver);
175    }
176
177    /**
178     * Creates a {@link Message} using {@link XMLStreamReader} as payload.
179     *
180     * @param payload
181     *      XMLStreamReader payload is {@link Message}'s payload
182     *      Must not be null. Once this method is invoked, the created
183     *      {@link Message} will own the {@link XMLStreamReader}, so it shall
184     *      never be touched directly.
185     *
186     * @param ver
187     *      The SOAP version of the message. Must not be null.
188     */
189    public static Message createUsingPayload(XMLStreamReader payload, SOAPVersion ver) {
190        return new PayloadStreamReaderMessage(payload, ver);
191    }
192
193    /**
194     * Creates a {@link Message} from an {@link Element} that represents
195     * a payload.
196     *
197     * @param payload
198     *      The element that becomes the child element of the SOAP body.
199     *      Must not be null.
200     *
201     * @param ver
202     *      The SOAP version of the message. Must not be null.
203     */
204    public static Message createUsingPayload(Element payload, SOAPVersion ver) {
205        return new DOMMessage(ver,payload);
206    }
207
208    /**
209     * Creates a {@link Message} from an {@link Element} that represents
210     * the whole SOAP message.
211     *
212     * @param soapEnvelope
213     *      The SOAP envelope element.
214     */
215    public static Message create(Element soapEnvelope) {
216        SOAPVersion ver = SOAPVersion.fromNsUri(soapEnvelope.getNamespaceURI());
217        // find the headers
218        Element header = DOMUtil.getFirstChild(soapEnvelope, ver.nsUri, "Header");
219        HeaderList headers = null;
220        if(header!=null) {
221            for( Node n=header.getFirstChild(); n!=null; n=n.getNextSibling() ) {
222                if(n.getNodeType()==Node.ELEMENT_NODE) {
223                    if(headers==null)
224                        headers = new HeaderList(ver);
225                    headers.add(Headers.create((Element)n));
226                }
227            }
228        }
229
230        // find the payload
231        Element body = DOMUtil.getFirstChild(soapEnvelope, ver.nsUri, "Body");
232        if(body==null)
233            throw new WebServiceException("Message doesn't have <S:Body> "+soapEnvelope);
234        Element payload = DOMUtil.getFirstChild(soapEnvelope, ver.nsUri, "Body");
235
236        if(payload==null) {
237            return new EmptyMessageImpl(headers, new AttachmentSetImpl(), ver);
238        } else {
239            return new DOMMessage(ver,headers,payload);
240        }
241    }
242
243    /**
244     * Creates a {@link Message} using Source as entire envelope.
245     *
246     * @param envelope
247     *      Source envelope is used to create {@link Message}
248     *      Must not be null. Once this method is invoked, the created
249     *      {@link Message} will own the {@link Source}, so it shall
250     *      never be touched directly.
251     *
252     */
253    public static Message create(Source envelope, SOAPVersion soapVersion) {
254        return new ProtocolSourceMessage(envelope, soapVersion);
255    }
256
257
258    /**
259     * Creates a {@link Message} that doesn't have any payload.
260     */
261    public static Message createEmpty(SOAPVersion soapVersion) {
262        return new EmptyMessageImpl(soapVersion);
263    }
264
265    /**
266     * Creates a {@link Message} from {@link XMLStreamReader} that points to
267     * the start of the envelope.
268     *
269     * @param reader
270     *      can point to the start document or the start element (of &lt;s:Envelope>)
271     */
272    public static @NotNull Message create(@NotNull XMLStreamReader reader) {
273        // skip until the root element
274        if(reader.getEventType()!=XMLStreamConstants.START_ELEMENT)
275            XMLStreamReaderUtil.nextElementContent(reader);
276        assert reader.getEventType()== XMLStreamConstants.START_ELEMENT :reader.getEventType();
277
278        SOAPVersion ver = SOAPVersion.fromNsUri(reader.getNamespaceURI());
279
280        return Codecs.createSOAPEnvelopeXmlCodec(ver).decode(reader);
281    }
282
283    /**
284     * Creates a {@link Message} from {@link XMLStreamBuffer} that retains the
285     * whole envelope infoset.
286     *
287     * @param xsb
288     *      This buffer must contain the infoset of the whole envelope.
289     */
290    public static @NotNull Message create(@NotNull XMLStreamBuffer xsb) {
291        // TODO: we should be able to let Messae know that it's working off from a buffer,
292        // to make some of the operations more efficient.
293        // meanwhile, adding this as an API so that our users can take advantage of it
294        // when we get around to such an implementation later.
295        try {
296            return create(xsb.readAsXMLStreamReader());
297        } catch (XMLStreamException e) {
298            throw new XMLStreamReaderException(e);
299        }
300    }
301
302    /**
303     * Creates a {@link Message} that represents an exception as a fault. The
304     * created message reflects if t or t.getCause() is SOAPFaultException.
305     *
306     * creates a fault message with default faultCode env:Server if t or t.getCause()
307     * is not SOAPFaultException. Otherwise, it use SOAPFaultException's faultCode
308     *
309     * @return
310     *      Always non-null. A message that wraps this {@link Throwable}.
311     *
312     */
313    public static Message create(Throwable t, SOAPVersion soapVersion) {
314        return SOAPFaultBuilder.createSOAPFaultMessage(soapVersion, null, t);
315    }
316
317    /**
318     * Creates a fault {@link Message}.
319     *
320     * <p>
321     * This method is not designed for efficiency, and we don't expect
322     * to be used for the performance critical codepath.
323     *
324     * @param fault
325     *      The populated SAAJ data structure that represents a fault
326     *      in detail.
327     *
328     * @return
329     *      Always non-null. A message that wraps this {@link SOAPFault}.
330     */
331    public static Message create(SOAPFault fault) {
332        SOAPVersion ver = SOAPVersion.fromNsUri(fault.getNamespaceURI());
333        return new DOMMessage(ver,fault);
334    }
335
336    /**
337     * @deprecated
338     *      Use {@link #createAddressingFaultMessage(WSBinding, Packet, QName)}
339     */
340    public static Message createAddressingFaultMessage(WSBinding binding, QName missingHeader) {
341        return createAddressingFaultMessage(binding,null,missingHeader);
342    }
343
344    /**
345     * Creates a fault {@link Message} that captures the code/subcode/subsubcode
346     * defined by WS-Addressing if one of the expected WS-Addressing headers is
347     * missing in the message
348     *
349     * @param binding WSBinding
350     * @param p
351     *      {@link Packet} that was missing a WS-Addressing header.
352     * @param missingHeader The missing WS-Addressing Header
353     * @return
354     *      A message representing SOAPFault that contains the WS-Addressing code/subcode/subsubcode.
355     */
356    public static Message createAddressingFaultMessage(WSBinding binding, Packet p, QName missingHeader) {
357        AddressingVersion av = binding.getAddressingVersion();
358        if(av == null) {
359            // Addressing is not enabled.
360            throw new WebServiceException(AddressingMessages.ADDRESSING_SHOULD_BE_ENABLED());
361        }
362        WsaTubeHelper helper = av.getWsaHelper(null,null,binding);
363        return create(helper.newMapRequiredFault(new MissingAddressingHeaderException(missingHeader,p)));
364    }
365    /**
366     * Creates a fault {@link Message} that captures the code/subcode/subsubcode
367     * defined by WS-Addressing if wsa:Action is not supported.
368     *
369     * @param unsupportedAction The unsupported Action. Must not be null.
370     * @param av The WS-Addressing version of the message. Must not be null.
371     * @param sv The SOAP Version of the message. Must not be null.
372     *
373     * @return
374     *      A message representing SOAPFault that contains the WS-Addressing code/subcode/subsubcode.
375     */
376    public static Message create(@NotNull String unsupportedAction, @NotNull AddressingVersion av, @NotNull SOAPVersion sv) {
377        QName subcode = av.actionNotSupportedTag;
378        String faultstring = String.format(av.actionNotSupportedText, unsupportedAction);
379
380        Message faultMessage;
381        SOAPFault fault;
382        try {
383            if (sv == SOAPVersion.SOAP_12) {
384                fault = SOAPVersion.SOAP_12.getSOAPFactory().createFault();
385                fault.setFaultCode(SOAPConstants.SOAP_SENDER_FAULT);
386                fault.appendFaultSubcode(subcode);
387                Detail detail = fault.addDetail();
388                SOAPElement se = detail.addChildElement(av.problemActionTag);
389                se = se.addChildElement(av.actionTag);
390                se.addTextNode(unsupportedAction);
391            } else {
392                fault = SOAPVersion.SOAP_11.getSOAPFactory().createFault();
393                fault.setFaultCode(subcode);
394            }
395            fault.setFaultString(faultstring);
396
397            faultMessage = SOAPFaultBuilder.createSOAPFaultMessage(sv, fault);
398            if (sv == SOAPVersion.SOAP_11) {
399                faultMessage.getHeaders().add(new ProblemActionHeader(unsupportedAction, av));
400            }
401        } catch (SOAPException e) {
402            throw new WebServiceException(e);
403        }
404
405        return faultMessage;
406    }
407
408    /**
409     * To be called to convert a  {@link ProtocolException} and faultcode for a given {@link SOAPVersion} in to a {@link Message}.
410     *
411     * @param soapVersion {@link SOAPVersion#SOAP_11} or {@link SOAPVersion#SOAP_12}
412     * @param pex a ProtocolException
413     * @param faultcode soap faultcode. Its ignored if the {@link ProtocolException} instance is {@link javax.xml.ws.soap.SOAPFaultException} and it has a
414     * faultcode present in the underlying {@link SOAPFault}.
415     * @return {@link Message} representing SOAP fault
416     */
417    public static @NotNull Message create(@NotNull SOAPVersion soapVersion, @NotNull ProtocolException pex, @Nullable QName faultcode){
418        return SOAPFaultBuilder.createSOAPFaultMessage(soapVersion, pex, faultcode);
419    }
420}
421