org.cleverbus.core.common.ws.HeaderAndPayloadValidatingInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for org.cleverbus.core.common.ws.HeaderAndPayloadValidatingInterceptor.java

Source

/*
 * Copyright (C) 2015
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.cleverbus.core.common.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;

import org.cleverbus.api.exception.InternalErrorEnum;
import org.cleverbus.api.exception.ValidationIntegrationException;

import org.springframework.util.ObjectUtils;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.SoapFaultDetailElement;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
 * Interceptor that validates the presence of trace header.
 * <p/>
 * When the payload is invalid, this interceptor stops processing of the interceptor chain. Additionally, if the message
 * is a SOAP request message, a SOAP Fault is created as reply. Invalid SOAP responses do not result in a fault.
 *
 * @author <a href="mailto:petr.juza@cleverlance.com">Petr Juza</a>
 */
public class HeaderAndPayloadValidatingInterceptor extends PayloadValidatingInterceptor {

    private static final String DEFAULT_FAULT_HEADER_REASON = InternalErrorEnum.E104.getErrorCode() + ": "
            + InternalErrorEnum.E104.getErrDesc();

    public static QName TRACE_HEADER_ELM = new QName("http://cleverbus.org/ws/Common-v1", "traceHeader");

    private boolean validateHeader = true;

    private String faultHeaderStringOrReason = DEFAULT_FAULT_HEADER_REASON;

    private Set<QName> ignoreRequests;

    @Override
    public boolean handleRequest(MessageContext messageContext, Object endpoint)
            throws IOException, SAXException, TransformerException {

        // validate payload
        boolean reqValResult = super.handleRequest(messageContext, endpoint);

        // validate header
        if (reqValResult && validateHeader && !ignoreRequest(messageContext)) {
            SaajSoapMessage soapMessage = (SaajSoapMessage) messageContext.getRequest();
            SoapHeader soapHeader = soapMessage.getSoapHeader();

            ValidationIntegrationException[] errors = validateHeader(soapHeader);
            if (!ObjectUtils.isEmpty(errors)) {
                return handleHeaderValidationErrors(messageContext, errors);
            } else if (logger.isDebugEnabled()) {
                logger.debug("Request header validated");
            }
        }

        return reqValResult;
    }

    /**
     * Checks if input request should be ignored from header checking.
     *
     * @param messageContext the msg context
     * @return {@code true} when input request should be ignored, otherwise {@code false}
     */
    private boolean ignoreRequest(MessageContext messageContext) {
        Node reqNode = ((DOMSource) messageContext.getRequest().getPayloadSource()).getNode();
        QName reqName = new QName(reqNode.getNamespaceURI(), reqNode.getLocalName());
        return ignoreRequests.contains(reqName);
    }

    /**
     * Validate SOAP header - check existence of trace header.
     *
     * @param soapHeader the SOAP header
     * @return array of possible validation errors
     */
    private ValidationIntegrationException[] validateHeader(SoapHeader soapHeader) {
        List<ValidationIntegrationException> errors = new ArrayList<ValidationIntegrationException>();

        boolean headerFound = false;
        if (soapHeader != null) {
            // iterate over header elements
            Iterator<SoapHeaderElement> itElements = soapHeader.examineAllHeaderElements();
            while (!headerFound && itElements.hasNext()) {
                SoapHeaderElement elm = itElements.next();
                if (TRACE_HEADER_ELM.equals(elm.getName())) {
                    headerFound = true;
                }
            }
        }

        if (!headerFound) {
            errors.add(new ValidationIntegrationException("there is no header element: " + TRACE_HEADER_ELM));
        }

        return errors.toArray(new ValidationIntegrationException[errors.size()]);
    }

    /**
     * Template method that is called when the request SOAP headers contains validation errors.
     * Default implementation logs all errors, and returns <code>false</code>, i.e. do not process the request.
     *
     * @param messageContext the message context
     * @param errors         the validation errors
     * @return <code>true</code> to continue processing the request, <code>false</code> (the default) otherwise
     */
    protected boolean handleHeaderValidationErrors(MessageContext messageContext,
            ValidationIntegrationException[] errors) throws TransformerException {

        for (ValidationIntegrationException error : errors) {
            logger.warn("XML validation error on request: " + error.getMessage());
        }

        if (messageContext.getResponse() instanceof SoapMessage) {
            SoapMessage response = (SoapMessage) messageContext.getResponse();
            SoapBody body = response.getSoapBody();
            SoapFault fault = body.addClientOrSenderFault(faultHeaderStringOrReason,
                    getFaultStringOrReasonLocale());

            if (getAddValidationErrorDetail()) {
                SoapFaultDetail detail = fault.addFaultDetail();
                for (ValidationIntegrationException error : errors) {
                    SoapFaultDetailElement detailElement = detail.addFaultDetailElement(getDetailElementName());
                    detailElement.addText(error.getMessage());
                }
            }
        }

        return false;
    }

    /**
     * Sets whether validate SOAP header.
     *
     * @param validateHeader {@code true} for validation, otherwise {@code false}
     */
    public void setValidateHeader(boolean validateHeader) {
        this.validateHeader = validateHeader;
    }

    /**
     * Sets fault reason when there is no requested header.
     *
     * @param faultHeaderStringOrReason the fault message
     */
    public void setFaultHeaderStringOrReason(String faultHeaderStringOrReason) {
        this.faultHeaderStringOrReason = faultHeaderStringOrReason;
    }

    /**
     * Sets request root element names which will be ignored from trace header checking.
     *
     * @param ignoreRequests the array of element names, e.g.
     *                       {@code {http://cleverbus.org/ws/SubscriberService-v1}getCounterDataRequest }
     */
    public void setIgnoreRequests(Collection<String> ignoreRequests) {
        this.ignoreRequests = new HashSet<QName>();
        for (String ignoreRequest : ignoreRequests) {
            this.ignoreRequests.add(QName.valueOf(ignoreRequest));
        }
    }
}