com.alliander.osgp.adapter.ws.endpointinterceptors.WebServiceMonitorInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.alliander.osgp.adapter.ws.endpointinterceptors.WebServiceMonitorInterceptor.java

Source

/**
 * Copyright 2015 Smart Society Services B.V.
 *
 * Licensed 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
 */
package com.alliander.osgp.adapter.ws.endpointinterceptors;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.SoapMessage;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import com.alliander.osgp.adapter.ws.infra.jms.LoggingMessageSender;
import com.alliander.osgp.adapter.ws.infra.jms.LoggingRequestMessage;
import com.alliander.osgp.domain.core.exceptions.WebServiceMonitorInterceptorException;

@Transactional(value = "transactionManager")
public class WebServiceMonitorInterceptor implements EndpointInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebServiceMonitorInterceptor.class);

    @Autowired
    @Qualifier("loggingMessageSender")
    private LoggingMessageSender loggingMessageSender;

    private static final String CLASS_NAME = "className";
    private static final String METHOD_NAME = "methodName";
    private static final String CORRELATION_UID = "correlationUid";
    private static final String DEVICE_ID = "deviceId";
    private static final String RESPONSE_RESULT = "result";
    private static final String RESPONSE_DATA_SIZE = "dataSize";

    private static final String XML_ELEMENT_CORRELATION_UID = "CorrelationUid";
    private static final String XML_ELEMENT_DEVICE_ID = "DeviceId";
    private static final String XML_ELEMENT_OSP_RESULT_TYPE = "Result";

    private static final String FAULT_RESPONSE_RESULT = "SOAP_FAULT";

    private final String organisationIdentification;
    private final String userName;
    private final String applicationName;

    public WebServiceMonitorInterceptor(final String organisationIdentification, final String userName,
            final String applicationName) {
        this.organisationIdentification = organisationIdentification;
        this.userName = userName;
        this.applicationName = applicationName;
    }

    @Override
    public boolean handleRequest(final MessageContext messageContext, final Object endpoint) throws Exception {
        // This method is not used, but is part of the EndpointInterceptor
        // interface.
        return true;
    }

    @Override
    public boolean handleResponse(final MessageContext messageContext, final Object endpoint) throws Exception {

        final LoggingRequestMessage loggingRequestMessage = this.createLoggingRequestMessage(messageContext,
                endpoint);
        this.loggingMessageSender.send(loggingRequestMessage);

        return true;
    }

    @Override
    public boolean handleFault(final MessageContext messageContext, final Object endpoint) throws Exception {

        final LoggingRequestMessage loggingRequestMessage = this.createLoggingRequestMessage(messageContext,
                endpoint);
        loggingRequestMessage.setResponseResult(FAULT_RESPONSE_RESULT);
        this.loggingMessageSender.send(loggingRequestMessage);

        return true;
    }

    @Override
    public void afterCompletion(final MessageContext messageContext, final Object endpoint, final Exception ex)
            throws Exception {
        // This method is not used, but is part of the EndpointInterceptor
        // interface.
    }

    /**
     * Get the class name and method name from the end point parameter.
     *
     * @param endpoint
     *            Object representing the end point class.
     *
     * @return Map containing CLASS_NAME and METHOD_NAME.
     */
    private Map<String, String> getEndPointClassAndMethod(final Object endpoint) {
        final MethodEndpoint method = (MethodEndpoint) endpoint;
        final String className = method.getBean().toString();
        final String methodName = method.getMethod().getName();

        final Map<String, String> classAndMethod = new HashMap<String, String>(2);
        classAndMethod.put(CLASS_NAME, className.split("@")[0]);
        classAndMethod.put(METHOD_NAME, methodName + "()");

        return classAndMethod;
    }

    /**
     * Get a value for a valueName from the soap header.
     *
     * @param soapHeader
     *            The soap header.
     *
     * @return The value, or an empty string if not found.
     */
    private String getHeaderValue(final SoapHeader soapHeader, final String valueName) {
        String value = "";
        final Iterator<SoapHeaderElement> iterator = soapHeader.examineAllHeaderElements();

        while (iterator.hasNext()) {
            final SoapHeaderElement element = iterator.next();

            if (element.getName().getLocalPart().equals(valueName)) {
                value = element.getText();
                break;
            }
        }

        return value;
    }

    /**
     * Try to find the XML_ELEMENT_CORRELATION_UID, XML_ELEMENT_DEVICE_ID and
     * the XML_ELEMENT_OSP_RESULT_TYPE from the soap message. Note that these
     * elements are not always present in the soap message. In that case, the
     * values will be null. Also, determine the data size of the response.
     *
     * @param soapMessage
     *            The soap message.
     *
     * @return Map containing CORRELATION_UID, DEVICE_ID, RESPONSE_RESULT and
     *         RESPONSE_DATA_SIZE.
     */
    private Map<String, Object> parseSoapMessage(final SoapMessage soapMessage) {
        try {
            // Determine the data size of the message (stream).
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            soapMessage.writeTo(outputStream);
            final int dataSize = outputStream.size();

            // final String message = new String(outputStream.toByteArray());
            // LOGGER.info("soap message: {}", message);

            // Try to find the desired XML elements in the document.
            final Document document = soapMessage.getDocument();
            final String correlationUid = this.evaluateXPathExpression(document, XML_ELEMENT_CORRELATION_UID);
            final String deviceId = this.evaluateXPathExpression(document, XML_ELEMENT_DEVICE_ID);
            final String result = this.evaluateXPathExpression(document, XML_ELEMENT_OSP_RESULT_TYPE);

            // Create the Map containing the output.
            final Map<String, Object> map = new HashMap<String, Object>(4);
            map.put(CORRELATION_UID, correlationUid);
            map.put(DEVICE_ID, deviceId);
            map.put(RESPONSE_RESULT, result);
            map.put(RESPONSE_DATA_SIZE, dataSize);

            return map;
        } catch (final Exception e) {
            LOGGER.error("failed to parse soap message or to execute xPath expression", e);
            return null;
        }
    }

    /**
     * Search an XML element using an XPath expression.
     *
     * @param document
     *            The XML document.
     * @param element
     *            The name of the desired XML element.
     *
     * @return The content of the XML element, or null if the element is not
     *         found.
     *
     * @throws XPathExpressionException
     *             In case the expression fails to compile or evaluate, an
     *             exception will be thrown.
     */
    private String evaluateXPathExpression(final Document document, final String element)
            throws XPathExpressionException {
        final String expression = String.format("//*[contains(local-name(), '%s')]", element);

        final XPathFactory xFactory = XPathFactory.newInstance();
        final XPath xPath = xFactory.newXPath();

        final XPathExpression xPathExpression = xPath.compile(expression);
        final NodeList nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);

        if (nodeList != null && nodeList.getLength() > 0) {
            return nodeList.item(0).getTextContent();
        } else {
            return null;
        }
    }

    /**
     * Create the logging Request Message.
     *
     * @param messageContext
     *            The messageContext.
     * @param endpoint
     *            The endpoint.
     *
     * @return The loggingRequestMessage.
     *
     * @throws WebServiceMonitorInterceptorException
     */
    private LoggingRequestMessage createLoggingRequestMessage(final MessageContext messageContext,
            final Object endpoint) throws WebServiceMonitorInterceptorException {
        // Get the current time.
        final Date now = new Date();

        // Get EndPointClass and EndPointMethod.
        final Map<String, String> classAndMethod = this.getEndPointClassAndMethod(endpoint);

        // Get the request.
        Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
        final SoapMessage request = (SoapMessage) messageContext.getRequest();
        this.printSoapMessage(request);

        final SoapHeader soapHeader = request.getSoapHeader();

        // Read OrganisationIdentification from header from request.
        final String orgIdentification = this.getHeaderValue(soapHeader, this.organisationIdentification);

        // Read UserName from header from request.
        final String usrName = this.getHeaderValue(soapHeader, this.userName);

        // Read ApplicationName from header from request.
        final String appName = this.getHeaderValue(soapHeader, this.applicationName);

        // Read correlationUid and deviceId from request.
        final Map<String, Object> requestData = this.parseSoapMessage(request);

        if (requestData == null) {
            throw new WebServiceMonitorInterceptorException(
                    "unable to get correlationUid or deviceId from request");
        }

        // Get the response.
        Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
        final SoapMessage response = (SoapMessage) messageContext.getResponse();
        this.printSoapMessage(response);

        // Read correlationUid and deviceId and result and data size from
        // response.
        final Map<String, Object> responseData = this.parseSoapMessage(response);

        if (responseData == null) {
            throw new WebServiceMonitorInterceptorException(
                    "unable to get correlationUid or deviceId or result from response");
        }

        // Check response for correlationId, otherwise request
        String correlationId = (String) responseData.get(CORRELATION_UID);
        if (StringUtils.isEmpty(correlationId)) {
            correlationId = (String) requestData.get(CORRELATION_UID);
        }

        // Creating the logging request message
        return new LoggingRequestMessage(now, orgIdentification, usrName, appName, classAndMethod.get(CLASS_NAME),
                classAndMethod.get(METHOD_NAME), (String) requestData.get(DEVICE_ID), correlationId,
                (String) responseData.get(RESPONSE_RESULT), (int) responseData.get(RESPONSE_DATA_SIZE));
    }

    /**
     * Print a soap message.
     * 
     * @param soapMessage
     *            The message to print.
     */
    private void printSoapMessage(final SoapMessage soapMessage) {
        try {
            final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            soapMessage.writeTo(outputStream);
            final String message = new String(outputStream.toByteArray());
            LOGGER.info("soap message: {}", message);
        } catch (final IOException e) {
            LOGGER.error("Unexpected error while writing soap message", e);
        }
    }
}