blob: 804312c202c10a2fad65410b5eb66ab8775eab5c [file] [log] [blame]
// Copyright 2003-2004 The Apache Software Foundation.
// (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.test;
import java.io.*;
import java.net.*;
/**
* @author hawkeye
*
* Handles the connection from a client to the server
* this class is responsible for cleaning up the socket
*
*/
public class TestClientThread extends ChildHandler implements Runnable
{
public static final String STOPTCPMON_STRING ="STOPTCPMON";
private boolean continueToRun =true;
private boolean serverClosedSocket;
// the responder back to the client
// private ClientReturner clientReturner =null;
// We need to keep hold of this so that we can tell what state it's in when
// the
// read fails
// and also to close it.
private Socket clientSocket;
// I didn't want to make this global but it has to be for the constructor
// pattern to work :-(
private int bytesRead =0;
private static final int READ_BUFFER_SIZE =32768; // 32k
private char[] readBuffer =new char[READ_BUFFER_SIZE];
// the request from the client
private BufferedReader clientRequestStream =null;
// the connection to the server where we forwaard the clients request to.
private BufferedWriter streamToServer =null;
// How many times to try to connect to the server before giving in
private static final int CREATE_SOCKET_TO_SERVER_RETRY =5;
/**
* Class constructor with the client socket used to communicate with the
* client. This is quite a powerful constructor it actually reads in bytes
* from the client socket to work out whether it should stay alive or not if
* not then it throws an exception
*
* @param socket reference to the socket connected to the client
* @exception StopRequestException if a stoprequest is received from the
* client
* @exception ConnectException if the first read off the wire gave us -1
* i.e. the stream from the client is closed
*/
public TestClientThread(Socket clientSocket, String serviceHostName,
int servicePort) throws StopRequestException , IOException ,
ConnectException , ConnectionNotEstablishedException
{
// System.out.println( "TestClientThread(3): entry");
this.clientSocket=clientSocket;
serverClosedSocket=false;
try
{
IsStopMessage(clientSocket);
}
catch(StopRequestException stopRequestException)
{
// have to close anything we've created
close();
throw stopRequestException;
}
Socket serviceSocket=createSocketToServer(serviceHostName, servicePort);
writeToServer(readBuffer, bytesRead);
// OK, now we've done that we can create the new thread to stream
// the result back to the client
ClientReturner clientReturner=null;
try
{
clientReturner=ClientReturnerFactory.getClientReturner(clientSocket, serviceSocket, this);
}
catch(Exception exception)
{
exception.printStackTrace(System.err);
throw new ConnectException("Cannot Create Client Returner");
}
addChild(clientReturner);
new Thread(clientReturner).start( );
}
/**
* if a class overrides this to get the lifecycle but not the function then it needs this constructor
*
*/
protected TestClientThread()
{}
private void IsStopMessage(Socket clientSocket) throws IOException,
StopRequestException
{
// Get the input stream from the client
clientRequestStream=new BufferedReader(new InputStreamReader(
clientSocket.getInputStream( )));
// Read in the first few bytes to ensure that we are not being told to
// stop
try
{
bytesRead=clientRequestStream.read(readBuffer, 0,
STOPTCPMON_STRING.length( ));
}
catch (ConnectException connectException)
{
// did we manage to read any bytes in?
if (bytesRead>0)
{
// Well we did !
System.err
.println("Got a connectException when reading in the first few bytes from the stream");
System.err
.println("Read in some bytes but not enough to work out whether we should stop or not");
System.err
.println("Highly unlikely this is good so we'll stop !");
throw connectException;
}
else
{
System.err
.println("Read in zero bytes before the client shut the connection");
System.err.println("Continuing to read in future requests");
}
}
catch (IOException exception)
{
System.err
.println("IOException when reading the clients initial request: "
+exception);
}
if (bytesRead!=-1)
{
String inputLine=new String(readBuffer, 0, bytesRead);
if (inputLine.startsWith(STOPTCPMON_STRING))
{
clientRequestStream=null;
throw new StopRequestException(
"Received a stop monitor message");
}
else
{
// now put the line into the request file for later processing
TCPMonitor.getInstance( ).writeRequest(readBuffer, bytesRead);
}
}
else
{
// looks like they closed the connection so throw an exception to
// say we have closed
throw new ConnectException(
"Connection closed when reading first few bytes of client's request");
}
}
/**
* Reads the request from the client and if of a valid format will extract
* the test ID and required data and call the TestSingleton class to set or
* get the information. It is assumed that all requests are UTF Strings.
* <p>
* If the incoming request does not contain a test ID, or is not of a
* recognised format then the socket will be closed and this object will
* finish.
* </p>
*/
public void run( )
{
while (continueToRun)
{
try
{
bytesRead = readBytes(readBuffer, 0, READ_BUFFER_SIZE);
if (bytesRead==-1)
{
continueToRun=false;
}
else
{
// System.out.println( "About to write some bytes to the
// server");
try
{
writeToServer(readBuffer, bytesRead);
TCPMonitor.getInstance( ).writeRequest(readBuffer,
bytesRead);
}
catch (Exception exception)
{
System.err
.println("TestClientThread#run(): IOException when forwarding the request to the server");
exception.printStackTrace(System.err);
continueToRun=false;
}
}
}
catch (SocketException socketException)
{
continueToRun=false;
if (!serverClosedSocket)
{
String exceptionString = socketException.getMessage().toLowerCase();
if (exceptionString.indexOf("connection") != -1
&& exceptionString.indexOf("reset") != -1)
{
// tihs appears to happen when the client has stopped
// sending us data and we should close down gracefully
// but when I check the socket for it's status it tells
// me that all is well but for
// the fact that the stream is not ready() but ready()
// returning false is not a reason to shut !
// ah well - never mind - let's close gracefully.
// no need to print this out as an exception
System.out
.println("TestClientThread#run(): Connection reset when reading from client - closing gracefully");
}
else
{
socketException.printStackTrace(System.err);
}
}
else
{
// the server has closed the socket so ignore any errors !
}
}
catch (IOException exception)
{
System.err
.println("TestClientThread#run(): IOException when reading clients request: "
+exception);
throw new RuntimeException(
"TestClientThread#run(): IOException when reading clients request: "
+exception);
}
}
// System.out.println( "TestClientThread#run(): exit");
}
/**
* @param bufferToreadInto
* @param startingPointInBuffer
* @param sizeOfReadBuffer
* @return the number of bytes read
*/
protected int readBytes(char[] bufferToreadInto, int startingPointInBuffer, int sizeOfReadBuffer)throws IOException
{
return clientRequestStream.read(bufferToreadInto, startingPointInBuffer,
sizeOfReadBuffer);
}
public Socket createSocketToServer(String serviceHostName, int servicePort)
throws IOException, ConnectionNotEstablishedException
{
Socket serviceSocket=null;
int retry=CREATE_SOCKET_TO_SERVER_RETRY;
do
{
try
{
serviceSocket=TCPMonitor.getClientSocket(serviceHostName, servicePort);
System.out.println( "TestClientThread: local addr="+serviceSocket.getLocalPort());
}
catch (UnknownHostException unknownHostException)
{
// oh dear !
throw unknownHostException;
}
catch (ConnectException connectException)
{
System.err
.println("ConnectionException when Monitor connecting to server <"+serviceHostName+":"+servicePort+">"
+connectException.getMessage( ));
connectException.printStackTrace(System.err);
throw new ConnectionNotEstablishedException(connectException);
}
catch (Exception se)
{
System.err.println("Failed to open socket to service: "+se);
if (retry>0)
{
// go to sleep
System.err.println("Going to sleep before retrying");
try
{
Thread.currentThread( ).sleep(2500);
}
catch (InterruptedException interruptedException)
{
// don't know if this is an issue?
System.out
.println("Got an interruptedxception sleeping on this thread "
+interruptedException);
}
System.err.println("Woke up ");
}
else
{
throw new ConnectionNotEstablishedException(se);
}
}
}
while (serviceSocket==null&&retry-->0);
try
{
streamToServer=new BufferedWriter(new OutputStreamWriter(
serviceSocket.getOutputStream( )));
}
catch (IOException exception)
{
System.err
.println("IOEXception when getting the writer to the service "
+exception);
throw exception;
}
return serviceSocket;
}
protected void writeToServer(char[] request, int bytesToWrite)
throws IOException
{
//System.out.println( "writeToServer: "+new String(request, 0,
// bytesToWrite));
streamToServer.write(request, 0, bytesToWrite);
streamToServer.flush( );
}
/**
* This method is called when the socket from the server is closed
*
*/
public void notifyOfServerClosingSocket()
{
serverClosedSocket=true;
}
protected void close()
{
try
{
clientSocket.close();
}
catch(IOException exception)
{
// swallow exceptions on close
//exception.printStackTrace(System.err);
}
super.close();
}
}