org.apache.axis2.jaxws.handler.HandlerUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of axis2-jaxws Show documentation
Show all versions of axis2-jaxws Show documentation
Axis2 JAXWS Implementation
/*
* 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.context.OperationContext;
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.JavaUtils;
import org.apache.axis2.util.LoggingControl;
import org.apache.axis2.wsdl.WSDLConstants;
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 registerSOAPHandlerHeaders(MessageContext msgContext, List handlers){
List understood = new ArrayList();
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 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 understood, List 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 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();
}
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 unprocessed, List 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;
}
}
/**
* isHighFidelity
*
* The JAX-WS engine attempts to stream data as fast as possible.
* For example, the message payload may be transformed into a JAXB object early in the processing.
* Unfortunately such transformations are lossy, some information is lost.
* An installed SOAP handler will see different namespaces (etc) then the original message.
*
* If the a customer enables the "jaxws.payload.highFidelity" flag, then lossy transformations are
* avoided until necessary.
*
* @see Constants.JAXWS_HIGH_FIDELITY
*
* @param mc
* @return true if high fidelity is requested
*/
public static boolean isHighFidelity(MessageContext mc) {
boolean rc = _isHighFidelity(mc);
// If not true, check the OUT MessageContext.
// On the client, we need this setting when we receive (inbound)
// the message; however the customer set it on the outbound context.
if (!rc) {
rc = _isHighFidelity(getRelatedMessageContext(mc));
}
return rc;
}
/**
* @param mc
* @return
*/
private static MessageContext getRelatedMessageContext(MessageContext mc) {
if (log.isDebugEnabled()) {
log.debug("Enter getRelatedMessageContext for:" + mc);
}
MessageContext relatedMC = null;
if (mc != null) {
OperationContext oc = mc.getOperationContext();
if (oc != null) {
try {
relatedMC = oc.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
if (relatedMC == mc) {
relatedMC = oc.getMessageContext(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
}
} catch (AxisFault e) {
// TODO This should never occur in this scenario, swallow and continue
}
}
}
if (log.isDebugEnabled()) {
log.debug("Exit getRelatedMessageContext related messageContext is" + relatedMC);
}
return relatedMC;
}
/**
* isHighFidelity
*
* The JAX-WS engine attempts to stream data as fast as possible.
* For example, the message payload may be transformed into a JAXB object early in the processing.
* Unfortunately such transformations are lossy, some information is lost.
* An installed SOAP handler will see different namespaces (etc) then the original message.
*
* If the a customer enables the "jaxws.payload.highFidelity" flag, then lossy transformations are
* avoided until necessary.
*
* @see Constants.JAXWS_HIGH_FIDELITY
*
* @param mc
* @return true if high fidelity is requested
*/
private static boolean _isHighFidelity(MessageContext mc) {
boolean value = false;
if (mc == null) {
if (log.isDebugEnabled()) {
log.debug("_isHighFidelity returns false due to missing MessageContext");
}
return false;
}
// First examine the high fidelity flag on the context hierarchy
Boolean highFidelity = (Boolean) mc.getProperty(
org.apache.axis2.jaxws.Constants.JAXWS_PAYLOAD_HIGH_FIDELITY);
if (highFidelity != null) {
value = highFidelity.booleanValue();
if (log.isDebugEnabled()) {
log.debug("_isHighFidelity returns " + value + " per Context property " +
org.apache.axis2.jaxws.Constants.JAXWS_PAYLOAD_HIGH_FIDELITY);
}
return value;
}
// Second examine the deprecated jaxb streaming flag
Boolean jaxbStreaming = (Boolean) mc.getProperty(
org.apache.axis2.jaxws.Constants.JAXWS_ENABLE_JAXB_PAYLOAD_STREAMING);
if (jaxbStreaming != null) {
value = !jaxbStreaming.booleanValue();
if (log.isDebugEnabled()) {
log.debug("_isHighFidelity returns " + value + " per inspection of Context property " +
org.apache.axis2.jaxws.Constants.JAXWS_ENABLE_JAXB_PAYLOAD_STREAMING);
}
return value;
}
// Now look at the high fidelity parameter
Parameter p = mc.getParameter(org.apache.axis2.jaxws.Constants.JAXWS_PAYLOAD_HIGH_FIDELITY);
if (p != null) {
value = JavaUtils.isTrue(p.getValue());
if (log.isDebugEnabled()) {
log.debug("_isHighFidelity returns " + value + " per inspection of Configuration property " +
org.apache.axis2.jaxws.Constants.JAXWS_PAYLOAD_HIGH_FIDELITY);
}
return value;
}
if (log.isDebugEnabled()) {
log.debug("_isHighFidelity returns the default: false");
}
return false;
}
}
class HandlerRolePlayer implements RolePlayer {
List roles = new ArrayList();
HandlerRolePlayer(List additionalRoles) {
roles.addAll(additionalRoles);
}
public List getRoles() {
return roles;
}
public boolean isUltimateDestination() {
return false;
}
}