src/share/jaxws_classes/com/sun/xml/internal/ws/addressing/WsaTube.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 637
9c07ef4934dd
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

     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  */
    26 package com.sun.xml.internal.ws.addressing;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.xml.internal.ws.addressing.model.InvalidAddressingHeaderException;
    30 import com.sun.xml.internal.ws.addressing.model.MissingAddressingHeaderException;
    31 import com.sun.xml.internal.ws.api.SOAPVersion;
    32 import com.sun.xml.internal.ws.api.WSBinding;
    33 import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
    34 import com.sun.xml.internal.ws.api.message.AddressingUtils;
    35 import com.sun.xml.internal.ws.api.message.Header;
    36 import com.sun.xml.internal.ws.api.message.Message;
    37 import com.sun.xml.internal.ws.api.message.Messages;
    38 import com.sun.xml.internal.ws.api.message.Packet;
    39 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation;
    40 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
    41 import com.sun.xml.internal.ws.api.pipe.NextAction;
    42 import com.sun.xml.internal.ws.api.pipe.Tube;
    43 import com.sun.xml.internal.ws.api.pipe.TubeCloner;
    44 import com.sun.xml.internal.ws.api.pipe.helper.AbstractFilterTubeImpl;
    45 import com.sun.xml.internal.ws.developer.MemberSubmissionAddressingFeature;
    46 import com.sun.xml.internal.ws.message.FaultDetailHeader;
    47 import com.sun.xml.internal.ws.resources.AddressingMessages;
    49 import javax.xml.namespace.QName;
    50 import javax.xml.soap.SOAPFault;
    51 import javax.xml.stream.XMLStreamException;
    52 import javax.xml.ws.WebServiceException;
    53 import javax.xml.ws.soap.AddressingFeature;
    54 import javax.xml.ws.soap.SOAPBinding;
    55 import java.util.Iterator;
    56 import java.util.logging.Logger;
    57 import java.util.logging.Level;
    59 /**
    60  * WS-Addressing processing code shared between client and server.
    61  *
    62  * <p>
    63  * This tube is used only when WS-Addressing is enabled.
    64  *
    65  * @author Rama Pulavarthi
    66  * @author Arun Gupta
    67  */
    68 abstract class WsaTube extends AbstractFilterTubeImpl {
    69     /**
    70      * Port that we are processing.
    71      */
    72     protected final @NotNull WSDLPort wsdlPort;
    73     protected final WSBinding binding;
    74     final WsaTubeHelper helper;
    75     protected final @NotNull AddressingVersion addressingVersion;
    76     protected final SOAPVersion soapVersion;
    78     /**
    79      * True if the addressing headers are mandatory.
    80      */
    81     private final boolean addressingRequired;
    83     public WsaTube(WSDLPort wsdlPort, WSBinding binding, Tube next) {
    84         super(next);
    85         this.wsdlPort = wsdlPort;
    86         this.binding = binding;
    87         addKnownHeadersToBinding(binding);
    88         addressingVersion = binding.getAddressingVersion();
    89         soapVersion = binding.getSOAPVersion();
    90         helper = getTubeHelper();
    91         addressingRequired = AddressingVersion.isRequired(binding);
    92     }
    94     public WsaTube(WsaTube that, TubeCloner cloner) {
    95         super(that, cloner);
    96         this.wsdlPort = that.wsdlPort;
    97         this.binding = that.binding;
    98         this.helper = that.helper;
    99         addressingVersion = that.addressingVersion;
   100         soapVersion = that.soapVersion;
   101         addressingRequired = that.addressingRequired;
   102     }
   104     private void addKnownHeadersToBinding(WSBinding binding) {
   105         for (AddressingVersion addrVersion: AddressingVersion.values()) {
   106           binding.addKnownHeader(addrVersion.actionTag);
   107           binding.addKnownHeader(addrVersion.faultDetailTag);
   108           binding.addKnownHeader(addrVersion.faultToTag);
   109           binding.addKnownHeader(addrVersion.fromTag);
   110           binding.addKnownHeader(addrVersion.messageIDTag);
   111           binding.addKnownHeader(addrVersion.relatesToTag);
   112           binding.addKnownHeader(addrVersion.replyToTag);
   113           binding.addKnownHeader(addrVersion.toTag);
   114         }
   115     }
   117     @Override
   118     public @NotNull NextAction processException(Throwable t) {
   119         return super.processException(t);
   120     }
   122     protected WsaTubeHelper getTubeHelper() {
   123         if(binding.isFeatureEnabled(AddressingFeature.class)) {
   124             return new WsaTubeHelperImpl(wsdlPort, null, binding);
   125         } else if(binding.isFeatureEnabled(MemberSubmissionAddressingFeature.class)) {
   126             //seiModel is null as it is not needed.
   127             return new com.sun.xml.internal.ws.addressing.v200408.WsaTubeHelperImpl(wsdlPort, null, binding);
   128         } else {
   129             // Addressing is not enabled, WsaTube should not be included in the pipeline
   130             throw new WebServiceException(AddressingMessages.ADDRESSING_NOT_ENABLED(this.getClass().getSimpleName()));
   131         }
   132     }
   134     /**
   135      * Validates the inbound message. If an error is found, create
   136      * a fault message and returns that. Otherwise
   137      * it will pass through the parameter 'packet' object to the return value.
   138      */
   139     protected Packet validateInboundHeaders(Packet packet) {
   140         SOAPFault soapFault;
   141         FaultDetailHeader s11FaultDetailHeader;
   143         try {
   144             checkMessageAddressingProperties(packet);
   145             return packet;
   146         } catch (InvalidAddressingHeaderException e) {
   147             LOGGER.log(Level.WARNING,
   148                     addressingVersion.getInvalidMapText()+", Problem header:" + e.getProblemHeader()+ ", Reason: "+ e.getSubsubcode(),e);
   149             soapFault = helper.createInvalidAddressingHeaderFault(e, addressingVersion);
   150             s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getProblemHeader());
   151         } catch (MissingAddressingHeaderException e) {
   152             LOGGER.log(Level.WARNING,addressingVersion.getMapRequiredText()+", Problem header:"+ e.getMissingHeaderQName(),e);
   153             soapFault = helper.newMapRequiredFault(e);
   154             s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getMissingHeaderQName());
   155         }
   157         if (soapFault != null) {
   158             // WS-A fault processing for one-way methods
   159             if ((wsdlPort !=null)  && packet.getMessage().isOneWay(wsdlPort)) {
   160                 return packet.createServerResponse(null, wsdlPort, null, binding);
   161             }
   163             Message m = Messages.create(soapFault);
   164             if (soapVersion == SOAPVersion.SOAP_11) {
   165                 m.getHeaders().add(s11FaultDetailHeader);
   166             }
   168             return packet.createServerResponse(m, wsdlPort, null,  binding);
   169         }
   171         return packet;
   172     }
   174     /**
   175      * This method checks all the WS-Addressing headers are valid and as per the spec definded rules.
   176      * Mainly it checks the cardinality of the WSA headers and checks that mandatory headers exist.
   177      * It also checks if the SOAPAction is equal to wsa:Action value when non-empty.
   178      *
   179      * Override this method if you need to additional checking of headers other than just existence of the headers.
   180      * For ex: On server-side, check Anonymous and Non-Anonymous semantics in addition to checking cardinality.
   181      *
   182      * Override checkMandatoryHeaders(Packet p) to have different validation rules for different versions
   183      *
   184      * @param packet
   185      */
   186     protected void checkMessageAddressingProperties(Packet packet) {
   187         checkCardinality(packet);
   188     }
   190     final boolean isAddressingEngagedOrRequired(Packet packet, WSBinding binding) {
   191         if (AddressingVersion.isRequired(binding))
   192             return true;
   194         if (packet == null)
   195             return false;
   197         if (packet.getMessage() == null)
   198             return false;
   200         if (packet.getMessage().getHeaders() != null)
   201             return false;
   203         String action = AddressingUtils.getAction(
   204                 packet.getMessage().getHeaders(),
   205                 addressingVersion, soapVersion);
   206         if (action == null)
   207             return true;
   209         return true;
   210     }
   212     /**
   213      * Checks the cardinality of WS-Addressing headers on an inbound {@link Packet}. This method
   214      * checks for the cardinality if WS-Addressing is engaged (detected by the presence of wsa:Action
   215      * header) or wsdl:required=true.
   216      *
   217      * @param packet The inbound packet.
   218      * @throws WebServiceException if:
   219      * <ul>
   220      * <li>there is an error reading ReplyTo or FaultTo</li>
   221      * <li>WS-Addressing is required and {@link Message} within <code>packet</code> is null</li>
   222      * <li>WS-Addressing is required and no headers are found in the {@link Message}</li>
   223      * <li>an uknown WS-Addressing header is present</li>
   224      * </ul>
   225      */
   226     protected void checkCardinality(Packet packet) {
   227         Message message = packet.getMessage();
   228         if (message == null) {
   229             if (addressingRequired)
   230                 throw new WebServiceException(AddressingMessages.NULL_MESSAGE());
   231             else
   232                 return;
   233         }
   235         Iterator<Header> hIter = message.getHeaders().getHeaders(addressingVersion.nsUri, true);
   237         if (!hIter.hasNext()) {
   238             // no WS-A headers are found
   239             if (addressingRequired)
   240                 // if WS-A is required, then throw an exception looking for wsa:Action header
   241                 throw new MissingAddressingHeaderException(addressingVersion.actionTag,packet);
   242             else
   243                 // else no need to process
   244                 return;
   245         }
   247         boolean foundFrom = false;
   248         boolean foundTo = false;
   249         boolean foundReplyTo = false;
   250         boolean foundFaultTo = false;
   251         boolean foundAction = false;
   252         boolean foundMessageId = false;
   253         boolean foundRelatesTo = false;
   254         QName duplicateHeader = null;
   256         while (hIter.hasNext()) {
   257             Header h = hIter.next();
   259             // check if the Header is in current role
   260             if (!isInCurrentRole(h, binding)) {
   261                 continue;
   262             }
   264             String local = h.getLocalPart();
   265             if (local.equals(addressingVersion.fromTag.getLocalPart())) {
   266                 if (foundFrom) {
   267                     duplicateHeader = addressingVersion.fromTag;
   268                     break;
   269                 }
   270                 foundFrom = true;
   271             } else if (local.equals(addressingVersion.toTag.getLocalPart())) {
   272                 if (foundTo) {
   273                     duplicateHeader = addressingVersion.toTag;
   274                     break;
   275                 }
   276                 foundTo = true;
   277             } else if (local.equals(addressingVersion.replyToTag.getLocalPart())) {
   278                 if (foundReplyTo) {
   279                     duplicateHeader = addressingVersion.replyToTag;
   280                     break;
   281                 }
   282                 foundReplyTo = true;
   283                 try { // verify that the header is in a good shape
   284                     h.readAsEPR(addressingVersion);
   285                 } catch (XMLStreamException e) {
   286                     throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e);
   287                 }
   288             } else if (local.equals(addressingVersion.faultToTag.getLocalPart())) {
   289                 if (foundFaultTo) {
   290                     duplicateHeader = addressingVersion.faultToTag;
   291                     break;
   292                 }
   293                 foundFaultTo = true;
   294                 try { // verify that the header is in a good shape
   295                     h.readAsEPR(addressingVersion);
   296                 } catch (XMLStreamException e) {
   297                     throw new WebServiceException(AddressingMessages.FAULT_TO_CANNOT_PARSE(), e);
   298                 }
   299             } else if (local.equals(addressingVersion.actionTag.getLocalPart())) {
   300                 if (foundAction) {
   301                     duplicateHeader = addressingVersion.actionTag;
   302                     break;
   303                 }
   304                 foundAction = true;
   305             } else if (local.equals(addressingVersion.messageIDTag.getLocalPart())) {
   306                 if (foundMessageId) {
   307                     duplicateHeader = addressingVersion.messageIDTag;
   308                     break;
   309                 }
   310                 foundMessageId = true;
   311             } else if (local.equals(addressingVersion.relatesToTag.getLocalPart())) {
   312                 foundRelatesTo = true;
   313             } else if (local.equals(addressingVersion.faultDetailTag.getLocalPart())) {
   314                 // TODO: should anything be done here ?
   315                 // TODO: fault detail element - only for SOAP 1.1
   316             } else {
   317                 System.err.println(AddressingMessages.UNKNOWN_WSA_HEADER());
   318             }
   319         }
   321         // check for invalid cardinality first before checking for mandatory headers
   322         if (duplicateHeader != null) {
   323             throw new InvalidAddressingHeaderException(duplicateHeader, addressingVersion.invalidCardinalityTag);
   324         }
   326         // WS-A is engaged if wsa:Action header is found
   327         boolean engaged = foundAction;
   329         // check for mandatory set of headers only if:
   330         // 1. WS-A is engaged or
   331         // 2. wsdl:required=true
   332         // Both wsa:Action and wsa:To MUST be present on request (for oneway MEP) and
   333         // response messages (for oneway and request/response MEP only)
   334         if (engaged || addressingRequired) {
   335             // Check for mandatory headers always (even for Protocol messages).
   336             // If it breaks any interop scenarios, Remove the comments.
   337             /*
   338             WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
   339             // no need to check for for non-application messages
   340             if (wbo == null)
   341                 return;
   342             */
   343             checkMandatoryHeaders(packet, foundAction, foundTo, foundReplyTo,
   344                     foundFaultTo, foundMessageId, foundRelatesTo);
   345         }
   346     }
   348     final boolean isInCurrentRole(Header header, WSBinding binding) {
   349         // TODO: binding will be null for protocol messages
   350         // TODO: returning true assumes that protocol messages are
   351         // TODO: always in current role, this may not to be fixed.
   352         if (binding == null)
   353             return true;
   354         return ((SOAPBinding)binding).getRoles().contains(header.getRole(soapVersion));
   356     }
   358     protected final WSDLBoundOperation getWSDLBoundOperation(Packet packet) {
   359         //we can find Req/Response or Oneway only with WSDLModel
   360         if(wsdlPort == null)
   361             return null;
   362         QName opName = packet.getWSDLOperation();
   363         if(opName != null)
   364             return wsdlPort.getBinding().get(opName);
   365         return null;
   366     }
   368     protected void validateSOAPAction(Packet packet) {
   369         String gotA = AddressingUtils.getAction(
   370                 packet.getMessage().getHeaders(),
   371                 addressingVersion, soapVersion);
   372         if (gotA == null)
   373             throw new WebServiceException(AddressingMessages.VALIDATION_SERVER_NULL_ACTION());
   374         if(packet.soapAction != null && !packet.soapAction.equals("\"\"") && !packet.soapAction.equals("\""+gotA+"\"")) {
   375             throw new InvalidAddressingHeaderException(addressingVersion.actionTag, addressingVersion.actionMismatchTag);
   376         }
   377     }
   379     protected abstract void validateAction(Packet packet);
   381     /**
   382      * This should be called only when Addressing is engaged.
   383      *
   384      * Checks only for presence of wsa:Action and validates that wsa:Action
   385      * equals SOAPAction header when non-empty
   386      * Should be overridden if other wsa headers need to be checked based on version.
   387      *
   388      * @param packet
   389      * @param foundAction
   390      * @param foundTo
   391      * @param foundReplyTo
   392      * @param foundFaultTo
   393      * @param foundMessageId
   394      * @param foundRelatesTo
   395      */
   396     protected void checkMandatoryHeaders(
   397         Packet packet, boolean foundAction, boolean foundTo, boolean foundReplyTo,
   398             boolean foundFaultTo, boolean foundMessageId, boolean foundRelatesTo) {
   399         // if no wsa:Action header is found
   400         if (!foundAction)
   401             throw new MissingAddressingHeaderException(addressingVersion.actionTag,packet);
   402         validateSOAPAction(packet);
   403     }
   404     private static final Logger LOGGER = Logger.getLogger(WsaTube.class.getName());
   405 }

mercurial