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.ws.api.message.saaj;
  27 
  28 import com.sun.xml.internal.ws.api.SOAPVersion;
  29 import com.sun.xml.internal.ws.api.WSBinding;
  30 import com.sun.xml.internal.ws.api.message.Header;
  31 import com.sun.xml.internal.ws.api.message.MessageHeaders;
  32 import com.sun.xml.internal.ws.binding.SOAPBindingImpl;
  33 import com.sun.xml.internal.ws.message.saaj.SAAJHeader;
  34 
  35 import javax.xml.namespace.QName;
  36 import javax.xml.soap.SOAPException;
  37 import javax.xml.soap.SOAPHeader;
  38 import javax.xml.soap.SOAPHeaderElement;
  39 import javax.xml.soap.SOAPMessage;
  40 import java.util.ArrayList;
  41 import java.util.Collections;
  42 import java.util.HashMap;
  43 import java.util.HashSet;
  44 import java.util.Iterator;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Set;
  48 
  49 public class SAAJMessageHeaders implements MessageHeaders {
  50     SOAPMessage sm;
  51     Map<SOAPHeaderElement, Header> nonSAAJHeaders;
  52     Map<QName, Integer> notUnderstoodCount;
  53     SOAPVersion soapVersion;
  54     private Set<QName> understoodHeaders;
  55 
  56     public SAAJMessageHeaders(SOAPMessage sm, SOAPVersion version) {
  57         this.sm = sm;
  58         this.soapVersion = version;
  59         initHeaderUnderstanding();
  60     }
  61 
  62     /** Set the initial understood/not understood state of the headers in this
  63      * object
  64      */
  65     private void initHeaderUnderstanding() {
  66         SOAPHeader soapHeader = ensureSOAPHeader();
  67         if (soapHeader == null) {
  68             return;
  69         }
  70 
  71         Iterator allHeaders = soapHeader.examineAllHeaderElements();
  72         while(allHeaders.hasNext()) {
  73             SOAPHeaderElement nextHdrElem = (SOAPHeaderElement) allHeaders.next();
  74             if (nextHdrElem == null) {
  75                 continue;
  76             }
  77             if (nextHdrElem.getMustUnderstand()) {
  78                 notUnderstood(nextHdrElem.getElementQName());
  79             }
  80             //only headers explicitly marked as understood should be
  81             //in the understoodHeaders set, so don't add anything to
  82             //that set at the beginning
  83         }
  84 
  85     }
  86 
  87     @Override
  88     public void understood(Header header) {
  89         understood(header.getNamespaceURI(), header.getLocalPart());
  90     }
  91 
  92     @Override
  93     public void understood(String nsUri, String localName) {
  94         understood(new QName(nsUri, localName));
  95     }
  96 
  97     @Override
  98     public void understood(QName qName) {
  99         if (notUnderstoodCount == null) {
 100             notUnderstoodCount = new HashMap<QName, Integer>();
 101         }
 102 
 103         Integer count = notUnderstoodCount.get(qName);
 104         if (count != null && count.intValue() > 0) {
 105             //found the header in notUnderstood headers - decrement count
 106             count = count.intValue() - 1;
 107             if (count <= 0) {
 108                 //if the value is zero or negative, remove that header name
 109                 //since all headers by that name are understood now
 110                 notUnderstoodCount.remove(qName);
 111             } else {
 112                 notUnderstoodCount.put(qName, count);
 113             }
 114         }
 115 
 116         if (understoodHeaders == null) {
 117             understoodHeaders = new HashSet<QName>();
 118         }
 119         //also add it to the understood headers list (optimization for getUnderstoodHeaders)
 120         understoodHeaders.add(qName);
 121 
 122     }
 123 
 124     @Override
 125     public boolean isUnderstood(Header header) {
 126         return isUnderstood(header.getNamespaceURI(), header.getLocalPart());
 127     }
 128     @Override
 129     public boolean isUnderstood(String nsUri, String localName) {
 130         return isUnderstood(new QName(nsUri, localName));
 131     }
 132 
 133     @Override
 134     public boolean isUnderstood(QName name) {
 135         if (understoodHeaders == null) {
 136             return false;
 137         }
 138         return understoodHeaders.contains(name);
 139     }
 140 
 141     public boolean isUnderstood(int index) {
 142         // TODO Auto-generated method stub
 143         return false;
 144     }
 145 
 146     @Override
 147     public Header get(String nsUri, String localName, boolean markAsUnderstood) {
 148         SOAPHeaderElement h = find(nsUri, localName);
 149         if (h != null) {
 150             if (markAsUnderstood) {
 151                 understood(nsUri, localName);
 152             }
 153             return new SAAJHeader(h);
 154         }
 155         return null;
 156     }
 157 
 158     @Override
 159     public Header get(QName name, boolean markAsUnderstood) {
 160         return get(name.getNamespaceURI(), name.getLocalPart(), markAsUnderstood);
 161     }
 162 
 163     @Override
 164     public Iterator<Header> getHeaders(QName headerName,
 165             boolean markAsUnderstood) {
 166         return getHeaders(headerName.getNamespaceURI(), headerName.getLocalPart(), markAsUnderstood);
 167     }
 168 
 169     @Override
 170     public Iterator<Header> getHeaders(final String nsUri, final String localName,
 171             final boolean markAsUnderstood) {
 172         SOAPHeader soapHeader = ensureSOAPHeader();
 173         if (soapHeader == null) {
 174             return null;
 175         }
 176         Iterator allHeaders = soapHeader.examineAllHeaderElements();
 177         if (markAsUnderstood) {
 178             //mark all the matchingheaders as understood up front
 179             //make an iterator while we're doing that
 180             List<Header> headers = new ArrayList<Header>();
 181             while (allHeaders.hasNext()) {
 182                 SOAPHeaderElement nextHdr = (SOAPHeaderElement) allHeaders.next();
 183                 if (nextHdr != null &&
 184                         nextHdr.getNamespaceURI().equals(nsUri)) {
 185                     if (localName == null ||
 186                             nextHdr.getLocalName().equals(localName)) {
 187                         understood(nextHdr.getNamespaceURI(), nextHdr.getLocalName());
 188                         headers.add(new SAAJHeader(nextHdr));
 189                     }
 190                 }
 191             }
 192             return headers.iterator();
 193         }
 194         //if we got here markAsUnderstood is false - return a lazy iterator rather
 195         //than traverse the entire list of headers now
 196         return new HeaderReadIterator(allHeaders, nsUri, localName);
 197     }
 198 
 199     @Override
 200     public Iterator<Header> getHeaders(String nsUri, boolean markAsUnderstood) {
 201         return getHeaders(nsUri, null, markAsUnderstood);
 202     }
 203     @Override
 204     public boolean add(Header header) {
 205         try {
 206             header.writeTo(sm);
 207         } catch (SOAPException e) {
 208             //TODO log exception
 209             return false;
 210         }
 211 
 212         //the newly added header is not understood by default
 213         notUnderstood(new QName(header.getNamespaceURI(), header.getLocalPart()));
 214 
 215         //track non saaj headers so that they can be retrieved later
 216         if (isNonSAAJHeader(header)) {
 217             //TODO assumes only one header with that name?
 218             addNonSAAJHeader(find(header.getNamespaceURI(), header.getLocalPart()),
 219                     header);
 220         }
 221 
 222         return true;
 223     }
 224 
 225     @Override
 226     public Header remove(QName name) {
 227         return remove(name.getNamespaceURI(), name.getLocalPart());
 228     }
 229 
 230     @Override
 231     public Header remove(String nsUri, String localName) {
 232         SOAPHeader soapHeader = ensureSOAPHeader();
 233         if (soapHeader == null) {
 234             return null;
 235         }
 236         SOAPHeaderElement headerElem = find(nsUri, localName);
 237         if (headerElem == null) {
 238             return null;
 239         }
 240         headerElem = (SOAPHeaderElement) soapHeader.removeChild(headerElem);
 241 
 242         //it might have been a nonSAAJHeader - remove from that map
 243         removeNonSAAJHeader(headerElem);
 244 
 245         //remove it from understoodHeaders and notUnderstoodHeaders if present
 246         QName hdrName = (nsUri == null) ? new QName(localName) : new QName(nsUri, localName);
 247         if (understoodHeaders != null) {
 248             understoodHeaders.remove(hdrName);
 249         }
 250         removeNotUnderstood(hdrName);
 251 
 252         return new SAAJHeader(headerElem);
 253     }
 254 
 255     private void removeNotUnderstood(QName hdrName) {
 256         if (notUnderstoodCount == null) {
 257             return;
 258         }
 259         Integer notUnderstood = notUnderstoodCount.get(hdrName);
 260         if (notUnderstood != null) {
 261             int intNotUnderstood = notUnderstood;
 262             intNotUnderstood--;
 263             if (intNotUnderstood <= 0) {
 264                 notUnderstoodCount.remove(hdrName);
 265             }
 266         }
 267 
 268     }
 269 
 270     private SOAPHeaderElement find(QName qName) {
 271         return find(qName.getNamespaceURI(), qName.getLocalPart());
 272     }
 273 
 274     private SOAPHeaderElement find(String nsUri, String localName) {
 275         SOAPHeader soapHeader = ensureSOAPHeader();
 276         if (soapHeader == null) {
 277             return null;
 278         }
 279         Iterator allHeaders = soapHeader.examineAllHeaderElements();
 280         while(allHeaders.hasNext()) {
 281             SOAPHeaderElement nextHdrElem = (SOAPHeaderElement) allHeaders.next();
 282             if (nextHdrElem.getNamespaceURI().equals(nsUri) &&
 283                     nextHdrElem.getLocalName().equals(localName)) {
 284                 return nextHdrElem;
 285             }
 286         }
 287         return null;
 288     }
 289 
 290     private void notUnderstood(QName qName) {
 291         if (notUnderstoodCount == null) {
 292             notUnderstoodCount = new HashMap<QName, Integer>();
 293         }
 294         Integer count = notUnderstoodCount.get(qName);
 295         if (count == null) {
 296             notUnderstoodCount.put(qName, 1);
 297         } else {
 298             notUnderstoodCount.put(qName, count + 1);
 299         }
 300 
 301         //if for some strange reason it was previously understood and now is not,
 302         //remove it from understoodHeaders if it exists there
 303         if (understoodHeaders != null) {
 304             understoodHeaders.remove(qName);
 305         }
 306     }
 307 
 308     /**
 309      * Utility method to get the SOAPHeader from a SOAPMessage, adding one if
 310      * one is not present in the original message.
 311      */
 312     private SOAPHeader ensureSOAPHeader() {
 313         SOAPHeader header;
 314         try {
 315             header = sm.getSOAPPart().getEnvelope().getHeader();
 316             if (header != null) {
 317                 return header;
 318             } else {
 319                 return sm.getSOAPPart().getEnvelope().addHeader();
 320             }
 321         } catch (Exception e) {
 322             return null;
 323         }
 324     }
 325 
 326     private boolean isNonSAAJHeader(Header header) {
 327         return !(header instanceof SAAJHeader);
 328     }
 329 
 330     private void addNonSAAJHeader(SOAPHeaderElement headerElem, Header header) {
 331         if (nonSAAJHeaders == null) {
 332             nonSAAJHeaders = new HashMap<>();
 333         }
 334         nonSAAJHeaders.put(headerElem, header);
 335     }
 336 
 337     private void removeNonSAAJHeader(SOAPHeaderElement headerElem) {
 338         if (nonSAAJHeaders != null) {
 339             nonSAAJHeaders.remove(headerElem);
 340         }
 341     }
 342 
 343     @Override
 344     public boolean addOrReplace(Header header) {
 345         remove(header.getNamespaceURI(), header.getLocalPart());
 346         return add(header);
 347     }
 348 
 349     @Override
 350     public void replace(Header old, Header header) {
 351         if (remove(old.getNamespaceURI(), old.getLocalPart()) == null)
 352             throw new IllegalArgumentException();
 353         add(header);
 354     }
 355 
 356     @Override
 357     public Set<QName> getUnderstoodHeaders() {
 358         return understoodHeaders;
 359     }
 360 
 361     @Override
 362     public Set<QName> getNotUnderstoodHeaders(Set<String> roles,
 363             Set<QName> knownHeaders, WSBinding binding) {
 364         Set<QName> notUnderstoodHeaderNames = new HashSet<QName>();
 365         if (notUnderstoodCount == null) {
 366             return notUnderstoodHeaderNames;
 367         }
 368         for (QName headerName : notUnderstoodCount.keySet()) {
 369             int count = notUnderstoodCount.get(headerName);
 370             if (count <= 0) {
 371                 continue;
 372             }
 373             SOAPHeaderElement hdrElem = find(headerName);
 374             if (!hdrElem.getMustUnderstand()) {
 375                 continue;
 376             }
 377             SAAJHeader hdr = new SAAJHeader(hdrElem);
 378             //mustUnderstand attribute is true - but there may be
 379             //additional criteria
 380             boolean understood = false;
 381             if (roles != null) {
 382                 understood = !roles.contains(hdr.getRole(soapVersion));
 383             }
 384             if (understood) {
 385                 continue;
 386             }
 387             //if it must be understood see if it is understood by the binding
 388             //or is in knownheaders
 389             if (binding != null && binding instanceof SOAPBindingImpl) {
 390                 understood = ((SOAPBindingImpl) binding).understandsHeader(headerName);
 391                 if (!understood) {
 392                     if (knownHeaders != null && knownHeaders.contains(headerName)) {
 393                         understood = true;
 394                     }
 395                 }
 396             }
 397             if (!understood) {
 398                 notUnderstoodHeaderNames.add(headerName);
 399             }
 400         }
 401         return notUnderstoodHeaderNames;
 402     }
 403 
 404     @Override
 405     public Iterator<Header> getHeaders() {
 406         SOAPHeader soapHeader = ensureSOAPHeader();
 407         if (soapHeader == null) {
 408             return null;
 409         }
 410         Iterator allHeaders = soapHeader.examineAllHeaderElements();
 411         return new HeaderReadIterator(allHeaders, null, null);
 412     }
 413 
 414     private static class HeaderReadIterator implements Iterator<Header> {
 415         SOAPHeaderElement current;
 416         Iterator soapHeaders;
 417         String myNsUri;
 418         String myLocalName;
 419 
 420         public HeaderReadIterator(Iterator allHeaders, String nsUri,
 421                 String localName) {
 422             this.soapHeaders = allHeaders;
 423             this.myNsUri = nsUri;
 424             this.myLocalName = localName;
 425         }
 426 
 427         @Override
 428         public boolean hasNext() {
 429             if (current == null) {
 430                 advance();
 431             }
 432             return (current != null);
 433         }
 434 
 435         @Override
 436         public Header next() {
 437             if (!hasNext()) {
 438                 return null;
 439             }
 440             if (current == null) {
 441                 return null;
 442             }
 443 
 444             SAAJHeader ret = new SAAJHeader(current);
 445             current = null;
 446             return ret;
 447         }
 448 
 449         @Override
 450         public void remove() {
 451             throw new UnsupportedOperationException();
 452         }
 453 
 454         private void advance() {
 455             while (soapHeaders.hasNext()) {
 456                 SOAPHeaderElement nextHdr = (SOAPHeaderElement) soapHeaders.next();
 457                 if (nextHdr != null &&
 458                         (myNsUri == null || nextHdr.getNamespaceURI().equals(myNsUri)) &&
 459                         (myLocalName == null || nextHdr.getLocalName().equals(myLocalName))) {
 460                         current = nextHdr;
 461                         //found it
 462                         return;
 463                     }
 464                 }
 465             //if we got here we didn't find a match
 466             current = null;
 467         }
 468 
 469     }
 470 
 471     @Override
 472     public boolean hasHeaders() {
 473         SOAPHeader soapHeader = ensureSOAPHeader();
 474         if (soapHeader == null) {
 475             return false;
 476         }
 477 
 478         Iterator allHeaders = soapHeader.examineAllHeaderElements();
 479         return allHeaders.hasNext();
 480     }
 481 
 482     @Override
 483     public List<Header> asList() {
 484         SOAPHeader soapHeader = ensureSOAPHeader();
 485         if (soapHeader == null) {
 486             return Collections.emptyList();
 487         }
 488 
 489         Iterator allHeaders = soapHeader.examineAllHeaderElements();
 490         List<Header> headers = new ArrayList<Header>();
 491         while (allHeaders.hasNext()) {
 492             SOAPHeaderElement nextHdr = (SOAPHeaderElement) allHeaders.next();
 493             headers.add(new SAAJHeader(nextHdr));
 494         }
 495         return headers;
 496     }
 497 }