/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package org.apache.axis2.jaxws.handler; | |
import org.apache.axiom.soap.RolePlayer; | |
import org.apache.axiom.soap.SOAP11Constants; | |
import org.apache.axiom.soap.SOAP12Constants; | |
import org.apache.axiom.soap.SOAPConstants; | |
import org.apache.axiom.soap.SOAPEnvelope; | |
import org.apache.axiom.soap.SOAPHeaderBlock; | |
import org.apache.axis2.AxisFault; | |
import org.apache.axis2.Constants; | |
import org.apache.axis2.context.MessageContext; | |
import org.apache.axis2.description.AxisService; | |
import org.apache.axis2.description.Parameter; | |
import org.apache.axis2.i18n.Messages; | |
import org.apache.axis2.jaxws.description.EndpointDescription; | |
import org.apache.axis2.util.LoggingControl; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import javax.xml.namespace.QName; | |
import javax.xml.ws.handler.Handler; | |
import javax.xml.ws.handler.soap.SOAPHandler; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Set; | |
/* | |
* Utility class to perform static utility type of operations on Handlers. | |
*/ | |
public class HandlerUtils { | |
private static Log log = LogFactory.getLog(HandlerUtils.class); | |
/** | |
* registerHandlerHeaders will invoke getHeaders on SOAPHandlers and return a List of headers | |
* that are Understood by the handlers. | |
* @param msgContext | |
* @param handlers | |
*/ | |
public static List<QName> registerSOAPHandlerHeaders(MessageContext msgContext, List<Handler> handlers){ | |
List<QName> understood = new ArrayList<QName>(); | |
if(msgContext == null){ | |
return understood; | |
} | |
for(Handler handler:handlers){ | |
if(handler instanceof SOAPHandler){ | |
SOAPHandler soapHandler = (SOAPHandler)handler; | |
//Invoking getHeaders. | |
if(log.isDebugEnabled()){ | |
log.debug("Invoking getHeader() on SOAPHandler"); | |
} | |
Set<QName> headers = soapHandler.getHeaders(); | |
if(headers!=null){ | |
for(QName header:headers){ | |
if(!understood.contains(header)){ | |
if(log.isDebugEnabled()){ | |
log.debug("Adding Header QName" + header + " to uderstoodHeaderQName List"); | |
} | |
//Adding this to understood header list. | |
understood.add(header); | |
} | |
} | |
} | |
} | |
} | |
return understood; | |
} | |
/** | |
* checkMustUnderstand will validate headers that where delegated by Axis Engine | |
* to MessageReceiver for mustUnderstand check. | |
* | |
* Note that there is other JAX-WS related mustUnderstand checking occuring elsewhere: | |
* @see org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher | |
* @see org.apache.axis2.jaxws.dispatchers.MustUnderstandChecker | |
* | |
* @param msgContext Contains the SOAPEnvelope and optionally a list of headers not | |
* understood by the AxisEngine | |
* @param understood A list of header QNames understood by JAX-WS, for example those understood | |
* by associated application handlers. Can be null. | |
* @param additionalRoles An instance of RolePlayer for any addtional roles played by JAX-WS | |
* for example, roles configured for associated handlers. Can be null. | |
* | |
* @throws AxisFault if any headers marked mustUndersand are not understood. | |
*/ | |
public static void checkMustUnderstand(MessageContext msgContext, List<QName> understood, List<String> additionalRoles) throws AxisFault { | |
if (msgContext == null || !msgContext.isHeaderPresent()) { | |
return; | |
} | |
SOAPEnvelope envelope = msgContext.getEnvelope(); | |
if (envelope.getHeader() == null) { | |
return; | |
} | |
if(log.isDebugEnabled()){ | |
log.debug("Reading UnprocessedHeaderNames from Message Context properties"); | |
} | |
List<QName> unprocessed = (List)msgContext.getProperty(Constants.UNPROCESSED_HEADER_QNAMES); | |
// Add to the unprocessed header list any headers that are unprocssed and mustUnderstand | |
// for addtional roles, for example those played by associated JAXWS handlers | |
if (additionalRoles != null) { | |
if (log.isDebugEnabled()) { | |
log.debug("Adding any mustUnderstand headers based on additonal SOAP roles: " + additionalRoles); | |
} | |
HandlerRolePlayer handlerRolePlayer = new HandlerRolePlayer(additionalRoles); | |
Iterator headerBlocks = envelope.getHeader().getHeadersToProcess(handlerRolePlayer); | |
while (headerBlocks.hasNext()) { | |
SOAPHeaderBlock shb = (SOAPHeaderBlock) headerBlocks.next(); | |
if (unprocessed == null) { | |
unprocessed = new ArrayList<QName>(); | |
} | |
if (!shb.isProcessed() && shb.getMustUnderstand()) { | |
unprocessed.add(shb.getQName()); | |
if (log.isDebugEnabled()) { | |
log.debug("Added header to unprocessed list: " + shb.getQName()); | |
} | |
} | |
} | |
} | |
if(unprocessed == null || unprocessed.size() == 0){ | |
if(log.isDebugEnabled()){ | |
log.debug("UNPROCESSED_HEADER_QNAMES not found."); | |
} | |
return; | |
} | |
//lets go thru each header only if @HandlerChain is present | |
if(!canUnderstand(msgContext)){ | |
QName[] qNames = unprocessed.toArray(new QName[0]); | |
String[] headerNames = new String[qNames.length]; | |
for(int i=0; i<qNames.length; i++){ | |
headerNames[i] ="{" + qNames[i].getNamespaceURI()+ "}" + qNames[i].getLocalPart(); | |
} | |
QName faultQName = envelope.getVersion().getMustUnderstandFaultCode(); | |
throw new AxisFault(Messages.getMessage("mustunderstandfailed2", headerNames), faultQName); | |
} | |
checkUnprocessed(envelope, unprocessed, understood, msgContext); | |
//resetting the FAULTY_HEADER_QNAME to null. | |
msgContext.setProperty(Constants.UNPROCESSED_HEADER_QNAMES, null); | |
} | |
private static void checkUnprocessed(SOAPEnvelope envelope, List<QName> unprocessed, List<QName> understood, MessageContext msgContext) throws AxisFault{ | |
for (QName headerQName : unprocessed) { | |
if (understood != null && !understood.isEmpty()) { | |
if (understood.contains(headerQName)) { | |
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) { | |
log.debug("MustUnderstand header registered as understood on AxisOperation: " + headerQName); | |
} | |
continue; | |
} | |
} | |
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) { | |
log.debug("MustUnderstand header not processed or registered as understood " + headerQName); | |
} | |
// Throw a MustUnderstand fault for the current SOAP version | |
String prefix = envelope.getNamespace().getPrefix(); | |
if (!msgContext.isSOAP11()) { | |
if (prefix == null || "".equals(prefix)) { | |
prefix = SOAPConstants.SOAP_DEFAULT_NAMESPACE_PREFIX; | |
} | |
// TODO: should we be using a prefix on the faultcode? What about | |
// the QName object Constants.FAULT_SOAP12_MUSTUNDERSTAND? | |
throw new AxisFault(Messages.getMessage("mustunderstandfailed", | |
prefix, | |
headerQName.toString()), | |
SOAP12Constants.FAULT_CODE_MUST_UNDERSTAND); | |
} else { | |
// TODO: should we be using a prefix on the faultcode? What about | |
// the QName object Constants.FAULT_MUSTUNDERSTAND? | |
throw new AxisFault(Messages.getMessage("mustunderstandfailed", | |
prefix, | |
headerQName.toString()), | |
SOAP11Constants.FAULT_CODE_MUST_UNDERSTAND); | |
} | |
} | |
} | |
private static boolean canUnderstand(MessageContext msgContext){ | |
//JAXWSMessageReceiver will only commit to handling must understand if @HandlerChain annotation is present on the | |
//Endpoint. This will indicate to AxisEngine that Faulty Header names are understood however the mustUnderstand | |
//Check will be performed in HandlerUtils class after Handlers are injected in application. | |
AxisService axisSvc = msgContext.getAxisService(); | |
if (axisSvc.getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER) != null) { | |
Parameter param = axisSvc.getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER); | |
EndpointDescription ed = (EndpointDescription)param.getValue(); | |
//Lets check if there is a Handler implementation present using Metadata layer. | |
//HandlerChain annotation can be present in Service Endpoint or ServiceEndpointInterface. | |
// ed.getHandlerChain() looks for HandlerAnnotation at both Endpoint and at SEI. | |
if(log.isDebugEnabled()){ | |
log.debug("Check to see if a jaxws handler is configured."); | |
} | |
if(ed.getHandlerChain()!=null){ | |
return true; | |
} | |
return false; | |
} | |
else{ | |
//If we cannot get to ServiceDescription to check for HandlerChain annotation we will return true; | |
return true; | |
} | |
} | |
} | |
class HandlerRolePlayer implements RolePlayer { | |
List<String> roles = new ArrayList<String>(); | |
HandlerRolePlayer(List<String> additionalRoles) { | |
roles.addAll(additionalRoles); | |
} | |
public List getRoles() { | |
return roles; | |
} | |
public boolean isUltimateDestination() { | |
return false; | |
} | |
} | |