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