at.spardat.xma.baserpc.BaseRPCClient 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: BaseRPCClient.java 2089 2007-11-28 13:56:13Z s3460 $
package at.spardat.xma.baserpc;
import java.util.Collection;
import java.util.StringTokenizer;
import at.spardat.enterprise.exc.SysException;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.transport.ConnectException;
import at.spardat.xma.boot.transport.Transport;
import at.spardat.xma.boot.transport.XMA_URI;
import at.spardat.xma.exception.Codes;
import at.spardat.xma.serializer.Deserializer;
import at.spardat.xma.serializer.Serializer;
import at.spardat.xma.serializer.SerializerFactory;
import at.spardat.xma.session.XMASessionClient;
import at.spardat.xma.util.ByteArray;
/**
* Client side class to make a base RPC. Usage:
*
* BaseRPCClient rpc = new BaseRPCClient ("myRPC");
* // set parameters
* rpc.setParameter (1, "toServer");
* // execute
* rpc.execute();
* // read output parameters
* ... getParameter (1, "fromServer");
*
*
* This class is for framework-internal use only.
*/
public class BaseRPCClient {
/**
* Name of the RPC
*/
private String fName;
/**
* The resource at the server (a servlet) that is responsible for handling the RPC
* at the server.
*/
private String fServerRessource;
/**
* The session
*/
private XMASessionClient fSession;
/**
* Models the data sent to the server
*/
private ClientToServerData fCallData = new ClientToServerData();
/**
* Models the data sent from the server
*/
private ServerToClientData fReplyData;
/**
* communicate encrypted (use SSL)
*/
private boolean fNeedEncryption = false;
/**
* Constructor.
*
* @param name the name of the base rpc. Used at the server side to discriminate
* among different calls.
* @param serverRessource the name of the servlet-ressource that is responsible
* for handling the request at the server.
* @param session the client's session.
*/
public BaseRPCClient (String name, String serverRessource, XMASessionClient session) {
if (name == null) throw new IllegalArgumentException();
fName = name;
fCallData.setName (name);
fSession = session;
fServerRessource = serverRessource;
}
/**
* Sets a parameter transferred that is to be transferred to the server.
*
* @param id a numeric id of the parameter. Must not be less than zero and greater than 127.
* @param value serializable object. Must not be null.
*/
public void setParameter (int id, Object value) {
fCallData.setParameter (id, value);
}
/**
* Returns a parameter set at the server side.
*
* @param id numeric id of the parameter
* @return object or null if there is no such parameter.
*/
public Object getParameter (int id) {
return fReplyData.getParameter(id);
}
/**
* Sets a parameter that should be transferred to the server which is
* a collection that should not be serialized directly.
*
* @param id a numeric id of the parameter. Must not be less than zero and greater than 127.
* @param value collection of objects that must be serializable themselves.
*/
public void setUnserializableCollection (int id, Collection value) {
fCallData.setUnserializableCollection(id, value);
}
/**
* Returns a Collection from the server. Please not that the concrete subtype of
* Collection may be different from that provided via setUnserializableCollection
* at the server side.
*
* @param id numeric id of the parameter.
* @return collection of objects. You cannot make any assumptions on the concrete type
* of the returned value.
*/
public Collection getUnserializableCollection (int id) {
return fReplyData.getUnserializableCollection(id);
}
/**
* Executes this RPC.
*/
public void execute () {
/**
* serialize parameters
*/
SerializerFactory fac = new BaseRpcSerFactory (false);
boolean isBinaryMode = fac.isModeBinary(null);
Serializer ser = fac.createSerializer(null, 1024);
ByteArray serResult = null;
ser.addHeader();
try {
fCallData.externalize (ser);
serResult = ser.getResult();
} catch (Exception ex) {
throw new SysException (ex, "Cannot externalize parameters at client").setCode (Codes.BASERPC_CLIENT_EXTERNALIZE);
}
/**
* compress
*/
if (isBinaryMode && doCompress (serResult.size())) {
serResult = serResult.getCompressed();
}
if (!isBinaryMode) serResult.setComputeHeaderLength (false);
/**
* Do the RPC using a Transport object
*/
Transport transport = Transport.getTransport();
XMA_URI uri = fSession.getUri();
uri.setResource(fServerRessource);
byte [] transportOutputBytes = null;
if(fNeedEncryption&&!"https".equals(uri.getProtocol_())) {
uri.setProtocol("https");
String ports = fSession.getRuntimeProperty("SSLPort","443");
for(StringTokenizer tok=new StringTokenizer(ports,",;|");tok.hasMoreTokens();) {
String port = tok.nextToken().trim();
uri.setPort(new Integer(port).intValue());
try {
transportOutputBytes = transport.callServerEvent (fSession, uri, serResult.getBytes());
break;
} catch (ConnectException ex) {
if(tok.hasMoreTokens()) {
// exception allready logged by Transport
fSession.getLogger().log(LogLevel.INFO,"Server not reachable at port "+port+" trying next secure port");
} else {
throw new SysException (ex, "Server returned error or is not reachable.").setCode(Codes.BASEPRC_CLIENT_TRANSPORT_ERROR);
}
} catch (Exception ex) {
throw new SysException (ex, "Server returned error or is not reachable.").setCode(Codes.BASEPRC_CLIENT_TRANSPORT_ERROR);
}
}
} else {
try {
transportOutputBytes = transport.callServerEvent (fSession, uri, serResult.getBytes());
} catch (Exception ex) {
throw new SysException (ex, "Server returned error or is not reachable.").setCode(Codes.BASEPRC_CLIENT_TRANSPORT_ERROR);
}
}
/**
* First 4 bytes in transportOutputBytes is the length. This must match with
* the length of the array.
*/
if (transportOutputBytes == null || transportOutputBytes.length < 4) {
throw new SysException ("server returned less than 4 bytes").setCode(Codes.BASERPC_CLIENT_READ_LENGTH);
}
ByteArray transportOutputByteArray = new ByteArray (transportOutputBytes);
transportOutputByteArray.setHeader(true);
int expectedLength = transportOutputByteArray.getLengthInHeader();
if (expectedLength != -1 && expectedLength != transportOutputBytes.length) {
throw new SysException ("expected result of "+expectedLength+", but got "+transportOutputBytes.length)
.setCode(Codes.BASERPC_CLIENT_LENGTH_MISMATCH);
}
/**
* uncompress if compressed
*/
if (transportOutputByteArray.isCompressed()) {
transportOutputByteArray = transportOutputByteArray.getUncompressed();
}
/**
* Deserialize RemoteReplyData from byte array
*/
try {
Deserializer deser = fac.createDeserializer(null, transportOutputByteArray.getBuffer(), ByteArray.HEADER_LEN, transportOutputByteArray.size()-ByteArray.HEADER_LEN);
fReplyData = new ServerToClientData ();
fReplyData.internalize (deser);
} catch (Exception ex) {
throw new SysException (ex, "cannot deserialize reply from server").setCode (Codes.BASERPC_INTERNALIZE_RESPONSE);
}
/**
* if the reply contained an Exception from the server, throw it
*/
if (fReplyData.getException() != null) throw fReplyData.getException();
}
/**
* Determines if data sent to the server of length should be compressed.
*/
private boolean doCompress (int length) {
String comprThrAsString = fSession.getRuntimeProperty("RpcCompressionThreshold");
if (comprThrAsString == null) return false;
int compressionThreshold = Integer.parseInt (comprThrAsString);
if (compressionThreshold == -1) return false;
else return length > compressionThreshold;
}
/**
* Determines if data has to be send encrypted.
* @param needEncryption if true https will be used for transport.
* @author s2877
*/
public void setNeedEncryption(boolean needEncryption) {
fNeedEncryption = needEncryption;
}
}