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