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 
  26 package com.sun.xml.internal.messaging.saaj.soap.impl;
  27 
  28 import java.util.Iterator;
  29 import java.util.Locale;
  30 import java.util.logging.Level;
  31 
  32 import javax.xml.namespace.QName;
  33 import javax.xml.stream.XMLStreamException;
  34 import javax.xml.stream.XMLStreamReader;
  35 import javax.xml.parsers.DocumentBuilder;
  36 import javax.xml.parsers.DocumentBuilderFactory;
  37 
  38 import com.sun.xml.internal.messaging.saaj.util.SAAJUtil;
  39 
  40 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
  41 import com.sun.xml.internal.messaging.saaj.soap.SOAPDocument;
  42 import com.sun.xml.internal.messaging.saaj.soap.SOAPDocumentImpl;
  43 import com.sun.xml.internal.messaging.saaj.soap.StaxBridge;
  44 import com.sun.xml.internal.messaging.saaj.soap.name.NameImpl;
  45 import javax.xml.soap.Name;
  46 import javax.xml.soap.SOAPBody;
  47 import javax.xml.soap.SOAPBodyElement;
  48 import javax.xml.soap.SOAPElement;
  49 import javax.xml.soap.SOAPEnvelope;
  50 import javax.xml.soap.SOAPException;
  51 import javax.xml.soap.SOAPFault;
  52 import org.w3c.dom.Document;
  53 import org.w3c.dom.DocumentFragment;
  54 import org.w3c.dom.Element;
  55 import org.w3c.dom.Node;
  56 import org.w3c.dom.NodeList;
  57 
  58 /**
  59  * The implementation of SOAP-ENV:BODY or the SOAPBody abstraction.
  60  *
  61  * @author Anil Vijendran (anil@sun.com)
  62  */
  63 public abstract class BodyImpl extends ElementImpl implements SOAPBody {
  64     private SOAPFault fault;
  65 //  private XMLStreamReaderToXMLStreamWriter staxBridge;
  66     private StaxBridge staxBridge;
  67     private boolean payloadStreamRead = false;
  68 
  69     protected BodyImpl(SOAPDocumentImpl ownerDoc, NameImpl bodyName) {
  70         super(ownerDoc, bodyName);
  71     }
  72 
  73     public BodyImpl(SOAPDocumentImpl ownerDoc, Element domElement) {
  74         super(ownerDoc, domElement);
  75     }
  76 
  77     protected abstract NameImpl getFaultName(String name);
  78     protected abstract boolean isFault(SOAPElement child);
  79     protected abstract SOAPBodyElement createBodyElement(Name name);
  80     protected abstract SOAPBodyElement createBodyElement(QName name);
  81     protected abstract SOAPFault createFaultElement();
  82     protected abstract QName getDefaultFaultCode();
  83 
  84     @Override
  85     public SOAPFault addFault() throws SOAPException {
  86         if (hasFault()) {
  87             log.severe("SAAJ0110.impl.fault.already.exists");
  88             throw new SOAPExceptionImpl("Error: Fault already exists");
  89         }
  90 
  91         fault = createFaultElement();
  92 
  93         addNode(fault);
  94 
  95         fault.setFaultCode(getDefaultFaultCode());
  96         fault.setFaultString("Fault string, and possibly fault code, not set");
  97 
  98         return fault;
  99     }
 100 
 101     @Override
 102     public SOAPFault addFault(
 103         Name faultCode,
 104         String faultString,
 105         Locale locale)
 106         throws SOAPException {
 107 
 108         SOAPFault fault = addFault();
 109         fault.setFaultCode(faultCode);
 110         fault.setFaultString(faultString, locale);
 111         return fault;
 112     }
 113 
 114     @Override
 115    public SOAPFault addFault(
 116         QName faultCode,
 117         String faultString,
 118         Locale locale)
 119         throws SOAPException {
 120 
 121         SOAPFault fault = addFault();
 122         fault.setFaultCode(faultCode);
 123         fault.setFaultString(faultString, locale);
 124         return fault;
 125     }
 126 
 127     @Override
 128     public SOAPFault addFault(Name faultCode, String faultString)
 129         throws SOAPException {
 130 
 131         SOAPFault fault = addFault();
 132         fault.setFaultCode(faultCode);
 133         fault.setFaultString(faultString);
 134         return fault;
 135     }
 136 
 137     @Override
 138     public SOAPFault addFault(QName faultCode, String faultString)
 139         throws SOAPException {
 140 
 141         SOAPFault fault = addFault();
 142         fault.setFaultCode(faultCode);
 143         fault.setFaultString(faultString);
 144         return fault;
 145     }
 146 
 147     void initializeFault() {
 148         FaultImpl flt = (FaultImpl) findFault();
 149         fault = flt;
 150     }
 151 
 152     protected SOAPElement findFault() {
 153         Iterator<Node> eachChild = getChildElementNodes();
 154         while (eachChild.hasNext()) {
 155             SOAPElement child = (SOAPElement) eachChild.next();
 156             if (isFault(child)) {
 157                 return child;
 158             }
 159         }
 160 
 161         return null;
 162     }
 163 
 164     @Override
 165     public boolean hasFault() {
 166         QName payloadQName = getPayloadQName();
 167         return getFaultQName().equals(payloadQName);
 168     }
 169 
 170     private Object getFaultQName() {
 171         return new QName(getNamespaceURI(), "Fault");
 172     }
 173 
 174     @Override
 175     public SOAPFault getFault() {
 176         if (hasFault()) {
 177             if (fault == null) {
 178                 //initialize fault member
 179                 fault = (SOAPFault) getSoapDocument().find(getFirstChildElement());
 180             }
 181             return fault;
 182         }
 183         return null;
 184     }
 185 
 186     @Override
 187     public SOAPBodyElement addBodyElement(Name name) throws SOAPException {
 188         SOAPBodyElement newBodyElement =
 189             (SOAPBodyElement) ElementFactory.createNamedElement(
 190                 ((SOAPDocument) getOwnerDocument()).getDocument(),
 191                 name.getLocalName(),
 192                 name.getPrefix(),
 193                 name.getURI());
 194         if (newBodyElement == null) {
 195             newBodyElement = createBodyElement(name);
 196         }
 197         addNode(newBodyElement);
 198         return newBodyElement;
 199     }
 200 
 201     @Override
 202     public SOAPBodyElement addBodyElement(QName qname) throws SOAPException {
 203         SOAPBodyElement newBodyElement =
 204             (SOAPBodyElement) ElementFactory.createNamedElement(
 205                 ((SOAPDocument) getOwnerDocument()).getDocument(),
 206                 qname.getLocalPart(),
 207                 qname.getPrefix(),
 208                 qname.getNamespaceURI());
 209         if (newBodyElement == null) {
 210             newBodyElement = createBodyElement(qname);
 211         }
 212         addNode(newBodyElement);
 213         return newBodyElement;
 214     }
 215 
 216     @Override
 217     public void setParentElement(SOAPElement element) throws SOAPException {
 218 
 219         if (!(element instanceof SOAPEnvelope)) {
 220             log.severe("SAAJ0111.impl.body.parent.must.be.envelope");
 221             throw new SOAPException("Parent of SOAPBody has to be a SOAPEnvelope");
 222         }
 223         super.setParentElement(element);
 224     }
 225 
 226     @Override
 227     protected SOAPElement addElement(Name name) throws SOAPException {
 228         return addBodyElement(name);
 229     }
 230 
 231     @Override
 232     protected SOAPElement addElement(QName name) throws SOAPException {
 233         return addBodyElement(name);
 234     }
 235 
 236     //    public Node insertBefore(Node newElement, Node ref) throws DOMException {
 237     //        if (!(newElement instanceof SOAPBodyElement) && (newElement instanceof SOAPElement)) {
 238     //            newElement = new ElementWrapper((ElementImpl) newElement);
 239     //        }
 240     //        return super.insertBefore(newElement, ref);
 241     //    }
 242     //
 243     //    public Node replaceChild(Node newElement, Node ref) throws DOMException {
 244     //        if (!(newElement instanceof SOAPBodyElement) && (newElement instanceof SOAPElement)) {
 245     //            newElement = new ElementWrapper((ElementImpl) newElement);
 246     //        }
 247     //        return super.replaceChild(newElement, ref);
 248     //    }
 249 
 250     @Override
 251     public SOAPBodyElement addDocument(Document document)
 252         throws SOAPException {
 253         /*
 254 
 255                 Element rootNode =
 256                     document.getDocumentElement();
 257                 // Causes all deferred nodes to be inflated
 258                 rootNode.normalize();
 259                 adoptElement(rootNode);
 260                 SOAPBodyElement bodyElement = (SOAPBodyElement) convertToSoapElement(rootNode);
 261                 addNode(bodyElement);
 262                 return bodyElement;
 263         */
 264         ///*
 265         SOAPBodyElement newBodyElement = null;
 266         DocumentFragment docFrag = document.createDocumentFragment();
 267         Element rootElement = document.getDocumentElement();
 268         if(rootElement != null) {
 269             docFrag.appendChild(rootElement);
 270 
 271             Document ownerDoc = getOwnerDocument();
 272             // This copies the whole tree which could be very big so it's slow.
 273             // However, it does have the advantage of actually working.
 274             org.w3c.dom.Node replacingNode = ownerDoc.importNode(docFrag, true);
 275             // Adding replacingNode at the last of the children list of body
 276             addNode(replacingNode);
 277             Iterator<javax.xml.soap.Node> i =
 278                 getChildElements(NameImpl.copyElementName(rootElement));
 279             // Return the child element with the required name which is at the
 280             // end of the list
 281             while(i.hasNext())
 282                 newBodyElement = (SOAPBodyElement) i.next();
 283         }
 284         return newBodyElement;
 285         //*/
 286     }
 287 
 288     @Override
 289     protected SOAPElement convertToSoapElement(Element element) {
 290         final Node soapNode = getSoapDocument().findIfPresent(element);
 291         if ((soapNode instanceof SOAPBodyElement) &&
 292             //this check is required because ElementImpl currently
 293             // implements SOAPBodyElement
 294             !(soapNode.getClass().equals(ElementImpl.class))) {
 295             return (SOAPElement) soapNode;
 296         } else {
 297             return replaceElementWithSOAPElement(
 298                 element,
 299                 (ElementImpl) createBodyElement(NameImpl
 300                     .copyElementName(element)));
 301         }
 302     }
 303 
 304     @Override
 305     public SOAPElement setElementQName(QName newName) throws SOAPException {
 306         log.log(Level.SEVERE,
 307                 "SAAJ0146.impl.invalid.name.change.requested",
 308                 new Object[] {elementQName.getLocalPart(),
 309                               newName.getLocalPart()});
 310         throw new SOAPException("Cannot change name for "
 311                                 + elementQName.getLocalPart() + " to "
 312                                 + newName.getLocalPart());
 313     }
 314 
 315     @Override
 316     public Document extractContentAsDocument() throws SOAPException {
 317 
 318         Iterator<javax.xml.soap.Node> eachChild = getChildElements();
 319         javax.xml.soap.Node firstBodyElement = null;
 320 
 321         while (eachChild.hasNext() &&
 322                !(firstBodyElement instanceof SOAPElement))
 323             firstBodyElement = (javax.xml.soap.Node) eachChild.next();
 324 
 325         boolean exactlyOneChildElement = true;
 326         if (firstBodyElement == null)
 327             exactlyOneChildElement = false;
 328         else {
 329             for (org.w3c.dom.Node node = firstBodyElement.getNextSibling();
 330                  node != null;
 331                  node = node.getNextSibling()) {
 332 
 333                 if (node instanceof Element) {
 334                     exactlyOneChildElement = false;
 335                     break;
 336                 }
 337             }
 338         }
 339 
 340         if(!exactlyOneChildElement) {
 341             log.log(Level.SEVERE,
 342                     "SAAJ0250.impl.body.should.have.exactly.one.child");
 343             throw new SOAPException("Cannot extract Document from body");
 344         }
 345 
 346         Document document = null;
 347         try {
 348             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance("com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", SAAJUtil.getSystemClassLoader());
 349             factory.setNamespaceAware(true);
 350             DocumentBuilder builder = factory.newDocumentBuilder();
 351             document = builder.newDocument();
 352 
 353             Element rootElement = (Element) document.importNode(
 354                                                 firstBodyElement,
 355                                                 true);
 356 
 357             document.appendChild(rootElement);
 358 
 359         } catch(Exception e) {
 360             log.log(Level.SEVERE,
 361                     "SAAJ0251.impl.cannot.extract.document.from.body");
 362             throw new SOAPExceptionImpl(
 363                 "Unable to extract Document from body", e);
 364         }
 365 
 366         firstBodyElement.detachNode();
 367 
 368         return document;
 369     }
 370 
 371     private void materializePayloadWrapException() {
 372         try {
 373             materializePayload();
 374         } catch (SOAPException e) {
 375             throw new RuntimeException(e);
 376         }
 377     }
 378     private void materializePayload() throws SOAPException {
 379         if (staxBridge != null) {
 380             if (payloadStreamRead) {
 381                 //the payload has already been read via stream reader and the
 382                 //stream has been exhausted already. Throw an
 383                 //exception since we are now trying to materialize as DOM and
 384                 //there is no stream left to read
 385                 throw new SOAPException("SOAPBody payload stream has been fully read - cannot materialize as DOM!");
 386             }
 387             try {
 388                 staxBridge.bridgePayload();
 389                 staxBridge = null;
 390                 payloadStreamRead = true;
 391             } catch (XMLStreamException e) {
 392                 throw new SOAPException(e);
 393             }
 394         }
 395     }
 396 
 397     @Override
 398     public boolean hasChildNodes() {
 399         boolean hasChildren = super.hasChildNodes();
 400         //to answer this question we need to know _whether_ we have at least one child
 401         //So no need to materialize body if we already know we have a header child
 402         if (!hasChildren) {
 403             materializePayloadWrapException();
 404         }
 405         return super.hasChildNodes();
 406     }
 407 
 408     @Override
 409     public NodeList getChildNodes() {
 410         materializePayloadWrapException();
 411         return super.getChildNodes();
 412     }
 413 
 414     @Override
 415     public Node getFirstChild() {
 416         Node child = super.getFirstChild();
 417         if (child == null) {
 418             materializePayloadWrapException();
 419         }
 420         return super.getFirstChild();
 421     }
 422 
 423     public Node getFirstChildNoMaterialize() {
 424         return super.getFirstChild();
 425     }
 426 
 427     @Override
 428     public Node getLastChild() {
 429         materializePayloadWrapException();
 430         return super.getLastChild();
 431     }
 432 
 433     XMLStreamReader getPayloadReader() {
 434         return staxBridge.getPayloadReader();
 435     }
 436 
 437     void setStaxBridge(StaxBridge bridge) {
 438         this.staxBridge = bridge;
 439     }
 440 
 441     StaxBridge getStaxBridge() {
 442         return staxBridge;
 443     }
 444 
 445     void setPayloadStreamRead() {
 446         this.payloadStreamRead = true;
 447     }
 448 
 449     QName getPayloadQName() {
 450         if (staxBridge != null) {
 451                 return staxBridge.getPayloadQName();
 452         } else {
 453             //not lazy - Just get first child element and return its name
 454             Element elem = getFirstChildElement();
 455             if (elem != null) {
 456                 String ns = elem.getNamespaceURI();
 457                 String pref = elem.getPrefix();
 458                 String local = elem.getLocalName();
 459                 if (pref != null) return new QName(ns, local, pref);
 460                 if (ns != null) return new QName(ns, local);
 461                 return new QName(local);
 462             }
 463         }
 464         return null;
 465     }
 466 
 467     String getPayloadAttributeValue(String attName) {
 468         if (staxBridge != null) {
 469             return staxBridge.getPayloadAttributeValue(attName);
 470         } else {
 471             //not lazy -Just get first child element and return its attribute
 472             Element elem = getFirstChildElement();
 473             if (elem != null) {
 474                 return elem.getAttribute(getLocalName());
 475             }
 476         }
 477         return null;
 478     }
 479 
 480     String getPayloadAttributeValue(QName attNAme) {
 481         if (staxBridge != null) {
 482             return staxBridge.getPayloadAttributeValue(attNAme);
 483         } else {
 484             //not lazy -Just get first child element and return its attribute
 485             Element elem = getFirstChildElement();
 486             if (elem != null) {
 487                 return elem.getAttributeNS(attNAme.getNamespaceURI(), attNAme.getLocalPart());
 488             }
 489         }
 490         return null;
 491     }
 492 
 493     public boolean isLazy() {
 494         return (staxBridge != null && !payloadStreamRead);
 495     }
 496 
 497 }