GroupManager.java revision 5081e9ab01cd629017696999c99b593b6d746f63
/*
* 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 2007-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* This class provides a mechanism for interacting with all groups defined in
* the Directory Server. It will handle all necessary processing at server
* startup to identify and load all group implementations, as well as to find
* all group instances within the server.
* <BR><BR>
* FIXME: At the present time, it assumes that all of the necessary
* information about all of the groups defined in the server can be held in
* memory. If it is determined that this approach is not workable in all cases,
* then we will need an alternate strategy.
*/
public class GroupManager extends InternalDirectoryServerPlugin
implements ConfigurationChangeListener<GroupImplementationCfg>,
{
/**
* Used by group instances to determine if new groups have been registered or
* groups deleted.
*/
private volatile long refreshToken;
/**
* A mapping between the DNs of the config entries and the associated group
* implementations.
*/
/**
* A mapping between the DNs of all group entries and the corresponding group
* instances.
*/
/** Lock to protect internal data structures. */
private final ReentrantReadWriteLock lock;
/** Dummy configuration DN for Group Manager. */
private final ServerContext serverContext;
/**
* Creates a new instance of this group manager.
*
* @param serverContext
* The server context.
* @throws DirectoryException
* If a problem occurs while creating an instance of the group
* manager.
*/
{
PluginType.POST_SYNCHRONIZATION_MODIFY_DN), true);
this.serverContext = serverContext;
groupImplementations = new ConcurrentHashMap<>();
groupInstances = new DITCacheMap<>();
lock = new ReentrantReadWriteLock();
}
/**
* Initializes all group implementations currently defined in the Directory
* Server configuration. This should only be called at Directory Server
* startup.
*
* @throws ConfigException If a configuration problem causes the group
* implementation initialization process to fail.
*
* @throws InitializationException If a problem occurs while initializing
* the group implementations that is not
* related to the server configuration.
*/
public void initializeGroupImplementations()
{
// Get the root configuration object.
// Register as an add and delete listener with the root configuration so we
// can be notified if any group implementation entries are added or removed.
//Initialize the existing group implementations.
{
if (groupConfiguration.isEnabled())
{
try
{
}
catch (InitializationException ie)
{
// Log error but keep going
}
}
}
}
/** {@inheritDoc} */
public boolean isConfigurationAddAcceptable(
{
if (configuration.isEnabled())
{
try
{
}
catch (InitializationException ie)
{
return false;
}
}
return true;
}
/** {@inheritDoc} */
{
configuration.addChangeListener(this);
if (! configuration.isEnabled())
{
return ccr;
}
try
{
}
catch (InitializationException ie)
{
}
{
}
// FIXME -- We need to make sure to find all groups of this type in the
// server before returning.
return ccr;
}
/** {@inheritDoc} */
public boolean isConfigurationDeleteAcceptable(
{
// FIXME -- We should try to perform some check to determine whether the
// group implementation is in use.
return true;
}
/** {@inheritDoc} */
{
{
try
{
{
{
}
}
}
finally
{
}
}
return ccr;
}
/** {@inheritDoc} */
public boolean isConfigurationChangeAcceptable(
{
if (configuration.isEnabled())
{
try
{
}
catch (InitializationException ie)
{
return false;
}
}
return true;
}
/** {@inheritDoc} */
{
// Get the existing group implementation if it's already enabled.
// If the new configuration has the group implementation disabled, then
// disable it if it is enabled, or do nothing if it's already disabled.
if (! configuration.isEnabled())
{
if (existingGroup != null)
{
{
try
{
{
{
}
}
}
finally
{
}
}
}
return ccr;
}
// Get the class for the group implementation. If the group is already
// enabled, then we shouldn't do anything with it although if the class has
// changed then we'll at least need to indicate that administrative action
// is required. If the group implementation is disabled, then instantiate
// the class and initialize and register it as a group implementation.
if (existingGroup != null)
{
{
ccr.setAdminActionRequired(true);
}
return ccr;
}
try
{
}
catch (InitializationException ie)
{
}
{
}
// FIXME -- We need to make sure to find all groups of this type in the
// server before returning.
return ccr;
}
/**
* Loads the specified class, instantiates it as a group implementation, and
* optionally initializes that instance.
*
* @param className The fully-qualified name of the group implementation
* class to load, instantiate, and initialize.
* @param configuration The configuration to use to initialize the group
* implementation. It must not be {@code null}.
* @param initialize Indicates whether the group implementation instance
* should be initialized.
*
* @return The possibly initialized group implementation.
*
* @throws InitializationException If a problem occurred while attempting to
* initialize the group implementation.
*/
boolean initialize)
throws InitializationException
{
try
{
if (initialize)
{
}
else
{
{
}
}
return group;
}
catch (Exception e)
{
throw new InitializationException(message, e);
}
}
/**
* Performs any cleanup work that may be needed when the server is shutting
* down.
*/
public void finalizeGroupManager()
{
{
}
}
/**
* Retrieves an {@code Iterable} object that may be used to cursor across the
* group implementations defined in the server.
*
* @return An {@code Iterable} object that may be used to cursor across the
* group implementations defined in the server.
*/
{
return groupImplementations.values();
}
/**
* Retrieves an {@code Iterable} object that may be used to cursor across the
* group instances defined in the server.
*
* @return An {@code Iterable} object that may be used to cursor across the
* group instances defined in the server.
*/
{
try
{
// Return a copy to protect from structural changes.
}
finally
{
}
}
/**
* Retrieves the group instance defined in the entry with the specified DN.
*
* @param entryDN The DN of the entry containing the definition of the group
* instance to retrieve.
*
* @return The group instance defined in the entry with the specified DN, or
* {@code null} if no such group is currently defined.
*/
{
try
{
}
finally
{
}
}
/**
* {@inheritDoc} In this case, the server will search the backend to find
* all group instances that it may contain and register them with this group
* manager.
*/
{
{
try
{
{
}
}
catch (Exception e)
{
logger.traceException(e);
continue;
}
{
try
{
{
continue;
}
}
catch (Exception e)
{
logger.traceException(e);
continue;
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Is there anything that we need to do here?
continue;
}
try
{
{
try
{
refreshToken++;
}
catch (DirectoryException e)
{
logger.traceException(e);
// Nothing specific to do, as it's already logged.
}
}
}
finally
{
}
}
}
}
/**
* {@inheritDoc} In this case, the server will de-register all group
* instances associated with entries in the provided backend.
*/
{
try
{
{
{
}
}
}
finally
{
}
}
/**
* In this case, each entry is checked to see if it contains
* a group definition, and if so it will be instantiated and
* registered with this group manager.
*/
{
{
return;
}
}
{
if (requestControls != null)
{
for (Control c : requestControls)
{
{
return true;
}
}
}
return false;
}
/**
* In this case, if the entry is associated with a registered
* group instance, then that group instance will be deregistered.
*/
{
{
return;
}
try
{
{
refreshToken++;
}
}
finally
{
}
}
/**
* In this case, if the entry is associated with a registered
* group instance, then that instance will be recreated from
* the contents of the provided entry and re-registered with
* the group manager.
*/
{
{
return;
}
try
{
{
// If the modified entry is not in any group instance, it's probably
// not a group, exit fast
return;
}
}
finally
{
}
try
{
{
{
// This should never happen, but check for it anyway.
}
}
}
finally
{
}
}
/**
* In this case, if the entry is associated with a registered
* group instance, then that instance will be recreated from
* the contents of the provided entry and re-registered with
* the group manager under the new DN, and the old instance
* will be deregistered.
*/
{
{
return;
}
try
{
{
}
{
refreshToken++;
}
}
finally
{
}
}
/** {@inheritDoc} */
public PostOperation doPostOperation(
{
// Only do something if the operation is successful, meaning there
// has been a change.
{
}
// If we've gotten here, then everything is acceptable.
}
/** {@inheritDoc} */
public PostOperation doPostOperation(
{
// Only do something if the operation is successful, meaning there
// has been a change.
{
}
// If we've gotten here, then everything is acceptable.
}
/** {@inheritDoc} */
public PostOperation doPostOperation(
{
// Only do something if the operation is successful, meaning there
// has been a change.
{
}
// If we've gotten here, then everything is acceptable.
}
/** {@inheritDoc} */
public PostOperation doPostOperation(
{
// Only do something if the operation is successful, meaning there
// has been a change.
{
}
// If we've gotten here, then everything is acceptable.
}
/** {@inheritDoc} */
public void doPostSynchronization(
{
{
}
}
/** {@inheritDoc} */
public void doPostSynchronization(
{
{
}
}
/** {@inheritDoc} */
public void doPostSynchronization(
{
{
}
}
/** {@inheritDoc} */
public void doPostSynchronization(
{
{
}
}
/**
* Attempts to create a group instance from the provided entry, and if that is
* successful then register it with the server, overwriting any existing
* group instance that may be registered with the same DN.
*
* @param entry The entry containing the potential group definition.
*/
{
{
try
{
{
try
{
refreshToken++;
}
finally
{
}
}
}
catch (DirectoryException e)
{
logger.traceException(e);
}
}
}
/**
* Removes all group instances that might happen to be registered with the
* group manager. This method is only intended for testing purposes and
* should not be called by any other code.
*/
void deregisterAllGroups()
{
try
{
}
finally
{
}
}
/**
* Compare the specified token against the current group manager
* token value. Can be used to reload cached group instances if there has
* been a group instance change.
*
* @param token The current token that the group class holds.
*
* @return {@code true} if the group class should reload its nested groups,
* or {@code false} if it shouldn't.
*/
public boolean hasInstancesChanged(long token) {
return token != this.refreshToken;
}
/**
* Return the current refresh token value. Can be used to
* reload cached group instances if there has been a group instance change.
*
* @return The current token value.
*/
public long refreshToken() {
return this.refreshToken;
}
}