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

matthew_swift
19.47.2007 980147a25e6e1d817e24c0fa7ebe173313dce27d
Fix a bug in add/delete listener registration for instantiable
(one-to-many) relations. Listener registration would fail if
the entry associated with the relation did not exist (this is
the case when the parent managed object has only just been
created). The solution is to wait for the "relation" entry
to be created and, when it is added, automatically register
the add/delete listener then. This automatic registration is
managed by a "delayed" config add listener which is registered
against the parent managed object.
1 files added
1 files modified
227 ■■■■ changed files
opends/src/server/org/opends/server/admin/server/DelayedConfigAddListener.java 164 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java 63 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/DelayedConfigAddListener.java
New file
@@ -0,0 +1,164 @@
/*
 * 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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.ResultCode;
/**
 * A configuration add listener which will monitor a parent entry to
 * see when a specified child entry has been added. When the child
 * entry is added the add listener will automatically register its
 * "delayed" add or delete listener.
 */
final class DelayedConfigAddListener implements ConfigAddListener {
  // The name of the parent entry.
  private final DN parent;
  // The name of the subordinate entry which should have an add or
  // delete listener registered with it when it is created.
  private final DN child;
  // The add listener to be registered with the subordinate entry when
  // it is added (or null if a delete listener should be registered).
  private final ConfigAddListener delayedAddListener;
  // The delete listener to be registered with the subordinate entry
  // when it is added (or null if an add listener should be
  // registered).
  private final ConfigDeleteListener delayedDeleteListener;
  /**
   * Create a new delayed add listener which will register an add
   * listener with the specified entry when it is added.
   *
   * @param parent
   *          The name of the parent entry.
   * @param child
   *          The name of the subordinate entry which should have an
   *          add listener registered with it when it is created.
   * @param addListener
   *          The add listener to be added to the subordinate entry
   *          when it is added.
   */
  public DelayedConfigAddListener(DN parent, DN child,
      ConfigAddListener addListener) {
    this.parent = parent;
    this.child = child;
    this.delayedAddListener = addListener;
    this.delayedDeleteListener = null;
  }
  /**
   * Create a new delayed add listener which will register a delete
   * listener with the specified entry when it is added.
   *
   * @param parent
   *          The name of the parent entry.
   * @param child
   *          The name of the subordinate entry which should have a
   *          delete listener registered with it when it is created.
   * @param deleteListener
   *          The delete listener to be added to the subordinate entry
   *          when it is added.
   */
  public DelayedConfigAddListener(DN parent, DN child,
      ConfigDeleteListener deleteListener) {
    this.parent = parent;
    this.child = child;
    this.delayedAddListener = null;
    this.delayedDeleteListener = deleteListener;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
      ConfigEntry configEntry) {
    if (configEntry.getDN().equals(child)) {
      // The subordinate entry matched our criteria so register the
      // listener(s).
      if (delayedAddListener != null) {
        configEntry.registerAddListener(delayedAddListener);
      }
      if (delayedDeleteListener != null) {
        configEntry.registerDeleteListener(delayedDeleteListener);
      }
      // We are no longer needed.
      try {
        ConfigEntry myEntry = DirectoryServer.getConfigEntry(parent);
        if (myEntry != null) {
          myEntry.deregisterAddListener(this);
        }
      } catch (ConfigException e) {
        if (debugEnabled()) {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        // Ignore this error as it implies that this listener has
        // already been deregistered.
      }
    }
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean configAddIsAcceptable(ConfigEntry configEntry,
      StringBuilder unacceptableReason) {
    // Always acceptable.
    return true;
  }
}
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -720,10 +720,19 @@
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigEntry configEntry = getListenerConfigEntry(baseDN);
    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path,
        d, listener);
    configEntry.registerAddListener(adaptor);
    if (relationEntry != null) {
      relationEntry.registerAddListener(adaptor);
    } else {
      // The relation entry does not exist yet so register a delayed
      // add listener.
      ConfigAddListener delayedListener = new DelayedConfigAddListener(
          configEntry.getDN(), baseDN, adaptor);
      configEntry.registerAddListener(delayedListener);
    }
  }
@@ -795,11 +804,19 @@
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigEntry configEntry = getListenerConfigEntry(baseDN);
    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(
        path, d, listener);
    configEntry.registerDeleteListener(adaptor);
    if (relationEntry != null) {
      relationEntry.registerDeleteListener(adaptor);
    } else {
      // The relation entry does not exist yet so register a delayed
      // add listener.
      ConfigAddListener delayedListener = new DelayedConfigAddListener(
          configEntry.getDN(), baseDN, adaptor);
      configEntry.registerAddListener(delayedListener);
    }
  }
@@ -851,11 +868,13 @@
      DN baseDN, ConfigurationAddListener<M> listener) {
    try {
      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
      for (ConfigAddListener l : configEntry.getAddListeners()) {
        if (l instanceof ConfigAddListenerAdaptor) {
          ConfigAddListenerAdaptor adaptor = (ConfigAddListenerAdaptor) l;
          if (adaptor.getConfigurationAddListener() == listener) {
            configEntry.deregisterAddListener(adaptor);
      if (configEntry != null) {
        for (ConfigAddListener l : configEntry.getAddListeners()) {
          if (l instanceof ConfigAddListenerAdaptor) {
            ConfigAddListenerAdaptor adaptor = (ConfigAddListenerAdaptor) l;
            if (adaptor.getConfigurationAddListener() == listener) {
              configEntry.deregisterAddListener(adaptor);
            }
          }
        }
      }
@@ -875,11 +894,15 @@
      ConfigurationDeleteListener<M> listener) {
    try {
      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
      for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
        if (l instanceof ConfigDeleteListenerAdaptor) {
          ConfigDeleteListenerAdaptor adaptor = (ConfigDeleteListenerAdaptor) l;
          if (adaptor.getConfigurationDeleteListener() == listener) {
            configEntry.deregisterDeleteListener(adaptor);
      if (configEntry != null) {
        for (ConfigDeleteListener l : configEntry
            .getDeleteListeners()) {
          if (l instanceof ConfigDeleteListenerAdaptor) {
            ConfigDeleteListenerAdaptor adaptor =
              (ConfigDeleteListenerAdaptor) l;
            if (adaptor.getConfigurationDeleteListener() == listener) {
              configEntry.deregisterDeleteListener(adaptor);
            }
          }
        }
      }
@@ -915,7 +938,7 @@
  // Gets a config entry required for a listener and throws a config
  // exception on failure.
  // exception on failure or returns null if the entry does not exist.
  private ConfigEntry getListenerConfigEntry(DN dn)
      throws ConfigException {
    // Attempt to retrieve the listener base entry.
@@ -934,14 +957,6 @@
      throw new ConfigException(msgID, message, e);
    }
    // The configuration handler is free to return null indicating
    // that the entry does not exist.
    if (configEntry == null) {
      int msgID = AdminMessages.MSGID_ADMIN_LISTENER_BASE_DOES_NOT_EXIST;
      String message = getMessage(msgID, String.valueOf(dn));
      throw new ConfigException(msgID, message);
    }
    return configEntry;
  }