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

matthew_swift
17.44.2007 88d778f65264beffea072eb153647a22b977b4da
Fix issue 1793: server side notification of changes to inherited defaults

When a component (A) contains a property (PA) which inherits default values from a property (PB) in another component (PB), component A must be notified when component B is modified since the values of property PA may have effectively changed. This change modifies the server-side change notification framework to handle this notification automatically. The server-side framework can handle multiple levels of inheritance so a property X and inherit from property Y which inherits from property Z. Changes to the component containing Z will cause both change listeners for property Y and X to be notified.
2 files added
4 files modified
669 ■■■■■ changed files
opendj-sdk/opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java 172 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java 2 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java 302 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java 153 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java 36 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java 4 ●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/CleanerConfigDeleteListener.java
New file
@@ -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;
  }
}
opendj-sdk/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;
opendj-sdk/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;
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/server/DependencyConfigChangeListener.java
New file
@@ -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;
  }
}
opendj-sdk/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);
      }
opendj-sdk/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");