1/*
2 * Copyright (c) 1997, 2015, 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.saaj;
27
28import java.util.Iterator;
29
30import javax.xml.soap.AttachmentPart;
31import javax.xml.soap.MessageFactory;
32import javax.xml.soap.SAAJMetaFactory;
33import javax.xml.soap.SOAPException;
34import javax.xml.soap.SOAPFactory;
35import javax.xml.soap.SOAPMessage;
36import javax.xml.stream.XMLStreamException;
37
38import org.xml.sax.SAXException;
39
40import com.sun.xml.internal.bind.marshaller.SAX2DOMEx;
41import com.sun.xml.internal.ws.api.SOAPVersion;
42import com.sun.xml.internal.ws.api.message.Attachment;
43import com.sun.xml.internal.ws.api.message.AttachmentEx;
44import com.sun.xml.internal.ws.api.message.Message;
45import com.sun.xml.internal.ws.api.message.Packet;
46import com.sun.xml.internal.ws.message.saaj.SAAJMessage;
47import com.sun.xml.internal.ws.util.ServiceFinder;
48import com.sun.xml.internal.ws.util.xml.XmlUtil;
49
50/**
51 * Factory SPI for SAAJ implementations
52 *
53 * @since 2.2.6
54 */
55public class SAAJFactory {
56        private static final SAAJFactory instance = new SAAJFactory();
57
58    /**
59     * Creates a new <code>MessageFactory</code> object that is an instance
60     * of the specified implementation.  May be a dynamic message factory,
61     * a SOAP 1.1 message factory, or a SOAP 1.2 message factory. A dynamic
62     * message factory creates messages based on the MIME headers specified
63     * as arguments to the <code>createMessage</code> method.
64     *
65     * This method uses the SAAJMetaFactory to locate the implementation class
66     * and create the MessageFactory instance.
67     *
68     * @return a new instance of a <code>MessageFactory</code>
69     *
70     * @param protocol  a string constant representing the class of the
71     *                   specified message factory implementation. May be
72     *                   either <code>DYNAMIC_SOAP_PROTOCOL</code>,
73     *                   <code>DEFAULT_SOAP_PROTOCOL</code> (which is the same
74     *                   as) <code>SOAP_1_1_PROTOCOL</code>, or
75     *                   <code>SOAP_1_2_PROTOCOL</code>.
76     *
77     * @exception SOAPException if there was an error in creating the
78     *            specified implementation of  <code>MessageFactory</code>.
79     * @see SAAJMetaFactory
80     */
81        public static MessageFactory getMessageFactory(String protocol) throws SOAPException {
82                for (SAAJFactory s : ServiceFinder.find(SAAJFactory.class)) {
83                        MessageFactory mf = s.createMessageFactory(protocol);
84                        if (mf != null)
85                                return mf;
86                }
87
88        return instance.createMessageFactory(protocol);
89        }
90
91    /**
92     * Creates a new <code>SOAPFactory</code> object that is an instance of
93     * the specified implementation, this method uses the SAAJMetaFactory to
94     * locate the implementation class and create the SOAPFactory instance.
95     *
96     * @return a new instance of a <code>SOAPFactory</code>
97     *
98     * @param protocol  a string constant representing the protocol of the
99     *                   specified SOAP factory implementation. May be
100     *                   either <code>DYNAMIC_SOAP_PROTOCOL</code>,
101     *                   <code>DEFAULT_SOAP_PROTOCOL</code> (which is the same
102     *                   as) <code>SOAP_1_1_PROTOCOL</code>, or
103     *                   <code>SOAP_1_2_PROTOCOL</code>.
104     *
105     * @exception SOAPException if there was an error creating the
106     *            specified <code>SOAPFactory</code>
107     * @see SAAJMetaFactory
108     */
109        public static SOAPFactory getSOAPFactory(String protocol) throws SOAPException {
110                for (SAAJFactory s : ServiceFinder.find(SAAJFactory.class)) {
111                        SOAPFactory sf = s.createSOAPFactory(protocol);
112                        if (sf != null)
113                                return sf;
114                }
115
116        return instance.createSOAPFactory(protocol);
117        }
118
119        /**
120         * Creates Message from SOAPMessage
121         * @param saaj SOAPMessage
122         * @return created Message
123         */
124        public static Message create(SOAPMessage saaj) {
125                for (SAAJFactory s : ServiceFinder.find(SAAJFactory.class)) {
126                        Message m = s.createMessage(saaj);
127                        if (m != null)
128                                return m;
129                }
130
131        return instance.createMessage(saaj);
132        }
133
134        /**
135         * Reads Message as SOAPMessage.  After this call message is consumed.
136         * @param soapVersion SOAP version
137         * @param message Message
138         * @return Created SOAPMessage
139         * @throws SOAPException if SAAJ processing fails
140         */
141        public static SOAPMessage read(SOAPVersion soapVersion, Message message) throws SOAPException {
142                for (SAAJFactory s : ServiceFinder.find(SAAJFactory.class)) {
143                        SOAPMessage msg = s.readAsSOAPMessage(soapVersion, message);
144                        if (msg != null)
145                                return msg;
146                }
147
148        return instance.readAsSOAPMessage(soapVersion, message);
149        }
150
151        /**
152     * Reads Message as SOAPMessage.  After this call message is consumed.
153     * @param soapVersion SOAP version
154     * @param message Message
155     * @param packet The packet that owns the Message
156     * @return Created SOAPMessage
157     * @throws SOAPException if SAAJ processing fails
158     */
159    public static SOAPMessage read(SOAPVersion soapVersion, Message message, Packet packet) throws SOAPException {
160        SAAJFactory saajfac = packet.getSAAJFactory();
161        if (saajfac != null) {
162            SOAPMessage msg = saajfac.readAsSOAPMessage(soapVersion, message, packet);
163            if (msg != null) return msg;
164        }
165        for (SAAJFactory s : ServiceFinder.find(SAAJFactory.class)) {
166            SOAPMessage msg = s.readAsSOAPMessage(soapVersion, message, packet);
167            if (msg != null)
168                return msg;
169        }
170
171        return instance.readAsSOAPMessage(soapVersion, message, packet);
172    }
173
174    /**
175     * Reads the message within the Packet to a SAAJMessage.  After this call message is consumed.
176     * @param packet Packet
177     * @return Created SAAJPMessage
178     * @throws SOAPException if SAAJ processing fails
179     */
180    public static SAAJMessage read(Packet packet) throws SOAPException {
181        SAAJFactory saajfac = packet.getSAAJFactory();
182        if (saajfac != null) {
183            SAAJMessage msg = saajfac.readAsSAAJ(packet);
184            if (msg != null) return msg;
185        }
186        // Use the Component from the Packet if it exists.  Note the logic
187        // in the ServiceFinder is such that find(Class) is not equivalent
188        // to find (Class, null), so the ternary operator is needed.
189        ServiceFinder<SAAJFactory> factories = (packet.component != null ?
190                ServiceFinder.find(SAAJFactory.class, packet.component) :
191                ServiceFinder.find(SAAJFactory.class));
192        for (SAAJFactory s : factories) {
193            SAAJMessage msg = s.readAsSAAJ(packet);
194            if (msg != null) return msg;
195        }
196        return instance.readAsSAAJ(packet);
197    }
198
199    /**
200     * Reads the message within the Packet to a SAAJMessage.  After this call message is consumed.
201     * @param packet Packet
202     * @return Created SAAJPMessage
203     * @throws SOAPException if SAAJ processing fails
204     */
205    public SAAJMessage readAsSAAJ(Packet packet) throws SOAPException {
206        SOAPVersion v = packet.getMessage().getSOAPVersion();
207        SOAPMessage msg = readAsSOAPMessage(v, packet.getMessage());
208        return new SAAJMessage(msg);
209    }
210
211    /**
212     * Creates a new <code>MessageFactory</code> object that is an instance
213     * of the specified implementation.  May be a dynamic message factory,
214     * a SOAP 1.1 message factory, or a SOAP 1.2 message factory. A dynamic
215     * message factory creates messages based on the MIME headers specified
216     * as arguments to the <code>createMessage</code> method.
217     *
218     * This method uses the SAAJMetaFactory to locate the implementation class
219     * and create the MessageFactory instance.
220     *
221     * @return a new instance of a <code>MessageFactory</code>
222     *
223     * @param protocol  a string constant representing the class of the
224     *                   specified message factory implementation. May be
225     *                   either <code>DYNAMIC_SOAP_PROTOCOL</code>,
226     *                   <code>DEFAULT_SOAP_PROTOCOL</code> (which is the same
227     *                   as) <code>SOAP_1_1_PROTOCOL</code>, or
228     *                   <code>SOAP_1_2_PROTOCOL</code>.
229     *
230     * @exception SOAPException if there was an error in creating the
231     *            specified implementation of  <code>MessageFactory</code>.
232     * @see SAAJMetaFactory
233     */
234        public MessageFactory createMessageFactory(String protocol) throws SOAPException {
235                return MessageFactory.newInstance(protocol);
236        }
237
238    /**
239     * Creates a new <code>SOAPFactory</code> object that is an instance of
240     * the specified implementation, this method uses the SAAJMetaFactory to
241     * locate the implementation class and create the SOAPFactory instance.
242     *
243     * @return a new instance of a <code>SOAPFactory</code>
244     *
245     * @param protocol  a string constant representing the protocol of the
246     *                   specified SOAP factory implementation. May be
247     *                   either <code>DYNAMIC_SOAP_PROTOCOL</code>,
248     *                   <code>DEFAULT_SOAP_PROTOCOL</code> (which is the same
249     *                   as) <code>SOAP_1_1_PROTOCOL</code>, or
250     *                   <code>SOAP_1_2_PROTOCOL</code>.
251     *
252     * @exception SOAPException if there was an error creating the
253     *            specified <code>SOAPFactory</code>
254     * @see SAAJMetaFactory
255     */
256        public SOAPFactory createSOAPFactory(String protocol) throws SOAPException {
257                return SOAPFactory.newInstance(protocol);
258        }
259
260        /**
261         * Creates Message from SOAPMessage
262         * @param saaj SOAPMessage
263         * @return created Message
264         */
265        public Message createMessage(SOAPMessage saaj) {
266                return new SAAJMessage(saaj);
267        }
268
269        /**
270         * Reads Message as SOAPMessage.  After this call message is consumed.
271         * @param soapVersion SOAP version
272         * @param message Message
273         * @return Created SOAPMessage
274         * @throws SOAPException if SAAJ processing fails
275         */
276        public SOAPMessage readAsSOAPMessage(final SOAPVersion soapVersion, final Message message) throws SOAPException {
277        SOAPMessage msg = soapVersion.getMessageFactory().createMessage();
278        SaajStaxWriter writer = new SaajStaxWriter(msg, soapVersion.nsUri);
279        try {
280            message.writeTo(writer);
281        } catch (XMLStreamException e) {
282            throw (e.getCause() instanceof SOAPException) ? (SOAPException) e.getCause() : new SOAPException(e);
283        }
284        msg = writer.getSOAPMessage();
285        addAttachmentsToSOAPMessage(msg, message);
286        if (msg.saveRequired())
287                msg.saveChanges();
288        return msg;
289        }
290
291    public SOAPMessage readAsSOAPMessageSax2Dom(final SOAPVersion soapVersion, final Message message) throws SOAPException {
292        SOAPMessage msg = soapVersion.getMessageFactory().createMessage();
293        SAX2DOMEx s2d = new SAX2DOMEx(msg.getSOAPPart());
294        try {
295            message.writeTo(s2d, XmlUtil.DRACONIAN_ERROR_HANDLER);
296        } catch (SAXException e) {
297            throw new SOAPException(e);
298        }
299        addAttachmentsToSOAPMessage(msg, message);
300        if (msg.saveRequired())
301            msg.saveChanges();
302        return msg;
303    }
304
305        static protected void addAttachmentsToSOAPMessage(SOAPMessage msg, Message message) {
306        for(Attachment att : message.getAttachments()) {
307            AttachmentPart part = msg.createAttachmentPart();
308            part.setDataHandler(att.asDataHandler());
309
310            // Be safe and avoid double angle-brackets.
311            String cid = att.getContentId();
312            if (cid != null) {
313                if (cid.startsWith("<") && cid.endsWith(">"))
314                    part.setContentId(cid);
315                else
316                    part.setContentId('<' + cid + '>');
317            }
318
319            // Add any MIME headers beside Content-ID, which is already
320            // accounted for above, and Content-Type, which is provided
321            // by the DataHandler above.
322            if (att instanceof AttachmentEx) {
323                AttachmentEx ax = (AttachmentEx) att;
324                Iterator<AttachmentEx.MimeHeader> imh = ax.getMimeHeaders();
325                while (imh.hasNext()) {
326                    AttachmentEx.MimeHeader ame = imh.next();
327                    if ((!"Content-ID".equals(ame.getName()))
328                            && (!"Content-Type".equals(ame.getName())))
329                        part.addMimeHeader(ame.getName(), ame.getValue());
330                }
331            }
332            msg.addAttachmentPart(part);
333        }
334    }
335
336    /**
337     * Reads Message as SOAPMessage.  After this call message is consumed.
338     * The implementation in this class simply calls readAsSOAPMessage(SOAPVersion, Message),
339     * and ignores the other parameters
340     * Subclasses can override and choose to base SOAPMessage creation on Packet properties if needed
341     * @param soapVersion SOAP version
342     * @param message Message
343     * @return Created SOAPMessage
344     * @throws SOAPException if SAAJ processing fails
345     */
346        public SOAPMessage readAsSOAPMessage(SOAPVersion soapVersion, Message message, Packet packet) throws SOAPException {
347            return readAsSOAPMessage(soapVersion, message);
348        }
349}
350