1 /*
   2  * Copyright (c) 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8159058 8186441
  27  * @summary Test that empty default namespace declaration clears the
  28  *          default namespace value
  29  * @modules java.xml.ws/com.sun.xml.internal.ws.api
  30  *          java.xml.ws/com.sun.xml.internal.ws.api.message.saaj
  31  *          java.xml.ws/com.sun.xml.internal.ws.message.stream
  32  * @run testng/othervm SaajEmptyNamespaceTest
  33  */
  34 
  35 import com.sun.xml.internal.ws.api.SOAPVersion;
  36 import com.sun.xml.internal.ws.api.message.saaj.SAAJFactory;
  37 import com.sun.xml.internal.ws.message.stream.StreamMessage;
  38 import java.io.ByteArrayInputStream;
  39 import java.io.StringReader;
  40 import java.io.StringWriter;
  41 import java.io.UnsupportedEncodingException;
  42 import javax.xml.namespace.QName;
  43 import javax.xml.soap.MessageFactory;
  44 import javax.xml.soap.SOAPBody;
  45 import javax.xml.soap.SOAPElement;
  46 import javax.xml.soap.SOAPException;
  47 import javax.xml.soap.SOAPMessage;
  48 import javax.xml.stream.XMLInputFactory;
  49 import javax.xml.stream.XMLStreamReader;
  50 import javax.xml.transform.OutputKeys;
  51 import javax.xml.transform.Transformer;
  52 import javax.xml.transform.TransformerException;
  53 import javax.xml.transform.TransformerFactory;
  54 import javax.xml.transform.dom.DOMSource;
  55 import javax.xml.transform.stream.StreamResult;
  56 import javax.xml.transform.stream.StreamSource;
  57 import org.testng.Assert;
  58 import org.testng.annotations.Test;
  59 import org.w3c.dom.Node;
  60 
  61 public class SaajEmptyNamespaceTest {
  62 
  63     /*
  64      * Test that SOAP reader doesn't move namespaces declarations to SOAP body element
  65      *  as reported in JDK-8186441
  66      */
  67     @Test
  68     public void testPreserveNamespacesPosition() throws Exception {
  69         // Create SOAP message from XML string and process it with SAAJ reader
  70         XMLStreamReader envelope = XMLInputFactory.newFactory().createXMLStreamReader(
  71                 new StringReader(INPUT_SOAP_MESSAGE_2));
  72         StreamMessage streamMessage = new StreamMessage(SOAPVersion.SOAP_11,
  73                 envelope, null);
  74         SAAJFactory saajFact = new SAAJFactory();
  75         SOAPMessage soapMessage = saajFact.readAsSOAPMessage(SOAPVersion.SOAP_11, streamMessage);
  76 
  77         //Get SOAP body and convert it to string representation
  78         SOAPBody body = soapMessage.getSOAPBody();
  79         String bodyAsString = nodeToText(body);
  80         Assert.assertEquals(bodyAsString, PRESERVE_NAMESPACES_EXPECTED_RESULT);
  81     }
  82 
  83     /*
  84      * Test that SOAP message with default namespace declaration that contains empty
  85      * string is properly processed by SAAJ reader.
  86      */
  87     @Test
  88     public void testResetDefaultNamespaceSAAJ() throws Exception {
  89         // Create SOAP message from XML string and process it with SAAJ reader
  90         XMLStreamReader envelope = XMLInputFactory.newFactory().createXMLStreamReader(
  91                 new StringReader(INPUT_SOAP_MESSAGE));
  92         StreamMessage streamMessage = new StreamMessage(SOAPVersion.SOAP_11,
  93                 envelope, null);
  94         SAAJFactory saajFact = new SAAJFactory();
  95         SOAPMessage soapMessage = saajFact.readAsSOAPMessage(SOAPVersion.SOAP_11, streamMessage);
  96 
  97         // Check if constructed object model meets local names and namespace expectations
  98         SOAPElement request = (SOAPElement) soapMessage.getSOAPBody().getFirstChild();
  99         // Check top body element name
 100         Assert.assertEquals(request.getLocalName(), "SampleServiceRequest");
 101         // Check top body element namespace
 102         Assert.assertEquals(request.getNamespaceURI(), TEST_NS);
 103         SOAPElement params = (SOAPElement) request.getFirstChild();
 104         // Check first child name
 105         Assert.assertEquals(params.getLocalName(), "RequestParams");
 106         // Check if first child namespace is null
 107         Assert.assertNull(params.getNamespaceURI());
 108 
 109         // Check inner elements of the first child
 110         SOAPElement param1 = (SOAPElement) params.getFirstChild();
 111         Assert.assertEquals(param1.getLocalName(), "Param1");
 112         Assert.assertNull(param1.getNamespaceURI());
 113         SOAPElement param2 = (SOAPElement) params.getChildNodes().item(1);
 114         Assert.assertEquals(param2.getLocalName(), "Param2");
 115         Assert.assertNull(param2.getNamespaceURI());
 116         // Check full content of SOAP body
 117         Assert.assertEquals(nodeToText(request), EXPECTED_RESULT);
 118     }
 119 
 120     /*
 121      * Test that adding element with explicitly null namespace URI shall put the
 122      * element into global namespace. Namespace declarations are not added explicitly.
 123      */
 124     @Test
 125     public void testAddElementToNullNsNoDeclarations() throws Exception {
 126         // Create empty SOAP message
 127         SOAPMessage msg = createSoapMessage();
 128         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 129 
 130         // Add elements
 131         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 132         SOAPElement childGlobalNS = parentExplicitNS.addChildElement("global-child", "", null);
 133         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 134 
 135         // Check namespace URIs
 136         Assert.assertNull(childGlobalNS.getNamespaceURI());
 137         Assert.assertEquals(childDefaultNS.getNamespaceURI(), TEST_NS);
 138     }
 139 
 140     /*
 141      * Test that adding element with explicitly empty namespace URI shall put
 142      * the element into global namespace. Namespace declarations are not added
 143      * explicitly.
 144      */
 145     @Test
 146     public void testAddElementToGlobalNsNoDeclarations() throws Exception {
 147         // Create empty SOAP message
 148         SOAPMessage msg = createSoapMessage();
 149         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 150 
 151         // Add elements
 152         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 153         SOAPElement childGlobalNS = parentExplicitNS.addChildElement("global-child", "", "");
 154         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 155 
 156         // Check namespace URIs
 157         Assert.assertNull(childGlobalNS.getNamespaceURI());
 158         Assert.assertEquals(childDefaultNS.getNamespaceURI(), TEST_NS);
 159     }
 160 
 161     /*
 162      * Test that adding element with explicitly empty namespace URI set via QName
 163      * shall put the element into global namespace.
 164      */
 165     @Test
 166     public void testAddElementToNullNsQName() throws Exception {
 167         // Create empty SOAP message
 168         SOAPMessage msg = createSoapMessage();
 169         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 170 
 171         // Add elements
 172         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 173         parentExplicitNS.addNamespaceDeclaration("", TEST_NS);
 174         SOAPElement childGlobalNS = parentExplicitNS.addChildElement(new QName(null, "global-child"));
 175         childGlobalNS.addNamespaceDeclaration("", "");
 176         SOAPElement grandChildGlobalNS = childGlobalNS.addChildElement("global-grand-child");
 177         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 178 
 179         // Check namespace URIs
 180         Assert.assertNull(childGlobalNS.getNamespaceURI());
 181         Assert.assertNull(grandChildGlobalNS.getNamespaceURI());
 182         Assert.assertEquals(childDefaultNS.getNamespaceURI(), TEST_NS);
 183     }
 184 
 185     /*
 186      * Test that adding element with explicitly empty namespace URI shall put
 187      * the element into global namespace.
 188      */
 189     @Test
 190     public void testAddElementToGlobalNs() throws Exception {
 191         // Create empty SOAP message
 192         SOAPMessage msg = createSoapMessage();
 193         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 194 
 195         // Add elements
 196         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 197         parentExplicitNS.addNamespaceDeclaration("", TEST_NS);
 198         SOAPElement childGlobalNS = parentExplicitNS.addChildElement("global-child", "", "");
 199         childGlobalNS.addNamespaceDeclaration("", "");
 200         SOAPElement grandChildGlobalNS = childGlobalNS.addChildElement("global-grand-child");
 201         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 202 
 203         // Check namespace URIs
 204         Assert.assertNull(childGlobalNS.getNamespaceURI());
 205         Assert.assertNull(grandChildGlobalNS.getNamespaceURI());
 206         Assert.assertEquals(childDefaultNS.getNamespaceURI(), TEST_NS);
 207     }
 208 
 209     /*
 210      * Test that adding element with explicitly null namespace URI shall put
 211      * the element into global namespace.
 212      */
 213     @Test
 214     public void testAddElementToNullNs() throws Exception {
 215         // Create empty SOAP message
 216         SOAPMessage msg = createSoapMessage();
 217         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 218 
 219         // Add elements
 220         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 221         parentExplicitNS.addNamespaceDeclaration("", TEST_NS);
 222         SOAPElement childGlobalNS = parentExplicitNS.addChildElement("global-child", "", null);
 223         childGlobalNS.addNamespaceDeclaration("", null);
 224         SOAPElement grandChildGlobalNS = childGlobalNS.addChildElement("global-grand-child");
 225         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 226 
 227         // Check namespace URIs
 228         Assert.assertNull(childGlobalNS.getNamespaceURI());
 229         Assert.assertNull(grandChildGlobalNS.getNamespaceURI());
 230         Assert.assertEquals(TEST_NS, childDefaultNS.getNamespaceURI());
 231     }
 232 
 233     /*
 234      * Test that adding element with explicitly empty namespace URI via QName
 235      * shall put the element in global namespace.
 236      */
 237     @Test
 238     public void testAddElementToGlobalNsQName() throws Exception {
 239         // Create empty SOAP message
 240         SOAPMessage msg = createSoapMessage();
 241         SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
 242 
 243         // Add elements
 244         SOAPElement parentExplicitNS = body.addChildElement("content", "", TEST_NS);
 245         parentExplicitNS.addNamespaceDeclaration("", TEST_NS);
 246         SOAPElement childGlobalNS = parentExplicitNS.addChildElement(new QName("", "global-child"));
 247         childGlobalNS.addNamespaceDeclaration("", "");
 248         SOAPElement grandChildGlobalNS = childGlobalNS.addChildElement("global-grand-child");
 249         SOAPElement childDefaultNS = parentExplicitNS.addChildElement("default-child");
 250 
 251         // Check namespace URIs
 252         Assert.assertNull(childGlobalNS.getNamespaceURI());
 253         Assert.assertNull(grandChildGlobalNS.getNamespaceURI());
 254         Assert.assertEquals(childDefaultNS.getNamespaceURI(),TEST_NS);
 255     }
 256 
 257     // Convert DOM node to text representation
 258     private String nodeToText(Node node) throws TransformerException {
 259         Transformer trans = TransformerFactory.newInstance().newTransformer();
 260         trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
 261         StringWriter writer = new StringWriter();
 262         StreamResult result = new StreamResult(writer);
 263         trans.transform(new DOMSource(node), result);
 264         String bodyContent = writer.toString();
 265         System.out.println("SOAP body content read by SAAJ:"+bodyContent);
 266         return bodyContent;
 267     }
 268 
 269     // Create SOAP message with empty body
 270     private static SOAPMessage createSoapMessage() throws SOAPException, UnsupportedEncodingException {
 271         String xml = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 272                     +"<SOAP-ENV:Body/></SOAP-ENV:Envelope>";
 273         MessageFactory mFactory = MessageFactory.newInstance();
 274         SOAPMessage msg = mFactory.createMessage();
 275         msg.getSOAPPart().setContent(new StreamSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));
 276         return msg;
 277     }
 278 
 279     // Namespace value used in tests
 280     private static String TEST_NS = "http://example.org/test";
 281 
 282     // Content of SOAP message passed to SAAJ factory
 283     private static String INPUT_SOAP_MESSAGE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
 284             + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 285             + "<s:Body>"
 286             + "<SampleServiceRequest xmlns=\"http://example.org/test\""
 287             + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
 288             + "<RequestParams xmlns=\"\">"
 289             + "<Param1>hogehoge</Param1>"
 290             + "<Param2>fugafuga</Param2>"
 291             + "</RequestParams>"
 292             + "</SampleServiceRequest>"
 293             + "</s:Body>"
 294             + "</s:Envelope>";
 295 
 296     // Expected body content after SAAJ processing
 297     private static String EXPECTED_RESULT = "<SampleServiceRequest"
 298             + " xmlns=\"http://example.org/test\""
 299             + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
 300             + "<RequestParams xmlns=\"\">"
 301             + "<Param1>hogehoge</Param1>"
 302             + "<Param2>fugafuga</Param2>"
 303             + "</RequestParams>"
 304             + "</SampleServiceRequest>";
 305 
 306     private static String PRESERVE_NAMESPACES_EXPECTED_RESULT =
 307             "<s:Body xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 308             +"<Request xmlns=\"http://example.org/NS_1\">"
 309             +"<Item><Contact xmlns=\"http://example.org/NS_2\">Test_Contact</Contact>"
 310             +"</Item></Request></s:Body>";
 311 
 312     private static String INPUT_SOAP_MESSAGE_2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
 313             + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 314             + "<s:Body>"
 315             + "<Request xmlns=\"http://example.org/NS_1\">"
 316             + "<Item>"
 317             + "<Contact xmlns=\"http://example.org/NS_2\">Test_Contact</Contact>"
 318             + "</Item>"
 319             + "</Request>"
 320             + "</s:Body>"
 321             + "</s:Envelope>";
 322 }