mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
28.33.2009 d80f86a08f6955e0c623a80357c8481b38e92d61
Fix issue 4043 - intermittent failures in ACI unit tests

Introduce API for registering internal plugins which are not exposed in the user configuration. Update ACI cache to use a post-op plugin instead of a change listener.
1 files added
5 files modified
846 ■■■■■ changed files
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java 21 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/plugin/InternalDirectoryServerPlugin.java 96 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java 426 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 64 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PluginConfigManager.java 230 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java 9 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
@@ -114,23 +114,26 @@
   * plugins regardless of type.  This should only be called by the
   * core Directory Server code during the course of loading a plugin.
   *
   * @param  configuration  The configuration for this plugin.
   * @param  pluginTypes    The set of plugin types for which this
   *                        plugin is registered.
   * @param pluginDN
   *          The configuration entry name of this plugin.
   * @param pluginTypes
   *          The set of plugin types for which this plugin is
   *          registered.
   * @param invokeForInternalOps
   *          Indicates whether this plugin should be invoked for
   *          internal operations.
   */
  @org.opends.server.types.PublicAPI(
       stability=org.opends.server.types.StabilityLevel.PRIVATE,
       mayInstantiate=false,
       mayExtend=false,
       mayInvoke=false)
  public final void initializeInternal(PluginCfg configuration,
                                       Set<PluginType> pluginTypes)
  public final void initializeInternal(DN pluginDN,
      Set<PluginType> pluginTypes, boolean invokeForInternalOps)
  {
    this.pluginDN = pluginDN;
    this.pluginTypes = pluginTypes;
    pluginDN = configuration.dn();
    invokeForInternalOps =
         configuration.isInvokeForInternalOperations();
    this.invokeForInternalOps = invokeForInternalOps;
  }
opends/src/server/org/opends/server/api/plugin/InternalDirectoryServerPlugin.java
New file
@@ -0,0 +1,96 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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 2009 Sun Microsystems, Inc.
 */
package org.opends.server.api.plugin;
import java.util.List;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
/**
 * An internal directory server plugin which can be registered with
 * the server without requiring any associated configuration.
 */
public abstract class InternalDirectoryServerPlugin extends
    DirectoryServerPlugin<PluginCfg>
{
  /**
   * Creates a new internal directory server plugin using the provided
   * component name and plugin types.
   *
   * @param componentDN
   *          The configuration entry name of the component associated
   *          with this internal plugin.
   * @param pluginTypes
   *          The set of plugin types for which this internal plugin
   *          is registered.
   * @param invokeForInternalOps
   *          Indicates whether this internal plugin should be invoked
   *          for internal operations.
   */
  protected InternalDirectoryServerPlugin(DN componentDN,
      Set<PluginType> pluginTypes, boolean invokeForInternalOps)
  {
    initializeInternal(componentDN, pluginTypes,
        invokeForInternalOps);
  }
  /**
   * {@inheritDoc}
   */
  public final void initializePlugin(Set<PluginType> pluginTypes,
      PluginCfg configuration) throws ConfigException,
      InitializationException
  {
    // Unused.
  }
  /**
   * {@inheritDoc}
   */
  public final boolean isConfigurationAcceptable(
      PluginCfg configuration, List<Message> unacceptableReasons)
  {
    // Unused.
    return true;
  }
}
opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -22,21 +22,27 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.authorization.dseecompat;
import org.opends.messages.Message;
import org.opends.server.workflowelement.localbackend.*;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.Backend;
import org.opends.server.api.AlertGenerator;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
import org.opends.server.types.operation.PostResponseModifyOperation;
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.plugin.PluginResult.PostOperation;
import org.opends.server.types.operation.PostOperationAddOperation;
import org.opends.server.types.operation.PostOperationDeleteOperation;
import org.opends.server.types.operation.PostOperationModifyDNOperation;
import org.opends.server.types.operation.PostOperationModifyOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPControl;
@@ -44,38 +50,166 @@
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.*;
import static org.opends.messages.AccessControlMessages.*;
import org.opends.server.core.DirectoryServer;
import static org.opends.server.util.ServerConstants.*;
import java.util.*;
/**
 * The AciListenerManager updates an ACI list after each
 * modification operation. Also, updates ACI list when backends are initialized
 * and finalized.
 * The AciListenerManager updates an ACI list after each modification
 * operation. Also, updates ACI list when backends are initialized and
 * finalized.
 */
public class AciListenerManager
        implements ChangeNotificationListener, BackendInitializationListener,
                   AlertGenerator {
public class AciListenerManager implements
    BackendInitializationListener, AlertGenerator
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
    /**
     * The fully-qualified name of this class.
     */
    private static final String CLASS_NAME =
         "org.opends.server.authorization.dseecompat.AciListenerManager";
  /**
   * Internal plugin used for updating the cache before a response is
   * sent to the client.
   */
  private final class AciChangeListenerPlugin extends
      InternalDirectoryServerPlugin
  {
    private AciChangeListenerPlugin()
    {
      super(configurationDN, EnumSet.of(PluginType.POST_OPERATION_ADD,
          PluginType.POST_OPERATION_DELETE,
          PluginType.POST_OPERATION_MODIFY,
          PluginType.POST_OPERATION_MODIFY_DN), true);
    }
    /**
     * {@inheritDoc}
     */
    public PostOperation doPostOperation(
        PostOperationAddOperation addOperation)
    {
      // This entry might have both global and aci attribute types.
      Entry entry = addOperation.getEntryToAdd();
      boolean hasAci, hasGlobalAci = false;
      if ((hasAci = entry.hasOperationalAttribute(AciHandler.aciType))
          || (hasGlobalAci =
              entry.hasAttribute(AciHandler.globalAciType)))
      {
        // Ignore this list, the ACI syntax has already passed and it
        // should be empty.
        LinkedList<Message> failedACIMsgs = new LinkedList<Message>();
        aciList.addAci(entry, hasAci, hasGlobalAci, failedACIMsgs);
      }
      // If we've gotten here, then everything is acceptable.
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /**
     * {@inheritDoc}
     */
    public PostOperation doPostOperation(
        PostOperationDeleteOperation deleteOperation)
    {
      // This entry might have both global and aci attribute types.
      boolean hasAci, hasGlobalAci = false;
      Entry entry = deleteOperation.getEntryToDelete();
      if ((hasAci = entry.hasOperationalAttribute(AciHandler.aciType))
          || (hasGlobalAci =
              entry.hasAttribute(AciHandler.globalAciType)))
      {
        aciList.removeAci(entry, hasAci, hasGlobalAci);
      }
      // If we've gotten here, then everything is acceptable.
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /**
     * {@inheritDoc}
     */
    public PostOperation doPostOperation(
        PostOperationModifyDNOperation modifyDNOperation)
    {
      aciList.renameAci(modifyDNOperation.getOriginalEntry().getDN(),
          modifyDNOperation.getUpdatedEntry().getDN());
      // If we've gotten here, then everything is acceptable.
      return PluginResult.PostOperation.continueOperationProcessing();
    }
    /**
     * {@inheritDoc}
     */
    public PostOperation doPostOperation(
        PostOperationModifyOperation modifyOperation)
    {
      // A change to the ACI list is expensive so let's first make sure
      // that the modification included changes to the ACI. We'll check
      // for both "aci" attribute types and global "ds-cfg-global-aci"
      // attribute types.
      boolean hasAci = false, hasGlobalAci = false;
      List<Modification> mods = modifyOperation.getModifications();
      for (Modification mod : mods)
      {
        AttributeType attributeType =
            mod.getAttribute().getAttributeType();
        if (attributeType.equals(AciHandler.aciType))
        {
          hasAci = true;
        }
        else if (attributeType.equals(AciHandler.globalAciType))
        {
          hasGlobalAci = true;
        }
        if (hasAci && hasGlobalAci)
        {
          break;
        }
      }
      if (hasAci || hasGlobalAci)
      {
        Entry oldEntry = modifyOperation.getCurrentEntry();
        Entry newEntry = modifyOperation.getModifiedEntry();
        aciList.modAciOldNewEntry(oldEntry, newEntry, hasAci,
            hasGlobalAci);
      }
      // If we've gotten here, then everything is acceptable.
      return PluginResult.PostOperation.continueOperationProcessing();
    }
  }
    /*
     *  The configuration DN.
     */
    private DN configurationDN;
    /*
     *  True if the server is in lockdown mode.
     */
@@ -92,149 +226,95 @@
    private static SearchFilter aciFilter;
    /*
     * The aci attribute type is operational so we need to specify it to be
     * returned.
   * Internal plugin used for updating the cache before a response is
   * sent to the client.
     */
    private static LinkedHashSet<String> attrs = new LinkedHashSet<String>();
  private final AciChangeListenerPlugin plugin;
    static {
  /*
   * The aci attribute type is operational so we need to specify it to
   * be returned.
   */
  private static LinkedHashSet<String> attrs =
      new LinkedHashSet<String>();
  static
  {
        /*
         * Set up the filter used to search private and public contexts.
         */
        try {
    try
    {
            aciFilter=SearchFilter.createFilterFromString("(aci=*)");
        } catch (DirectoryException ex) {
    }
    catch (DirectoryException ex)
    {
            //TODO should never happen, error message?
        }
        attrs.add("aci");
    }
    /**
     * Save the list created by the AciHandler routine. Registers as an
     * Alert Generator that can send alerts when the server is being put
     * in lockdown  mode. Registers as backend initialization listener that is
     * used to manage the ACI list cache when backends are
     * initialized/finalized. Registers as a change notification listener that
     * is used to manage the ACI list cache after ACI modifications have been
     * performed.
   * in lockdown mode. Registers as backend initialization listener that
   * is used to manage the ACI list cache when backends are
   * initialized/finalized. Registers as a change notification listener
   * that is used to manage the ACI list cache after ACI modifications
   * have been performed.
     *
     * @param aciList The list object created and loaded by the handler.
     * @param cfgDN The DN of the access control configuration entry.
   * @param aciList
   *          The list object created and loaded by the handler.
   * @param cfgDN
   *          The DN of the access control configuration entry.
     */
    public AciListenerManager(AciList aciList, DN cfgDN) {
  public AciListenerManager(AciList aciList, DN cfgDN)
  {
        this.aciList=aciList;
        this.configurationDN=cfgDN;
        DirectoryServer.registerChangeNotificationListener(this);
    this.plugin = new AciChangeListenerPlugin();
    DirectoryServer.registerInternalPlugin(plugin);
        DirectoryServer.registerBackendInitializationListener(this);
        DirectoryServer.registerAlertGenerator(this);
    }
   /**
    * Deregister from the change notification listener, the backend
    * initialization listener and the alert generator.
    */
    public void finalizeListenerManager() {
        DirectoryServer.deregisterChangeNotificationListener(this);
  public void finalizeListenerManager()
  {
    DirectoryServer.deregisterInternalPlugin(plugin);
        DirectoryServer.deregisterBackendInitializationListener(this);
        DirectoryServer.deregisterAlertGenerator(this);
    }
    /**
     * A delete operation succeeded. Remove any ACIs associated with the
     * entry deleted.
     * @param deleteOperation The delete operation.
     * @param entry The entry being deleted.
     */
    public void handleDeleteOperation(PostResponseDeleteOperation
            deleteOperation, Entry entry) {
        boolean hasAci,  hasGlobalAci=false;
        //This entry might have both global and aci attribute types.
        if((hasAci=entry.hasOperationalAttribute(AciHandler.aciType)) ||
                (hasGlobalAci=entry.hasAttribute(AciHandler.globalAciType)))
            aciList.removeAci(entry, hasAci, hasGlobalAci);
    }
    /**
     * An Add operation succeeded. Add any ACIs associated with the
     * entry being added.
     * @param addOperation  The add operation.
     * @param entry   The entry being added.
   * {@inheritDoc} In this case, the server will search the backend to
   * find all aci attribute type values that it may contain and add them
   * to the ACI list.
     */
    public void handleAddOperation(PostResponseAddOperation addOperation,
                                   Entry entry) {
        boolean hasAci, hasGlobalAci=false;
        //Ignore this list, the ACI syntax has already passed and it should be
        //empty.
        LinkedList<Message>failedACIMsgs=new LinkedList<Message>();
        //This entry might have both global and aci attribute types.
        if((hasAci=entry.hasOperationalAttribute(AciHandler.aciType)) ||
                (hasGlobalAci=entry.hasAttribute(AciHandler.globalAciType)))
            aciList.addAci(entry, hasAci, hasGlobalAci, failedACIMsgs);
    }
    /**
     * A modify operation succeeded. Adjust the ACIs by removing
     * ACIs based on the oldEntry and then adding ACIs based on the new
     * entry.
     * @param modOperation  the modify operation.
     * @param oldEntry The old entry to examine.
     * @param newEntry  The new entry to examine.
     */
    public void handleModifyOperation(PostResponseModifyOperation modOperation,
                                      Entry oldEntry, Entry newEntry)
  public void performBackendInitializationProcessing(Backend backend)
    {
        // A change to the ACI list is expensive so let's first make sure that
        // the modification included changes to the ACI. We'll check for
        //both "aci" attribute types and global "ds-cfg-global-aci" attribute
        //types.
        boolean hasAci = false, hasGlobalAci=false;
        List<Modification> mods = modOperation.getModifications();
        for (Modification mod : mods) {
            AttributeType attributeType=mod.getAttribute().getAttributeType();
            if (attributeType.equals(AciHandler.aciType))
                hasAci = true;
           else if(attributeType.equals(AciHandler.globalAciType))
                hasGlobalAci=true;
            if(hasAci && hasGlobalAci)
               break;
        }
        if (hasAci || hasGlobalAci)
            aciList.modAciOldNewEntry(oldEntry, newEntry, hasAci, hasGlobalAci);
    }
    /**
     * A modify DN operation has succeeded. Adjust the ACIs by moving ACIs
     * under the old entry DN to the new entry DN.
     * @param modifyDNOperation  The LDAP modify DN operation.
     * @param oldEntry  The old entry.
     * @param newEntry The new entry.
     */
    public void handleModifyDNOperation(
            PostResponseModifyDNOperation modifyDNOperation,
            Entry oldEntry, Entry newEntry)
    // Check to make sure that the backend has a presence index defined
    // for the ACI attribute. If it does not, then log a warning message
    // because this processing could be very expensive.
    AttributeType aciType =
        DirectoryServer.getAttributeType("aci", true);
    if (backend.getEntryCount() > 0
        && !backend.isIndexed(aciType, IndexType.PRESENCE))
    {
        aciList.renameAci(oldEntry.getDN(), newEntry.getDN());
      logError(WARN_ACI_ATTRIBUTE_NOT_INDEXED.get(backend
          .getBackendID(), "aci"));
    }
    /**
     * {@inheritDoc}  In this case, the server will search the backend to find
     * all aci attribute type values that it may contain and add them to the
     * ACI list.
     */
    public void performBackendInitializationProcessing(Backend backend) {
      // Check to make sure that the backend has a presence index defined for
      // the ACI attribute.  If it does not, then log a warning message because
      // this processing could be very expensive.
      AttributeType aciType = DirectoryServer.getAttributeType("aci", true);
      if (backend.getEntryCount() > 0 &&
          ! backend.isIndexed(aciType, IndexType.PRESENCE))
      {
        logError(WARN_ACI_ATTRIBUTE_NOT_INDEXED.get(backend.getBackendID(),
                                                    "aci"));
      }
      InternalClientConnection conn =
           InternalClientConnection.getRootConnection();
      LinkedList<Message>failedACIMsgs=new LinkedList<Message>();
@@ -244,14 +324,19 @@
      controls.add(new LDAPControl(OID_MANAGE_DSAIT_CONTROL, true));
      //Add group membership control to let a backend look for it and
      //decide if it would abort searches.
      controls.add(new LDAPControl(
          OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE ,false));
      for (DN baseDN : backend.getBaseDNs()) {
        try {
          if (! backend.entryExists(baseDN))  {
    controls.add(new LDAPControl(OID_INTERNAL_GROUP_MEMBERSHIP_UPDATE,
        false));
    for (DN baseDN : backend.getBaseDNs())
    {
      try
      {
        if (!backend.entryExists(baseDN))
        {
            continue;
          }
        } catch (Exception e) {
      }
      catch (Exception e)
      {
            if (debugEnabled())
            {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -259,41 +344,50 @@
            continue;
        }
        InternalSearchOperation internalSearch =
             new InternalSearchOperation(
                  conn,
                  InternalClientConnection.nextOperationID(),
                  InternalClientConnection.nextMessageID(),
                  controls, baseDN, SearchScope.WHOLE_SUBTREE,
                  DereferencePolicy.NEVER_DEREF_ALIASES,
                  0, 0, false, aciFilter, attrs, null);
          new InternalSearchOperation(conn, InternalClientConnection
              .nextOperationID(), InternalClientConnection
              .nextMessageID(), controls, baseDN,
              SearchScope.WHOLE_SUBTREE,
              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
              aciFilter, attrs, null);
        LocalBackendSearchOperation localInternalSearch =
          new LocalBackendSearchOperation(internalSearch);
        try  {
      try
      {
          backend.search(localInternalSearch);
        } catch (Exception e) {
      }
      catch (Exception e)
      {
            if (debugEnabled())
            {
                TRACER.debugCaught(DebugLogLevel.ERROR, e);
            }
            continue;
        }
        if(!internalSearch.getSearchEntries().isEmpty()) {
          int validAcis = aciList.addAci(
               internalSearch.getSearchEntries(), failedACIMsgs);
      if (!internalSearch.getSearchEntries().isEmpty())
      {
        int validAcis =
            aciList.addAci(internalSearch.getSearchEntries(),
                failedACIMsgs);
          if(!failedACIMsgs.isEmpty())
                    logMsgsSetLockDownMode(failedACIMsgs);
          Message message = INFO_ACI_ADD_LIST_ACIS.get(
              Integer.toString(validAcis), String.valueOf(baseDN));
        Message message =
            INFO_ACI_ADD_LIST_ACIS.get(Integer.toString(validAcis),
                String.valueOf(baseDN));
          logError(message);
        }
      }
    }
    /**
     * {@inheritDoc}  In this case, the server will remove all aci attribute
     * type values associated with entries in the provided backend.
   * {@inheritDoc} In this case, the server will remove all aci
   * attribute type values associated with entries in the provided
   * backend.
     */
    public void performBackendFinalizationProcessing(Backend backend) {
  public void performBackendFinalizationProcessing(Backend backend)
  {
        aciList.removeAci(backend);
    }
@@ -312,12 +406,13 @@
    }
    /**
     * Retrieves the DN of the configuration entry used to configure the
     * handler.
     *
     * @return  The DN of the configuration entry containing the Access Control
     *          configuration information.
   * @return The DN of the configuration entry containing the Access
   *         Control configuration information.
     */
    public DN getComponentEntryDN()
    {
@@ -325,12 +420,13 @@
    }
    /**
     * Retrieves information about the set of alerts that this generator may
     * produce.  The map returned should be between the notification type for a
     * particular notification and the human-readable description for that
     * notification.  This alert generator must not generate any alerts with
     * types that are not contained in this list.
   * Retrieves information about the set of alerts that this generator
   * may produce. The map returned should be between the notification
   * type for a particular notification and the human-readable
   * description for that notification. This alert generator must not
   * generate any alerts with types that are not contained in this list.
     *
     * @return  Information about the set of alerts that this generator may
     *          produce.
@@ -345,15 +441,20 @@
    }
    /**
     * Log the exception messages from the failed ACI decode and then put the
     * server in lockdown mode -- if needed.
     *
     * @param failedACIMsgs  List of exception messages from failed ACI decodes.
     */
    public  void logMsgsSetLockDownMode(LinkedList<Message> failedACIMsgs) {
        for(Message msg : failedACIMsgs) {
  /**
   * Log the exception messages from the failed ACI decode and then put
   * the server in lockdown mode -- if needed.
   *
   * @param failedACIMsgs
   *          List of exception messages from failed ACI decodes.
   */
  public void logMsgsSetLockDownMode(LinkedList<Message> failedACIMsgs)
  {
    for (Message msg : failedACIMsgs)
    {
            Message message=WARN_ACI_SERVER_DECODE_FAILED.get(msg);
            logError(message);
        }
@@ -362,20 +463,21 @@
    }
    /**
     * Send an WARN_ACI_ENTER_LOCKDOWN_MODE alert notification and put the
     * server in lockdown mode.
     *
     */
    private void setLockDownMode() {
        if(!inLockDownMode) {
  private void setLockDownMode()
  {
    if (!inLockDownMode)
    {
            inLockDownMode=true;
            //Send ALERT_TYPE_ACCESS_CONTROL_PARSE_FAILED alert that
            //lockdown is about to be entered.
            Message lockDownMsg=WARN_ACI_ENTER_LOCKDOWN_MODE.get();
            DirectoryServer.sendAlertNotification(this,
                    ALERT_TYPE_ACCESS_CONTROL_PARSE_FAILED,
                    lockDownMsg );
          ALERT_TYPE_ACCESS_CONTROL_PARSE_FAILED, lockDownMsg);
            //Enter lockdown mode.
            DirectoryServer.setLockdownMode(true);
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -123,6 +123,7 @@
import org.opends.server.api.SynchronizationProvider;
import org.opends.server.api.TrustManagerProvider;
import org.opends.server.api.WorkQueue;
import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
import org.opends.server.api.plugin.PluginResult;
import org.opends.server.api.plugin.PluginType;
import org.opends.server.api.ExtensibleMatchingRule;
@@ -1341,6 +1342,11 @@
      initializeSchema();
      // Initialize the plugin manager so that internal plugins can be
      // registered.
      pluginConfigManager.initializePluginConfigManager();
      // Initialize all the virtual attribute handlers.
      initializeVirtualAttributes();
@@ -1474,9 +1480,8 @@
      initializePasswordPolicyComponents();
      // Load and initialize all the plugins, and then call the registered
      // startup plugins.
      initializePlugins();
      // Load and initialize the user plugins.
      pluginConfigManager.initializeUserPlugins(null);
      // Initialize any synchronization providers that may be defined.
      if (!environmentConfig.disableSynchronization())
@@ -1491,6 +1496,7 @@
      workQueue = new WorkQueueConfigManager().initializeWorkQueue();
      // Invoke the startup plugins.
      PluginResult.Startup startupPluginResult =
           pluginConfigManager.invokeStartupPlugins();
      if (! startupPluginResult.continueProcessing())
@@ -2906,24 +2912,6 @@
  /**
   * Initializes the set of plugins defined in the Directory Server.
   *
   * @throws  ConfigException  If there is a configuration problem with any of
   *                           the Directory Server plugins.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the plugins that is not related to the
   *                                   server configuration.
   */
  public void initializePlugins()
         throws ConfigException, InitializationException
  {
    pluginConfigManager.initializePluginConfig(null);
  }
  /**
   * Initializes the set of plugins defined in the Directory Server.  Only the
   * specified types of plugins will be initialized.
   *
@@ -2941,7 +2929,8 @@
         throws ConfigException, InitializationException
  {
    pluginConfigManager = new PluginConfigManager();
    pluginConfigManager.initializePluginConfig(pluginTypes);
    pluginConfigManager.initializePluginConfigManager();
    pluginConfigManager.initializeUserPlugins(pluginTypes);
  }
@@ -3005,6 +2994,37 @@
  /**
   * Registers the provided internal plugin with the Directory Server
   * and ensures that it will be invoked in the specified ways.
   *
   * @param plugin
   *          The internal plugin to register with the Directory Server.
   *          The plugin must specify a configuration entry which is
   *          guaranteed to be unique.
   */
  public static void registerInternalPlugin(
      InternalDirectoryServerPlugin plugin)
  {
    directoryServer.pluginConfigManager.registerInternalPlugin(plugin);
  }
  /**
   * Deregisters the provided internal plugin with the Directory Server.
   *
   * @param plugin
   *          The internal plugin to deregister from the Directory Server.
   */
  public static void deregisterInternalPlugin(
      InternalDirectoryServerPlugin plugin)
  {
    directoryServer.pluginConfigManager.deregisterInternalPlugin(plugin);
  }
  /**
   * Retrieves the requested entry from the Directory Server configuration.
   *
   * @param  entryDN  The DN of the configuration entry to retrieve.
opends/src/server/org/opends/server/core/PluginConfigManager.java
@@ -234,46 +234,58 @@
  /**
   * Initializes the configuration associated with the Directory Server plugins.
   * This should only be called at Directory Server startup.
   * Initializes this plugin configuration manager. This should only
   * be called at Directory Server startup and before user plugins are
   * loaded.
   *
   * @param  pluginTypes  The set of plugin types for the plugins to initialize,
   *                      or <CODE>null</CODE> to initialize all types of
   *                      plugins defined in the server configuration.  In
   *                      general, this should only be non-null for cases in
   *                      which the server is running in a special mode that
   *                      only uses a minimal set of plugins (e.g., LDIF import
   *                      or export).
   *
   * @throws  ConfigException  If a critical configuration problem prevents the
   *                           plugin initialization from succeeding.
   *
   * @throws  InitializationException  If a problem occurs while initializing
   *                                   the plugins that is not related to the
   *                                   server configuration.
   * @throws ConfigException
   *           If a critical configuration problem prevents the plugin
   *           initialization from succeeding.
   */
  public void initializePluginConfig(Set<PluginType> pluginTypes)
         throws ConfigException, InitializationException
  public void initializePluginConfigManager() throws ConfigException
  {
    registeredPlugins.clear();
    // Get the root configuration object.
    ServerManagementContext managementContext =
         ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
         managementContext.getRootConfiguration();
    // Get the plugin root configuration and register with it as an add and
    // delete listener so we can be notified if any plugin entries are added or
    // removed.
    pluginRootConfig = rootConfiguration.getPluginRoot();
    pluginRootConfig.addPluginAddListener(this);
    pluginRootConfig.addPluginDeleteListener(this);
  }
    //Initialize the existing plugins.
  /**
   * Initializes any plugins defined in the directory server
   * configuration. This should only be called at Directory Server
   * startup and after this plugin configuration manager has been
   * initialized.
   *
   * @param pluginTypes
   *          The set of plugin types for the plugins to initialize, or
   *          <CODE>null</CODE> to initialize all types of plugins
   *          defined in the server configuration. In general, this
   *          should only be non-null for cases in which the server is
   *          running in a special mode that only uses a minimal set of
   *          plugins (e.g., LDIF import or export).
   * @throws ConfigException
   *           If a critical configuration problem prevents the plugin
   *           initialization from succeeding.
   * @throws InitializationException
   *           If a problem occurs while initializing the plugins that
   *           is not related to the server configuration.
   */
  public void initializeUserPlugins(Set<PluginType> pluginTypes)
         throws ConfigException, InitializationException
  {
    //Initialize the user plugins.
    for (String pluginName : pluginRootConfig.listPlugins())
    {
      PluginCfg pluginConfiguration = pluginRootConfig.getPlugin(pluginName);
@@ -360,11 +372,11 @@
      if (initialize)
      {
        Method method = plugin.getClass().getMethod("initializeInternal",
                                                    PluginCfg.class, Set.class);
        method.invoke(plugin, configuration, pluginTypes);
        plugin.initializeInternal(configuration.dn(), pluginTypes,
            configuration.isInvokeForInternalOperations());
        method = plugin.getClass().getMethod("initializePlugin", Set.class,
        Method method =
            plugin.getClass().getMethod("initializePlugin", Set.class,
            configuration.configurationClass());
        method.invoke(plugin, pluginTypes, configuration);
      }
@@ -553,43 +565,60 @@
  /**
   * Registers the provided plugin with this plugin config manager and ensures
   * that it will be invoked in the specified ways.
   * Registers the provided internal plugin with this plugin config
   * manager and ensures that it will be invoked in the specified ways.
   *
   * @param  plugin         The plugin to register with the server.
   * @param  pluginEntryDN  The DN of the configuration entry for the provided
   *                        plugin.
   * @param  pluginTypes    The plugin types that will be used to control the
   *                        points at which the provided plugin is invoked.
   * @param plugin
   *          The internal plugin to register with the server. The
   *          plugin must specify a configuration entry which is
   *          guaranteed to be unique.
   */
  private void registerPlugin(
                    DirectoryServerPlugin<? extends PluginCfg> plugin,
                    DN pluginEntryDN, Set<PluginType> pluginTypes)
  void registerInternalPlugin(InternalDirectoryServerPlugin plugin)
  {
    pluginLock.lock();
    try
    {
      registeredPlugins.put(pluginEntryDN, plugin);
      registerPlugin0(plugin, plugin.getPluginTypes());
    }
    finally
    {
      pluginLock.unlock();
    }
  }
  /**
   * Register a plugin in the appropriate tables.
   *
   * @param plugin
   *          The plugin to register with the server.
   * @param pluginTypes
   *          The plugin types that will be used to control the points
   *          at which the provided plugin is invoked.
   */
  private void registerPlugin0(
      DirectoryServerPlugin<? extends PluginCfg> plugin,
      Set<PluginType> pluginTypes)
  {
      for (PluginType t : pluginTypes)
      {
        switch (t)
        {
          case STARTUP:
            startupPlugins =
                 addPlugin(startupPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderStartup());
            addPlugin(startupPlugins, plugin, t, pluginRootConfig
                .getPluginOrderStartup());
            break;
          case SHUTDOWN:
            shutdownPlugins =
                 addPlugin(shutdownPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderShutdown());
            addPlugin(shutdownPlugins, plugin, t, pluginRootConfig
                .getPluginOrderShutdown());
            break;
          case POST_CONNECT:
            postConnectPlugins =
                 addPlugin(postConnectPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderPostConnect());
            addPlugin(postConnectPlugins, plugin, t, pluginRootConfig
                .getPluginOrderPostConnect());
            break;
          case POST_DISCONNECT:
            postDisconnectPlugins =
@@ -598,13 +627,13 @@
            break;
          case LDIF_IMPORT:
            ldifImportPlugins =
                 addPlugin(ldifImportPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderLDIFImport());
            addPlugin(ldifImportPlugins, plugin, t, pluginRootConfig
                .getPluginOrderLDIFImport());
            break;
          case LDIF_IMPORT_END:
            ldifImportEndPlugins =
                 addPlugin(ldifImportEndPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderLDIFImportEnd());
            addPlugin(ldifImportEndPlugins, plugin, t, pluginRootConfig
                .getPluginOrderLDIFImportEnd());
            break;
          case LDIF_IMPORT_BEGIN:
            ldifImportBeginPlugins =
@@ -613,8 +642,8 @@
            break;
          case LDIF_EXPORT:
            ldifExportPlugins =
                 addPlugin(ldifExportPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderLDIFExport());
            addPlugin(ldifExportPlugins, plugin, t, pluginRootConfig
                .getPluginOrderLDIFExport());
            break;
          case PRE_PARSE_ABANDON:
            preParseAbandonPlugins =
@@ -623,13 +652,13 @@
            break;
          case PRE_PARSE_ADD:
            preParseAddPlugins =
                 addPlugin(preParseAddPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderPreParseAdd());
            addPlugin(preParseAddPlugins, plugin, t, pluginRootConfig
                .getPluginOrderPreParseAdd());
            break;
          case PRE_PARSE_BIND:
            preParseBindPlugins =
                 addPlugin(preParseBindPlugins, plugin, t,
                           pluginRootConfig.getPluginOrderPreParseBind());
            addPlugin(preParseBindPlugins, plugin, t, pluginRootConfig
                .getPluginOrderPreParseBind());
            break;
          case PRE_PARSE_COMPARE:
            preParseComparePlugins =
@@ -799,26 +828,25 @@
          case POST_SYNCHRONIZATION_ADD:
            postSynchronizationAddPlugins =
                 addPlugin(postSynchronizationAddPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationAdd());
                pluginRootConfig.getPluginOrderPostSynchronizationAdd());
            break;
          case POST_SYNCHRONIZATION_DELETE:
            postSynchronizationDeletePlugins =
                 addPlugin(postSynchronizationDeletePlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationDelete());
                pluginRootConfig
                    .getPluginOrderPostSynchronizationDelete());
            break;
          case POST_SYNCHRONIZATION_MODIFY:
            postSynchronizationModifyPlugins =
                 addPlugin(postSynchronizationModifyPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationModify());
                pluginRootConfig
                    .getPluginOrderPostSynchronizationModify());
            break;
          case POST_SYNCHRONIZATION_MODIFY_DN:
            postSynchronizationModifyDNPlugins =
                 addPlugin(postSynchronizationModifyDNPlugins, plugin, t,
                           pluginRootConfig.
                                getPluginOrderPostSynchronizationModifyDN());
                pluginRootConfig
                    .getPluginOrderPostSynchronizationModifyDN());
            break;
          case SEARCH_RESULT_ENTRY:
            searchResultEntryPlugins =
@@ -844,6 +872,31 @@
        }
      }
    }
  /**
   * Registers the provided plugin with this plugin config manager and
   * ensures that it will be invoked in the specified ways.
   *
   * @param plugin
   *          The plugin to register with the server.
   * @param pluginEntryDN
   *          The DN of the configuration entry for the provided plugin.
   * @param pluginTypes
   *          The plugin types that will be used to control the points
   *          at which the provided plugin is invoked.
   */
  private void registerPlugin(
      DirectoryServerPlugin<? extends PluginCfg> plugin,
      DN pluginEntryDN, Set<PluginType> pluginTypes)
  {
    pluginLock.lock();
    registeredPlugins.put(pluginEntryDN, plugin);
    try
    {
      registerPlugin0(plugin, pluginTypes);
    }
    finally
    {
      pluginLock.unlock();
@@ -1067,24 +1120,64 @@
  /**
   * Deregisters the provided internal plugin.
   *
   * @param plugin
   *          The internal plugin to deregister from the server.
   */
  void deregisterInternalPlugin(InternalDirectoryServerPlugin plugin)
  {
    pluginLock.lock();
    try
    {
      deregisterPlugin0(plugin);
      plugin.finalizePlugin();
    }
    finally
    {
      pluginLock.unlock();
    }
  }
  /**
   * Deregisters the plugin with the provided configuration entry DN.
   *
   * @param  configEntryDN  The DN of the configuration entry for the plugin to
   * @param configEntryDN
   *          The DN of the configuration entry for the plugin to
   *                        deregister.
   */
  private void deregisterPlugin(DN configEntryDN)
  {
    pluginLock.lock();
    DirectoryServerPlugin<? extends PluginCfg> plugin;
    try
    {
      plugin = registeredPlugins.remove(configEntryDN);
      if (plugin == null)
      if (plugin != null)
      {
        return;
        deregisterPlugin0(plugin);
        plugin.finalizePlugin();
      }
    }
    finally
    {
      pluginLock.unlock();
    }
      }
  /**
   * Deregisters the provided plugin.
   *
   * @param plugin
   *          The plugin to deregister from the server.
   */
  private void deregisterPlugin0(
      DirectoryServerPlugin<? extends PluginCfg> plugin)
  {
      for (PluginType t : plugin.getPluginTypes())
      {
        switch (t)
@@ -1288,13 +1381,6 @@
        }
      }
    }
    finally
    {
      pluginLock.unlock();
    }
    plugin.finalizePlugin();
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
@@ -560,8 +560,9 @@
    sigList = new LinkedList<String>();
    sigList.add("initializeInternal");
    sigList.add("void");
    sigList.add("org.opends.server.admin.std.server.PluginCfg");
    sigList.add("org.opends.server.types.DN");
    sigList.add("java.util.Set");
    sigList.add("boolean");
    expectedPublicMethods.add(sigList);
    sigList = new LinkedList<String>();
@@ -821,7 +822,8 @@
      pluginTypes.add(t);
    }
    nullPlugin.initializeInternal(configuration, pluginTypes);
    nullPlugin.initializeInternal(configuration.dn(), pluginTypes,
        configuration.isInvokeForInternalOperations());
    assertEquals(nullPlugin.getPluginEntryDN(), pluginEntryDN);
  }
@@ -907,7 +909,8 @@
      pluginTypes.add(t);
    }
    nullPlugin.initializeInternal(configuration, pluginTypes);
    nullPlugin.initializeInternal(configuration.dn(), pluginTypes,
        configuration.isInvokeForInternalOperations());
    assertEquals(nullPlugin.getPluginTypes(), pluginTypes);
  }