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);
}
}