blob: 7b5cbc1cb3d6609ae52ce46f9746f63cdf4618c1 [file] [log] [blame]
/*
* 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.handlers.addressing;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.RolePlayer;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.AddressingConstants;
import org.apache.axis2.addressing.AddressingFaultsHelper;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.addressing.EndpointReferenceHelper;
import org.apache.axis2.addressing.RelatesTo;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.HandlerDescription;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.axis2.util.JavaUtils;
import org.apache.axis2.util.LoggingControl;
import org.apache.axis2.util.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Iterator;
public class AddressingInHandler extends AbstractHandler implements AddressingConstants {
private static final int TO_FLAG = 1, FROM_FLAG = 2, REPLYTO_FLAG = 3,
FAULTO_FLAG = 4, MESSAGEID_FLAG = 6, ACTION_FLAG = 0;
private static final Log log = LogFactory.getLog(AddressingInHandler.class);
private boolean disableRefparamExtract = false;
private AxisConfiguration configuration = null;
private RolePlayer rolePlayer = null;
public void init(HandlerDescription handlerdesc){
super.init(handlerdesc);
// check whether to process reference parameters.
Parameter param = handlerdesc.getParameter(DISABLE_REF_PARAMETER_EXTRACT);
String value = Utils.getParameterValue(param);
disableRefparamExtract = JavaUtils.isTrueExplicitly(value);
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("disableRefparamExtract=" + disableRefparamExtract);
}
}
public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
//Set the defaults on the message context.
msgContext.setProperty(DISABLE_ADDRESSING_FOR_OUT_MESSAGES, Boolean.TRUE);
msgContext.setProperty(IS_ADDR_INFO_ALREADY_PROCESSED, Boolean.FALSE);
//Determine if we want to ignore addressing headers. This parameter must
//be retrieved from the message context because it's value can vary on a
//per service basis.
Parameter disableParam = msgContext.getParameter(DISABLE_ADDRESSING_FOR_IN_MESSAGES);
String value = Utils.getParameterValue(disableParam);
if (JavaUtils.isTrueExplicitly(value)) {
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug(
"The AddressingInHandler has been disabled. No further processing will take place.");
}
return InvocationResponse.CONTINUE;
}
// if there are not headers put a flag to disable addressing temporary
SOAPHeader header = msgContext.getEnvelope().getHeader();
if (header == null) {
return InvocationResponse.CONTINUE;
}
if(configuration == null){
AxisConfiguration conf = msgContext.getConfigurationContext().getAxisConfiguration();
rolePlayer = (RolePlayer)conf.getParameterValue(Constants.SOAP_ROLE_PLAYER_PARAMETER);
configuration = conf;
}
// check whether another handler has explicitly set which addressing namespace to expect.
Iterator iterator = null;
String namespace = (String) msgContext.getProperty(WS_ADDRESSING_VERSION);
// check whether the service is configured to use a particular version of WS-Addressing,
// e.g. via JAX-WS annotations.
if (namespace == null) {
Parameter namespaceParam = msgContext.getParameter(WS_ADDRESSING_VERSION);
namespace = Utils.getParameterValue(namespaceParam);
}
if (namespace == null) {
namespace = Final.WSA_NAMESPACE;
iterator = header.getHeadersToProcess(rolePlayer, namespace);
if (!iterator.hasNext()) {
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("No headers present corresponding to " + namespace);
}
namespace = Submission.WSA_NAMESPACE;
iterator = header.getHeadersToProcess(rolePlayer, namespace);
}
}
else if (Final.WSA_NAMESPACE.equals(namespace) || Submission.WSA_NAMESPACE.equals(namespace)) {
iterator = header.getHeadersToProcess(rolePlayer, namespace);
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("The preconfigured namespace is, , " + namespace);
}
}
else {
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("The specified namespace is not supported by this handler, " + namespace);
}
return InvocationResponse.CONTINUE;
}
if (iterator.hasNext()) {
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug(namespace +
" headers present in the SOAP message. Starting to process ...");
}
//Need to set these properties here, before we extract the WS-Addressing
//information, in case we throw a fault.
msgContext.setProperty(WS_ADDRESSING_VERSION, namespace);
msgContext.setProperty(DISABLE_ADDRESSING_FOR_OUT_MESSAGES, Boolean.FALSE);
extractAddressingInformation(msgContext, iterator, namespace);
// check for reference parameters
if (!disableRefparamExtract) {
extractToEprReferenceParameters(msgContext.getTo(), header, namespace);
}
//We should only get to this point if we haven't thrown a fault.
msgContext.setProperty(IS_ADDR_INFO_ALREADY_PROCESSED, Boolean.TRUE);
}
else {
if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) {
log.debug("No headers present corresponding to " + namespace);
}
}
return InvocationResponse.CONTINUE;
}
/**
* Pull addressing headers out from the SOAP message.
*
* @param messageContext the active MessageContext
* @param headers an Iterator over the addressing headers targeted to me
* @param namespace the addressing namespace
* @throws AxisFault if an error occurs
*/
private void extractAddressingInformation(MessageContext messageContext, Iterator headers,
String namespace)
throws AxisFault {
Options messageContextOptions = messageContext.getOptions();
ArrayList duplicateHeaderNames = new ArrayList(1); // Normally will not be used for more than 1 header
ArrayList relatesToHeaders = null;
SOAPHeaderBlock actionBlock = null, toBlock = null, messageIDBlock = null, replyToBlock =
null, faultToBlock = null, fromBlock = null;
// Per the SOAP Binding spec "headers with an incorrect cardinality MUST NOT be used" So these variables
// are used to keep track of invalid cardinality headers so they are not deserialised.
boolean[] ignoreHeaders = new boolean[7];
boolean[] checkedHeaderNames = new boolean[7];
// First pass just check for duplicates
while (headers.hasNext()) {
SOAPHeaderBlock soapHeaderBlock = (SOAPHeaderBlock)headers.next();
String localName = soapHeaderBlock.getLocalName();
if (WSA_ACTION.equals(localName)) {
actionBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_ACTION, ACTION_FLAG,
checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_TO.equals(localName)) {
toBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_TO, TO_FLAG, checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_MESSAGE_ID.equals(localName)) {
messageIDBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_MESSAGE_ID, MESSAGEID_FLAG,
checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_REPLY_TO.equals(localName)) {
replyToBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_REPLY_TO, REPLYTO_FLAG,
checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_FAULT_TO.equals(localName)) {
faultToBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_FAULT_TO, FAULTO_FLAG,
checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_FROM.equals(localName)) {
fromBlock = soapHeaderBlock;
checkDuplicateHeaders(WSA_FROM, FROM_FLAG,
checkedHeaderNames, ignoreHeaders,
duplicateHeaderNames);
} else if (WSA_RELATES_TO.equals(localName)) {
if (relatesToHeaders == null) {
relatesToHeaders = new ArrayList(1);
}
relatesToHeaders.add(soapHeaderBlock);
}
}
if (actionBlock != null && !ignoreHeaders[ACTION_FLAG]) {
extractActionInformation(actionBlock, messageContext);
}
if (toBlock != null && !ignoreHeaders[TO_FLAG]) {
extractToEPRInformation(toBlock,
messageContextOptions,
namespace);
}
if (messageIDBlock != null && !ignoreHeaders[MESSAGEID_FLAG]) {
extractMessageIDInformation(messageIDBlock, messageContext);
}
if (relatesToHeaders != null) {
for (int i = 0; i < relatesToHeaders.size(); i++) {
extractRelatesToInformation((SOAPHeaderBlock) relatesToHeaders.get(i),
messageContextOptions);
}
}
if (replyToBlock != null && !ignoreHeaders[REPLYTO_FLAG]) {
extractReplyToEPRInformation(replyToBlock, namespace, messageContext);
}
if (faultToBlock != null && !ignoreHeaders[FAULTO_FLAG]) {
extractFaultToEPRInformation(faultToBlock, namespace, messageContext);
}
if (fromBlock != null && !ignoreHeaders[FROM_FLAG]) {
extractFromEPRInformation(fromBlock, namespace, messageContext);
}
// Now that all the valid wsa headers have been read, throw an exception if there was an invalid cardinality
// This means that if for example there are multiple MessageIDs and a FaultTo, the FaultTo will be respected.
if (!duplicateHeaderNames.isEmpty()) {
// Simply choose the first problem header we came across as we can only fault for one of them.
AddressingFaultsHelper.triggerInvalidCardinalityFault(messageContext,
(String) duplicateHeaderNames
.get(0));
}
// check for the presence of madatory addressing headers
checkForMandatoryHeaders(checkedHeaderNames, messageContext, namespace);
// provide default values for headers that have not been found.
setDefaults(checkedHeaderNames, messageContext, namespace);
}
private void checkForMandatoryHeaders(boolean[] alreadyFoundAddrHeader,
MessageContext messageContext,
String namespace) throws AxisFault {
if (Final.WSA_NAMESPACE.equals(namespace)) {
//Unable to validate the wsa:MessageID header here as we do not yet know which MEP
//is in effect.
// @see AddressingValidationHandler#checkMessageIDHeader
if (!alreadyFoundAddrHeader[ACTION_FLAG]) {
AddressingFaultsHelper
.triggerMessageAddressingRequiredFault(messageContext, WSA_ACTION);
}
}
else {
if (!alreadyFoundAddrHeader[TO_FLAG]) {
AddressingFaultsHelper.triggerMessageAddressingRequiredFault(messageContext, WSA_TO);
}
if (!alreadyFoundAddrHeader[ACTION_FLAG]) {
AddressingFaultsHelper
.triggerMessageAddressingRequiredFault(messageContext, WSA_ACTION);
}
if (alreadyFoundAddrHeader[REPLYTO_FLAG] ||
alreadyFoundAddrHeader[FAULTO_FLAG]) {
if (!alreadyFoundAddrHeader[MESSAGEID_FLAG]) {
AddressingFaultsHelper
.triggerMessageAddressingRequiredFault(messageContext, WSA_MESSAGE_ID);
}
}
}
}
private void setDefaults(boolean[] alreadyFoundAddrHeader, MessageContext messageContext, String namespace) {
if (Final.WSA_NAMESPACE.equals(namespace)) {
//According to the WS-Addressing spec, we should default the wsa:To header to the
//anonymous URI. Doing that, however, might prevent a different value from being
//used instead, such as the transport URL. Therefore, we only apply the default
//on the inbound response side of a synchronous request-response exchange.
if (!alreadyFoundAddrHeader[TO_FLAG] && !messageContext.isServerSide()) {
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace(messageContext.getLogIDString() +
" setDefaults: Setting WS-Addressing default value for the To property.");
}
messageContext.setTo(new EndpointReference(Final.WSA_ANONYMOUS_URL));
}
if (!alreadyFoundAddrHeader[REPLYTO_FLAG]) {
messageContext.setReplyTo(new EndpointReference(Final.WSA_ANONYMOUS_URL));
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace(messageContext.getLogIDString() +
" setDefaults: Setting WS-Addressing default value for the ReplyTo property.");
}
}
}
else {
//The none URI is not defined in the 2004/08 spec, but it is used here anyway
//as a flag to indicate the correct semantics to apply, i.e. in the 2004/08 spec
//the absence of a ReplyTo header indicates that a response is NOT required.
if (!alreadyFoundAddrHeader[REPLYTO_FLAG]) {
messageContext.setReplyTo(new EndpointReference(Final.WSA_NONE_URI));
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace(
"setDefaults: Setting WS-Addressing default value for the ReplyTo property.");
}
}
}
}
private void checkDuplicateHeaders(String addressingHeaderName, int headerFlag,
boolean[] checkedHeaderNames, boolean[] ignoreHeaders,
ArrayList duplicateHeaderNames) {//throws AxisFault {
// If the header name has been seen before then we should return true and add it to the list
// of duplicate header names. Otherwise it is the first time we've seen the header so add it
// to the checked liat and return false.
ignoreHeaders[headerFlag] = checkedHeaderNames[headerFlag];
if (ignoreHeaders[headerFlag]) {
duplicateHeaderNames.add(addressingHeaderName);
} else {
checkedHeaderNames[headerFlag] = true;
}
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("checkDuplicateHeaders: addressingHeaderName=" + addressingHeaderName
+ " isDuplicate=" + ignoreHeaders[headerFlag]);
}
}
private void extractRelatesToInformation(SOAPHeaderBlock soapHeaderBlock,
Options messageContextOptions) {
String address = soapHeaderBlock.getText();
// Extract the RelationshipType attribute if it exists
OMAttribute relationshipType =
soapHeaderBlock.getAttribute(
new QName(AddressingConstants.WSA_RELATES_TO_RELATIONSHIP_TYPE));
String relationshipTypeString =
relationshipType == null ? null : relationshipType.getAttributeValue();
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractRelatesToInformation: Extracted Relationship. Value=" + address +
" RelationshipType=" + relationshipTypeString);
}
RelatesTo relatesTo = new RelatesTo(address, relationshipTypeString);
ArrayList attributes = extractAttributesFromSOAPHeaderBlock(soapHeaderBlock);
relatesTo.setExtensibilityAttributes(attributes);
messageContextOptions.addRelatesTo(relatesTo);
// Completed processing of this header
soapHeaderBlock.setProcessed();
}
private void extractFaultToEPRInformation(SOAPHeaderBlock soapHeaderBlock,
String addressingNamespace,
MessageContext messageContext) throws AxisFault {
Options messageContextOptions = messageContext.getOptions();
EndpointReference epr = messageContextOptions.getFaultTo();
if (epr == null) {
epr = new EndpointReference("");
messageContextOptions.setFaultTo(epr);
}
extractEPRInformation(soapHeaderBlock, epr, addressingNamespace, messageContext);
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractFaultToEPRInformation: Extracted FaultTo EPR: " + epr);
}
soapHeaderBlock.setProcessed();
}
private void extractReplyToEPRInformation(SOAPHeaderBlock soapHeaderBlock,
String addressingNamespace,
MessageContext messageContext) throws AxisFault {
Options messageContextOptions = messageContext.getOptions();
EndpointReference epr = messageContextOptions.getReplyTo();
if (epr == null) {
epr = new EndpointReference("");
messageContextOptions.setReplyTo(epr);
}
extractEPRInformation(soapHeaderBlock, epr, addressingNamespace, messageContext);
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractReplyToEPRInformation: Extracted ReplyTo EPR: " + epr);
}
soapHeaderBlock.setProcessed();
}
private void extractFromEPRInformation(SOAPHeaderBlock soapHeaderBlock,
String addressingNamespace,
MessageContext messageContext) throws AxisFault {
Options messageContextOptions = messageContext.getOptions();
EndpointReference epr = messageContextOptions.getFrom();
if (epr == null) {
epr = new EndpointReference(
""); // I don't know the address now. Let me pass the empty string now and fill this
// once I process the Elements under this.
messageContextOptions.setFrom(epr);
}
extractEPRInformation(soapHeaderBlock, epr, addressingNamespace, messageContext);
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractFromEPRInformation: Extracted From EPR: " + epr);
}
soapHeaderBlock.setProcessed();
}
private void extractToEPRInformation(SOAPHeaderBlock soapHeaderBlock,
Options messageContextOptions, String namespace) {
EndpointReference epr;
//here the addressing epr overidde what ever already there in the message context
epr = new EndpointReference(soapHeaderBlock.getText());
messageContextOptions.setTo(epr);
// check for address attributes
Iterator addressAttributes = soapHeaderBlock.getAllAttributes();
if (addressAttributes != null && addressAttributes.hasNext()) {
ArrayList attributes = new ArrayList();
while (addressAttributes.hasNext()) {
OMAttribute attr = (OMAttribute) addressAttributes.next();
attributes.add(attr);
}
epr.setAddressAttributes(attributes);
}
soapHeaderBlock.setProcessed();
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractToEPRInformation: Extracted To EPR: " + epr);
}
}
private void extractToEprReferenceParameters(EndpointReference toEPR, SOAPHeader header,
String namespace) {
if (Final.WSA_NAMESPACE.equals(namespace)) {
Iterator headerBlocks = header.getChildElements();
while (headerBlocks.hasNext()) {
OMElement headerElement = (OMElement)headerBlocks.next();
OMAttribute isRefParamAttr =
headerElement.getAttribute(new QName(namespace, "IsReferenceParameter"));
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractToEprReferenceParameters: Checking header: " +
headerElement.getQName());
}
if (isRefParamAttr != null && "true".equals(isRefParamAttr.getAttributeValue())) {
toEPR.addReferenceParameter(headerElement);
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractToEprReferenceParameters: Header: " +
headerElement.getQName() +
" has IsReferenceParameter attribute. Adding to toEPR.");
}
}
}
}
else {
// there is no exact way to identify ref parameters for Submission version. So let's have a handler
// at the end of the flow, which puts all the handlers (which are of course mustUnderstand=false)
// as reference parameters
// TODO : Chinthaka
}
}
//We assume that any action that already exists in the message context must be the
//soapaction. We compare that action to the WS-Addressing action, and if they are
//different we throw a fault.
private void extractActionInformation(SOAPHeaderBlock soapHeaderBlock,
MessageContext messageContext)
throws AxisFault {
Options messageContextOptions = messageContext.getOptions();
String soapAction = messageContextOptions.getAction();
String wsaAction = soapHeaderBlock.getText();
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace("extractActionInformation: soapAction='" + soapAction + "' wsa:Action='" +
wsaAction + "'");
}
// Need to validate that the content of the wsa:Action header is not null or whitespace
if ((wsaAction == null) || "".equals(wsaAction.trim())) {
AddressingFaultsHelper.triggerActionNotSupportedFault(messageContext, wsaAction);
}
// The isServerSide check is because the underlying Options object is
// shared between request and response MessageContexts for Sync
// invocations. If the soapAction is set outbound and a wsa:Action is
// received on the response they will differ (because there is no
// SOAPAction header on an HTTP response). In this case we should not
// check that soapAction==wsa:Action
if (soapAction != null && !"".equals(soapAction) && messageContext.isServerSide()) {
if (!soapAction.equals(wsaAction)) {
AddressingFaultsHelper.triggerActionMismatchFault(messageContext, soapAction, wsaAction);
}
} else {
messageContextOptions.setAction(wsaAction);
}
ArrayList attributes = extractAttributesFromSOAPHeaderBlock(soapHeaderBlock);
if (attributes != null) {
messageContext.setProperty(AddressingConstants.ACTION_ATTRIBUTES, attributes);
}
soapHeaderBlock.setProcessed();
}
private void extractMessageIDInformation(SOAPHeaderBlock soapHeaderBlock,
MessageContext messageContext) throws AxisFault {
messageContext.getOptions().setMessageId(soapHeaderBlock.getText());
ArrayList attributes = extractAttributesFromSOAPHeaderBlock(soapHeaderBlock);
if (attributes != null) {
messageContext.setProperty(AddressingConstants.MESSAGEID_ATTRIBUTES, attributes);
}
soapHeaderBlock.setProcessed();
}
/**
* Given the soap header block, this should extract the information within EPR.
*
* @param headerBlock a SOAP header which is of type EndpointReference
* @param epr the EndpointReference to fill in with the extracted data
* @param addressingNamespace the WSA namespace URI
* @param messageContext the active MessageContext
* @throws AxisFault if there is a problem
*/
private void extractEPRInformation(SOAPHeaderBlock headerBlock, EndpointReference epr,
String addressingNamespace, MessageContext messageContext)
throws AxisFault {
String namespace = null;
try {
namespace = EndpointReferenceHelper.fromOM(epr, headerBlock);
} catch (AxisFault af) {
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace(
"extractEPRInformation: Exception occurred deserialising an EndpointReference.",
af);
}
AddressingFaultsHelper
.triggerMissingAddressInEPRFault(messageContext, headerBlock.getLocalName());
}
//Check that the EPR has the correct namespace.
if (!namespace.equals(addressingNamespace)) {
if (LoggingControl.debugLoggingAllowed && log.isTraceEnabled()) {
log.trace(
"extractEPRInformation: Addressing namespace = " + addressingNamespace +
", EPR namespace = " + namespace);
}
AddressingFaultsHelper
.triggerInvalidEPRFault(messageContext, headerBlock.getLocalName());
}
}
private ArrayList extractAttributesFromSOAPHeaderBlock(SOAPHeaderBlock soapHeaderBlock) {
Iterator actionAttributes = soapHeaderBlock.getAllAttributes();
if (actionAttributes != null && actionAttributes.hasNext()) {
ArrayList attributes = new ArrayList();
while (actionAttributes.hasNext()) {
OMAttribute attr = (OMAttribute) actionAttributes.next();
attributes.add(attr);
}
return attributes;
}
return null;
}
}