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