All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.baserpc.BaseRPCServer Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

//@(#) $Id: BaseRPCServer.java 2619 2008-07-09 09:58:16Z gub $
package at.spardat.xma.baserpc;

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

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

import at.spardat.enterprise.exc.AppException;
import at.spardat.enterprise.exc.BaseException;
import at.spardat.enterprise.exc.SysException;
import at.spardat.properties.XProperties;
import at.spardat.xma.exception.Codes;
import at.spardat.xma.monitoring.TimeingEvent;
import at.spardat.xma.rpc.RPCServletServer;
import at.spardat.xma.serializer.Deserializer;
import at.spardat.xma.serializer.Serializer;
import at.spardat.xma.serializer.SerializerFactory;
import at.spardat.xma.util.ByteArray;

/**
 * The server side implementation of a base RPC.
 *
 * @author YSD, 28.09.2004
 */
public class BaseRPCServer {

    /**
     * models the data sent from the client
     */
    private ClientToServerData         fRequestData;

    /**
     * holds the data to send back to the client
     */
    private ServerToClientData         fReplyData;



    /**
     * Must be called from the servlets doPost method
     *
     * @param executor implementation that actually executes the RPC.
     */
    public void handlePost (HttpServletRequest request, HttpServletResponse response,
                            ServletConfig servletConfig,
                            IRPCExecutorServer executor)
        throws ServletException, IOException {

        /**
         * construct data sent from the client
         */

        try {

            /**
             * read data sent from the client
             */
            fReplyData = new ServerToClientData();
            fRequestData = readDataFromServletInput (request);

            /**
             * execute the method
             */
            TimeingEvent ev = null;
            XProperties node = XProperties.getNodeOfPackage("at.spardat.xma");
            if("true".equalsIgnoreCase(node.get("rpcDetailStatistics","false"))) {
                String      contextPath = request.getContextPath();
                if (contextPath.length() > 0 && contextPath.charAt(0) == '/') {
                    contextPath = contextPath.substring(1);
                }
                ev = new TimeingEvent ("app<"+contextPath+">:rpcBase<"+fRequestData.getName()+">");
            }
            try {
                executor.execute (fRequestData.getName(), fRequestData, fReplyData, request);
                if(ev!=null) ev.success();
            } catch (Exception x) {
                throw new SysException (x, "Exception in server side base rpc method").setCode (Codes.BASERPC_SERVER_ENDPOINTMETHODFAULT);
            } finally {
                if(ev!=null) ev.failure();
            }

        } catch (Throwable ex) {
            handleReturnableException(ex, fReplyData, servletConfig);
        }

        /**
         * Stream result back to the client
         */
        SerializerFactory       fac = new BaseRpcSerFactory(true);
        boolean                 isBinaryMode = fac.isModeBinary(null);
        Serializer              serializer = fac.createSerializer(null, 2048);
        ByteArray               serializerResult = null;
        serializer.addHeader();
        try {
            fReplyData.externalize (serializer);
            serializerResult = serializer.getResult();
        } catch (Exception ex) {
            throw new SysException (ex, "cannot serialize data to be sent back to the client").setCode(Codes.BASERPC_SERVER_EXT_STREAM);
        }

        /**
         * Compress the result if length exceeds some limit
         */
        if (isBinaryMode && doCompress (serializerResult.size())) {
            serializerResult = serializerResult.getCompressed();
        }

        if (!isBinaryMode) serializerResult.setComputeHeaderLength (false);

        // write to ServletOuputStream
        response.setContentLength (serializerResult.size());  // set content length
        ServletOutputStream     servletOut = response.getOutputStream();
        servletOut.write (serializerResult.getBuffer(), 0, serializerResult.size());
    }


    /**
     * Exceptions that occur in the course of the execution of the base rpc are treated in this method.
     * Usually, they are sent back to the client, and, depending on their nature, also logged.
     *
     * @param ex       the exception
     * @param toClient the data to send back to the client
     * @param servletConfig used for logging purpose
     */
    private void handleReturnableException (Throwable ex, ServerToClientData toClient, ServletConfig servletConfig) {
        BaseException        toReturn;
        /**
         * here goes the default error handling. Our strategy is to pack the
         * exception in the reply object and stream it back to the client.
         * Only if this does not work for some reason, we fall back to a ServletException
         */
        if (ex instanceof BaseException) {
            toReturn = (BaseException) ex;
        } else {
            // the exception is no BaseException;
            toReturn = new SysException (ex, "Error in the execution of server side base rpc.").setCode(Codes.BASERPC_SERVER_ERROR);
        }
        /**
         * If the exception is not an AppException, is must be logged here to show
         * up in the server log of the application-server.
         */
        if (!(toReturn instanceof AppException)) {
            ServletContext      servletCtx = servletConfig.getServletContext();
            if (servletCtx != null) {
                servletCtx.log("Exception in baserpc", toReturn);
            } else {
                toReturn.printStackTrace();
            }
        }
        // prepare the exception for migration an put it into the reply
        // after this the exception can not be used on the server any more
        toReturn.prepareMigration();
        toReturn = toReturn.truncateSubclasses();
        toClient.setException (toReturn);
    }

    /**
     * Reads servlet input stream and constructs a data object representing the data sent from the client
     */
    private ClientToServerData readDataFromServletInput (HttpServletRequest request) throws IOException {
        ByteArray               upstreamBytes = new ByteArray (1024);
        upstreamBytes.setHeader(true);
        InputStream             upstream = request.getInputStream();
        upstreamBytes.readFrom (upstream, 4);  // read first 4 bytes which is the length of the stream
        if (upstreamBytes.size() != 4)
            throw new SysException ("not even read 4 bytes from ServletInputStream").setCode(Codes.BASERPC_SERVER_READ_LENGTH);
        int                     length = upstreamBytes.getLengthInHeader();
        if (length == -1) {
            // length is not set in the header; we read until EOF
            upstreamBytes.readFrom(upstream);
        } else {
            // read the remaining bytes
            upstreamBytes.readFrom (upstream, length);
            if (upstreamBytes.size() != length)
                throw new SysException (""+upstreamBytes.size()+" read from ServletInputStream, but header indicated length of "+length)
                                        .setCode(Codes.BASERPC_SERVER_LENGTH_MISMATCH);
        }
        // decompress if compressed
        if (upstreamBytes.isCompressed()) {
            upstreamBytes = upstreamBytes.getUncompressed();
        }
        // construct ClientToServerData object
        ClientToServerData      result = new ClientToServerData();

        /**
         * Deserialize data got from the client
         */
        try {
            Deserializer        deser = new BaseRpcSerFactory(true).createDeserializer(null, upstreamBytes.getBuffer(), ByteArray.HEADER_LEN, upstreamBytes.size()-ByteArray.HEADER_LEN);
            result.internalize (deser);
        } catch (Exception ex) {
            throw new SysException (ex, "cannot deserialize data from client").setCode (Codes.BASERPC_SERVER_INTERNALIZE);
        }

        return result;
    }


    /**
     * Uses the same compression logic as RPCServletServer.
     */
    private static boolean doCompress (int length) {
        return RPCServletServer.doCompress(length);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy