package com.sap.trex.client;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class Channel implements StreamBase {
private OutputStream itsOutput = null;
private InputStream itsInput = null;
private Socket itsSocket = null;
private int itsRefCounter = 0;
private int itsState = 0;
private int itsReadSize = 0;
private int itsBufferPos = 0;
private byte[] itsBuffer;
private int itsBufferSize = 8192;
public Channel() {
}
public void open(String theLocation, boolean theSecure) throws UnknownHostException, NoSuchAlgorithmException, IOException, TrexException
{
int pos = theLocation.lastIndexOf(":");
if(pos==-1) {
itsSocket = new Socket(theLocation,0); // will fail and produce a reasonable exception
} else {
String host = theLocation.substring(0,pos);
int port = new Integer(theLocation.substring(pos+1)).intValue();
if(theSecure) {
SSLSocket socket = (SSLSocket)SSLSocketFactory.getDefault().createSocket(host,port);
socket.setUseClientMode(true);
socket.startHandshake();
itsSocket = socket;
} else {
itsSocket = new Socket(host,port);
}
}
itsSocket.setSoLinger(true, 30);
itsBuffer = new byte[itsBufferSize];
itsOutput = new OutputStream(this);
itsInput = new InputStream(this);
itsRefCounter = 1;
}
public void writeHeader(String theSession, int theSequence, String theMethod) throws IOException, TrexException
{
writeType(BufferedIO.TyBOF);
itsOutput.writeIntegral(0x18100800, 4);
itsOutput.writeIntegral(theMethod.getBytes().length, 4);
//write version
itsOutput.writeIntegral(1, 1); // major
itsOutput.writeIntegral(2, 1); // minor
itsOutput.writeIntegral(4, 1); // revision
//write header length
int size = 25 + theMethod.getBytes().length + 8 + theSession.getBytes().length;
itsOutput.writeIntegral(size,4);
//write dsr flag
itsOutput.writeIntegral(0,1);
//write method name
writeBytes(theMethod.getBytes(), theMethod.getBytes().length);
itsOutput.writeIntegral(32,4); // 32 = trex client
itsOutput.writeIntegral(Version.PROTOCOLVERSION,4);
// revision 4:
itsOutput.writeIntegral(theSession.getBytes().length,4);
writeBytes(theSession.getBytes(), theSession.getBytes().length);
itsOutput.writeIntegral(theSequence,4);
}
public OutputStream output()
{
return itsOutput;
}
public InputStream input()
{
return itsInput;
}
public void addRef() {
itsRefCounter++;
}
public void releaseRef() {
itsRefCounter--;
if(itsRefCounter == 0) {
if (itsState == BufferedIO.SOCKET_READ) {
try {
while (itsState==BufferedIO.SOCKET_READ)
itsInput.ignoreOne();
} catch (IOException ex) {
itsState = BufferedIO.SOCKET_ERROR;
} catch (TrexException ex) {
itsState = BufferedIO.SOCKET_ERROR;
}
}
if (itsState == BufferedIO.SOCKET_WRITE1) {
itsState = BufferedIO.SOCKET_IDLE; // discard the non written stuff
}
if (itsState == BufferedIO.SOCKET_WRITEN) {
try {
writeEOF(); // give server a chance to recover from aborting in the middle of writing
if (readEOF()) // must test if server has used this chance
itsState = BufferedIO.SOCKET_IDLE;
else
itsState = BufferedIO.SOCKET_ERROR;
} catch (IOException ex) {
itsState = BufferedIO.SOCKET_ERROR;
} catch (TrexException ex2) {
itsState = BufferedIO.SOCKET_ERROR;
}
}
try {
itsSocket.close();
} catch (IOException ex) {
itsState = BufferedIO.SOCKET_ERROR;
}
}
}
public byte peekType() throws IOException {
if (itsBufferPos == itsReadSize)
receive();
return (byte)(itsBuffer[itsBufferPos] & BufferedIO.TypeMask);
}
public byte readType(byte theType) throws IOException, TrexException {
if(itsState != BufferedIO.SOCKET_READ)
throw new TrexException(Error.TDBC_CONN_INVALID_STATE, "writeByte(state!=read)");
if(itsBufferPos == itsReadSize)
receive();
byte b = itsBuffer[itsBufferPos++];
byte aType = (byte)(b & BufferedIO.TypeMask);
if(aType==BufferedIO.TyEOF)
itsState = BufferedIO.SOCKET_IDLE;
if(theType != aType)
throw new TrexException(Error.IMSRC_TCPIP_OBJECT_ID_MISMATCH,
"got "+BufferedIO.getTypeName(aType)+", expected "+BufferedIO.getTypeName(theType));
return (byte)(b & BufferedIO.SizeMask);
}
public void readBytes(byte[] theBuf, int theLength) throws IOException {
int l2 = 0;
while (l2 < theLength) {
if (itsBufferPos == itsReadSize)
receive();
// check, if no more data is available
if( itsReadSize == -1 )
return;
int l3 = java.lang.Math.min(theLength - l2, itsReadSize - itsBufferPos);
if (theBuf != null)
{
System.arraycopy(itsBuffer, itsBufferPos, theBuf, l2, l3);
theBuf[l2] = itsBuffer[itsBufferPos];
}
l2 += l3;
itsBufferPos += l3;
}
}
public boolean readEOF() throws IOException, TrexException {
if (itsState == BufferedIO.SOCKET_IDLE)
return true;
if (itsState != BufferedIO.SOCKET_READ)
throw new TrexException(Error.TDBC_CONN_INVALID_STATE,
"readEOF(state!=read)");
if (itsBufferPos == itsReadSize)
receive();
if (itsBuffer[itsBufferPos] == BufferedIO.TyEOF) {
itsBufferPos++;
itsState = BufferedIO.SOCKET_IDLE;
return true;
}
return false;
}
public void receive() throws IOException {
itsBufferPos = 0;
try {
itsReadSize = itsSocket.getInputStream().read(itsBuffer, 0, itsBufferSize);
} catch (IOException ex) {
itsReadSize = 0;
itsState = BufferedIO.SOCKET_ERROR;
throw ex;
}
}
public void writeType(byte theVal) throws TrexException, IOException {
if (itsState == BufferedIO.SOCKET_IDLE) {
itsBufferPos = 0;
itsState = BufferedIO.SOCKET_WRITE1;
}
if (itsState != BufferedIO.SOCKET_WRITE1
&& itsState != BufferedIO.SOCKET_WRITEN)
throw new TrexException(Error.TDBC_CONN_INVALID_STATE,
"writeByte(state!=write)");
itsBuffer[itsBufferPos++] = theVal;
if (itsBufferPos == itsBufferSize)
send(false);
}
public void writeBytes(byte[] theBuffer, int theLength) throws IOException, TrexException {
int l2 = 0;
while (l2 < theLength) {
int l3 = java.lang.Math.min(theLength - l2,
(int) (itsBufferSize - itsBufferPos));
System.arraycopy(theBuffer, l2, itsBuffer, itsBufferPos, l3);
l2 += l3;
itsBufferPos += l3;
if (itsBufferPos == itsBufferSize)
send(false);
}
}
public void writeEOF() throws IOException, TrexException
{
writeType(BufferedIO.TyEOF);
send(true);
}
void send(boolean eof) throws TrexException, IOException {
if (itsState != BufferedIO.SOCKET_WRITE1
&& itsState != BufferedIO.SOCKET_WRITEN)
throw new TrexException(Error.TDBC_CONN_INVALID_STATE, this.getClass().getName()
+ "." + this.getClass());
try {
itsSocket.getOutputStream().write(itsBuffer, 0, itsBufferPos);
itsState = BufferedIO.SOCKET_WRITEN;
} catch (IOException ex) {
itsState = BufferedIO.SOCKET_ERROR;
throw ex;
}
itsBufferPos = 0;
if (eof) {
itsSocket.getOutputStream().flush();
itsState = BufferedIO.SOCKET_READ;
itsReadSize = 0;
}
}
}