1 /*
   2  * Copyright (c) 1997, 2012, 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.ws.api.addressing;
  27 
  28 import com.sun.istack.internal.FinalArrayList;
  29 import com.sun.istack.internal.NotNull;
  30 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
  31 import com.sun.xml.internal.stream.buffer.XMLStreamBufferException;
  32 import com.sun.xml.internal.ws.api.message.Header;
  33 import com.sun.xml.internal.ws.message.AbstractHeaderImpl;
  34 import com.sun.xml.internal.ws.util.xml.XMLStreamWriterFilter;
  35 import org.w3c.dom.Element;
  36 import org.xml.sax.Attributes;
  37 import org.xml.sax.ContentHandler;
  38 import org.xml.sax.ErrorHandler;
  39 import org.xml.sax.SAXException;
  40 import org.xml.sax.helpers.AttributesImpl;
  41 import org.xml.sax.helpers.XMLFilterImpl;
  42 
  43 import javax.xml.namespace.QName;
  44 import javax.xml.soap.SOAPException;
  45 import javax.xml.soap.SOAPMessage;
  46 import javax.xml.soap.SOAPHeader;
  47 import javax.xml.stream.XMLStreamException;
  48 import javax.xml.stream.XMLStreamReader;
  49 import javax.xml.stream.XMLStreamWriter;
  50 import javax.xml.stream.util.StreamReaderDelegate;
  51 import javax.xml.ws.WebServiceException;
  52 
  53 /**
  54  * Used to represent outbound header created from {@link WSEndpointReference}'s
  55  * referenec parameters.
  56  *
  57  * <p>
  58  * This is optimized for outbound use, so it implements some of the methods lazily,
  59  * in a slow way.
  60  *
  61  * <p>
  62  * This header adds "wsa:IsReferenceParameter" and thus only used for the W3C version.
  63  *
  64  * @author Kohsuke Kawaguchi
  65  */
  66 final class OutboundReferenceParameterHeader extends AbstractHeaderImpl {
  67     private final XMLStreamBuffer infoset;
  68     private final String nsUri,localName;
  69 
  70     /**
  71      * The attributes on the header element.
  72      * Lazily parsed.
  73      * Null if not parsed yet.
  74      */
  75     private FinalArrayList<Attribute> attributes;
  76 
  77     OutboundReferenceParameterHeader(XMLStreamBuffer infoset, String nsUri, String localName) {
  78         this.infoset = infoset;
  79         this.nsUri = nsUri;
  80         this.localName = localName;
  81     }
  82 
  83     @Override
  84     public @NotNull String getNamespaceURI() {
  85         return nsUri;
  86     }
  87 
  88     @Override
  89     public @NotNull String getLocalPart() {
  90         return localName;
  91     }
  92 
  93     @Override
  94     public String getAttribute(String nsUri, String localName) {
  95         if(attributes==null) {
  96             parseAttributes();
  97         }
  98         for(int i=attributes.size()-1; i>=0; i-- ) {
  99             Attribute a = attributes.get(i);
 100             if (a.localName.equals(localName) && a.nsUri.equals(nsUri)) {
 101                 return a.value;
 102             }
 103         }
 104         return null;
 105     }
 106 
 107     /**
 108      * We don't really expect this to be used, but just to satisfy
 109      * the {@link Header} contract.
 110      *
 111      * So this is rather slow.
 112      */
 113     private void parseAttributes() {
 114         try {
 115             XMLStreamReader reader = readHeader();
 116             reader.nextTag();   // move to the first element, which is the header element
 117 
 118             attributes = new FinalArrayList<Attribute>();
 119             boolean refParamAttrWritten = false;
 120             for (int i = 0; i < reader.getAttributeCount(); i++) {
 121                 final String attrLocalName = reader.getAttributeLocalName(i);
 122                 final String namespaceURI = reader.getAttributeNamespace(i);
 123                 final String value = reader.getAttributeValue(i);
 124                 if (namespaceURI.equals(AddressingVersion.W3C.nsUri)&& attrLocalName.equals("IS_REFERENCE_PARAMETER")) {
 125                     refParamAttrWritten = true;
 126                 }
 127                 attributes.add(new Attribute(namespaceURI,attrLocalName,value));
 128             }
 129             // we are adding one more attribute "wsa:IsReferenceParameter", if its not alrady there
 130             if (!refParamAttrWritten) {
 131                 attributes.add(new Attribute(AddressingVersion.W3C.nsUri,IS_REFERENCE_PARAMETER,TRUE_VALUE));
 132             }
 133         } catch (XMLStreamException e) {
 134             throw new WebServiceException("Unable to read the attributes for {"+nsUri+"}"+localName+" header",e);
 135         }
 136     }
 137 
 138     @Override
 139     public XMLStreamReader readHeader() throws XMLStreamException {
 140         return new StreamReaderDelegate(infoset.readAsXMLStreamReader()) {
 141             int state=0; /* 0:expecting root, 1:in root, 2:past root */
 142             @Override
 143             public int next() throws XMLStreamException {
 144                 return check(super.next());
 145             }
 146 
 147             @Override
 148             public int nextTag() throws XMLStreamException {
 149                 return check(super.nextTag());
 150             }
 151 
 152             private int check(int type) {
 153                 switch (state) {
 154                     case 0:
 155                         if (type == START_ELEMENT) {
 156                             state = 1;
 157                         }
 158                         break;
 159                     case 1:
 160                         state = 2;
 161                         break;
 162                     default:
 163                         break;
 164                 }
 165 
 166                 return type;
 167             }
 168 
 169             @Override
 170             public int getAttributeCount() {
 171                 if (state == 1) {
 172                     return super.getAttributeCount()+1;
 173                 } else {
 174                     return super.getAttributeCount();
 175                 }
 176             }
 177 
 178             @Override
 179             public String getAttributeLocalName(int index) {
 180                 if (state == 1 && index == super.getAttributeCount()) {
 181                     return IS_REFERENCE_PARAMETER;
 182                 } else {
 183                     return super.getAttributeLocalName(index);
 184                 }
 185             }
 186 
 187             @Override
 188             public String getAttributeNamespace(int index) {
 189                 if (state == 1 && index==super.getAttributeCount()) {
 190                     return AddressingVersion.W3C.nsUri;
 191                 }
 192                 else {
 193                     return super.getAttributeNamespace(index);
 194                 }
 195             }
 196 
 197             @Override
 198             public String getAttributePrefix(int index) {
 199                 if(state==1 && index==super.getAttributeCount()) {
 200                     return "wsa";
 201                 } else {
 202                     return super.getAttributePrefix(index);
 203                 }
 204             }
 205 
 206             @Override
 207             public String getAttributeType(int index) {
 208                 if(state==1 && index==super.getAttributeCount()) {
 209                     return "CDATA";
 210                 } else {
 211                     return super.getAttributeType(index);
 212                 }
 213             }
 214 
 215             @Override
 216             public String getAttributeValue(int index) {
 217                 if(state==1 && index==super.getAttributeCount()) {
 218                     return TRUE_VALUE;
 219                 } else {
 220                     return super.getAttributeValue(index);
 221                 }
 222             }
 223 
 224             @Override
 225             public QName getAttributeName(int index) {
 226                 if(state==1 && index==super.getAttributeCount()) {
 227                     return new QName(AddressingVersion.W3C.nsUri, IS_REFERENCE_PARAMETER, "wsa");
 228                 } else {
 229                     return super.getAttributeName(index);
 230                 }
 231             }
 232 
 233             @Override
 234             public String getAttributeValue(String namespaceUri, String localName) {
 235                 if(state==1 && localName.equals(IS_REFERENCE_PARAMETER) && namespaceUri.equals(AddressingVersion.W3C.nsUri)) {
 236                     return TRUE_VALUE;
 237                 } else {
 238                     return super.getAttributeValue(namespaceUri, localName);
 239                 }
 240             }
 241         };
 242     }
 243 
 244     @Override
 245     public void writeTo(XMLStreamWriter w) throws XMLStreamException {
 246         infoset.writeToXMLStreamWriter(new XMLStreamWriterFilter(w) {
 247             private boolean root=true;
 248             private boolean onRootEl = true;
 249             @Override
 250             public void writeStartElement(String localName) throws XMLStreamException {
 251                 super.writeStartElement(localName);
 252                 writeAddedAttribute();
 253             }
 254 
 255             private void writeAddedAttribute() throws XMLStreamException {
 256                 if(!root) {
 257                     onRootEl = false;
 258                     return;
 259                 }
 260                 root=false;
 261                 writeNamespace("wsa",AddressingVersion.W3C.nsUri);
 262                 super.writeAttribute("wsa",AddressingVersion.W3C.nsUri,IS_REFERENCE_PARAMETER,TRUE_VALUE);
 263             }
 264 
 265             @Override
 266             public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
 267                 super.writeStartElement(namespaceURI, localName);
 268                 writeAddedAttribute();
 269             }
 270 
 271             @Override
 272             public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
 273                 //TODO: Verify with KK later
 274                 //check if prefix is declared before writing start element.
 275                 boolean prefixDeclared = isPrefixDeclared(prefix,namespaceURI);
 276                 super.writeStartElement(prefix, localName, namespaceURI);
 277                 if (!prefixDeclared && !prefix.equals("")) {
 278                     super.writeNamespace(prefix,namespaceURI);
 279                 }
 280                 writeAddedAttribute();
 281             }
 282             @Override
 283             public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException{
 284                 if (!isPrefixDeclared(prefix, namespaceURI)) {
 285                     super.writeNamespace(prefix,namespaceURI);
 286                 }
 287             }
 288 
 289             @Override
 290             public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException {
 291                 //skip if its on root element and attribute is wsa;IsReferenceParameter, as we write it.
 292                 if(onRootEl && namespaceURI.equals(AddressingVersion.W3C.nsUri)
 293                             && localName.equals(IS_REFERENCE_PARAMETER)) {
 294                     return;
 295                 }
 296                 writer.writeAttribute(prefix, namespaceURI, localName, value);
 297             }
 298 
 299             @Override
 300             public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
 301                 writer.writeAttribute(namespaceURI, localName, value);
 302             }
 303             private boolean isPrefixDeclared(String prefix, String namespaceURI ) {
 304                 return namespaceURI.equals(getNamespaceContext().getNamespaceURI(prefix));
 305             }
 306         },true);
 307     }
 308 
 309     @Override
 310     public void writeTo(SOAPMessage saaj) throws SOAPException {
 311         //TODO: SAAJ returns null instead of throwing SOAPException,
 312         // when there is no SOAPHeader in the message,
 313         // which leads to NPE.
 314         try {
 315             SOAPHeader header = saaj.getSOAPHeader();
 316             if (header == null) {
 317                 header = saaj.getSOAPPart().getEnvelope().addHeader();
 318             }
 319             Element node = (Element)infoset.writeTo(header);
 320             node.setAttributeNS(AddressingVersion.W3C.nsUri,AddressingVersion.W3C.getPrefix()+":"+IS_REFERENCE_PARAMETER,TRUE_VALUE);
 321         } catch (XMLStreamBufferException e) {
 322             throw new SOAPException(e);
 323         }
 324     }
 325 
 326     @Override
 327     public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException {
 328         class Filter extends XMLFilterImpl {
 329             Filter(ContentHandler ch) { setContentHandler(ch); }
 330             private int depth=0;
 331             @Override
 332             public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
 333                 if(depth++==0) {
 334                     // add one more attribute
 335                     super.startPrefixMapping("wsa",AddressingVersion.W3C.nsUri);
 336 
 337                     //Add the attirbute wsa:IsReferenceParameter if not present already
 338                     if(atts.getIndex(AddressingVersion.W3C.nsUri,IS_REFERENCE_PARAMETER) == -1) {
 339                         AttributesImpl atts2 = new AttributesImpl(atts);
 340                         atts2.addAttribute(
 341                                 AddressingVersion.W3C.nsUri,
 342                                 IS_REFERENCE_PARAMETER,
 343                                 "wsa:IsReferenceParameter",
 344                                 "CDATA",
 345                                 TRUE_VALUE);
 346                         atts = atts2;
 347                     }
 348                 }
 349 
 350                 super.startElement(uri, localName, qName, atts);
 351             }
 352 
 353             @Override
 354             public void endElement(String uri, String localName, String qName) throws SAXException {
 355                 super.endElement(uri, localName, qName);
 356                 if (--depth == 0) {
 357                     super.endPrefixMapping("wsa");
 358                 }
 359             }
 360         }
 361 
 362         infoset.writeTo(new Filter(contentHandler),errorHandler);
 363     }
 364 
 365 
 366     /**
 367      * Keep the information about an attribute on the header element.
 368      */
 369     static final class Attribute {
 370         /**
 371          * Can be empty but never null.
 372          */
 373         final String nsUri;
 374         final String localName;
 375         final String value;
 376 
 377         public Attribute(String nsUri, String localName, String value) {
 378             this.nsUri = fixNull(nsUri);
 379             this.localName = localName;
 380             this.value = value;
 381         }
 382 
 383         /**
 384          * Convert null to "".
 385          */
 386         private static String fixNull(String s) {
 387             return s == null ? "" : s;
 388         }
 389     }
 390 
 391     /**
 392      * We the performance paranoid people in the JAX-WS RI thinks
 393      * saving three bytes is worth while...
 394      */
 395     private static final String TRUE_VALUE = "1";
 396     private static final String IS_REFERENCE_PARAMETER = "IsReferenceParameter";
 397 }