com.cordys.coe.ac.httpconnector.impl.StandardRequestHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.cordys.coe.ac.httpconnector.impl.StandardRequestHandler.java

Source

/**
 * Copyright 2006 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys HTTP Connector. 
 *
 * 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
 * 
 * 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 com.cordys.coe.ac.httpconnector.impl;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;

import com.cordys.coe.ac.httpconnector.IRequestHandler;
import com.cordys.coe.ac.httpconnector.config.IMethodConfiguration;
import com.cordys.coe.ac.httpconnector.config.IServerConnection;
import com.cordys.coe.ac.httpconnector.config.IXSLTStore;
import com.cordys.coe.ac.httpconnector.exception.ConnectorException;
import com.cordys.coe.ac.httpconnector.exception.HandlerException;
import com.cordys.coe.ac.httpconnector.exception.HandlerExceptionMessages;
import com.cordys.coe.ac.httpconnector.utils.Utils;
import com.cordys.coe.ac.httpconnector.utils.XmlUtils;
import com.cordys.coe.util.xml.nom.XPathHelper;
import com.eibus.util.logger.CordysLogger;
import com.eibus.xml.nom.Node;
import com.eibus.xml.xpath.XPath;
import com.eibus.xml.xpath.XPathMetaInfo;
import com.eibus.xml.xpath.XSLT;

/**
 * Standard HTTP connector request handler. This will execute the XSLT
 * transformation, if configured
 * 
 * @author mpoyhone
 */
public class StandardRequestHandler implements IRequestHandler {
    /**
     * Holds the name of the tag 'xslt'.
     */
    private static final String TAG_XSLT = "xslt";
    /**
     * Holds the name of the tag 'root-xpath'.
     */
    private static final String TAG_ROOT_XPATH = "root-xpath";
    /**
     * Holds the name of the tag 'remove-namespaces'.
     */
    private static final String TAG_REMOVE_NAMESPACES = "remove-namespaces";
    /**
     * Holds the name of the tag 'namespace-uri'.
     */
    private static final String TAG_NAMESPACE_URI = "namespace-uri";
    /**
     * Holds the name of the custom request header.
     */
    private static final String TAG_REQ_HEADERS = "req-headers";
    /**
     * Holds the name of the tag 'header'.
     */
    private static final String TAG_REQ_HEADER = "header";
    /**
     * Logger for log messages from this class.
     */
    private static final CordysLogger LOG = CordysLogger.getCordysLogger(StandardRequestHandler.class);
    /**
     * Default content type for the HTTP request.
     */
    protected static final String DEFAULT_CONTENT_TYPE = "text/xml; charset=UTF-8";
    /**
     * Contains the method configuration to which this handler is attached to.
     */
    protected IMethodConfiguration m_method;
    /**
     * If <code>true</code> all namespace definitions are removed.
     */
    private boolean m_removeAllNamespaces;
    /**
     * Contains namespace URI's which will be removed from the final XML.
     */
    private Set<String> m_removeNamespaceUriSet;
    /**
     * Contains an optional request root element selection XPath expression.
     */
    private XPath m_requestRootXPath;

    private IXSLTStore xslStore;

    private int xsltNode;

    private XSLT xslt;

    boolean isInXMLStore = false;

    /**
     * Contains request headers that are read from the method implementation.
     */
    private RequestHeader[] requestHeadersArray = null;

    /**
     * @see IRequestHandler#initialize(int, IXSLTStore, IMethodConfiguration,
     *      XPathMetaInfo)
     */
    @Override
    public void initialize(int configXml, IXSLTStore xslStore, IMethodConfiguration method, XPathMetaInfo xmi)
            throws HandlerException {
        this.xslStore = xslStore;
        m_method = method;

        int tmpNode;

        // Read the XSLT information.
        if ((tmpNode = XPathHelper.selectSingleNode(configXml, "ns:" + TAG_XSLT, xmi)) != 0) {
            xsltNode = Node.duplicate(tmpNode);
            if (Node.getAttribute(xsltNode, "file", null) == null
                    && Node.getAttribute(xsltNode, "xmlstore", null) != null) {
                isInXMLStore = true;
            }
        }

        // Read request root element XPath
        if ((tmpNode = XPathHelper.selectSingleNode(configXml, "ns:" + TAG_ROOT_XPATH, xmi)) != 0) {
            String value = Node.getDataWithDefault(tmpNode, "");

            if (value.length() > 0) {
                m_requestRootXPath = XPath.getXPathInstance(value);
            }
        }

        // Read namespaces to be removed from the request.
        if ((tmpNode = XPathHelper.selectSingleNode(configXml, "ns:" + TAG_REMOVE_NAMESPACES, xmi)) != 0) {
            int[] nodes = XPathHelper.selectNodes(tmpNode, "ns:" + TAG_NAMESPACE_URI, xmi);

            if ((nodes != null) && (nodes.length > 0)) {
                for (int node : nodes) {
                    String uri = Node.getDataWithDefault(node, "");

                    if (uri.length() > 0) {
                        if ("*".equals(uri)) {
                            m_removeAllNamespaces = true;
                            m_removeNamespaceUriSet = null;
                            break;
                        }

                        if (m_removeNamespaceUriSet == null) {
                            m_removeNamespaceUriSet = new HashSet<String>();
                        }

                        m_removeNamespaceUriSet.add(uri);
                    }
                }
            }
        }
        initalizeRequestHeaders(configXml, xmi);
    }

    private void initalizeRequestHeaders(int configXml, XPathMetaInfo xmi) {
        int[] params = XPathHelper.selectNodes(configXml, "ns:" + TAG_REQ_HEADERS + "/ns:" + TAG_REQ_HEADER, xmi);
        requestHeadersArray = new RequestHeader[params.length];
        for (int i = 0; i < params.length; i++) {
            int paramNode = params[i];
            RequestHeader p = new RequestHeader();
            p.name = Node.getAttribute(paramNode, "name", "");
            p.value = Node.getDataWithDefault(paramNode, "");
            requestHeadersArray[i] = p;
        }

    }

    /**
     * @see IRequestHandler#process(int, IServerConnection, HttpClient)
     */
    @Override
    public HttpMethod process(int requestNode, IServerConnection connection, HttpClient httpClient)
            throws HandlerException {
        String uri = getRequestUri(requestNode, connection, httpClient);
        EntityEnclosingMethod httpMethod;

        if (LOG.isDebugEnabled()) {
            LOG.debug("HTTP method is: " + m_method.getHttpMethodType());
        }

        switch (m_method.getHttpMethodType()) {
        case GET:
            HttpMethod httpMethodGet = new GetMethod(uri); // Get method does not have a body. 
            setRequestHeaders(httpMethodGet);
            return httpMethodGet;

        case POST:
            httpMethod = new PostMethod(uri);
            break;

        case PUT:
            httpMethod = new PutMethod(uri);
            break;

        case DELETE:
            HttpMethod httpMethodDelete = new DeleteMethod(uri); // Delete method does not have a body.
            setRequestHeaders(httpMethodDelete);
            return httpMethodDelete;

        default:
            throw new HandlerException(HandlerExceptionMessages.UNKNOWN_HTTP_METHOD);
        }

        int reqNode = requestNode;

        try {
            reqNode = preProcessXml(reqNode, reqNode != requestNode);

            if (xsltNode != 0) {
                reqNode = executeXslt(reqNode, m_method, reqNode != requestNode);
            }

            if (m_requestRootXPath != null) {
                reqNode = handleRequestXPath(reqNode, m_method, reqNode != requestNode);
            }

            if ((m_removeNamespaceUriSet != null) || m_removeAllNamespaces) {
                XmlUtils.removeNamespacesRecursively(reqNode, m_removeNamespaceUriSet);
            }

            reqNode = postProcessXml(reqNode, reqNode != requestNode);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Final Request XML: " + Node.writeToString(reqNode, true));
            }

            // Get the data that should be posted.
            byte[] reqData = getPostData(reqNode, connection);
            String contentType = getContentType();

            httpMethod.setRequestEntity(new ByteArrayRequestEntity(reqData, contentType));

            if (LOG.isDebugEnabled()) {
                LOG.debug("Sending data: " + new String(reqData));
            }

            httpMethod.setRequestHeader("Content-type", contentType);
            setRequestHeaders(httpMethod);
        } finally {
            if ((reqNode != 0) && (reqNode != requestNode)) {
                Node.delete(reqNode);
                reqNode = 0;
            }
        }

        return httpMethod;
    }

    private void setRequestHeaders(HttpMethod httpMethod) {
        for (RequestHeader requestHeader : requestHeadersArray) {
            httpMethod.setRequestHeader(requestHeader.name, String.valueOf(requestHeader.value));
        }
    }

    /**
     * Converts the XML structure into a byte array.
     * 
     * @param requestNode
     *            node XML node to be converted.
     * 
     * @return A byte array containing the XML data.
     * 
     * @throws HandlerException
     */
    protected byte[] convertXmlToBytes(int requestNode) throws HandlerException {
        String xmlStr = Node.writeToString(requestNode, false);

        try {
            return xmlStr.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new HandlerException(e,
                    HandlerExceptionMessages.UNABLE_TO_CONVERT_THE_XML_STRING_TO_A_BYTE_ARRAY);
        }
    }

    /**
     * Executes the XSLT transformation configured for this handler.
     * 
     * @param requestNode
     *            Request node to be transformed.
     * @param method
     *            Current method configuration.
     * @param mustDelete
     *            If <code>true</code> the parameter node must be deleted, if a
     *            new XML node is returned. Otherwise the node cannot be
     *            deleted.
     * 
     * @return Transformed XML.
     * @throws HandlerException
     */
    protected int executeXslt(int requestNode, IMethodConfiguration method, boolean mustDelete)
            throws HandlerException {
        XSLT xslt = this.xslt;
        try {
            if (xslt == null) {
                xslt = xslStore.loadXslt(xsltNode);
                if (!isInXMLStore) {
                    this.xslt = xslt;
                }
            }
        } catch (ConnectorException e) {
            throw new HandlerException(e, e.getMessageObject(), e.getMessageParameters());
        }
        int newNode = xslt.xslTransform(requestNode);

        if (LOG.isDebugEnabled()) {
            LOG.debug("Request XML after XSLT transformation: " + Node.writeToString(newNode, true));
        }

        if (mustDelete) {
            Node.delete(requestNode);
            requestNode = 0;
        }

        return newNode;
    }

    /**
     * Returns the HTTP request content type.
     * 
     * @return Content type.
     */
    protected String getContentType() {
        return DEFAULT_CONTENT_TYPE;
    }

    /**
     * This method returns the data that should be put into the post data.
     * 
     * @param requestNode
     *            The current request XML.
     * @param connection
     *            The current server connection.
     * 
     * @return The byte data to post.
     * 
     * @throws HandlerException
     *             In case of any exceptions.
     */
    protected byte[] getPostData(int requestNode, IServerConnection connection) throws HandlerException {
        return convertXmlToBytes(requestNode);
    }

    /**
     * Returns the URI for this request.
     * 
     * @param requestNode
     *            Incoming request XML node.
     * @param connection
     *            Current connection.
     * @param httpClient
     *            HTTP client object.
     * 
     * @return Request URI. This cannot be <code>null</code>.
     * 
     * @throws HandlerException
     *             In case of any exceptions.
     */
    protected String getRequestUri(int requestNode, IServerConnection connection, HttpClient httpClient)
            throws HandlerException {
        String uri = m_method.getUri();

        if ((uri == null) || (uri.length() == 0)) {
            URL url = connection.getUrl();
            uri = Utils.getUrlPath(url);
        }

        return uri;
    }

    /**
     * Fetches the request element using the configured XPath.
     * 
     * @param requestNode
     *            Request node to be transformed.
     * @param method
     *            Current method configuration.
     * @param mustDelete
     *            If <code>true</code> the parameter node must be deleted, if a
     *            new XML node is returned. Otherwise the node cannot be
     *            deleted.
     * 
     * @return Transformed XML.
     */
    protected int handleRequestXPath(int requestNode, IMethodConfiguration method, boolean mustDelete) {
        int node = m_requestRootXPath.firstMatch(requestNode, method.getXPathMetaInfo());

        if (node != 0) {
            node = Node.unlink(node);

            if (mustDelete) {
                Node.delete(requestNode);
            }

            return node;
        }

        return requestNode;
    }

    /**
     * Method which can do processing of the request XML after the XSLT
     * transform is executed. This implementation just returns the node as-is.
     * 
     * @param reqNode
     *            Request XML node.
     * @param mustDelete
     *            If <code>true</code> the parameter node must be deleted, if a
     *            new XML node is returned. Otherwise the node cannot be
     *            deleted.
     * 
     * @return Processed XML.
     * 
     * @throws HandlerException
     *             in case of any errors.
     */
    protected int postProcessXml(int reqNode, boolean mustDelete) throws HandlerException {
        return reqNode;
    }

    /**
     * Method which can do processing of the request XML before the XSLT
     * transform is called. This implementation returns the first child element
     * of the request XML as the root is the SOAP request method node.
     * 
     * @param reqNode
     *            Request XML node.
     * @param mustDelete
     *            If <code>true</code> the parameter node must be deleted, if a
     *            new XML node is returned. Otherwise the node cannot be
     *            deleted.
     * 
     * @return Processed XML.
     * 
     * @throws HandlerException
     *             In case of any errors.
     */
    protected int preProcessXml(int reqNode, boolean mustDelete) throws HandlerException {
        return reqNode;
    }

    private static class RequestHeader {
        /**
         * Holds the type for the parameter.
         */
        private String name;
        /**
         * Holds the value if the parameter is fixed.
         */
        private Object value;
    }
}