1/*
2 * Copyright (c) 1997, 2017, 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.messaging.saaj.soap.impl;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.io.OutputStreamWriter;
31
32import java.util.logging.Level;
33
34import javax.xml.namespace.QName;
35import javax.xml.soap.*;
36import javax.xml.stream.XMLStreamException;
37import javax.xml.stream.XMLStreamReader;
38import javax.xml.stream.XMLStreamWriter;
39import javax.xml.transform.*;
40import javax.xml.transform.dom.DOMSource;
41import javax.xml.transform.stream.StreamResult;
42
43import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
44import com.sun.xml.internal.messaging.saaj.soap.LazyEnvelope;
45import com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl;
46import com.sun.xml.internal.messaging.saaj.soap.StaxBridge;
47import com.sun.xml.internal.messaging.saaj.soap.StaxLazySourceBridge;
48import com.sun.xml.internal.messaging.saaj.soap.name.NameImpl;
49import com.sun.xml.internal.messaging.saaj.util.FastInfosetReflection;
50import com.sun.xml.internal.messaging.saaj.util.stax.LazyEnvelopeStaxReader;
51import com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer;
52
53import com.sun.xml.internal.org.jvnet.staxex.util.DOMStreamReader;
54import com.sun.xml.internal.org.jvnet.staxex.util.XMLStreamReaderToXMLStreamWriter;
55import org.w3c.dom.Element;
56
57/**
58 * Our implementation of the SOAP envelope.
59 *
60 * @author Anil Vijendran (anil@sun.com)
61 */
62public abstract class EnvelopeImpl extends ElementImpl implements LazyEnvelope {
63    protected HeaderImpl header;
64    protected BodyImpl body;
65    String omitXmlDecl = "yes";
66    String charset = "utf-8";
67    String xmlDecl = null;
68
69    protected EnvelopeImpl(SOAPDocumentImpl ownerDoc, Name name) {
70        super(ownerDoc, name);
71    }
72
73    protected EnvelopeImpl(SOAPDocumentImpl ownerDoc, QName name) {
74        super(ownerDoc, name);
75    }
76
77    protected EnvelopeImpl(
78        SOAPDocumentImpl ownerDoc,
79        NameImpl name,
80        boolean createHeader,
81        boolean createBody)
82        throws SOAPException {
83        this(ownerDoc, name);
84
85        ensureNamespaceIsDeclared(
86            getElementQName().getPrefix(), getElementQName().getNamespaceURI());
87
88        // XXX
89        if (createHeader)
90            addHeader();
91
92        if (createBody)
93            addBody();
94    }
95
96    public EnvelopeImpl(SOAPDocumentImpl ownerDoc, Element domElement) {
97        super(ownerDoc, domElement);
98    }
99
100    protected abstract NameImpl getHeaderName(String prefix);
101    protected abstract NameImpl getBodyName(String prefix);
102
103    @Override
104    public SOAPHeader addHeader() throws SOAPException {
105        return addHeader(null);
106    }
107
108    public SOAPHeader addHeader(String prefix) throws SOAPException {
109
110        if (prefix == null || prefix.equals("")) {
111            prefix = getPrefix();
112        }
113
114        NameImpl headerName = getHeaderName(prefix);
115        NameImpl bodyName = getBodyName(prefix);
116
117        HeaderImpl header = null;
118        SOAPElement firstChild = (SOAPElement) getFirstChildElement();
119
120        if (firstChild != null) {
121            if (firstChild.getElementName().equals(headerName)) {
122                log.severe("SAAJ0120.impl.header.already.exists");
123                throw new SOAPExceptionImpl("Can't add a header when one is already present.");
124            } else if (!firstChild.getElementName().equals(bodyName)) {
125                log.severe("SAAJ0121.impl.invalid.first.child.of.envelope");
126                throw new SOAPExceptionImpl("First child of Envelope must be either a Header or Body");
127            }
128        }
129
130        header = (HeaderImpl) createElement(headerName);
131        insertBefore(header.getDomElement(), firstChild);
132        header.ensureNamespaceIsDeclared(headerName.getPrefix(), headerName.getURI());
133
134        return header;
135    }
136
137    protected void lookForHeader() throws SOAPException {
138        NameImpl headerName = getHeaderName(null);
139
140        HeaderImpl hdr = (HeaderImpl) findChild(headerName);
141        header = hdr;
142    }
143
144    @Override
145    public SOAPHeader getHeader() throws SOAPException {
146        lookForHeader();
147        return header;
148    }
149
150    protected void lookForBody() throws SOAPException {
151        NameImpl bodyName = getBodyName(null);
152
153        BodyImpl bodyChildElement = (BodyImpl) findChild(bodyName);
154        body = bodyChildElement;
155    }
156
157    @Override
158    public SOAPBody addBody() throws SOAPException {
159        return addBody(null);
160    }
161
162    public SOAPBody addBody(String prefix) throws SOAPException {
163        lookForBody();
164
165        if (prefix == null || prefix.equals("")) {
166            prefix = getPrefix();
167        }
168
169        if (body == null) {
170            NameImpl bodyName = getBodyName(prefix);
171            body = (BodyImpl) createElement(bodyName);
172            insertBefore(body.getDomElement(), null);
173            body.ensureNamespaceIsDeclared(bodyName.getPrefix(), bodyName.getURI());
174        } else {
175            log.severe("SAAJ0122.impl.body.already.exists");
176            throw new SOAPExceptionImpl("Can't add a body when one is already present.");
177        }
178
179        return body;
180    }
181
182    @Override
183    protected SOAPElement addElement(Name name) throws SOAPException {
184        if (getBodyName(null).equals(name)) {
185            return addBody(name.getPrefix());
186        }
187        if (getHeaderName(null).equals(name)) {
188            return addHeader(name.getPrefix());
189        }
190
191        return super.addElement(name);
192    }
193
194    @Override
195    protected SOAPElement addElement(QName name) throws SOAPException {
196        if (getBodyName(null).equals(NameImpl.convertToName(name))) {
197            return addBody(name.getPrefix());
198        }
199        if (getHeaderName(null).equals(NameImpl.convertToName(name))) {
200            return addHeader(name.getPrefix());
201        }
202
203        return super.addElement(name);
204    }
205
206    @Override
207    public SOAPBody getBody() throws SOAPException {
208        lookForBody();
209        return body;
210    }
211
212    @Override
213    public Source getContent() {
214        return new DOMSource(getOwnerDocument());
215    }
216
217    @Override
218    public Name createName(String localName, String prefix, String uri)
219        throws SOAPException {
220
221        // validating parameters before passing them on
222        // to make sure that the namespace specification rules are followed
223
224        // reserved xmlns prefix cannot be used.
225        if ("xmlns".equals(prefix)) {
226            log.severe("SAAJ0123.impl.no.reserved.xmlns");
227            throw new SOAPExceptionImpl("Cannot declare reserved xmlns prefix");
228        }
229        // Qualified name cannot be xmlns.
230        if ((prefix == null) && ("xmlns".equals(localName))) {
231            log.severe("SAAJ0124.impl.qualified.name.cannot.be.xmlns");
232            throw new SOAPExceptionImpl("Qualified name cannot be xmlns");
233        }
234
235        return NameImpl.create(localName, prefix, uri);
236    }
237
238    public Name createName(String localName, String prefix)
239        throws SOAPException {
240        String namespace = getNamespaceURI(prefix);
241        if (namespace == null) {
242            log.log(
243                Level.SEVERE,
244                "SAAJ0126.impl.cannot.locate.ns",
245                new String[] { prefix });
246            throw new SOAPExceptionImpl(
247                "Unable to locate namespace for prefix " + prefix);
248        }
249        return NameImpl.create(localName, prefix, namespace);
250    }
251
252    @Override
253    public Name createName(String localName) throws SOAPException {
254        return NameImpl.createFromUnqualifiedName(localName);
255    }
256
257    public void setOmitXmlDecl(String value) {
258        this.omitXmlDecl = value;
259    }
260
261    public void setXmlDecl(String value) {
262        this.xmlDecl = value;
263    }
264
265    public void setCharsetEncoding(String value) {
266        charset = value;
267    }
268
269    @Override
270    public void output(OutputStream out) throws IOException {
271        try {
272//            materializeBody();
273            Transformer transformer =
274                EfficientStreamingTransformer.newTransformer();
275
276            transformer.setOutputProperty(
277                OutputKeys.OMIT_XML_DECLARATION, "yes");
278                /*omitXmlDecl);*/
279            // no equivalent for "setExpandEmptyElements"
280            transformer.setOutputProperty(
281                OutputKeys.ENCODING,
282                charset);
283
284            if (omitXmlDecl.equals("no") && xmlDecl == null) {
285                xmlDecl = "<?xml version=\"" + getOwnerDocument().getXmlVersion() + "\" encoding=\"" +
286                    charset + "\" ?>";
287            }
288
289           StreamResult result = new StreamResult(out);
290            if (xmlDecl != null) {
291                OutputStreamWriter writer = new OutputStreamWriter(out, charset);
292                writer.write(xmlDecl);
293                writer.flush();
294                result = new StreamResult(writer);
295            }
296
297            if (log.isLoggable(Level.FINE)) {
298                log.log(Level.FINE, "SAAJ0190.impl.set.xml.declaration",
299                        new String[] { omitXmlDecl });
300                log.log(Level.FINE, "SAAJ0191.impl.set.encoding",
301                        new String[] { charset });
302            }
303
304            //StreamResult result = new StreamResult(out);
305            transformer.transform(getContent(), result);
306        } catch (Exception ex) {
307            throw new IOException(ex.getMessage());
308        }
309    }
310
311    /**
312     * Serialize to FI if boolean parameter set.
313     */
314    @Override
315    public void output(OutputStream out, boolean isFastInfoset)
316        throws IOException
317    {
318        if (!isFastInfoset) {
319            output(out);
320        }
321        else {
322            try {
323                // Run transform and generate FI output from content
324                Transformer transformer = EfficientStreamingTransformer.newTransformer();
325                    transformer.transform(getContent(),
326                        FastInfosetReflection.FastInfosetResult_new(out));
327            }
328            catch (Exception ex) {
329                throw new IOException(ex.getMessage());
330            }
331        }
332    }
333
334    //    public void prettyPrint(OutputStream out) throws IOException {
335    //        if (getDocument() == null)
336    //            initDocument();
337    //
338    //        OutputFormat format = OutputFormat.createPrettyPrint();
339    //
340    //        format.setIndentSize(2);
341    //        format.setNewlines(true);
342    //        format.setTrimText(true);
343    //        format.setPadText(true);
344    //        format.setExpandEmptyElements(false);
345    //
346    //        XMLWriter writer = new XMLWriter(out, format);
347    //        writer.write(getDocument());
348    //    }
349    //
350    //    public void prettyPrint(Writer out) throws IOException {
351    //        if (getDocument() == null)
352    //            initDocument();
353    //
354    //        OutputFormat format = OutputFormat.createPrettyPrint();
355    //
356    //        format.setIndentSize(2);
357    //        format.setNewlines(true);
358    //        format.setTrimText(true);
359    //        format.setPadText(true);
360    //        format.setExpandEmptyElements(false);
361    //
362    //        XMLWriter writer = new XMLWriter(out, format);
363    //        writer.write(getDocument());
364    //    }
365
366
367    @Override
368     public SOAPElement setElementQName(QName newName) throws SOAPException {
369        log.log(Level.SEVERE,
370                "SAAJ0146.impl.invalid.name.change.requested",
371                new Object[] {elementQName.getLocalPart(),
372                              newName.getLocalPart()});
373        throw new SOAPException("Cannot change name for "
374                                + elementQName.getLocalPart() + " to "
375                                + newName.getLocalPart());
376     }
377
378    @Override
379    public void setStaxBridge(StaxBridge bridge) throws SOAPException {
380        //set it on the body
381        ((BodyImpl) getBody()).setStaxBridge(bridge);
382    }
383
384    @Override
385    public StaxBridge getStaxBridge() throws SOAPException {
386        return ((BodyImpl) getBody()).getStaxBridge();
387    }
388
389    @Override
390    public XMLStreamReader getPayloadReader() throws SOAPException {
391        return ((BodyImpl) getBody()).getPayloadReader();
392    }
393
394    @Override
395    public void writeTo(final XMLStreamWriter writer) throws XMLStreamException, SOAPException {
396        StaxBridge readBridge = this.getStaxBridge();
397        if (readBridge != null && readBridge instanceof StaxLazySourceBridge) {
398//              StaxSoapWriteBridge writingBridge =  new StaxSoapWriteBridge(this);
399//              writingBridge.write(writer);
400                final String soapEnvNS = this.getNamespaceURI();
401                final DOMStreamReader reader = new DOMStreamReader(this);
402                XMLStreamReaderToXMLStreamWriter writingBridge =  new XMLStreamReaderToXMLStreamWriter();
403                writingBridge.bridge( new XMLStreamReaderToXMLStreamWriter.Breakpoint(reader, writer) {
404                        @Override
405                        public boolean proceedAfterStartElement()  {
406                                if ("Body".equals(reader.getLocalName()) && soapEnvNS.equals(reader.getNamespaceURI()) ){
407                                        return false;
408                                } else
409                                        return true;
410                        }
411            });//bridgeToBodyStartTag
412            ((StaxLazySourceBridge)readBridge).writePayloadTo(writer);
413            writer.writeEndElement();//body
414            writer.writeEndElement();//env
415            writer.writeEndDocument();
416            writer.flush();
417        } else {
418                LazyEnvelopeStaxReader lazyEnvReader = new LazyEnvelopeStaxReader(this);
419                XMLStreamReaderToXMLStreamWriter writingBridge = new XMLStreamReaderToXMLStreamWriter();
420                writingBridge.bridge(lazyEnvReader, writer);
421//            writingBridge.bridge(new XMLStreamReaderToXMLStreamWriter.Breakpoint(lazyEnvReader, writer));
422        }
423        //Assume the staxBridge is exhausted now since we would have read the body reader
424        ((BodyImpl) getBody()).setPayloadStreamRead();
425    }
426
427    @Override
428    public QName getPayloadQName() throws SOAPException {
429        return ((BodyImpl) getBody()).getPayloadQName();
430    }
431
432    @Override
433    public String getPayloadAttributeValue(String localName) throws SOAPException {
434        return ((BodyImpl) getBody()).getPayloadAttributeValue(localName);
435    }
436
437    @Override
438    public String getPayloadAttributeValue(QName qName) throws SOAPException {
439        return ((BodyImpl) getBody()).getPayloadAttributeValue(qName);
440    }
441
442    @Override
443    public boolean isLazy() {
444        try {
445            return ((BodyImpl) getBody()).isLazy();
446        } catch (SOAPException e) {
447            return false;
448        }
449    }
450
451}
452