com.legstar.c2ws.servlet.C2wsProxy.java Source code

Java tutorial

Introduction

Here is the source code for com.legstar.c2ws.servlet.C2wsProxy.java

Source

/*******************************************************************************
 * Copyright (c) 2010 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.c2ws.servlet;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.legstar.proxy.invoke.ProxyConfigurationException;
import com.legstar.proxy.invoke.ProxyInvokerException;
import com.legstar.proxy.invoke.ServiceProxy;

/**
 * This proxy servlet receives HTTP requests and hand over control to
 * {@link com.legstar.proxy.invoke.ServiceProxy} which performs the
 * actual call to a remote process.
 * <p/>
 * The web.xml configuration file must provide all the necessary parameters
 * for ServiceProxy to perform its task.
 * <p/>
 * The servlet does not have any knowledge of the binding types or target
 * service. It is only concerned with HTTP transport to/from a mainframe.
 *
 */
public class C2wsProxy extends javax.servlet.http.HttpServlet {

    /** The serial ID. */
    static final long serialVersionUID = 1L;

    /** The old content-type supported by this class. */
    private static final String BINARY_CONTENT_TYPE = "binary/octet-stream";

    /** This content type is more easily understood by regular web servers.
     * Starting from now it will be the preferred content type. Even if we 
     * continue to support binary/octet-stream. */
    private static final String APPLICATION_CONTENT_TYPE = "application/octet-stream";

    /** The special http header for correlation ids. */
    private static final String CORRELATION_ID_HDR = "Correlation-id";

    /** Flood prevention for large data.   */
    private static final int MAX_TRACES_BYTES = 500;

    /** Multi threaded version of the service proxy.*/
    private ServiceProxy _serviceProxy;

    /** Logger. */
    private static final Log LOG = LogFactory.getLog(C2wsProxy.class);

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public void init(final ServletConfig config) throws ServletException {
        super.init(config);

        if (LOG.isDebugEnabled()) {
            LOG.debug("Initializing ");
        }
        Map<String, String> proxyConfig = new HashMap<String, String>();
        Enumeration<String> en = getServletConfig().getInitParameterNames();
        while (en.hasMoreElements()) {
            setInitParameter(proxyConfig, en.nextElement(), null);
        }
        try {
            _serviceProxy = new ServiceProxy(proxyConfig);
        } catch (ProxyConfigurationException e) {
            throw new ServletException(e);
        }
    }

    /** {@inheritDoc} */
    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException {

        long startTime = System.currentTimeMillis();

        /* Use correlation id received in http header. This allows logs on
         * this side to be easily correlated with the mainframe logs. */
        String requestID = request.getHeader(CORRELATION_ID_HDR);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Start proxy request " + requestID + " from client " + request.getRemoteHost());
        }

        /* Make sure this is a Mainframe LegStar request. */
        if (!isSupportedContentType(request)) {
            throw new ServletException("Content type " + request.getContentType() + " is not supported");
        }

        try {
            /* Get all the bytes from the request in a byte array */
            int requestBytesLen = request.getContentLength();
            byte[] requestBytes = new byte[requestBytesLen];
            InputStream in = request.getInputStream();
            int offset = 0;
            int n;
            do {
                n = in.read(requestBytes, offset, requestBytesLen - offset);
            } while (n != -1);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Request data from host:");
                traceData(requestID, requestBytes, LOG);
            }

            /* Call the proxy getting a byte array back */
            byte[] replyBytes = getServiceProxy().invoke(requestID, requestBytes);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Reply data to host:");
                traceData(requestID, replyBytes, LOG);
            }

            /* Push the reply byte array into the http response */
            response.setContentType(request.getContentType());
            response.getOutputStream().write(replyBytes);

        } catch (IOException e) {
            throw (new ServletException(e));
        } catch (ProxyInvokerException e) {
            throw (new ServletException(e));
        }

        long endTime = System.currentTimeMillis();
        if (LOG.isDebugEnabled()) {
            LOG.debug("End proxy request " + requestID + " from client " + request.getRemoteHost() + " serviced in "
                    + (endTime - startTime) + " msecs");
        }
    }

    /**
     * Check that the client is sending a content type that we understand.
     * @param request the http request
     * @return true if the request has supported encoding
     */
    private boolean isSupportedContentType(final HttpServletRequest request) {
        if (request.getContentType().trim().compareToIgnoreCase(BINARY_CONTENT_TYPE) == 0
                || request.getContentType().trim().compareToIgnoreCase(APPLICATION_CONTENT_TYPE) == 0) {
            return true;
        }
        return false;
    }

    /**
     * Locate an initialization parameter from the servlet configuration.
     * If the parameter is not found, a default value is used. The parameter
     * is then copied over to the in memory configuration.
     * @param proxyConfig the proxy configuration
     * @param parameterName the parameter name
     * @param defaultValue the default value if not found
     */
    private void setInitParameter(final Map<String, String> proxyConfig, final String parameterName,
            final String defaultValue) {
        String value = getServletConfig().getInitParameter(parameterName);
        if (value == null || value.length() == 0) {
            value = defaultValue;
            if (LOG.isDebugEnabled()) {
                LOG.debug("Parameter " + parameterName + " not found." + " Using default value: " + value);
            }
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Parameter " + parameterName + " found." + " Using value: " + value);
            }
        }
        proxyConfig.put(parameterName.replace("c2ws.", ""), value);
    }

    /**
     * Produce a dump-like report of a data buffer content.
     * @param requestID a correlation id
     * @param data the raw data to trace
     * @param log where to log
     */
    public static void traceData(final String requestID, final byte[] data, final Log log) {

        StringBuilder dumpLine = new StringBuilder(); // 128
        String dumpChar; //[5];
        StringBuilder dumpString = new StringBuilder(); //[17];

        for (int i = 0; i < data.length && i < MAX_TRACES_BYTES; i++) {
            /* print every 16 byte on a different line */
            dumpChar = String.format("%02X ", data[i] & 0xff);
            dumpLine.append(dumpChar);
            if (Character.isValidCodePoint(data[i])) {
                dumpChar = String.format("%c", data[i]);
            } else {
                dumpChar = "?";
            }
            if (dumpChar.length() > 0) {
                dumpString.append(dumpChar);
            } else {
                dumpString.append(" ");
            }
            if (i % 16 == 15 || i == data.length - 1) {
                while (i % 16 < 15) {
                    dumpLine.append("   ");
                    i++;
                }
                dumpLine.append(" -- ");
                dumpLine.append(dumpString);
                log.debug(dumpLine);
                dumpString = new StringBuilder();
                dumpLine = new StringBuilder();
            }
        }

        if (data.length > MAX_TRACES_BYTES) {
            dumpLine.append(String.format("...data was truncated at %d bytes", MAX_TRACES_BYTES));
            log.debug(dumpLine);
        }
    }

    /**
     * @return the service associated service proxy
     */
    public ServiceProxy getServiceProxy() {
        return _serviceProxy;
    }
}