/*
* 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
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2013-2014 ForgeRock AS
*/
/**
* This class defines a connection handler that will be used for communicating
* with clients over HTTP. The connection handler is responsible for
*/
public class HTTPConnectionHandler extends
ConnectionHandler<HTTPConnectionHandlerCfg> implements
{
/** The tracer object for the debug logger. */
/** Default friendly name for this connection handler. */
/** SSL instance name used in context creation. */
/** The initialization configuration. */
/** The current configuration. */
/**
* Indicates whether the Directory Server is in the process of shutting down.
*/
private volatile boolean shutdownRequested;
/** Indicates whether this connection handler is enabled. */
private boolean enabled;
/** The set of listeners for this connection handler. */
/** The HTTP server embedded in OpenDJ. */
/** The HTTP probe that collects stats. */
/**
* Holds the current client connections. Using {@link ConcurrentHashMap} to
* only use the keys, so it does not matter what value is put there.
*/
/** The set of statistics collected for this connection handler. */
/**
* The client connection monitor provider associated with this connection
* handler.
*/
/** The unique name assigned to this connection handler. */
/** The protocol used by this connection handler. */
/**
* The condition variable that will be used by the start method to wait for
* the socket port to be opened and ready to process requests before
* returning.
*/
/** The friendly name of this connection handler. */
/**
* The SSL engine configurator is used for obtaining default SSL parameters.
*/
/**
* Default constructor. It is invoked by reflection to create this
* {@link ConnectionHandler}.
*/
public HTTPConnectionHandler()
{
super(DEFAULT_FRIENDLY_NAME);
}
/**
* Returns whether unauthenticated HTTP requests are allowed. The server
* checks whether unauthenticated requests are allowed server-wide first then
* for the HTTP Connection Handler second.
*
* @return true if unauthenticated requests are allowed, false otherwise.
*/
public boolean acceptUnauthenticatedRequests()
{
// the global setting overrides the more specific setting here.
return !DirectoryServer.rejectUnauthenticatedRequests()
&& !this.currentConfig.isAuthenticationRequired();
}
/**
* Registers a client connection to track it.
*
* @param clientConnection
* the client connection to register
*/
{
}
/** {@inheritDoc} */
{
// Create variables to include in the response.
boolean adminActionRequired = false;
{
adminActionRequired = true;
}
// Reconfigure SSL if needed.
try
{
}
catch (DirectoryException e)
{
if (debugEnabled())
{
}
messages);
}
{ // server was running and will still be running
// if the "enabled" was flipped, leave it to the stop / start server to
// handle it
{ // it must now keep stats while it was not previously
setHttpStatsProbe(this.httpServer);
}
{ // it must NOT keep stats anymore
}
}
this.initConfig = config;
this.currentConfig = config;
messages);
}
{
}
{
{
}
}
{
}
{
}
throws DirectoryException
{
{
}
else
{
}
}
/** {@inheritDoc} */
{
shutdownRequested = true;
// Unregister this as a change listener.
if (connMonitor != null)
{
}
if (statTracker != null)
{
}
}
/** {@inheritDoc} */
{
return alerts;
}
/** {@inheritDoc} */
{
return HTTPConnectionHandler.class.getName();
}
/** {@inheritDoc} */
{
return clientConnections.keySet();
}
/** {@inheritDoc} */
{
return currentConfig.dn();
}
/** {@inheritDoc} */
{
return handlerName;
}
/**
* Returns the current config of this connection handler.
*
* @return the current config of this connection handler
*/
{
return this.currentConfig;
}
/** {@inheritDoc} */
{
if (configurator != null)
{
}
return super.getEnabledSSLCipherSuites();
}
/** {@inheritDoc} */
{
if (configurator != null)
{
}
return super.getEnabledSSLProtocols();
}
/** {@inheritDoc} */
{
return listeners;
}
/**
* Returns the listen port for this connection handler.
*
* @return the listen port for this connection handler.
*/
int getListenPort()
{
return this.initConfig.getListenPort();
}
/** {@inheritDoc} */
{
return protocol;
}
/**
* Returns the SSL engine configured for this connection handler if SSL is
* enabled, null otherwise.
*
* @return the SSL engine if SSL is enabled, null otherwise
*/
{
return sslEngineConfigurator.createSSLEngine();
}
/** {@inheritDoc} */
{
return handlerName;
}
/**
* Retrieves the set of statistics maintained by this connection handler.
*
* @return The set of statistics maintained by this connection handler.
*/
{
return statTracker;
}
/** {@inheritDoc} */
{
if (friendlyName == null)
{
}
{
}
// Configure SSL if needed.
try
{
}
catch (DirectoryException e)
{
if (debugEnabled())
{
}
throw new InitializationException(e.getMessageObject());
}
// Create and register monitors.
connMonitor = new ClientConnectionMonitorProvider(this);
// Register this as a change listener.
config.addHTTPChangeListener(this);
this.initConfig = config;
this.currentConfig = config;
}
{
{
}
return nameBuffer.toString();
}
/** {@inheritDoc} */
{
{
// Attempt to bind to the listen port on all configured addresses to
// verify whether the connection handler will be able to start.
if (errorMessage != null)
{
return false;
}
}
{
// Check that the SSL configuration is valid.
{
try
{
}
catch (DirectoryException e)
{
if (debugEnabled())
{
}
return false;
}
}
}
return true;
}
/**
* Checks whether any listen address is in use for the given port. The check
* is performed by binding to each address and port.
*
* @param listenAddresses
* the listen {@link InetAddress} to test
* @param listenPort
* the listen port to test
* @param allowReuseAddress
* whether addresses can be reused
* @param configEntryDN
* the configuration entry DN
* @return an error message if at least one of the address is already in use,
* null otherwise.
*/
{
for (InetAddress a : listenAddresses)
{
try
{
{
}
}
catch (IOException e)
{
if (debugEnabled())
{
}
getExceptionMessage(e));
}
}
return null;
}
/** {@inheritDoc} */
public boolean isConfigurationChangeAcceptable(
{
}
/**
* Indicates whether this connection handler should maintain usage statistics.
*
* @return <CODE>true</CODE> if this connection handler should maintain usage
* statistics, or <CODE>false</CODE> if not.
*/
public boolean keepStats()
{
return currentConfig.isKeepStats();
}
/** {@inheritDoc} */
{
shutdownRequested = true;
}
private boolean isListening()
{
return httpServer != null;
}
/** {@inheritDoc} */
public void start()
{
// The Directory Server start process should only return
// when the connection handlers port are fully opened
// and working. The start method therefore needs to wait for
// the created thread to
synchronized (waitListen)
{
super.start();
try
{
waitListen.wait();
}
catch (InterruptedException e)
{
// If something interrupted the start its probably better
// to return ASAP.
}
}
}
/**
* Unregisters a client connection to stop tracking it.
*
* @param clientConnection
* the client connection to unregister
*/
{
}
/** {@inheritDoc} */
public void run()
{
boolean lastIterationFailed = false;
while (!shutdownRequested)
{
// If this connection handler is not enabled, then just sleep
// for a bit and check again.
if (!this.enabled)
{
if (isListening())
{
}
continue;
}
if (isListening())
{
// If already listening, then sleep for a bit and check again.
continue;
}
try
{
// At this point, the connection Handler either started
// correctly or failed to start but the start process
// should be notified and resume its work in any cases.
synchronized (waitListen)
{
waitListen.notify();
}
// If we have gotten here, then we are about to start listening
// for the first time since startup or since we were previously
// disabled. Start the embedded HTTP server
lastIterationFailed = false;
}
catch (Exception e)
{
// clean up the messed up HTTP server
// error + alert about the horked config
if (debugEnabled())
{
}
if (lastIterationFailed)
{
// The last time through the accept loop we also
// encountered a failure. Rather than enter a potential
// infinite loop of failures, disable this acceptor and
// log an error.
this.enabled = false;
}
else
{
lastIterationFailed = true;
}
}
}
// Initiate shutdown
}
{
// silence Grizzly's own logging
{
}
this.httpServer = createHttpServer();
// register servlet as default servlet and also able to serve REST requests
this.httpServer.start();
}
{
if (keepStats())
{
}
// configure the network listener
final NetworkListener listener =
// configure the network transport
final int numRequestHandlers =
// configure SSL
if (sslEngineConfigurator != null)
{
}
return server;
}
{
}
{
return monitoringCfg.getHttpConfig();
}
{
// parse and use JSON config
final JsonValue configuration =
new CollectClientConnectionsFilter(this, authenticationConfig);
// Used for hooking our HTTPClientConnection in Rest2LDAP
// Create and deploy the Web app context
}
final JsonValue configuration)
{
"supportHTTPBasicAuthentication"));
"supportAltAuthentication"));
.asString());
.asString());
return result;
}
{
}
{
}
{
{
final CollectionResourceProvider provider =
}
}
{
// Parse the config file.
{
+ "' does not contain a valid JSON configuration");
}
// TODO JNR should we restrict the possible configurations in this file?
// Should we remove any config that does not make any sense to the
// HTTP Connection Handler?
}
private void stopHttpServer()
{
if (this.httpServer != null)
{
this.httpServer.shutdownNow();
}
}
private void cleanUpHttpServer()
{
this.httpServer = null;
}
/** {@inheritDoc} */
{
}
{
{
return null;
}
try
{
configurator.setClientMode(false);
// configure with defaults from the JVM
{
}
{
}
switch (config.getSSLClientAuthPolicy())
{
case DISABLED:
configurator.setNeedClientAuth(false);
configurator.setWantClientAuth(false);
break;
case REQUIRED:
configurator.setNeedClientAuth(true);
configurator.setWantClientAuth(true);
break;
case OPTIONAL:
default:
configurator.setNeedClientAuth(false);
configurator.setWantClientAuth(true);
break;
}
return configurator;
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
throws Exception
{
{
return null;
}
if (keyManagerProvider == null)
{
keyManagerProvider = new NullKeyManagerProvider();
}
{
}
else
{
.getKeyManagers(), alias);
}
if (trustManagerProvider == null)
{
}
return sslContext;
}
}