From 54f8773ef2d74f398f3d77f66f708a3d7a5e6855 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Tue, 17 Jul 2007 09:44:09 +0000
Subject: [PATCH] Fix issue 1793: server side notification of changes to inherited defaults

---
 opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java                 |  302 +++++++++++++++++++++++-
 opends/src/server/org/opends/server/admin/server/ServerManagedObject.java                         |   36 ++
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java |    4 
 opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java                    |    2 
 opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java              |  153 ++++++++++++
 opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java                 |  172 ++++++++++++++
 6 files changed, 647 insertions(+), 22 deletions(-)

diff --git a/opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java b/opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java
new file mode 100644
index 0000000..dd5c7b3
--- /dev/null
+++ b/opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opends.server.api.ConfigChangeListener;
+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.loggers.debug.DebugTracer;
+import org.opends.server.messages.AdminMessages;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.ResultCode;
+import org.opends.server.util.StaticUtils;
+
+
+
+/**
+ * A configuration delete listener which detects when a specified
+ * entry is removed and, when it is, cleans up any listeners
+ * associated with it.
+ */
+final class CleanerConfigDeleteListener implements ConfigDeleteListener {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // The change listeners.
+  private Map<DN, ConfigChangeListener> changeListeners =
+    new HashMap<DN, ConfigChangeListener>();
+
+  // The DN of the monitored configuration entry.
+  private final DN dn;
+
+
+
+  /**
+   * Creates a new cleaner configuration change listener which will
+   * remove any registered listeners when then configuration entry it
+   * is monitoring is removed.
+   *
+   * @param dn
+   *          The DN of the entry to be monitored.
+   */
+  public CleanerConfigDeleteListener(DN dn) {
+    this.dn = dn;
+  }
+
+
+
+  /**
+   * Register a configuration change listener for removal when the
+   * monitored entry is removed.
+   *
+   * @param dn
+   *          The name of the entry associated with the configuration
+   *          change listener.
+   * @param listener
+   *          The configuration change listener.
+   */
+  public void addConfigChangeListener(DN dn, ConfigChangeListener listener) {
+    changeListeners.put(dn, listener);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
+    // Remove the listeners if the deleted entry is the monitored
+    // entry.
+    if (configEntry.getDN().equals(dn)) {
+      for (Map.Entry<DN, ConfigChangeListener> me :
+        changeListeners.entrySet()) {
+        ConfigEntry listenerConfigEntry = getConfigEntry(me.getKey());
+        if (listenerConfigEntry != null) {
+          listenerConfigEntry.deregisterChangeListener(me.getValue());
+        }
+      }
+
+      // Now remove this listener as we are no longer needed.
+      ConfigEntry parentConfigEntry = getConfigEntry(dn.getParent());
+      if (parentConfigEntry != null) {
+        parentConfigEntry.deregisterDeleteListener(this);
+      }
+    }
+
+    return new ConfigChangeResult(ResultCode.SUCCESS, false);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
+      StringBuilder unacceptableReason) {
+    // Always acceptable.
+    return true;
+  }
+
+
+
+  // Returns the named configuration entry or null if it could not be
+  // retrieved.
+  private ConfigEntry getConfigEntry(DN dn) {
+    try {
+      ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
+      if (configEntry != null) {
+        return configEntry;
+      } else {
+        int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST;
+        String message = getMessage(msgID, String.valueOf(dn));
+        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.MILD_ERROR,
+            message, msgID);
+      }
+    } catch (ConfigException e) {
+      // The dependent entry could not be retrieved.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT;
+      String message = getMessage(msgID, String.valueOf(dn), StaticUtils
+          .getExceptionMessage(e));
+      logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.MILD_ERROR,
+          message, msgID);
+    }
+
+    return null;
+  }
+}
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
index 8d55359..d4d3c33 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -175,7 +175,7 @@
     ServerManagedObject<? extends S> mo;
     try {
       mo = ServerManagedObject.decode(childPath, r
-          .getChildDefinition(), configEntry);
+          .getChildDefinition(), configEntry, configEntry);
     } catch (DecodingException e) {
       generateUnacceptableReason(e, unacceptableReason);
       return false;
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
index 439c308..58ae50a 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
+++ b/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -28,43 +28,194 @@
 
 
 
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AliasDefaultBehaviorProvider;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.DecodingException;
+import org.opends.server.admin.DefaultBehaviorProvider;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
 import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
 import org.opends.server.api.ConfigChangeListener;
 import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.messages.AdminMessages;
 import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.util.StaticUtils;
 
 
 
 /**
  * An adaptor class which converts {@link ConfigChangeListener}
- * callbacks to strongly typed {@link ConfigurationChangeListener}
- * callbacks.
+ * call-backs to strongly typed {@link ConfigurationChangeListener}
+ * call-backs.
  *
  * @param <S>
  *          The type of server configuration handled by the change
  *          listener.
  */
-final class ConfigChangeListenerAdaptor<S extends Configuration>
-    extends AbstractConfigListenerAdaptor implements
-    ConfigChangeListener {
+final class ConfigChangeListenerAdaptor<S extends Configuration> extends
+    AbstractConfigListenerAdaptor implements ConfigChangeListener {
 
-  // The managed object path.
-  private final ManagedObjectPath path;
+  /**
+   * A default behavior visitor used for determining the set of
+   * dependencies.
+   *
+   * @param <T>
+   *          The type of property.
+   */
+  private static final class Visitor<T> implements
+      DefaultBehaviorProviderVisitor<T, Void, ManagedObjectPath> {
+
+    /**
+     * Finds the dependencies associated with the provided property
+     * definition.
+     *
+     * @param <T>
+     * @param path
+     *          The current base path used for relative name
+     *          resolution.
+     * @param pd
+     *          The property definition.
+     * @param dependencies
+     *          Add dependencies names to this collection.
+     */
+    public static <T> void find(ManagedObjectPath path,
+        PropertyDefinition<T> pd, Collection<DN> dependencies) {
+      Visitor<T> v = new Visitor<T>(dependencies);
+      DefaultBehaviorProvider<T> db = pd.getDefaultBehaviorProvider();
+      db.accept(v, path);
+    }
+
+    // The names of entries that this change listener depends on.
+    private final Collection<DN> dependencies;
+
+
+
+    // Prevent instantiation.
+    private Visitor(Collection<DN> dependencies) {
+      this.dependencies = dependencies;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Void visitAbsoluteInherited(
+        AbsoluteInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
+      ManagedObjectPath next = d.getManagedObjectPath();
+      dependencies.add(DNBuilder.create(next));
+
+      // If the dependent property uses inherited defaults then
+      // recursively get those as well.
+      String propertyName = d.getPropertyName();
+      AbstractManagedObjectDefinition<?, ?> mod = d
+          .getManagedObjectDefinition();
+      PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
+      find(next, pd, dependencies);
+
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Void visitAlias(AliasDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Void visitDefined(DefinedDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Void visitRelativeInherited(
+        RelativeInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
+      ManagedObjectPath next = d.getManagedObjectPath(p);
+      dependencies.add(DNBuilder.create(next));
+
+      // If the dependent property uses inherited defaults then
+      // recursively get those as well.
+      String propertyName = d.getPropertyName();
+      AbstractManagedObjectDefinition<?, ?> mod = d
+          .getManagedObjectDefinition();
+      PropertyDefinition<?> pd = mod.getPropertyDefinition(propertyName);
+      find(next, pd, dependencies);
+
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Void visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      return null;
+    }
+  }
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // Cached managed object between accept/apply call-backs.
+  private ServerManagedObject<? extends S> cachedManagedObject;
 
   // The managed object definition.
   private final AbstractManagedObjectDefinition<?, S> d;
 
+  // The names of entries that this change listener depends on.
+  private final Set<DN> dependencies;
+
+  // The listener used to notify this listener when dependency entries
+  // are modified.
+  private final DependencyConfigChangeListener dependencyListener;
+
+  // The DN associated with this listener.
+  private final DN dn;
+
   // The underlying change listener.
   private final ConfigurationChangeListener<? super S> listener;
 
-  // Cached managed object between accept/apply callbacks.
-  private ServerManagedObject<? extends S> cachedManagedObject;
+  // The managed object path.
+  private final ManagedObjectPath path;
 
 
 
@@ -82,9 +233,47 @@
       AbstractManagedObjectDefinition<?, S> d,
       ConfigurationChangeListener<? super S> listener) {
     this.path = path;
+    this.dn = DNBuilder.create(path);
     this.d = d;
     this.listener = listener;
     this.cachedManagedObject = null;
+
+    // This change listener should be notified when dependent entries
+    // are modified. Determine the dependencies and register change
+    // listeners against them.
+    this.dependencies = new HashSet<DN>();
+    this.dependencyListener = new DependencyConfigChangeListener(dn, this);
+
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      Visitor.find(path, pd, dependencies);
+    }
+
+    CleanerConfigDeleteListener cleaner = new CleanerConfigDeleteListener(dn);
+    for (DN entryDN : dependencies) {
+      // Be careful not to register listeners against the dependent
+      // entry itself.
+      if (!entryDN.equals(dn)) {
+        ConfigEntry configEntry = getConfigEntry(entryDN);
+        if (configEntry != null) {
+          configEntry.registerChangeListener(dependencyListener);
+          cleaner.addConfigChangeListener(entryDN, dependencyListener);
+        }
+      }
+    }
+
+    // Register a delete listener which will remove the dependency
+    // listeners when this entry is removed.
+
+    // FIXME: we should really remove the dependency listeners when
+    // this listener is deregistered, but we have no way to track
+    // that.
+    DN parent = dn.getParent();
+    if (parent != null) {
+      ConfigEntry configEntry = getConfigEntry(dn.getParent());
+      if (configEntry != null) {
+        configEntry.registerDeleteListener(cleaner);
+      }
+    }
   }
 
 
@@ -92,13 +281,33 @@
   /**
    * {@inheritDoc}
    */
-  public ConfigChangeResult applyConfigurationChange(
-      ConfigEntry configEntry) {
+  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
+    return applyConfigurationChange(configEntry, configEntry);
+  }
+
+
+
+  /**
+   * Attempts to apply a new configuration to this Directory Server
+   * component based on the provided changed entry.
+   *
+   * @param configEntry
+   *          The configuration entry that containing the updated
+   *          configuration for this component.
+   * @param newConfigEntry
+   *          The configuration entry that caused the notification
+   *          (will be different from <code>configEntry</code> if a
+   *          dependency was modified).
+   * @return Information about the result of processing the
+   *         configuration change.
+   */
+  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry,
+      ConfigEntry newConfigEntry) {
     // TODO: looking at the ConfigFileHandler implementation reveals
     // that this ConfigEntry will actually be a different object to
-    // the one passed in the previous callback (it will have the same
-    // content though). This config entry has the correct listener
-    // lists.
+    // the one passed in the previous call-back (it will have the same
+    // content though). This configuration entry has the correct
+    // listener lists.
     cachedManagedObject.setConfigEntry(configEntry);
 
     return listener.applyConfigurationChange(cachedManagedObject
@@ -112,9 +321,36 @@
    */
   public boolean configChangeIsAcceptable(ConfigEntry configEntry,
       StringBuilder unacceptableReason) {
+    return configChangeIsAcceptable(configEntry, unacceptableReason,
+        configEntry);
+  }
+
+
+
+  /**
+   * Indicates whether the configuration entry that will result from a
+   * proposed modification is acceptable to this change listener.
+   *
+   * @param configEntry
+   *          The configuration entry that will result from the
+   *          requested update.
+   * @param unacceptableReason
+   *          A buffer to which this method can append a
+   *          human-readable message explaining why the proposed
+   *          change is not acceptable.
+   * @param newConfigEntry
+   *          The configuration entry that caused the notification
+   *          (will be different from <code>configEntry</code> if a
+   *          dependency was modified).
+   * @return <CODE>true</CODE> if the proposed entry contains an
+   *         acceptable configuration, or <CODE>false</CODE> if it
+   *         does not.
+   */
+  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
+      StringBuilder unacceptableReason, ConfigEntry newConfigEntry) {
     try {
-      cachedManagedObject = ServerManagedObject.decode(path, d,
-          configEntry);
+      cachedManagedObject = ServerManagedObject.decode(path, d, configEntry,
+          newConfigEntry);
     } catch (DecodingException e) {
       generateUnacceptableReason(e, unacceptableReason);
       return false;
@@ -133,7 +369,7 @@
 
 
   /**
-   * Get the configuiration change listener associated with this
+   * Get the configuration change listener associated with this
    * adaptor.
    *
    * @return Returns the configuration change listener associated with
@@ -142,4 +378,36 @@
   ConfigurationChangeListener<? super S> getConfigurationChangeListener() {
     return listener;
   }
+
+
+
+  // Returns the named configuration entry or null if it could not be
+  // retrieved.
+  private ConfigEntry getConfigEntry(DN dn) {
+    try {
+      ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
+      if (configEntry != null) {
+        return configEntry;
+      } else {
+        int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST;
+        String message = getMessage(msgID, String.valueOf(dn));
+        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+            message, msgID);
+      }
+    } catch (ConfigException e) {
+      // The dependent entry could not be retrieved.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT;
+      String message = getMessage(msgID, String.valueOf(dn), StaticUtils
+          .getExceptionMessage(e));
+      logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_ERROR,
+          message, msgID);
+    }
+
+    return null;
+  }
+
 }
diff --git a/opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java b/opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java
new file mode 100644
index 0000000..c3ed339
--- /dev/null
+++ b/opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java
@@ -0,0 +1,153 @@
+/*
+ * 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.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.MessageHandler.*;
+
+import org.opends.server.api.ConfigChangeListener;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.messages.AdminMessages;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.ResultCode;
+import org.opends.server.util.StaticUtils;
+
+
+
+/**
+ * A configuration change listener which can be used to notify a
+ * change listener when modifications are made to configuration
+ * entries that it depends upon.
+ */
+final class DependencyConfigChangeListener implements ConfigChangeListener {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // The DN of the dependent configuration entry.
+  private final DN dependentDN;
+
+  // The dependent configuration change listener adaptor.
+  private final ConfigChangeListenerAdaptor<?> dependentListener;
+
+
+
+  /**
+   * Creates a new dependency configuration change listener which will
+   * notify the dependent listener whenever the configuration entry
+   * that this listener monitors is modified.
+   *
+   * @param dependentDN
+   *          The DN of the dependent configuration entry.
+   * @param dependentListener
+   *          The dependent configuration change listener adaptor.
+   */
+  public DependencyConfigChangeListener(DN dependentDN,
+      ConfigChangeListenerAdaptor<?> dependentListener) {
+    this.dependentDN = dependentDN;
+    this.dependentListener = dependentListener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
+    ConfigEntry dependentConfigEntry = getConfigEntry(dependentDN);
+    if (dependentConfigEntry != null) {
+      return dependentListener.applyConfigurationChange(dependentConfigEntry,
+          configEntry);
+    } else {
+      // The dependent entry was not found.
+      configEntry.deregisterChangeListener(this);
+      return new ConfigChangeResult(ResultCode.SUCCESS, false);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
+      StringBuilder unacceptableReason) {
+    ConfigEntry dependentConfigEntry = getConfigEntry(dependentDN);
+    if (dependentConfigEntry != null) {
+      return dependentListener.configChangeIsAcceptable(dependentConfigEntry,
+          unacceptableReason, configEntry);
+    } else {
+      // The dependent entry was not found.
+      configEntry.deregisterChangeListener(this);
+      return true;
+    }
+  }
+
+
+
+  // Returns the named configuration entry or null if it could not be
+  // retrieved.
+  private ConfigEntry getConfigEntry(DN dn) {
+    try {
+      ConfigEntry configEntry = DirectoryServer.getConfigEntry(dn);
+      if (configEntry != null) {
+        return configEntry;
+      } else {
+        int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST;
+        String message = getMessage(msgID, String.valueOf(dn));
+        logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.MILD_ERROR,
+            message, msgID);
+      }
+    } catch (ConfigException e) {
+      // The dependent entry could not be retrieved.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT;
+      String message = getMessage(msgID, String.valueOf(dn), StaticUtils
+          .getExceptionMessage(e));
+      logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.MILD_ERROR,
+          message, msgID);
+    }
+
+    return null;
+  }
+
+}
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
index b2792fe..ed9e19a 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -396,6 +396,40 @@
       ManagedObjectPath path, AbstractManagedObjectDefinition<?, S> definition,
       ConfigEntry configEntry) throws DefinitionDecodingException,
       ServerManagedObjectDecodingException {
+    return decode(path, definition, configEntry, null);
+  }
+
+
+
+  /**
+   * Decodes a configuration entry into the required type of server
+   * managed object.
+   *
+   * @param <S>
+   *          The type of server configuration represented by the
+   *          decoded server managed object.
+   * @param path
+   *          The location of the server managed object.
+   * @param definition
+   *          The required managed object type.
+   * @param configEntry
+   *          The configuration entry that should be decoded.
+   * @param newConfigEntry
+   *          Optional new configuration that does not exist yet in
+   *          the configuration back-end. This will be used for
+   *          resolving inherited default values.
+   * @return Returns the new server-side managed object from the
+   *         provided definition and configuration entry.
+   * @throws DefinitionDecodingException
+   *           If the managed object's type could not be determined.
+   * @throws ServerManagedObjectDecodingException
+   *           If one or more of the managed object's properties could
+   *           not be decoded.
+   */
+  static <S extends Configuration> ServerManagedObject<? extends S> decode(
+      ManagedObjectPath path, AbstractManagedObjectDefinition<?, S> definition,
+      ConfigEntry configEntry, ConfigEntry newConfigEntry)
+      throws DefinitionDecodingException, ServerManagedObjectDecodingException {
     // First determine the correct definition to use for the entry.
     // This could either be the provided definition, or one of its
     // sub-definitions.
@@ -410,7 +444,7 @@
     for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
       List<String> values = getAttribute(mod, pd, configEntry);
       try {
-        decodeProperty(properties, path, pd, values, configEntry);
+        decodeProperty(properties, path, pd, values, newConfigEntry);
       } catch (PropertyException e) {
         exceptions.add(e);
       }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java
index 5d850e4..b1ff264 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java
@@ -581,13 +581,11 @@
    * change listener. This test makes sure that a component is
    * notified when the default values it inherits from another
    * component are modified.
-   * <p>
-   * FIXME: disabled - waiting for fix to issue 1793.
    *
    * @throws Exception
    *           If the test unexpectedly fails.
    */
-  @Test(enabled = false)
+  @Test
   public void testChangeListenerChildValues4() throws Exception {
     TestParentCfg parent = getParent("test parent 1");
 

--
Gitblit v1.10.0