/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.tools.ldapdecoder;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Date;
import javax.net.ssl.SSLSocketFactory;
import com.slamd.asn1.ASN1Element;
import com.slamd.asn1.ASN1Reader;
import com.slamd.asn1.ASN1Writer;
import com.slamd.tools.ldapdecoder.protocol.LDAPMessage;
/**
* This class defines a thread that may be used for handling all communication
* with the directory server.
*
*
* @author Neil A. Wilson
*/
public class LDAPDecoderServerThread
extends Thread
{
// The ASN.1 reader that may be used to read responses from the directory
// server.
private ASN1Reader serverReader;
// The ASN.1 writer that may be used to send requests to the directory server.
private ASN1Writer serverWriter;
// Indicates whether a request has been made to close the connection to the
// server.
private boolean closeRequested;
// Indicates whether the decoded information should be written to a SLAMD job
// script.
private boolean writeJobScript;
// The client connection with which this server thread is associated.
private LDAPClientConnection clientConnection;
// The LDAP decoder that created this server thread.
private LDAPDecoder decoder;
// The output writer that will be used to write information to the SLAMD job
// script that is being generated.
private PrintStream scriptWriter;
// The socket that will be used for communicating with the directory server.
private Socket serverSocket;
/**
* Creates a new thread for handling all interaction with the directory
* server.
*
* @param decoder The LDAP decoder with which this server thread is
* associated.
* @param clientConnection The client connection with which this server
* thread is associated.
*
* @throws LDAPDecoderException If a problem occurs while initializing the
* thread.
*/
public LDAPDecoderServerThread(LDAPDecoder decoder,
LDAPClientConnection clientConnection)
throws LDAPDecoderException
{
this.decoder = decoder;
this.clientConnection = clientConnection;
setName("Connection " +
clientConnection.clientSocket.getInetAddress().getHostAddress() +
':' + clientConnection.clientSocket.getPort() + " server thread");
writeJobScript = decoder.writeJobScript;
scriptWriter = decoder.scriptWriter;
// Establish the connection to the server.
if (decoder.useSSLForServer)
{
try
{
SSLSocketFactory socketFactory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
serverSocket = socketFactory.createSocket(decoder.serverAddress,
decoder.serverPort);
serverReader = new ASN1Reader(serverSocket);
serverWriter = new ASN1Writer(serverSocket);
serverSocket.setTcpNoDelay(true);
}
catch (Exception e)
{
throw new LDAPDecoderException("Unable to establish SSL-based " +
"connection to the directory server", e);
}
}
else
{
try
{
serverSocket = new Socket(decoder.serverAddress, decoder.serverPort);
serverReader = new ASN1Reader(serverSocket);
serverWriter = new ASN1Writer(serverSocket);
serverSocket.setTcpNoDelay(true);
}
catch (Exception e)
{
throw new LDAPDecoderException("Unable to establish a connection" +
"to the directory server", e);
}
}
closeRequested = false;
}
/**
* Loops, reading information from the server, decoding it, and passing it on
* to the client.
*/
public void run()
{
boolean failedLastTime = false;
while (! closeRequested)
{
try
{
// Read an element from the server.
ASN1Element element = serverReader.readElement();
if (element == null)
{
// The server closed the connection.
closeRequested = true;
clientConnection.closeConnection();
return;
}
// Decode it as necessary and write information about it to the log.
synchronized (clientConnection.outputWriter)
{
clientConnection.outputWriter.println(
clientConnection.dateFormat.format(new Date()) +
" -- Read data from the server");
if (decoder.displayRawBytes)
{
byte[] elementBytes = element.encode();
clientConnection.outputWriter.println("Raw Data from Server:");
clientConnection.outputWriter.println(
LDAPMessage.byteArrayToString(elementBytes, 4));
}
try
{
LDAPMessage message = LDAPMessage.decode(element);
clientConnection.outputWriter.println("Decoded Data from Server:");
clientConnection.outputWriter.println(message.toString(4));
if (writeJobScript && (! decoder.excludeResponses()))
{
synchronized (scriptWriter)
{
message.toSLAMDScript(scriptWriter);
}
}
if (decoder.verboseMode)
{
System.err.println("Decoded an " +
message.getProtocolOp().getProtocolOpType() +
" message");
}
}
catch (Exception e)
{
clientConnection.outputWriter.println("Unable to Decode Data " +
"from Server:");
if (decoder.verboseMode)
{
e.printStackTrace(clientConnection.outputWriter);
}
}
clientConnection.outputWriter.println();
clientConnection.outputWriter.println();
}
// Forward the data on to the client.
clientConnection.writeToClient(element);
}
catch (Exception e)
{
if (failedLastTime)
{
synchronized (clientConnection.outputWriter)
{
clientConnection.outputWriter.println(
clientConnection.dateFormat.format(new Date()) +
" -- Error reading data from the server");
if (decoder.verboseMode)
{
e.printStackTrace(clientConnection.outputWriter);
}
}
clientConnection.closeConnection();
}
else
{
failedLastTime = true;
}
}
}
}
/**
* Writes the provided ASN.1 element to the server.
*
* @param element The ASN.1 element to write to the server.
*
* @throws IOException If a problem occurs while trying to write the element
* to the server.
*/
public void writeToServer(ASN1Element element)
throws IOException
{
serverWriter.writeElement(element);
}
/**
* Closes the connection to the server.
*/
public void closeConnection()
{
closeRequested = true;
try
{
serverReader.close();
serverWriter.close();
} catch (Exception e) {}
try
{
serverSocket.close();
} catch (Exception e) {}
}
}