LDAPRequestHandler.java revision e3fed6978fb8085b119df2966b5c27785756d70b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2014 ForgeRock AS
*/
/**
* This class defines an LDAP request handler, which is associated with an LDAP
* connection handler and is responsible for reading and decoding any requests
* that LDAP clients may send to the server. Multiple request handlers may be
* used in conjunction with a single connection handler for better performance
* and scalability.
*/
public class LDAPRequestHandler
extends DirectoryThread
implements ServerShutdownListener
{
// Indicates whether the Directory Server is in the process of shutting down.
private volatile boolean shutdownRequested = false;
// The current set of selection keys.
// The queue that will be used to hold the set of pending connections that
// need to be registered with the selector.
// TODO: revisit, see Issue 4202.
new LinkedList<LDAPClientConnection>();
// Lock object for synchronizing access to the pending connections queue.
// The list of connections ready for request processing.
new LinkedList<LDAPClientConnection>();
// The selector that will be used to monitor the client connections.
// The name to use for this request handler.
private final String handlerName;
/**
* Creates a new LDAP request handler that will be associated with the
* provided connection handler.
*
* @param connectionHandler The LDAP connection handler with which this
* request handler is associated.
* @param requestHandlerID The integer value that may be used to distingush
* this request handler from others associated with
* the same connection handler.
*
* @throws InitializationException If a problem occurs while initializing
* this request handler.
*/
int requestHandlerID)
throws InitializationException
{
super("LDAP Request Handler " + requestHandlerID +
handlerName = getName();
try
{
}
catch (Exception e)
{
logger.traceException(e);
throw new InitializationException(message, e);
}
try
{
// Check to see if we get an error while trying to perform a select. If
// we do, then it's likely CR 6322825 and the server won't be able to
// handle LDAP requests in its current state.
}
catch (IOException ioe)
{
{
{
}
}
}
}
/**
* Operates in a loop, waiting for client requests to arrive and ensuring that
* they are processed properly.
*/
public void run()
{
// Operate in a loop until the server shuts down. Each time through the
// loop, check for new requests, then check for new connections.
while (!shutdownRequested)
{
{
try
{
boolean ldapMessageProcessed = false;
while (true)
{
if (asn1Reader.elementAvailable())
{
if (!ldapMessageProcessed)
{
{
ldapMessageProcessed = true;
}
else
{
break;
}
}
else
{
break;
}
}
else
{
{
break;
}
}
}
}
catch (DecodeException e)
{
logger.traceException(e);
e.getMessageObject());
}
catch (LDAPException e)
{
logger.traceException(e);
e.getMessageObject());
}
catch (Exception e)
{
logger.traceException(e);
}
}
// Check to see if we have any pending connections that need to be
// registered with the selector.
synchronized (pendingConnectionsLock)
{
if (!pendingConnections.isEmpty())
{
}
}
{
for (LDAPClientConnection c : tmp)
{
try
{
socketChannel.configureBlocking(false);
logConnect(c);
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
// Create a copy of the selection keys which can be used in a
// thread-safe manner by getClientConnections. This copy is only
// updated once per loop, so may not be accurate.
int selectedKeys = 0;
try
{
// We timeout every second so that we can refresh the key list.
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Should we do something else with this?
}
if (shutdownRequested)
{
// Avoid further processing and disconnect all clients.
break;
}
if (selectedKeys > 0)
{
{
try
{
if (key.isReadable())
{
try
{
try
{
if (readResult < 0)
{
}
if (readResult > 0) {
}
}
catch (Exception e)
{
logger.traceException(e);
// Some other error occurred while we were trying to read data
// from the client.
// FIXME -- Should we log this?
false, null);
}
}
catch (Exception e)
{
logger.traceException(e);
// We got some other kind of error. If nothing else, cancel the
// key, but if the client connection is available then
// disconnect it as well.
if (clientConnection != null)
{
false, null);
}
}
}
{
}
}
catch (CancelledKeyException cke)
{
// This could happen if a connection was closed between the time
// that select returned and the time that we try to access the
// associated channel. If that was the case, we don't need to do
// anything.
}
catch (Exception e)
{
logger.traceException(e);
// This should not happen, and it would have caused our reader
// thread to die. Log a severe error.
}
finally
{
{
// Help GC - release the connection.
}
}
}
}
}
// Disconnect all active connections.
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
// Disconnect all pending connections.
synchronized (pendingConnectionsLock)
{
for (LDAPClientConnection c : pendingConnections)
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
}
/**
* Registers the provided client connection with this request
* handler so that any requests received from that client will be
* processed.
*
* @param clientConnection
* The client connection to be registered with this request
* handler.
* @return <CODE>true</CODE> if the client connection was properly
* registered with this request handler, or
* <CODE>false</CODE> if not.
*/
{
// FIXME -- Need to check if the maximum client limit has been reached.
// If the server is in the process of shutting down, then we don't want to
// accept it.
if (shutdownRequested)
{
return false;
}
// Try to add the new connection to the queue. If it succeeds, then wake
// up the selector so it will be picked up right away. Otherwise,
// disconnect the client.
synchronized (pendingConnectionsLock)
{
}
return true;
}
/**
* Retrieves the set of all client connections that are currently registered
* with this request handler.
*
* @return The set of all client connections that are currently registered
* with this request handler.
*/
{
{
// If the client has disconnected the attachment may be null.
if (c != null)
{
}
}
return connList;
}
/**
* Retrieves the human-readable name for this shutdown listener.
*
* @return The human-readable name for this shutdown listener.
*/
public String getShutdownListenerName()
{
return handlerName;
}
/**
* Causes this request handler to register itself as a shutdown listener with
* the Directory Server. This must be called if the connection handler is
* shut down without closing all associated connections, otherwise the thread
* would not be stopped by the server.
*/
public void registerShutdownListener()
{
}
/**
* Indicates that the Directory Server has received a request to stop running
* and that this shutdown listener should take any action necessary to prepare
* for it.
*
* @param reason The human-readable reason for the shutdown.
*/
{
shutdownRequested = true;
}
}