com.novell.ldap.Connection Maven / Gradle / Ivy
/* **************************************************************************
* $OpenLDAP: pkg/jldap/com/novell/ldap/Connection.java,v 1.88 2006/09/07 06:18:29 npalani Exp $
*
* Copyright (C) 1999 - 2002 Novell, Inc. All Rights Reserved.
*
* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
* TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
* TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
* AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
* IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
* OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
* PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
* THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
******************************************************************************/
package com.novell.ldap;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import com.novell.ldap.asn1.*;
import com.novell.ldap.client.*;
import com.novell.ldap.rfc2251.*;
import com.novell.ldap.resources.*;
/**
* The class that creates a connection to the LDAP server. After the
* connection is made, a thread is created that reads data from the
* connection.
*
* The application's thread sends a request to the MessageAgent class, which
* creates a Message class. The Message class calls the writeMessage method
* of this class to send the request to the server. The application thread
* will then query the MessageAgent class for a response.
*
* The reader thread multiplexes response messages received from the
* server to the appropriate Message class. Each Message class
* has its own message queue.
*
* Unsolicited messages are process separately, and if the application
* has registered a handler, a separate thread is created for that
* application's handler to process the message.
*
* Note: the reader thread must not be a "selfish" thread, since some
* operating systems do not time slice.
*
*/
/*package*/
final class Connection
{
private Object writeSemaphore = new Object();
private int writeSemaphoreOwner = 0;
private int writeSemaphoreCount = 0;
// We need a message number for disconnect to grab the semaphore,
// but may not have one, so we invent a unique one.
private int ephemeralId = -1;
private BindProperties bindProperties = null;
private int bindSemaphoreId = 0; // 0 is never used by to lock a semaphore
private Thread reader = null; // New thread that reads data from the server.
private Thread deadReader = null; // Identity of last reader thread
private IOException deadReaderException = null; // Last exception of reader
private LBEREncoder encoder = new LBEREncoder();
private LBERDecoder decoder = new LBERDecoder();
/*
* socket is the current socket being used.
* nonTLSBackup is the backup socket if startTLS is called.
* if nonTLSBackup is null then startTLS has not been called,
* or stopTLS has been called to end TLS protection
*/
private Socket socket = null;
private Socket nonTLSBackup = null;
private InputStream in = null;
private OutputStream out = null;
// When set to true the client connection is up and running
private boolean clientActive = true;
// Indicates we have received a server shutdown unsolicited notification
private boolean unsolSvrShutDnNotification = false;
// LDAP message IDs are all positive numbers so we can use negative
// numbers as flags. This are flags assigned to stopReaderMessageID
// to tell the reader what state we are in.
private final static int CONTINUE_READING = -99;
private final static int STOP_READING = -98;
// Stops the reader thread when a Message with the passed-in ID is read.
// This parameter is set by stopReaderOnReply and stopTLS
private int stopReaderMessageID = CONTINUE_READING;
// Place to save message information classes
private MessageVector messages = new MessageVector(5,5);
// Connection created to follow referral
private ReferralInfo activeReferral = null;
// Place to save unsolicited message listeners
private java.util.Vector unsolicitedListeners = new java.util.Vector(3,3);
// The LDAPSocketFactory to be used as the default to create new connections
static private LDAPSocketFactory socketFactory = null;
// The LDAPSocketFactory used for this connection
private LDAPSocketFactory mySocketFactory;
private int myTimeOut = 0;
private String host = null;
private int port = 0;
// Number of clones in addition to original LDAPConnection using this
// connection.
private int cloneCount = 0;
// Connection number & name used only for debug
private String name = "";
private static Object nameLock = new Object(); // protect connNum
private static int connNum = 0;
// These attributes can be retreived using the getProperty
// method in LDAPConnection. Future releases might require
// these to be local variables that can be modified using
// the setProperty method.
/* package */
static String sdk = new String("4.3");
/* package */
static Integer protocol = new Integer(3);
/* package */
static String security = "simple";
/**
* Create a new Connection object
*
* @param factory specifies the factory to use to produce SSL sockets.
*/
/* package */
Connection( LDAPSocketFactory factory)
{
if( factory != null) {
/* verify the 'setFactory' permision is set */
SecurityManager security = System.getSecurityManager();
if (security != null){
security.checkSetFactory();
}
// save socket factory
mySocketFactory = factory;
} else {
mySocketFactory = socketFactory;
}
if( Debug.LDAP_DEBUG) {
synchronized(nameLock) {
name = "Connection(" + ++connNum + "): ";
}
Debug.trace( Debug.messages, name + "Created");
}
return;
}
/**
* Copy this Connection object.
*
*
This is not a true clone, but creates a new object encapsulating
* part of the connection information from the original object.
* The new object will have the same default socket factory,
* designated socket factory, host, port, and protocol version
* as the original object.
* The new object is NOT be connected to the host.
*
* @return a shallow copy of this object
*/
/* package */
Object copy()
{
Connection c = new Connection(this.mySocketFactory);
c.host = this.host;
c.port = this.port;
c.protocol = this.protocol;
return c;
}
/**
* Create a new Connection object
*
* @param timeout specifies the socket timeout to be used when the server is stalled.
*/
Connection( int timeout)
{
myTimeOut = timeout;
if( Debug.LDAP_DEBUG) {
synchronized(nameLock) {
name = "Connection(" + ++connNum + "): ";
}
Debug.trace( Debug.messages, name + "Created");
}
return;
}
/**
* Copy this Connection object.
*
* This is not a true clone, but creates a new object encapsulating
* part of the connection information from the original object.
* The new object will have the same default socket factory,
* designated socket factory, host, port, and protocol version
* as the original object.
* The new object is NOT be connected to the host.
*
* @return a shallow copy of this object
*/
/* package */
Object copy_timeout()
{
Connection c = new Connection(this.myTimeOut);
c.host = this.host;
c.port = this.port;
c.protocol = this.protocol;
return c;
}
/**
* Acquire a simple counting semaphore that synchronizes state affecting
* bind. This method generates an ephemeral message id (negative number).
*
* We bind using the message ID because a different thread may unlock
* the semaphore than the one that set it. It is cleared when the
* response to the bind is processed, or when the bind operation times out.
*
* Returns when the semaphore is acquired
*
* @return the ephemeral message id that identifies semaphore's owner
*/
/* package */
final int acquireWriteSemaphore()
{
return acquireWriteSemaphore(0);
}
/**
* Acquire a simple counting semaphore that synchronizes state affecting
* bind. The semaphore is held by setting a value in writeSemaphoreOwner.
*
* We bind using the message ID because a different thread may unlock
* the semaphore than the one that set it. It is cleared when the
* response to the bind is processed, or when the bind operation times out.
* Returns when the semaphore is acquired.
*
* @param msgId a value that identifies the owner of this semaphore. A
* value of zero means assign a unique semaphore value.
*
* @return the semaphore value used to acquire the lock
*/
/* package */
final int acquireWriteSemaphore(int msgId)
{
int id = msgId;
synchronized( writeSemaphore) {
if( id == 0) {
ephemeralId = ((ephemeralId == Integer.MIN_VALUE)
? (ephemeralId = -1) : --ephemeralId);
id = ephemeralId;
}
while( true) {
if( writeSemaphoreOwner == 0) {
// we have acquired the semahpore
writeSemaphoreOwner = id;
break;
} else {
if( writeSemaphoreOwner == id) {
// we already own the semahpore
break;
}
try {
// Keep trying for the lock
writeSemaphore.wait();
continue;
} catch( InterruptedException ex) {
// Keep trying for the lock
continue;
}
}
}
writeSemaphoreCount++;
}
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.bindSemaphore, name +
"Acquired Socket Write Semaphore(" + id + ") count " +
writeSemaphoreCount);
}
return id;
}
/**
* Release a simple counting semaphore that synchronizes state affecting
* bind. Frees the semaphore when number of acquires and frees for this
* thread match.
*
* @param msgId a value that identifies the owner of this semaphore
*/
/* package */
final void freeWriteSemaphore(int msgId)
{
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.bindSemaphore, name +
"Free'd Socket Write Semaphore(" + msgId + ") count " +
(writeSemaphoreCount - 1));
}
synchronized( writeSemaphore) {
if( writeSemaphoreOwner == 0) {
throw new RuntimeException("Connection.freeWriteSemaphore("
+ msgId + "): semaphore not owned by any thread");
} else
if( writeSemaphoreOwner != msgId) {
throw new RuntimeException("Connection.freeWriteSemaphore("
+ msgId + "): thread does not own the semaphore, owned by "
+ writeSemaphoreOwner);
}
// if all instances of this semaphore for this thread are released,
// wake up all threads waiting.
if( --writeSemaphoreCount == 0) {
writeSemaphoreOwner = 0;
writeSemaphore.notify();
}
}
return;
}
/*
* Wait until the reader thread ID matches the specified parameter.
* Null = wait for the reader to terminate
* Non Null = wait for the reader to start
* Returns when the ID matches, i.e. reader stopped, or reader started.
*
* @param the thread id to match
*/
private void waitForReader( Thread thread)
throws LDAPException
{
// wait for previous reader thread to terminate
while( reader != thread) {
// Don't initialize connection while previous reader thread still
// active.
try {
if( Debug.LDAP_DEBUG) {
if( thread == null) {
Debug.trace( Debug.messages, name +
"waiting for reader thread to exit");
} else {
Debug.trace( Debug.messages, name +
"waiting for reader thread to start");
}
}
/*
* The reader thread may start and immediately terminate.
* To prevent the waitForReader from waiting forever
* for the dead to rise, we leave traces of the deceased.
* If the thread is already gone, we throw an exception.
*/
if( thread == deadReader) {
if (thread == null) /* then we wanted a shutdown */
return;
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"reader already terminated, throw exception");
}
IOException lex = deadReaderException;
deadReaderException = null;
deadReader = null;
// Reader thread terminated
throw new LDAPException(
ExceptionMessages.CONNECTION_READER,
LDAPException.CONNECT_ERROR, null, lex);
}
synchronized( this) {
this.wait(5);
}
} catch ( InterruptedException ex) {
;
}
}
deadReaderException = null;
deadReader = null;
return;
}
/**
* Constructs a TCP/IP connection to a server specified in host and port.
*
* @param host The host to connect to.
*
* @param port The port on the host to connect to.
*/
/* package */
void connect(String host, int port)
throws LDAPException
{
connect( host, port, 0);
return;
}
/**
* Constructs a TCP/IP connection to a server specified in host and port.
* Starts the reader thread.
*
* @param host The host to connect to.
*
* @param port The port on the host to connect to.
*
* @param semaphoreId The write semaphore ID to use for the connect
*/
private void connect(String host, int port, int semaphoreId)
throws LDAPException
{
/* Synchronized so all variables are in a consistant state and
* so that another thread isn't doing a connect, disconnect, or clone
* at the same time.
*/
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"connect(" + host + "," + port + ")");
}
// Wait for active reader to terminate
waitForReader(null);
// Clear the server shutdown notification flag. This should already
// be false unless of course we are reusing the same Connection object
// after a server shutdown notification
unsolSvrShutDnNotification = false;
int semId = acquireWriteSemaphore( semaphoreId);
// Make socket connection to specified host and port
if( port == 0) {
port = LDAPConnection.DEFAULT_PORT;
}
try {
if( (in == null) || (out == null) ) {
if(mySocketFactory != null) {
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"connect(socketFactory specified)");
}
socket = mySocketFactory.createSocket(host, port);
} else {
socket = new Socket(host, port);
if(myTimeOut > 0)
{
socket.setSoTimeout(myTimeOut);
}
}
in = socket.getInputStream();
out = socket.getOutputStream();
} else {
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"connect(input/out Stream specified)");
}
}
}catch(IOException ioe) {
// Unable to connect to server host:port
freeWriteSemaphore(semId);
throw new LDAPException(
ExceptionMessages.CONNECTION_ERROR,
new Object[] { host, new Integer(port) },
LDAPException.CONNECT_ERROR, null, ioe);
}
// Set host and port
this.host = host;
this.port = port;
// start the reader thread
this.startReader();
freeWriteSemaphore(semId);
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name + " connect: setup complete");
}
clientActive = true; // Client is up
return;
}
/**
* Indicates whether clones exist for LDAPConnection
*
* @return true if clones exist, false otherwise.
*/
/* package */
final boolean isCloned()
{
return( cloneCount > 0);
}
/**
* Increments the count of cloned connections
*/
/* package */
synchronized final void incrCloneCount()
{
cloneCount++;
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"incrCloneCount(" + cloneCount + ")");
}
return;
}
/**
* Destroys a clone of LDAPConnection
.
*
* This method first determines if only one LDAPConnection
* object is associated with this connection, i.e. if no clone exists.
*
* If no clone exists, the socket is closed, and the current
* Connection
object is returned.
*
* If multiple LDAPConnection
objects are associated
* with this connection, i.e. clones exist, a {@link #copy} of the
* this object is made, but is not connected to any host. This
* disassociates that clone from the original connection. The new
* Connection
object is returned.
*
*
Only one destroyClone instance is allowed to run at any one time.
*
* If the connection is closed, any threads waiting for operations
* on that connection will wake with an LDAPException indicating
* the connection is closed.
*
* @param apiCall true
indicates the application is closing the
* connection or or creating a new one by calling either the
* connect
or disconnect
methods
* of LDAPConnection
. false
* indicates that LDAPConnection
is being finalized.
*
* @return a Connection object or null if finalizing.
*/
/* package */
synchronized final Connection destroyClone( boolean apiCall)
{
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"destroyClone(" + apiCall + ")");
}
Connection conn = this;
if( cloneCount > 0) {
cloneCount--;
// This is a clone, set a new connection object.
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"destroyClone(" + cloneCount + ") create new connection");
}
if( apiCall) {
conn = (Connection)this.copy();
} else {
conn = null;
}
} else {
if( in != null) {
// Not a clone and connected
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"destroyClone(" + cloneCount +
") destroy old connection");
}
/*
* Either the application has called disconnect or connect
* resulting in the current connection being closed. If the
* application has any queues waiting on messages, we
* need wake these up so the application does not hang.
* The boolean flag indicates whether the close came
* from an API call or from the object being finalized.
*/
InterThreadException notify = new InterThreadException(
(apiCall ? ExceptionMessages.CONNECTION_CLOSED :
ExceptionMessages.CONNECTION_FINALIZED),
null, LDAPException.CONNECT_ERROR, null, null);
// Destroy old connection
shutdown("destroy clone", 0, notify);
}
}
return conn;
}
/**
* sets the default socket factory
*
* @param factory the default factory to set
*/
/* package */
final static void setSocketFactory( LDAPSocketFactory factory)
{
/* verify the 'setFactory' permision is set */
SecurityManager security = System.getSecurityManager();
if (security != null){
security.checkSetFactory();
}
socketFactory = factory;
return;
}
/**
* gets the socket factory used for this connection
*
* @return the default factory for this connection
*/
/* package */
final LDAPSocketFactory getSocketFactory()
{
return mySocketFactory;
}
/**
* gets the host used for this connection
*/
/* package */
final String getHost()
{
return host;
}
/**
* gets the port used for this connection
*/
/* package */
final int getPort()
{
return port;
}
/**
* gets the writeSemaphore id used for active bind operation
*/
/* package */
int getBindSemId()
{
return bindSemaphoreId;
}
/**
* sets the writeSemaphore id used for active bind operation
*/
/* package */
void setBindSemId(int id)
{
bindSemaphoreId = id;
return;
}
/**
* clears the writeSemaphore id used for active bind operation
*/
/* package */
void clearBindSemId()
{
bindSemaphoreId = 0;
return;
}
/**
* Gets SocketTimeOut value set.
*
* If not set, returns 0.
*
*/
final int getSocketTimeOut()
{
return myTimeOut;
}
/**
* Sets the SocketTimeOut value.
*
*/
final void setSocketTimeOut(int timeout)
{
try
{
socket.setSoTimeout(timeout);
myTimeOut = timeout;
} catch(SocketException e) {}
return;
}
/**
* checks if the writeSemaphore id used for active bind operation is clear
*/
/* package */
boolean isBindSemIdClear()
{
if( bindSemaphoreId == 0) {
return true;
}
return false;
}
/**
* Writes an LDAPMessage to the LDAP server over a socket.
*
* @param info the Message containing the message to write.
*/
/* package */
void writeMessage(Message info)
throws LDAPException
{
messages.addElement( info);
// For bind requests, if not connected, attempt to reconnect
if( info.isBindRequest() && (isConnected() == false) && (host != null)){
connect( host, port, info.getMessageID());
}
LDAPMessage msg = info.getRequest();
writeMessage( msg);
return;
}
/**
* Writes an LDAPMessage to the LDAP server over a socket.
*
* @param msg the message to write.
*/
/* package */
void writeMessage(LDAPMessage msg)
throws LDAPException
{
int id;
// Get the correct semaphore id for bind operations
if( bindSemaphoreId == 0) {
// Semaphore id for normal operations
id = msg.getMessageID();
} else {
// Semaphore id for sasl bind operations
id = bindSemaphoreId;
}
OutputStream myOut = out;
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name + "Writing Message(" +
msg.getMessageID() + ")");
Debug.trace( Debug.rawInput, name + "RawWrite: " +
msg.getASN1Object().toString());
}
acquireWriteSemaphore(id);
try {
if( myOut == null) {
throw new IOException("Output stream not initialized");
}
byte[] ber = msg.getASN1Object().getEncoding(encoder);
myOut.write(ber, 0, ber.length);
myOut.flush();
} catch( IOException ioe) {
if( Debug.LDAP_DEBUG ) {
Debug.trace( Debug.messages, name +
"I/O Exception on host" + host + ":" + port +
" " + ioe.toString());
}
/*
* IOException could be due to a server shutdown notification which
* caused our Connection to quit. If so we send back a slightly
* different error message. We could have checked this a little
* earlier in the method but that would be an expensive check each
* time we send out a message. Since this shutdown request is
* going to be an infrequent occurence we check for it only when
* we get an IOException. shutdown() will do the cleanup.
*/
if( clientActive) { // We beliefe the connection was alive
if (unsolSvrShutDnNotification) { // got server shutdown
throw new LDAPException( ExceptionMessages.SERVER_SHUTDOWN_REQ,
new Object[] { host, new Integer(port)},
LDAPException.CONNECT_ERROR, null,
ioe);
}
// Other I/O Exceptions on host:port are reported as is
throw new LDAPException(ExceptionMessages.IO_EXCEPTION,
new Object[] {host, new Integer(port)},
LDAPException.CONNECT_ERROR, null, ioe);
}
} finally {
freeWriteSemaphore(id);
}
return;
}
/**
* Returns the message agent for this msg ID
*/
/* package */
final MessageAgent getMessageAgent( int msgId)
throws NoSuchFieldException
{
Message info = messages.findMessageById( msgId);
return info.getMessageAgent();
}
/**
* Return whether the application is bound to this connection.
* Note: an anonymous bind returns false - not bound
*/
/* package */
final boolean isBound()
{
if( bindProperties != null) {
// Bound if not anonymous
return( ! bindProperties.isAnonymous());
}
return false;
}
/**
* Return whether a connection has been made
*/
/* package */
final boolean isConnected()
{
return (in != null);
}
/**
* Checks whether a connection is still alive or not by sending data to
* the server on this connection's socket.If the connection is not alive
* the send will generate an IOException and the function will return
* false.
* @return true If connection is alive
* false If connection is not alive.
*/
final boolean isConnectionAlive()
{
boolean isConn=false;
int id;
LDAPExtendedOperation op=null;
if ( in!= null ) {
isConn=true;
op= new LDAPExtendedOperation("0.0.0.0",null);
LDAPMessage msg =new LDAPExtendedRequest(op, null);
id = msg.getMessageID();
acquireWriteSemaphore(id);
OutputStream myOut = out;
try {
if( myOut == null) {
throw new IOException("Output stream not initialized");
}
byte[] ber = msg.getASN1Object().getEncoding(encoder);
myOut.write(ber, 0, ber.length);
myOut.flush();
} catch( IOException ioe) {
isConn=false;
}
finally {
freeWriteSemaphore(id);
}
}
return isConn;
}
/**
* Removes a Message class from the Connection's list
*
* @param info the Message class to remove from the list
*/
/* package */
final void removeMessage( Message info)
{
boolean done = messages.removeElement(info);
if( Debug.LDAP_DEBUG) {
if( done) {
Debug.trace( Debug.messages, name +
"Removed Message(" + info.getMessageID() + ")");
} else {
Debug.trace( Debug.messages, name +
"Removing Message(" + info.getMessageID() + ") - not found");
}
}
return;
}
/**
* Cleans up resources associated with this connection.
*/
protected void finalize()
{
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"finalize: shutdown connection");
}
shutdown("Finalize",0, null);
return;
}
/**
* Cleans up resources associated with this connection.
* This method may be called by finalize() for the connection, or it may
* be called by LDAPConnection.disconnect().
* Should not have a writeSemaphore lock in place, as deadlock can occur
* while abandoning connections.
*/
private void shutdown( String reason, int semaphoreId, InterThreadException notifyUser)
{
Message info = null;
if( ! clientActive) {
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"shutdown: already shutdown - " + reason);
}
return;
}
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"shutdown: Shutting down connection - " + reason);
}
clientActive = false;
while( true ) {
// remove messages from connection list and send abandon
try {
info = (Message)messages.remove(0);
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"Shutdown removed message(" + info.getMessageID() + ")");
}
} catch( ArrayIndexOutOfBoundsException ex) {
// No more messages
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"Shutdown no messages to remove");
}
break;
}
info.abandon( null, notifyUser); // also notifies the application
}
int semId = acquireWriteSemaphore( semaphoreId);
// Now send unbind if socket not closed
if( (bindProperties != null) &&
(out != null) &&
(! bindProperties.isAnonymous()))
{
try {
LDAPMessage msg = new LDAPUnbindRequest( null);
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.messages, name +
"Writing unbind request (" + msg.getMessageID() + ")");
Debug.trace( Debug.rawInput, name + "RawWrite: " +
msg.getASN1Object().toString());
}
byte[] ber = msg.getASN1Object().getEncoding(encoder);
out.write(ber, 0, ber.length);
out.flush();
} catch( Exception ex) {
; // don't worry about error
}
}
bindProperties = null;
in = null;
out = null;
if( socket != null) {
// Close the socket
try {
socket.close();
} catch(java.io.IOException ie) {
// ignore problem closing socket
}
socket = null;
}
// wait until reader threads stops completely
try {
if (reader!= Thread.currentThread())
reader.join();
// reader.join();
reader=null;
}
catch(InterruptedException iex) {
;
}
catch(NullPointerException npe) {
;
}
freeWriteSemaphore( semId);
return;
}
/**
*
* Sets the authentication credentials in the object
* and set flag indicating successful bind.
*
*
*
* @param bindProps The BindProperties object to set.
*/
/* package */
final void setBindProperties( BindProperties bindProps)
{
bindProperties = bindProps;
return;
}
/**
*
* Sets the authentication credentials in the object
* and set flag indicating successful bind.
*
*
*
* @return The BindProperties object for this connection.
*/
/* package */
final BindProperties getBindProperties()
{
return bindProperties;
}
/**
* This tests to see if there are any outstanding messages. If no messages
* are in the queue it returns true. Each message will be tested to
* verify that it is complete.
* The writeSemaphore must be set for this method to be reliable!
*
* @return true if no outstanding messages
*/
/* package */
final boolean areMessagesComplete(){
Object[] messages = this.messages.getObjectArray();
int length = messages.length;
if( Debug.LDAP_DEBUG) {
Debug.trace( Debug.TLS, "startTLS: areMessagesComplete? " +
"MessageVector size = " + length +
", bindSemaphoreId=" + bindSemaphoreId);
}
// Check if SASL bind in progress
if( bindSemaphoreId != 0) {
return false;
}
// Check if any messages queued
if(length == 0) {
return true;
}
for(int i=0; i