BackendConfigManager.java revision 6dbb06deae8b725845c16596360285ef251e1997
/*
* 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-2008 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
*/
/**
* This class defines a utility that will be used to manage the configuration
* for the set of backends defined in the Directory Server. It will perform
* the necessary initialization of those backends when the server is first
* started, and then will manage any changes to them while the server is
* running.
*/
public class BackendConfigManager implements
{
/**
* The mapping between configuration entry DNs and their corresponding backend
* implementations.
*/
private final ConcurrentHashMap<DN, Backend<?>> registeredBackends = new ConcurrentHashMap<DN, Backend<?>>();
private final ServerContext serverContext;
/**
* Creates a new instance of this backend config manager.
*
* @param serverContext
* The server context.
*/
{
this.serverContext = serverContext;
}
/**
* Initializes the configuration associated with the Directory Server
* backends. This should only be called at Directory Server startup.
*
* @throws ConfigException
* If a critical configuration problem prevents the backend
* initialization from succeeding.
* @throws InitializationException
* If a problem occurs while initializing the backends that is not
* related to the server configuration.
*/
public void initializeBackendConfig()
{
// Create an internal server management context and retrieve
// the root configuration.
// Register add and delete listeners.
root.addBackendAddListener(this);
root.addBackendDeleteListener(this);
// Get the configuration entry that is at the root of all the backends in
// the server.
try
{
}
catch (Exception e)
{
logger.traceException(e);
throw new ConfigException(message, e);
}
// If the configuration root entry is null, then assume it doesn't exist.
// In that case, then fail. At least that entry must exist in the
// configuration, even if there are no backends defined below it.
if (backendRoot == null)
{
throw new ConfigException(message);
}
// Initialize existing backends.
{
// Get the handler's configuration.
// This will decode and validate its properties.
// Register as a change listener for this backend so that we can be
// notified when it is disabled or enabled.
backendCfg.addChangeListener(this);
// Ignore this handler if it is disabled.
if (backendCfg.isEnabled())
{
// If there is already a backend registered with the specified ID,
// then log an error and skip it.
{
continue;
}
// See if the entry contains an attribute that specifies the class name
// for the backend implementation. If it does, then load it and make
// sure that it's a valid backend implementation. There is no such
// attribute, the specified class cannot be loaded, or it does not
// contain a valid backend implementation, then log an error and skip it.
try
{
}
catch (Exception e)
{
logger.traceException(e);
logger.error(ERR_CONFIG_BACKEND_CANNOT_INSTANTIATE, className, backendDN, stackTraceToSingleLineString(e));
continue;
}
// If this backend is a configuration manager, then we don't want to do
// any more with it because the configuration will have already been
// started.
if (backend instanceof ConfigHandler)
{
continue;
}
// Set the backend ID and writability mode for this backend.
// Acquire a shared lock on this backend. This will prevent operations
// like LDIF import or restore from occurring while the backend is
// active.
try
{
{
// FIXME -- Do we need to send an admin alert?
continue;
}
}
catch (Exception e)
{
logger.traceException(e);
logger.error(ERR_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK, backendID, stackTraceToSingleLineString(e));
// FIXME -- Do we need to send an admin alert?
continue;
}
// Perform the necessary initialization for the backend entry.
try
{
}
catch (Exception e)
{
logger.traceException(e);
logger.error(ERR_CONFIG_BACKEND_CANNOT_INITIALIZE, className, backendDN, stackTraceToSingleLineString(e));
try
{
{
// FIXME -- Do we need to send an admin alert?
}
}
{
logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backendID, stackTraceToSingleLineString(e2));
// FIXME -- Do we need to send an admin alert?
}
continue;
}
// Notify any backend initialization listeners.
{
}
// Register the backend with the server.
try
{
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Do we need to send an admin alert?
}
// Put this backend in the hash so that we will be able to find it if it
// is altered.
}
else
{
// The backend is explicitly disabled. Log a mild warning and continue.
}
}
}
/** {@inheritDoc} */
public boolean isConfigurationChangeAcceptable(
{
// See if the backend is registered with the server. If it is, then
// see what's changed and whether those changes are acceptable.
{
{
{
}
}
// Copy the directory server's base DN registry and make the
// requested changes to see if it complains.
{
try
{
}
catch (DirectoryException de)
{
return false;
}
}
{
try
{
}
catch (DirectoryException de)
{
return false;
}
}
}
// See if the entry contains an attribute that specifies the class name
// for the backend implementation. If it does, then load it and make sure
// that it's a valid backend implementation. There is no such attribute,
// the specified class cannot be loaded, or it does not contain a valid
// backend implementation, then log an error and skip it.
try
{
{
return false;
}
{
return false;
}
}
catch (Exception e)
{
logger.traceException(e);
return false;
}
// If we've gotten to this point, then it is acceptable as far as we are
// concerned. If it is unacceptable according to the configuration for that
// backend, then the backend itself will need to make that determination.
return true;
}
/** {@inheritDoc} */
{
// See if the entry contains an attribute that indicates whether the
// backend should be enabled.
boolean needToEnable = false;
try
{
{
// The backend is marked as enabled. See if that is already true.
{
needToEnable = true;
} // else already enabled, no need to do anything.
}
else
{
// The backend is marked as disabled. See if that is already true.
{
// It isn't disabled, so we will do so now and deregister it from the
// Directory Server.
{
}
// Remove the shared lock for this backend.
try
{
{
// FIXME -- Do we need to send an admin alert?
}
}
{
// FIXME -- Do we need to send an admin alert?
}
return ccr;
} // else already disabled, no need to do anything.
}
}
catch (Exception e)
{
logger.traceException(e);
return ccr;
}
// See if the entry contains an attribute that specifies the class name
// for the backend implementation. If it does, then load it and make sure
// that it's a valid backend implementation. There is no such attribute,
// the specified class cannot be loaded, or it does not contain a valid
// backend implementation, then log an error and skip it.
// See if this backend is currently active and if so if the name of the
// class is the same.
{
// It is not the same. Try to load it and see if it is a valid backend
// implementation.
try
{
{
// It appears to be a valid backend class. We'll return that the
// change is successful, but indicate that some administrative
// action is required.
ccr.setAdminActionRequired(true);
}
else
{
// It is not a valid backend class. This is an error.
}
return ccr;
}
catch (Exception e)
{
logger.traceException(e);
return ccr;
}
}
// If we've gotten here, then that should mean that we need to enable the
// backend. Try to do so.
if (needToEnable)
{
try
{
}
catch (Exception e)
{
// It is not a valid backend class. This is an error.
return ccr;
}
// Set the backend ID and writability mode for this backend.
// Acquire a shared lock on this backend. This will prevent operations
// like LDIF import or restore from occurring while the backend is active.
try
{
{
LocalizableMessage message = ERR_CONFIG_BACKEND_CANNOT_ACQUIRE_SHARED_LOCK.get(backendID,failureReason);
// FIXME -- Do we need to send an admin alert?
ccr.setAdminActionRequired(true);
return ccr;
}
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Do we need to send an admin alert?
ccr.setAdminActionRequired(true);
return ccr;
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
try
{
{
// FIXME -- Do we need to send an admin alert?
}
}
{
// FIXME -- Do we need to send an admin alert?
}
return ccr;
}
// Notify any backend initialization listeners.
{
}
// Register the backend with the server.
try
{
}
catch (Exception e)
{
logger.traceException(e);
backendID, getExceptionMessage(e));
// FIXME -- Do we need to send an admin alert?
return ccr;
}
}
{
}
return ccr;
}
/** {@inheritDoc} */
public boolean isConfigurationAddAcceptable(
{
// See if the entry contains an attribute that specifies the backend ID. If
// it does not, then skip it.
{
return false;
}
// See if the entry contains an attribute that specifies the set of base DNs
// for the backend. If it does not, then skip it.
// See if the entry contains an attribute that specifies the class name
// for the backend implementation. If it does, then load it and make sure
// that it's a valid backend implementation. There is no such attribute,
// the specified class cannot be loaded, or it does not contain a valid
// backend implementation, then log an error and skip it.
try
{
}
catch (Exception e)
{
logger.traceException(e);
return false;
}
// Make sure that all of the base DNs are acceptable for use in the server.
{
try
{
}
catch (DirectoryException de)
{
return false;
}
catch (Exception e)
{
return false;
}
}
{
return false;
}
// If we've gotten to this point, then it is acceptable as far as we are
// concerned. If it is unacceptable according to the configuration for that
// backend, then the backend itself will need to make that determination.
return true;
}
/** {@inheritDoc} */
{
// Register as a change listener for this backend entry so that we will
// be notified of any changes that may be made to it.
cfg.addChangeListener(this);
// See if the entry contains an attribute that indicates whether the
// backend should be enabled. If it does not, or if it is not set to
// "true", then skip it.
{
// The backend is explicitly disabled. We will log a message to
// indicate that it won't be enabled and return.
return ccr;
}
// See if the entry contains an attribute that specifies the backend ID. If
// it does not, then skip it.
{
return ccr;
}
// See if the entry contains an attribute that specifies the class name
// for the backend implementation. If it does, then load it and make sure
// that it's a valid backend implementation. There is no such attribute,
// the specified class cannot be loaded, or it does not contain a valid
// backend implementation, then log an error and skip it.
try
{
}
catch (Exception e)
{
logger.traceException(e);
return ccr;
}
// Set the backend ID and writability mode for this backend.
// Acquire a shared lock on this backend. This will prevent operations
// like LDIF import or restore from occurring while the backend is active.
try
{
{
// FIXME -- Do we need to send an admin alert?
ccr.setAdminActionRequired(true);
return ccr;
}
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Do we need to send an admin alert?
ccr.setAdminActionRequired(true);
return ccr;
}
// Perform the necessary initialization for the backend entry.
try
{
}
catch (Exception e)
{
logger.traceException(e);
try
{
{
// FIXME -- Do we need to send an admin alert?
}
}
{
logger.warn(WARN_CONFIG_BACKEND_CANNOT_RELEASE_SHARED_LOCK, backendID, stackTraceToSingleLineString(e2));
// FIXME -- Do we need to send an admin alert?
}
return ccr;
}
// Notify any backend initialization listeners.
{
}
// At this point, the backend should be online. Add it as one of the
// registered backends for this backend config manager.
try
{
}
catch (Exception e)
{
logger.traceException(e);
backendID, getExceptionMessage(e));
// FIXME -- Do we need to send an admin alert?
return ccr;
}
return ccr;
}
@SuppressWarnings("unchecked")
{
}
{
switch (writabilityMode)
{
case DISABLED:
return WritabilityMode.DISABLED;
case ENABLED:
return WritabilityMode.ENABLED;
case INTERNAL_ONLY:
return WritabilityMode.INTERNAL_ONLY;
default:
return WritabilityMode.ENABLED;
}
}
/** {@inheritDoc} */
public boolean isConfigurationDeleteAcceptable(
{
// See if this backend config manager has a backend registered with the
// provided DN. If not, then we don't care if the entry is deleted. If we
// do know about it, then that means that it is enabled and we will not
// allow removing a backend that is enabled.
{
return true;
}
// See if the backend has any subordinate backends. If so, then it is not
// acceptable to remove it. Otherwise, it should be fine.
{
return false;
}
return true;
}
/** {@inheritDoc} */
{
// See if this backend config manager has a backend registered with the
// provided DN. If not, then we don't care if the entry is deleted.
{
return ccr;
}
// See if the backend has any subordinate backends. If so, then it is not
// acceptable to remove it. Otherwise, it should be fine.
{
return ccr;
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
{
}
configEntry.removeChangeListener(this);
// Remove the shared lock for this backend.
try
{
{
// FIXME -- Do we need to send an admin alert?
}
}
{
// FIXME -- Do we need to send an admin alert?
}
return ccr;
}
{
}
}