src/share/jaxws_classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java

Sat, 02 Dec 2017 14:27:37 +0000

author
aefimov
date
Sat, 02 Dec 2017 14:27:37 +0000
changeset 1561
46562ec770e7
parent 1550
c4309a2d981b
permissions
-rw-r--r--

8186441: Change of behavior in the getMessage () method of the SOAPMessageContextImpl class
Reviewed-by: lancea

     1 /*
     2  * Copyright (c) 2013, 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  */
    26 package com.sun.xml.internal.ws.api.message.saaj;
    28 import java.util.Iterator;
    29 import java.util.Arrays;
    30 import java.util.List;
    31 import java.util.LinkedList;
    33 import javax.xml.namespace.NamespaceContext;
    34 import javax.xml.namespace.QName;
    35 import javax.xml.soap.SOAPElement;
    36 import javax.xml.soap.SOAPException;
    37 import javax.xml.soap.SOAPMessage;
    38 import javax.xml.stream.XMLStreamException;
    39 import javax.xml.stream.XMLStreamWriter;
    41 import org.w3c.dom.Comment;
    42 import org.w3c.dom.Node;
    44 /**
    45  * SaajStaxWriter builds a SAAJ SOAPMessage by using XMLStreamWriter interface.
    46  *
    47  * <p>
    48  * Defers creation of SOAPElement until all the aspects of the name of the element are known.
    49  * In some cases, the namespace uri is indicated only by the {@link #writeNamespace(String, String)} call.
    50  * After opening an element ({@code writeStartElement}, {@code writeEmptyElement} methods), all attributes
    51  * and namespace assignments are retained within {@link DeferredElement} object ({@code deferredElement} field).
    52  * As soon as any other method than {@code writeAttribute}, {@code writeNamespace}, {@code writeDefaultNamespace}
    53  * or {@code setNamespace} is called, the contents of {@code deferredElement} is transformed into new SOAPElement
    54  * (which is appropriately inserted into the SOAPMessage under construction).
    55  * This mechanism is necessary to fix JDK-8159058 issue.
    56  * </p>
    57  *
    58  * @author shih-chang.chen@oracle.com
    59  */
    60 public class SaajStaxWriter implements XMLStreamWriter {
    62     protected SOAPMessage soap;
    63     protected String envURI;
    64     protected SOAPElement currentElement;
    65     protected DeferredElement deferredElement;
    67     static final protected String Envelope = "Envelope";
    68     static final protected String Header = "Header";
    69     static final protected String Body = "Body";
    70     static final protected String xmlns = "xmlns";
    72     public SaajStaxWriter(final SOAPMessage msg) throws SOAPException {
    73         soap = msg;
    74         currentElement = soap.getSOAPPart().getEnvelope();
    75         envURI = currentElement.getNamespaceURI();
    76         this.deferredElement = new DeferredElement();
    77     }
    79     public SOAPMessage getSOAPMessage() {
    80         return soap;
    81     }
    83     @Override
    84     public void writeStartElement(final String localName) throws XMLStreamException {
    85         currentElement = deferredElement.flushTo(currentElement);
    86         deferredElement.setLocalName(localName);
    87     }
    89     @Override
    90     public void writeStartElement(final String ns, final String ln) throws XMLStreamException {
    91         writeStartElement(null, ln, ns);
    92     }
    94     @Override
    95     public void writeStartElement(final String prefix, final String ln, final String ns) throws XMLStreamException {
    96         currentElement = deferredElement.flushTo(currentElement);
    98         if (envURI.equals(ns)) {
    99             try {
   100                 if (Envelope.equals(ln)) {
   101                     currentElement = soap.getSOAPPart().getEnvelope();
   102                     fixPrefix(prefix);
   103                     return;
   104                 } else if (Header.equals(ln)) {
   105                     currentElement = soap.getSOAPHeader();
   106                     fixPrefix(prefix);
   107                     return;
   108                 } else if (Body.equals(ln)) {
   109                     currentElement = soap.getSOAPBody();
   110                     fixPrefix(prefix);
   111                     return;
   112                 }
   113             } catch (SOAPException e) {
   114                 throw new XMLStreamException(e);
   115             }
   117         }
   119         deferredElement.setLocalName(ln);
   120         deferredElement.setNamespaceUri(ns);
   121         deferredElement.setPrefix(prefix);
   123     }
   125     private void fixPrefix(final String prfx) throws XMLStreamException {
   126         String oldPrfx = currentElement.getPrefix();
   127         if (prfx != null && !prfx.equals(oldPrfx)) {
   128             currentElement.setPrefix(prfx);
   129         }
   130     }
   132     @Override
   133     public void writeEmptyElement(final String uri, final String ln) throws XMLStreamException {
   134         writeStartElement(null, ln, uri);
   135     }
   137     @Override
   138     public void writeEmptyElement(final String prefix, final String ln, final String uri) throws XMLStreamException {
   139         writeStartElement(prefix, ln, uri);
   140     }
   142     @Override
   143     public void writeEmptyElement(final String ln) throws XMLStreamException {
   144         writeStartElement(null, ln, null);
   145     }
   147     @Override
   148     public void writeEndElement() throws XMLStreamException {
   149         currentElement = deferredElement.flushTo(currentElement);
   150         if (currentElement != null) currentElement = currentElement.getParentElement();
   151     }
   153     @Override
   154     public void writeEndDocument() throws XMLStreamException {
   155         currentElement = deferredElement.flushTo(currentElement);
   156     }
   158     @Override
   159     public void close() throws XMLStreamException {
   160     }
   162     @Override
   163     public void flush() throws XMLStreamException {
   164     }
   166     @Override
   167     public void writeAttribute(final String ln, final String val) throws XMLStreamException {
   168         writeAttribute(null, null, ln, val);
   169     }
   171     @Override
   172     public void writeAttribute(final String prefix, final String ns, final String ln, final String value) throws XMLStreamException {
   173         if (ns == null && prefix == null && xmlns.equals(ln)) {
   174             writeNamespace("", value);
   175         } else {
   176             if (deferredElement.isInitialized()) {
   177                 deferredElement.addAttribute(prefix, ns, ln, value);
   178             } else {
   179                 addAttibuteToElement(currentElement, prefix, ns, ln, value);
   180             }
   181         }
   182     }
   184     @Override
   185     public void writeAttribute(final String ns, final String ln, final String val) throws XMLStreamException {
   186         writeAttribute(null, ns, ln, val);
   187     }
   189     @Override
   190     public void writeNamespace(String prefix, final String uri) throws XMLStreamException {
   191         // make prefix default if null or "xmlns" (according to javadoc)
   192         String thePrefix = prefix == null || "xmlns".equals(prefix) ? "" : prefix;
   193         if (deferredElement.isInitialized()) {
   194             deferredElement.addNamespaceDeclaration(thePrefix, uri);
   195         } else {
   196             try {
   197                 currentElement.addNamespaceDeclaration(thePrefix, uri);
   198             } catch (SOAPException e) {
   199                 throw new XMLStreamException(e);
   200             }
   201         }
   202     }
   204     @Override
   205     public void writeDefaultNamespace(final String uri) throws XMLStreamException {
   206         writeNamespace("", uri);
   207     }
   209     @Override
   210     public void writeComment(final String data) throws XMLStreamException {
   211         currentElement = deferredElement.flushTo(currentElement);
   212         Comment c = soap.getSOAPPart().createComment(data);
   213         currentElement.appendChild(c);
   214     }
   216     @Override
   217     public void writeProcessingInstruction(final String target) throws XMLStreamException {
   218         currentElement = deferredElement.flushTo(currentElement);
   219         Node n = soap.getSOAPPart().createProcessingInstruction(target, "");
   220         currentElement.appendChild(n);
   221     }
   223     @Override
   224     public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
   225         currentElement = deferredElement.flushTo(currentElement);
   226         Node n = soap.getSOAPPart().createProcessingInstruction(target, data);
   227         currentElement.appendChild(n);
   228     }
   230     @Override
   231     public void writeCData(final String data) throws XMLStreamException {
   232         currentElement = deferredElement.flushTo(currentElement);
   233         Node n = soap.getSOAPPart().createCDATASection(data);
   234         currentElement.appendChild(n);
   235     }
   237     @Override
   238     public void writeDTD(final String dtd) throws XMLStreamException {
   239         currentElement = deferredElement.flushTo(currentElement);
   240     }
   242     @Override
   243     public void writeEntityRef(final String name) throws XMLStreamException {
   244         currentElement = deferredElement.flushTo(currentElement);
   245         Node n = soap.getSOAPPart().createEntityReference(name);
   246         currentElement.appendChild(n);
   247     }
   249     @Override
   250     public void writeStartDocument() throws XMLStreamException {
   251     }
   253     @Override
   254     public void writeStartDocument(final String version) throws XMLStreamException {
   255         if (version != null) soap.getSOAPPart().setXmlVersion(version);
   256     }
   258     @Override
   259     public void writeStartDocument(final String encoding, final String version) throws XMLStreamException {
   260         if (version != null) soap.getSOAPPart().setXmlVersion(version);
   261         if (encoding != null) {
   262             try {
   263                 soap.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, encoding);
   264             } catch (SOAPException e) {
   265                 throw new XMLStreamException(e);
   266             }
   267         }
   268     }
   270     @Override
   271     public void writeCharacters(final String text) throws XMLStreamException {
   272         currentElement = deferredElement.flushTo(currentElement);
   273         try {
   274             currentElement.addTextNode(text);
   275         } catch (SOAPException e) {
   276             throw new XMLStreamException(e);
   277         }
   278     }
   280     @Override
   281     public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
   282         currentElement = deferredElement.flushTo(currentElement);
   283         char[] chr = (start == 0 && len == text.length) ? text : Arrays.copyOfRange(text, start, start + len);
   284         try {
   285             currentElement.addTextNode(new String(chr));
   286         } catch (SOAPException e) {
   287             throw new XMLStreamException(e);
   288         }
   289     }
   291     @Override
   292     public String getPrefix(final String uri) throws XMLStreamException {
   293         return currentElement.lookupPrefix(uri);
   294     }
   296     @Override
   297     public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
   298         // TODO: this in fact is not what would be expected from XMLStreamWriter
   299         //       (e.g. XMLStreamWriter for writing to output stream does not write anything as result of
   300         //        this method, it just rememebers that given prefix is associated with the given uri
   301         //        for the scope; to actually declare the prefix assignment in the resulting XML, one
   302         //        needs to call writeNamespace(...) method
   303         // Kept for backwards compatibility reasons - this might be worth of further investigation.
   304         if (deferredElement.isInitialized()) {
   305             deferredElement.addNamespaceDeclaration(prefix, uri);
   306         } else {
   307             throw new XMLStreamException("Namespace not associated with any element");
   308         }
   309     }
   311     @Override
   312     public void setDefaultNamespace(final String uri) throws XMLStreamException {
   313         setPrefix("", uri);
   314     }
   316     @Override
   317     public void setNamespaceContext(final NamespaceContext context)throws XMLStreamException {
   318         throw new UnsupportedOperationException();
   319     }
   321     @Override
   322     public Object getProperty(final String name) throws IllegalArgumentException {
   323         //TODO the following line is to make eclipselink happy ... they are aware of this problem -
   324         if (javax.xml.stream.XMLOutputFactory.IS_REPAIRING_NAMESPACES.equals(name)) return Boolean.FALSE;
   325         return null;
   326     }
   328     @Override
   329     public NamespaceContext getNamespaceContext() {
   330         return new NamespaceContext() {
   331             public String getNamespaceURI(final String prefix) {
   332                 return currentElement.getNamespaceURI(prefix);
   333             }
   334             public String getPrefix(final String namespaceURI) {
   335                 return currentElement.lookupPrefix(namespaceURI);
   336             }
   337             public Iterator getPrefixes(final String namespaceURI) {
   338                 return new Iterator<String>() {
   339                     String prefix = getPrefix(namespaceURI);
   340                     public boolean hasNext() {
   341                         return (prefix != null);
   342                     }
   343                     public String next() {
   344                         if (!hasNext()) throw new java.util.NoSuchElementException();
   345                         String next = prefix;
   346                         prefix = null;
   347                         return next;
   348                     }
   349                     public void remove() {}
   350                 };
   351             }
   352         };
   353     }
   355     static void addAttibuteToElement(SOAPElement element, String prefix, String ns, String ln, String value)
   356             throws XMLStreamException {
   357         try {
   358             if (ns == null) {
   359                 element.setAttributeNS("", ln, value);
   360             } else {
   361                 QName name = prefix == null ? new QName(ns, ln) : new QName(ns, ln, prefix);
   362                 element.addAttribute(name, value);
   363             }
   364         } catch (SOAPException e) {
   365             throw new XMLStreamException(e);
   366         }
   367     }
   369     /**
   370      * Holds details of element that needs to be deferred in order to manage namespace assignments correctly.
   371      *
   372      * <p>
   373      * An instance of can be set with all the aspects of the element name (local name, prefix, namespace uri).
   374      * Attributes and namespace declarations (special case of attribute) can be added.
   375      * Namespace declarations are handled so that the element namespace is updated if it is implied by the namespace
   376      * declaration and the namespace was not set to non-{@code null} value previously.
   377      * </p>
   378      *
   379      * <p>
   380      * The state of this object can be {@link #flushTo(SOAPElement) flushed} to SOAPElement - new SOAPElement will
   381      * be added a child element; the new element will have exactly the shape as represented by the state of this
   382      * object. Note that the {@link #flushTo(SOAPElement)} method does nothing
   383      * (and returns the argument immediately) if the state of this object is not initialized
   384      * (i.e. local name is null).
   385      * </p>
   386      *
   387      * @author ondrej.cerny@oracle.com
   388      */
   389     static class DeferredElement {
   390         private String prefix;
   391         private String localName;
   392         private String namespaceUri;
   393         private final List<NamespaceDeclaration> namespaceDeclarations;
   394         private final List<AttributeDeclaration> attributeDeclarations;
   396         DeferredElement() {
   397             this.namespaceDeclarations = new LinkedList<NamespaceDeclaration>();
   398             this.attributeDeclarations = new LinkedList<AttributeDeclaration>();
   399             reset();
   400         }
   403         /**
   404          * Set prefix of the element.
   405          * @param prefix namespace prefix
   406          */
   407         public void setPrefix(final String prefix) {
   408             this.prefix = prefix;
   409         }
   411         /**
   412          * Set local name of the element.
   413          *
   414          * <p>
   415          *     This method initializes the element.
   416          * </p>
   417          *
   418          * @param localName local name {@code not null}
   419          */
   420         public void setLocalName(final String localName) {
   421             if (localName == null) {
   422                 throw new IllegalArgumentException("localName can not be null");
   423             }
   424             this.localName = localName;
   425         }
   427         /**
   428          * Set namespace uri.
   429          *
   430          * @param namespaceUri namespace uri
   431          */
   432         public void setNamespaceUri(final String namespaceUri) {
   433             this.namespaceUri = namespaceUri;
   434         }
   436         /**
   437          * Adds namespace prefix assignment to the element.
   438          *
   439          * @param prefix prefix (not {@code null})
   440          * @param namespaceUri namespace uri
   441          */
   442         public void addNamespaceDeclaration(final String prefix, final String namespaceUri) {
   443             if (null == this.namespaceUri && null != namespaceUri && prefix.equals(emptyIfNull(this.prefix))) {
   444                 this.namespaceUri = namespaceUri;
   445             }
   446             this.namespaceDeclarations.add(new NamespaceDeclaration(prefix, namespaceUri));
   447         }
   449         /**
   450          * Adds attribute to the element.
   451          * @param prefix prefix
   452          * @param ns namespace
   453          * @param ln local name
   454          * @param value value
   455          */
   456         public void addAttribute(final String prefix, final String ns, final String ln, final String value) {
   457             if (ns == null && prefix == null && xmlns.equals(ln)) {
   458                 this.addNamespaceDeclaration(prefix, value);
   459             } else {
   460                 this.attributeDeclarations.add(new AttributeDeclaration(prefix, ns, ln, value));
   461             }
   462         }
   464         /**
   465          * Flushes state of this element to the {@code target} element.
   466          *
   467          * <p>
   468          * If this element is initialized then it is added with all the namespace declarations and attributes
   469          * to the {@code target} element as a child. The state of this element is reset to uninitialized.
   470          * The newly added element object is returned.
   471          * </p>
   472          * <p>
   473          * If this element is not initialized then the {@code target} is returned immediately, nothing else is done.
   474          * </p>
   475          *
   476          * @param target target element
   477          * @return {@code target} or new element
   478          * @throws XMLStreamException on error
   479          */
   480         public SOAPElement flushTo(final SOAPElement target) throws XMLStreamException {
   481             try {
   482                 if (this.localName != null) {
   483                     // add the element appropriately (based on namespace declaration)
   484                     final SOAPElement newElement;
   485                     if (this.namespaceUri == null) {
   486                         // add element with inherited scope
   487                         newElement = target.addChildElement(this.localName);
   488                     } else if (prefix == null) {
   489                         newElement = target.addChildElement(new QName(this.namespaceUri, this.localName));
   490                     } else {
   491                         newElement = target.addChildElement(this.localName, this.prefix, this.namespaceUri);
   492                     }
   493                     // add namespace declarations
   494                     for (NamespaceDeclaration namespace : this.namespaceDeclarations) {
   495                         newElement.addNamespaceDeclaration(namespace.prefix, namespace.namespaceUri);
   496                     }
   497                     // add attribute declarations
   498                     for (AttributeDeclaration attribute : this.attributeDeclarations) {
   499                         addAttibuteToElement(newElement,
   500                                 attribute.prefix, attribute.namespaceUri, attribute.localName, attribute.value);
   501                     }
   502                     // reset state
   503                     this.reset();
   505                     return newElement;
   506                 } else {
   507                     return target;
   508                 }
   509                 // else after reset state -> not initialized
   510             } catch (SOAPException e) {
   511                 throw new XMLStreamException(e);
   512             }
   513         }
   515         /**
   516          * Is the element initialized?
   517          * @return boolean indicating whether it was initialized after last flush
   518          */
   519         public boolean isInitialized() {
   520             return this.localName != null;
   521         }
   523         private void reset() {
   524             this.localName = null;
   525             this.prefix = null;
   526             this.namespaceUri = null;
   527             this.namespaceDeclarations.clear();
   528             this.attributeDeclarations.clear();
   529         }
   531         private static String emptyIfNull(String s) {
   532             return s == null ? "" : s;
   533         }
   534     }
   536     static class NamespaceDeclaration {
   537         final String prefix;
   538         final String namespaceUri;
   540         NamespaceDeclaration(String prefix, String namespaceUri) {
   541             this.prefix = prefix;
   542             this.namespaceUri = namespaceUri;
   543         }
   544     }
   546     static class AttributeDeclaration {
   547         final String prefix;
   548         final String namespaceUri;
   549         final String localName;
   550         final String value;
   552         AttributeDeclaration(String prefix, String namespaceUri, String localName, String value) {
   553             this.prefix = prefix;
   554             this.namespaceUri = namespaceUri;
   555             this.localName = localName;
   556             this.value = value;
   557         }
   558     }
   559 }

mercurial