From 1d62ce980388e01ab7f28e54e7b9e70dcd0bd64f Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Wed, 27 Nov 2013 17:42:48 +0000
Subject: [PATCH] OpenDJ 3 : config framework

---
 opendj-admin/src/main/java/org/opends/server/admin/PropertyIsSingleValuedException.java                 |   58 
 opendj-admin/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java                       | 1627 ++
 opendj-admin/src/main/java/org/opends/server/admin/condition/ANDCondition.java                          |  112 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectAlreadyExistsException.java             |   56 
 opendj-admin/src/main/java/org/opends/server/admin/BooleanPropertyDefinition.java                       |  182 
 opendj-admin/src/main/java/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java        |  131 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinition.java                         |  105 
 opendj-admin/src/main/java/org/opends/server/admin/StringPropertyDefinition.java                        |  335 
 opendj-admin/src/main/java/org/opends/server/admin/condition/package-info.java                          |   36 
 opendj-admin/src/main/java/org/opends/server/admin/condition/NOTCondition.java                          |   97 
 opendj-admin/src/main/java/org/opends/server/admin/EnumPropertyDefinition.java                          |  275 
 opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObject.java                            |  938 +
 opendj-admin/src/main/java/org/opends/server/admin/DefinitionDecodingException.java                     |  136 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectOption.java                             |   49 
 opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueStringException.java             |   89 
 opendj-admin/src/main/java/org/opends/server/admin/client/AdminClientException.java                     |   80 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java              |  492 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListenerAdaptor.java    |  100 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListener.java        |   80 
 opendj-admin/src/main/java/org/opends/server/admin/client/AdminSecurityException.java                   |   74 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPath.java                               | 1445 ++
 opendj-admin/src/main/java/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java        |  150 
 opendj-admin/src/main/java/org/opends/server/admin/ACIPropertyDefinition.java                           |  151 
 opendj-admin/src/main/java/org/opends/server/admin/client/MissingMandatoryPropertiesException.java      |  173 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java                 |  281 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyProvider.java                                |   89 
 opendj-admin/src/main/java/org/opends/server/admin/Tag.java                                             |  212 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java              |   76 
 opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorException.java                        |   69 
 opendj-admin/src/main/java/org/opends/server/admin/AdminRuntimeException.java                           |   91 
 opendj-admin/src/main/java/org/opends/server/admin/Configuration.java                                   |   55 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyIsMandatoryException.java                    |   58 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java                      | 1692 ++
 opendj-admin/src/main/java/org/opends/server/admin/DurationUnit.java                                    |  385 
 opendj-admin/src/main/java/org/opends/server/admin/client/ClientConstraintHandler.java                  |  165 
 opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java                                |   90 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionVisitor.java                       |  297 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyOption.java                                  |   71 
 opendj-admin/src/main/java/org/opends/server/admin/RelationDefinition.java                              |  421 
 opendj-admin/src/main/java/org/opends/server/admin/AdministrationDataSync.java                          |  353 
 opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationException.java                  |   97 
 opendj-admin/src/main/java/org/opends/server/admin/DefaultManagedObject.java                            |  200 
 opendj-admin/src/main/java/org/opends/server/admin/SetRelationDefinition.java                           |  289 
 opendj-admin/src/main/java/org/opends/server/admin/client/spi/Driver.java                               |  809 +
 opendj-admin/src/main/java/org/opends/server/admin/Reference.java                                       |  245 
 opendj-admin/src/main/java/org/opends/server/admin/SizeUnit.java                                        |  394 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java             |  346 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java     |  148 
 opendj-admin/src/main/java/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java            |   72 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationAddListener.java                 |   76 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyIsReadOnlyException.java                     |   58 
 opendj-admin/src/main/java/org/opends/server/admin/client/ManagementContext.java                        |  528 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java                  |  986 +
 opendj-admin/src/main/java/org/opends/server/admin/SizePropertyDefinition.java                          |  410 
 opendj-admin/src/main/java/org/opends/server/admin/AttributeTypePropertyDefinition.java                 |  215 
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java                          |  706 +
 opendj-admin/src/main/java/org/opends/server/admin/DecodingException.java                               |   58 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPathSerializer.java                     |  135 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java             |  179 
 opendj-admin/src/main/java/org/opends/server/admin/client/package-info.java                             |   39 
 opendj-admin/src/main/java/org/opends/server/admin/OperationsException.java                             |   72 
 opendj-admin/src/main/java/org/opends/server/admin/RelationDefinitionVisitor.java                       |  130 
 opendj-admin/src/main/java/org/opends/server/admin/server/package-info.java                             |   41 
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/package-info.java                        |   40 
 opendj-admin/src/main/java/org/opends/server/admin/condition/IsPresentCondition.java                    |  104 
 opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObjectDecodingException.java           |  146 
 opendj-admin/src/main/java/org/opends/server/admin/AbstractManagedObjectDefinition.java                 | 1158 ++
 opendj-admin/src/main/java/org/opends/server/admin/DefinedDefaultBehaviorProvider.java                  |   93 
 opendj-admin/src/main/java/org/opends/server/admin/DurationPropertyDefinition.java                      |  609 +
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagedObject.java                   |  386 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyNotFoundException.java                       |   75 
 opendj-admin/src/main/java/org/opends/server/admin/DNPropertyDefinition.java                            |  240 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionResource.java                 |  156 
 opendj-admin/src/main/java/org/opends/server/admin/UndefinedDefaultBehaviorProvider.java                |   59 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListener.java           |   78 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java                  |  193 
 opendj-admin/src/main/java/org/opends/server/admin/AdminException.java                                  |   73 
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPConnection.java                      |  149 
 opendj-admin/src/main/java/org/opends/server/admin/UnknownPropertyDefinitionException.java              |   78 
 opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationNotSupportedException.java      |   99 
 opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java                 |  191 
 opendj-admin/src/main/java/org/opends/server/admin/client/AuthorizationException.java                   |   98 
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/JNDIDirContextAdaptor.java               |  337 
 opendj-admin/src/main/java/org/opends/server/admin/client/spi/Property.java                             |  140 
 opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java                             |  191 
 opendj-admin/src/main/java/org/opends/server/admin/IntegerPropertyDefinition.java                       |  394 
 opendj-admin/src/main/java/org/opends/server/admin/TopCfgDefn.java                                      |   74 
 opendj-admin/src/main/java/org/opends/server/admin/ConfigurationClient.java                             |  100 
 opendj-admin/src/main/java/org/opends/server/admin/AggregationPropertyDefinition.java                   | 1204 ++
 opendj-admin/src/main/java/org/opends/server/admin/ClassPropertyDefinition.java                         |  382 
 opendj-admin/src/main/java/org/opends/server/admin/client/IllegalManagedObjectNameException.java        |  143 
 opendj-admin/src/main/java/org/opends/server/admin/client/spi/AbstractManagedObject.java                | 1048 +
 opendj-admin/src/main/java/org/opends/server/admin/condition/ContainsCondition.java                     |  214 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyException.java                               |   99 
 opendj-admin/src/main/java/org/opends/server/admin/condition/Conditions.java                            |  237 
 opendj-admin/src/main/java/org/opends/server/admin/client/ConcurrentModificationException.java          |  100 
 opendj-admin/src/main/java/org/opends/server/admin/AliasDefaultBehaviorProvider.java                    |  115 
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagementContext.java               |   77 
 opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueException.java                   |   88 
 opendj-admin/src/main/java/org/opends/server/admin/condition/Condition.java                             |   94 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListener.java        |   80 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java                   |  148 
 opendj-admin/src/main/java/org/opends/server/admin/Constraint.java                                      |  122 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionUsageBuilder.java                  |  378 
 opendj-admin/src/main/java/org/opends/server/admin/DefinitionResolver.java                              |   72 
 opendj-admin/src/main/java/org/opends/server/admin/client/spi/package-info.java                         |   39 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java              |   77 
 opendj-admin/src/main/java/org/opends/server/admin/doc/package-info.java                                |   38 
 opendj-admin/src/main/java/org/opends/server/admin/package-info.java                                    |   39 
 opendj-admin/src/main/java/org/opends/server/admin/client/OperationRejectedException.java               |  243 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyValueVisitor.java                            |  337 
 opendj-admin/src/main/java/org/opends/server/admin/ClassLoaderProvider.java                             |  830 +
 opendj-admin/src/main/java/org/opends/server/admin/AdministrationConnector.java                         |  804 +
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPNameBuilder.java                     |  246 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListenerAdaptor.java |  102 
 opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProviderVisitor.java                  |  116 
 opendj-admin/src/main/java/org/opends/server/admin/client/CommunicationException.java                   |  100 
 opendj-admin/src/main/java/org/opends/server/admin/IPAddressPropertyDefinition.java                     |  188 
 opendj-admin/src/main/java/org/opends/server/admin/IPAddressMaskPropertyDefinition.java                 |  166 
 opendj-admin/src/main/java/org/opends/server/admin/GenericConstraint.java                               |  223 
 opendj-admin/src/main/java/org/opends/server/admin/client/spi/PropertySet.java                          |  371 
 opendj-admin/src/main/java/org/opends/server/admin/SingletonRelationDefinition.java                     |  184 
 opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java              |  302 
 opendj-admin/src/main/java/org/opends/server/admin/InstantiableRelationDefinition.java                  |  304 
 opendj-admin/src/main/java/org/opends/server/admin/OptionalRelationDefinition.java                      |  183 
 opendj-admin/src/main/java/org/opends/server/admin/condition/ORCondition.java                           |  112 
 opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinition.java                              |  675 +
 opendj-admin/src/main/java/org/opends/server/admin/LDAPProfile.java                                     |  423 
 opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectNotFoundException.java                  |   67 
 opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProvider.java                         |  104 
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListenerAdaptor.java |  101 
 opendj-admin/src/main/java/org/opends/server/admin/RelationOption.java                                  |   49 
 132 files changed, 33,894 insertions(+), 0 deletions(-)

diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ACIPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/ACIPropertyDefinition.java
new file mode 100644
index 0000000..7bdc64f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ACIPropertyDefinition.java
@@ -0,0 +1,151 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+import org.opends.server.authorization.dseecompat.Aci;
+import org.opends.server.authorization.dseecompat.AciException;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.ByteString;
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+
+/**
+ * ACI property definition.
+ */
+public class ACIPropertyDefinition extends PropertyDefinition<Aci> {
+
+
+  /**
+   * An interface for incrementally constructing ACI property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<Aci, ACIPropertyDefinition> {
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected ACIPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName, EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<Aci> defaultBehavior) {
+      return new ACIPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior);
+    }
+  }
+
+
+  /**
+   * Create a ACI property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new ACI property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+  // Private constructor.
+  private ACIPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<Aci> defaultBehavior) {
+    super(d, Aci.class, propertyName, options, adminAction,
+        defaultBehavior);
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(Aci value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No additional validation required.
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Aci decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      return Aci.decode(ByteString.valueOf(value), DN.NULL_DN);
+    } catch (AciException e) {
+      // TODO: it would be nice to throw the cause.
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitACI(this, p);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, Aci value, P p) {
+    return v.visitACI(this, value, p);
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(Aci o1, Aci o2) {
+    return o1.toString().compareTo(o2.toString());
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java
new file mode 100644
index 0000000..bd4cb2b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java
@@ -0,0 +1,131 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * A default behavior provider which retrieves default values from a
+ * managed object in an absolute location. It should be used by
+ * properties which inherit their default value(s) from properties
+ * held in an other managed object.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public final class AbsoluteInheritedDefaultBehaviorProvider<T> extends
+    DefaultBehaviorProvider<T> {
+
+  // The absolute path to the managed object containing the property.
+  private ManagedObjectPath<?, ?> path = null;
+
+  // The string representation of the managed object path specifying
+  // the absolute location of the managed object.
+  private final String pathString;
+
+  // The name of the property containing the inherited default values.
+  private final String propertyName;
+
+
+
+  /**
+   * Create an absolute inherited default behavior provider associated
+   * with the managed object at the specified absolute location.
+   *
+   * @param pathString
+   *          The string representation of the managed object path
+   *          specifying the absolute location of the managed object.
+   * @param propertyName
+   *          The name of the property containing the inherited
+   *          default values.
+   */
+  public AbsoluteInheritedDefaultBehaviorProvider(String pathString,
+      String propertyName) {
+    this.pathString = pathString;
+    this.propertyName = propertyName;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <R, P> R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p) {
+    return v.visitAbsoluteInherited(this, p);
+  }
+
+
+
+  /**
+   * Get the definition of the parent managed object containing the
+   * inherited default values.
+   *
+   * @return Returns the definition of the parent managed object
+   *         containing the inherited default values.
+   */
+  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
+    return path.getManagedObjectDefinition();
+  }
+
+
+
+  /**
+   * Get the absolute path of the managed object containing the
+   * property which has the default values.
+   *
+   * @return Returns the absolute path of the managed object
+   *         containing the property which has the default values.
+   */
+  public ManagedObjectPath<?, ?> getManagedObjectPath() {
+    return path;
+  }
+
+
+
+  /**
+   * Gets the name of the property containing the inherited default
+   * values.
+   *
+   * @return Returns the name of the property containing the inherited
+   *         default values.
+   */
+  public String getPropertyName() {
+    return propertyName;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception {
+    // Decode the path.
+    path = ManagedObjectPath.valueOf(pathString);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AbstractManagedObjectDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/AbstractManagedObjectDefinition.java
new file mode 100644
index 0000000..e570e0d
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AbstractManagedObjectDefinition.java
@@ -0,0 +1,1158 @@
+/*
+ * 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 2007-2010 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+import java.util.Vector;
+import org.forgerock.i18n.LocalizableMessage;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+
+
+
+/**
+ * Defines the structure of an abstract managed object. Abstract managed objects
+ * cannot be instantiated.
+ * <p>
+ * Applications can query a managed object definition in order to determine the
+ * overall configuration model of an application.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this definition
+ *          represents.
+ * @param <S>
+ *          The type of server managed object configuration that this definition
+ *          represents.
+ */
+public abstract class AbstractManagedObjectDefinition
+    <C extends ConfigurationClient, S extends Configuration> {
+
+  // The name of the definition.
+  private final String name;
+
+  // The parent managed object definition if applicable.
+  private final AbstractManagedObjectDefinition<? super C, ? super S> parent;
+
+  // The set of constraints associated with this managed object
+  // definition.
+  private final Collection<Constraint> constraints;
+
+  // The set of property definitions applicable to this managed object
+  // definition.
+  private final Map<String, PropertyDefinition<?>> propertyDefinitions;
+
+  // The set of relation definitions applicable to this managed object
+  // definition.
+  private final Map<String, RelationDefinition<?, ?>> relationDefinitions;
+
+  // The set of relation definitions directly referencing this managed
+  // object definition.
+  private final Set<RelationDefinition<C, S>> reverseRelationDefinitions;
+
+  // The set of all property definitions associated with this managed
+  // object definition including inherited property definitions.
+  private final Map<String, PropertyDefinition<?>> allPropertyDefinitions;
+
+  // The set of all relation definitions associated with this managed
+  // object definition including inherited relation definitions.
+  private final Map<String, RelationDefinition<?, ?>> allRelationDefinitions;
+
+  // The set of aggregation property definitions applicable to this
+  // managed object definition.
+  private final Map<String, AggregationPropertyDefinition<?, ?>>
+    aggregationPropertyDefinitions;
+
+  // The set of aggregation property definitions directly referencing this
+  // managed object definition.
+  private final Vector<AggregationPropertyDefinition<?, ?>>
+    reverseAggregationPropertyDefinitions;
+
+  // The set of all aggregation property definitions associated with this
+  // managed object definition including inherited relation definitions.
+  private final Map<String, AggregationPropertyDefinition<?, ?>>
+    allAggregationPropertyDefinitions;
+
+  // The set of tags associated with this managed object.
+  private final Set<Tag> allTags;
+
+  // Options applicable to this definition.
+  private final Set<ManagedObjectOption> options;
+
+  // The set of managed object definitions which inherit from this definition.
+  private final Map<String,
+    AbstractManagedObjectDefinition<? extends C, ? extends S>> children;
+
+
+
+  /**
+   * Create a new abstract managed object definition.
+   *
+   * @param name
+   *          The name of the definition.
+   * @param parent
+   *          The parent definition, or <code>null</code> if there
+   *          is no parent (only the {@link TopCfgDefn} should have a
+   *          <code>null</code> parent, unless the definition is
+   *          being used for testing).
+   */
+  protected AbstractManagedObjectDefinition(String name,
+      AbstractManagedObjectDefinition<? super C, ? super S> parent) {
+    this.name = name;
+    this.parent = parent;
+    this.constraints = new LinkedList<Constraint>();
+    this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
+    this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>();
+    this.reverseRelationDefinitions = new HashSet<RelationDefinition<C,S>>();
+    this.allPropertyDefinitions = new HashMap<String, PropertyDefinition<?>>();
+    this.allRelationDefinitions =
+      new HashMap<String, RelationDefinition<?, ?>>();
+    this.aggregationPropertyDefinitions =
+      new HashMap<String, AggregationPropertyDefinition<?,?>>();
+    this.reverseAggregationPropertyDefinitions =
+      new Vector<AggregationPropertyDefinition<?,?>>();
+    this.allAggregationPropertyDefinitions =
+      new HashMap<String, AggregationPropertyDefinition<?, ?>>();
+    this.allTags = new HashSet<Tag>();
+    this.options = EnumSet.noneOf(ManagedObjectOption.class);
+
+    this.children = new HashMap<String,
+        AbstractManagedObjectDefinition<? extends C, ? extends S>>();
+
+    // If we have a parent definition then inherit its features.
+    if (parent != null) {
+      registerInParent();
+
+      for (PropertyDefinition<?> pd : parent.getAllPropertyDefinitions()) {
+        allPropertyDefinitions.put(pd.getName(), pd);
+      }
+
+      for (RelationDefinition<?, ?> rd : parent.getAllRelationDefinitions()) {
+        allRelationDefinitions.put(rd.getName(), rd);
+      }
+
+      for (AggregationPropertyDefinition<?, ?> apd :
+        parent.getAllAggregationPropertyDefinitions()) {
+
+        allAggregationPropertyDefinitions.put(apd.getName(), apd);
+      }
+
+      // Tag inheritance is performed during preprocessing.
+    }
+  }
+
+
+
+  /**
+   * Get all the child managed object definitions which inherit from
+   * this managed object definition.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         subordinate managed object definitions which inherit from
+   *         this managed object definition.
+   */
+  public final Collection<AbstractManagedObjectDefinition
+      <? extends C, ? extends S>> getAllChildren() {
+    List<AbstractManagedObjectDefinition<? extends C, ? extends S>> list =
+      new ArrayList<AbstractManagedObjectDefinition<? extends C, ? extends S>>(
+        children.values());
+
+    for (AbstractManagedObjectDefinition<? extends C, ? extends S> child :
+        children.values()) {
+      list.addAll(child.getAllChildren());
+    }
+
+    return Collections.unmodifiableCollection(list);
+  }
+
+
+
+  /**
+   * Get all the constraints associated with this type of managed
+   * object. The returned collection will contain inherited
+   * constraints.
+   *
+   * @return Returns a collection containing all the constraints
+   *         associated with this type of managed object. The caller
+   *         is free to modify the collection if required.
+   */
+  public final Collection<Constraint> getAllConstraints() {
+    // This method does not used a cached set of constraints because
+    // constraints may be updated after child definitions have been
+    // defined.
+    List<Constraint> allConstraints = new LinkedList<Constraint>();
+
+    if (parent != null) {
+      allConstraints.addAll(parent.getAllConstraints());
+    }
+    allConstraints.addAll(constraints);
+
+    return allConstraints;
+  }
+
+
+
+  /**
+   * Get all the property definitions associated with this type of
+   * managed object. The returned collection will contain inherited
+   * property definitions.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         property definitions associated with this type of managed
+   *         object.
+   */
+  public final Collection<PropertyDefinition<?>> getAllPropertyDefinitions() {
+    return Collections.unmodifiableCollection(allPropertyDefinitions.values());
+  }
+
+
+
+  /**
+   * Get all the relation definitions associated with this type of
+   * managed object. The returned collection will contain inherited
+   * relation definitions.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         relation definitions associated with this type of managed
+   *         object.
+   */
+  public final Collection<RelationDefinition<?, ?>>
+      getAllRelationDefinitions() {
+    return Collections.unmodifiableCollection(allRelationDefinitions.values());
+  }
+
+
+
+  /**
+   * Get all the relation definitions which refer to this managed
+   * object definition. The returned collection will contain relation
+   * definitions which refer to parents of this managed object
+   * definition.
+   *
+   * @return Returns a collection containing all the relation
+   *         definitions which refer to this managed object
+   *         definition. The caller is free to modify the collection
+   *         if required.
+   */
+  public final Collection<RelationDefinition<? super C, ? super S>>
+  getAllReverseRelationDefinitions() {
+    // This method does not used a cached set of relations because
+    // relations may be updated after child definitions have been
+    // defined.
+    List<RelationDefinition<? super C, ? super S>> rdlist =
+      new LinkedList<RelationDefinition<? super C, ? super S>>();
+
+    if (parent != null) {
+      rdlist.addAll(parent.getAllReverseRelationDefinitions());
+    }
+    rdlist.addAll(reverseRelationDefinitions);
+
+    return rdlist;
+  }
+
+
+
+  /**
+   * Get all the aggregation property definitions associated with this type of
+   * managed object. The returned collection will contain inherited
+   * aggregation property definitions.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         aggregation property definitions associated with this type of
+   *         managed object.
+   */
+  public final Collection<AggregationPropertyDefinition<?, ?>>
+      getAllAggregationPropertyDefinitions() {
+    return Collections.unmodifiableCollection(
+      allAggregationPropertyDefinitions.values());
+  }
+
+
+
+  /**
+   * Get all the aggregation property definitions which refer to this managed
+   * object definition. The returned collection will contain aggregation
+   * property definitions which refer to parents of this managed object
+   * definition.
+   *
+   * @return Returns a collection containing all the aggregation property
+   *         definitions which refer to this managed object
+   *         definition. The caller is free to modify the collection
+   *         if required.
+   */
+  public final Collection<AggregationPropertyDefinition<?, ?>>
+  getAllReverseAggregationPropertyDefinitions() {
+    // This method does not used a cached set of aggregation properties because
+    // aggregation properties may be updated after child definitions have been
+    // defined.
+    List<AggregationPropertyDefinition<?, ?>> apdlist =
+      new LinkedList<AggregationPropertyDefinition<?, ?>>();
+
+    if (parent != null) {
+      apdlist.addAll(parent.getAllReverseAggregationPropertyDefinitions());
+    }
+    apdlist.addAll(reverseAggregationPropertyDefinitions);
+
+    return apdlist;
+  }
+
+
+
+  /**
+   * Get all the tags associated with this type of managed object. The
+   * returned collection will contain inherited tags.
+   *
+   * @return Returns an unmodifiable collection containing all the
+   *         tags associated with this type of managed object.
+   */
+  public final Collection<Tag> getAllTags() {
+    return Collections.unmodifiableCollection(allTags);
+  }
+
+
+
+  /**
+   * Get the named child managed object definition which inherits from
+   * this managed object definition. This method will recursively
+   * search down through the inheritance hierarchy.
+   *
+   * @param name
+   *          The name of the managed object definition sub-type.
+   * @return Returns the named child managed object definition which
+   *         inherits from this managed object definition.
+   * @throws IllegalArgumentException
+   *           If the specified managed object definition name was
+   *           null or empty or if the requested subordinate managed
+   *           object definition was not found.
+   */
+  public final AbstractManagedObjectDefinition<? extends C, ? extends S>
+      getChild(String name) throws IllegalArgumentException {
+    if ((name == null) || (name.length() == 0)) {
+      throw new IllegalArgumentException("null or empty managed object name");
+    }
+
+    AbstractManagedObjectDefinition<? extends C, ? extends S> d = children
+        .get(name);
+
+    if (d == null) {
+      // Recursively search.
+      for (AbstractManagedObjectDefinition<? extends C, ? extends S> child :
+          children.values()) {
+        try {
+          d = child.getChild(name);
+          break;
+        } catch (IllegalArgumentException e) {
+          // Try the next child.
+        }
+      }
+    }
+
+    if (d == null) {
+      throw new IllegalArgumentException("child managed object definition \""
+          + name + "\" not found");
+    }
+
+    return d;
+  }
+
+
+
+  /**
+   * Get the child managed object definitions which inherit directly
+   * from this managed object definition.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         subordinate managed object definitions which inherit
+   *         directly from this managed object definition.
+   */
+  public final Collection<AbstractManagedObjectDefinition
+      <? extends C, ? extends S>> getChildren() {
+    return Collections.unmodifiableCollection(children.values());
+  }
+
+
+
+  /**
+   * Get the constraints defined by this managed object definition.
+   * The returned collection will not contain inherited constraints.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         constraints defined by this managed object definition.
+   */
+  public final Collection<Constraint> getConstraints() {
+    return Collections.unmodifiableCollection(constraints);
+  }
+
+
+
+  /**
+   * Gets the optional description of this managed object definition
+   * in the default locale.
+   *
+   * @return Returns the description of this managed object definition
+   *         in the default locale, or <code>null</code> if there is
+   *         no description.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getDescription() throws UnsupportedOperationException {
+    return getDescription(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional description of this managed object definition
+   * in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the description of this managed object definition
+   *         in the specified locale, or <code>null</code> if there
+   *         is no description.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getDescription(Locale locale)
+      throws UnsupportedOperationException {
+    try {
+      return ManagedObjectDefinitionI18NResource.getInstance()
+        .getMessage(this, "description", locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Get the name of the definition.
+   *
+   * @return Returns the name of the definition.
+   */
+  public final String getName() {
+    return name;
+  }
+
+
+
+  /**
+   * Get the parent managed object definition, if applicable.
+   *
+   * @return Returns the parent of this managed object definition, or
+   *         <code>null</code> if this definition is the
+   *         {@link TopCfgDefn}.
+   */
+  public final AbstractManagedObjectDefinition<? super C,
+      ? super S> getParent() {
+    return parent;
+  }
+
+
+
+  /**
+   * Get the specified property definition associated with this type
+   * of managed object. The search will include any inherited property
+   * definitions.
+   *
+   * @param name
+   *          The name of the property definition to be retrieved.
+   * @return Returns the specified property definition associated with
+   *         this type of managed object.
+   * @throws IllegalArgumentException
+   *           If the specified property name was null or empty or if
+   *           the requested property definition was not found.
+   */
+  public final PropertyDefinition<?> getPropertyDefinition(String name)
+      throws IllegalArgumentException {
+    if ((name == null) || (name.length() == 0)) {
+      throw new IllegalArgumentException("null or empty property name");
+    }
+
+    PropertyDefinition<?> d = allPropertyDefinitions.get(name);
+    if (d == null) {
+      throw new IllegalArgumentException("property definition \"" + name
+          + "\" not found");
+    }
+
+    return d;
+  }
+
+
+
+  /**
+   * Get the property definitions defined by this managed object
+   * definition. The returned collection will not contain inherited
+   * property definitions.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         property definitions defined by this managed object
+   *         definition.
+   */
+  public final Collection<PropertyDefinition<?>> getPropertyDefinitions() {
+    return Collections.unmodifiableCollection(propertyDefinitions
+        .values());
+  }
+
+
+
+  /**
+   * Get the specified relation definition associated with this type
+   * of managed object.The search will include any inherited relation
+   * definitions.
+   *
+   * @param name
+   *          The name of the relation definition to be retrieved.
+   * @return Returns the specified relation definition associated with
+   *         this type of managed object.
+   * @throws IllegalArgumentException
+   *           If the specified relation name was null or empty or if
+   *           the requested relation definition was not found.
+   */
+  public final RelationDefinition<?, ?> getRelationDefinition(String name)
+      throws IllegalArgumentException {
+    if ((name == null) || (name.length() == 0)) {
+      throw new IllegalArgumentException("null or empty relation name");
+    }
+
+    RelationDefinition<?, ?> d = allRelationDefinitions.get(name);
+    if (d == null) {
+      throw new IllegalArgumentException("relation definition \"" + name
+          + "\" not found");
+    }
+
+    return d;
+  }
+
+
+
+  /**
+   * Get the relation definitions defined by this managed object
+   * definition. The returned collection will not contain inherited
+   * relation definitions.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         relation definitions defined by this managed object
+   *         definition.
+   */
+  public final Collection<RelationDefinition<?,?>> getRelationDefinitions() {
+    return Collections.unmodifiableCollection(relationDefinitions.values());
+  }
+
+
+
+  /**
+   * Get the relation definitions which refer directly to this managed
+   * object definition. The returned collection will not contain
+   * relation definitions which refer to parents of this managed
+   * object definition.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         relation definitions which refer directly to this managed
+   *         object definition.
+   */
+  public final Collection<RelationDefinition<C, S>>
+      getReverseRelationDefinitions() {
+    return Collections.unmodifiableCollection(reverseRelationDefinitions);
+  }
+
+
+
+  /**
+   * Get the specified aggregation property definition associated with this type
+   * of managed object.The search will include any inherited aggregation
+   * property definitions.
+   *
+   * @param name
+   *          The name of the aggregation property definition to be retrieved.
+   * @return Returns the specified aggregation property definition associated
+   *         with this type of managed object.
+   * @throws IllegalArgumentException
+   *           If the specified aggregation property name was null or empty or
+   *           if the requested aggregation property definition was not found.
+   */
+  public final AggregationPropertyDefinition<?, ?>
+    getAggregationPropertyDefinition(String name)
+    throws IllegalArgumentException {
+    if ((name == null) || (name.length() == 0)) {
+      throw new IllegalArgumentException(
+        "null or empty aggregation property name");
+    }
+
+    AggregationPropertyDefinition<?, ?> d =
+      allAggregationPropertyDefinitions.get(name);
+    if (d == null) {
+      throw new IllegalArgumentException("aggregation property definition \""
+        + name + "\" not found");
+    }
+
+    return d;
+  }
+
+  /**
+   * Get the aggregation property definitions defined by this managed object
+   * definition. The returned collection will not contain inherited
+   * aggregation property definitions.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         aggregation property definitions defined by this managed object
+   *         definition.
+   */
+  public final Collection<AggregationPropertyDefinition<?, ?>>
+    getAggregationPropertyDefinitions() {
+    return Collections.unmodifiableCollection(
+      aggregationPropertyDefinitions.values());
+  }
+
+  /**
+   * Get the aggregation property definitions which refer directly to this
+   * managed object definition. The returned collection will not contain
+   * aggregation property definitions which refer to parents of this managed
+   * object definition.
+   *
+   * @return Returns an unmodifiable collection containing the
+   *         aggregation property definitions which refer directly to this
+   *         managed object definition.
+   */
+  public final Collection<AggregationPropertyDefinition<?, ?>>
+    getReverseAggregationPropertyDefinitions() {
+    return Collections.unmodifiableCollection(
+      reverseAggregationPropertyDefinitions);
+  }
+
+  /**
+   * Gets the synopsis of this managed object definition in the
+   * default locale.
+   *
+   * @return Returns the synopsis of this managed object definition in
+   *         the default locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getSynopsis() throws UnsupportedOperationException {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this managed object definition in the
+   * specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this managed object definition in
+   *         the specified locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getSynopsis(Locale locale)
+      throws UnsupportedOperationException {
+    return ManagedObjectDefinitionI18NResource.getInstance()
+        .getMessage(this, "synopsis", locale);
+  }
+
+
+
+  /**
+   * Gets the user friendly name of this managed object definition in
+   * the default locale.
+   *
+   * @return Returns the user friendly name of this managed object
+   *         definition in the default locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getUserFriendlyName()
+      throws UnsupportedOperationException {
+    return getUserFriendlyName(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the user friendly name of this managed object definition in
+   * the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the user friendly name of this managed object
+   *         definition in the specified locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getUserFriendlyName(Locale locale)
+      throws UnsupportedOperationException {
+    // TODO: have admin framework getMessage return a Message
+    return Message.raw(ManagedObjectDefinitionI18NResource.getInstance()
+        .getMessage(this, "user-friendly-name", locale));
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this managed object
+   * definition in the default locale.
+   *
+   * @return Returns the user friendly plural name of this managed
+   *         object definition in the default locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getUserFriendlyPluralName()
+      throws UnsupportedOperationException {
+    return getUserFriendlyPluralName(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this managed object
+   * definition in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the user friendly plural name of this managed
+   *         object definition in the specified locale.
+   * @throws UnsupportedOperationException
+   *           If this managed object definition is the
+   *           {@link TopCfgDefn}.
+   */
+  public final Message getUserFriendlyPluralName(Locale locale)
+      throws UnsupportedOperationException {
+    return ManagedObjectDefinitionI18NResource.getInstance()
+        .getMessage(this, "user-friendly-plural-name", locale);
+  }
+
+
+
+  /**
+   * Determine whether there are any child managed object definitions which
+   * inherit from this managed object definition.
+   *
+   * @return Returns <code>true</code> if this type of managed object has any
+   *         child managed object definitions, <code>false</code> otherwise.
+   */
+  public final boolean hasChildren() {
+    return !children.isEmpty();
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object definition has the
+   * specified option.
+   *
+   * @param option
+   *          The option to test.
+   * @return Returns <code>true</code> if the option is set, or
+   *         <code>false</code> otherwise.
+   */
+  public final boolean hasOption(ManagedObjectOption option) {
+    return options.contains(option);
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object definition has the
+   * specified tag.
+   *
+   * @param t
+   *          The tag definition.
+   * @return Returns <code>true</code> if this managed object
+   *         definition has the specified tag.
+   */
+  public final boolean hasTag(Tag t) {
+    return allTags.contains(t);
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object definition is a
+   * sub-type of the provided managed object definition. This managed
+   * object definition is a sub-type of the provided managed object
+   * definition if they are both the same or if the provided managed
+   * object definition can be obtained by recursive invocations of the
+   * {@link #getParent()} method.
+   *
+   * @param d
+   *          The managed object definition to be checked.
+   * @return Returns <code>true</code> if this managed object
+   *         definition is a sub-type of the provided managed object
+   *         definition.
+   */
+  public final boolean isChildOf(AbstractManagedObjectDefinition<?, ?> d) {
+    AbstractManagedObjectDefinition<?, ?> i;
+    for (i = this; i != null; i = i.parent) {
+      if (i == d) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object definition is a
+   * super-type of the provided managed object definition. This
+   * managed object definition is a super-type of the provided managed
+   * object definition if they are both the same or if the provided
+   * managed object definition is a member of the set of children
+   * returned from {@link #getAllChildren()}.
+   *
+   * @param d
+   *          The managed object definition to be checked.
+   * @return Returns <code>true</code> if this managed object
+   *         definition is a super-type of the provided managed object
+   *         definition.
+   */
+  public final boolean isParentOf(AbstractManagedObjectDefinition<?, ?> d) {
+    return d.isChildOf(this);
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object definition is the
+   * {@link TopCfgDefn}.
+   *
+   * @return Returns <code>true</code> if this managed object
+   *         definition is the {@link TopCfgDefn}.
+   */
+  public final boolean isTop() {
+    return (this instanceof TopCfgDefn);
+  }
+
+
+
+  /**
+   * Finds a sub-type of this managed object definition which most closely
+   * corresponds to the matching criteria of the provided definition resolver.
+   *
+   * @param r
+   *          The definition resolver.
+   * @return Returns the sub-type of this managed object definition which most
+   *         closely corresponds to the matching criteria of the provided
+   *         definition resolver.
+   * @throws DefinitionDecodingException
+   *           If no matching sub-type could be found or if the resolved
+   *           definition was abstract.
+   * @see DefinitionResolver
+   */
+  @SuppressWarnings("unchecked")
+  public final ManagedObjectDefinition<? extends C, ? extends S>
+      resolveManagedObjectDefinition(
+      DefinitionResolver r) throws DefinitionDecodingException {
+    AbstractManagedObjectDefinition<? extends C, ? extends S> rd;
+    rd = resolveManagedObjectDefinitionAux(this, r);
+    if (rd == null) {
+      // Unable to resolve the definition.
+      throw new DefinitionDecodingException(this,
+          Reason.WRONG_TYPE_INFORMATION);
+    } else if (rd instanceof ManagedObjectDefinition) {
+      return (ManagedObjectDefinition<? extends C, ? extends S>) rd;
+    } else {
+      // Resolved definition was abstract.
+      throw new DefinitionDecodingException(this,
+          Reason.ABSTRACT_TYPE_INFORMATION);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public final String toString() {
+    StringBuilder builder = new StringBuilder();
+    toString(builder);
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Append a string representation of the managed object definition to the
+   * provided string builder.
+   *
+   * @param builder
+   *          The string builder where the string representation should be
+   *          appended.
+   */
+  public final void toString(StringBuilder builder) {
+    builder.append(getName());
+  }
+
+
+
+  /**
+   * Initializes all of the components associated with this managed
+   * object definition.
+   *
+   * @throws Exception
+   *           If this managed object definition could not be
+   *           initialized.
+   */
+  protected final void initialize() throws Exception {
+    for (PropertyDefinition<?> pd : getAllPropertyDefinitions()) {
+      pd.initialize();
+      pd.getDefaultBehaviorProvider().initialize();
+    }
+
+    for (RelationDefinition<?, ?> rd : getAllRelationDefinitions()) {
+      rd.initialize();
+    }
+
+    for (AggregationPropertyDefinition<?, ?> apd :
+      getAllAggregationPropertyDefinitions()) {
+
+      apd.initialize();
+      // Now register the aggregation property in the referenced managed object
+      // definition for reverse lookups.
+      registerReverseAggregationPropertyDefinition(apd);
+    }
+
+    for (Constraint constraint : getAllConstraints()) {
+      constraint.initialize();
+    }
+  }
+
+
+
+  /**
+   * Register a constraint with this managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param constraint
+   *          The constraint to be registered.
+   */
+  protected final void registerConstraint(Constraint constraint) {
+    constraints.add(constraint);
+  }
+
+
+
+  /**
+   * Register a property definition with this managed object definition,
+   * overriding any existing property definition with the same name.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param d
+   *          The property definition to be registered.
+   */
+  protected final void registerPropertyDefinition(PropertyDefinition<?> d) {
+    String propName = d.getName();
+
+    propertyDefinitions.put(propName, d);
+    allPropertyDefinitions.put(propName, d);
+
+    if (d instanceof AggregationPropertyDefinition<?,?>) {
+      AggregationPropertyDefinition<?, ?> apd =
+        (AggregationPropertyDefinition<?, ?>) d;
+      aggregationPropertyDefinitions.put(propName, apd);
+      // The key must also contain the managed object name, since several MOs
+      // in an inheritance tree may aggregate the same aggregation property name
+      allAggregationPropertyDefinitions.put(
+        apd.getManagedObjectDefinition().getName() + ":" + propName, apd);
+    }
+  }
+
+
+
+  /**
+   * Register a relation definition with this managed object definition,
+   * overriding any existing relation definition with the same name.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param d
+   *          The relation definition to be registered.
+   */
+  protected final void registerRelationDefinition(RelationDefinition<?, ?> d) {
+    // Register the relation in this managed object definition.
+    String relName = d.getName();
+
+    relationDefinitions.put(relName, d);
+    allRelationDefinitions.put(relName, d);
+
+    // Now register the relation in the referenced managed object
+    // definition for reverse lookups.
+    registerReverseRelationDefinition(d);
+  }
+
+
+
+  /**
+   * Register an option with this managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param option
+   *          The option to be registered.
+   */
+  protected final void registerOption(ManagedObjectOption option) {
+    options.add(option);
+  }
+
+
+
+  /**
+   * Register a tag with this managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications.
+   *
+   * @param tag
+   *          The tag to be registered.
+   */
+  protected final void registerTag(Tag tag) {
+    allTags.add(tag);
+  }
+
+
+
+  /**
+   * Deregister a constraint from the managed object definition.
+   * <p>
+   * This method <b>must not</b> be called by applications and is
+   * only intended for internal testing.
+   *
+   * @param constraint
+   *          The constraint to be deregistered.
+   */
+  final void deregisterConstraint(Constraint constraint) {
+    if (!constraints.remove(constraint)) {
+      throw new RuntimeException("Failed to deregister a constraint");
+    }
+  }
+
+
+
+  /**
+   * Deregister a relation definition from the managed object
+   * definition.
+   * <p>
+   * This method <b>must not</b> be called by applications and is
+   * only intended for internal testing.
+   *
+   * @param d
+   *          The relation definition to be deregistered.
+   */
+  final void deregisterRelationDefinition(
+      RelationDefinition<?, ?> d) {
+   // Deregister the relation from this managed object definition.
+    String relName = d.getName();
+    relationDefinitions.remove(relName);
+    allRelationDefinitions.remove(relName);
+
+    // Now deregister the relation from the referenced managed object
+    // definition for reverse lookups.
+    d.getChildDefinition().reverseRelationDefinitions.remove(d);
+  }
+
+
+
+  /**
+   * Register this managed object definition in its parent.
+   * <p>
+   * This method <b>must not</b> be called by applications and is
+   * only intended for internal testing.
+   */
+  final void registerInParent() {
+    if (parent != null) {
+      parent.children.put(name, this);
+    }
+  }
+
+
+
+  // Register a relation definition in the referenced managed object
+  // definition's reverse lookup table.
+  private <CC extends ConfigurationClient, SS extends Configuration>
+  void registerReverseRelationDefinition(RelationDefinition<CC, SS> rd) {
+    rd.getChildDefinition().reverseRelationDefinitions.add(rd);
+  }
+
+
+
+  // Register a aggregation property definition in the referenced managed object
+  // definition's reverse lookup table.
+  private void registerReverseAggregationPropertyDefinition(
+    AggregationPropertyDefinition<?, ?> apd) {
+
+    apd.getRelationDefinition().getChildDefinition().
+      reverseAggregationPropertyDefinitions.add(apd);
+  }
+
+
+
+  // Recursively descend definition hierarchy to find the best match definition.
+  private AbstractManagedObjectDefinition<? extends C, ? extends S>
+      resolveManagedObjectDefinitionAux(
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d,
+      DefinitionResolver r) {
+    if (!r.matches(d)) {
+      return null;
+    }
+
+    for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : d
+        .getChildren()) {
+      AbstractManagedObjectDefinition<? extends C, ? extends S> rd =
+        resolveManagedObjectDefinitionAux(child, r);
+      if (rd != null) {
+        return rd;
+      }
+    }
+
+    return d;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AdminException.java b/opendj-admin/src/main/java/org/opends/server/admin/AdminException.java
new file mode 100644
index 0000000..dea9632
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AdminException.java
@@ -0,0 +1,73 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.messages.Message;
+
+import org.opends.server.types.OpenDsException;
+
+
+
+/**
+ * Exceptions thrown when interacting with administration framework.
+ */
+public abstract class AdminException extends OpenDsException {
+
+  /**
+   * Fake serialization ID.
+   */
+  private static final long serialVersionUID = 1L;
+
+
+
+  /**
+   * Create an admin exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected AdminException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an admin exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected AdminException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AdminRuntimeException.java b/opendj-admin/src/main/java/org/opends/server/admin/AdminRuntimeException.java
new file mode 100644
index 0000000..379e2d1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AdminRuntimeException.java
@@ -0,0 +1,91 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * Exceptions thrown when interacting with administration framework
+ * that applications are not expected to catch.
+ */
+public abstract class AdminRuntimeException extends RuntimeException {
+
+  /**
+   * Fake serialization ID.
+   */
+  private static final long serialVersionUID = 1L;
+
+
+
+  // Message that explains the problem.
+  private final Message message;
+
+
+
+  /**
+   * Create an admin runtime exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected AdminRuntimeException(Message message, Throwable cause) {
+    super(message.toString(), cause);
+    this.message = message;
+  }
+
+
+
+  /**
+   * Create an admin runtime exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected AdminRuntimeException(Message message) {
+    super(message.toString());
+    this.message = message;
+  }
+
+
+
+  /**
+   * Returns the message that explains the problem that occurred.
+   *
+   * @return Returns the message describing the problem that occurred
+   *         (never <code>null</code>).
+   */
+  public Message getMessageObject() {
+    return this.message;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AdministrationConnector.java b/opendj-admin/src/main/java/org/opends/server/admin/AdministrationConnector.java
new file mode 100644
index 0000000..46c1c73
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AdministrationConnector.java
@@ -0,0 +1,804 @@
+/*
+ * 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 2006-2010 Sun Microsystems, Inc.
+ *      Portions copyright 2011-2013 ForgeRock AS
+ */
+package org.opends.server.admin;
+
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.messages.AdminMessages.*;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import javax.naming.ldap.Rdn;
+import org.opends.messages.Message;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ServerManagementContext;
+import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn.
+  SSLClientAuthPolicy;
+import org.opends.server.admin.std.server.AdministrationConnectorCfg;
+import org.opends.server.admin.std.server.ConnectionHandlerCfg;
+import org.opends.server.admin.std.server.KeyManagerProviderCfg;
+import org.opends.server.admin.std.server.FileBasedKeyManagerProviderCfg;
+import org.opends.server.admin.std.server.FileBasedTrustManagerProviderCfg;
+import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.SynchronousStrategy;
+import org.opends.server.protocols.ldap.LDAPConnectionHandler;
+import org.opends.server.types.AddressMask;
+import org.opends.server.types.ConfigChangeResult;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.util.CertificateManager;
+import org.opends.server.util.SetupUtils;
+import org.opends.server.admin.std.server.TrustManagerProviderCfg;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.ErrorLogger;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.FilePermission;
+
+/**
+ * This class is a wrapper on top of LDAPConnectionHandler to manage
+ * the administration connector, which is an LDAPConnectionHandler
+ * with specific (limited) configuration properties.
+ */
+public final class AdministrationConnector implements
+    ConfigurationChangeListener<AdministrationConnectorCfg>
+{
+
+  /**
+   * Default Administration Connector port.
+   */
+  public static final int DEFAULT_ADMINISTRATION_CONNECTOR_PORT = 4444;
+
+  /**
+   * Validity (in days) of the generated certificate.
+   */
+  public static final int ADMIN_CERT_VALIDITY = 20 * 365;
+
+  // Friendly name of the administration connector
+  private static final String FRIENDLY_NAME = "Administration Connector";
+
+  // The tracer object for the debug logger.
+  private static final DebugTracer TRACER = getTracer();
+
+  private LDAPConnectionHandler adminConnectionHandler;
+
+  private AdministrationConnectorCfg config; //
+
+  // Predefined values for Administration Connector configuration
+  //
+  private static final String ADMIN_CLASS_NAME =
+    "org.opends.server.protocols.ldap.LDAPConnectionHandler";
+
+  private static final boolean ADMIN_ALLOW_LDAP_V2 = false;
+
+  private static final boolean ADMIN_ALLOW_START_TLS = false;
+
+  private static final SortedSet<AddressMask> ADMIN_ALLOWED_CLIENT =
+    new TreeSet<AddressMask>();
+
+  private static final SortedSet<AddressMask> ADMIN_DENIED_CLIENT =
+    new TreeSet<AddressMask>();
+
+  private static final boolean ADMIN_ENABLED = true;
+
+  private static final boolean ADMIN_KEEP_STATS = true;
+
+  private static final boolean ADMIN_USE_SSL = true;
+
+  private static final int ADMIN_ACCEPT_BACKLOG = 128;
+
+  private static final boolean ADMIN_ALLOW_TCP_REUSE_ADDRESS = true;
+
+  private static final long ADMIN_MAX_BLOCKED_WRITE_TIME_LIMIT = 120000; // 2mn
+
+  private static final int ADMIN_MAX_REQUEST_SIZE = 5000000; // 5 Mb
+
+  private static final int ADMIN_WRITE_BUFFER_SIZE = 4096;
+
+  private static final int ADMIN_NUM_REQUEST_HANDLERS = 1;
+
+  private static final boolean ADMIN_SEND_REJECTION_NOTICE = true;
+
+  private static final boolean ADMIN_USE_TCP_KEEP_ALIVE = true;
+
+  private static final boolean ADMIN_USE_TCP_NO_DELAY = true;
+
+  private static final SSLClientAuthPolicy ADMIN_SSL_CLIENT_AUTH_POLICY =
+    SSLClientAuthPolicy.DISABLED;
+
+  private static final SortedSet<String> ADMIN_SSL_CIPHER_SUITE =
+    new TreeSet<String>();
+
+  private static final SortedSet<String> ADMIN_SSL_PROTOCOL =
+    new TreeSet<String>();
+
+
+
+  /**
+   * Initializes this administration connector provider based on the
+   * information in the provided administration connector
+   * configuration.
+   *
+   * @param configuration
+   *          The connection handler configuration that contains the
+   *          information to use to initialize this connection
+   *          handler.
+   * @throws ConfigException
+   *           If an unrecoverable problem arises in the process of
+   *           performing the initialization as a result of the server
+   *           configuration.
+   * @throws InitializationException
+   *           If a problem occurs during initialization that is not
+   *           related to the server configuration.
+   */
+  public void initializeAdministrationConnector(
+      AdministrationConnectorCfg configuration) throws ConfigException,
+      InitializationException
+  {
+    this.config = configuration;
+
+    // Create a fake LDAP connection handler configuration
+    LDAPConnectionHandlerCfg ldapConnectionHandlerCfg =
+      new FakeLDAPConnectionHandlerCfg(config);
+
+    // Administration Connector uses the LDAP connection handler
+    // implementation
+    adminConnectionHandler = new LDAPConnectionHandler(
+        new SynchronousStrategy(), FRIENDLY_NAME);
+    adminConnectionHandler
+        .initializeConnectionHandler(ldapConnectionHandlerCfg);
+    adminConnectionHandler.setAdminConnectionHandler();
+
+    // Register this as a change listener.
+    config.addChangeListener(this);
+  }
+
+
+
+  /**
+   * Create an instance of the administration connector.
+   */
+  public AdministrationConnector()
+  {
+    // Do nothing.
+  }
+
+
+
+  /**
+   * Retrieves the connection handler linked to this administration
+   * connector.
+   *
+   * @return The connection handler linked to this administration
+   *         connector.
+   */
+  public LDAPConnectionHandler getConnectionHandler()
+  {
+    return adminConnectionHandler;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+      AdministrationConnectorCfg configuration,
+      List<Message> unacceptableReasons)
+  {
+    LDAPConnectionHandlerCfg cfg = new FakeLDAPConnectionHandlerCfg(
+        configuration);
+    return adminConnectionHandler.isConfigurationAcceptable(cfg,
+        unacceptableReasons);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      AdministrationConnectorCfg configuration)
+  {
+    return new ConfigChangeResult(ResultCode.SUCCESS, true,
+        new ArrayList<Message>());
+  }
+
+
+
+  /**
+   * This private class implements a fake LDAP connection Handler
+   * configuration. This allows to re-use the LDAPConnectionHandler as
+   * it is.
+   */
+  private static class FakeLDAPConnectionHandlerCfg implements
+      LDAPConnectionHandlerCfg
+  {
+
+    private final AdministrationConnectorCfg config;
+
+
+
+    public FakeLDAPConnectionHandlerCfg(AdministrationConnectorCfg config)
+    {
+      this.config = config;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Class<? extends LDAPConnectionHandlerCfg> configurationClass()
+    {
+      return LDAPConnectionHandlerCfg.class;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addLDAPChangeListener(
+        ConfigurationChangeListener<LDAPConnectionHandlerCfg> listener)
+    {
+      // do nothing. change listener already added.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeLDAPChangeListener(
+        ConfigurationChangeListener<LDAPConnectionHandlerCfg> listener)
+    {
+      // do nothing. change listener already added.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getAcceptBacklog()
+    {
+      return ADMIN_ACCEPT_BACKLOG;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAllowLDAPV2()
+    {
+      return ADMIN_ALLOW_LDAP_V2;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAllowStartTLS()
+    {
+      return ADMIN_ALLOW_START_TLS;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAllowTCPReuseAddress()
+    {
+      return ADMIN_ALLOW_TCP_REUSE_ADDRESS;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getJavaClass()
+    {
+      return ADMIN_CLASS_NAME;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isKeepStats()
+    {
+      return ADMIN_KEEP_STATS;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getKeyManagerProvider()
+    {
+      return config.getKeyManagerProvider();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public DN getKeyManagerProviderDN()
+    {
+      return config.getKeyManagerProviderDN();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<InetAddress> getListenAddress()
+    {
+      return config.getListenAddress();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getListenPort()
+    {
+      return config.getListenPort();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getMaxBlockedWriteTimeLimit()
+    {
+      return ADMIN_MAX_BLOCKED_WRITE_TIME_LIMIT;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getMaxRequestSize()
+    {
+      return ADMIN_MAX_REQUEST_SIZE;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getBufferSize()
+    {
+      return ADMIN_WRITE_BUFFER_SIZE;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Integer getNumRequestHandlers()
+    {
+      return ADMIN_NUM_REQUEST_HANDLERS;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isSendRejectionNotice()
+    {
+      return ADMIN_SEND_REJECTION_NOTICE;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getSSLCertNickname()
+    {
+      return config.getSSLCertNickname();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<String> getSSLCipherSuite()
+    {
+      return config.getSSLCipherSuite();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SSLClientAuthPolicy getSSLClientAuthPolicy()
+    {
+      return ADMIN_SSL_CLIENT_AUTH_POLICY;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<String> getSSLProtocol()
+    {
+      return config.getSSLProtocol();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getTrustManagerProvider()
+    {
+      return config.getTrustManagerProvider();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public DN getTrustManagerProviderDN()
+    {
+      return config.getTrustManagerProviderDN();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isUseSSL()
+    {
+      return ADMIN_USE_SSL;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isUseTCPKeepAlive()
+    {
+      return ADMIN_USE_TCP_KEEP_ALIVE;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isUseTCPNoDelay()
+    {
+      return ADMIN_USE_TCP_NO_DELAY;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addChangeListener(
+        ConfigurationChangeListener<ConnectionHandlerCfg> listener)
+    {
+      // do nothing. change listener already added.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeChangeListener(
+        ConfigurationChangeListener<ConnectionHandlerCfg> listener)
+    {
+      // do nothing. change listener already added.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<AddressMask> getAllowedClient()
+    {
+      return ADMIN_ALLOWED_CLIENT;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<AddressMask> getDeniedClient()
+    {
+      return ADMIN_DENIED_CLIENT;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEnabled()
+    {
+      return ADMIN_ENABLED;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public DN dn()
+    {
+      return config.dn();
+    }
+  }
+
+
+
+  /**
+   * Creates a self-signed JKS certificate if needed.
+   *
+   * @throws InitializationException
+   *           If an unexpected error occurred whilst trying to create the
+   *           certificate.
+   */
+  public static void createSelfSignedCertificateIfNeeded()
+      throws InitializationException
+  {
+    try
+    {
+      RootCfg root = ServerManagementContext.getInstance()
+          .getRootConfiguration();
+      AdministrationConnectorCfg config = root.getAdministrationConnector();
+
+      // Check if certificate generation is needed
+      String certAlias = config.getSSLCertNickname();
+      KeyManagerProviderCfg keyMgrConfig = root.getKeyManagerProvider(config
+          .getKeyManagerProvider());
+      TrustManagerProviderCfg trustMgrConfig = root
+          .getTrustManagerProvider(config.getTrustManagerProvider());
+
+      if (hasDefaultConfigChanged(keyMgrConfig, trustMgrConfig))
+      {
+        // nothing to do
+        return;
+      }
+
+      FileBasedKeyManagerProviderCfg fbKeyManagerConfig =
+        (FileBasedKeyManagerProviderCfg) keyMgrConfig;
+      String keystorePath = getFullPath(fbKeyManagerConfig.getKeyStoreFile());
+      FileBasedTrustManagerProviderCfg fbTrustManagerConfig =
+        (FileBasedTrustManagerProviderCfg) trustMgrConfig;
+      String truststorePath = getFullPath(fbTrustManagerConfig
+          .getTrustStoreFile());
+      String pinFilePath = getFullPath(fbKeyManagerConfig.getKeyStorePinFile());
+
+      // Check that either we do not have any file,
+      // or we have the 3 required files (keystore, truststore, pin
+      // file)
+      boolean keystore = false;
+      boolean truststore = false;
+      boolean pinFile = false;
+      int nbFiles = 0;
+      if (new File(keystorePath).exists())
+      {
+        keystore = true;
+        nbFiles++;
+      }
+      if (new File(truststorePath).exists())
+      {
+        truststore = true;
+        nbFiles++;
+      }
+      if (new File(pinFilePath).exists())
+      {
+        pinFile = true;
+        nbFiles++;
+      }
+      if (nbFiles == 3)
+      {
+        // nothing to do
+        return;
+      }
+      if (nbFiles != 0)
+      {
+        // 1 or 2 files are missing : error
+        String err = "";
+        if (!keystore)
+        {
+          err += keystorePath + " ";
+        }
+        if (!truststore)
+        {
+          err += truststorePath + " ";
+        }
+        if (!pinFile)
+        {
+          err += pinFilePath + " ";
+        }
+        Message message = ERR_ADMIN_CERTIFICATE_GENERATION_MISSING_FILES
+            .get(err);
+        logError(message);
+        throw new InitializationException(message);
+      }
+
+      // Generate a password
+      String pwd = new String(SetupUtils.createSelfSignedCertificatePwd());
+
+      // Generate a self-signed certificate
+      CertificateManager certManager = new CertificateManager(
+          getFullPath(fbKeyManagerConfig.getKeyStoreFile()), fbKeyManagerConfig
+              .getKeyStoreType(), pwd);
+      String hostName =
+        SetupUtils.getHostNameForCertificate(DirectoryServer.getServerRoot());
+      String subjectDN = "cn="
+          + Rdn.escapeValue(hostName) + ",O="
+          + FRIENDLY_NAME + " Self-Signed Certificate";
+      certManager.generateSelfSignedCertificate(certAlias, subjectDN,
+          ADMIN_CERT_VALIDITY);
+
+      // Export the certificate
+      String tempCertPath = getFullPath("config" + File.separator
+          + "admin-cert.txt");
+      SetupUtils.exportCertificate(certManager, certAlias, tempCertPath);
+
+      // Create a new trust store and import the server certificate
+      // into it
+      CertificateManager trustManager = new CertificateManager(truststorePath,
+          CertificateManager.KEY_STORE_TYPE_JKS, pwd);
+      trustManager.addCertificate(certAlias, new File(tempCertPath));
+
+      // Generate a password file
+      if (!new File(pinFilePath).exists())
+      {
+        FileWriter file = new FileWriter(pinFilePath);
+        PrintWriter out = new PrintWriter(file);
+        out.println(pwd);
+        out.flush();
+        out.close();
+        file.close();
+      }
+
+      // Change the password file permission if possible
+      if (FilePermission.canSetPermissions())
+      {
+        try
+        {
+          if (!FilePermission.setPermissions(new File(pinFilePath),
+              new FilePermission(0600)))
+          {
+            // Log a warning that the permissions were not set.
+            Message message = WARN_ADMIN_SET_PERMISSIONS_FAILED
+                .get(pinFilePath);
+            ErrorLogger.logError(message);
+          }
+        }
+        catch (DirectoryException e)
+        {
+          // Log a warning that the permissions were not set.
+          Message message = WARN_ADMIN_SET_PERMISSIONS_FAILED.get(pinFilePath);
+          ErrorLogger.logError(message);
+        }
+      }
+
+      // Delete the exported certificate
+      File f = new File(tempCertPath);
+      f.delete();
+    }
+    catch (InitializationException e)
+    {
+      throw e;
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      Message message = ERR_ADMIN_CERTIFICATE_GENERATION.get(e.getMessage());
+      logError(message);
+      throw new InitializationException(message);
+    }
+  }
+
+  /**
+   * Check if default configuration for administrator's key manager and trust
+   * manager provider has changed.
+   *
+   * @param keyConfig
+   *          key manager provider configuration
+   * @param trustConfig
+   *          trust manager provider configuration
+   * @return true if default configuration has changed, false otherwise
+   */
+  private static boolean hasDefaultConfigChanged(
+      KeyManagerProviderCfg keyConfig, TrustManagerProviderCfg trustConfig)
+  {
+    if (keyConfig.isEnabled()
+        && (keyConfig instanceof FileBasedKeyManagerProviderCfg)
+        && trustConfig.isEnabled()
+        && (trustConfig instanceof FileBasedTrustManagerProviderCfg))
+    {
+      FileBasedKeyManagerProviderCfg fileKeyConfig =
+          (FileBasedKeyManagerProviderCfg) keyConfig;
+      boolean pinIsProvidedByFileOnly =
+          (fileKeyConfig.getKeyStorePinFile() != null)
+              && (fileKeyConfig.getKeyStorePin() == null)
+              && (fileKeyConfig.getKeyStorePinEnvironmentVariable() == null)
+              && (fileKeyConfig.getKeyStorePinProperty() == null);
+      return !pinIsProvidedByFileOnly;
+    }
+    return true;
+  }
+
+  private static String getFullPath(String path)
+  {
+    File file = new File(path);
+    if (!file.isAbsolute())
+    {
+      path = DirectoryServer.getInstanceRoot() + File.separator + path;
+    }
+
+    return path;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AdministrationDataSync.java b/opendj-admin/src/main/java/org/opends/server/admin/AdministrationDataSync.java
new file mode 100644
index 0000000..7e72050
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AdministrationDataSync.java
@@ -0,0 +1,353 @@
+/*
+ * 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 2006-2008 Sun Microsystems, Inc.
+ *      Portions Copyright 2012 ForgeRock AS
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.schema.DirectoryStringSyntax;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.ByteString;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.LDAPException;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+
+
+
+/**
+ * Check if information found in "cn=admin data" is coherent with
+ * cn=config. If and inconsistency is detected, we log a warning
+ * message and update "cn=admin data"
+ */
+public final class AdministrationDataSync
+{
+
+  /**
+   * The root connection.
+   */
+  private InternalClientConnection internalConnection;
+
+  /**
+   * The attribute name used to store the port. TODO Use the default
+   * one.
+   */
+  private static final String LDAP_PORT = "ds-cfg-listen-port";
+
+
+
+  /**
+   * Create an object that will syncrhonize configuration and the
+   * admin data.
+   *
+   * @param internalConnection
+   *          The root connection.
+   */
+  public AdministrationDataSync(InternalClientConnection internalConnection)
+  {
+    this.internalConnection = internalConnection;
+  }
+
+
+
+  /**
+   * Check if information found in "cn=admin data" is coherent with
+   * cn=config. If and inconsistancy is detected, we log a warning
+   * message and update "cn=admin data"
+   */
+  public void synchronize()
+  {
+    // Check if the admin connector is in sync
+    checkAdminConnector();
+  }
+
+
+
+  /**
+   * Check if the admin connector is in sync. The desynchronization
+   * could occurs after the upgrade from 1.0.
+   */
+  private void checkAdminConnector()
+  {
+    // Look for the server registration in "cn=admin data"
+    DN serverEntryDN = searchServerEntry();
+    if (serverEntryDN == null)
+    {
+      // Nothing to do
+      return;
+    }
+
+    // Get the admin port
+    String adminPort = getAttr("cn=Administration Connector,cn=config",
+        LDAP_PORT);
+    if (adminPort == null)
+    {
+      // best effort.
+      return;
+    }
+
+    LinkedList<Modification> mods = new LinkedList<Modification>();
+    // adminport
+    String attName = "adminport";
+    AttributeType attrType = DirectoryServer.getAttributeType(attName
+        .toLowerCase());
+    if (attrType == null)
+    {
+      attrType = DirectoryServer.getDefaultAttributeType(attName.toLowerCase());
+    }
+    mods.add(new Modification(ModificationType.REPLACE, Attributes.create(
+        attrType, adminPort)));
+
+    // adminEnabled
+    attName = "adminEnabled";
+    attrType = DirectoryServer.getAttributeType(attName.toLowerCase());
+    if (attrType == null)
+    {
+      attrType = DirectoryServer.getDefaultAttributeType(attName.toLowerCase());
+    }
+    mods.add(new Modification(ModificationType.REPLACE, Attributes.create(
+        attrType, "true")));
+
+    // Process modification
+    internalConnection.processModify(serverEntryDN, mods);
+  }
+
+
+
+  /**
+   * Look for the DN of the local register server. Assumption: default
+   * Connection Handler naming is used.
+   *
+   * @return The DN of the local register server or null.
+   */
+  private DN searchServerEntry()
+  {
+    DN returnDN = null;
+
+    // Get the LDAP and LDAPS port
+    String ldapPort = getAttr(
+        "cn=LDAP Connection Handler,cn=Connection Handlers,cn=config",
+        LDAP_PORT);
+    String ldapsPort = getAttr(
+        "cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config",
+        LDAP_PORT);
+    boolean ldapsPortEnable = false;
+    String val = getAttr(
+        "cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config",
+        "ds-cfg-enabled");
+    if (val != null)
+    {
+      ldapsPortEnable = val.toLowerCase().equals("true");
+    }
+    if ((ldapPort == null) && (ldapsPort == null))
+    {
+      // best effort (see assumption)
+      return null;
+    }
+
+    // Get the IP address of the local host.
+    String hostName;
+    try
+    {
+      hostName = java.net.InetAddress.getLocalHost().getCanonicalHostName();
+    }
+    catch (Throwable t)
+    {
+      // best effort.
+      return null;
+    }
+
+    // Look for a local server with the Ldap Port.
+    String attrName = "hostname";
+    AttributeType hostnameType = DirectoryServer.getAttributeType(attrName);
+    if (hostnameType == null)
+    {
+      hostnameType = DirectoryServer.getDefaultAttributeType(attrName);
+    }
+    try
+    {
+      InternalSearchOperation op = internalConnection.processSearch(
+          "cn=Servers,cn=admin data",
+          SearchScope.SINGLE_LEVEL, "objectclass=*");
+      if (op.getResultCode() == ResultCode.SUCCESS)
+      {
+        Entry entry = null;
+        for (Entry currentEntry : op.getSearchEntries())
+        {
+          String currentHostname = currentEntry.getAttributeValue(hostnameType,
+              DirectoryStringSyntax.DECODER);
+          try
+          {
+            String currentIPAddress = java.net.InetAddress.getByName(
+                currentHostname).getCanonicalHostName();
+            if (currentIPAddress.equals(hostName))
+            {
+              // Check if one of the port match
+              attrName = "ldapport";
+              AttributeType portType = DirectoryServer
+                  .getAttributeType(attrName);
+              if (portType == null)
+              {
+                portType = DirectoryServer.getDefaultAttributeType(attrName);
+              }
+              String currentport = currentEntry.getAttributeValue(portType,
+                  DirectoryStringSyntax.DECODER);
+              if (currentport.equals(ldapPort))
+              {
+                entry = currentEntry;
+                break;
+              }
+              if (ldapsPortEnable)
+              {
+                attrName = "ldapsport";
+                portType = DirectoryServer.getAttributeType(attrName);
+                if (portType == null)
+                {
+                  portType = DirectoryServer.getDefaultAttributeType(attrName);
+                }
+                currentport = currentEntry.getAttributeValue(portType,
+                    DirectoryStringSyntax.DECODER);
+                if (currentport.equals(ldapsPort))
+                {
+                  entry = currentEntry;
+                  break;
+                }
+              }
+            }
+          }
+          catch (Exception e)
+          {
+            // best effort.
+            continue;
+          }
+        }
+
+        if (entry != null)
+        {
+          returnDN = entry.getDN();
+        }
+      }
+
+    }
+    catch (DirectoryException e)
+    {
+      // never happens because the filter is always valid.
+      return null;
+    }
+    return returnDN;
+  }
+
+
+
+  /**
+   * Gets an attribute value from an entry.
+   *
+   * @param DN
+   *          The DN of the entry.
+   * @param attrName
+   *          The attribute name.
+   * @return The attribute value or {@code null} if the value could
+   *         not be retrieved.
+   */
+  private String getAttr(String baseDN, String attrName)
+  {
+    // Prepare the ldap search
+    LDAPFilter filter;
+    try
+    {
+      filter = LDAPFilter.decode("objectclass=*");
+    }
+    catch (LDAPException e)
+    {
+      // can not happen
+      // best effort.
+      // TODO Log an Error.
+      return null;
+    }
+
+    LinkedHashSet<String> attributes = new LinkedHashSet<String>(1);
+    attributes.add(attrName);
+    InternalSearchOperation search = internalConnection.processSearch(
+        ByteString.valueOf(baseDN), SearchScope.BASE_OBJECT,
+        DereferencePolicy.DEREF_ALWAYS, 0, 0, false, filter, attributes);
+
+    if ((search.getResultCode() != ResultCode.SUCCESS))
+    {
+      // can not happen
+      // best effort.
+      // TODO Log an Error.
+      return null;
+    }
+
+    SearchResultEntry adminConnectorEntry = null;
+
+    /*
+     * Read the port from the PORT attribute
+     */
+    LinkedList<SearchResultEntry> result = search.getSearchEntries();
+    if (!result.isEmpty())
+    {
+      adminConnectorEntry = result.getFirst();
+    }
+
+    AttributeType attrType = DirectoryServer.getAttributeType(attrName);
+    if (attrType == null)
+    {
+      attrType = DirectoryServer.getDefaultAttributeType(attrName);
+    }
+
+    List<Attribute> attrs = adminConnectorEntry.getAttribute(attrType);
+
+    if (attrs == null)
+    {
+      // can not happen
+      // best effort.
+      // TODO Log an Error.
+      return null;
+    }
+
+    // Get the attribute value
+    return attrs.get(0).iterator().next().toString();
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java b/opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java
new file mode 100644
index 0000000..2b8d069
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AdministratorAction.java
@@ -0,0 +1,191 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+
+
+
+/**
+ * Defines an optional action which administators must perform after
+ * they have modified a property. By default modifications to
+ * properties are assumed to take effect immediately and require no
+ * additional administrative action. Developers should be aware that,
+ * where feasible, they should implement components such that property
+ * modifications require no additional administrative action. This is
+ * required in order to minimize server downtime during administration
+ * and provide a more user-friendly experience.
+ */
+public final class AdministratorAction {
+
+  /**
+   * Specifies the type of administrator action which must be
+   * performed in order for pending changes to take effect.
+   */
+  public static enum Type {
+    /**
+     * Used when modifications to a property require a component
+     * restart in order to take effect (usually by disabling and
+     * re-enabling the component). May have a description describing
+     * any additional administrator action that is required when the
+     * component is restarted.
+     */
+    COMPONENT_RESTART("component-restart"),
+
+    /**
+     * Used when modifications to a property take effect immediately,
+     * and no additional administrator action is required. May have a
+     * description describing how changes to the modified property
+     * will take effect.
+     */
+    NONE("none"),
+
+    /**
+     * Used when modifications to a property require an additional
+     * administrative action in order to take effect. This should be
+     * used when neither a server restart nor a component restart are
+     * applicable. Always has a description which describes the
+     * additional administrator action which is required when the
+     * property is modified.
+     */
+    OTHER("other"),
+
+    /**
+     * Used when modifications to a property require a server restart
+     * in order to take effect. May have a description describing any
+     * additional administrator action that is required when the
+     * component is restarted.
+     */
+    SERVER_RESTART("server-restart");
+
+    // The user-friendly name of the type.
+    private final String name;
+
+
+
+    // Private constructor.
+    private Type(String name) {
+      this.name = name;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      return name;
+    }
+
+  }
+
+  // The managed object definition associated with this administrator
+  // action.
+  private final AbstractManagedObjectDefinition<?, ?> definition;
+
+  // The name of the property definition associated with this
+  // administrator action.
+  private final String propertyName;
+
+  // The type of administration action.
+  private final Type type;
+
+
+
+  /**
+   * Create a new administrator action.
+   *
+   * @param type
+   *          The type of this administration action.
+   * @param d
+   *          The managed object definition associated with this
+   *          administrator action.
+   * @param propertyName
+   *          The name of the property definition associated with this
+   *          administrator action.
+   */
+  public AdministratorAction(Type type,
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    this.type = type;
+    this.definition = d;
+    this.propertyName = propertyName;
+  }
+
+
+
+  /**
+   * Gets the synopsis of this administrator action in the default
+   * locale.
+   *
+   * @return Returns the synopsis of this administrator action in the
+   *         default locale, or <code>null</code> if there is no
+   *         synopsis defined.
+   */
+  public final Message getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this administrator action in the specified
+   * locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this administrator action in the
+   *         specified locale, or <code>null</code> if there is no
+   *         synopsis defined.
+   */
+  public final Message getSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + propertyName
+        + ".requires-admin-action.synopsis";
+    try {
+      return resource.getMessage(definition, property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Gets the type of this administrator action.
+   *
+   * @return Returns the type of this administrator action.
+   */
+  public final Type getType() {
+    return type;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AggregationPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/AggregationPropertyDefinition.java
new file mode 100644
index 0000000..a028505
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -0,0 +1,1204 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.Validator.*;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.SortedSet;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.condition.Condition;
+import org.opends.server.admin.condition.Conditions;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.server.ServerConstraintHandler;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.admin.server.ServerManagedObjectChangeListener;
+import org.opends.server.admin.server.ServerManagementContext;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.ErrorLogger;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.ConfigChangeResult;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
+import org.opends.server.util.StaticUtils;
+
+
+
+/**
+ * Aggregation property definition.
+ * <p>
+ * An aggregation property names one or more managed objects which are
+ * required by the managed object associated with this property. An
+ * aggregation property definition takes care to perform referential
+ * integrity checks: referenced managed objects cannot be deleted. Nor
+ * can an aggregation reference non-existent managed objects.
+ * Referential integrity checks are <b>not</b> performed during value
+ * validation. Instead they are performed when changes to the managed
+ * object are committed.
+ * <p>
+ * An aggregation property definition can optionally identify two
+ * properties:
+ * <ul>
+ * <li>an <code>enabled</code> property in the aggregated managed
+ * object - the property must be a {@link BooleanPropertyDefinition}
+ * and indicate whether the aggregated managed object is enabled or
+ * not. If specified, the administration framework will prevent the
+ * aggregated managed object from being disabled while it is
+ * referenced
+ * <li>an <code>enabled</code> property in this property's managed
+ * object - the property must be a {@link BooleanPropertyDefinition}
+ * and indicate whether this property's managed object is enabled or
+ * not. If specified, and as long as there is an equivalent
+ * <code>enabled</code> property defined for the aggregated managed
+ * object, the <code>enabled</code> property in the aggregated
+ * managed object will only be checked when this property is true.
+ * </ul>
+ * In other words, these properties can be used to make sure that
+ * referenced managed objects are not disabled while they are
+ * referenced.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          aggregation property definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          aggregation property definition refers to.
+ */
+public final class AggregationPropertyDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends PropertyDefinition<String> {
+
+  /**
+   * An interface for incrementally constructing aggregation property
+   * definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this aggregation property definition refers to.
+   */
+  public static class Builder
+      <C extends ConfigurationClient, S extends Configuration>
+      extends AbstractBuilder<String, AggregationPropertyDefinition<C, S>> {
+
+    // The string representation of the managed object path specifying
+    // the parent of the aggregated managed objects.
+    private String parentPathString = null;
+
+    // The name of a relation in the parent managed object which
+    // contains the aggregated managed objects.
+    private String rdName = null;
+
+    // The condition which is used to determine if a referenced
+    // managed object is enabled.
+    private Condition targetIsEnabledCondition = Conditions.TRUE;
+
+    // The condition which is used to determine whether or not
+    // referenced managed objects need to be enabled.
+    private Condition targetNeedsEnablingCondition = Conditions.TRUE;
+
+
+
+    // Private constructor
+    private Builder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Sets the name of the managed object which is the parent of the
+     * aggregated managed objects.
+     * <p>
+     * This must be defined before the property definition can be
+     * built.
+     *
+     * @param pathString
+     *          The string representation of the managed object path
+     *          specifying the parent of the aggregated managed
+     *          objects.
+     */
+    public final void setParentPath(String pathString) {
+      this.parentPathString = pathString;
+    }
+
+
+
+    /**
+     * Sets the relation in the parent managed object which contains
+     * the aggregated managed objects.
+     * <p>
+     * This must be defined before the property definition can be
+     * built.
+     *
+     * @param rdName
+     *          The name of a relation in the parent managed object
+     *          which contains the aggregated managed objects.
+     */
+    public final void setRelationDefinition(String rdName) {
+      this.rdName = rdName;
+    }
+
+
+
+    /**
+     * Sets the condition which is used to determine if a referenced
+     * managed object is enabled. By default referenced managed
+     * objects are assumed to always be enabled.
+     *
+     * @param condition
+     *          The condition which is used to determine if a
+     *          referenced managed object is enabled.
+     */
+    public final void setTargetIsEnabledCondition(Condition condition) {
+      this.targetIsEnabledCondition = condition;
+    }
+
+
+
+    /**
+     * Sets the condition which is used to determine whether or not
+     * referenced managed objects need to be enabled. By default
+     * referenced managed objects must always be enabled.
+     *
+     * @param condition
+     *          The condition which is used to determine whether or
+     *          not referenced managed objects need to be enabled.
+     */
+    public final void setTargetNeedsEnablingCondition(Condition condition) {
+      this.targetNeedsEnablingCondition = condition;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected AggregationPropertyDefinition<C, S> buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options, AdministratorAction adminAction,
+        DefaultBehaviorProvider<String> defaultBehavior) {
+      // Make sure that the parent path has been defined.
+      if (parentPathString == null) {
+        throw new IllegalStateException("Parent path undefined");
+      }
+
+      // Make sure that the relation definition has been defined.
+      if (rdName == null) {
+        throw new IllegalStateException("Relation definition undefined");
+      }
+
+      return new AggregationPropertyDefinition<C, S>(d, propertyName, options,
+          adminAction, defaultBehavior, parentPathString, rdName,
+          targetNeedsEnablingCondition, targetIsEnabledCondition);
+    }
+
+  }
+
+
+
+  /**
+   * A change listener which prevents the named component from being
+   * disabled.
+   */
+  private class ReferentialIntegrityChangeListener implements
+      ServerManagedObjectChangeListener<S> {
+
+    // The error message which should be returned if an attempt is
+    // made to disable the referenced component.
+    private final Message message;
+
+    // The path of the referenced component.
+    private final ManagedObjectPath<C, S> path;
+
+
+
+    // Creates a new referential integrity delete listener.
+    private ReferentialIntegrityChangeListener(ManagedObjectPath<C, S> path,
+        Message message) {
+      this.path = path;
+      this.message = message;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigChangeResult applyConfigurationChange(
+        ServerManagedObject<? extends S> mo) {
+      try {
+        if (targetIsEnabledCondition.evaluate(mo)) {
+          return new ConfigChangeResult(ResultCode.SUCCESS, false);
+        }
+      } catch (ConfigException e) {
+        // This should not happen - ignore it and throw an exception
+        // anyway below.
+      }
+
+      // This should not happen - the previous call-back should have
+      // trapped this.
+      throw new IllegalStateException("Attempting to disable a referenced "
+          + relationDefinition.getChildDefinition().getUserFriendlyName());
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConfigurationChangeAcceptable(
+        ServerManagedObject<? extends S> mo,
+        List<Message> unacceptableReasons) {
+      // Always prevent the referenced component from being
+      // disabled.
+      try {
+        if (!targetIsEnabledCondition.evaluate(mo)) {
+          unacceptableReasons.add(message);
+          return false;
+        } else {
+          return true;
+        }
+      } catch (ConfigException e) {
+        // The condition could not be evaluated.
+        if (debugEnabled()) {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message = ERR_REFINT_UNABLE_TO_EVALUATE_TARGET_CONDITION.get(mo
+            .getManagedObjectDefinition().getUserFriendlyName(), String
+            .valueOf(mo.getDN()), StaticUtils.getExceptionMessage(e));
+        ErrorLogger.logError(message);
+        unacceptableReasons.add(message);
+        return false;
+      }
+    }
+
+
+
+    // Gets the path associated with this listener.
+    private ManagedObjectPath<C, S> getManagedObjectPath() {
+      return path;
+    }
+
+  }
+
+
+
+  /**
+   * A delete listener which prevents the named component from being
+   * deleted.
+   */
+  private class ReferentialIntegrityDeleteListener implements
+      ConfigurationDeleteListener<S> {
+
+    // The DN of the referenced configuration entry.
+    private final DN dn;
+
+    // The error message which should be returned if an attempt is
+    // made to delete the referenced component.
+    private final Message message;
+
+
+
+    // Creates a new referential integrity delete listener.
+    private ReferentialIntegrityDeleteListener(DN dn, Message message) {
+      this.dn = dn;
+      this.message = message;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigChangeResult applyConfigurationDelete(S configuration) {
+      // This should not happen - the
+      // isConfigurationDeleteAcceptable() call-back should have
+      // trapped this.
+      if (configuration.dn().equals(dn)) {
+        // This should not happen - the
+        // isConfigurationDeleteAcceptable() call-back should have
+        // trapped this.
+        throw new IllegalStateException("Attempting to delete a referenced "
+            + relationDefinition.getChildDefinition().getUserFriendlyName());
+      } else {
+        return new ConfigChangeResult(ResultCode.SUCCESS, false);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isConfigurationDeleteAcceptable(S configuration,
+        List<Message> unacceptableReasons) {
+      if (configuration.dn().equals(dn)) {
+        // Always prevent deletion of the referenced component.
+        unacceptableReasons.add(message);
+        return false;
+      }
+
+      return true;
+    }
+
+  }
+
+
+
+  /**
+   * The server-side constraint handler implementation.
+   */
+  private class ServerHandler extends ServerConstraintHandler {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isUsable(ServerManagedObject<?> managedObject,
+        Collection<Message> unacceptableReasons) throws ConfigException {
+      SortedSet<String> names = managedObject
+          .getPropertyValues(AggregationPropertyDefinition.this);
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      Message thisUFN = managedObject.getManagedObjectDefinition()
+          .getUserFriendlyName();
+      String thisDN = managedObject.getDN().toString();
+      Message thatUFN = getRelationDefinition().getUserFriendlyName();
+
+      boolean isUsable = true;
+      boolean needsEnabling = targetNeedsEnablingCondition
+          .evaluate(managedObject);
+      for (String name : names) {
+        ManagedObjectPath<C, S> path = getChildPath(name);
+        String thatDN = path.toDN().toString();
+
+        if (!context.managedObjectExists(path)) {
+          Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name,
+              getName(), thisUFN, thisDN, thatUFN, thatDN);
+          unacceptableReasons.add(msg);
+          isUsable = false;
+        } else if (needsEnabling) {
+          // Check that the referenced component is enabled if
+          // required.
+          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+          if (!targetIsEnabledCondition.evaluate(ref)) {
+            Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name,
+                getName(), thisUFN, thisDN, thatUFN, thatDN);
+            unacceptableReasons.add(msg);
+            isUsable = false;
+          }
+        }
+      }
+
+      return isUsable;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void performPostAdd(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      // First make sure existing listeners associated with this
+      // managed object are removed. This is required in order to
+      // prevent multiple change listener registrations from
+      // occurring, for example if this call-back is invoked multiple
+      // times after the same add event.
+      performPostDelete(managedObject);
+
+      // Add change and delete listeners against all referenced
+      // components.
+      Message thisUFN = managedObject.getManagedObjectDefinition()
+          .getUserFriendlyName();
+      String thisDN = managedObject.getDN().toString();
+      Message thatUFN = getRelationDefinition().getUserFriendlyName();
+
+      // Referenced managed objects will only need a change listener
+      // if they have can be disabled.
+      boolean needsChangeListeners = targetNeedsEnablingCondition
+          .evaluate(managedObject);
+
+      // Delete listeners need to be registered against the parent
+      // entry of the referenced components.
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      ManagedObjectPath<?, ?> parentPath = getParentPath();
+      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
+
+      // Create entries in the listener tables.
+      List<ReferentialIntegrityDeleteListener> dlist =
+        new LinkedList<ReferentialIntegrityDeleteListener>();
+      deleteListeners.put(managedObject.getDN(), dlist);
+
+      List<ReferentialIntegrityChangeListener> clist =
+        new LinkedList<ReferentialIntegrityChangeListener>();
+      changeListeners.put(managedObject.getDN(), clist);
+
+      for (String name : managedObject
+          .getPropertyValues(AggregationPropertyDefinition.this)) {
+        ManagedObjectPath<C, S> path = getChildPath(name);
+        DN dn = path.toDN();
+        String thatDN = dn.toString();
+
+        // Register the delete listener.
+        Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN,
+            getName(), thisUFN, thisDN);
+        ReferentialIntegrityDeleteListener dl =
+          new ReferentialIntegrityDeleteListener(dn, msg);
+        parent.registerDeleteListener(getRelationDefinition(), dl);
+        dlist.add(dl);
+
+        // Register the change listener if required.
+        if (needsChangeListeners) {
+          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+          msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN,
+              getName(), thisUFN, thisDN);
+          ReferentialIntegrityChangeListener cl =
+            new ReferentialIntegrityChangeListener(path, msg);
+          ref.registerChangeListener(cl);
+          clist.add(cl);
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void performPostDelete(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      // Remove any registered delete and change listeners.
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      DN dn = managedObject.getDN();
+
+      // Delete listeners need to be deregistered against the parent
+      // entry of the referenced components.
+      ManagedObjectPath<?, ?> parentPath = getParentPath();
+      ServerManagedObject<?> parent = context.getManagedObject(parentPath);
+      if (deleteListeners.containsKey(dn)) {
+        for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
+          parent.deregisterDeleteListener(getRelationDefinition(), dl);
+        }
+        deleteListeners.remove(dn);
+      }
+
+      // Change listeners need to be deregistered from their
+      // associated referenced component.
+      if (changeListeners.containsKey(dn)) {
+        for (ReferentialIntegrityChangeListener cl : changeListeners.get(dn)) {
+          ManagedObjectPath<C, S> path = cl.getManagedObjectPath();
+          ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+          ref.deregisterChangeListener(cl);
+        }
+        changeListeners.remove(dn);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void performPostModify(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      // Remove all the constraints associated with this managed
+      // object and then re-register them.
+      performPostDelete(managedObject);
+      performPostAdd(managedObject);
+    }
+  }
+
+
+
+  /**
+   * The client-side constraint handler implementation which enforces
+   * referential integrity when aggregating managed objects are added
+   * or modified.
+   */
+  private class SourceClientHandler extends ClientConstraintHandler {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAddAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      // If all of this managed object's "enabled" properties are true
+      // then any referenced managed objects must also be enabled.
+      boolean needsEnabling = targetNeedsEnablingCondition.evaluate(context,
+          managedObject);
+
+      // Check the referenced managed objects exist and, if required,
+      // are enabled.
+      boolean isAcceptable = true;
+      Message ufn = getRelationDefinition().getUserFriendlyName();
+      for (String name : managedObject
+          .getPropertyValues(AggregationPropertyDefinition.this)) {
+        // Retrieve the referenced managed object and make sure it
+        // exists.
+        ManagedObjectPath<?, ?> path = getChildPath(name);
+        ManagedObject<?> ref;
+        try {
+          ref = context.getManagedObject(path);
+        } catch (DefinitionDecodingException e) {
+          Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name,
+              getName(), e.getMessageObject());
+          unacceptableReasons.add(msg);
+          isAcceptable = false;
+          continue;
+        } catch (ManagedObjectDecodingException e) {
+          Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name,
+              getName(), e.getMessageObject());
+          unacceptableReasons.add(msg);
+          isAcceptable = false;
+          continue;
+        } catch (ManagedObjectNotFoundException e) {
+          Message msg = ERR_CLIENT_REFINT_TARGET_DANGLING_REFERENCE.get(ufn,
+              name, getName());
+          unacceptableReasons.add(msg);
+          isAcceptable = false;
+          continue;
+        }
+
+        // Make sure the reference managed object is enabled.
+        if (needsEnabling) {
+          if (!targetIsEnabledCondition.evaluate(context, ref)) {
+            Message msg = ERR_CLIENT_REFINT_TARGET_DISABLED.get(ufn, name,
+                getName());
+            unacceptableReasons.add(msg);
+            isAcceptable = false;
+          }
+        }
+      }
+      return isAcceptable;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isModifyAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      // The same constraint applies as for adds.
+      return isAddAcceptable(context, managedObject, unacceptableReasons);
+    }
+
+  }
+
+
+
+  /**
+   * The client-side constraint handler implementation which enforces
+   * referential integrity when aggregated managed objects are deleted
+   * or modified.
+   */
+  private class TargetClientHandler extends ClientConstraintHandler {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isDeleteAcceptable(ManagementContext context,
+        ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      // Any references to the deleted managed object should cause a
+      // constraint violation.
+      boolean isAcceptable = true;
+      for (ManagedObject<?> mo : findReferences(context,
+          getManagedObjectDefinition(), path.getName())) {
+        String name = mo.getManagedObjectPath().getName();
+        if (name == null) {
+          Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITHOUT_NAME.get(
+              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
+              getManagedObjectDefinition().getUserFriendlyName());
+          unacceptableReasons.add(msg);
+        } else {
+          Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITH_NAME.get(
+              getName(), mo.getManagedObjectDefinition().getUserFriendlyName(),
+              name, getManagedObjectDefinition().getUserFriendlyName());
+          unacceptableReasons.add(msg);
+        }
+        isAcceptable = false;
+      }
+      return isAcceptable;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isModifyAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      // If the modified managed object is disabled and there are some
+      // active references then refuse the change.
+      if (targetIsEnabledCondition.evaluate(context, managedObject)) {
+        return true;
+      }
+
+      // The referenced managed object is disabled. Need to check for
+      // active references.
+      boolean isAcceptable = true;
+      for (ManagedObject<?> mo : findReferences(context,
+          getManagedObjectDefinition(), managedObject.getManagedObjectPath()
+              .getName())) {
+        if (targetNeedsEnablingCondition.evaluate(context, mo)) {
+          String name = mo.getManagedObjectPath().getName();
+          if (name == null) {
+            Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITHOUT_NAME.get(
+                managedObject.getManagedObjectDefinition()
+                    .getUserFriendlyName(), getName(), mo
+                    .getManagedObjectDefinition().getUserFriendlyName());
+            unacceptableReasons.add(msg);
+          } else {
+            Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITH_NAME.get(
+                managedObject.getManagedObjectDefinition()
+                    .getUserFriendlyName(), getName(), mo
+                    .getManagedObjectDefinition().getUserFriendlyName(), name);
+            unacceptableReasons.add(msg);
+          }
+          isAcceptable = false;
+        }
+      }
+      return isAcceptable;
+    }
+
+
+
+    // Find all managed objects which reference the named managed
+    // object using this property.
+    private <CC extends ConfigurationClient>
+        List<ManagedObject<? extends CC>> findReferences(
+        ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod,
+        String name) throws AuthorizationException, CommunicationException {
+      List<ManagedObject<? extends CC>> instances = findInstances(context, mod);
+
+      Iterator<ManagedObject<? extends CC>> i = instances.iterator();
+      while (i.hasNext()) {
+        ManagedObject<? extends CC> mo = i.next();
+        boolean hasReference = false;
+
+        for (String value : mo
+            .getPropertyValues(AggregationPropertyDefinition.this)) {
+          if (compare(value, name) == 0) {
+            hasReference = true;
+            break;
+          }
+        }
+
+        if (!hasReference) {
+          i.remove();
+        }
+      }
+
+      return instances;
+    }
+
+
+
+    // Find all instances of a specific type of managed object.
+    @SuppressWarnings("unchecked")
+    private <CC extends ConfigurationClient>
+        List<ManagedObject<? extends CC>> findInstances(
+        ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod)
+        throws AuthorizationException, CommunicationException {
+      List<ManagedObject<? extends CC>> instances =
+        new LinkedList<ManagedObject<? extends CC>>();
+
+      if (mod == RootCfgDefn.getInstance()) {
+        instances.add((ManagedObject<? extends CC>) context
+            .getRootConfigurationManagedObject());
+      } else {
+        for (RelationDefinition<? super CC, ?> rd : mod
+            .getAllReverseRelationDefinitions()) {
+          for (ManagedObject<?> parent : findInstances(context, rd
+              .getParentDefinition())) {
+            try {
+              if (rd instanceof SingletonRelationDefinition) {
+                SingletonRelationDefinition<? super CC, ?> srd =
+                  (SingletonRelationDefinition<? super CC, ?>) rd;
+                ManagedObject<?> mo = parent.getChild(srd);
+                if (mo.getManagedObjectDefinition().isChildOf(mod)) {
+                  instances.add((ManagedObject<? extends CC>) mo);
+                }
+              } else if (rd instanceof OptionalRelationDefinition) {
+                OptionalRelationDefinition<? super CC, ?> ord =
+                  (OptionalRelationDefinition<? super CC, ?>) rd;
+                ManagedObject<?> mo = parent.getChild(ord);
+                if (mo.getManagedObjectDefinition().isChildOf(mod)) {
+                  instances.add((ManagedObject<? extends CC>) mo);
+                }
+              } else if (rd instanceof InstantiableRelationDefinition) {
+                InstantiableRelationDefinition<? super CC, ?> ird =
+                  (InstantiableRelationDefinition<? super CC, ?>) rd;
+
+                for (String name : parent.listChildren(ird)) {
+                  ManagedObject<?> mo = parent.getChild(ird, name);
+                  if (mo.getManagedObjectDefinition().isChildOf(mod)) {
+                    instances.add((ManagedObject<? extends CC>) mo);
+                  }
+                }
+              }
+            } catch (OperationsException e) {
+              // Ignore all operations exceptions.
+            }
+          }
+        }
+      }
+
+      return instances;
+    }
+  }
+
+
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
+  /**
+   * Creates an aggregation property definition builder.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new aggregation property definition builder.
+   */
+  public static <C extends ConfigurationClient, S extends Configuration>
+      Builder<C, S> createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder<C, S>(d, propertyName);
+  }
+
+  // The active server-side referential integrity change listeners
+  // associated with this property.
+  private final Map<DN, List<ReferentialIntegrityChangeListener>>
+    changeListeners = new HashMap<DN,
+      List<ReferentialIntegrityChangeListener>>();
+
+  // The active server-side referential integrity delete listeners
+  // associated with this property.
+  private final Map<DN, List<ReferentialIntegrityDeleteListener>>
+    deleteListeners = new HashMap<DN,
+      List<ReferentialIntegrityDeleteListener>>();
+
+  // The name of the managed object which is the parent of the
+  // aggregated managed objects.
+  private ManagedObjectPath<?, ?> parentPath;
+
+  // The string representation of the managed object path specifying
+  // the parent of the aggregated managed objects.
+  private final String parentPathString;
+
+  // The name of a relation in the parent managed object which
+  // contains the aggregated managed objects.
+  private final String rdName;
+
+  // The relation in the parent managed object which contains the
+  // aggregated managed objects.
+  private InstantiableRelationDefinition<C, S> relationDefinition;
+
+  // The source constraint.
+  private final Constraint sourceConstraint;
+
+  // The condition which is used to determine if a referenced managed
+  // object is enabled.
+  private final Condition targetIsEnabledCondition;
+
+  // The condition which is used to determine whether or not
+  // referenced managed objects need to be enabled.
+  private final Condition targetNeedsEnablingCondition;
+
+
+
+  // Private constructor.
+  private AggregationPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options, AdministratorAction adminAction,
+      DefaultBehaviorProvider<String> defaultBehavior, String parentPathString,
+      String rdName, Condition targetNeedsEnablingCondition,
+      Condition targetIsEnabledCondition) {
+    super(d, String.class, propertyName, options, adminAction, defaultBehavior);
+
+    this.parentPathString = parentPathString;
+    this.rdName = rdName;
+    this.targetNeedsEnablingCondition = targetNeedsEnablingCondition;
+    this.targetIsEnabledCondition = targetIsEnabledCondition;
+    this.sourceConstraint = new Constraint() {
+
+      /**
+       * {@inheritDoc}
+       */
+      public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
+        ClientConstraintHandler handler = new SourceClientHandler();
+        return Collections.singleton(handler);
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+        ServerConstraintHandler handler = new ServerHandler();
+        return Collections.singleton(handler);
+      }
+    };
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitAggregation(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
+    return v.visitAggregation(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      validateValue(value);
+      return value;
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+  }
+
+
+
+  /**
+   * Constructs a DN for a referenced managed object having the
+   * provided name. This method is implemented by first calling
+   * {@link #getChildPath(String)} and then invoking
+   * {@code ManagedObjectPath.toDN()} on the returned path.
+   *
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns a DN for a referenced managed object having the
+   *         provided name.
+   */
+  public final DN getChildDN(String name) {
+    return getChildPath(name).toDN();
+  }
+
+
+
+  /**
+   * Constructs a managed object path for a referenced managed object
+   * having the provided name.
+   *
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns a managed object path for a referenced managed
+   *         object having the provided name.
+   */
+  public final ManagedObjectPath<C, S> getChildPath(String name) {
+    return parentPath.child(relationDefinition, name);
+  }
+
+
+
+  /**
+   * Gets the name of the managed object which is the parent of the
+   * aggregated managed objects.
+   *
+   * @return Returns the name of the managed object which is the
+   *         parent of the aggregated managed objects.
+   */
+  public final ManagedObjectPath<?, ?> getParentPath() {
+    return parentPath;
+  }
+
+
+
+  /**
+   * Gets the relation in the parent managed object which contains the
+   * aggregated managed objects.
+   *
+   * @return Returns the relation in the parent managed object which
+   *         contains the aggregated managed objects.
+   */
+  public final InstantiableRelationDefinition<C, S> getRelationDefinition() {
+    return relationDefinition;
+  }
+
+
+
+  /**
+   * Gets the constraint which should be enforced on the aggregating
+   * managed object.
+   *
+   * @return Returns the constraint which should be enforced on the
+   *         aggregating managed object.
+   */
+  public final Constraint getSourceConstraint() {
+    return sourceConstraint;
+  }
+
+
+
+  /**
+   * Gets the optional constraint synopsis of this aggregation
+   * property definition in the default locale. The constraint
+   * synopsis describes when and how referenced managed objects must
+   * be enabled. When there are no constraints between the source
+   * managed object and the objects it references through this
+   * aggregation, <code>null</code> is returned.
+   *
+   * @return Returns the optional constraint synopsis of this
+   *         aggregation property definition in the default locale, or
+   *         <code>null</code> if there is no constraint synopsis.
+   */
+  public final Message getSourceConstraintSynopsis() {
+    return getSourceConstraintSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional constraint synopsis of this aggregation
+   * property definition in the specified locale.The constraint
+   * synopsis describes when and how referenced managed objects must
+   * be enabled. When there are no constraints between the source
+   * managed object and the objects it references through this
+   * aggregation, <code>null</code> is returned.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the optional constraint synopsis of this
+   *         aggregation property definition in the specified locale,
+   *         or <code>null</code> if there is no constraint
+   *         synopsis.
+   */
+  public final Message getSourceConstraintSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + getName()
+        + ".syntax.aggregation.constraint-synopsis";
+    try {
+      return resource
+          .getMessage(getManagedObjectDefinition(), property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Gets the condition which is used to determine if a referenced
+   * managed object is enabled.
+   *
+   * @return Returns the condition which is used to determine if a
+   *         referenced managed object is enabled.
+   */
+  public final Condition getTargetIsEnabledCondition() {
+    return targetIsEnabledCondition;
+  }
+
+
+
+  /**
+   * Gets the condition which is used to determine whether or not
+   * referenced managed objects need to be enabled.
+   *
+   * @return Returns the condition which is used to determine whether
+   *         or not referenced managed objects need to be enabled.
+   */
+  public final Condition getTargetNeedsEnablingCondition() {
+    return targetNeedsEnablingCondition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String normalizeValue(String value)
+      throws IllegalPropertyValueException {
+    try {
+      Reference<C, S> reference = Reference.parseName(parentPath,
+          relationDefinition, value);
+      return reference.getNormalizedName();
+    } catch (IllegalArgumentException e) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    super.toString(builder);
+
+    builder.append(" parentPath=");
+    builder.append(parentPath);
+
+    builder.append(" relationDefinition=");
+    builder.append(relationDefinition.getName());
+
+    builder.append(" targetNeedsEnablingCondition=");
+    builder.append(String.valueOf(targetNeedsEnablingCondition));
+
+    builder.append(" targetIsEnabledCondition=");
+    builder.append(String.valueOf(targetIsEnabledCondition));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(String value) throws IllegalPropertyValueException {
+    try {
+      Reference.parseName(parentPath, relationDefinition, value);
+    } catch (IllegalArgumentException e) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @SuppressWarnings("unchecked")
+  @Override
+  public void initialize() throws Exception {
+    // Decode the path.
+    parentPath = ManagedObjectPath.valueOf(parentPathString);
+
+    // Decode the relation definition.
+    AbstractManagedObjectDefinition<?, ?> parent = parentPath
+        .getManagedObjectDefinition();
+    RelationDefinition<?, ?> rd = parent.getRelationDefinition(rdName);
+    relationDefinition = (InstantiableRelationDefinition<C, S>) rd;
+
+    // Now decode the conditions.
+    targetNeedsEnablingCondition.initialize(getManagedObjectDefinition());
+    targetIsEnabledCondition.initialize(rd.getChildDefinition());
+
+    // Register a client-side constraint with the referenced
+    // definition. This will be used to enforce referential integrity
+    // for actions performed against referenced managed objects.
+    Constraint constraint = new Constraint() {
+
+      /**
+       * {@inheritDoc}
+       */
+      public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
+        ClientConstraintHandler handler = new TargetClientHandler();
+        return Collections.singleton(handler);
+      }
+
+
+
+      /**
+       * {@inheritDoc}
+       */
+      public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+        return Collections.emptyList();
+      }
+    };
+
+    rd.getChildDefinition().registerConstraint(constraint);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AliasDefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/AliasDefaultBehaviorProvider.java
new file mode 100644
index 0000000..fc9d3ea
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AliasDefaultBehaviorProvider.java
@@ -0,0 +1,115 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+import java.util.Locale;
+
+
+
+/**
+ * A default behavior provider which indicates special behavior. It should be
+ * used by properties which have a default behavior which cannot be directly
+ * represented using real values of the property. For example, a property
+ * containing a set of user names might default to "all users" when no values
+ * are provided. This meaning cannot be represented using a finite set of
+ * values.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public final class AliasDefaultBehaviorProvider<T> extends
+    DefaultBehaviorProvider<T> {
+
+  // The managed object definition associated with this default
+  // behavior.
+  private final AbstractManagedObjectDefinition<?, ?> definition;
+
+  // The name of the property definition associated with this default
+  // behavior.
+  private final String propertyName;
+
+
+
+  /**
+   * Create an alias default behavior provider.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          default behavior.
+   * @param propertyName
+   *          The name of the property definition associated with this
+   *          default behavior.
+   */
+  public AliasDefaultBehaviorProvider(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    this.definition = d;
+    this.propertyName = propertyName;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <R, P> R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p) {
+    return v.visitAlias(this, p);
+  }
+
+
+
+  /**
+   * Gets the synopsis of this alias default behavior in the
+   * default locale.
+   *
+   * @return Returns the synopsis of this alias default behavior in
+   *         the default locale.
+   */
+  public final Message getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this alias default behavior in the specified
+   * locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this alias default behavior in
+   *         the specified locale.
+   */
+  public final Message getSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + propertyName
+        + ".default-behavior.alias.synopsis";
+    return resource.getMessage(definition, property, locale);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/AttributeTypePropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/AttributeTypePropertyDefinition.java
new file mode 100644
index 0000000..3defe92
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/AttributeTypePropertyDefinition.java
@@ -0,0 +1,215 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.util.EnumSet;
+
+import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.opends.server.core.DirectoryServer;
+
+/**
+ * Attribute type property definition.
+ */
+public final class AttributeTypePropertyDefinition extends
+    PropertyDefinition<AttributeType> {
+
+  /**
+   * An interface for incrementally constructing attribute type
+   * property definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<AttributeType, AttributeTypePropertyDefinition> {
+
+    // Private constructor
+    private Builder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected AttributeTypePropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<AttributeType> defaultBehavior) {
+      return new AttributeTypePropertyDefinition(d, propertyName,
+          options, adminAction, defaultBehavior);
+    }
+  }
+
+  // Flag indicating whether or not attribute type names should be
+  // validated against the schema.
+  private static boolean isCheckSchema = true;
+
+
+
+  /**
+   * Create a attribute type property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new attribute type property definition
+   *         builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  /**
+   * Determines whether or not attribute type names should be
+   * validated against the schema.
+   *
+   * @return Returns <code>true</code> if attribute type names
+   *         should be validated against the schema.
+   */
+  public static boolean isCheckSchema() {
+    return isCheckSchema;
+  }
+
+
+
+  /**
+   * Specify whether or not attribute type names should be validated
+   * against the schema.
+   * <p>
+   * By default validation is switched on.
+   *
+   * @param value
+   *          <code>true</code> if attribute type names should be
+   *          validated against the schema.
+   */
+  public static void setCheckSchema(boolean value) {
+    isCheckSchema = value;
+  }
+
+
+
+  // Private constructor.
+  private AttributeTypePropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<AttributeType> defaultBehavior) {
+    super(d, AttributeType.class, propertyName, options,
+        adminAction, defaultBehavior);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitAttributeType(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v,
+      AttributeType value, P p) {
+    return v.visitAttributeType(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(AttributeType o1, AttributeType o2) {
+    return o1.getNameOrOID().compareToIgnoreCase(o2.getNameOrOID());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public AttributeType decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    String name = value.trim().toLowerCase();
+    AttributeType type = DirectoryServer.getAttributeType(name,
+        !isCheckSchema);
+
+    if (type == null) {
+      throw new IllegalPropertyValueStringException(this, value);
+    } else {
+      try {
+        validateValue(type);
+        return type;
+      } catch (IllegalPropertyValueException e) {
+        throw new IllegalPropertyValueStringException(this, value);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(AttributeType value)
+      throws IllegalPropertyValueException {
+    return value.getNameOrOID();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(AttributeType value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No implementation required.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/BooleanPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/BooleanPropertyDefinition.java
new file mode 100644
index 0000000..7a4a6f2
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/BooleanPropertyDefinition.java
@@ -0,0 +1,182 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+
+
+/**
+ * Boolean property definition.
+ */
+public final class BooleanPropertyDefinition extends
+    PropertyDefinition<Boolean> {
+
+  /**
+   * Mapping used for parsing boolean values. This mapping is more flexible than
+   * the standard boolean string parser and supports common true/false synonyms
+   * used in configuration.
+   */
+  private static final Map<String, Boolean> VALUE_MAP;
+  static {
+    VALUE_MAP = new HashMap<String, Boolean>();
+
+    // We could have more possibilities but decided against in issue 1960.
+    VALUE_MAP.put("false", Boolean.FALSE);
+    VALUE_MAP.put("true", Boolean.TRUE);
+  }
+
+
+
+  /**
+   * An interface for incrementally constructing boolean property definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<Boolean, BooleanPropertyDefinition> {
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected BooleanPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<Boolean> defaultBehavior) {
+      return new BooleanPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior);
+    }
+
+  }
+
+
+
+  /**
+   * Create a boolean property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new boolean property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private BooleanPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<Boolean> defaultBehavior) {
+    super(d, Boolean.class, propertyName, options, adminAction,
+        defaultBehavior);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(Boolean value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No additional validation required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Boolean decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    String nvalue = value.trim().toLowerCase();
+    Boolean b = VALUE_MAP.get(nvalue);
+
+    if (b == null) {
+      throw new IllegalPropertyValueStringException(this, value);
+    } else {
+      return b;
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitBoolean(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, Boolean value, P p) {
+    return v.visitBoolean(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(Boolean o1, Boolean o2) {
+    return o1.compareTo(o2);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ClassLoaderProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/ClassLoaderProvider.java
new file mode 100644
index 0000000..3e7f27d
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ClassLoaderProvider.java
@@ -0,0 +1,830 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2012 ForgeRock AS.
+ */
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.ServerConstants.EOL;
+
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.InitializationException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * Manages the class loader which should be used for loading
+ * configuration definition classes and associated extensions.
+ * <p>
+ * For extensions which define their own extended configuration
+ * definitions, the class loader will make sure that the configuration
+ * definition classes are loaded and initialized.
+ * <p>
+ * Initially the class loader provider is disabled, and calls to the
+ * {@link #getClassLoader()} will return the system default class
+ * loader.
+ * <p>
+ * Applications <b>MUST NOT</b> maintain persistent references to the
+ * class loader as it can change at run-time.
+ */
+public final class ClassLoaderProvider {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  /**
+   * Private URLClassLoader implementation. This is only required so
+   * that we can provide access to the addURL method.
+   */
+  private static final class MyURLClassLoader extends URLClassLoader {
+
+    /**
+     * Create a class loader with the default parent class loader.
+     */
+    public MyURLClassLoader() {
+      super(new URL[0]);
+    }
+
+
+
+    /**
+     * Create a class loader with the provided parent class loader.
+     *
+     * @param parent
+     *          The parent class loader.
+     */
+    public MyURLClassLoader(ClassLoader parent) {
+      super(new URL[0], parent);
+    }
+
+
+
+    /**
+     * Add a Jar file to this class loader.
+     *
+     * @param jarFile
+     *          The name of the Jar file.
+     * @throws MalformedURLException
+     *           If a protocol handler for the URL could not be found,
+     *           or if some other error occurred while constructing
+     *           the URL.
+     * @throws SecurityException
+     *           If a required system property value cannot be
+     *           accessed.
+     */
+    public void addJarFile(File jarFile) throws SecurityException,
+        MalformedURLException {
+      addURL(jarFile.toURI().toURL());
+    }
+
+  }
+
+  // The name of the manifest file listing the core configuration
+  // definition classes.
+  private static final String CORE_MANIFEST = "core.manifest";
+
+  // The name of the manifest file listing a extension's configuration
+  // definition classes.
+  private static final String EXTENSION_MANIFEST = "extension.manifest";
+
+  // The name of the lib directory.
+  private static final String LIB_DIR = "lib";
+
+  // The name of the extensions directory.
+  private static final String EXTENSIONS_DIR = "extensions";
+
+  // The singleton instance.
+  private static final ClassLoaderProvider INSTANCE = new ClassLoaderProvider();
+
+  // Attribute name in jar's MANIFEST corresponding to the revision number.
+  private static final String REVISION_NUMBER = "Revision-Number";
+
+  // The attribute names for build information is name, version and revision
+  // number
+  private static final String[] BUILD_INFORMATION_ATTRIBUTE_NAMES =
+                 new String[]{Attributes.Name.EXTENSION_NAME.toString(),
+                              Attributes.Name.IMPLEMENTATION_VERSION.toString(),
+                              REVISION_NUMBER};
+
+
+  /**
+   * Get the single application wide class loader provider instance.
+   *
+   * @return Returns the single application wide class loader provider
+   *         instance.
+   */
+  public static ClassLoaderProvider getInstance() {
+    return INSTANCE;
+  }
+
+  // Set of registered Jar files.
+  private Set<File> jarFiles = new HashSet<File>();
+
+  // Underlying class loader used to load classes and resources (null
+  // if disabled).
+  //
+  // We contain a reference to the URLClassLoader rather than
+  // sub-class it so that it is possible to replace the loader at
+  // run-time. For example, when removing or replacing extension Jar
+  // files (the URLClassLoader only supports adding new
+  // URLs, not removal).
+  private MyURLClassLoader loader = null;
+
+
+
+  // Private constructor.
+  private ClassLoaderProvider() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Add the named extensions to this class loader provider.
+   *
+   * @param extensions
+   *          The names of the extensions to be loaded. The names
+   *          should not contain any path elements and must be located
+   *          within the extensions folder.
+   * @throws InitializationException
+   *           If one of the extensions could not be loaded and
+   *           initialized.
+   * @throws IllegalStateException
+   *           If this class loader provider is disabled.
+   * @throws IllegalArgumentException
+   *           If one of the extension names was not a single relative
+   *           path name element or was an absolute path.
+   */
+  public synchronized void addExtension(String... extensions)
+      throws InitializationException, IllegalStateException,
+      IllegalArgumentException {
+    Validator.ensureNotNull(extensions);
+
+    if (loader == null) {
+      throw new IllegalStateException(
+          "Class loader provider is disabled.");
+    }
+
+    File libPath = new File(DirectoryServer.getInstanceRoot(), LIB_DIR);
+    File extensionsPath = new File(libPath, EXTENSIONS_DIR);
+
+    ArrayList<File> files = new ArrayList<File>(extensions.length);
+    for (String extension : extensions) {
+      File file = new File(extensionsPath, extension);
+
+      // For security reasons we need to make sure that the file name
+      // passed in did not contain any path elements and names a file
+      // in the extensions folder.
+
+      // Can handle potential null parent.
+      if (!extensionsPath.equals(file.getParentFile())) {
+        throw new IllegalArgumentException("Illegal file name: "
+            + extension);
+      }
+
+      // The file is valid.
+      files.add(file);
+    }
+
+    // Add the extensions.
+    addExtension(files.toArray(new File[files.size()]));
+  }
+
+
+
+  /**
+   * Disable this class loader provider and removed any registered
+   * extensions.
+   *
+   * @throws IllegalStateException
+   *           If this class loader provider is already disabled.
+   */
+  public synchronized void disable() throws IllegalStateException {
+    if (loader == null) {
+      throw new IllegalStateException(
+          "Class loader provider already disabled.");
+    }
+    loader = null;
+    jarFiles = new HashSet<File>();
+  }
+
+
+
+  /**
+   * Enable this class loader provider using the application's
+   * class loader as the parent class loader.
+   *
+   * @throws InitializationException
+   *           If the class loader provider could not initialize
+   *           successfully.
+   * @throws IllegalStateException
+   *           If this class loader provider is already enabled.
+   */
+  public synchronized void enable() throws InitializationException,
+      IllegalStateException {
+    enable(RootCfgDefn.class.getClassLoader());
+  }
+
+
+
+  /**
+   * Enable this class loader provider using the provided parent class
+   * loader.
+   *
+   * @param parent
+   *          The parent class loader.
+   * @throws InitializationException
+   *           If the class loader provider could not initialize
+   *           successfully.
+   * @throws IllegalStateException
+   *           If this class loader provider is already enabled.
+   */
+  public synchronized void enable(ClassLoader parent)
+      throws InitializationException, IllegalStateException {
+    if (loader != null) {
+      throw new IllegalStateException(
+          "Class loader provider already enabled.");
+    }
+
+    if (parent != null) {
+      loader = new MyURLClassLoader(parent);
+    } else {
+      loader = new MyURLClassLoader();
+    }
+
+    // Forcefully load all configuration definition classes in
+    // OpenDS.jar.
+    initializeCoreComponents();
+
+    // Put extensions jars into the class loader and load all
+    // configuration definition classes in that they contain.
+    // First load the extension from the install directory, then
+    // from the instance directory.
+    File libDir ;
+    File installExtensionsPath ;
+    File instanceExtensionsPath ;
+
+
+    // load install dir extension
+    libDir = new File(DirectoryServer.getServerRoot(), LIB_DIR);
+    try
+    {
+      installExtensionsPath =
+        new File(libDir, EXTENSIONS_DIR).getCanonicalFile();
+    }
+    catch (Exception e)
+    {
+      installExtensionsPath = new File(libDir, EXTENSIONS_DIR);
+    }
+    initializeAllExtensions(installExtensionsPath);
+
+    // load instance dir extension
+    libDir = new File(DirectoryServer.getInstanceRoot(),LIB_DIR);
+    try
+    {
+      instanceExtensionsPath =
+        new File(libDir, EXTENSIONS_DIR).getCanonicalFile();
+    }
+    catch (Exception e)
+    {
+      instanceExtensionsPath = new File(libDir, EXTENSIONS_DIR);
+    }
+    if (! installExtensionsPath.getAbsolutePath().equals(
+        instanceExtensionsPath.getAbsolutePath()))
+    {
+      initializeAllExtensions(instanceExtensionsPath);
+    }
+  }
+
+
+
+  /**
+   * Gets the class loader which should be used for loading classes
+   * and resources. When this class loader provider is disabled, the
+   * system default class loader will be returned by default.
+   * <p>
+   * Applications <b>MUST NOT</b> maintain persistent references to
+   * the class loader as it can change at run-time.
+   *
+   * @return Returns the class loader which should be used for loading
+   *         classes and resources.
+   */
+  public synchronized ClassLoader getClassLoader() {
+    if (loader != null) {
+      return loader;
+    } else {
+      return ClassLoader.getSystemClassLoader();
+    }
+  }
+
+
+
+  /**
+   * Indicates whether this class loader provider is enabled.
+   *
+   * @return Returns <code>true</code> if this class loader provider
+   *         is enabled.
+   */
+  public synchronized boolean isEnabled() {
+    return loader != null;
+  }
+
+
+
+  /**
+   * Add the named extensions to this class loader.
+   *
+   * @param extensions
+   *          The names of the extensions to be loaded.
+   * @throws InitializationException
+   *           If one of the extensions could not be loaded and
+   *           initialized.
+   */
+  private synchronized void addExtension(File... extensions)
+      throws InitializationException {
+    // First add the Jar files to the class loader.
+    List<JarFile> jars = new LinkedList<JarFile>();
+    for (File extension : extensions) {
+      if (jarFiles.contains(extension)) {
+        // Skip this file as it is already loaded.
+        continue;
+      }
+
+      // Attempt to load it.
+      jars.add(loadJarFile(extension));
+
+      // Register the Jar file with the class loader.
+      try {
+        loader.addJarFile(extension);
+      } catch (Exception e) {
+        if (debugEnabled()) {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE.
+            get(extension.getName(), extension.getParent(),
+                stackTraceToSingleLineString(e));
+        throw new InitializationException(message);
+      }
+      jarFiles.add(extension);
+    }
+
+    // Now forcefully load the configuration definition classes.
+    for (JarFile jar : jars) {
+      initializeExtension(jar);
+    }
+  }
+
+
+
+  /**
+   * Prints out all information about extensions.
+   *
+   * @return a String instance representing all information about extensions;
+   *         <code>null</code> if there is no information available.
+   */
+  public String printExtensionInformation() {
+    File extensionsPath =
+            new File(new StringBuilder(DirectoryServer.getServerRoot()).
+                                append(File.separator).
+                                append(LIB_DIR).
+                                append(File.separator).
+                                append(EXTENSIONS_DIR).
+                                toString());
+
+    if (!extensionsPath.exists() || !extensionsPath.isDirectory()) {
+      // no extensions' directory
+      return null;
+    }
+
+    File[] extensions = extensionsPath.listFiles(new FileFilter(){
+      public boolean accept(File pathname) {
+        // only files with names ending with ".jar"
+        return pathname.isFile() && pathname.getName().endsWith(".jar");
+      }
+    });
+
+    if ( extensions.length == 0 ) {
+      return null;
+    }
+
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PrintStream ps = new PrintStream(baos);
+    // prints:
+    // --
+    //            Name                 Build number         Revision number
+    ps.printf("--%s           %-20s %-20s %-20s%s",
+              EOL,
+              "Name",
+              "Build number",
+              "Revision number",
+              EOL);
+
+    for(File extension : extensions) {
+      // retrieve MANIFEST entry and display name, build number and revision
+      // number
+      try {
+        JarFile jarFile = new JarFile(extension);
+        JarEntry entry = jarFile.getJarEntry("admin/" + EXTENSION_MANIFEST);
+        if (entry == null)
+        {
+          continue;
+        }
+
+        String[] information = getBuildInformation(jarFile);
+
+        ps.append("Extension: ");
+        boolean addBlank = false;
+        for(String name : information) {
+          if ( addBlank ) {
+            ps.append(addBlank ? " " : ""); // add blank if not first append
+          } else {
+            addBlank = true;
+          }
+
+          ps.printf("%-20s", name);
+        }
+        ps.append(EOL);
+      } catch(Exception e) {
+        // ignore extra information for this extension
+      }
+    }
+
+    return baos.toString();
+  }
+
+
+
+  /**
+   * Returns a String array with the following information :
+   * <br>index 0: the name of the extension.
+   * <br>index 1: the build number of the extension.
+   * <br>index 2: the revision number of the extension.
+   *
+   * @param extension the jar file of the extension
+   * @return a String array containing the name, the build number and the
+   *         revision number of the extension given in argument
+   * @throws java.io.IOException thrown if the jar file has been closed.
+   */
+  private String[] getBuildInformation(JarFile extension) throws IOException {
+    String[] result = new String[3];
+
+    // retrieve MANIFEST entry and display name, version and revision
+    Manifest manifest = extension.getManifest();
+
+    if ( manifest != null ) {
+      Attributes attributes = manifest.getMainAttributes();
+
+      int index = 0;
+      for(String name : BUILD_INFORMATION_ATTRIBUTE_NAMES) {
+        String value = attributes.getValue(name);
+        if ( value == null ) {
+          value = "<unknown>";
+        }
+        result[index++] = value;
+      }
+    }
+
+    return result;
+  }
+
+
+
+  /**
+   * Put extensions jars into the class loader and load all
+   * configuration definition classes in that they contain.
+   * @param extensionsPath Indicates where extensions are located.
+   *
+   * @throws InitializationException
+   *           If the extensions folder could not be accessed or if a
+   *           extension jar file could not be accessed or if one of
+   *           the configuration definition classes could not be
+   *           initialized.
+   */
+  private void initializeAllExtensions(File extensionsPath)
+      throws InitializationException {
+
+    try {
+      if (!extensionsPath.exists()) {
+        // The extensions directory does not exist. This is not a
+        // critical problem.
+        Message message = ERR_ADMIN_NO_EXTENSIONS_DIR.get(
+                String.valueOf(extensionsPath));
+        logError(message);
+        return;
+      }
+
+      if (!extensionsPath.isDirectory()) {
+        // The extensions directory is not a directory. This is more
+        // critical.
+        Message message =
+            ERR_ADMIN_EXTENSIONS_DIR_NOT_DIRECTORY.get(
+                    String.valueOf(extensionsPath));
+        throw new InitializationException(message);
+      }
+
+      // Get each extension file name.
+      FileFilter filter = new FileFilter() {
+
+        /**
+         * Must be a Jar file.
+         */
+        public boolean accept(File pathname) {
+          if (!pathname.isFile()) {
+            return false;
+          }
+
+          String name = pathname.getName();
+          return name.endsWith(".jar");
+        }
+
+      };
+
+      // Add and initialize the extensions.
+      addExtension(extensionsPath.listFiles(filter));
+    } catch (InitializationException e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      throw e;
+    } catch (Exception e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_ADMIN_EXTENSIONS_CANNOT_LIST_FILES.get(
+          String.valueOf(extensionsPath), stackTraceToSingleLineString(e));
+      throw new InitializationException(message, e);
+    }
+  }
+
+
+
+  /**
+   * Make sure all core configuration definitions are loaded.
+   *
+   * @throws InitializationException
+   *           If the core manifest file could not be read or if one
+   *           of the configuration definition classes could not be
+   *           initialized.
+   */
+  private void initializeCoreComponents()
+      throws InitializationException {
+    InputStream is = RootCfgDefn.class.getResourceAsStream("/admin/"
+        + CORE_MANIFEST);
+
+    if (is == null) {
+      Message message = ERR_ADMIN_CANNOT_FIND_CORE_MANIFEST.get(CORE_MANIFEST);
+      throw new InitializationException(message);
+    }
+
+    try {
+      loadDefinitionClasses(is);
+    } catch (InitializationException e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_CLASS_LOADER_CANNOT_LOAD_CORE.get(CORE_MANIFEST,
+          stackTraceToSingleLineString(e));
+      throw new InitializationException(message);
+    }
+  }
+
+
+
+  /**
+   * Make sure all the configuration definition classes in a extension
+   * are loaded.
+   *
+   * @param jarFile
+   *          The extension's Jar file.
+   * @throws InitializationException
+   *           If the extension jar file could not be accessed or if
+   *           one of the configuration definition classes could not
+   *           be initialized.
+   */
+  private void initializeExtension(JarFile jarFile)
+      throws InitializationException {
+    JarEntry entry = jarFile.getJarEntry("admin/"
+        + EXTENSION_MANIFEST);
+    if (entry != null) {
+      InputStream is;
+      try {
+        is = jarFile.getInputStream(entry);
+      } catch (Exception e) {
+        if (debugEnabled()) {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message = ERR_ADMIN_CANNOT_READ_EXTENSION_MANIFEST.get(
+            EXTENSION_MANIFEST, jarFile.getName(),
+            stackTraceToSingleLineString(e));
+        throw new InitializationException(message);
+      }
+
+      try {
+        loadDefinitionClasses(is);
+      } catch (InitializationException e) {
+        if (debugEnabled()) {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message = ERR_CLASS_LOADER_CANNOT_LOAD_EXTENSION.get(jarFile
+            .getName(), EXTENSION_MANIFEST, stackTraceToSingleLineString(e));
+        throw new InitializationException(message);
+      }
+      try {
+        // Log build information of extensions in the error log
+        String[] information = getBuildInformation(jarFile);
+        logError(
+          NOTE_LOG_EXTENSION_INFORMATION.
+            get(jarFile.getName(),
+                information[1],
+                information[2]));
+      } catch(Exception e) {
+        // Do not log information for that extension
+      }
+    }
+  }
+
+
+
+  /**
+   * Forcefully load configuration definition classes named in a
+   * manifest file.
+   *
+   * @param is
+   *          The manifest file input stream.
+   * @throws InitializationException
+   *           If the definition classes could not be loaded and
+   *           initialized.
+   */
+  private void loadDefinitionClasses(InputStream is)
+      throws InitializationException {
+    BufferedReader reader = new BufferedReader(new InputStreamReader(
+        is));
+    List<AbstractManagedObjectDefinition<?, ?>> definitions =
+      new LinkedList<AbstractManagedObjectDefinition<?,?>>();
+    while (true) {
+      String className;
+      try {
+        className = reader.readLine();
+      } catch (IOException e) {
+        Message msg = ERR_CLASS_LOADER_CANNOT_READ_MANIFEST_FILE.get(String
+            .valueOf(e.getMessage()));
+        throw new InitializationException(msg, e);
+      }
+
+      // Break out when the end of the manifest is reached.
+      if (className == null) {
+        break;
+      }
+
+      // Skip blank lines.
+      className = className.trim();
+      if (className.length() == 0) {
+        continue;
+      }
+
+      // Skip lines beginning with #.
+      if (className.startsWith("#")) {
+        continue;
+      }
+
+      TRACER.debugMessage(DebugLogLevel.INFO, "Loading class " + className);
+
+      // Load the class and get an instance of it if it is a definition.
+      Class<?> theClass;
+      try {
+        theClass = Class.forName(className, true, loader);
+      } catch (Exception e) {
+        Message msg = ERR_CLASS_LOADER_CANNOT_LOAD_CLASS.get(className, String
+            .valueOf(e.getMessage()));
+        throw new InitializationException(msg, e);
+      }
+      if (AbstractManagedObjectDefinition.class.isAssignableFrom(theClass)) {
+        // We need to instantiate it using its getInstance() static method.
+        Method method;
+        try {
+          method = theClass.getMethod("getInstance");
+        } catch (Exception e) {
+          Message msg = ERR_CLASS_LOADER_CANNOT_FIND_GET_INSTANCE_METHOD.get(
+              className, String.valueOf(e.getMessage()));
+          throw new InitializationException(msg, e);
+        }
+
+        // Get the definition instance.
+        AbstractManagedObjectDefinition<?, ?> d;
+        try {
+          d = (AbstractManagedObjectDefinition<?, ?>) method.invoke(null);
+        } catch (Exception e) {
+          Message msg = ERR_CLASS_LOADER_CANNOT_INVOKE_GET_INSTANCE_METHOD.get(
+              className, String.valueOf(e.getMessage()));
+          throw new InitializationException(msg, e);
+        }
+        definitions.add(d);
+      }
+    }
+
+    // Initialize any definitions that were loaded.
+    for (AbstractManagedObjectDefinition<?, ?> d : definitions) {
+      try {
+        d.initialize();
+      } catch (Exception e) {
+        Message msg = ERR_CLASS_LOADER_CANNOT_INITIALIZE_DEFN.get(d.getName(),
+            d.getClass().getName(), String.valueOf(e.getMessage()));
+        throw new InitializationException(msg, e);
+      }
+    }
+  }
+
+
+
+  /**
+   * Load the named Jar file.
+   *
+   * @param jar
+   *          The name of the Jar file to load.
+   * @return Returns the loaded Jar file.
+   * @throws InitializationException
+   *           If the Jar file could not be loaded.
+   */
+  private JarFile loadJarFile(File jar)
+      throws InitializationException {
+    JarFile jarFile;
+
+    try {
+      // Load the extension jar file.
+      jarFile = new JarFile(jar);
+    } catch (Exception e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_ADMIN_CANNOT_OPEN_JAR_FILE.get(
+          jar.getName(), jar.getParent(), stackTraceToSingleLineString(e));
+      throw new InitializationException(message);
+    }
+    return jarFile;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ClassPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/ClassPropertyDefinition.java
new file mode 100644
index 0000000..ef9e783
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ClassPropertyDefinition.java
@@ -0,0 +1,382 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+
+
+
+/**
+ * Class property definition.
+ * <p>
+ * A class property definition defines a property whose values
+ * represent a Java class. It is possible to restrict the type of java
+ * class by specifying "instance of" constraints.
+ * <p>
+ * Note that in a client/server environment, the client is probably
+ * not capable of validating the Java class (e.g. it will not be able
+ * to load it nor have access to the interfaces it is supposed to
+ * implement). For this reason, it is possible to switch off
+ * validation in the client by calling the static method
+ * {@link #setAllowClassValidation(boolean)}.
+ */
+public final class ClassPropertyDefinition extends PropertyDefinition<String> {
+
+  /**
+   * An interface for incrementally constructing class property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<String, ClassPropertyDefinition> {
+
+    // List of interfaces which property values must implement.
+    private List<String> instanceOfInterfaces;
+
+
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+
+      this.instanceOfInterfaces = new LinkedList<String>();
+    }
+
+
+
+    /**
+     * Add an class name which property values must implement.
+     *
+     * @param className
+     *          The name of a class which property values must
+     *          implement.
+     */
+    public final void addInstanceOf(String className) {
+      ensureNotNull(className);
+
+      // Do some basic checks to make sure the string representation
+      // is valid.
+      String value = className.trim();
+      if (!value.matches(CLASS_RE)) {
+        throw new IllegalArgumentException("\"" + value
+            + "\" is not a valid Java class name");
+      }
+
+      // If possible try and load the class in order to perform
+      // additional
+      // validation.
+      if (isAllowClassValidation()) {
+        // Check that the class can be loaded so that validation can
+        // be
+        // performed.
+        try {
+          loadClass(value);
+        } catch (ClassNotFoundException e) {
+          // TODO: can we do something better here?
+          throw new RuntimeException(e);
+        }
+      }
+
+      instanceOfInterfaces.add(value);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected ClassPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName, EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<String> defaultBehavior) {
+      return new ClassPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior, instanceOfInterfaces);
+    }
+
+  }
+
+  // Regular expression for validating class names.
+  private static final String CLASS_RE =
+    "^([A-Za-z][A-Za-z0-9_]*\\.)*[A-Za-z][A-Za-z0-9_]*(\\$[A-Za-z0-9_]+)*$";
+
+  // Flag indicating whether class property values should be
+  // validated.
+  private static boolean allowClassValidation = true;
+
+
+
+  /**
+   * Create a class property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new class property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  /**
+   * Determine whether or not class property definitions should
+   * validate class name property values. Validation involves checking
+   * that the class exists and that it implements the required
+   * interfaces.
+   *
+   * @return Returns <code>true</code> if class property definitions
+   *         should validate class name property values.
+   */
+  public static boolean isAllowClassValidation() {
+    return allowClassValidation;
+  }
+
+
+
+  /**
+   * Specify whether or not class property definitions should validate
+   * class name property values. Validation involves checking that the
+   * class exists and that it implements the required interfaces.
+   * <p>
+   * By default validation is switched on.
+   *
+   * @param value
+   *          <code>true</code> if class property definitions should
+   *          validate class name property values.
+   */
+  public static void setAllowClassValidation(boolean value) {
+    allowClassValidation = value;
+  }
+
+
+
+  // Load a named class.
+  private static Class<?> loadClass(String className)
+      throws ClassNotFoundException, LinkageError {
+    return Class.forName(className, true, ClassLoaderProvider
+        .getInstance().getClassLoader());
+  }
+
+  // List of interfaces which property values must implement.
+  private final List<String> instanceOfInterfaces;
+
+
+
+  // Private constructor.
+  private ClassPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<String> defaultBehavior,
+      List<String> instanceOfInterfaces) {
+    super(d, String.class, propertyName, options, adminAction, defaultBehavior);
+
+    this.instanceOfInterfaces = Collections
+        .unmodifiableList(new LinkedList<String>(instanceOfInterfaces));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitClass(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
+    return v.visitClass(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      validateValue(value);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    return value;
+  }
+
+
+
+  /**
+   * Get an unmodifiable list of classes which values of this property
+   * must implement.
+   *
+   * @return Returns an unmodifiable list of classes which values of
+   *         this property must implement.
+   */
+  public List<String> getInstanceOfInterface() {
+    return instanceOfInterfaces;
+  }
+
+
+
+  /**
+   * Validate and load the named class, and cast it to a subclass of
+   * the specified class.
+   *
+   * @param <T>
+   *          The requested type.
+   * @param className
+   *          The name of the class to validate and load.
+   * @param instanceOf
+   *          The class representing the requested type.
+   * @return Returns the named class cast to a subclass of the
+   *         specified class.
+   * @throws IllegalPropertyValueException
+   *           If the named class was invalid, could not be loaded, or
+   *           did not implement the required interfaces.
+   * @throws ClassCastException
+   *           If the referenced class does not implement the
+   *           requested type.
+   */
+  public <T> Class<? extends T> loadClass(String className,
+      Class<T> instanceOf) throws IllegalPropertyValueException,
+      ClassCastException {
+    ensureNotNull(className, instanceOf);
+
+    // Make sure that the named class is valid.
+    validateClassName(className);
+    Class<?> theClass = validateClassInterfaces(className);
+
+    // Cast it to the required type.
+    return theClass.asSubclass(instanceOf);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String normalizeValue(String value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    return value.trim();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(String value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // Always make sure the name is a valid class name.
+    validateClassName(value);
+
+    // If additional validation is enabled then attempt to load the
+    // class and
+    // check the interfaces that it implements/extends.
+    if (allowClassValidation) {
+      validateClassInterfaces(value);
+    }
+  }
+
+
+
+  // Make sure that named class implements the interfaces named by
+  // this
+  // definition.
+  private Class<?> validateClassInterfaces(String className)
+      throws IllegalPropertyValueException {
+    String nvalue = className.trim();
+
+    Class<?> theClass;
+    try {
+      theClass = loadClass(nvalue);
+    } catch (Exception e) {
+      // If the class cannot be loaded then it is an invalid value.
+      throw new IllegalPropertyValueException(this, className);
+    }
+
+    for (String i : instanceOfInterfaces) {
+      try {
+        Class<?> instanceOfClass = loadClass(i);
+
+        if (!instanceOfClass.isAssignableFrom(theClass)) {
+          throw new IllegalPropertyValueException(this, className);
+        }
+      } catch (Exception e) {
+        // Should not happen because the class was validated when the
+        // property
+        // definition was constructed.
+        throw new IllegalPropertyValueException(this, className);
+      }
+    }
+
+    return theClass;
+  }
+
+
+
+  // Do some basic checks to make sure the string representation is
+  // valid.
+  private void validateClassName(String className)
+      throws IllegalPropertyValueException {
+    String nvalue = className.trim();
+    if (!nvalue.matches(CLASS_RE)) {
+      throw new IllegalPropertyValueException(this, className);
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/Configuration.java b/opendj-admin/src/main/java/org/opends/server/admin/Configuration.java
new file mode 100644
index 0000000..421b8d1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/Configuration.java
@@ -0,0 +1,55 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+import org.forgerock.opendj.ldap.DN;
+
+/**
+ * A common base interface for all server managed object
+ * configurations.
+ */
+public interface Configuration {
+
+  /**
+   * Gets the DN of the LDAP entry associated with this configuration.
+   *
+   * @return Returns the DN of the LDAP entry associated with this
+   *         configuration.
+   */
+  DN dn();
+
+
+
+  /**
+   * Gets the configuration class associated with this configuration.
+   *
+   * @return Returns the configuration class associated with this
+   *         configuration.
+   */
+  Class<? extends Configuration> configurationClass();
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ConfigurationClient.java b/opendj-admin/src/main/java/org/opends/server/admin/ConfigurationClient.java
new file mode 100644
index 0000000..e08939f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ConfigurationClient.java
@@ -0,0 +1,100 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ConcurrentModificationException;
+import org.opends.server.admin.client.MissingMandatoryPropertiesException;
+import org.opends.server.admin.client.OperationRejectedException;
+
+
+
+/**
+ * A common base interface for all managed object configuration
+ * clients.
+ */
+public interface ConfigurationClient {
+
+  /**
+   * Get the configuration definition associated with this
+   * configuration.
+   *
+   * @return Returns the configuration definition associated with this
+   *         configuration.
+   */
+  ManagedObjectDefinition<? extends ConfigurationClient,
+      ? extends Configuration> definition();
+
+
+
+  /**
+   * Get a property provider view of this configuration.
+   *
+   * @return Returns a property provider view of this configuration.
+   */
+  PropertyProvider properties();
+
+
+
+  /**
+   * If this is a new configuration this method will attempt to add it
+   * to the server, otherwise it will commit any changes made to this
+   * configuration.
+   *
+   * @throws ManagedObjectAlreadyExistsException
+   *           If this is a new configuration but it could not be
+   *           added to the server because it already exists.
+   * @throws MissingMandatoryPropertiesException
+   *           If this configuration contains some mandatory
+   *           properties which have been left undefined.
+   * @throws ConcurrentModificationException
+   *           If this is a new configuration which is being added to
+   *           the server but its parent has been removed by another
+   *           client, or if this configuration is being modified but
+   *           it has been removed from the server by another client.
+   * @throws OperationRejectedException
+   *           If the server refuses to add or modify this
+   *           configuration due to some server-side constraint which
+   *           cannot be satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to add or modify this
+   *           configuration because the client does not have the
+   *           correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  void commit() throws ManagedObjectAlreadyExistsException,
+      MissingMandatoryPropertiesException, ConcurrentModificationException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException;
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/Constraint.java b/opendj-admin/src/main/java/org/opends/server/admin/Constraint.java
new file mode 100644
index 0000000..46ca795
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/Constraint.java
@@ -0,0 +1,122 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.server.ServerConstraintHandler;
+
+
+
+/**
+ * An interface for enforcing constraints and dependencies between
+ * managed objects and their properties. Constraints express
+ * relationships between managed objects and their properties, for
+ * example:
+ * <ul>
+ * <li>referential integrity: where one managed object references
+ * another a constraint can enforce referential integrity. The
+ * constraint can prevent creation of references to non-existent
+ * managed objects, and also prevent deletion of referenced managed
+ * objects
+ * <li>property dependencies: for example, when a boolean property is
+ * <code>true</code>, one or more additional properties must be
+ * specified. This is useful for features like SSL, which when
+ * enabled, requires that various SSL related configuration options
+ * are specified
+ * <li>property constraints: for example, when an upper limit
+ * property must not have a value which is less than the lower limit
+ * property.
+ * </ul>
+ * On the client-side constraints are enforced immediately before a
+ * write operation is performed. That is to say, immediately before a
+ * new managed object is created, changes to a managed object are
+ * applied, or an existing managed object is deleted.
+ */
+public abstract class Constraint {
+
+  /**
+   * Creates a new constraint.
+   */
+  protected Constraint() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Gets the client-side constraint handlers which will be used to
+   * enforce this constraint in client applications. The default
+   * implementation is to return an empty set of client constraint
+   * handlers.
+   *
+   * @return Returns the client-side constraint handlers which will be
+   *         used to enforce this constraint in client applications.
+   *         The returned collection must not be <code>null</code>
+   *         but maybe empty (indicating that the constraint can only
+   *         be enforced on the server-side).
+   */
+  public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
+    return Collections.emptySet();
+  }
+
+
+
+  /**
+   * Gets the server-side constraint handlers which will be used to
+   * enforce this constraint within the server. The default
+   * implementation is to return an empty set of server constraint
+   * handlers.
+   *
+   * @return Returns the server-side constraint handlers which will be
+   *         used to enforce this constraint within the server. The
+   *         returned collection must not be <code>null</code> and
+   *         must not be empty, since constraints must always be
+   *         enforced on the server.
+   */
+  public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+    return Collections.emptySet();
+  }
+
+
+
+  /**
+   * Initializes this constraint. The default implementation is to do
+   * nothing.
+   *
+   * @throws Exception
+   *           If this constraint could not be initialized.
+   */
+  protected void initialize() throws Exception {
+    // Default implementation is to do nothing.
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DNPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/DNPropertyDefinition.java
new file mode 100644
index 0000000..a27b348
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DNPropertyDefinition.java
@@ -0,0 +1,240 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DirectoryException;
+
+
+
+/**
+ * DN property definition.
+ */
+public final class DNPropertyDefinition extends PropertyDefinition<DN> {
+
+  // Optional base DN which all valid values must be immediately
+  // subordinate to.
+  private final DN baseDN;
+
+
+
+  /**
+   * An interface for incrementally constructing DN property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<DN, DNPropertyDefinition> {
+
+    // Optional base DN which all valid values must be immediately
+    // subordinate to.
+    private DN baseDN = null;
+
+
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Set the base DN which all valid values must be immediately
+     * subordinate to. By default there is no based DN.
+     *
+     * @param baseDN
+     *          The string representation of the base DN.
+     * @throws IllegalArgumentException
+     *           If the provided string is not a valid DN string
+     *           representation.
+     */
+    public void setBaseDN(String baseDN)
+        throws IllegalArgumentException {
+      if (baseDN == null) {
+        setBaseDN((DN) null);
+      } else {
+        try {
+          setBaseDN(DN.decode(baseDN));
+        } catch (DirectoryException e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+
+
+    /**
+     * Set the base DN which all valid values must be immediately
+     * subordinate to. By default there is no based DN.
+     *
+     * @param baseDN
+     *          The base DN.
+     */
+    public void setBaseDN(DN baseDN) {
+      this.baseDN = baseDN;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected DNPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<DN> defaultBehavior) {
+      return new DNPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior, baseDN);
+    }
+  }
+
+
+
+  /**
+   * Create a DN property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new boolean property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private DNPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<DN> defaultBehavior, DN baseDN) {
+    super(d, DN.class, propertyName, options, adminAction, defaultBehavior);
+    this.baseDN = baseDN;
+  }
+
+
+
+  /**
+   * Get the base DN which all valid values must be immediately
+   * subordinate to, or <code>null</code> if there is no based DN.
+   *
+   * @return Returns the base DN which all valid values must be
+   *         immediately subordinate to.
+   */
+  public DN getBaseDN() {
+    return baseDN;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(DN value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    if (baseDN != null) {
+      DN parent = value.getParent();
+
+      if (parent == null) {
+        parent = DN.nullDN();
+      }
+
+      if (!parent.equals(baseDN)) {
+        throw new IllegalPropertyValueException(this, value);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public DN decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      DN dn = DN.decode(value);
+      validateValue(dn);
+      return dn;
+    } catch (DirectoryException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitDN(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, DN value, P p) {
+    return v.visitDN(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(DN o1, DN o2) {
+    return o1.compareTo(o2);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DecodingException.java b/opendj-admin/src/main/java/org/opends/server/admin/DecodingException.java
new file mode 100644
index 0000000..1affb2a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DecodingException.java
@@ -0,0 +1,58 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * The requested managed object was found but it could not be decoded.
+ */
+public abstract class DecodingException extends OperationsException {
+
+  /**
+   * Fake serialization ID.
+   */
+  private static final long serialVersionUID = 1L;
+
+
+
+  /**
+   * Create a decoding exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected DecodingException(Message message) {
+    super(message);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorException.java b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorException.java
new file mode 100644
index 0000000..838ed01
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorException.java
@@ -0,0 +1,69 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * This exception is thrown when a property's default values cannot be
+ * determined. This can occur in the following situations:
+ * <ul>
+ * <li>the property has a well-defined set of default values but they
+ * are invalid according to the property's syntax
+ * <li>the property inherits its default values from another managed
+ * object but they could not be retrieved, perhaps because of a
+ * communication problem.
+ * </ul>
+ */
+public class DefaultBehaviorException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -2542117466747573053L;
+
+
+
+  /**
+   * Create a new default behavior exception with a cause.
+   *
+   * @param pd
+   *          The property definition whose default values could not
+   *          be determined.
+   * @param cause
+   *          The exception that prevented the default values from
+   *          being determined.
+   */
+  public DefaultBehaviorException(PropertyDefinition<?> pd, Throwable cause) {
+    super(pd, ERR_DEFAULT_BEHAVIOR_PROPERTY_EXCEPTION.get(pd.getName()), cause);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProvider.java
new file mode 100644
index 0000000..48802c9
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProvider.java
@@ -0,0 +1,104 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * An interface for determining the default behavior of a property. A
+ * property exhibits default behavior when it has no values defined.
+ * There are four different types of default behavior:
+ * <ol>
+ * <li>there is no default behavior - e.g. leaving a "description"
+ * unset has no side-effects. This default behavior is represented
+ * using the {@link UndefinedDefaultBehaviorProvider} implementation
+ * <li>the property defaults to one or more real values of the
+ * property. This default behavior is represented using the
+ * {@link DefinedDefaultBehaviorProvider} implementation
+ * <li>the property defaults to some special behavior that cannot be
+ * represented using real property values. This default behavior is
+ * represented using the {@link AliasDefaultBehaviorProvider}
+ * implementation
+ * <li>the property inherits its values from property held in another
+ * managed object (e.g. the parent managed object). This default
+ * behavior is represented using the
+ * {@link AbsoluteInheritedDefaultBehaviorProvider} and
+ * {@link RelativeInheritedDefaultBehaviorProvider} implementations.
+ * </ol>
+ * An application can perform actions based on the type of the default
+ * behavior by implementing the {@link DefaultBehaviorProviderVisitor}
+ * interface.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public abstract class DefaultBehaviorProvider<T> {
+
+  /**
+   * Creates a new default behavior provider.
+   */
+  protected DefaultBehaviorProvider() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Apply a visitor to this default behavior provider.
+   *
+   * @param <R>
+   *          The return type of the visitor's methods.
+   * @param <P>
+   *          The type of the additional parameters to the visitor's
+   *          methods.
+   * @param v
+   *          The default behavior visitor.
+   * @param p
+   *          Optional additional visitor parameter.
+   * @return Returns a result as specified by the visitor.
+   */
+  public abstract <R, P>
+  R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p);
+
+
+
+  /**
+   * Performs any run-time initialization required by this default
+   * behavior provider. This may include resolving managed object
+   * paths and property names.
+   * <p>
+   * The default implementation is to do nothing.
+   *
+   * @throws Exception
+   *           If this default behavior provider could not be
+   *           initialized.
+   */
+  protected void initialize() throws Exception {
+    // Default implementation is to do nothing.
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProviderVisitor.java b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProviderVisitor.java
new file mode 100644
index 0000000..09dcc25
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefaultBehaviorProviderVisitor.java
@@ -0,0 +1,116 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * A visitor of default behavior providers, in the style of the visitor design
+ * pattern. Classes implementing this interface can query default behavior
+ * providers in a type-safe manner when the kind of default behavior provider
+ * is unknown at compile time. When a visitor is passed to a default behavior
+ * provider's accept method, the corresponding visit method most applicable to
+ * that default behavior provider is invoked.
+ *
+ * @param <T>
+ *          The type of values represented by the default value provider.
+ * @param <R>
+ *          The return type of this visitor's methods. Use
+ *          {@link java.lang.Void} for visitors that do not need to return
+ *          results.
+ * @param <P>
+ *          The type of the additional parameter to this visitor's methods. Use
+ *          {@link java.lang.Void} for visitors that do not need an additional
+ *          parameter.
+ */
+public interface DefaultBehaviorProviderVisitor<T, R, P> {
+
+  /**
+   * Visit an absolute inherited default behavior provider.
+   *
+   * @param d
+   *          The absolute inherited default behavior provider to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  R visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, P p);
+
+
+
+  /**
+   * Visit an alias default behavior provider.
+   *
+   * @param d
+   *          The alias default behavior provider to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  R visitAlias(AliasDefaultBehaviorProvider<T> d, P p);
+
+
+
+  /**
+   * Visit an defined default behavior provider.
+   *
+   * @param d
+   *          The defined default behavior provider to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  R visitDefined(DefinedDefaultBehaviorProvider<T> d, P p);
+
+
+
+  /**
+   * Visit a relative inherited default behavior provider.
+   *
+   * @param d
+   *          The relative inherited default behavior provider to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  R visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, P p);
+
+
+
+  /**
+   * Visit an undefined default behavior provider.
+   *
+   * @param d
+   *          The undefined default behavior provider to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  R visitUndefined(UndefinedDefaultBehaviorProvider<T> d, P p);
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefaultManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/DefaultManagedObject.java
new file mode 100644
index 0000000..bcdd201
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefaultManagedObject.java
@@ -0,0 +1,200 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+
+
+/**
+ * A default managed object which should be created when a parent
+ * managed object is created. Default managed objects are associated
+ * with a {@link RelationDefinition}.
+ *
+ * @param <C>
+ *          The type of client default managed object configuration.
+ * @param <S>
+ *          The type of server default managed object configuration.
+ */
+public final class DefaultManagedObject
+    <C extends ConfigurationClient, S extends Configuration>
+    implements PropertyProvider {
+
+  /**
+   * An interface for incrementally constructing default managed
+   * objects.
+   *
+   * @param <C>
+   *          The type of client default managed object configuration.
+   * @param <S>
+   *          The type of server default managed object configuration.
+   */
+  public static final class Builder
+      <C extends ConfigurationClient, S extends Configuration> {
+
+    // The default managed object's definition.
+    private final ManagedObjectDefinition<C, S> definition;
+
+    // The string encoded default managed object's properties.
+    private final Map<String, List<String>> propertyStringValues =
+      new HashMap<String, List<String>>();
+
+
+
+    /**
+     * Creates a new default managed object builder.
+     *
+     * @param definition
+     *          The default managed object's definition.
+     */
+    public Builder(ManagedObjectDefinition<C, S> definition) {
+      this.definition = definition;
+    }
+
+
+
+    /**
+     * Construct a default managed object based on the properties of
+     * this builder.
+     *
+     * @return Returns the new default managed object.
+     */
+    public DefaultManagedObject<C, S> getInstance() {
+      return new DefaultManagedObject<C, S>(definition, propertyStringValues);
+    }
+
+
+
+    /**
+     * Defines a property's values for the default managed object.
+     *
+     * @param name
+     *          The name of the property.
+     * @param values
+     *          One or more property values in the string
+     *          representation.
+     */
+    public void setPropertyValues(String name, String... values) {
+      if (values == null || values.length == 0) {
+        throw new IllegalArgumentException(
+            "null or empty values specified for property " + name);
+      }
+
+      propertyStringValues.put(name, Arrays.asList(values));
+    }
+  }
+
+  // The default managed object's definition.
+  private final ManagedObjectDefinition<C, S> definition;
+
+  // The string encoded default managed object's properties.
+  private final Map<String, List<String>> propertyStringValues;
+
+
+
+  // Private constructor.
+  private DefaultManagedObject(ManagedObjectDefinition<C, S> definition,
+      Map<String, List<String>> propertyStringValues) {
+    this.definition = definition;
+    this.propertyStringValues = propertyStringValues;
+  }
+
+
+
+  /**
+   * Gets the managed object definition associated with this default
+   * managed object.
+   *
+   * @return Returns the managed object definition associated with
+   *         this default managed object.
+   */
+  public ManagedObjectDefinition<C, S> getManagedObjectDefinition() {
+    return definition;
+  }
+
+
+
+  /**
+   * Gets a mutable copy of the set of property values for the
+   * specified property.
+   *
+   * @param <T>
+   *          The type of the property to be retrieved.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns a newly allocated set containing a copy of the
+   *         property's values. An empty set indicates that the
+   *         property has no values defined and any default behavior
+   *         is applicable.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> pd)
+      throws IllegalArgumentException {
+    // Validate the property definition.
+    definition.getPropertyDefinition(pd.getName());
+
+    // Do a defensive copy.
+    SortedSet<T> values = new TreeSet<T>(pd);
+    List<String> stringValues = propertyStringValues.get(pd.getName());
+    if (stringValues != null) {
+      for (String stringValue : stringValues) {
+        values.add(pd.decodeValue(stringValue));
+      }
+    }
+    return values;
+  }
+
+
+
+  /**
+   * Performs run-time initialization of properties.
+   *
+   * @throws Exception
+   *           If this default managed object could not be
+   *           initialized.
+   */
+  void initialize() throws Exception {
+    // FIXME: it would be nice if we could decode all property values
+    // at this point. However this is not possible on the server side
+    // since some properties will be determined to be invalid since
+    // the schema is not loaded.
+
+    // Validate provided property names.
+    for (String name : propertyStringValues.keySet()) {
+      definition.getPropertyDefinition(name);
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefinedDefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/DefinedDefaultBehaviorProvider.java
new file mode 100644
index 0000000..704c222
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefinedDefaultBehaviorProvider.java
@@ -0,0 +1,93 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+
+
+/**
+ * A default behavior provider which represents a well-defined set of default
+ * values. It should be used by properties which have default value(s) which are
+ * valid value(s) according to the constraints of the property's definition.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public final class DefinedDefaultBehaviorProvider<T> extends
+    DefaultBehaviorProvider<T> {
+
+  // The collection of default values.
+  private final Collection<String> values;
+
+
+
+  /**
+   * Create a new defined default behavior provider associated with the
+   * specified list of values.
+   *
+   * @param values
+   *          The list of values (must be non-<code>null</code> and not
+   *          empty) in their string representation.
+   * @throws IllegalArgumentException
+   *           If the list of values was <code>null</code> or empty.
+   */
+  public DefinedDefaultBehaviorProvider(String... values)
+      throws IllegalArgumentException {
+    if (values == null || values.length == 0) {
+      throw new IllegalArgumentException(
+          "Null or empty list of default values");
+    }
+    this.values = Arrays.asList(values);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <R, P> R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p) {
+    return v.visitDefined(this, p);
+  }
+
+
+
+  /**
+   * Get a copy of the default values.
+   *
+   * @return Returns a newly allocated collection containing a copy of the
+   *         default values.
+   */
+  public Collection<String> getDefaultValues() {
+    return new ArrayList<String>(values);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefinitionDecodingException.java b/opendj-admin/src/main/java/org/opends/server/admin/DefinitionDecodingException.java
new file mode 100644
index 0000000..da41458
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefinitionDecodingException.java
@@ -0,0 +1,136 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * The requested managed object was found but its type could not be
+ * determined.
+ */
+public class DefinitionDecodingException extends DecodingException {
+
+  /**
+   * An enumeration defining the reasons why the definition could not
+   * be resolved.
+   */
+  public static enum Reason {
+    /**
+     * The managed object could be found but its type resolved to an
+     * abstract managed object definition.
+     */
+    ABSTRACT_TYPE_INFORMATION(),
+
+    /**
+     * The managed object could be found but did not contain any type
+     * information (eg missing object classes in LDAP).
+     */
+    NO_TYPE_INFORMATION(),
+
+    /**
+     * The managed object could be found but did not contain the
+     * expected type information (eg incorrect object classes in
+     * LDAP).
+     */
+    WRONG_TYPE_INFORMATION();
+
+  }
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = 3459033551415663416L;
+
+
+
+  // Create the message.
+  private static Message createMessage(AbstractManagedObjectDefinition<?, ?> d,
+      Reason reason) {
+    Message ufn = d.getUserFriendlyName();
+    switch (reason) {
+    case NO_TYPE_INFORMATION:
+      return ERR_DECODING_EXCEPTION_NO_TYPE_INFO.get(ufn);
+    case WRONG_TYPE_INFORMATION:
+      return ERR_DECODING_EXCEPTION_WRONG_TYPE_INFO.get(ufn);
+    default:
+      return ERR_DECODING_EXCEPTION_ABSTRACT_TYPE_INFO.get(ufn);
+    }
+  }
+
+  // The expected type of managed object.
+  private final AbstractManagedObjectDefinition<?, ?> d;
+
+  // The reason why the definition could not be determined.
+  private final Reason reason;
+
+
+
+  /**
+   * Create a new definition decoding exception.
+   *
+   * @param d
+   *          The expected type of managed object.
+   * @param reason
+   *          The reason why the definition could not be determined.
+   */
+  public DefinitionDecodingException(AbstractManagedObjectDefinition<?, ?> d,
+      Reason reason) {
+    super(createMessage(d, reason));
+    this.d = d;
+    this.reason = reason;
+  }
+
+
+
+  /**
+   * Gets the expected managed object definition.
+   *
+   * @return Returns the expected managed object definition.
+   */
+  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
+    return d;
+  }
+
+
+
+  /**
+   * Gets the reason why the definition could not be determined.
+   *
+   * @return Returns the reason why the definition could not be
+   *         determined.
+   */
+  public Reason getReason() {
+    return reason;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DefinitionResolver.java b/opendj-admin/src/main/java/org/opends/server/admin/DefinitionResolver.java
new file mode 100644
index 0000000..0aa51a0
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DefinitionResolver.java
@@ -0,0 +1,72 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * This interface is used to determine the "best match" managed object
+ * definition in a definition hierarchy.
+ * <p>
+ * Managed object definitions, like Java classes, are arranged in an
+ * inheritance hierarchy. When managed objects are decoded (e.g. from
+ * LDAP entries), the driver implementation is provided with an
+ * "expected managed object definition". However, the actual decoded
+ * managed object is often an instance of a sub-type of this
+ * definition. For example, when decoding a connection handler managed
+ * object, the actual type can never be a connection handler because
+ * it is an abstract managed object type. Instead, the decoded managed
+ * object must be a "concrete" sub-type: an LDAP connection handler or
+ * JMX connection handler.
+ * <p>
+ * This resolution process is coordinated by the
+ * <code>resolveManagedObjectDefinition</code> method in managed
+ * object definitions, where it is passed a
+ * <code>DefinitionResolver</code> implementation. The
+ * <code>resolveManagedObjectDefinition</code> method takes care of
+ * recursively descending through the definition hierarchy and invokes
+ * the {@link #matches(AbstractManagedObjectDefinition)} method
+ * against each potential sub-type. It is the job of the resolver to
+ * indicate whether the provided managed object definition is a
+ * candidate definition. For example, the LDAP driver provides a
+ * definition resolver which uses the decoded LDAP entry's object
+ * classes to determine the final appropriate managed object
+ * definition.
+ */
+public interface DefinitionResolver {
+
+  /**
+   * Determines whether or not the provided managed object definition matches
+   * this resolver's criteria.
+   *
+   * @param d
+   *          The managed object definition.
+   * @return Returns <code>true</code> if the the provided managed object
+   *         definition matches this resolver's criteria.
+   */
+  boolean matches(AbstractManagedObjectDefinition<?, ?> d);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DurationPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/DurationPropertyDefinition.java
new file mode 100644
index 0000000..fdf1c1c
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DurationPropertyDefinition.java
@@ -0,0 +1,609 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.*;
+
+import java.util.EnumSet;
+
+
+
+/**
+ * Duration property definition.
+ * <p>
+ * A duration property definition comprises of:
+ * <ul>
+ * <li>a <i>base unit</i> - specifies the minimum granularity which
+ * can be used to specify duration property values. For example, if
+ * the base unit is in seconds then values represented in milliseconds
+ * will not be permitted. The default base unit is seconds
+ * <li>an optional <i>maximum unit</i> - specifies the biggest
+ * duration unit which can be used to specify duration property
+ * values. Values presented in units greater than this unit will not
+ * be permitted. There is no default maximum unit
+ * <li><i>lower limit</i> - specifies the smallest duration
+ * permitted by the property. The default lower limit is 0 and can
+ * never be less than 0
+ * <li>an optional <i>upper limit</i> - specifies the biggest
+ * duration permitted by the property. By default, there is no upper
+ * limit
+ * <li>support for <i>unlimited</i> durations - when permitted users
+ * can specify "unlimited" durations. These are represented using the
+ * decoded value, -1, or the encoded string value "unlimited". By
+ * default, unlimited durations are not permitted. In addition, it is
+ * not possible to define an upper limit and support unlimited values.
+ * </ul>
+ * Decoded values are represented using <code>long</code> values in
+ * the base unit defined for the duration property definition.
+ */
+public final class DurationPropertyDefinition extends PropertyDefinition<Long> {
+
+  // String used to represent unlimited durations.
+  private static final String UNLIMITED = "unlimited";
+
+  // The base unit for this property definition.
+  private final DurationUnit baseUnit;
+
+  // The optional maximum unit for this property definition.
+  private final DurationUnit maximumUnit;
+
+  // The lower limit of the property value in milli-seconds.
+  private final long lowerLimit;
+
+  // The optional upper limit of the property value in milli-seconds.
+  private final Long upperLimit;
+
+  // Indicates whether this property allows the use of the "unlimited"
+  // duration value (represented using a -1L or the string
+  // "unlimited").
+  private final boolean allowUnlimited;
+
+
+
+  /**
+   * An interface for incrementally constructing duration property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<Long, DurationPropertyDefinition> {
+
+    // The base unit for this property definition.
+    private DurationUnit baseUnit = DurationUnit.SECONDS;
+
+    // The optional maximum unit for this property definition.
+    private DurationUnit maximumUnit = null;
+
+    // The lower limit of the property value in milli-seconds.
+    private long lowerLimit = 0L;
+
+    // The optional upper limit of the property value in
+    // milli-seconds.
+    private Long upperLimit = null;
+
+    // Indicates whether this property allows the use of the
+    // "unlimited" duration value (represented using a -1L or the
+    // string "unlimited").
+    private boolean allowUnlimited = false;
+
+
+
+    // Private constructor
+    private Builder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Set the base unit for this property definition (values
+     * including limits are specified in this unit). By default a
+     * duration property definition uses seconds.
+     *
+     * @param unit
+     *          The string representation of the base unit (must not
+     *          be <code>null</code>).
+     * @throws IllegalArgumentException
+     *           If the provided unit name did not correspond to a
+     *           known duration unit, or if the base unit is bigger
+     *           than the maximum unit.
+     */
+    public final void setBaseUnit(String unit) throws IllegalArgumentException {
+      ensureNotNull(unit);
+
+      setBaseUnit(DurationUnit.getUnit(unit));
+    }
+
+
+
+    /**
+     * Set the base unit for this property definition (values
+     * including limits are specified in this unit). By default a
+     * duration property definition uses seconds.
+     *
+     * @param unit
+     *          The base unit (must not be <code>null</code>).
+     * @throws IllegalArgumentException
+     *           If the provided base unit is bigger than the maximum
+     *           unit.
+     */
+    public final void setBaseUnit(DurationUnit unit)
+        throws IllegalArgumentException {
+      ensureNotNull(unit);
+
+      // Make sure that the base unit is not bigger than the maximum
+      // unit.
+      if (maximumUnit != null) {
+        if (unit.getDuration() > maximumUnit.getDuration()) {
+          throw new IllegalArgumentException(
+              "Base unit greater than maximum unit");
+        }
+      }
+
+      this.baseUnit = unit;
+    }
+
+
+
+    /**
+     * Set the maximum unit for this property definition. By default
+     * there is no maximum unit.
+     *
+     * @param unit
+     *          The string representation of the maximum unit, or
+     *          <code>null</code> if there should not be a maximum
+     *          unit.
+     * @throws IllegalArgumentException
+     *           If the provided unit name did not correspond to a
+     *           known duration unit, or if the maximum unit is
+     *           smaller than the base unit.
+     */
+    public final void setMaximumUnit(String unit)
+        throws IllegalArgumentException {
+      if (unit == null) {
+        setMaximumUnit((DurationUnit) null);
+      } else {
+        setMaximumUnit(DurationUnit.getUnit(unit));
+      }
+    }
+
+
+
+    /**
+     * Set the maximum unit for this property definition. By default
+     * there is no maximum unit.
+     *
+     * @param unit
+     *          The maximum unit, or <code>null</code> if there
+     *          should not be a maximum unit.
+     * @throws IllegalArgumentException
+     *           If the provided maximum unit is smaller than the base
+     *           unit.
+     */
+    public final void setMaximumUnit(DurationUnit unit)
+        throws IllegalArgumentException {
+      if (unit != null) {
+        // Make sure that the maximum unit is not smaller than the
+        // base unit.
+        if (unit.getDuration() < baseUnit.getDuration()) {
+          throw new IllegalArgumentException(
+              "Maximum unit smaller than base unit");
+        }
+      }
+
+      this.maximumUnit = unit;
+    }
+
+
+
+    /**
+     * Set the lower limit in milli-seconds.
+     *
+     * @param lowerLimit
+     *          The new lower limit (must be >= 0) in milli-seconds.
+     * @throws IllegalArgumentException
+     *           If a negative lower limit was specified, or the lower
+     *           limit is greater than the upper limit.
+     */
+    public final void setLowerLimit(long lowerLimit)
+        throws IllegalArgumentException {
+      if (lowerLimit < 0) {
+        throw new IllegalArgumentException("Negative lower limit");
+      }
+
+      if (upperLimit != null && lowerLimit > upperLimit) {
+        throw new IllegalArgumentException(
+            "Lower limit greater than upper limit");
+      }
+
+      this.lowerLimit = lowerLimit;
+    }
+
+
+
+    /**
+     * Set the lower limit using a string representation of the limit.
+     * If the string does not specify a unit, the current base unit
+     * will be used.
+     *
+     * @param lowerLimit
+     *          The string representation of the new lower limit.
+     * @throws IllegalArgumentException
+     *           If the lower limit could not be parsed, or if a
+     *           negative lower limit was specified, or the lower
+     *           limit is greater than the upper limit.
+     */
+    public final void setLowerLimit(String lowerLimit)
+        throws IllegalArgumentException {
+      setLowerLimit(DurationUnit.parseValue(lowerLimit, baseUnit));
+    }
+
+
+
+    /**
+     * Set the upper limit in milli-seconds.
+     *
+     * @param upperLimit
+     *          The new upper limit in milli-seconds, or
+     *          <code>null</code> if there is no upper limit.
+     * @throws IllegalArgumentException
+     *           If a negative upper limit was specified, or the lower
+     *           limit is greater than the upper limit or unlimited
+     *           durations are permitted.
+     */
+    public final void setUpperLimit(Long upperLimit)
+        throws IllegalArgumentException {
+      if (upperLimit != null) {
+        if (upperLimit < 0) {
+          throw new IllegalArgumentException("Negative upper limit");
+        }
+
+        if (lowerLimit > upperLimit) {
+          throw new IllegalArgumentException(
+              "Lower limit greater than upper limit");
+        }
+
+        if (allowUnlimited) {
+          throw new IllegalArgumentException(
+              "Upper limit specified when unlimited durations are permitted");
+        }
+      }
+
+      this.upperLimit = upperLimit;
+    }
+
+
+
+    /**
+     * Set the upper limit using a string representation of the limit.
+     * If the string does not specify a unit, the current base unit
+     * will be used.
+     *
+     * @param upperLimit
+     *          The string representation of the new upper limit, or
+     *          <code>null</code> if there is no upper limit.
+     * @throws IllegalArgumentException
+     *           If the upper limit could not be parsed, or if the
+     *           lower limit is greater than the upper limit.
+     */
+    public final void setUpperLimit(String upperLimit)
+        throws IllegalArgumentException {
+      if (upperLimit == null) {
+        setUpperLimit((Long) null);
+      } else {
+        setUpperLimit(DurationUnit.parseValue(upperLimit, baseUnit));
+      }
+    }
+
+
+
+    /**
+     * Specify whether or not this property definition will allow
+     * unlimited values (default is false).
+     *
+     * @param allowUnlimited
+     *          <code>true</code> if the property will allow
+     *          unlimited values, or <code>false</code> otherwise.
+     * @throws IllegalArgumentException
+     *           If unlimited values are to be permitted but there is
+     *           an upper limit specified.
+     */
+    public final void setAllowUnlimited(boolean allowUnlimited)
+        throws IllegalArgumentException {
+      if (allowUnlimited && upperLimit != null) {
+        throw new IllegalArgumentException(
+            "Upper limit specified when unlimited durations are permitted");
+      }
+
+      this.allowUnlimited = allowUnlimited;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected DurationPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<Long> defaultBehavior) {
+      return new DurationPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior, baseUnit, maximumUnit, lowerLimit,
+          upperLimit, allowUnlimited);
+    }
+  }
+
+
+
+  /**
+   * Create a duration property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new integer property definition builder.
+   */
+  public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d,
+      String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private DurationPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
+      String propertyName, EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<Long> defaultBehavior, DurationUnit baseUnit,
+      DurationUnit maximumUnit, Long lowerLimit, Long upperLimit,
+      boolean allowUnlimited) {
+    super(d, Long.class, propertyName, options, adminAction, defaultBehavior);
+    this.baseUnit = baseUnit;
+    this.maximumUnit = maximumUnit;
+    this.lowerLimit = lowerLimit;
+    this.upperLimit = upperLimit;
+    this.allowUnlimited = allowUnlimited;
+  }
+
+
+
+  /**
+   * Get the base unit for this property definition (values including
+   * limits are specified in this unit).
+   *
+   * @return Returns the base unit for this property definition
+   *         (values including limits are specified in this unit).
+   */
+  public DurationUnit getBaseUnit() {
+    return baseUnit;
+  }
+
+
+
+  /**
+   * Get the maximum unit for this property definition if specified.
+   *
+   * @return Returns the maximum unit for this property definition, or
+   *         <code>null</code> if there is no maximum unit.
+   */
+  public DurationUnit getMaximumUnit() {
+    return maximumUnit;
+  }
+
+
+
+  /**
+   * Get the lower limit in milli-seconds.
+   *
+   * @return Returns the lower limit in milli-seconds.
+   */
+  public long getLowerLimit() {
+    return lowerLimit;
+  }
+
+
+
+  /**
+   * Get the upper limit in milli-seconds.
+   *
+   * @return Returns the upper limit in milli-seconds, or
+   *         <code>null</code> if there is no upper limit.
+   */
+  public Long getUpperLimit() {
+    return upperLimit;
+  }
+
+
+
+  /**
+   * Determine whether this property allows unlimited durations.
+   *
+   * @return Returns <code>true</code> if this this property allows
+   *         unlimited durations.
+   */
+  public boolean isAllowUnlimited() {
+    return allowUnlimited;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(Long value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    long nvalue = baseUnit.toMilliSeconds(value);
+    if (!allowUnlimited && nvalue < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+
+      // unlimited allowed
+    } else if (nvalue >= 0 && nvalue < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+
+    if ((upperLimit != null) && (nvalue > upperLimit)) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(Long value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // Make sure that we correctly encode negative values as
+    // "unlimited".
+    if (allowUnlimited) {
+      if (value < 0) {
+        return UNLIMITED;
+      }
+    }
+
+    // Encode the size value using the base unit.
+    StringBuilder builder = new StringBuilder();
+    builder.append(value);
+    builder.append(' ');
+    builder.append(baseUnit.toString());
+    return builder.toString();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Long decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    // First check for the special "unlimited" value when necessary.
+    if (allowUnlimited) {
+      if (value.trim().equalsIgnoreCase(UNLIMITED)) {
+        return -1L;
+      }
+    }
+
+    // Parse the string representation.
+    long ms;
+    try {
+      ms = DurationUnit.parseValue(value);
+    } catch (NumberFormatException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    // Check the unit is in range - values must not be more granular
+    // than the base unit.
+    if ((ms % baseUnit.getDuration()) != 0) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    // Convert the value a long in the property's required unit.
+    Long i = (long) baseUnit.fromMilliSeconds(ms);
+    try {
+      validateValue(i);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+    return i;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitDuration(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
+    return v.visitDuration(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    super.toString(builder);
+
+    builder.append(" baseUnit=");
+    builder.append(baseUnit);
+
+    if (maximumUnit != null) {
+      builder.append(" maximumUnit=");
+      builder.append(maximumUnit);
+    }
+
+    builder.append(" lowerLimit=");
+    builder.append(lowerLimit);
+    builder.append("ms");
+
+    if (upperLimit != null) {
+      builder.append(" upperLimit=");
+      builder.append(upperLimit);
+      builder.append("ms");
+    }
+
+    builder.append(" allowUnlimited=");
+    builder.append(allowUnlimited);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(Long o1, Long o2) {
+    return o1.compareTo(o2);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/DurationUnit.java b/opendj-admin/src/main/java/org/opends/server/admin/DurationUnit.java
new file mode 100644
index 0000000..ec4d3dd
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/DurationUnit.java
@@ -0,0 +1,385 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+
+/**
+ * This enumeration defines various duration units.
+ */
+public enum DurationUnit {
+
+  /**
+   * A day unit.
+   */
+  DAYS((long) 24 * 60 * 60 * 1000, "d", "days"),
+
+  /**
+   * An hour unit.
+   */
+  HOURS((long) 60 * 60 * 1000, "h", "hours"),
+
+  /**
+   * A millisecond unit.
+   */
+  MILLI_SECONDS(1L, "ms", "milliseconds"),
+
+  /**
+   * A minute unit.
+   */
+  MINUTES((long) 60 * 1000, "m", "minutes"),
+
+  /**
+   * A second unit.
+   */
+  SECONDS(1000L, "s", "seconds"),
+
+  /**
+   * A week unit.
+   */
+  WEEKS((long) 7 * 24 * 60 * 60 * 1000, "w", "weeks");
+
+  // A lookup table for resolving a unit from its name.
+  private static final Map<String, DurationUnit> nameToUnit;
+  static {
+    nameToUnit = new HashMap<String, DurationUnit>();
+
+    for (DurationUnit unit : DurationUnit.values()) {
+      nameToUnit.put(unit.shortName, unit);
+      nameToUnit.put(unit.longName, unit);
+    }
+  }
+
+
+
+  /**
+   * Get the unit corresponding to the provided unit name.
+   *
+   * @param s
+   *          The name of the unit. Can be the abbreviated or long
+   *          name and can contain white space and mixed case
+   *          characters.
+   * @return Returns the unit corresponding to the provided unit name.
+   * @throws IllegalArgumentException
+   *           If the provided name did not correspond to a known
+   *           duration unit.
+   */
+  public static DurationUnit getUnit(String s) throws IllegalArgumentException {
+    DurationUnit unit = nameToUnit.get(s.trim().toLowerCase());
+    if (unit == null) {
+      throw new IllegalArgumentException("Illegal duration unit \"" + s + "\"");
+    }
+    return unit;
+  }
+
+
+
+  /**
+   * Parse the provided duration string and return its equivalent
+   * duration in milliseconds. The duration string must specify the
+   * unit e.g. "10s". This method will parse duration string
+   * representations produced from the {@link #toString(long)} method.
+   * Therefore, a duration can comprise of multiple duration
+   * specifiers, for example <code>1d15m25s</code>.
+   *
+   * @param s
+   *          The duration string to be parsed.
+   * @return Returns the parsed duration in milliseconds.
+   * @throws NumberFormatException
+   *           If the provided duration string could not be parsed.
+   * @see #toString(long)
+   */
+  public static long parseValue(String s) throws NumberFormatException {
+    return parseValue(s, null);
+  }
+
+
+
+  /**
+   * Parse the provided duration string and return its equivalent
+   * duration in milliseconds. This method will parse duration string
+   * representations produced from the {@link #toString(long)} method.
+   * Therefore, a duration can comprise of multiple duration
+   * specifiers, for example <code>1d15m25s</code>.
+   *
+   * @param s
+   *          The duration string to be parsed.
+   * @param defaultUnit
+   *          The default unit to use if there is no unit specified in
+   *          the duration string, or <code>null</code> if the
+   *          string must always contain a unit.
+   * @return Returns the parsed duration in milliseconds.
+   * @throws NumberFormatException
+   *           If the provided duration string could not be parsed.
+   * @see #toString(long)
+   */
+  public static long parseValue(String s, DurationUnit defaultUnit)
+      throws NumberFormatException {
+    String ns = s.trim();
+    if (ns.length() == 0) {
+      throw new NumberFormatException("Empty duration value \"" + s + "\"");
+    }
+
+    Pattern p1 = Pattern.compile("^\\s*((\\d+)\\s*w)?" + "\\s*((\\d+)\\s*d)?"
+        + "\\s*((\\d+)\\s*h)?" + "\\s*((\\d+)\\s*m)?" + "\\s*((\\d+)\\s*s)?"
+        + "\\s*((\\d+)\\s*ms)?\\s*$", Pattern.CASE_INSENSITIVE);
+    Matcher m1 = p1.matcher(ns);
+    if (m1.matches()) {
+      // Value must be of the form produced by toString(long).
+      String weeks = m1.group(2);
+      String days = m1.group(4);
+      String hours = m1.group(6);
+      String minutes = m1.group(8);
+      String seconds = m1.group(10);
+      String ms = m1.group(12);
+
+      long duration = 0;
+
+      try {
+        if (weeks != null) {
+          duration += Long.valueOf(weeks) * WEEKS.getDuration();
+        }
+
+        if (days != null) {
+          duration += Long.valueOf(days) * DAYS.getDuration();
+        }
+
+        if (hours != null) {
+          duration += Long.valueOf(hours) * HOURS.getDuration();
+        }
+
+        if (minutes != null) {
+          duration += Long.valueOf(minutes) * MINUTES.getDuration();
+        }
+
+        if (seconds != null) {
+          duration += Long.valueOf(seconds) * SECONDS.getDuration();
+        }
+
+        if (ms != null) {
+          duration += Long.valueOf(ms) * MILLI_SECONDS.getDuration();
+        }
+      } catch (NumberFormatException e) {
+        throw new NumberFormatException("Invalid duration value \"" + s + "\"");
+      }
+
+      return duration;
+    } else {
+      // Value must be a floating point number followed by a unit.
+      Pattern p2 = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*(\\w+)?\\s*$");
+      Matcher m2 = p2.matcher(ns);
+
+      if (!m2.matches()) {
+        throw new NumberFormatException("Invalid duration value \"" + s + "\"");
+      }
+
+      // Group 1 is the float.
+      double d;
+      try {
+        d = Double.valueOf(m2.group(1));
+      } catch (NumberFormatException e) {
+        throw new NumberFormatException("Invalid duration value \"" + s + "\"");
+      }
+
+      // Group 3 is the unit.
+      String unitString = m2.group(3);
+      DurationUnit unit;
+      if (unitString == null) {
+        if (defaultUnit == null) {
+          throw new NumberFormatException("Invalid duration value \"" + s
+              + "\"");
+        } else {
+          unit = defaultUnit;
+        }
+      } else {
+        try {
+          unit = getUnit(unitString);
+        } catch (IllegalArgumentException e) {
+          throw new NumberFormatException("Invalid duration value \"" + s
+              + "\"");
+        }
+      }
+
+      return unit.toMilliSeconds(d);
+    }
+  }
+
+
+
+  /**
+   * Returns a string representation of the provided duration. The
+   * string representation can be parsed using the
+   * {@link #parseValue(String)} method. The string representation is
+   * comprised of one or more of the number of weeks, days, hours,
+   * minutes, seconds, and milliseconds. Here are some examples:
+   *
+   * <pre>
+   * toString(0)       // 0 ms
+   * toString(999)     // 999 ms
+   * toString(1000)    // 1 s
+   * toString(1500)    // 1 s 500 ms
+   * toString(3650000) // 1 h 50 s
+   * toString(3700000) // 1 h 1 m 40 s
+   * </pre>
+   *
+   * @param duration
+   *          The duration in milliseconds.
+   * @return Returns a string representation of the provided duration.
+   * @throws IllegalArgumentException
+   *           If the provided duration is negative.
+   * @see #parseValue(String)
+   * @see #parseValue(String, DurationUnit)
+   */
+  public static String toString(long duration) throws IllegalArgumentException {
+    if (duration < 0) {
+      throw new IllegalArgumentException("Negative duration " + duration);
+    }
+
+    if (duration == 0) {
+      return "0 ms";
+    }
+
+    DurationUnit[] units = new DurationUnit[] { WEEKS, DAYS, HOURS, MINUTES,
+        SECONDS, MILLI_SECONDS };
+    long remainder = duration;
+    StringBuilder builder = new StringBuilder();
+    boolean isFirst = true;
+    for (DurationUnit unit : units) {
+      long count = remainder / unit.getDuration();
+      if (count > 0) {
+        if (!isFirst) {
+          builder.append(' ');
+        }
+        builder.append(count);
+        builder.append(' ');
+        builder.append(unit.getShortName());
+        remainder = remainder - (count * unit.getDuration());
+        isFirst = false;
+      }
+    }
+    return builder.toString();
+  }
+
+  // The long name of the unit.
+  private final String longName;
+
+  // The abbreviation of the unit.
+  private final String shortName;
+
+  // The size of the unit in milliseconds.
+  private final long sz;
+
+
+
+  // Private constructor.
+  private DurationUnit(long sz, String shortName, String longName) {
+    this.sz = sz;
+    this.shortName = shortName;
+    this.longName = longName;
+  }
+
+
+
+  /**
+   * Converts the specified duration in milliseconds to this unit.
+   *
+   * @param duration
+   *          The duration in milliseconds.
+   * @return Returns milliseconds in this unit.
+   */
+  public double fromMilliSeconds(long duration) {
+    return ((double) duration / sz);
+  }
+
+
+
+  /**
+   * Get the number of milliseconds that this unit represents.
+   *
+   * @return Returns the number of milliseconds that this unit
+   *         represents.
+   */
+  public long getDuration() {
+    return sz;
+  }
+
+
+
+  /**
+   * Get the long name of this unit.
+   *
+   * @return Returns the long name of this unit.
+   */
+  public String getLongName() {
+    return longName;
+  }
+
+
+
+  /**
+   * Get the abbreviated name of this unit.
+   *
+   * @return Returns the abbreviated name of this unit.
+   */
+  public String getShortName() {
+    return shortName;
+  }
+
+
+
+  /**
+   * Converts the specified duration in this unit to milliseconds.
+   *
+   * @param duration
+   *          The duration as a quantity of this unit.
+   * @return Returns the number of milliseconds that the duration
+   *         represents.
+   */
+  public long toMilliSeconds(double duration) {
+    return (long) (sz * duration);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * This implementation returns the abbreviated name of this duration
+   * unit.
+   */
+  @Override
+  public String toString() {
+    return shortName;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/EnumPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/EnumPropertyDefinition.java
new file mode 100644
index 0000000..815bdfc
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/EnumPropertyDefinition.java
@@ -0,0 +1,275 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+
+
+
+/**
+ * Enumeration property definition.
+ *
+ * @param <E>
+ *          The enumeration that should be used for values of this
+ *          property definition.
+ */
+public final class EnumPropertyDefinition<E extends Enum<E>> extends
+    PropertyDefinition<E> {
+
+  /**
+   * An interface for incrementally constructing enumeration property
+   * definitions.
+   *
+   * @param <E>
+   *          The enumeration that should be used for values of this
+   *          property definition.
+   */
+  public static class Builder<E extends Enum<E>> extends
+      AbstractBuilder<E, EnumPropertyDefinition<E>> {
+
+    // The enumeration class.
+    private Class<E> enumClass;
+
+
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+      this.enumClass = null;
+    }
+
+
+
+    /**
+     * Set the enumeration class which should be used for values of
+     * this property definition.
+     *
+     * @param enumClass
+     *          The enumeration class which should be used for values
+     *          of this property definition.
+     */
+    public final void setEnumClass(Class<E> enumClass) {
+      this.enumClass = enumClass;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected EnumPropertyDefinition<E> buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<E> defaultBehavior) {
+      // Make sure that the enumeration class has been defined.
+      if (enumClass == null) {
+        throw new IllegalStateException("Enumeration class undefined");
+      }
+
+      return new EnumPropertyDefinition<E>(d, propertyName, options,
+          adminAction, defaultBehavior, enumClass);
+    }
+  }
+
+
+
+  /**
+   * Create an enumeration property definition builder.
+   *
+   * @param <E>
+   *          The enumeration that should be used for values of this
+   *          property definition.
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new enumeration property definition builder.
+   */
+  public static <E extends Enum<E>> Builder<E> createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder<E>(d, propertyName);
+  }
+
+  // The enumeration class.
+  private final Class<E> enumClass;
+
+  // Map used for decoding values.
+  private final Map<String, E> decodeMap;
+
+
+
+  // Private constructor.
+  private EnumPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
+      String propertyName, EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<E> defaultBehavior, Class<E> enumClass) {
+    super(d, enumClass, propertyName, options, adminAction, defaultBehavior);
+    this.enumClass = enumClass;
+
+    // Initialize the decoding map.
+    this.decodeMap = new HashMap<String, E>();
+    for (E value : EnumSet.<E> allOf(enumClass)) {
+      String s = value.toString().trim().toLowerCase();
+      this.decodeMap.put(s, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitEnum(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, E value, P p) {
+    return v.visitEnum(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public E decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    String nvalue = value.trim().toLowerCase();
+    E eValue = decodeMap.get(nvalue);
+    if (eValue == null) {
+      throw new IllegalPropertyValueStringException(this, value);
+    } else {
+      return eValue;
+    }
+  }
+
+
+
+  /**
+   * Get the enumeration class used for values of this property.
+   *
+   * @return Returns the enumeration class used for values of this
+   *         property.
+   */
+  public Class<E> getEnumClass() {
+    return enumClass;
+  }
+
+
+
+  /**
+   * Gets the synopsis of the specified enumeration value of this
+   * enumeration property definition in the default locale.
+   *
+   * @param value
+   *          The enumeration value.
+   * @return Returns the synopsis of the specified enumeration value
+   *         of this enumeration property definition in the default
+   *         locale.
+   */
+  public final Message getValueSynopsis(E value) {
+    return getValueSynopsis(Locale.getDefault(), value);
+  }
+
+
+
+  /**
+   * Gets the synopsis of the specified enumeration value of this
+   * enumeration property definition in the specified locale.
+   *
+   * @param value
+   *          The enumeration value.
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of the specified enumeration value
+   *         of this enumeration property definition in the specified
+   *         locale.
+   */
+  public final Message getValueSynopsis(Locale locale, E value) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + getName()
+        + ".syntax.enumeration.value." + value.toString()
+        + ".synopsis";
+    try {
+      return resource.getMessage(getManagedObjectDefinition(),
+          property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String normalizeValue(E value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    return value.toString().trim().toLowerCase();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(E value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No additional validation required.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/GenericConstraint.java b/opendj-admin/src/main/java/org/opends/server/admin/GenericConstraint.java
new file mode 100644
index 0000000..87ae550
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/GenericConstraint.java
@@ -0,0 +1,223 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.condition.Condition;
+import org.opends.server.admin.server.ServerConstraintHandler;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * A generic constraint which comprises of an underlying condition and
+ * a description. The condition must evaluate to <code>true</code>
+ * in order for a new managed object to be created or modified.
+ */
+public class GenericConstraint extends Constraint {
+
+  /**
+   * The client-side constraint handler.
+   */
+  private class ClientHandler extends ClientConstraintHandler {
+
+    // Private constructor.
+    private ClientHandler() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAddAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      if (!condition.evaluate(context, managedObject)) {
+        unacceptableReasons.add(getSynopsis());
+        return false;
+      } else {
+        return true;
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isModifyAcceptable(ManagementContext context,
+        ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+        throws AuthorizationException, CommunicationException {
+      if (!condition.evaluate(context, managedObject)) {
+        unacceptableReasons.add(getSynopsis());
+        return false;
+      } else {
+        return true;
+      }
+    }
+
+  };
+
+
+
+  /**
+   * The server-side constraint handler.
+   */
+  private class ServerHandler extends ServerConstraintHandler {
+
+    // Private constructor.
+    private ServerHandler() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isUsable(ServerManagedObject<?> managedObject,
+        Collection<Message> unacceptableReasons) throws ConfigException {
+      if (!condition.evaluate(managedObject)) {
+        unacceptableReasons.add(getSynopsis());
+        return false;
+      } else {
+        return true;
+      }
+    }
+
+  };
+
+  // The client-side constraint handler.
+  private final ClientConstraintHandler clientHandler = new ClientHandler();
+
+  // The condition associated with this constraint.
+  private final Condition condition;
+
+  // The managed object definition associated with this constraint.
+  private final AbstractManagedObjectDefinition<?, ?> definition;
+
+  // The constraint ID.
+  private final int id;
+
+  // The server-side constraint handler.
+  private final ServerConstraintHandler serverHandler = new ServerHandler();
+
+
+
+  /**
+   * Creates a new generic constraint.
+   *
+   * @param definition
+   *          The managed object definition associated with this
+   *          constraint.
+   * @param id
+   *          The constraint ID.
+   * @param condition
+   *          The condition associated with this constraint.
+   */
+  public GenericConstraint(AbstractManagedObjectDefinition<?, ?> definition,
+      int id, Condition condition) {
+    this.definition = definition;
+    this.id = id;
+    this.condition = condition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
+    return Collections.singleton(clientHandler);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
+    return Collections.singleton(serverHandler);
+  }
+
+
+
+  /**
+   * Gets the synopsis of this constraint in the default locale.
+   *
+   * @return Returns the synopsis of this constraint in the default
+   *         locale.
+   */
+  public final Message getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this constraint in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this constraint in the specified
+   *         locale.
+   */
+  public final Message getSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "constraint." + id + ".synopsis";
+    return resource.getMessage(definition, property, locale);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception {
+    condition.initialize(definition);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/IPAddressMaskPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/IPAddressMaskPropertyDefinition.java
new file mode 100644
index 0000000..caf5b95
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/IPAddressMaskPropertyDefinition.java
@@ -0,0 +1,166 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.AddressMask;
+
+
+
+/**
+ * IP address mask property definition.
+ */
+public final class IPAddressMaskPropertyDefinition extends
+    PropertyDefinition<AddressMask> {
+
+  /**
+   * An interface for incrementally constructing IP address mask property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<AddressMask, IPAddressMaskPropertyDefinition> {
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected IPAddressMaskPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName, EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<AddressMask> defaultBehavior) {
+      return new IPAddressMaskPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior);
+    }
+
+  }
+
+
+
+  /**
+   * Create a IP address mask property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new IP address mask property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private IPAddressMaskPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<AddressMask> defaultBehavior) {
+    super(d, AddressMask.class, propertyName, options, adminAction,
+        defaultBehavior);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(AddressMask value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No additional validation required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public AddressMask decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      return AddressMask.decode(value);
+    } catch (ConfigException e) {
+      // TODO: it would be nice to throw the cause.
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitIPAddressMask(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, AddressMask value, P p) {
+    return v.visitIPAddressMask(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(AddressMask o1, AddressMask o2) {
+    return o1.toString().compareTo(o2.toString());
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/IPAddressPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/IPAddressPropertyDefinition.java
new file mode 100644
index 0000000..380920b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/IPAddressPropertyDefinition.java
@@ -0,0 +1,188 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.EnumSet;
+
+
+
+/**
+ * IP address property definition.
+ */
+public final class IPAddressPropertyDefinition extends
+    PropertyDefinition<InetAddress> {
+
+  /**
+   * An interface for incrementally constructing IP address property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<InetAddress, IPAddressPropertyDefinition> {
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected IPAddressPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<InetAddress> defaultBehavior) {
+      return new IPAddressPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior);
+    }
+
+  }
+
+
+
+  /**
+   * Create a IP address property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new IP address property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private IPAddressPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<InetAddress> defaultBehavior) {
+    super(d, InetAddress.class, propertyName, options, adminAction,
+        defaultBehavior);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(InetAddress value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // No additional validation required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public InetAddress decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      return InetAddress.getByName(value);
+    } catch (UnknownHostException e) {
+      // TODO: it would be nice to throw the cause.
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(InetAddress value)
+      throws IllegalPropertyValueException {
+    // We should return the host name if it is available, or the IP
+    // address if not.
+
+    // Unforunately, there is no InetAddress method for doing this, so
+    // we have to resort to hacking at the toString() encoding.
+    String s = value.toString();
+    int i = s.indexOf('/');
+    if (i > 0) {
+      // Host address is before the forward slash.
+      return s.substring(0, i);
+    } else {
+      return value.getHostAddress();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitIPAddress(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, InetAddress value, P p) {
+    return v.visitIPAddress(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(InetAddress o1, InetAddress o2) {
+    return o1.getHostAddress().compareTo(o2.getHostAddress());
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueException.java b/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueException.java
new file mode 100644
index 0000000..969520a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueException.java
@@ -0,0 +1,88 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * Thrown to indicate that a property value was invalid according to
+ * its associated property definition.
+ */
+public class IllegalPropertyValueException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -3145632074909281823L;
+
+  // The illegal property value.
+  private final Object value;
+
+
+
+  /**
+   * Create a new illegal property value exception.
+   *
+   * @param pd
+   *          The property definition.
+   * @param value
+   *          The illegal property value.
+   */
+  public IllegalPropertyValueException(PropertyDefinition<?> pd, Object value) {
+    super(pd, createMessage(pd, value));
+    this.value = value;
+  }
+
+
+
+  /**
+   * Get the illegal property value that caused the exception.
+   *
+   * @return Returns the illegal property value.
+   */
+  public final Object getIllegalValue() {
+    return value;
+  }
+
+
+
+  // Create the message.
+  private static Message createMessage(PropertyDefinition<?> pd, Object value) {
+    PropertyDefinitionUsageBuilder builder = new PropertyDefinitionUsageBuilder(
+        true);
+    return ERR_ILLEGAL_PROPERTY_VALUE_EXCEPTION.get(String.valueOf(value), pd
+        .getName(), builder.getUsage(pd));
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueStringException.java b/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueStringException.java
new file mode 100644
index 0000000..b0a6ae7
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/IllegalPropertyValueStringException.java
@@ -0,0 +1,89 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * Thrown to indicate that a property value string was invalid
+ * according to its associated property definition.
+ */
+public class IllegalPropertyValueStringException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -3145632074909281823L;
+
+  // The illegal property value string.
+  private final String value;
+
+
+
+  /**
+   * Create a new illegal property value string exception.
+   *
+   * @param pd
+   *          The property definition.
+   * @param value
+   *          The illegal property value string.
+   */
+  public IllegalPropertyValueStringException(PropertyDefinition<?> pd,
+      String value) {
+    super(pd, createMessage(pd, value));
+    this.value = value;
+  }
+
+
+
+  /**
+   * Get the illegal property value string that caused the exception.
+   *
+   * @return Returns the illegal property value string.
+   */
+  public final String getIllegalValueString() {
+    return value;
+  }
+
+
+
+  // Create the message.
+  private static Message createMessage(PropertyDefinition<?> pd, String value) {
+    PropertyDefinitionUsageBuilder builder = new PropertyDefinitionUsageBuilder(
+        true);
+    return ERR_ILLEGAL_PROPERTY_VALUE_STRING_EXCEPTION.get(value, pd.getName(),
+        builder.getUsage(pd));
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/InstantiableRelationDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/InstantiableRelationDefinition.java
new file mode 100644
index 0000000..2600a97
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/InstantiableRelationDefinition.java
@@ -0,0 +1,304 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.Validator.*;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+
+
+/**
+ * A managed object composite relationship definition which represents
+ * a composition of zero or more managed objects.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          relation definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          relation definition refers to.
+ */
+public final class InstantiableRelationDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends RelationDefinition<C, S> {
+
+  /**
+   * An interface for incrementally constructing instantiable relation
+   * definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this relation definition refers to.
+   */
+  public static final class Builder
+      <C extends ConfigurationClient, S extends Configuration>
+      extends AbstractBuilder<C, S, InstantiableRelationDefinition<C, S>> {
+
+    // The optional naming property definition.
+    private PropertyDefinition<?> namingPropertyDefinition = null;
+
+    // The plural name of the relation.
+    private final String pluralName;
+
+    // The optional default managed objects associated with this
+    // instantiable relation definition.
+    private final Map<String, DefaultManagedObject<? extends C, ? extends S>>
+      defaultManagedObjects = new HashMap<String,
+        DefaultManagedObject<? extends C, ? extends S>>();
+
+
+    /**
+     * Creates a new builder which can be used to incrementally build
+     * an instantiable relation definition.
+     *
+     * @param pd
+     *          The parent managed object definition.
+     * @param name
+     *          The name of the relation.
+     * @param pluralName
+     *          The plural name of the relation.
+     * @param cd
+     *          The child managed object definition.
+     */
+    public Builder(AbstractManagedObjectDefinition<?, ?> pd, String name,
+        String pluralName, AbstractManagedObjectDefinition<C, S> cd) {
+      super(pd, name, cd);
+      this.pluralName = pluralName;
+    }
+
+
+
+    /**
+     * Adds the named default managed object to this instantiable
+     * relation definition.
+     *
+     * @param name
+     *          The name of the default managed object.
+     * @param defaultManagedObject
+     *          The default managed object.
+     */
+    public void setDefaultManagedObject(String name,
+        DefaultManagedObject<? extends C, ? extends S> defaultManagedObject) {
+      this.defaultManagedObjects.put(name, defaultManagedObject);
+    }
+
+
+
+    /**
+     * Sets the naming property for the instantiable relation
+     * definition.
+     *
+     * @param namingPropertyDefinition
+     *          The property of the child managed object definition
+     *          which should be used for naming, or <code>null</code>
+     *          if this relation does not use a property for naming.
+     */
+    public void setNamingProperty(
+        PropertyDefinition<?> namingPropertyDefinition) {
+      ensureNotNull(namingPropertyDefinition);
+      this.namingPropertyDefinition = namingPropertyDefinition;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected InstantiableRelationDefinition<C, S> buildInstance(
+        Common<C, S> common) {
+      return new InstantiableRelationDefinition<C, S>(common, pluralName,
+          namingPropertyDefinition, defaultManagedObjects);
+    }
+
+  }
+
+  // The optional naming property definition.
+  private final PropertyDefinition<?> namingPropertyDefinition;
+
+  // The plural name of the relation.
+  private final String pluralName;
+
+  // The optional default managed objects associated with this
+  // instantiable relation definition.
+  private final Map<String, DefaultManagedObject<? extends C, ? extends S>>
+    defaultManagedObjects;
+
+
+
+  // Private constructor.
+  private InstantiableRelationDefinition(Common<C, S> common,
+      String pluralName,
+      PropertyDefinition<?> namingPropertyDefinition,
+      Map<String, DefaultManagedObject<? extends C, ? extends S>>
+        defaultManagedObjects) {
+    super(common);
+    this.pluralName = pluralName;
+    this.namingPropertyDefinition = namingPropertyDefinition;
+    this.defaultManagedObjects = defaultManagedObjects;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p) {
+    return v.visitInstantiable(this, p);
+  }
+
+
+
+  /**
+   * Gets the named default managed object associated with this
+   * instantiable relation definition.
+   *
+   * @param name
+   *          The name of the default managed object.
+   * @return Returns the named default managed object.
+   * @throws IllegalArgumentException
+   *           If there is no default managed object associated with
+   *           the provided name.
+   */
+  public DefaultManagedObject<? extends C, ? extends S> getDefaultManagedObject(
+      String name) throws IllegalArgumentException {
+    if (!defaultManagedObjects.containsKey(name)) {
+      throw new IllegalArgumentException(
+          "unrecognized default managed object \"" + name + "\"");
+    }
+    return defaultManagedObjects.get(name);
+  }
+
+
+
+  /**
+   * Gets the names of the default managed objects associated with
+   * this instantiable relation definition.
+   *
+   * @return Returns an unmodifiable set containing the names of the
+   *         default managed object.
+   */
+  public Set<String> getDefaultManagedObjectNames() {
+    return Collections.unmodifiableSet(defaultManagedObjects.keySet());
+  }
+
+
+
+  /**
+   * Get the property of the child managed object definition which
+   * should be used for naming children.
+   *
+   * @return Returns the property of the child managed object
+   *         definition which should be used for naming, or
+   *         <code>null</code> if this relation does not use a
+   *         property for naming.
+   */
+  public PropertyDefinition<?> getNamingPropertyDefinition() {
+    return namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * Get the plural name of the relation.
+   *
+   * @return Returns the plural name of the relation.
+   */
+  public String getPluralName() {
+    return pluralName;
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this relation definition in
+   * the default locale.
+   *
+   * @return Returns the user friendly plural name of this relation
+   *         definition in the default locale.
+   */
+  public Message getUserFriendlyPluralName() {
+    return getUserFriendlyPluralName(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this relation definition in
+   * the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the user friendly plural name of this relation
+   *         definition in the specified locale.
+   */
+  public Message getUserFriendlyPluralName(Locale locale) {
+    String property = "relation." + getName() + ".user-friendly-plural-name";
+    return ManagedObjectDefinitionI18NResource.getInstance().getMessage(
+        getParentDefinition(), property, locale);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    builder.append("name=");
+    builder.append(getName());
+    builder.append(" type=collection parent=");
+    builder.append(getParentDefinition().getName());
+    builder.append(" child=");
+    builder.append(getChildDefinition().getName());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception {
+    for (DefaultManagedObject<?, ?> dmo : defaultManagedObjects.values()) {
+      dmo.initialize();
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/IntegerPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/IntegerPropertyDefinition.java
new file mode 100644
index 0000000..ec9d92d
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/IntegerPropertyDefinition.java
@@ -0,0 +1,394 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.MissingResourceException;
+
+
+
+/**
+ * Integer property definition.
+ * <p>
+ * All values must be zero or positive and within the lower/upper limit
+ * constraints. Support is provided for "unlimited" values. These are
+ * represented using a negative value or using the string "unlimited".
+ */
+public final class IntegerPropertyDefinition extends
+    PropertyDefinition<Integer> {
+
+  // String used to represent unlimited.
+  private static final String UNLIMITED = "unlimited";
+
+  // The lower limit of the property value.
+  private final int lowerLimit;
+
+  // The optional upper limit of the property value.
+  private final Integer upperLimit;
+
+  // Indicates whether this property allows the use of the "unlimited" value
+  // (represented using a -1 or the string "unlimited").
+  private final boolean allowUnlimited;
+
+
+
+  /**
+   * An interface for incrementally constructing integer property definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<Integer, IntegerPropertyDefinition> {
+
+    // The lower limit of the property value.
+    private int lowerLimit = 0;
+
+    // The optional upper limit of the property value.
+    private Integer upperLimit = null;
+
+    // Indicates whether this property allows the use of the "unlimited" value
+    // (represented using a -1 or the string "unlimited").
+    private boolean allowUnlimited = false;
+
+
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Set the lower limit.
+     *
+     * @param lowerLimit
+     *          The new lower limit (must be >= 0).
+     * @throws IllegalArgumentException
+     *           If a negative lower limit was specified or the lower limit is
+     *           greater than the upper limit.
+     */
+    public final void setLowerLimit(int lowerLimit)
+        throws IllegalArgumentException {
+      if (lowerLimit < 0) {
+        throw new IllegalArgumentException("Negative lower limit");
+      }
+      if (upperLimit != null && lowerLimit > upperLimit) {
+        throw new IllegalArgumentException(
+            "Lower limit greater than upper limit");
+      }
+      this.lowerLimit = lowerLimit;
+    }
+
+
+
+    /**
+     * Set the upper limit.
+     *
+     * @param upperLimit
+     *          The new upper limit or <code>null</code> if there is no upper
+     *          limit.
+     */
+    public final void setUpperLimit(Integer upperLimit) {
+      if (upperLimit != null) {
+        if (upperLimit < 0) {
+          throw new IllegalArgumentException("Negative lower limit");
+        }
+        if (lowerLimit > upperLimit) {
+          throw new IllegalArgumentException(
+              "Lower limit greater than upper limit");
+        }
+      }
+      this.upperLimit = upperLimit;
+    }
+
+
+
+    /**
+     * Specify whether or not this property definition will allow unlimited
+     * values (default is false).
+     *
+     * @param allowUnlimited
+     *          <code>true</code> if the property will allow unlimited values,
+     *          or <code>false</code> otherwise.
+     */
+    public final void setAllowUnlimited(boolean allowUnlimited) {
+      this.allowUnlimited = allowUnlimited;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected IntegerPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<Integer> defaultBehavior) {
+      return new IntegerPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior, lowerLimit, upperLimit, allowUnlimited);
+    }
+
+  }
+
+
+
+  /**
+   * Create an integer property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new integer property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private IntegerPropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<Integer> defaultBehavior, int lowerLimit,
+      Integer upperLimit, boolean allowUnlimited) {
+    super(d, Integer.class, propertyName, options, adminAction,
+        defaultBehavior);
+    this.lowerLimit = lowerLimit;
+    this.upperLimit = upperLimit;
+    this.allowUnlimited = allowUnlimited;
+  }
+
+
+
+  /**
+   * Get the lower limit.
+   *
+   * @return Returns the lower limit.
+   */
+  public int getLowerLimit() {
+    return lowerLimit;
+  }
+
+
+
+  /**
+   * Get the upper limit.
+   *
+   * @return Returns the upper limit or <code>null</code> if there is no upper
+   *         limit.
+   */
+  public Integer getUpperLimit() {
+    return upperLimit;
+  }
+
+
+
+  /**
+   * Gets the optional unit synopsis of this integer property
+   * definition in the default locale.
+   *
+   * @return Returns the unit synopsis of this integer property
+   *         definition in the default locale, or <code>null</code>
+   *         if there is no unit synopsis.
+   */
+  public Message getUnitSynopsis() {
+    return getUnitSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional unit synopsis of this integer property
+   * definition in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the unit synopsis of this integer property
+   *         definition in the specified locale, or <code>null</code>
+   *         if there is no unit synopsis.
+   */
+  public Message getUnitSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + getName() + ".syntax.integer.unit-synopsis";
+    try {
+      return resource.getMessage(getManagedObjectDefinition(),
+          property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Determine whether this property allows unlimited values.
+   *
+   * @return Returns <code>true</code> if this this property allows unlimited
+   *         values.
+   */
+  public boolean isAllowUnlimited() {
+    return allowUnlimited;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(Integer value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    if (!allowUnlimited && value < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+
+    // unlimited allowed
+    } else if (value >= 0 && value < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+
+    if ((upperLimit != null) && (value > upperLimit)) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(Integer value)
+          throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // Make sure that we correctly encode negative values as "unlimited".
+    if (allowUnlimited) {
+      if (value < 0) {
+        return UNLIMITED;
+      }
+    }
+
+    return value.toString();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Integer decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    if (allowUnlimited) {
+      if (value.trim().equalsIgnoreCase(UNLIMITED)) {
+        return -1;
+      }
+    }
+
+    Integer i;
+    try {
+      i = Integer.valueOf(value);
+    } catch (NumberFormatException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    try {
+      validateValue(i);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    return i;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitInteger(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, Integer value, P p) {
+    return v.visitInteger(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    super.toString(builder);
+
+    builder.append(" lowerLimit=");
+    builder.append(lowerLimit);
+
+    if (upperLimit != null) {
+      builder.append(" upperLimit=");
+      builder.append(upperLimit);
+    }
+
+    builder.append(" allowUnlimited=");
+    builder.append(allowUnlimited);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(Integer o1, Integer o2) {
+    return o1.compareTo(o2);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/LDAPProfile.java b/opendj-admin/src/main/java/org/opends/server/admin/LDAPProfile.java
new file mode 100644
index 0000000..027b896
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/LDAPProfile.java
@@ -0,0 +1,423 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+
+
+/**
+ * This class is used to map configuration elements to their LDAP
+ * schema names.
+ * <p>
+ * It is possible to augment the core LDAP profile with additional
+ * profile mappings at run-time using instances of {@link Wrapper}.
+ * This is useful for unit tests which need to add and remove mock
+ * components.
+ */
+public final class LDAPProfile {
+
+  /**
+   * LDAP profile wrappers can be used to provide temporary LDAP
+   * profile information for components which do not have LDAP profile
+   * property files. These components are typically "mock" components
+   * used in unit-tests.
+   */
+  public static abstract class Wrapper {
+
+    /**
+     * Default constructor.
+     */
+    protected Wrapper() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * Get the name of the LDAP attribute associated with the
+     * specified property definition.
+     * <p>
+     * The default implementation of this method is to return
+     * <code>null</code>.
+     *
+     * @param d
+     *          The managed object definition.
+     * @param pd
+     *          The property definition.
+     * @return Returns the name of the LDAP attribute associated with
+     *         the specified property definition, or <code>null</code>
+     *         if the property definition is not handled by this LDAP
+     *         profile wrapper.
+     */
+    public String getAttributeName(AbstractManagedObjectDefinition<?, ?> d,
+        PropertyDefinition<?> pd) {
+      return null;
+    }
+
+
+
+    /**
+     * Gets the LDAP RDN attribute type for child entries of an
+     * instantiable relation.
+     * <p>
+     * The default implementation of this method is to return
+     * <code>null</code>.
+     *
+     * @param r
+     *          The instantiable relation.
+     * @return Returns the LDAP RDN attribute type for child entries
+     *         of an instantiable relation, or <code>null</code> if
+     *         the instantiable relation is not handled by this LDAP
+     *         profile wrapper.
+     */
+    public String getRelationChildRDNType(
+        InstantiableRelationDefinition<?, ?> r) {
+      return null;
+    }
+
+
+
+    /**
+     * Gets the LDAP RDN attribute type for child entries of an set
+     * relation.
+     * <p>
+     * The default implementation of this method is to return
+     * <code>null</code>.
+     *
+     * @param r
+     *          The set relation.
+     * @return Returns the LDAP RDN attribute type for child entries of
+     *         an set relation, or <code>null</code> if the set relation
+     *         is not handled by this LDAP profile wrapper.
+     */
+    public String getRelationChildRDNType(SetRelationDefinition<?, ?> r)
+    {
+      return null;
+    }
+
+
+
+    /**
+     * Get the principle object class associated with the specified
+     * definition.
+     * <p>
+     * The default implementation of this method is to return
+     * <code>null</code>.
+     *
+     * @param d
+     *          The managed object definition.
+     * @return Returns the principle object class associated with the
+     *         specified definition, or <code>null</code> if the
+     *         managed object definition is not handled by this LDAP
+     *         profile wrapper.
+     */
+    public String getObjectClass(AbstractManagedObjectDefinition<?, ?> d) {
+      return null;
+    }
+
+
+
+    /**
+     * Get an LDAP RDN sequence associatied with a relation.
+     * <p>
+     * The default implementation of this method is to return
+     * <code>null</code>.
+     *
+     * @param r
+     *          The relation.
+     * @return Returns the LDAP RDN sequence associatied with a
+     *         relation, or <code>null</code> if the relation is not
+     *         handled by this LDAP profile wrapper.
+     */
+    public String getRelationRDNSequence(RelationDefinition<?, ?> r) {
+      return null;
+    }
+  }
+
+  // The singleton instance.
+  private static final LDAPProfile INSTANCE = new LDAPProfile();
+
+
+
+  /**
+   * Get the global LDAP profile instance.
+   *
+   * @return Returns the global LDAP profile instance.
+   */
+  public static LDAPProfile getInstance() {
+    return INSTANCE;
+  }
+
+  // The list of profile wrappers.
+  private final LinkedList<Wrapper> profiles = new LinkedList<Wrapper>();;
+
+  // The LDAP profile property table.
+  private final ManagedObjectDefinitionResource resource =
+    ManagedObjectDefinitionResource.createForProfile("ldap");
+
+
+
+  // Prevent construction.
+  private LDAPProfile() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Get the name of the LDAP attribute associated with the specified
+   * property definition.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param pd
+   *          The property definition.
+   * @return Returns the name of the LDAP attribute associated with
+   *         the specified property definition.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public String getAttributeName(AbstractManagedObjectDefinition<?, ?> d,
+      PropertyDefinition<?> pd) throws MissingResourceException {
+    for (Wrapper profile : profiles) {
+      String attributeName = profile.getAttributeName(d, pd);
+      if (attributeName != null) {
+        return attributeName;
+      }
+    }
+    return resource.getString(d, "attribute." + pd.getName());
+  }
+
+
+
+  /**
+   * Gets the LDAP RDN attribute type for child entries of an
+   * instantiable relation.
+   *
+   * @param r
+   *          The instantiable relation.
+   * @return Returns the LDAP RDN attribute type for child entries of
+   *         an instantiable relation.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public String getRelationChildRDNType(
+      InstantiableRelationDefinition<?, ?> r) throws MissingResourceException {
+    if (r.getNamingPropertyDefinition() != null) {
+      // Use the attribute associated with the naming property.
+      return getAttributeName(r.getChildDefinition(), r
+          .getNamingPropertyDefinition());
+    } else {
+      for (Wrapper profile : profiles) {
+        String rdnType = profile.getRelationChildRDNType(r);
+        if (rdnType != null) {
+          return rdnType;
+        }
+      }
+      return resource.getString(r.getParentDefinition(), "naming-attribute."
+          + r.getName());
+    }
+  }
+
+
+
+  /**
+   * Gets the LDAP object classes associated with an instantiable or set
+   * relation branch. The branch is the parent entry of child managed
+   * objects.
+   *
+   * @param r
+   *          The instantiable or set relation.
+   * @return Returns the LDAP object classes associated with an
+   *         instantiable or set relation branch.
+   */
+  public List<String> getRelationObjectClasses(
+      RelationDefinition<?, ?> r) {
+    return Arrays.asList(new String[] { "top", "ds-cfg-branch" });
+  }
+
+
+
+  /**
+   * Gets the LDAP RDN attribute type for child entries of an set
+   * relation.
+   *
+   * @param r
+   *          The set relation.
+   * @return Returns the LDAP RDN attribute type for child entries of an
+   *         set relation.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public String getRelationChildRDNType(SetRelationDefinition<?, ?> r)
+      throws MissingResourceException
+  {
+    for (Wrapper profile : profiles)
+    {
+      String rdnType = profile.getRelationChildRDNType(r);
+      if (rdnType != null)
+      {
+        return rdnType;
+      }
+    }
+    return resource.getString(r.getParentDefinition(),
+        "naming-attribute." + r.getName());
+  }
+
+
+
+  /**
+   * Get the principle object class associated with the specified
+   * definition.
+   *
+   * @param d
+   *          The managed object definition.
+   * @return Returns the principle object class associated with the
+   *         specified definition.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public String getObjectClass(AbstractManagedObjectDefinition<?, ?> d)
+      throws MissingResourceException {
+    if (d.isTop()) {
+      return "top";
+    }
+
+    for (Wrapper profile : profiles) {
+      String objectClass = profile.getObjectClass(d);
+      if (objectClass != null) {
+        return objectClass;
+      }
+    }
+    return resource.getString(d, "objectclass");
+  }
+
+
+
+  /**
+   * Get all the object classes associated with the specified
+   * definition.
+   * <p>
+   * The returned list is ordered such that the uppermost object
+   * classes appear first (e.g. top).
+   *
+   * @param d
+   *          The managed object definition.
+   * @return Returns all the object classes associated with the
+   *         specified definition.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public List<String> getObjectClasses(AbstractManagedObjectDefinition<?, ?> d)
+      throws MissingResourceException {
+    LinkedList<String> objectClasses = new LinkedList<String>();
+    Set<String> s = new HashSet<String>();
+
+    // Add the object classes from the parent hierarchy.
+    while (d != null) {
+      String oc = getObjectClass(d);
+      if (!s.contains(oc)) {
+        objectClasses.addFirst(oc);
+        s.add(oc);
+      }
+      d = d.getParent();
+    }
+
+    if (!s.contains("top")) {
+      objectClasses.addFirst("top");
+    }
+
+    return objectClasses;
+  }
+
+
+
+  /**
+   * Get an LDAP RDN sequence associatied with a relation.
+   *
+   * @param r
+   *          The relation.
+   * @return Returns the LDAP RDN sequence associatied with a
+   *         relation.
+   * @throws MissingResourceException
+   *           If the LDAP profile properties file associated with the
+   *           provided managed object definition could not be loaded.
+   */
+  public String getRelationRDNSequence(RelationDefinition<?, ?> r)
+      throws MissingResourceException {
+    for (Wrapper profile : profiles) {
+      String rdnSequence = profile.getRelationRDNSequence(r);
+      if (rdnSequence != null) {
+        return rdnSequence;
+      }
+    }
+    return resource.getString(r.getParentDefinition(), "rdn." + r.getName());
+  }
+
+
+
+  /**
+   * Removes the last LDAP profile wrapper added using
+   * {@link #pushWrapper(org.opends.server.admin.LDAPProfile.Wrapper)}.
+   *
+   * @throws NoSuchElementException
+   *           If there are no LDAP profile wrappers.
+   */
+  public void popWrapper() throws NoSuchElementException {
+    profiles.removeFirst();
+  }
+
+
+
+  /**
+   * Decorates the core LDAP profile with the provided LDAP profile
+   * wrapper. All profile requests will be directed to the provided
+   * wrapper before being forwarded onto the core profile if the
+   * request could not be satisfied.
+   *
+   * @param wrapper
+   *          The LDAP profile wrapper.
+   */
+  public void pushWrapper(Wrapper wrapper) {
+    profiles.addFirst(wrapper);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectAlreadyExistsException.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectAlreadyExistsException.java
new file mode 100644
index 0000000..c658baa
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectAlreadyExistsException.java
@@ -0,0 +1,56 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * A managed object could not be created because there is an existing
+ * managed object with the same name.
+ */
+public final class ManagedObjectAlreadyExistsException extends
+    OperationsException {
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = -2344653674171609366L;
+
+
+
+  /**
+   * Create a managed object already exists exception.
+   */
+  public ManagedObjectAlreadyExistsException() {
+    super(ERR_MANAGED_OBJECT_ALREADY_EXISTS_EXCEPTION.get());
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinition.java
new file mode 100644
index 0000000..5c79718
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinition.java
@@ -0,0 +1,105 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.server.ServerManagedObject;
+
+
+
+/**
+ * Defines the structure of a managed object which can be
+ * instantiated.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          definition represents.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          definition represents.
+ */
+public abstract class ManagedObjectDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends AbstractManagedObjectDefinition<C, S> {
+
+  /**
+   * Create a new managed object definition.
+   *
+   * @param name
+   *          The name of the definition.
+   * @param parent
+   *          The parent definition, or <code>null</code> if there
+   *          is no parent.
+   */
+  protected ManagedObjectDefinition(String name,
+      AbstractManagedObjectDefinition<? super C, ? super S> parent) {
+    super(name, parent);
+  }
+
+
+
+  /**
+   * Creates a client configuration view of the provided managed
+   * object. Modifications made to the underlying managed object will
+   * be reflected in the client configuration view and vice versa.
+   *
+   * @param managedObject
+   *          The managed object.
+   * @return Returns a client configuration view of the provided
+   *         managed object.
+   */
+  public abstract C createClientConfiguration(
+      ManagedObject<? extends C> managedObject);
+
+
+
+  /**
+   * Creates a server configuration view of the provided server
+   * managed object.
+   *
+   * @param managedObject
+   *          The server managed object.
+   * @return Returns a server configuration view of the provided
+   *         server managed object.
+   */
+  public abstract S createServerConfiguration(
+      ServerManagedObject<? extends S> managedObject);
+
+
+
+  /**
+   * Gets the server configuration class instance associated with this
+   * managed object definition.
+   *
+   * @return Returns the server configuration class instance
+   *         associated with this managed object definition.
+   */
+  public abstract Class<S> getServerConfigurationClass();
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java
new file mode 100644
index 0000000..1d621a8
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java
@@ -0,0 +1,346 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+
+
+/**
+ * A class for retrieving internationalized resource properties
+ * associated with a managed object definition.
+ * <p>
+ * I18N resource properties are not available for the
+ * {@link TopCfgDefn}.
+ */
+public final class ManagedObjectDefinitionI18NResource {
+
+  // Application-wide set of instances.
+  private static final Map<String, ManagedObjectDefinitionI18NResource>
+    INSTANCES = new HashMap<String, ManagedObjectDefinitionI18NResource>();
+
+
+
+  /**
+   * Gets the internationalized resource instance which can be used to
+   * retrieve the localized descriptions for the managed objects and
+   * their associated properties and relations.
+   *
+   * @return Returns the I18N resource instance.
+   */
+  public static ManagedObjectDefinitionI18NResource getInstance() {
+    return getInstance("admin.messages");
+  }
+
+
+
+  /**
+   * Gets the internationalized resource instance for the named
+   * profile.
+   *
+   * @param profile
+   *          The name of the profile.
+   * @return Returns the I18N resource instance for the named profile.
+   */
+  public static ManagedObjectDefinitionI18NResource getInstanceForProfile(
+      String profile) {
+    return getInstance("admin.profiles." + profile);
+  }
+
+
+
+  // Get a resource instance creating it if necessary.
+  private synchronized static ManagedObjectDefinitionI18NResource getInstance(
+      String prefix) {
+    ManagedObjectDefinitionI18NResource instance = INSTANCES
+        .get(prefix);
+
+    if (instance == null) {
+      instance = new ManagedObjectDefinitionI18NResource(prefix);
+      INSTANCES.put(prefix, instance);
+    }
+
+    return instance;
+  }
+
+
+
+  // Mapping from definition to locale-based resource bundle.
+  private final Map<AbstractManagedObjectDefinition<?, ?>,
+    Map<Locale, ResourceBundle>> resources;
+
+
+
+  // The resource name prefix.
+  private final String prefix;
+
+
+
+  // Private constructor.
+  private ManagedObjectDefinitionI18NResource(String prefix) {
+    this.resources = new HashMap<AbstractManagedObjectDefinition<?, ?>,
+      Map<Locale, ResourceBundle>>();
+    this.prefix = prefix;
+  }
+
+
+
+  /**
+   * Get the internationalized message associated with the specified
+   * key in the default locale.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param key
+   *          The resource key.
+   * @return Returns the internationalized message associated with the
+   *         specified key in the default locale.
+   * @throws MissingResourceException
+   *           If the key was not found.
+   * @throws UnsupportedOperationException
+   *           If the provided managed object definition was the
+   *           {@link TopCfgDefn}.
+   */
+  public LocalizableMessage getLocalizableMessage(AbstractManagedObjectDefinition<?, ?> d, String key)
+      throws MissingResourceException, UnsupportedOperationException {
+    return getLocalizableMessage(d, key, Locale.getDefault(), (String[]) null);
+  }
+
+
+
+  /**
+   * Get the internationalized message associated with the specified
+   * key and locale.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param key
+   *          The resource key.
+   * @param locale
+   *          The locale.
+   * @return Returns the internationalized message associated with the
+   *         specified key and locale.
+   * @throws MissingResourceException
+   *           If the key was not found.
+   * @throws UnsupportedOperationException
+   *           If the provided managed object definition was the
+   *           {@link TopCfgDefn}.
+   */
+  public LocalizableMessage getLocalizableMessage(AbstractManagedObjectDefinition<?, ?> d,
+      String key, Locale locale) throws MissingResourceException,
+      UnsupportedOperationException {
+    return getLocalizableMessage(d, key, locale, (String[]) null);
+  }
+
+
+
+  /**
+   * Get the parameterized internationalized message associated with
+   * the specified key and locale.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param key
+   *          The resource key.
+   * @param locale
+   *          The locale.
+   * @param args
+   *          Arguments that should be inserted into the retrieved
+   *          message.
+   * @return Returns the internationalized message associated with the
+   *         specified key and locale.
+   * @throws MissingResourceException
+   *           If the key was not found.
+   * @throws UnsupportedOperationException
+   *           If the provided managed object definition was the
+   *           {@link TopCfgDefn}.
+   */
+  public LocalizableMessage getLocalizableMessage(AbstractManagedObjectDefinition<?, ?> d,
+      String key, Locale locale, String... args)
+      throws MissingResourceException, UnsupportedOperationException {
+    ResourceBundle resource = getResourceBundle(d, locale);
+
+    // TODO: use message framework directly
+    if (args == null) {
+      return LocalizableMessage.raw(resource.getString(key));
+    } else {
+      return LocalizableMessage.raw(resource.getString(key), (Object[]) args);
+    }
+  }
+
+
+
+  /**
+   * Get the parameterized internationalized message associated with
+   * the specified key in the default locale.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param key
+   *          The resource key.
+   * @param args
+   *          Arguments that should be inserted into the retrieved
+   *          message.
+   * @return Returns the internationalized message associated with the
+   *         specified key in the default locale.
+   * @throws MissingResourceException
+   *           If the key was not found.
+   * @throws UnsupportedOperationException
+   *           If the provided managed object definition was the
+   *           {@link TopCfgDefn}.
+   */
+  public LocalizableMessage getLocalizableMessage(AbstractManagedObjectDefinition<?, ?> d,
+      String key, String... args) throws MissingResourceException,
+      UnsupportedOperationException {
+    return getLocalizableMessage(d, key, Locale.getDefault(), args);
+  }
+
+
+
+  /**
+   * Forcefully removes any resource bundles associated with the
+   * provided definition and using the default locale.
+   * <p>
+   * This method is intended for internal testing only.
+   *
+   * @param d
+   *          The managed object definition.
+   */
+  synchronized void removeResourceBundle(
+      AbstractManagedObjectDefinition<?, ?> d) {
+    removeResourceBundle(d, Locale.getDefault());
+  }
+
+
+
+  /**
+   * Forcefully removes any resource bundles associated with the
+   * provided definition and locale.
+   * <p>
+   * This method is intended for internal testing only.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param locale
+   *          The locale.
+   */
+  synchronized void removeResourceBundle(
+      AbstractManagedObjectDefinition<?, ?> d, Locale locale) {
+    // Get the locale resource mapping.
+    Map<Locale, ResourceBundle> map = resources.get(d);
+    if (map != null) {
+      map.remove(locale);
+    }
+  }
+
+
+
+  /**
+   * Forcefully adds the provided resource bundle to this I18N
+   * resource for the default locale.
+   * <p>
+   * This method is intended for internal testing only.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param resoureBundle
+   *          The resource bundle to be used.
+   */
+  synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d,
+      ResourceBundle resoureBundle) {
+    setResourceBundle(d, Locale.getDefault(), resoureBundle);
+  }
+
+
+
+  /**
+   * Forcefully adds the provided resource bundle to this I18N
+   * resource.
+   * <p>
+   * This method is intended for internal testing only.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param locale
+   *          The locale.
+   * @param resoureBundle
+   *          The resource bundle to be used.
+   */
+  synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d,
+      Locale locale, ResourceBundle resoureBundle) {
+    // First get the locale-resource mapping, creating it if
+    // necessary.
+    Map<Locale, ResourceBundle> map = resources.get(d);
+    if (map == null) {
+      map = new HashMap<Locale, ResourceBundle>();
+      resources.put(d, map);
+    }
+
+    // Add the resource bundle.
+    map.put(locale, resoureBundle);
+  }
+
+
+
+  // Retrieve the resource bundle associated with a managed object and
+  // locale, lazily loading it if necessary.
+  private synchronized ResourceBundle getResourceBundle(
+      AbstractManagedObjectDefinition<?, ?> d, Locale locale)
+      throws MissingResourceException, UnsupportedOperationException {
+    if (d.isTop()) {
+      throw new UnsupportedOperationException(
+          "I18n resources are not available for the "
+              + "Top configuration definition");
+    }
+
+    // First get the locale-resource mapping, creating it if
+    // necessary.
+    Map<Locale, ResourceBundle> map = resources.get(d);
+    if (map == null) {
+      map = new HashMap<Locale, ResourceBundle>();
+      resources.put(d, map);
+    }
+
+    // Now get the resource based on the locale, loading it if
+    // necessary.
+    ResourceBundle resourceBundle = map.get(locale);
+    if (resourceBundle == null) {
+      String baseName = prefix + "." + d.getClass().getName();
+      resourceBundle = ResourceBundle.getBundle(baseName, locale,
+          ClassLoaderProvider.getInstance().getClassLoader());
+      map.put(locale, resourceBundle);
+    }
+
+    return resourceBundle;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionResource.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionResource.java
new file mode 100644
index 0000000..3dcebcf
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectDefinitionResource.java
@@ -0,0 +1,156 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+
+
+
+/**
+ * A class for retrieving non-internationalized resource properties
+ * associated with a managed object definition.
+ * <p>
+ * Resource properties are not available for the {@link TopCfgDefn}.
+ */
+public final class ManagedObjectDefinitionResource {
+
+  // Mapping from definition to property tables.
+  private final Map<AbstractManagedObjectDefinition<?, ?>,
+      Properties> properties;
+
+  // The resource name prefix.
+  private final String prefix;
+
+
+
+  /**
+   * Creates a new resource instance for the named profile.
+   *
+   * @param profile
+   *          The name of the profile.
+   * @return Returns the resource instance for the named profile.
+   */
+  public static ManagedObjectDefinitionResource createForProfile(
+      String profile) {
+    return new ManagedObjectDefinitionResource("admin.profiles."
+        + profile);
+  }
+
+
+
+  // Private constructor.
+  private ManagedObjectDefinitionResource(String prefix) {
+    this.properties =
+      new HashMap<AbstractManagedObjectDefinition<?, ?>, Properties>();
+    this.prefix = prefix;
+  }
+
+
+
+  /**
+   * Get the resource value associated with the specified key.
+   *
+   * @param d
+   *          The managed object definition.
+   * @param key
+   *          The resource key.
+   * @return Returns the resource value associated with the specified
+   *         key.
+   * @throws MissingResourceException
+   *           If the key was not found.
+   * @throws UnsupportedOperationException
+   *           If the provided managed object definition was the
+   *           {@link TopCfgDefn}.
+   */
+  public String getString(AbstractManagedObjectDefinition<?, ?> d, String key)
+      throws MissingResourceException, UnsupportedOperationException {
+    if (d.isTop()) {
+      throw new UnsupportedOperationException(
+          "Profile resources are not available for the "
+              + "Top configuration definition");
+    }
+
+    Properties p = getProperties(d);
+    String result = p.getProperty(key);
+
+    if (result == null) {
+      String baseName = prefix + "." + d.getClass().getName();
+      String path = baseName.replace('.', '/') + ".properties";
+
+      throw new MissingResourceException("Can't find resource "
+          + path + ", key " + key, baseName, key);
+    }
+
+    return result;
+  }
+
+
+
+  // Retrieve the properties table associated with a managed object,
+  // lazily loading it if necessary.
+  private synchronized Properties getProperties(
+      AbstractManagedObjectDefinition<?, ?> d)
+      throws MissingResourceException {
+    Properties p = properties.get(d);
+
+    if (p == null) {
+      // Load the resource file.
+      String baseName = prefix + "." + d.getClass().getName();
+      String path = baseName.replace('.', '/') + ".properties";
+      InputStream stream = ClassLoaderProvider.getInstance()
+          .getClassLoader().getResourceAsStream(path);
+
+      if (stream == null) {
+        throw new MissingResourceException("Can't find resource "
+            + path, baseName, "");
+      }
+
+      p = new Properties();
+      try {
+        p.load(new BufferedInputStream(stream));
+      } catch (IOException e) {
+        throw new MissingResourceException("Can't load resource "
+            + path + " due to IO exception: " + e.getMessage(),
+            baseName, "");
+      }
+
+      // Cache the resource.
+      properties.put(d, p);
+    }
+
+    return p;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectNotFoundException.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectNotFoundException.java
new file mode 100644
index 0000000..bfcf0c1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectNotFoundException.java
@@ -0,0 +1,67 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * The requested managed object could not be located.
+ */
+public class ManagedObjectNotFoundException extends OperationsException {
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = -477551786551892978L;
+
+
+
+  /**
+   * Create a managed object not found exception.
+   */
+  public ManagedObjectNotFoundException() {
+    super(ERR_MANAGED_OBJECT_NOT_FOUND_EXCEPTION.get());
+  }
+
+
+
+  /**
+   * Create a managed object not found exception with the specified
+   * cause.
+   *
+   * @param cause
+   *          The cause of this exception.
+   */
+  public ManagedObjectNotFoundException(Throwable cause) {
+    super(ERR_MANAGED_OBJECT_NOT_FOUND_EXCEPTION.get(), cause);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectOption.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectOption.java
new file mode 100644
index 0000000..cda3666
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectOption.java
@@ -0,0 +1,49 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * This enumeration contains various options that can be associated
+ * with managed object definitions.
+ */
+public enum ManagedObjectOption {
+  /**
+   * Use this option to identify managed object types which should be
+   * considered as advanced and should not be exposed by default in
+   * client applications.
+   */
+  ADVANCED,
+
+  /**
+   * Use this option to identify managed object types which must not
+   * be directly exposed in client applications.
+   */
+  HIDDEN;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPath.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPath.java
new file mode 100644
index 0000000..91529d6
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPath.java
@@ -0,0 +1,1445 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ *      Portions Copyright 2011 ForgeRock AS
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.opends.server.admin.std.client.RootCfgClient;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.*;
+
+
+/**
+ * A path which can be used to determine the location of a managed
+ * object instance.
+ * <p>
+ * A path is made up of zero or more elements each of which represents
+ * a managed object. Managed objects are arranged hierarchically with
+ * the root configuration being the top-most managed object. Elements
+ * are ordered such that the root configuration managed object is the
+ * first element and subsequent elements representing managed objects
+ * further down the hierarchy.
+ * <p>
+ * A path can be encoded into a string representation using the
+ * {@link #toString()} and {@link #toString(StringBuilder)} methods.
+ * Conversely, this string representation can be parsed using the
+ * {@link #valueOf(String)} method.
+ * <p>
+ * The string representation of a managed object path is similar in
+ * principle to a UNIX file-system path and is defined as follows:
+ * <ul>
+ * <li>the root element is represented by the string <code>/</code>
+ * <li>subordinate elements are arranged in big-endian order
+ * separated by a forward slash <code>/</code> character
+ * <li>an element representing a managed object associated with a
+ * one-to-one (singleton) or one-to-zero-or-one (optional) relation
+ * has the form <code>relation=</code><i>relation</i>
+ * <code>[+type=</code><i>definition</i><code>]</code>, where
+ * <i>relation</i> is the name of the relation and <i>definition</i>
+ * is the name of the referenced managed object's definition if
+ * required (usually this is implied by the relation itself)
+ * <li>an element representing a managed object associated with a
+ * one-to-many (instantiable) relation has the form
+ * <code>relation=</code><i>relation</i><code>[+type=</code>
+ * <i>definition</i><code>]</code><code>+name=</code><i>name</i>,
+ * where <i>relation</i> is the name of the relation and
+ * <i>definition</i> is the name of the referenced managed object's
+ * definition if required (usually this is implied by the relation
+ * itself), and <i>name</i> is the name of the managed object
+ * instance
+ * <li>an element representing a managed object associated with a
+ * one-to-many (set) relation has the form
+ * <code>relation=</code><i>relation</i><code>[+type=</code>
+ * <i>definition</i><code>]</code>,
+ * where <i>relation</i> is the name of the relation and
+ * <i>definition</i> is the name of the referenced managed object's
+ * definition.
+ * </ul>
+ * The following path string representation identifies a connection
+ * handler instance (note that the <code>type</code> is not
+ * specified indicating that the path identifies a connection handler
+ * called <i>my handler</i> which can be any type of connection
+ * handler):
+ *
+ * <pre>
+ *  /relation=connection-handler+name=my handler
+ * </pre>
+ *
+ * If the identified connection handler must be an LDAP connection
+ * handler then the above path should include the <code>type</code>:
+ *
+ * <pre>
+ *  /relation=connection-handler+type=ldap-connection-handler+name=my handler
+ * </pre>
+ *
+ * The final example identifies the global configuration:
+ *
+ * <pre>
+ *  /relation=global-configuration
+ * </pre>
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          path references.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          path references.
+ */
+public final class ManagedObjectPath<C extends ConfigurationClient,
+    S extends Configuration> {
+
+  /**
+   * A serialize which is used to generate the toDN representation.
+   */
+  private static final class DNSerializer implements
+      ManagedObjectPathSerializer {
+
+    // The current DN.
+    private DN dn;
+
+    // The LDAP profile.
+    private final LDAPProfile profile;
+
+
+
+    // Create a new DN builder.
+    private DNSerializer() {
+      this.dn = DN.nullDN();
+      this.profile = LDAPProfile.getInstance();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+    void appendManagedObjectPathElement(
+        InstantiableRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d, String name) {
+      // Add the RDN sequence representing the relation.
+      appendManagedObjectPathElement(r);
+
+      // Now add the single RDN representing the named instance.
+      String type = profile.getRelationChildRDNType(r);
+      AttributeType atype = DirectoryServer.getAttributeType(
+          type.toLowerCase(), true);
+      AttributeValue avalue = AttributeValues.create(atype, name);
+      dn = dn.concat(RDN.create(atype, avalue));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+    void appendManagedObjectPathElement(
+        SetRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      // Add the RDN sequence representing the relation.
+      appendManagedObjectPathElement(r);
+
+      // Now add the single RDN representing the instance.
+      String type = profile.getRelationChildRDNType(r);
+      AttributeType atype = DirectoryServer.getAttributeType(
+          type.toLowerCase(), true);
+      AttributeValue avalue = AttributeValues.create(atype, d.getName());
+      dn = dn.concat(RDN.create(atype, avalue));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+    void appendManagedObjectPathElement(
+        OptionalRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      // Add the RDN sequence representing the relation.
+      appendManagedObjectPathElement(r);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+    void appendManagedObjectPathElement(
+        SingletonRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      // Add the RDN sequence representing the relation.
+      appendManagedObjectPathElement(r);
+    }
+
+
+
+    // Appends the RDN sequence representing the provided relation.
+    private void appendManagedObjectPathElement(RelationDefinition<?, ?> r) {
+      // Add the RDN sequence representing the relation.
+      try {
+        DN localName = DN.decode(profile.getRelationRDNSequence(r));
+        dn = dn.concat(localName);
+      } catch (DirectoryException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+
+
+    // Gets the serialized DN value.
+    private DN toDN() {
+      return dn;
+    }
+  }
+
+
+
+  /**
+   * Abstract path element.
+   */
+  private static abstract class Element<C extends ConfigurationClient,
+      S extends Configuration> {
+
+    // The type of managed object referenced by this element.
+    private final AbstractManagedObjectDefinition<C, S> definition;
+
+
+
+    /**
+     * Protected constructor.
+     *
+     * @param definition
+     *          The type of managed object referenced by this element.
+     */
+    protected Element(AbstractManagedObjectDefinition<C, S> definition) {
+      this.definition = definition;
+    }
+
+
+
+    /**
+     * Get the managed object definition associated with this element.
+     *
+     * @return Returns the managed object definition associated with
+     *         this element.
+     */
+    public final AbstractManagedObjectDefinition<C, S>
+        getManagedObjectDefinition() {
+      return definition;
+    }
+
+
+
+    /**
+     * Get the name associated with this element if applicable.
+     *
+     * @return Returns the name associated with this element if
+     *         applicable.
+     */
+    public String getName() {
+      return null;
+    }
+
+
+
+    /**
+     * Get the relation definition associated with this element.
+     *
+     * @return Returns the relation definition associated with this
+     *         element.
+     */
+    public abstract RelationDefinition<? super C, ? super S>
+        getRelationDefinition();
+
+
+
+    /**
+     * Serialize this path element using the provided serialization
+     * strategy.
+     *
+     * @param serializer
+     *          The managed object path serialization strategy.
+     */
+    public abstract void serialize(ManagedObjectPathSerializer serializer);
+  }
+
+
+
+  /**
+   * A path element representing an instantiable managed object.
+   */
+  private static final class InstantiableElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final <C extends ConfigurationClient,
+        S extends Configuration>
+        InstantiableElement<C, S> create(
+        InstantiableRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d, String name) {
+      return new InstantiableElement<C, S>(r, d, name);
+    }
+
+    // The name of the managed object.
+    private final String name;
+
+    // The instantiable relation.
+    private final InstantiableRelationDefinition<? super C, ? super S> r;
+
+
+
+    // Private constructor.
+    private InstantiableElement(
+        InstantiableRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d, String name) {
+      super(d);
+      this.r = r;
+      this.name = name;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getName() {
+      return name;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public InstantiableRelationDefinition<? super C, ? super S>
+        getRelationDefinition() {
+      return r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void serialize(ManagedObjectPathSerializer serializer) {
+      serializer.appendManagedObjectPathElement(r,
+          getManagedObjectDefinition(), name);
+    }
+  }
+
+
+
+  /**
+   * A path element representing an optional managed object.
+   */
+  private static final class OptionalElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final <C extends ConfigurationClient,
+        S extends Configuration> OptionalElement<C, S> create(
+        OptionalRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      return new OptionalElement<C, S>(r, d);
+    }
+
+    // The optional relation.
+    private final OptionalRelationDefinition<? super C, ? super S> r;
+
+
+
+    // Private constructor.
+    private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      super(d);
+      this.r = r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public OptionalRelationDefinition<? super C, ? super S>
+        getRelationDefinition() {
+      return r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void serialize(ManagedObjectPathSerializer serializer) {
+      serializer
+          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
+    }
+  }
+
+
+
+  /**
+   * A path element representing an set managed object.
+   */
+  private static final class SetElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final <C extends ConfigurationClient,
+        S extends Configuration>
+        SetElement<C, S> create(
+        SetRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      return new SetElement<C, S>(r, d);
+    }
+
+    // The set relation.
+    private final SetRelationDefinition<? super C, ? super S> r;
+
+
+
+    // Private constructor.
+    private SetElement(
+        SetRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      super(d);
+      this.r = r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SetRelationDefinition<? super C, ? super S>
+        getRelationDefinition() {
+      return r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void serialize(ManagedObjectPathSerializer serializer) {
+      serializer.appendManagedObjectPathElement(r,
+          getManagedObjectDefinition());
+    }
+  }
+
+
+
+  /**
+   * A path element representing a singleton managed object.
+   */
+  private static final class SingletonElement
+      <C extends ConfigurationClient, S extends Configuration>
+      extends Element<C, S> {
+
+    // Factory method.
+    private static final <C extends ConfigurationClient,
+        S extends Configuration> SingletonElement<C, S> create(
+        SingletonRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      return new SingletonElement<C, S>(r, d);
+    }
+
+    // The singleton relation.
+    private final SingletonRelationDefinition<? super C, ? super S> r;
+
+
+
+    // Private constructor.
+    private SingletonElement(
+        SingletonRelationDefinition<? super C, ? super S> r,
+        AbstractManagedObjectDefinition<C, S> d) {
+      super(d);
+      this.r = r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SingletonRelationDefinition<? super C, ? super S>
+        getRelationDefinition() {
+      return r;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void serialize(ManagedObjectPathSerializer serializer) {
+      serializer
+          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
+    }
+  }
+
+
+
+  /**
+   * A serialize which is used to generate the toString
+   * representation.
+   */
+  private static final class StringSerializer implements
+      ManagedObjectPathSerializer {
+
+    // Serialize to this string builder.
+    private final StringBuilder builder;
+
+
+
+    // Private constructor.
+    private StringSerializer(StringBuilder builder) {
+      this.builder = builder;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        InstantiableRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d, String name) {
+      serializeElement(r, d);
+
+      // Be careful to escape any forward slashes in the name.
+      builder.append("+name=");
+      builder.append(name.replace("/", "//"));
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        OptionalRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d) {
+      serializeElement(r, d);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        SingletonRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d) {
+      serializeElement(r, d);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <M extends ConfigurationClient, N extends Configuration>
+        void appendManagedObjectPathElement(
+        SetRelationDefinition<? super M, ? super N> r,
+        AbstractManagedObjectDefinition<M, N> d) {
+      serializeElement(r, d);
+    }
+
+
+
+    // Common element serialization.
+    private <M, N> void serializeElement(RelationDefinition<?, ?> r,
+        AbstractManagedObjectDefinition<?, ?> d) {
+      // Always specify the relation name.
+      builder.append("/relation=");
+      builder.append(r.getName());
+
+      // Only specify the type if it is a sub-type of the relation's
+      // type.
+      if (r.getChildDefinition() != d) {
+        builder.append("+type=");
+        builder.append(d.getName());
+      }
+    }
+  }
+
+  // Single instance of a root path.
+  private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH =
+      new ManagedObjectPath<RootCfgClient, RootCfg>(
+      new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance());
+
+  // A regular expression used to parse path elements.
+  private static final Pattern PE_REGEXP = Pattern
+      .compile("^\\s*relation=\\s*([^+]+)\\s*"
+          + "(\\+\\s*type=\\s*([^+]+)\\s*)?"
+          + "(\\+\\s*name=\\s*([^+]+)\\s*)?$");
+
+
+
+  /**
+   * Creates a new managed object path representing the configuration
+   * root.
+   *
+   * @return Returns a new managed object path representing the
+   *         configuration root.
+   */
+  public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() {
+    return EMPTY_PATH;
+  }
+
+
+
+  /**
+   * Returns a managed object path holding the value of the specified
+   * string.
+   *
+   * @param s
+   *          The string to be parsed.
+   * @return Returns a managed object path holding the value of the
+   *         specified string.
+   * @throws IllegalArgumentException
+   *           If the string could not be parsed.
+   */
+  public static ManagedObjectPath<?, ?> valueOf(String s)
+      throws IllegalArgumentException {
+    String ns = s.trim();
+
+    // Check for root special case.
+    if (ns.equals("/")) {
+      return EMPTY_PATH;
+    }
+
+    // Parse the elements.
+    LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>();
+    Element<?, ?> lastElement = null;
+    AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn
+        .getInstance();
+
+    if (!ns.startsWith("/")) {
+      throw new IllegalArgumentException("Invalid path \"" + ns
+          + "\": must begin with a \"/\"");
+    }
+
+    int start = 1;
+    while (true) {
+      // Get the next path element.
+      int end;
+      for (end = start; end < ns.length(); end++) {
+        char c = ns.charAt(end);
+        if (c == '/') {
+          if (end == (ns.length() - 1)) {
+            throw new IllegalArgumentException("Invalid path \"" + ns
+                + "\": must not end with a trailing \"/\"");
+          }
+
+          if (ns.charAt(end + 1) == '/') {
+            // Found an escaped forward slash.
+            end++;
+          } else {
+            // Found the end of this path element.
+            break;
+          }
+        }
+      }
+
+      // Get the next element.
+      String es = ns.substring(start, end);
+
+      Matcher m = PE_REGEXP.matcher(es);
+      if (!m.matches()) {
+        throw new IllegalArgumentException("Invalid path element \"" + es
+            + "\" in path \"" + ns + "\"");
+      }
+
+      // Mandatory.
+      String relation = m.group(1);
+
+      // Optional.
+      String type = m.group(3);
+
+      // Mandatory if relation is instantiable.
+      String name = m.group(5);
+
+      // Get the relation definition.
+      RelationDefinition<?, ?> r;
+      try {
+        r = definition.getRelationDefinition(relation);
+      } catch (IllegalArgumentException e) {
+        throw new IllegalArgumentException("Invalid path element \"" + es
+            + "\" in path \"" + ns + "\": unknown relation \"" + relation
+            + "\"");
+      }
+
+      // Append the next element.
+      lastElement = createElement(r, ns, es, type, name);
+      elements.add(lastElement);
+      definition = lastElement.getManagedObjectDefinition();
+
+      // Update start to point to the beginning of the next element.
+      if (end < ns.length()) {
+        // Skip to the beginning of the next element
+        start = end + 1;
+      } else {
+        // We reached the end of the string.
+        break;
+      }
+    }
+
+    // Construct the new path.
+    return create(elements, lastElement);
+  }
+
+
+
+  // Factory method required in order to allow generic wild-card
+  // construction of new paths.
+  private static <C extends ConfigurationClient, S extends Configuration>
+      ManagedObjectPath<C, S> create(
+      LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) {
+    return new ManagedObjectPath<C, S>(elements, lastElement
+        .getRelationDefinition(), lastElement.getManagedObjectDefinition());
+  }
+
+
+
+  // Decode an element.
+  private static <C extends ConfigurationClient, S extends Configuration>
+      Element<? extends C, ? extends S> createElement(
+      RelationDefinition<C, S> r, String path, String element, String type,
+      String name) {
+    // First determine the managed object definition.
+    AbstractManagedObjectDefinition<? extends C, ? extends S> d = null;
+
+    if (type != null) {
+      for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r
+          .getChildDefinition().getAllChildren()) {
+        if (child.getName().equals(type)) {
+          d = child;
+          break;
+        }
+      }
+
+      if (d == null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path + "\": unknown sub-type \"" + type + "\"");
+      }
+    } else {
+      d = r.getChildDefinition();
+    }
+
+    if (r instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<C, S> ir =
+        (InstantiableRelationDefinition<C, S>) r;
+
+      if (name == null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": no instance name for instantiable relation");
+      }
+
+      return InstantiableElement.create(ir, d, name);
+    } else if (r instanceof SetRelationDefinition) {
+      SetRelationDefinition<C, S> ir = (SetRelationDefinition<C, S>) r;
+
+      if (name != null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": instance name specified for set relation");
+      }
+
+      return SetElement.create(ir, d);
+    } else if (r instanceof OptionalRelationDefinition) {
+      OptionalRelationDefinition<C, S> or =
+        (OptionalRelationDefinition<C, S>) r;
+
+      if (name != null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": instance name specified for optional relation");
+      }
+
+      return OptionalElement.create(or, d);
+    } else if (r instanceof SingletonRelationDefinition) {
+      SingletonRelationDefinition<C, S> sr =
+        (SingletonRelationDefinition<C, S>) r;
+
+      if (name != null) {
+        throw new IllegalArgumentException("Invalid path element \"" + element
+            + "\" in path \"" + path
+            + "\": instance name specified for singleton relation");
+      }
+
+      return SingletonElement.create(sr, d);
+    } else {
+      throw new IllegalArgumentException("Invalid path element \"" + element
+          + "\" in path \"" + path + "\": unsupported relation type");
+    }
+  }
+
+  // The managed object definition in this path.
+  private final AbstractManagedObjectDefinition<C, S> d;
+
+  // The list of path elements in this path.
+  private final List<Element<?, ?>> elements;
+
+  // The last relation definition in this path.
+  private final RelationDefinition<? super C, ? super S> r;
+
+
+
+  // Private constructor.
+  private ManagedObjectPath(LinkedList<Element<?, ?>> elements,
+      RelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d) {
+    this.elements = Collections.unmodifiableList(elements);
+    this.r = r;
+    this.d = d;
+  }
+
+
+
+  /**
+   * Creates a new managed object path which has the same structure as
+   * this path except that the final path element is associated with
+   * the specified managed object definition.
+   *
+   * @param <CC>
+   *          The type of client managed object configuration that
+   *          this path will reference.
+   * @param <SS>
+   *          The type of server managed object configuration that
+   *          this path will reference.
+   * @param nd
+   *          The new managed object definition.
+   * @return Returns a new managed object path which has the same
+   *         structure as this path except that the final path element
+   *         is associated with the specified managed object
+   *         definition.
+   */
+  @SuppressWarnings("unchecked")
+  public <CC extends C, SS extends S> ManagedObjectPath<CC, SS> asSubType(
+      AbstractManagedObjectDefinition<CC, SS> nd) {
+    if (r instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<? super C, ? super S> ir =
+        (InstantiableRelationDefinition<? super C, ? super S>) r;
+      if (elements.size() == 0) {
+        return parent().child(ir, nd, "null");
+      } else {
+        return parent().child(ir, nd,
+            elements.get(elements.size() - 1).getName());
+      }
+    } else if (r instanceof SetRelationDefinition) {
+      SetRelationDefinition<? super C, ? super S> sr =
+        (SetRelationDefinition<? super C, ? super S>) r;
+      return parent().child(sr, nd);
+    } else if (r instanceof OptionalRelationDefinition) {
+      OptionalRelationDefinition<? super C, ? super S> or =
+        (OptionalRelationDefinition<? super C, ? super S>) r;
+      return parent().child(or, nd);
+    } else {
+      SingletonRelationDefinition<? super C, ? super S> sr =
+        (SingletonRelationDefinition<? super C, ? super S>) r;
+      return parent().child(sr, nd);
+    }
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path having the specified managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The instantiable relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @param name
+   *          The relative name of the child managed object.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   * @throws IllegalArgumentException
+   *           If the provided name is empty or blank.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      InstantiableRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d, String name)
+      throws IllegalArgumentException {
+    if (name.trim().length() == 0) {
+      throw new IllegalArgumentException(
+          "Empty or blank managed object names are not allowed");
+    }
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new InstantiableElement<M, N>(r, d, name));
+    return new ManagedObjectPath<M, N>(celements, r, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The instantiable relation referencing the child.
+   * @param name
+   *          The relative name of the child managed object.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   * @throws IllegalArgumentException
+   *           If the provided name is empty or blank.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      InstantiableRelationDefinition<M, N> r, String name)
+      throws IllegalArgumentException {
+    return child(r, r.getChildDefinition(), name);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path having the specified managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The optional relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      OptionalRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d) {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new OptionalElement<M, N>(r, d));
+    return new ManagedObjectPath<M, N>(celements, r, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The optional relation referencing the child.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(OptionalRelationDefinition<M, N> r) {
+    return child(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path having the specified managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The singleton relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      SingletonRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d) {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new SingletonElement<M, N>(r, d));
+    return new ManagedObjectPath<M, N>(celements, r, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The singleton relation referencing the child.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(SingletonRelationDefinition<M, N> r) {
+    return child(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path having the specified managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The set relation referencing the child.
+   * @param d
+   *          The managed object definition associated with the child
+   *          (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   * @throws IllegalArgumentException
+   *           If the provided name is empty or blank.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      SetRelationDefinition<? super M, ? super N> r,
+      AbstractManagedObjectDefinition<M, N> d)
+      throws IllegalArgumentException {
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements);
+    celements.add(new SetElement<M, N>(r, d));
+    return new ManagedObjectPath<M, N>(celements, r, d);
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided parent
+   * path having the managed object definition indicated by
+   * <code>name</code>.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          path references.
+   * @param r
+   *          The set relation referencing the child.
+   * @param name
+   *          The name of the managed object definition associated with
+   *          the child (must be a sub-type of the relation).
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   * @throws IllegalArgumentException
+   *           If the provided name is empty or blank or specifies a
+   *           managed object definition which is not a sub-type of the
+   *           relation's child definition.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<? extends M, ? extends N> child(
+      SetRelationDefinition<M, N> r,
+      String name)
+      throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<M, N> d = r.getChildDefinition();
+    return child(r, d.getChild(name));
+  }
+
+
+
+  /**
+   * Creates a new child managed object path beneath the provided
+   * parent path using the relation's child managed object definition.
+   *
+   * @param <M>
+   *          The type of client managed object configuration that the
+   *          child path references.
+   * @param <N>
+   *          The type of server managed object configuration that the
+   *          child path references.
+   * @param r
+   *          The set relation referencing the child.
+   * @return Returns a new child managed object path beneath the
+   *         provided parent path.
+   * @throws IllegalArgumentException
+   *           If the provided name is empty or blank.
+   */
+  public <M extends ConfigurationClient, N extends Configuration>
+      ManagedObjectPath<M, N> child(
+      SetRelationDefinition<M, N> r)
+      throws IllegalArgumentException {
+    return child(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    } else if (obj instanceof ManagedObjectPath) {
+      ManagedObjectPath<?, ?> other = (ManagedObjectPath<?, ?>) obj;
+      return toString().equals(other.toString());
+    } else {
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Get the definition of the managed object referred to by this
+   * path.
+   * <p>
+   * When the path is empty, the {@link RootCfgDefn} is returned.
+   *
+   * @return Returns the definition of the managed object referred to
+   *         by this path, or the {@link RootCfgDefn} if the path is
+   *         empty.
+   */
+  public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
+    return d;
+  }
+
+
+
+  /**
+   * Get the name of the managed object referred to by this path if
+   * applicable.
+   * <p>
+   * If there path does not refer to an instantiable managed object
+   * <code>null</code> is returned.
+   *
+   * @return Returns the name of the managed object referred to by
+   *         this path, or <code>null</code> if the managed object
+   *         does not have a name.
+   */
+  public String getName() {
+    if (elements.isEmpty()) {
+      return null;
+    } else {
+      return elements.get(elements.size() - 1).getName();
+    }
+  }
+
+
+
+  /**
+   * Get the relation definition of the managed object referred to by
+   * this path.
+   * <p>
+   * When the path is empty, the <code>null</code> is returned.
+   *
+   * @return Returns the relation definition of the managed object
+   *         referred to by this path, or the <code>null</code> if
+   *         the path is empty.
+   */
+  public RelationDefinition<? super C, ? super S> getRelationDefinition() {
+    return r;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int hashCode() {
+    return toString().hashCode();
+  }
+
+
+
+  /**
+   * Determine whether or not this path contains any path elements.
+   *
+   * @return Returns <code>true</code> if this path does not contain
+   *         any path elements.
+   */
+  public boolean isEmpty() {
+    return elements.isEmpty();
+  }
+
+
+
+  /**
+   * Determines whether this managed object path references the same
+   * location as the provided managed object path.
+   * <p>
+   * This method differs from <code>equals</code> in that it ignores
+   * sub-type definitions.
+   *
+   * @param other
+   *          The managed object path to be compared.
+   * @return Returns <code>true</code> if this managed object path
+   *         references the same location as the provided managed
+   *         object path.
+   */
+  public boolean matches(ManagedObjectPath<?, ?> other) {
+    DN thisDN = toDN();
+    DN otherDN = other.toDN();
+    return thisDN.equals(otherDN);
+  }
+
+
+
+  /**
+   * Creates a new parent managed object path representing the
+   * immediate parent of this path. This method is a short-hand for
+   * <code>parent(1)</code>.
+   *
+   * @return Returns a new parent managed object path representing the
+   *         immediate parent of this path.
+   * @throws IllegalArgumentException
+   *           If this path does not have a parent (i.e. it is the
+   *           empty path).
+   */
+  public ManagedObjectPath<?, ?> parent() throws IllegalArgumentException {
+    return parent(1);
+  }
+
+
+
+  /**
+   * Creates a new parent managed object path the specified number of
+   * path elements above this path.
+   *
+   * @param offset
+   *          The number of path elements (0 - means no offset, 1
+   *          means the parent, and 2 means the grand-parent).
+   * @return Returns a new parent managed object path the specified
+   *         number of path elements above this path.
+   * @throws IllegalArgumentException
+   *           If the offset is less than 0, or greater than the
+   *           number of path elements in this path.
+   */
+  public ManagedObjectPath<?, ?> parent(int offset)
+      throws IllegalArgumentException {
+    if (offset < 0) {
+      throw new IllegalArgumentException("Negative offset");
+    }
+
+    if (offset > elements.size()) {
+      throw new IllegalArgumentException(
+          "Offset is greater than the number of path elements");
+    }
+
+    // An offset of 0 leaves the path unchanged.
+    if (offset == 0) {
+      return this;
+    }
+
+    // Return the empty path if the parent has zero elements.
+    if (elements.size() == offset) {
+      return emptyPath();
+    }
+
+    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
+        elements.subList(0, elements.size() - offset));
+    return create(celements, celements.getLast());
+  }
+
+
+
+  /**
+   * Creates a new managed object path which has the same structure as
+   * this path except that the final path element is renamed. The
+   * final path element must comprise of an instantiable relation.
+   *
+   * @param newName
+   *          The new name of the final path element.
+   * @return Returns a new managed object path which has the same
+   *         structure as this path except that the final path element
+   *         is renamed.
+   * @throws IllegalStateException
+   *           If this managed object path is empty or if its final
+   *           path element does not comprise of an instantiable
+   *           relation.
+   */
+  @SuppressWarnings("unchecked")
+  public ManagedObjectPath<C, S> rename(String newName)
+      throws IllegalStateException {
+    if (elements.size() == 0) {
+      throw new IllegalStateException("Cannot rename an empty path");
+    }
+
+    if (r instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<? super C, ? super S> ir =
+        (InstantiableRelationDefinition<? super C, ? super S>) r;
+      return parent().child(ir, d, newName);
+    } else {
+      throw new IllegalStateException("Not an instantiable relation");
+    }
+  }
+
+
+
+  /**
+   * Serialize this managed object path using the provided
+   * serialization strategy.
+   * <p>
+   * The path elements will be passed to the serializer in big-endian
+   * order: starting from the root element and proceeding down to the
+   * leaf.
+   *
+   * @param serializer
+   *          The managed object path serialization strategy.
+   */
+  public void serialize(ManagedObjectPathSerializer serializer) {
+    for (Element<?, ?> element : elements) {
+      element.serialize(serializer);
+    }
+  }
+
+
+
+  /**
+   * Get the number of path elements in this managed object path.
+   *
+   * @return Returns the number of path elements (0 - means no offset,
+   *         1 means the parent, and 2 means the grand-parent).
+   */
+  public int size() {
+    return elements.size();
+  }
+
+
+
+  /**
+   * Creates a DN representation of this managed object path.
+   *
+   * @return Returns a DN representation of this managed object path.
+   */
+  public DN toDN() {
+    // Use a simple serializer to create the contents.
+    DNSerializer serializer = new DNSerializer();
+    serialize(serializer);
+    return serializer.toDN();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    toString(builder);
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Appends a string representation of this managed object path to
+   * the provided string builder.
+   *
+   * @param builder
+   *          Append the string representation to this builder.
+   * @see #toString()
+   */
+  public void toString(final StringBuilder builder) {
+    if (isEmpty()) {
+      // Special treatment of root configuration paths.
+      builder.append('/');
+    } else {
+      // Use a simple serializer to create the contents.
+      ManagedObjectPathSerializer serializer = new StringSerializer(builder);
+      serialize(serializer);
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPathSerializer.java b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPathSerializer.java
new file mode 100644
index 0000000..405f975
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPathSerializer.java
@@ -0,0 +1,135 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * A strategy for serializing managed object paths.
+ * <p>
+ * This interface provides a generic means for serializing managed
+ * object paths into application specific forms. For example, a JNDI
+ * client would use this interface to construct <code>LdapName</code>
+ * objects from a path. Similarly, on the server side, a serialization
+ * strategy is used to construct <code>DN</code> instances from a
+ * path.
+ * <p>
+ * During serialization the serializer is invoked for each element in
+ * the managed object path in big-endian order, starting from the root
+ * and proceeding down to the leaf element.
+ */
+public interface ManagedObjectPathSerializer {
+
+  /**
+   * Append a managed object path element identified by an
+   * instantiable relation and an instance name.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
+   * @param r
+   *          The instantiable relation.
+   * @param d
+   *          The managed object definition.
+   * @param name
+   *          The instance name.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      InstantiableRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d, String name);
+
+
+
+  /**
+   * Append a managed object path element identified by an optional
+   * relation.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
+   * @param r
+   *          The optional relation.
+   * @param d
+   *          The managed object definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      OptionalRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d);
+
+
+
+  /**
+   * Append a managed object path element identified by a singleton
+   * relation.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
+   * @param r
+   *          The singleton relation.
+   * @param d
+   *          The managed object definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      SingletonRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d);
+
+
+
+  /**
+   * Append a managed object path element identified by a
+   * set relation.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this path element references.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this path element references.
+   * @param r
+   *          The set relation.
+   * @param d
+   *          The managed object definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+      SetRelationDefinition<? super C, ? super S> r,
+      AbstractManagedObjectDefinition<C, S> d);
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/OperationsException.java b/opendj-admin/src/main/java/org/opends/server/admin/OperationsException.java
new file mode 100644
index 0000000..e8827ae
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/OperationsException.java
@@ -0,0 +1,72 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * Exceptions thrown as a result of errors that occurred when reading,
+ * listing, and modifying managed objects.
+ */
+public abstract class OperationsException extends AdminException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 6329910102360262187L;
+
+
+
+  /**
+   * Create an operations exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected OperationsException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an operations exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected OperationsException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/OptionalRelationDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/OptionalRelationDefinition.java
new file mode 100644
index 0000000..2b5bf54
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/OptionalRelationDefinition.java
@@ -0,0 +1,183 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * A managed object composite relationship definition which represents
+ * a composition of an optional single managed object (i.e. the
+ * referenced managed object may or may not be present).
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          relation definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          relation definition refers to.
+ */
+public final class OptionalRelationDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends RelationDefinition<C, S> {
+
+  /**
+   * An interface for incrementally constructing optional relation
+   * definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this relation definition refers to.
+   */
+  public static final class Builder
+      <C extends ConfigurationClient, S extends Configuration>
+      extends AbstractBuilder<C, S, OptionalRelationDefinition<C, S>> {
+
+    // The optional default managed object associated with this
+    // optional relation.
+    private DefaultManagedObject<? extends C, ? extends S>
+      defaultManagedObject = null;
+
+
+
+    /**
+     * Creates a new builder which can be used to incrementally build
+     * an optional relation definition.
+     *
+     * @param pd
+     *          The parent managed object definition.
+     * @param name
+     *          The name of the relation.
+     * @param cd
+     *          The child managed object definition.
+     */
+    public Builder(AbstractManagedObjectDefinition<?, ?> pd, String name,
+        AbstractManagedObjectDefinition<C, S> cd) {
+      super(pd, name, cd);
+    }
+
+
+
+    /**
+     * Sets the optional default managed object associated with this
+     * optional relation definition.
+     *
+     * @param defaultManagedObject
+     *          The default managed object or <code>null</code> if
+     *          there is no default managed object defined for this
+     *          relation definition.
+     */
+    public void setDefaultManagedObject(
+        DefaultManagedObject<? extends C, ? extends S> defaultManagedObject) {
+      this.defaultManagedObject = defaultManagedObject;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected OptionalRelationDefinition<C, S> buildInstance(
+        Common<C, S> common) {
+      return new OptionalRelationDefinition<C, S>(common, defaultManagedObject);
+    }
+
+  }
+
+
+
+  // The optional default managed object associated with this
+  // optional relation.
+  private final DefaultManagedObject<? extends C, ? extends S>
+    defaultManagedObject;
+
+
+
+  // Private constructor.
+  private OptionalRelationDefinition(Common<C, S> common,
+      DefaultManagedObject<? extends C, ? extends S> defaultManagedObject) {
+    super(common);
+    this.defaultManagedObject = defaultManagedObject;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p) {
+    return v.visitOptional(this, p);
+  }
+
+
+
+  /**
+   * Gets the optional default managed object associated with this
+   * optional relation definition.
+   *
+   * @return Returns the default managed object or <code>null</code>
+   *         if there is no default managed object defined for this
+   *         relation definition.
+   */
+  public DefaultManagedObject<? extends C, ? extends S>
+      getDefaultManagedObject() {
+    return defaultManagedObject;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    builder.append("name=");
+    builder.append(getName());
+    builder.append(" type=optional parent=");
+    builder.append(getParentDefinition().getName());
+    builder.append(" child=");
+    builder.append(getChildDefinition().getName());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception {
+    if (defaultManagedObject != null) {
+      defaultManagedObject.initialize();
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinition.java
new file mode 100644
index 0000000..3f92b5e
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinition.java
@@ -0,0 +1,675 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static com.forgerock.opendj.util.Validator.*;
+
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+import org.forgerock.i18n.LocalizableMessage;
+
+
+
+/**
+ * An interface for querying generic property definition features.
+ * <p>
+ * Property definitions are analogous to ConfigAttributes in the
+ * current model and will play a similar role. Eventually these will
+ * replace them.
+ * <p>
+ * Implementations <b>must</b> take care to implement the various
+ * comparison methods.
+ *
+ * @param <T>
+ *          The data-type of values of the property.
+ */
+public abstract class PropertyDefinition<T> implements Comparator<T>,
+    Comparable<PropertyDefinition<?>> {
+
+  /**
+   * An interface for incrementally constructing property definitions.
+   *
+   * @param <T>
+   *          The data-type of values of the property.
+   * @param <D>
+   *          The type of property definition constructed by this
+   *          builder.
+   */
+  protected abstract static class AbstractBuilder
+      <T, D extends PropertyDefinition<T>> {
+
+    // The administrator action.
+    private AdministratorAction adminAction;
+
+    // The default behavior provider.
+    private DefaultBehaviorProvider<T> defaultBehavior;
+
+    // The abstract managed object
+    private final AbstractManagedObjectDefinition<?, ?> definition;
+
+    // The options applicable to this definition.
+    private final EnumSet<PropertyOption> options;
+
+    // The name of this property definition.
+    private final String propertyName;
+
+
+
+    /**
+     * Create a property definition builder.
+     *
+     * @param d
+     *          The managed object definition associated with this
+     *          property definition.
+     * @param propertyName
+     *          The property name.
+     */
+    protected AbstractBuilder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      this.definition = d;
+      this.propertyName = propertyName;
+      this.options = EnumSet.noneOf(PropertyOption.class);
+      this.adminAction = new AdministratorAction(AdministratorAction.Type.NONE,
+          d, propertyName);
+      this.defaultBehavior = new UndefinedDefaultBehaviorProvider<T>();
+    }
+
+
+
+    /**
+     * Construct a property definition based on the properties of this
+     * builder.
+     *
+     * @return The new property definition.
+     */
+    public final D getInstance() {
+      return buildInstance(definition, propertyName, options, adminAction,
+          defaultBehavior);
+    }
+
+
+
+    /**
+     * Set the administrator action.
+     *
+     * @param adminAction
+     *          The administrator action.
+     */
+    public final void setAdministratorAction(AdministratorAction adminAction) {
+      ensureNotNull(adminAction);
+      this.adminAction = adminAction;
+    }
+
+
+
+    /**
+     * Set the default behavior provider.
+     *
+     * @param defaultBehavior
+     *          The default behavior provider.
+     */
+    public final void setDefaultBehaviorProvider(
+        DefaultBehaviorProvider<T> defaultBehavior) {
+      ensureNotNull(defaultBehavior);
+      this.defaultBehavior = defaultBehavior;
+    }
+
+
+
+    /**
+     * Add a property definition option.
+     *
+     * @param option
+     *          The property option.
+     */
+    public final void setOption(PropertyOption option) {
+      ensureNotNull(option);
+      options.add(option);
+    }
+
+
+
+    /**
+     * Build a property definition based on the properties of this
+     * builder.
+     *
+     * @param d
+     *          The managed object definition associated with this
+     *          property definition.
+     * @param propertyName
+     *          The property name.
+     * @param options
+     *          Options applicable to this definition.
+     * @param adminAction
+     *          The administrator action.
+     * @param defaultBehavior
+     *          The default behavior provider.
+     * @return The new property definition.
+     */
+    protected abstract D buildInstance(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName, EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<T> defaultBehavior);
+  }
+
+  // The administrator action.
+  private final AdministratorAction adminAction;
+
+  // The default behavior provider.
+  private final DefaultBehaviorProvider<T> defaultBehavior;
+
+  // The abstract managed object
+  private final AbstractManagedObjectDefinition<?, ?> definition;
+
+  // Options applicable to this definition.
+  private final Set<PropertyOption> options;
+
+  // The property name.
+  private final String propertyName;
+
+  // The property value class.
+  private final Class<T> theClass;
+
+
+
+  /**
+   * Create a property definition.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param theClass
+   *          The property value class.
+   * @param propertyName
+   *          The property name.
+   * @param options
+   *          Options applicable to this definition.
+   * @param adminAction
+   *          The administrator action.
+   * @param defaultBehavior
+   *          The default behavior provider.
+   */
+  protected PropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
+      Class<T> theClass, String propertyName, EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<T> defaultBehavior) {
+    ensureNotNull(d, theClass, propertyName);
+    ensureNotNull(options, adminAction, defaultBehavior);
+
+    this.definition = d;
+    this.theClass = theClass;
+    this.propertyName = propertyName;
+    this.options = EnumSet.copyOf(options);
+    this.adminAction = adminAction;
+    this.defaultBehavior = defaultBehavior;
+  }
+
+
+
+  /**
+   * Apply a visitor to this property definition.
+   *
+   * @param <R>
+   *          The return type of the visitor's methods.
+   * @param <P>
+   *          The type of the additional parameters to the visitor's
+   *          methods.
+   * @param v
+   *          The property definition visitor.
+   * @param p
+   *          Optional additional visitor parameter.
+   * @return Returns a result as specified by the visitor.
+   */
+  public abstract <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p);
+
+
+
+  /**
+   * Apply a visitor to a property value associated with this property
+   * definition.
+   *
+   * @param <R>
+   *          The return type of the visitor's methods.
+   * @param <P>
+   *          The type of the additional parameters to the visitor's
+   *          methods.
+   * @param v
+   *          The property value visitor.
+   * @param value
+   *          The property value.
+   * @param p
+   *          Optional additional visitor parameter.
+   * @return Returns a result as specified by the visitor.
+   */
+  public abstract <R, P> R accept(PropertyValueVisitor<R, P> v, T value, P p);
+
+
+
+  /**
+   * Cast the provided value to the type associated with this property
+   * definition.
+   * <p>
+   * This method only casts the object to the required type; it does
+   * not validate the value once it has been cast. Subsequent
+   * validation should be performed using the method
+   * {@link #validateValue(Object)}.
+   * <p>
+   * This method guarantees the following expression is always
+   * <code>true</code>:
+   *
+   * <pre>
+   *  PropertyDefinition d;
+   *  x == d.cast(x);
+   * </pre>
+   *
+   * @param object
+   *          The property value to be cast (can be <code>null</code>).
+   * @return Returns the property value cast to the correct type.
+   * @throws ClassCastException
+   *           If the provided property value did not have the correct
+   *           type.
+   */
+  public final T castValue(Object object) throws ClassCastException {
+    return theClass.cast(object);
+  }
+
+
+
+  /**
+   * Compares two property values for order. Returns a negative
+   * integer, zero, or a positive integer as the first argument is
+   * less than, equal to, or greater than the second.
+   * <p>
+   * This default implementation normalizes both values using
+   * {@link #normalizeValue(Object)} and then performs a
+   * case-sensitive string comparison.
+   *
+   * @param o1
+   *          the first object to be compared.
+   * @param o2
+   *          the second object to be compared.
+   * @return a negative integer, zero, or a positive integer as the
+   *         first argument is less than, equal to, or greater than
+   *         the second.
+   */
+  public int compare(T o1, T o2) {
+    ensureNotNull(o1, o2);
+
+    String s1 = normalizeValue(o1);
+    String s2 = normalizeValue(o2);
+
+    return s1.compareTo(s2);
+  }
+
+
+
+  /**
+   * Compares this property definition with the specified property
+   * definition for order. Returns a negative integer, zero, or a
+   * positive integer if this property definition is less than, equal
+   * to, or greater than the specified property definition.
+   * <p>
+   * The ordering must be determined first from the property name and
+   * then base on the underlying value type.
+   *
+   * @param o
+   *          The reference property definition with which to compare.
+   * @return Returns a negative integer, zero, or a positive integer
+   *         if this property definition is less than, equal to, or
+   *         greater than the specified property definition.
+   */
+  public final int compareTo(PropertyDefinition<?> o) {
+    int rc = propertyName.compareTo(o.propertyName);
+    if (rc == 0) {
+      rc = theClass.getName().compareTo(o.theClass.getName());
+    }
+    return rc;
+  }
+
+
+
+  /**
+   * Parse and validate a string representation of a property value.
+   *
+   * @param value
+   *          The property string value (must not be <code>null</code>).
+   * @return Returns the decoded property value.
+   * @throws IllegalPropertyValueStringException
+   *           If the property value string is invalid.
+   */
+  public abstract T decodeValue(String value)
+      throws IllegalPropertyValueStringException;
+
+
+
+  /**
+   * Encode the provided property value into its string
+   * representation.
+   * <p>
+   * This default implementation simply returns invokes the
+   * {@link Object#toString()} method on the provided value.
+   *
+   * @param value
+   *          The property value (must not be <code>null</code>).
+   * @return Returns the encoded property string value.
+   * @throws IllegalPropertyValueException
+   *           If the property value is invalid.
+   */
+  public String encodeValue(T value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    return value.toString();
+  }
+
+
+
+  /**
+   * Indicates whether some other object is &quot;equal to&quot; this
+   * property definition. This method must obey the general contract
+   * of <tt>Object.equals(Object)</tt>. Additionally, this method
+   * can return <tt>true</tt> <i>only</i> if the specified Object
+   * is also a property definition and it has the same name, as
+   * returned by {@link #getName()}, and also is deemed to be
+   * &quot;compatible&quot; with this property definition.
+   * Compatibility means that the two property definitions share the
+   * same underlying value type and provide similar comparator
+   * implementations.
+   *
+   * @param o
+   *          The reference object with which to compare.
+   * @return Returns <code>true</code> only if the specified object
+   *         is also a property definition and it has the same name
+   *         and is compatible with this property definition.
+   * @see java.lang.Object#equals(java.lang.Object)
+   * @see java.lang.Object#hashCode()
+   */
+  @Override
+  public final boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    } else if (o instanceof PropertyDefinition) {
+      PropertyDefinition<?> other = (PropertyDefinition<?>) o;
+      if (propertyName.equals(other.propertyName)) {
+        if (theClass.equals(other.theClass)) {
+          return true;
+        }
+      }
+      return false;
+    } else {
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Get the administrator action associated with this property
+   * definition. The administrator action describes any action which
+   * the administrator must perform in order for changes to this
+   * property to take effect.
+   *
+   * @return Returns the administrator action associated with this
+   *         property definition.
+   */
+  public final AdministratorAction getAdministratorAction() {
+    return adminAction;
+  }
+
+
+
+  /**
+   * Get the default behavior provider associated with this property
+   * definition.
+   *
+   * @return Returns the default behavior provider associated with
+   *         this property definition.
+   */
+  public final DefaultBehaviorProvider<T> getDefaultBehaviorProvider() {
+    return defaultBehavior;
+  }
+
+
+
+  /**
+   * Gets the optional description of this property definition in the
+   * default locale.
+   *
+   * @return Returns the description of this property definition in
+   *         the default locale, or <code>null</code> if there is no
+   *         description.
+   */
+  public final LocalizableMessage getDescription() {
+    return getDescription(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional description of this property definition in the
+   * specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the description of this property definition in
+   *         the specified locale, or <code>null</code> if there is
+   *         no description.
+   */
+  public final LocalizableMessage getDescription(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + propertyName + ".description";
+    try {
+      return resource.getLocalizableMessage(definition, property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Gets the managed object definition associated with this property
+   * definition.
+   *
+   * @return Returns the managed object definition associated with
+   *         this property definition.
+   */
+  public final AbstractManagedObjectDefinition<?, ?>
+      getManagedObjectDefinition() {
+    return definition;
+  }
+
+
+
+  /**
+   * Get the name of the property.
+   *
+   * @return Returns the name of the property.
+   */
+  public final String getName() {
+    return propertyName;
+  }
+
+
+
+  /**
+   * Gets the synopsis of this property definition in the default
+   * locale.
+   *
+   * @return Returns the synopsis of this property definition in the
+   *         default locale.
+   */
+  public final LocalizableMessage getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this property definition in the specified
+   * locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this property definition in the
+   *         specified locale.
+   */
+  public final LocalizableMessage getSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + propertyName + ".synopsis";
+    return resource.getLocalizableMessage(definition, property, locale);
+  }
+
+
+
+  /**
+   * Returns a hash code value for this property definition. The hash
+   * code should be derived from the property name and the type of
+   * values handled by this property definition.
+   *
+   * @return Returns the hash code value for this property definition.
+   */
+  @Override
+  public final int hashCode() {
+    int rc = 17 + propertyName.hashCode();
+    return 37 * rc + theClass.hashCode();
+  }
+
+
+
+  /**
+   * Check if the specified option is set for this property
+   * definition.
+   *
+   * @param option
+   *          The option to test.
+   * @return Returns <code>true</code> if the option is set, or
+   *         <code>false</code> otherwise.
+   */
+  public final boolean hasOption(PropertyOption option) {
+    return options.contains(option);
+  }
+
+
+
+  /**
+   * Get a normalized string representation of a property value. This
+   * can then be used for comparisons and for generating hash-codes.
+   * <p>
+   * This method may throw an exception if the provided value is
+   * invalid. However, applications should not assume that
+   * implementations of this method will always validate a value. This
+   * task is the responsibility of {@link #validateValue(Object)}.
+   * <p>
+   * This default implementation simply returns the string
+   * representation of the provided value. Sub-classes might want to
+   * override this method if this behavior is insufficient (for
+   * example, a string property definition might strip white-space and
+   * convert characters to lower-case).
+   *
+   * @param value
+   *          The property value to be normalized.
+   * @return Returns the normalized property value.
+   * @throws IllegalPropertyValueException
+   *           If the property value is invalid.
+   */
+  public String normalizeValue(T value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    return encodeValue(value);
+  }
+
+
+
+  /**
+   * Returns a string representation of this property definition.
+   *
+   * @return Returns a string representation of this property
+   *         definition.
+   * @see Object#toString()
+   */
+  @Override
+  public final String toString() {
+    StringBuilder builder = new StringBuilder();
+    toString(builder);
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Append a string representation of the property definition to the
+   * provided string builder.
+   * <p>
+   * This simple implementation just outputs the propertyName of the
+   * property definition. Sub-classes should override this method to
+   * provide more complete string representations.
+   *
+   * @param builder
+   *          The string builder where the string representation
+   *          should be appended.
+   */
+  public void toString(StringBuilder builder) {
+    builder.append(propertyName);
+  }
+
+
+
+  /**
+   * Determine if the provided property value is valid according to
+   * this property definition.
+   *
+   * @param value
+   *          The property value (must not be <code>null</code>).
+   * @throws IllegalPropertyValueException
+   *           If the property value is invalid.
+   */
+  public abstract void validateValue(T value)
+      throws IllegalPropertyValueException;
+
+
+
+  /**
+   * Performs any run-time initialization required by this property
+   * definition. This may include resolving managed object paths and
+   * property names.
+   *
+   * @throws Exception
+   *           If this property definition could not be initialized.
+   */
+  protected void initialize() throws Exception {
+    // No implementation required.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionUsageBuilder.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
new file mode 100644
index 0000000..ecb17af
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
@@ -0,0 +1,378 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+
+
+import java.text.NumberFormat;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+
+
+/**
+ * A property definition visitor which can be used to generate syntax
+ * usage information.
+ */
+public final class PropertyDefinitionUsageBuilder {
+
+  /**
+   * Underlying implementation.
+   */
+  private class MyPropertyDefinitionVisitor extends
+      PropertyDefinitionVisitor<Message, Void> {
+
+    // Flag indicating whether detailed syntax information will be
+    // generated.
+    private final boolean isDetailed;
+
+    // The formatter to use for numeric values.
+    private final NumberFormat numberFormat;
+
+
+
+    // Private constructor.
+    private MyPropertyDefinitionVisitor(boolean isDetailed) {
+      this.isDetailed = isDetailed;
+
+      this.numberFormat = NumberFormat.getNumberInstance();
+      this.numberFormat.setGroupingUsed(true);
+      this.numberFormat.setMaximumFractionDigits(2);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration>
+    Message visitAggregation(AggregationPropertyDefinition<C, S> d, Void p) {
+      return Message.raw("NAME");
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitAttributeType(AttributeTypePropertyDefinition d,
+        Void p) {
+      return Message.raw("OID");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitACI(ACIPropertyDefinition d,
+        Void p) {
+      return Message.raw("ACI");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitBoolean(BooleanPropertyDefinition d, Void p) {
+      if (isDetailed) {
+        return Message.raw("false | true");
+      } else {
+        return Message.raw("BOOLEAN");
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitClass(ClassPropertyDefinition d, Void p) {
+      if (isDetailed && !d.getInstanceOfInterface().isEmpty()) {
+        return Message.raw("CLASS <= " + d.getInstanceOfInterface().get(0));
+      } else {
+        return Message.raw("CLASS");
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitDN(DNPropertyDefinition d, Void p) {
+      if (isDetailed && d.getBaseDN() != null) {
+        return Message.raw("DN <= " + d.getBaseDN());
+      } else {
+        return Message.raw("DN");
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitDuration(DurationPropertyDefinition d, Void p) {
+      MessageBuilder builder = new MessageBuilder();
+      DurationUnit unit = d.getBaseUnit();
+
+      if (isDetailed && d.getLowerLimit() > 0) {
+        builder.append(DurationUnit.toString(d.getLowerLimit()));
+        builder.append(" <= ");
+      }
+
+      builder.append("DURATION (");
+      builder.append(unit.getShortName());
+      builder.append(")");
+
+      if (isDetailed) {
+        if (d.getUpperLimit() != null) {
+          builder.append(" <= ");
+          builder.append(DurationUnit.toString(d.getUpperLimit()));
+        }
+
+        if (d.isAllowUnlimited()) {
+          builder.append(" | unlimited");
+        }
+      }
+
+      return builder.toMessage();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <E extends Enum<E>> Message visitEnum(EnumPropertyDefinition<E> d,
+        Void p) {
+      if (!isDetailed) {
+        // Use the last word in the property name.
+        String name = d.getName();
+        int i = name.lastIndexOf('-');
+        if (i == -1 || i == (name.length() - 1)) {
+          return Message.raw(name.toUpperCase());
+        } else {
+          return Message.raw(name.substring(i + 1).toUpperCase());
+        }
+      } else {
+        Set<String> values = new TreeSet<String>();
+
+        for (Object value : EnumSet.allOf(d.getEnumClass())) {
+          values.add(value.toString().trim().toLowerCase());
+        }
+
+        boolean isFirst = true;
+        MessageBuilder builder = new MessageBuilder();
+        for (String s : values) {
+          if (!isFirst) {
+            builder.append(" | ");
+          }
+          builder.append(s);
+          isFirst = false;
+        }
+
+        return builder.toMessage();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitInteger(IntegerPropertyDefinition d, Void p) {
+      MessageBuilder builder = new MessageBuilder();
+
+      if (isDetailed) {
+        builder.append(String.valueOf(d.getLowerLimit()));
+        builder.append(" <= ");
+      }
+
+      builder.append("INTEGER");
+
+      if (isDetailed) {
+        if (d.getUpperLimit() != null) {
+          builder.append(" <= ");
+          builder.append(String.valueOf(d.getUpperLimit()));
+        } else if (d.isAllowUnlimited()) {
+          builder.append(" | unlimited");
+        }
+      }
+
+      return builder.toMessage();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitIPAddress(IPAddressPropertyDefinition d, Void p) {
+      return Message.raw("HOST_NAME");
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitIPAddressMask(IPAddressMaskPropertyDefinition d,
+        Void p) {
+      return Message.raw("IP_ADDRESS_MASK");
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitSize(SizePropertyDefinition d, Void p) {
+      MessageBuilder builder = new MessageBuilder();
+
+      if (isDetailed && d.getLowerLimit() > 0) {
+        SizeUnit unit = SizeUnit.getBestFitUnitExact(d.getLowerLimit());
+        builder.append(numberFormat.format(unit.fromBytes(d.getLowerLimit())));
+        builder.append(' ');
+        builder.append(unit.getShortName());
+        builder.append(" <= ");
+      }
+
+      builder.append("SIZE");
+
+      if (isDetailed) {
+        if (d.getUpperLimit() != null) {
+          long upperLimit = d.getUpperLimit();
+          SizeUnit unit = SizeUnit.getBestFitUnitExact(upperLimit);
+
+          // Quite often an upper limit is some power of 2 minus 1. In those
+          // cases lets use a "less than" relation rather than a "less than
+          // or equal to" relation. This will result in a much more readable
+          // quantity.
+          if (unit == SizeUnit.BYTES && upperLimit < Long.MAX_VALUE) {
+            unit = SizeUnit.getBestFitUnitExact(upperLimit + 1);
+            if (unit != SizeUnit.BYTES) {
+              upperLimit += 1;
+              builder.append(" < ");
+            } else {
+              builder.append(" <= ");
+            }
+          } else {
+            builder.append(" <= ");
+          }
+
+          builder.append(numberFormat.format(unit.fromBytes(upperLimit)));
+          builder.append(' ');
+          builder.append(unit.getShortName());
+        }
+
+        if (d.isAllowUnlimited()) {
+          builder.append(" | unlimited");
+        }
+      }
+
+      return builder.toMessage();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Message visitString(StringPropertyDefinition d, Void p) {
+      if (d.getPattern() != null) {
+        if (isDetailed) {
+          MessageBuilder builder = new MessageBuilder();
+          builder.append(d.getPatternUsage());
+          builder.append(" - ");
+          builder.append(d.getPatternSynopsis());
+          return builder.toMessage();
+        } else {
+          return Message.raw(d.getPatternUsage());
+        }
+      } else {
+        return Message.raw("STRING");
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> Message visitUnknown(PropertyDefinition<T> d, Void p)
+        throws UnknownPropertyDefinitionException {
+      return Message.raw("?");
+    }
+  }
+
+  // Underlying implementation.
+  private final MyPropertyDefinitionVisitor pimpl;
+
+
+
+  /**
+   * Creates a new property usage builder.
+   *
+   * @param isDetailed
+   *          Indicates whether or not the generated usage should
+   *          contain detailed information such as constraints.
+   */
+  public PropertyDefinitionUsageBuilder(boolean isDetailed) {
+    this.pimpl = new MyPropertyDefinitionVisitor(isDetailed);
+  }
+
+
+
+  /**
+   * Generates the usage information for the provided property
+   * definition.
+   *
+   * @param pd
+   *          The property definitions.
+   * @return Returns the usage information for the provided property
+   *         definition.
+   */
+  public Message getUsage(PropertyDefinition<?> pd) {
+    return pd.accept(pimpl, null);
+  };
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionVisitor.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionVisitor.java
new file mode 100644
index 0000000..5f0f9cb
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyDefinitionVisitor.java
@@ -0,0 +1,297 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * A visitor of property definitions, in the style of the visitor
+ * design pattern. Classes implementing this interface can query
+ * property definitions in a type-safe manner when the kind of
+ * property definition is unknown at compile time. When a visitor is
+ * passed to a property definition's accept method, the corresponding
+ * visit method most applicable to that property definition is
+ * invoked.
+ * <p>
+ * Each <code>visitXXX</code> method is provided with a default
+ * implementation which calls
+ * {@link #visitUnknown(PropertyDefinition, Object)}. Sub-classes can
+ * override any or all of the methods to provide their own
+ * type-specific behavior.
+ *
+ * @param <R>
+ *          The return type of this visitor's methods. Use
+ *          {@link java.lang.Void} for visitors that do not need to
+ *          return results.
+ * @param <P>
+ *          The type of the additional parameter to this visitor's
+ *          methods. Use {@link java.lang.Void} for visitors that do
+ *          not need an additional parameter.
+ */
+public abstract class PropertyDefinitionVisitor<R, P> {
+
+  /**
+   * Default constructor.
+   */
+  protected PropertyDefinitionVisitor() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Visit a dseecompat Global ACI property definition.
+   *
+   * @param pd
+   *          The Global ACI property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitACI(ACIPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit an aggregation property definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param pd
+   *          The aggregation property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+  R visitAggregation(AggregationPropertyDefinition<C, S> pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit an attribute type property definition.
+   *
+   * @param pd
+   *          The attribute type property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitAttributeType(AttributeTypePropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a boolean property definition.
+   *
+   * @param pd
+   *          The boolean property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitBoolean(BooleanPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a class property definition.
+   *
+   * @param pd
+   *          The class property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitClass(ClassPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a DN property definition.
+   *
+   * @param pd
+   *          The DN property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitDN(DNPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a duration property definition.
+   *
+   * @param pd
+   *          The duration property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitDuration(DurationPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit an enumeration property definition.
+   *
+   * @param <E>
+   *          The enumeration that should be used for values of the
+   *          property definition.
+   * @param pd
+   *          The enumeration property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public <E extends Enum<E>> R visitEnum(EnumPropertyDefinition<E> pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit an integer property definition.
+   *
+   * @param pd
+   *          The integer property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitInteger(IntegerPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a IP address property definition.
+   *
+   * @param pd
+   *          The IP address property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitIPAddress(IPAddressPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a IP address mask property definition.
+   *
+   * @param pd
+   *          The IP address mask property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitIPAddressMask(IPAddressMaskPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+  /**
+   * Visit a size property definition.
+   *
+   * @param pd
+   *          The size property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitSize(SizePropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+  /**
+   * Visit a string property definition.
+   *
+   * @param pd
+   *          The string property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitString(StringPropertyDefinition pd, P p) {
+    return visitUnknown(pd, p);
+  }
+
+
+
+
+  /**
+   * Visit an unknown type of property definition. Implementations of
+   * this method can provide default behavior for unknown property
+   * definition types.
+   * <p>
+   * The default implementation of this method throws an
+   * {@link UnknownPropertyDefinitionException}. Sub-classes can
+   * override this method with their own default behavior.
+   *
+   * @param <T>
+   *          The type of the underlying property.
+   * @param pd
+   *          The property definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   * @throws UnknownPropertyDefinitionException
+   *           Visitor implementations may optionally throw this
+   *           exception.
+   */
+  public <T> R visitUnknown(PropertyDefinition<T> pd, P p)
+      throws UnknownPropertyDefinitionException {
+    throw new UnknownPropertyDefinitionException(pd, p);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyException.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyException.java
new file mode 100644
index 0000000..2e5751a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyException.java
@@ -0,0 +1,99 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * Exceptions thrown as a result of errors that occurred when decoding
+ * and modifying property values.
+ */
+public abstract class PropertyException extends AdminRuntimeException {
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = -8465109598081914482L;
+
+  // The property definition associated with the property that caused
+  // the exception.
+  private final PropertyDefinition<?> pd;
+
+
+
+  /**
+   * Creates property exception without a cause.
+   *
+   * @param pd
+   *          The property definition associated with the property
+   *          that caused the exception.
+   * @param message
+   *          The message.
+   */
+  protected PropertyException(PropertyDefinition<?> pd, Message message) {
+    super(message);
+    this.pd = pd;
+  }
+
+
+
+  /**
+   * Creates property exception with a cause.
+   *
+   * @param pd
+   *          The property definition associated with the property
+   *          that caused the exception.
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected PropertyException(PropertyDefinition<?> pd, Message message,
+      Throwable cause) {
+    super(message, cause);
+    this.pd = pd;
+  }
+
+
+
+  /**
+   * Get the property definition associated with the property that
+   * caused the exception.
+   *
+   * @return Returns the property definition associated with the
+   *         property that caused the exception.
+   */
+  public final PropertyDefinition<?> getPropertyDefinition() {
+    return pd;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsMandatoryException.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsMandatoryException.java
new file mode 100644
index 0000000..198d4f8
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsMandatoryException.java
@@ -0,0 +1,58 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * Thrown when an attempt is made to remove a mandatory property.
+ */
+public class PropertyIsMandatoryException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 5328211711156565625L;
+
+
+
+  /**
+   * Create a new property is mandatory exception.
+   *
+   * @param pd
+   *          The property definition.
+   */
+  public PropertyIsMandatoryException(PropertyDefinition<?> pd) {
+    super(pd, ERR_PROPERTY_IS_MANDATORY_EXCEPTION.get(pd.getName()));
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsReadOnlyException.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsReadOnlyException.java
new file mode 100644
index 0000000..72e3954
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsReadOnlyException.java
@@ -0,0 +1,58 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * Thrown when an attempt is made to modify a read-only property.
+ */
+public class PropertyIsReadOnlyException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 5315348044141024459L;
+
+
+
+  /**
+   * Create a new property is read-only exception.
+   *
+   * @param pd
+   *          The property definition.
+   */
+  public PropertyIsReadOnlyException(PropertyDefinition<?> pd) {
+    super(pd, ERR_PROPERTY_IS_READ_ONLY_EXCEPTION.get(pd.getName()));
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsSingleValuedException.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsSingleValuedException.java
new file mode 100644
index 0000000..cae55c2
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyIsSingleValuedException.java
@@ -0,0 +1,58 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * Thrown when an attempt is made to add more than value to a
+ * single-valued property.
+ */
+public class PropertyIsSingleValuedException extends PropertyException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -8056602690887917027L;
+
+
+
+  /**
+   * Create a new property is single valued exception.
+   *
+   * @param pd
+   *          The property definition.
+   */
+  public PropertyIsSingleValuedException(PropertyDefinition<?> pd) {
+    super(pd, ERR_PROPERTY_IS_SINGLE_VALUED_EXCEPTION.get(pd.getName()));
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyNotFoundException.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyNotFoundException.java
new file mode 100644
index 0000000..853fc26
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyNotFoundException.java
@@ -0,0 +1,75 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+import static org.opends.messages.AdminMessages.*;
+
+
+/**
+ * Thrown when an attempt is made to retrieve a property using its name but the
+ * name was not recognized.
+ * <p>
+ * This exception can occur when attempt is made to retrieve inherited default
+ * values from a managed object.
+ */
+public class PropertyNotFoundException extends OperationsException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -895548482881819610L;
+
+  // The name of the property that could not be found.
+  private final String propertyName;
+
+
+
+  /**
+   * Create a new property not found exception.
+   *
+   * @param propertyName
+   *          The name of the property that could not be found.
+   */
+  public PropertyNotFoundException(String propertyName) {
+    super(ERR_PROPERTY_NOT_FOUND_EXCEPTION.get(propertyName));
+
+    this.propertyName = propertyName;
+  }
+
+
+
+  /**
+   * Get the name of the property that could not be found.
+   *
+   * @return Returns the name of the property that could not be found.
+   */
+  public String getPropertyName() {
+    return propertyName;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyOption.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyOption.java
new file mode 100644
index 0000000..f0629eb
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyOption.java
@@ -0,0 +1,71 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * This enumeration contains various options that can be associated
+ * with property definitions.
+ */
+public enum PropertyOption {
+  /**
+   * Use this option to identify properties which should be considered
+   * as advanced and should not be exposed by default in client
+   * applications.
+   */
+  ADVANCED,
+
+  /**
+   * Use this option to identify properties which must not be directly
+   * exposed in client applications.
+   */
+  HIDDEN,
+
+  /**
+   * Use this option to identify properties which must have a value.
+   */
+  MANDATORY,
+
+  /**
+   * Use this option to identify properties which are multi-valued.
+   */
+  MULTI_VALUED,
+
+  /**
+   * Use this option to identify properties which can be initialized
+   * once only and are read-only thereafter.
+   */
+  READ_ONLY,
+
+  /**
+   * Use this option to identify properties which are for monitoring
+   * purposes only and are generated automatically by the server..
+   */
+  MONITORING;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyProvider.java
new file mode 100644
index 0000000..af03a52
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyProvider.java
@@ -0,0 +1,89 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+
+
+
+/**
+ * An interface which can be used to initialize the contents of a managed
+ * object.
+ */
+public interface PropertyProvider {
+
+  /**
+   * A property provider which always returns empty property values, indicating
+   * default behavior.
+   */
+  public static final PropertyProvider DEFAULT_PROVIDER =
+    new PropertyProvider() {
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Collection<T> getPropertyValues(PropertyDefinition<T> d)
+        throws IllegalArgumentException {
+      return Collections.<T> emptySet();
+    }
+
+  };
+
+
+
+  /**
+   * Get the property values associated with the specified property definition.
+   * <p>
+   * Implementations are not required to validate the values that they provide.
+   * Specifically:
+   * <ul>
+   * <li>they do not need to guarantee that the provided values are valid
+   * according to the property's syntax
+   * <li>they do not need to provide values for mandatory properties
+   * <li>they do not need to ensure that single-valued properties do contain at
+   * most one value.
+   * </ul>
+   * The returned set of values is allowed to contain duplicates.
+   *
+   * @param <T>
+   *          The underlying type of the property.
+   * @param d
+   *          The Property definition.
+   * @return Returns a newly allocated set containing a copy of the property's
+   *         values. An empty set indicates that the property has no values
+   *         defined and any default behavior is applicable.
+   * @throws IllegalArgumentException
+   *           If this property provider does not recognise the requested
+   *           property definition.
+   */
+  <T> Collection<T> getPropertyValues(PropertyDefinition<T> d)
+      throws IllegalArgumentException;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/PropertyValueVisitor.java b/opendj-admin/src/main/java/org/opends/server/admin/PropertyValueVisitor.java
new file mode 100644
index 0000000..e2f7ceb
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/PropertyValueVisitor.java
@@ -0,0 +1,337 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import java.net.InetAddress;
+
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.schema.AttributeType;
+import org.opends.server.authorization.dseecompat.Aci;
+import org.opends.server.types.AddressMask;
+
+
+/**
+ * A visitor of property values, in the style of the visitor design
+ * pattern. Classes implementing this interface can query a property a
+ * value and its associated property definition in a type-safe manner
+ * when the kind of property value is unknown at compile time. When a
+ * visitor is passed to a property definition's accept method, the
+ * corresponding visit method most applicable to that property
+ * definition is invoked.
+ * <p>
+ * Each <code>visitXXX</code> method is provided with a default
+ * implementation which calls
+ * {@link #visitUnknown(PropertyDefinition, Object, Object)}.
+ * Sub-classes can override any or all of the methods to provide their
+ * own type-specific behavior.
+ *
+ * @param <R>
+ *          The return type of this visitor's methods. Use
+ *          {@link java.lang.Void} for visitors that do not need to
+ *          return results.
+ * @param <P>
+ *          The type of the additional parameter to this visitor's
+ *          methods. Use {@link java.lang.Void} for visitors that do
+ *          not need an additional parameter.
+ */
+public abstract class PropertyValueVisitor<R, P> {
+
+  /**
+   * Default constructor.
+   */
+  protected PropertyValueVisitor() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Visit a dseecompat ACI.
+   *
+   * @param pd
+   *          The dseecompat ACI property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitACI(ACIPropertyDefinition pd, Aci v,
+      P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit an aggregation property value.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this aggregation property definition refers to.
+   * @param pd
+   *          The aggregation property definition to visit.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+  R visitAggregation(
+      AggregationPropertyDefinition<C, S> pd, String v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit an attribute type.
+   *
+   * @param pd
+   *          The attribute type property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitAttributeType(AttributeTypePropertyDefinition pd,
+      AttributeType v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a boolean.
+   *
+   * @param pd
+   *          The boolean property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitBoolean(BooleanPropertyDefinition pd, Boolean v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a class.
+   *
+   * @param pd
+   *          The class property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitClass(ClassPropertyDefinition pd, String v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a DN.
+   *
+   * @param pd
+   *          The DN property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitDN(DNPropertyDefinition pd, DN v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a duration.
+   *
+   * @param pd
+   *          The duration property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitDuration(DurationPropertyDefinition pd, Long v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit an enumeration.
+   *
+   * @param <E>
+   *          The enumeration that should be used for values of the
+   *          property definition.
+   * @param pd
+   *          The enumeration property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public <E extends Enum<E>>
+  R visitEnum(EnumPropertyDefinition<E> pd, E v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit an integer.
+   *
+   * @param pd
+   *          The integer property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitInteger(IntegerPropertyDefinition pd, Integer v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a IP address.
+   *
+   * @param pd
+   *          The IP address property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitIPAddress(IPAddressPropertyDefinition pd, InetAddress v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a IP address mask.
+   *
+   * @param pd
+   *          The IP address mask property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitIPAddressMask(IPAddressMaskPropertyDefinition pd, AddressMask v,
+      P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+  /**
+   * Visit a size.
+   *
+   * @param pd
+   *          The size property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitSize(SizePropertyDefinition pd, Long v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit a string.
+   *
+   * @param pd
+   *          The string property definition.
+   * @param v
+   *          The property value to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  public R visitString(StringPropertyDefinition pd, String v, P p) {
+    return visitUnknown(pd, v, p);
+  }
+
+
+
+  /**
+   * Visit an unknown type of property value. Implementations of this
+   * method can provide default behavior for unknown types of
+   * property.
+   * <p>
+   * The default implementation of this method throws an
+   * {@link UnknownPropertyDefinitionException}. Sub-classes can
+   * override this method with their own default behavior.
+   *
+   * @param <T>
+   *          The type of property value to visit.
+   * @param pd
+   *          The property definition.
+   * @param v
+   *          The property value.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   * @throws UnknownPropertyDefinitionException
+   *           Visitor implementations may optionally throw this
+   *           exception.
+   */
+  public <T> R visitUnknown(PropertyDefinition<T> pd, T v, P p)
+      throws UnknownPropertyDefinitionException {
+    throw new UnknownPropertyDefinitionException(pd, p);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/Reference.java b/opendj-admin/src/main/java/org/opends/server/admin/Reference.java
new file mode 100644
index 0000000..cf1cfbd
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/Reference.java
@@ -0,0 +1,245 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import org.opends.server.types.AttributeValue;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.RDN;
+import org.opends.server.util.StaticUtils;
+
+
+
+/**
+ * A reference to another managed object.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          reference refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          reference refers to.
+ */
+public final class Reference<C extends ConfigurationClient,
+                             S extends Configuration> {
+
+  /**
+   * Parses a DN string value as a reference using the provided
+   * managed object path and relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this reference refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this reference refers to.
+   * @param p
+   *          The path of the referenced managed object's parent.
+   * @param rd
+   *          The instantiable relation in the parent which contains
+   *          the referenced managed object.
+   * @param s
+   *          The DN string value.
+   * @return Returns the new reference based on the provided DN string
+   *         value.
+   * @throws IllegalArgumentException
+   *           If the DN string value could not be decoded as a DN or
+   *           if the provided DN did not correspond to the provided
+   *           path and relation.
+   */
+  public static <C extends ConfigurationClient, S extends Configuration>
+  Reference<C, S> parseDN(
+      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
+      String s) throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation \"" + rd.getName()
+          + "\" is not associated with the definition \"" + d.getName() + "\"");
+    }
+
+    DN dn;
+    try {
+      dn = DN.decode(s);
+    } catch (DirectoryException e) {
+      throw new IllegalArgumentException("Unabled to decode the DN string: \""
+          + s + "\"");
+    }
+
+    RDN rdn = dn.getRDN();
+    if (rdn == null) {
+      throw new IllegalArgumentException("Unabled to decode the DN string: \""
+          + s + "\"");
+    }
+
+    AttributeValue av = rdn.getAttributeValue(0);
+    if (av == null) {
+      throw new IllegalArgumentException("Unabled to decode the DN string: \""
+          + s + "\"");
+    }
+
+    String name = av.getValue().toString();
+
+    // Check that the DN was valid.
+    DN expected = p.child(rd, name).toDN();
+    if (!dn.equals(expected)) {
+      throw new IllegalArgumentException("Unabled to decode the DN string: \""
+          + s + "\"");
+    }
+
+    return new Reference<C, S>(p, rd, name);
+  }
+
+
+
+  /**
+   * Parses a name as a reference using the provided managed object
+   * path and relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this reference refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this reference refers to.
+   * @param p
+   *          The path of the referenced managed object's parent.
+   * @param rd
+   *          The instantiable relation in the parent which contains
+   *          the referenced managed object.
+   * @param s
+   *          The name of the referenced managed object.
+   * @return Returns the new reference based on the provided name.
+   * @throws IllegalArgumentException
+   *           If the relation is not associated with the provided
+   *           parent's definition, or if the provided name is empty.
+   */
+  public static <C extends ConfigurationClient, S extends Configuration>
+  Reference<C, S> parseName(
+      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
+      String s) throws IllegalArgumentException {
+    // Sanity checks.
+    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation \"" + rd.getName()
+          + "\" is not associated with the definition \"" + d.getName() + "\"");
+    }
+
+    if (s.trim().length() == 0) {
+      throw new IllegalArgumentException("Empty names are not allowed");
+    }
+
+    return new Reference<C, S>(p, rd, s);
+  }
+
+  // The name of the referenced managed object.
+  private final String name;
+
+  // The path of the referenced managed object.
+  private final ManagedObjectPath<C, S> path;
+
+  // The instantiable relation in the parent which contains the
+  // referenced managed object.
+  private final InstantiableRelationDefinition<C, S> relation;
+
+
+
+  // Private constructor.
+  private Reference(ManagedObjectPath<?, ?> parent,
+      InstantiableRelationDefinition<C, S> relation, String name)
+      throws IllegalArgumentException {
+    this.relation = relation;
+    this.name = name;
+    this.path = parent.child(relation, name);
+  }
+
+
+
+  /**
+   * Gets the name of the referenced managed object.
+   *
+   * @return Returns the name of the referenced managed object.
+   */
+  public String getName() {
+    return name;
+  }
+
+
+
+  /**
+   * Gets the normalized name of the referenced managed object.
+   *
+   * @return Returns the normalized name of the referenced managed
+   *         object.
+   */
+  public String getNormalizedName() {
+    PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
+    return normalizeName(pd);
+  }
+
+
+
+  /**
+   * Gets the DN of the referenced managed object.
+   *
+   * @return Returns the DN of the referenced managed object.
+   */
+  public DN toDN() {
+    return path.toDN();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public String toString() {
+    return name;
+  }
+
+
+
+  // Normalize a value using the specified naming property definition
+  // if defined.
+  private <T> String normalizeName(PropertyDefinition<T> pd) {
+    if (pd != null) {
+      try {
+        T tvalue = pd.decodeValue(name);
+        return pd.normalizeValue(tvalue);
+      } catch (IllegalPropertyValueStringException e) {
+        // Fall through to default normalization.
+      }
+    }
+
+    // FIXME: should really use directory string normalizer.
+    String s = name.trim().replaceAll(" +", " ");
+    return StaticUtils.toLowerCase(s);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinition.java
new file mode 100644
index 0000000..bc6a863
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinition.java
@@ -0,0 +1,421 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.Validator.*;
+
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+
+
+/**
+ * Relation definitions define relationships between types of managed
+ * objects. In addition they define the ownership model:
+ * <ul>
+ * <li>composition - referenced managed objects are owned by the
+ * parent managed object and are deleted when the parent is deleted
+ * <li>aggregation - referenced managed objects are not owned by the
+ * parent managed object. Instead they are shared by other managed
+ * objects.
+ * </ul>
+ * Relations define how clients interact with the configuration. For
+ * example, clients manage aggregated managed objects in a shared
+ * location and attach them to parent managed objects. Composed
+ * managed objects, on the other hand, would be created directly
+ * beneath the parent managed object and destroyed with it too.
+ * <p>
+ * Within the server, listeners can choose to request notification of
+ * managed objects being added or removed from relations.
+ * <p>
+ * In LDAP, compositions are represented as follows:
+ * <ul>
+ * <li>singleton relations (one to one): a referenced managed object
+ * is represented using a child entry directly beneath the parent
+ * <li>optional relations (one to zero or one): a referenced managed
+ * object is represented using a child entry directly beneath the
+ * parent
+ * <li>instantiable relations (one to many): the relation is
+ * represented using a child entry directly beneath the parent.
+ * Referenced managed objects are represented using child entries of
+ * this "relation entry" and are named by the user
+ * <li>set relations (one to many): the relation is
+ * represented using a child entry directly beneath the parent.
+ * Referenced managed objects are represented using child entries of
+ * this "relation entry" whose name is the type of the managed object.
+ * </ul>
+ * Whereas, aggregations are represented by storing the DNs of the
+ * referenced managed objects in an attribute of the aggregating
+ * managed object.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          relation definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          relation definition refers to.
+ */
+public abstract class RelationDefinition
+    <C extends ConfigurationClient, S extends Configuration> {
+
+  /**
+   * An interface for incrementally constructing relation definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this relation definition refers to.
+   * @param <D>
+   *          The type of relation definition constructed by this
+   *          builder.
+   */
+  protected abstract static class AbstractBuilder
+      <C extends ConfigurationClient, S extends Configuration,
+       D extends RelationDefinition<C, S>> {
+
+    // Common fields.
+    private final Common<C, S> common;
+
+
+
+    /**
+     * Create a property definition builder.
+     *
+     * @param pd
+     *          The parent managed object definition.
+     * @param name
+     *          The name of the relation.
+     * @param cd
+     *          The child managed object definition.
+     */
+    protected AbstractBuilder(AbstractManagedObjectDefinition<?, ?> pd,
+        String name, AbstractManagedObjectDefinition<C, S> cd) {
+      this.common = new Common<C, S>(pd, name, cd);
+    }
+
+
+
+    /**
+     * Construct a relation definition based on the properties of this
+     * builder.
+     *
+     * @return The new relation definition.
+     */
+    public final D getInstance() {
+      return buildInstance(common);
+    }
+
+
+
+    /**
+     * Add a relation definition option.
+     *
+     * @param option
+     *          The relation option.
+     */
+    public final void setOption(RelationOption option) {
+      ensureNotNull(option);
+      common.options.add(option);
+    }
+
+
+
+    /**
+     * Build a relation definition based on the properties of this
+     * builder.
+     *
+     * @param common
+     *          The common fields of the new relation definition.
+     * @return The new relation definition.
+     */
+    protected abstract D buildInstance(Common<C, S> common);
+  }
+
+
+
+  /**
+   * Opaque structure containing fields common to all relation
+   * definition types.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this relation definition refers to.
+   */
+  protected static final class Common
+    <C extends ConfigurationClient, S extends Configuration> {
+
+    // The definition of the child managed object.
+    private final AbstractManagedObjectDefinition<C, S> cd;
+
+    // The name of the relation.
+    private final String name;
+
+    // Options applicable to this definition.
+    private final Set<RelationOption> options;
+
+    // The definition of the parent managed object.
+    private final AbstractManagedObjectDefinition<?, ?> pd;
+
+
+
+    // Private constructor.
+    private Common(AbstractManagedObjectDefinition<?, ?> pd, String name,
+        AbstractManagedObjectDefinition<C, S> cd) {
+      this.name = name;
+      this.pd = pd;
+      this.cd = cd;
+      this.options = EnumSet.noneOf(RelationOption.class);
+    }
+  }
+
+  // Common fields.
+  private final Common<C, S> common;
+
+
+
+  /**
+   * Create a new managed object relation definition with the
+   * specified common fields.
+   *
+   * @param common
+   *          The common fields of the new relation definition.
+   */
+  protected RelationDefinition(Common<C, S> common) {
+    this.common = common;
+  }
+
+
+
+  /**
+   * Apply a visitor to this relation definition.
+   *
+   * @param <R>
+   *          The return type of the visitor's methods.
+   * @param <P>
+   *          The type of the additional parameters to the visitor's
+   *          methods.
+   * @param v
+   *          The relation definition visitor.
+   * @param p
+   *          Optional additional visitor parameter.
+   * @return Returns a result as specified by the visitor.
+   */
+  public abstract <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p);
+
+
+
+  /**
+   * Get the definition of the child managed object.
+   *
+   * @return Returns the definition of the child managed object.
+   */
+  public final AbstractManagedObjectDefinition<C, S> getChildDefinition() {
+    return common.cd;
+  }
+
+
+
+  /**
+   * Gets the optional description of this relation definition in the
+   * default locale.
+   *
+   * @return Returns the description of this relation definition in
+   *         the default locale, or <code>null</code> if there is no
+   *         description.
+   */
+  public final Message getDescription() {
+    return getDescription(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional description of this relation definition in the
+   * specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the description of this relation definition in
+   *         the specified locale, or <code>null</code> if there is
+   *         no description.
+   */
+  public final Message getDescription(Locale locale) {
+    try {
+      String property = "relation." + common.name + ".description";
+      return ManagedObjectDefinitionI18NResource.getInstance().getMessage(
+          getParentDefinition(), property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Get the name of the relation.
+   *
+   * @return Returns the name of the relation.
+   */
+  public final String getName() {
+    return common.name;
+  }
+
+
+
+  /**
+   * Get the definition of the parent managed object.
+   *
+   * @return Returns the definition of the parent managed object.
+   */
+  public final AbstractManagedObjectDefinition<?, ?> getParentDefinition() {
+    return common.pd;
+  }
+
+
+
+  /**
+   * Gets the synopsis of this relation definition in the default
+   * locale.
+   *
+   * @return Returns the synopsis of this relation definition in the
+   *         default locale.
+   */
+  public final Message getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this relation definition in the specified
+   * locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this relation definition in the
+   *         specified locale.
+   */
+  public final Message getSynopsis(Locale locale) {
+    String property = "relation." + common.name + ".synopsis";
+    return ManagedObjectDefinitionI18NResource.getInstance().getMessage(
+        getParentDefinition(), property, locale);
+  }
+
+
+
+  /**
+   * Gets the user friendly name of this relation definition in the
+   * default locale.
+   *
+   * @return Returns the user friendly name of this relation
+   *         definition in the default locale.
+   */
+  public final Message getUserFriendlyName() {
+    return getUserFriendlyName(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the user friendly name of this relation definition in the
+   * specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the user friendly name of this relation
+   *         definition in the specified locale.
+   */
+  public final Message getUserFriendlyName(Locale locale) {
+    String property = "relation." + common.name + ".user-friendly-name";
+    return ManagedObjectDefinitionI18NResource.getInstance().getMessage(
+        getParentDefinition(), property, locale);
+  }
+
+
+
+  /**
+   * Check if the specified option is set for this relation
+   * definition.
+   *
+   * @param option
+   *          The option to test.
+   * @return Returns <code>true</code> if the option is set, or
+   *         <code>false</code> otherwise.
+   */
+  public final boolean hasOption(RelationOption option) {
+    return common.options.contains(option);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public final String toString() {
+    StringBuilder builder = new StringBuilder();
+    toString(builder);
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Append a string representation of the managed object relation to
+   * the provided string builder.
+   *
+   * @param builder
+   *          The string builder where the string representation
+   *          should be appended.
+   */
+  public abstract void toString(StringBuilder builder);
+
+
+
+  /**
+   * Performs any run-time initialization required by this relation
+   * definition. This may include resolving managed object paths and
+   * property names.
+   *
+   * @throws Exception
+   *           If this relation definition could not be initialized.
+   */
+  protected void initialize() throws Exception {
+    // No implementation required.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinitionVisitor.java b/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinitionVisitor.java
new file mode 100644
index 0000000..acbb429
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/RelationDefinitionVisitor.java
@@ -0,0 +1,130 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * A visitor of relation definitions, in the style of the visitor
+ * design pattern. Classes implementing this interface can query
+ * relation definitions in a type-safe manner when the kind of
+ * relation definition is unknown at compile time. When a visitor is
+ * passed to a relation definition's accept method, the corresponding
+ * visit method most applicable to that relation definition is
+ * invoked.
+ *
+ * @param <R>
+ *          The return type of this visitor's methods. Use
+ *          {@link java.lang.Void} for visitors that do not need to
+ *          return results.
+ * @param <P>
+ *          The type of the additional parameter to this visitor's
+ *          methods. Use {@link java.lang.Void} for visitors that do
+ *          not need an additional parameter.
+ */
+public interface RelationDefinitionVisitor<R, P> {
+
+  /**
+   * Visit an instantiable relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param rd
+   *          The instantiable relation definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  <C extends ConfigurationClient, S extends Configuration> R visitInstantiable(
+      InstantiableRelationDefinition<C, S> rd, P p);
+
+
+
+  /**
+   * Visit a set relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param rd
+   *          The set relation definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  <C extends ConfigurationClient, S extends Configuration> R visitSet(
+      SetRelationDefinition<C, S> rd, P p);
+
+
+
+  /**
+   * Visit an optional relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param rd
+   *          The optional relation definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  <C extends ConfigurationClient, S extends Configuration> R visitOptional(
+      OptionalRelationDefinition<C, S> rd, P p);
+
+
+
+  /**
+   * Visit a singleton relation definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param rd
+   *          The singleton relation definition to visit.
+   * @param p
+   *          A visitor specified parameter.
+   * @return Returns a visitor specified result.
+   */
+  <C extends ConfigurationClient, S extends Configuration> R visitSingleton(
+      SingletonRelationDefinition<C, S> rd, P p);
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/RelationOption.java b/opendj-admin/src/main/java/org/opends/server/admin/RelationOption.java
new file mode 100644
index 0000000..40c798e
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/RelationOption.java
@@ -0,0 +1,49 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * This enumeration contains various options that can be associated
+ * with relation definitions.
+ */
+public enum RelationOption {
+  /**
+   * Use this option to identify relations which should be considered
+   * as advanced and should not be exposed by default in client
+   * applications.
+   */
+  ADVANCED,
+
+  /**
+   * Use this option to identify relations which must not be directly
+   * exposed in client applications.
+   */
+  HIDDEN;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java
new file mode 100644
index 0000000..e127938
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java
@@ -0,0 +1,150 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * A default behavior provider which retrieves default values from a
+ * parent managed object. It should be used by properties which
+ * inherit their default value(s) from properties held in an other
+ * managed object.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public final class RelativeInheritedDefaultBehaviorProvider<T> extends
+    DefaultBehaviorProvider<T> {
+
+  // The type of managed object expected at the relative offset.
+  private final AbstractManagedObjectDefinition<?, ?> d;
+
+  // The relative offset (where 1 = parent, 2 = grandparent) of the
+  // managed object containing the property.
+  private final int offset;
+
+  // The name of the property containing the inherited default values.
+  private final String propertyName;
+
+
+
+  /**
+   * Create a relative inherited default behavior provider associated
+   * with a parent managed object.
+   *
+   * @param d
+   *          The type of parent managed object expected at the
+   *          relative location.
+   * @param propertyName
+   *          The name of the property containing the inherited
+   *          default values.
+   * @param offset
+   *          The relative location of the parent managed object
+   *          (where 0 is the managed object itself, 1 is the parent,
+   *          and 2 is the grand-parent).
+   * @throws IllegalArgumentException
+   *           If the offset is less than 0.
+   */
+  public RelativeInheritedDefaultBehaviorProvider(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName, int offset)
+      throws IllegalArgumentException {
+    // We do not decode the property name now because the property
+    // might not have been constructed at this point (e.g. when the
+    // offset is 0).
+    if (offset < 0) {
+      throw new IllegalArgumentException("Negative offset");
+    }
+    this.d = d;
+    this.propertyName = propertyName;
+    this.offset = offset;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <R, P> R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p) {
+    return v.visitRelativeInherited(this, p);
+  }
+
+
+
+  /**
+   * Get the definition of the parent managed object containing the
+   * inherited default values.
+   *
+   * @return Returns the definition of the parent managed object
+   *         containing the inherited default values.
+   */
+  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
+    return d;
+  }
+
+
+
+  /**
+   * Get the absolute path of the managed object containing the
+   * property which has the default values.
+   *
+   * @param path
+   *          The path of the current managed object from which the
+   *          relative path should be determined.
+   * @return Returns the absolute path of the managed object
+   *         containing the property which has the default values.
+   */
+  public ManagedObjectPath<?, ?> getManagedObjectPath(
+      ManagedObjectPath<?, ?> path) {
+    return path.parent(offset);
+  }
+
+
+
+  /**
+   * Gets the name of the property containing the inherited default
+   * values.
+   *
+   * @return Returns the name of the property containing the inherited
+   *         default values.
+   */
+  public String getPropertyName() {
+    return propertyName;
+  }
+
+
+
+  /**
+   * Get the relative location of the parent managed object.
+   *
+   * @return Returns the relative location of the parent managed
+   *         object (where 0 is the managed object itself, 1 is the
+   *         parent, and 2 is the grand-parent).
+   */
+  public int getRelativeOffset() {
+    return offset;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/SetRelationDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/SetRelationDefinition.java
new file mode 100644
index 0000000..426c97d
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/SetRelationDefinition.java
@@ -0,0 +1,289 @@
+/*
+ * 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.admin;
+
+
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * A managed object composite relationship definition which represents a
+ * composition of zero or more managed objects each of which must have a
+ * different type. The manage objects are named using their type name.
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          relation definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          relation definition refers to.
+ */
+public final class SetRelationDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends RelationDefinition<C, S>
+{
+
+  /**
+   * An interface for incrementally constructing set relation
+   * definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that this
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that this
+   *          relation definition refers to.
+   */
+  public static final class Builder
+      <C extends ConfigurationClient, S extends Configuration>
+      extends AbstractBuilder<C, S, SetRelationDefinition<C, S>>
+  {
+
+    // The plural name of the relation.
+    private final String pluralName;
+
+    // The optional default managed objects associated with this
+    // set relation definition.
+    private final Map<String,
+                      DefaultManagedObject<? extends C, ? extends S>>
+      defaultManagedObjects =
+        new HashMap<String, DefaultManagedObject<? extends C, ? extends S>>();
+
+
+
+    /**
+     * Creates a new builder which can be used to incrementally build a
+     * set relation definition.
+     *
+     * @param pd
+     *          The parent managed object definition.
+     * @param name
+     *          The name of the relation.
+     * @param pluralName
+     *          The plural name of the relation.
+     * @param cd
+     *          The child managed object definition.
+     */
+    public Builder(AbstractManagedObjectDefinition<?, ?> pd,
+        String name, String pluralName,
+        AbstractManagedObjectDefinition<C, S> cd)
+    {
+      super(pd, name, cd);
+      this.pluralName = pluralName;
+    }
+
+
+
+    /**
+     * Adds the default managed object to this set relation definition.
+     *
+     * @param defaultManagedObject
+     *          The default managed object.
+     */
+    public void setDefaultManagedObject(
+        DefaultManagedObject<? extends C, ? extends S> defaultManagedObject)
+    {
+      this.defaultManagedObjects
+          .put(defaultManagedObject.getManagedObjectDefinition()
+              .getName(), defaultManagedObject);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected SetRelationDefinition<C, S> buildInstance(
+        Common<C, S> common)
+    {
+      return new SetRelationDefinition<C, S>(common, pluralName,
+          defaultManagedObjects);
+    }
+
+  }
+
+
+
+  // The plural name of the relation.
+  private final String pluralName;
+
+  // The optional default managed objects associated with this
+  // set relation definition.
+  private final Map<String,
+                    DefaultManagedObject<? extends C, ? extends S>>
+    defaultManagedObjects;
+
+
+
+  // Private constructor.
+  private SetRelationDefinition(
+      Common<C, S> common,
+      String pluralName,
+      Map<String,
+          DefaultManagedObject<? extends C, ? extends S>> defaultManagedObjects)
+  {
+    super(common);
+    this.pluralName = pluralName;
+    this.defaultManagedObjects = defaultManagedObjects;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p)
+  {
+    return v.visitSet(this, p);
+  }
+
+
+
+  /**
+   * Gets the named default managed object associated with this set
+   * relation definition.
+   *
+   * @param name
+   *          The name of the default managed object (for set relations
+   *          this is the type of the default managed object).
+   * @return The named default managed object.
+   * @throws IllegalArgumentException
+   *           If there is no default managed object associated with the
+   *           provided name.
+   */
+  public DefaultManagedObject<? extends C, ? extends S> getDefaultManagedObject(
+      String name) throws IllegalArgumentException
+  {
+    if (!defaultManagedObjects.containsKey(name))
+    {
+      throw new IllegalArgumentException(
+          "unrecognized default managed object \"" + name + "\"");
+    }
+    return defaultManagedObjects.get(name);
+  }
+
+
+
+  /**
+   * Gets the names of the default managed objects associated with this
+   * set relation definition.
+   *
+   * @return An unmodifiable set containing the names of the default
+   *         managed object.
+   */
+  public Set<String> getDefaultManagedObjectNames()
+  {
+    return Collections.unmodifiableSet(defaultManagedObjects.keySet());
+  }
+
+
+
+  /**
+   * Gets the plural name of the relation.
+   *
+   * @return The plural name of the relation.
+   */
+  public String getPluralName()
+  {
+    return pluralName;
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this relation definition in
+   * the default locale.
+   *
+   * @return Returns the user friendly plural name of this relation
+   *         definition in the default locale.
+   */
+  public Message getUserFriendlyPluralName()
+  {
+    return getUserFriendlyPluralName(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the user friendly plural name of this relation definition in
+   * the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the user friendly plural name of this relation
+   *         definition in the specified locale.
+   */
+  public Message getUserFriendlyPluralName(Locale locale)
+  {
+    String property =
+        "relation." + getName() + ".user-friendly-plural-name";
+    return ManagedObjectDefinitionI18NResource.getInstance()
+        .getMessage(getParentDefinition(), property, locale);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder)
+  {
+    builder.append("name=");
+    builder.append(getName());
+    builder.append(" type=set parent=");
+    builder.append(getParentDefinition().getName());
+    builder.append(" child=");
+    builder.append(getChildDefinition().getName());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception
+  {
+    for (DefaultManagedObject<?, ?> dmo : defaultManagedObjects
+        .values())
+    {
+      dmo.initialize();
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/SingletonRelationDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/SingletonRelationDefinition.java
new file mode 100644
index 0000000..2f4f746
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/SingletonRelationDefinition.java
@@ -0,0 +1,184 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+/**
+ * A managed object composite relationship definition which represents
+ * a composition of a single managed object (i.e. the managed object
+ * must be present).
+ *
+ * @param <C>
+ *          The type of client managed object configuration that this
+ *          relation definition refers to.
+ * @param <S>
+ *          The type of server managed object configuration that this
+ *          relation definition refers to.
+ */
+public final class SingletonRelationDefinition
+    <C extends ConfigurationClient, S extends Configuration>
+    extends RelationDefinition<C, S> {
+
+  /**
+   * An interface for incrementally constructing singleton relation
+   * definitions.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that
+   *          this relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that
+   *          this relation definition refers to.
+   */
+  public static final class Builder
+      <C extends ConfigurationClient, S extends Configuration>
+      extends AbstractBuilder<C, S, SingletonRelationDefinition<C, S>> {
+
+    // The optional default managed object associated with this
+    // singleton relation.
+    private DefaultManagedObject<? extends C, ? extends S>
+      defaultManagedObject = null;
+
+
+
+    /**
+     * Creates a new builder which can be used to incrementally build
+     * an singleton relation definition.
+     *
+     * @param pd
+     *          The parent managed object definition.
+     * @param name
+     *          The name of the relation.
+     * @param cd
+     *          The child managed object definition.
+     */
+    public Builder(AbstractManagedObjectDefinition<?, ?> pd, String name,
+        AbstractManagedObjectDefinition<C, S> cd) {
+      super(pd, name, cd);
+    }
+
+
+
+    /**
+     * Sets the optional default managed object associated with this
+     * singleton relation definition.
+     *
+     * @param defaultManagedObject
+     *          The default managed object or <code>null</code> if
+     *          there is no default managed object defined for this
+     *          relation definition.
+     */
+    public void setDefaultManagedObject(
+        DefaultManagedObject<? extends C, ? extends S> defaultManagedObject) {
+      this.defaultManagedObject = defaultManagedObject;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected SingletonRelationDefinition<C, S> buildInstance(
+        Common<C, S> common) {
+      return new SingletonRelationDefinition<C, S>(common,
+          defaultManagedObject);
+    }
+
+  }
+
+
+
+  // The optional default managed object associated with this
+  // singleton relation.
+  private final DefaultManagedObject<? extends C, ? extends S>
+    defaultManagedObject;
+
+
+
+  // Private constructor.
+  private SingletonRelationDefinition(Common<C, S> common,
+      DefaultManagedObject<? extends C, ? extends S> defaultManagedObject) {
+    super(common);
+    this.defaultManagedObject = defaultManagedObject;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p) {
+    return v.visitSingleton(this, p);
+  }
+
+
+
+  /**
+   * Gets the optional default managed object associated with this
+   * singleton relation definition.
+   *
+   * @return Returns the default managed object or <code>null</code>
+   *         if there is no default managed object defined for this
+   *         relation definition.
+   */
+  public DefaultManagedObject<? extends C, ? extends S>
+      getDefaultManagedObject() {
+    return defaultManagedObject;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    builder.append("name=");
+    builder.append(getName());
+    builder.append(" type=singleton parent=");
+    builder.append(getParentDefinition().getName());
+    builder.append(" child=");
+    builder.append(getChildDefinition().getName());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void initialize() throws Exception {
+    if (defaultManagedObject != null) {
+      defaultManagedObject.initialize();
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/SizePropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/SizePropertyDefinition.java
new file mode 100644
index 0000000..aa82545
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/SizePropertyDefinition.java
@@ -0,0 +1,410 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+
+
+
+/**
+ * Memory size property definition.
+ * <p>
+ * All memory size property values are represented in bytes using longs.
+ * <p>
+ * All values must be zero or positive and within the lower/upper limit
+ * constraints. Support is provided for "unlimited" memory sizes. These are
+ * represented using a negative memory size value or using the string
+ * "unlimited".
+ */
+public final class SizePropertyDefinition extends PropertyDefinition<Long> {
+
+  // String used to represent unlimited memory sizes.
+  private static final String UNLIMITED = "unlimited";
+
+  // The lower limit of the property value in bytes.
+  private final long lowerLimit;
+
+  // The optional upper limit of the property value in bytes.
+  private final Long upperLimit;
+
+  // Indicates whether this property allows the use of the "unlimited" memory
+  // size value (represented using a -1L or the string "unlimited").
+  private final boolean allowUnlimited;
+
+
+
+  /**
+   * An interface for incrementally constructing memory size property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<Long, SizePropertyDefinition> {
+
+    // The lower limit of the property value in bytes.
+    private long lowerLimit = 0L;
+
+    // The optional upper limit of the property value in bytes.
+    private Long upperLimit = null;
+
+    // Indicates whether this property allows the use of the "unlimited" memory
+    // size value (represented using a -1L or the string "unlimited").
+    private boolean allowUnlimited = false;
+
+
+
+    // Private constructor
+    private Builder(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Set the lower limit in bytes.
+     *
+     * @param lowerLimit
+     *          The new lower limit (must be >= 0) in bytes.
+     * @throws IllegalArgumentException
+     *           If a negative lower limit was specified, or if the lower limit
+     *           is greater than the upper limit.
+     */
+    public final void setLowerLimit(long lowerLimit)
+        throws IllegalArgumentException {
+      if (lowerLimit < 0) {
+        throw new IllegalArgumentException("Negative lower limit");
+      }
+      if (upperLimit != null && lowerLimit > upperLimit) {
+        throw new IllegalArgumentException(
+            "Lower limit greater than upper limit");
+      }
+      this.lowerLimit = lowerLimit;
+    }
+
+
+
+    /**
+     * Set the lower limit using a string representation of the limit.
+     *
+     * @param lowerLimit
+     *          The string representation of the new lower limit.
+     * @throws IllegalArgumentException
+     *           If the lower limit could not be parsed, or if a negative lower
+     *           limit was specified, or the lower limit is greater than the
+     *           upper limit.
+     */
+    public final void setLowerLimit(String lowerLimit)
+        throws IllegalArgumentException {
+      setLowerLimit(SizeUnit.parseValue(lowerLimit, SizeUnit.BYTES));
+    }
+
+
+
+    /**
+     * Set the upper limit in bytes.
+     *
+     * @param upperLimit
+     *          The new upper limit in bytes or <code>null</code> if there is
+     *          no upper limit.
+     * @throws IllegalArgumentException
+     *           If the lower limit is greater than the upper limit.
+     */
+    public final void setUpperLimit(Long upperLimit)
+        throws IllegalArgumentException {
+      if (upperLimit != null) {
+        if (upperLimit < 0) {
+          throw new IllegalArgumentException("Negative upper limit");
+        }
+        if (lowerLimit > upperLimit) {
+          throw new IllegalArgumentException(
+              "Lower limit greater than upper limit");
+        }
+      }
+      this.upperLimit = upperLimit;
+    }
+
+
+
+    /**
+     * Set the upper limit using a string representation of the limit.
+     *
+     * @param upperLimit
+     *          The string representation of the new upper limit, or
+     *          <code>null</code> if there is no upper limit.
+     * @throws IllegalArgumentException
+     *           If the upper limit could not be parsed, or if the lower limit
+     *           is greater than the upper limit.
+     */
+    public final void setUpperLimit(String upperLimit)
+        throws IllegalArgumentException {
+      if (upperLimit == null) {
+        setUpperLimit((Long) null);
+      } else {
+        setUpperLimit(SizeUnit.parseValue(upperLimit, SizeUnit.BYTES));
+      }
+    }
+
+
+
+    /**
+     * Specify whether or not this property definition will allow unlimited
+     * values (default is false).
+     *
+     * @param allowUnlimited
+     *          <code>true</code> if the property will allow unlimited values,
+     *          or <code>false</code> otherwise.
+     */
+    public final void setAllowUnlimited(boolean allowUnlimited) {
+      this.allowUnlimited = allowUnlimited;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected SizePropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<Long> defaultBehavior) {
+      return new SizePropertyDefinition(d, propertyName, options, adminAction,
+          defaultBehavior, lowerLimit, upperLimit, allowUnlimited);
+    }
+
+  }
+
+
+
+  /**
+   * Create an memory size property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new integer property definition builder.
+   */
+  public static Builder createBuilder(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+
+
+  // Private constructor.
+  private SizePropertyDefinition(
+      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+      EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<Long> defaultBehavior, Long lowerLimit,
+      Long upperLimit, boolean allowUnlimited) {
+    super(d, Long.class, propertyName, options, adminAction,
+        defaultBehavior);
+    this.lowerLimit = lowerLimit;
+    this.upperLimit = upperLimit;
+    this.allowUnlimited = allowUnlimited;
+  }
+
+
+
+  /**
+   * Get the lower limit in bytes.
+   *
+   * @return Returns the lower limit in bytes.
+   */
+  public long getLowerLimit() {
+    return lowerLimit;
+  }
+
+
+
+  /**
+   * Get the upper limit in bytes.
+   *
+   * @return Returns the upper limit in bytes or <code>null</code> if there is
+   *         no upper limit.
+   */
+  public Long getUpperLimit() {
+    return upperLimit;
+  }
+
+
+
+  /**
+   * Determine whether this property allows unlimited memory sizes.
+   *
+   * @return Returns <code>true</code> if this this property allows unlimited
+   *         memory sizes.
+   */
+  public boolean isAllowUnlimited() {
+    return allowUnlimited;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(Long value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    if (!allowUnlimited && value < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+
+    // unlimited allowed
+    } else if (value >= 0 && value < lowerLimit) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+
+    if ((upperLimit != null) && (value > upperLimit)) {
+      throw new IllegalPropertyValueException(this, value);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String encodeValue(Long value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    // Make sure that we correctly encode negative values as "unlimited".
+    if (allowUnlimited) {
+      if (value < 0) {
+        return UNLIMITED;
+      }
+    }
+
+    // Encode the size value using the best-fit unit.
+    StringBuilder builder = new StringBuilder();
+    SizeUnit unit = SizeUnit.getBestFitUnitExact(value);
+
+    // Cast to a long to remove fractional part (which should not be there
+    // anyway as the best-fit unit should result in an exact conversion).
+    builder.append((long) unit.fromBytes(value));
+    builder.append(' ');
+    builder.append(unit.toString());
+    return builder.toString();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Long decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    // First check for the special "unlimited" value when necessary.
+    if (allowUnlimited) {
+      if (value.trim().equalsIgnoreCase(UNLIMITED)) {
+        return -1L;
+      }
+    }
+
+    // Decode the value.
+    Long i;
+    try {
+      i = SizeUnit.parseValue(value, SizeUnit.BYTES);
+    } catch (NumberFormatException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    try {
+      validateValue(i);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+    return i;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitSize(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, Long value, P p) {
+    return v.visitSize(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void toString(StringBuilder builder) {
+    super.toString(builder);
+
+    builder.append(" lowerLimit=");
+    builder.append(lowerLimit);
+
+    if (upperLimit != null) {
+      builder.append(" upperLimit=");
+      builder.append(upperLimit);
+    }
+
+    builder.append(" allowUnlimited=");
+    builder.append(allowUnlimited);
+
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int compare(Long o1, Long o2) {
+    return o1.compareTo(o2);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/SizeUnit.java b/opendj-admin/src/main/java/org/opends/server/admin/SizeUnit.java
new file mode 100644
index 0000000..d6ba4da
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/SizeUnit.java
@@ -0,0 +1,394 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+
+/**
+ * This enumeration defines various memory size units.
+ */
+public enum SizeUnit {
+
+  /**
+   * A byte unit.
+   */
+  BYTES(1L, "b", "bytes"),
+
+  /**
+   * A gibi-byte unit.
+   */
+  GIBI_BYTES((long) 1024 * 1024 * 1024, "gib", "gibibytes"),
+
+  /**
+   * A giga-byte unit.
+   */
+  GIGA_BYTES((long) 1000 * 1000 * 1000, "gb", "gigabytes"),
+
+  /**
+   * A kibi-byte unit.
+   */
+  KIBI_BYTES(1024L, "kib", "kibibytes"),
+
+  /**
+   * A kilo-byte unit.
+   */
+  KILO_BYTES(1000L, "kb", "kilobytes"),
+
+  /**
+   * A mebi-byte unit.
+   */
+  MEBI_BYTES((long) 1024 * 1024, "mib", "mebibytes"),
+
+  /**
+   * A mega-byte unit.
+   */
+  MEGA_BYTES((long) 1000 * 1000, "mb", "megabytes"),
+
+  /**
+   * A tebi-byte unit.
+   */
+  TEBI_BYTES((long) 1024 * 1024 * 1024 * 1024, "tib", "tebibytes"),
+
+  /**
+   * A tera-byte unit.
+   */
+  TERA_BYTES((long) 1000 * 1000 * 1000 * 1000, "tb", "terabytes");
+
+  // A lookup table for resolving a unit from its name.
+  private static final Map<String, SizeUnit> nameToUnit;
+  static {
+    nameToUnit = new HashMap<String, SizeUnit>();
+
+    for (SizeUnit unit : SizeUnit.values()) {
+      nameToUnit.put(unit.shortName, unit);
+      nameToUnit.put(unit.longName, unit);
+    }
+  }
+
+
+
+  /**
+   * Gets the best-fit unit for the specified number of bytes. The
+   * returned unit will be able to represent the number of bytes using
+   * a decimal number comprising of an integer part which is greater
+   * than zero. Bigger units are chosen in preference to smaller units
+   * and binary units are only returned if they are an exact fit. If
+   * the number of bytes is zero then the {@link #BYTES} unit is
+   * always returned. For example:
+   *
+   * <pre>
+   * getBestFitUnit(0)       // BYTES
+   * getBestFitUnit(999)     // BYTES
+   * getBestFitUnit(1000)    // KILO_BYTES
+   * getBestFitUnit(1024)    // KIBI_BYTES
+   * getBestFitUnit(1025)    // KILO_BYTES
+   * getBestFitUnit(999999)  // KILO_BYTES
+   * getBestFitUnit(1000000) // MEGA_BYTES
+   * </pre>
+   *
+   * @param bytes
+   *          The number of bytes.
+   * @return Returns the best fit unit.
+   * @throws IllegalArgumentException
+   *           If <code>bytes</code> is negative.
+   * @see #getBestFitUnitExact(long)
+   */
+  public static SizeUnit getBestFitUnit(long bytes)
+      throws IllegalArgumentException {
+    if (bytes < 0) {
+      throw new IllegalArgumentException("negative number of bytes: " + bytes);
+    } else if (bytes == 0) {
+      // Always use bytes for zero values.
+      return BYTES;
+    } else {
+      // Determine best fit: prefer non-binary units unless binary
+      // fits exactly.
+      SizeUnit[] nonBinary = new SizeUnit[] { TERA_BYTES, GIGA_BYTES,
+          MEGA_BYTES, KILO_BYTES };
+      SizeUnit[] binary = new SizeUnit[] { TEBI_BYTES, GIBI_BYTES, MEBI_BYTES,
+          KIBI_BYTES };
+
+      for (int i = 0; i < nonBinary.length; i++) {
+        if ((bytes % binary[i].getSize()) == 0) {
+          return binary[i];
+        } else if ((bytes / nonBinary[i].getSize()) > 0) {
+          return nonBinary[i];
+        }
+      }
+
+      return BYTES;
+    }
+  }
+
+
+
+  /**
+   * Gets the best-fit unit for the specified number of bytes which
+   * can represent the provided value using an integral value. Bigger
+   * units are chosen in preference to smaller units. If the number of
+   * bytes is zero then the {@link #BYTES} unit is always returned.
+   * For example:
+   *
+   * <pre>
+   * getBestFitUnitExact(0)       // BYTES
+   * getBestFitUnitExact(999)     // BYTES
+   * getBestFitUnitExact(1000)    // KILO_BYTES
+   * getBestFitUnitExact(1024)    // KIBI_BYTES
+   * getBestFitUnitExact(1025)    // BYTES
+   * getBestFitUnitExact(999999)  // BYTES
+   * getBestFitUnitExact(1000000) // MEGA_BYTES
+   * </pre>
+   *
+   * @param bytes
+   *          The number of bytes.
+   * @return Returns the best fit unit can represent the provided
+   *         value using an integral value.
+   * @throws IllegalArgumentException
+   *           If <code>bytes</code> is negative.
+   * @see #getBestFitUnit(long)
+   */
+  public static SizeUnit getBestFitUnitExact(long bytes)
+      throws IllegalArgumentException {
+    if (bytes < 0) {
+      throw new IllegalArgumentException("negative number of bytes: " + bytes);
+    } else if (bytes == 0) {
+      // Always use bytes for zero values.
+      return BYTES;
+    } else {
+      // Determine best fit.
+      SizeUnit[] units = new SizeUnit[] { TEBI_BYTES, TERA_BYTES, GIBI_BYTES,
+          GIGA_BYTES, MEBI_BYTES, MEGA_BYTES, KIBI_BYTES, KILO_BYTES };
+
+      for (SizeUnit unit : units) {
+        if ((bytes % unit.getSize()) == 0) {
+          return unit;
+        }
+      }
+
+      return BYTES;
+    }
+  }
+
+
+
+  /**
+   * Get the unit corresponding to the provided unit name.
+   *
+   * @param s
+   *          The name of the unit. Can be the abbreviated or long
+   *          name and can contain white space and mixed case
+   *          characters.
+   * @return Returns the unit corresponding to the provided unit name.
+   * @throws IllegalArgumentException
+   *           If the provided name did not correspond to a known
+   *           memory size unit.
+   */
+  public static SizeUnit getUnit(String s) throws IllegalArgumentException {
+    SizeUnit unit = nameToUnit.get(s.trim().toLowerCase());
+    if (unit == null) {
+      throw new IllegalArgumentException("Illegal memory size unit \"" + s
+          + "\"");
+    }
+    return unit;
+  }
+
+
+
+  /**
+   * Parse the provided size string and return its equivalent size in
+   * bytes. The size string must specify the unit e.g. "10kb".
+   *
+   * @param s
+   *          The size string to be parsed.
+   * @return Returns the parsed duration in bytes.
+   * @throws NumberFormatException
+   *           If the provided size string could not be parsed.
+   */
+  public static long parseValue(String s) throws NumberFormatException {
+    return parseValue(s, null);
+  }
+
+
+
+  /**
+   * Parse the provided size string and return its equivalent size in
+   * bytes.
+   *
+   * @param s
+   *          The size string to be parsed.
+   * @param defaultUnit
+   *          The default unit to use if there is no unit specified in
+   *          the size string, or <code>null</code> if the string
+   *          must always contain a unit.
+   * @return Returns the parsed size in bytes.
+   * @throws NumberFormatException
+   *           If the provided size string could not be parsed.
+   */
+  public static long parseValue(String s, SizeUnit defaultUnit)
+      throws NumberFormatException {
+    // Value must be a floating point number followed by a unit.
+    Pattern p = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*(\\w+)?\\s*$");
+    Matcher m = p.matcher(s);
+
+    if (!m.matches()) {
+      throw new NumberFormatException("Invalid size value \"" + s + "\"");
+    }
+
+    // Group 1 is the float.
+    double d;
+    try {
+      d = Double.valueOf(m.group(1));
+    } catch (NumberFormatException e) {
+      throw new NumberFormatException("Invalid size value \"" + s + "\"");
+    }
+
+    // Group 3 is the unit.
+    String unitString = m.group(3);
+    SizeUnit unit;
+    if (unitString == null) {
+      if (defaultUnit == null) {
+        throw new NumberFormatException("Invalid size value \"" + s + "\"");
+      } else {
+        unit = defaultUnit;
+      }
+    } else {
+      try {
+        unit = getUnit(unitString);
+      } catch (IllegalArgumentException e) {
+        throw new NumberFormatException("Invalid size value \"" + s + "\"");
+      }
+    }
+
+    return unit.toBytes(d);
+  }
+
+  // The long name of the unit.
+  private final String longName;
+
+  // The abbreviation of the unit.
+  private final String shortName;
+
+  // The size of the unit in bytes.
+  private final long sz;
+
+
+
+  // Private constructor.
+  private SizeUnit(long sz, String shortName, String longName) {
+    this.sz = sz;
+    this.shortName = shortName;
+    this.longName = longName;
+  }
+
+
+
+  /**
+   * Converts the specified size in bytes to this unit.
+   *
+   * @param amount
+   *          The size in bytes.
+   * @return Returns size in this unit.
+   */
+  public double fromBytes(long amount) {
+    return ((double) amount / sz);
+  }
+
+
+
+  /**
+   * Get the long name of this unit.
+   *
+   * @return Returns the long name of this unit.
+   */
+  public String getLongName() {
+    return longName;
+  }
+
+
+
+  /**
+   * Get the abbreviated name of this unit.
+   *
+   * @return Returns the abbreviated name of this unit.
+   */
+  public String getShortName() {
+    return shortName;
+  }
+
+
+
+  /**
+   * Get the number of bytes that this unit represents.
+   *
+   * @return Returns the number of bytes that this unit represents.
+   */
+  public long getSize() {
+    return sz;
+  }
+
+
+
+  /**
+   * Converts the specified size in this unit to bytes.
+   *
+   * @param amount
+   *          The size as a quantity of this unit.
+   * @return Returns the number of bytes that the size represents.
+   *
+   * @throws NumberFormatException
+   *           If the provided size exceeded long.MAX_VALUE.
+   */
+  public long toBytes(double amount) throws NumberFormatException {
+    double value =  sz * amount;
+    if (value > Long.MAX_VALUE)
+    {
+      throw new NumberFormatException
+        ("number too big (exceeded long.MAX_VALUE");
+    }
+    return (long) (value);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   * <p>
+   * This implementation returns the abbreviated name of this size
+   * unit.
+   */
+  @Override
+  public String toString() {
+    return shortName;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/StringPropertyDefinition.java b/opendj-admin/src/main/java/org/opends/server/admin/StringPropertyDefinition.java
new file mode 100644
index 0000000..56732e1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/StringPropertyDefinition.java
@@ -0,0 +1,335 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.Validator.ensureNotNull;
+
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+
+
+/**
+ * String property definition.
+ */
+public final class StringPropertyDefinition extends PropertyDefinition<String> {
+
+  /**
+   * An interface for incrementally constructing string property
+   * definitions.
+   */
+  public static class Builder extends
+      AbstractBuilder<String, StringPropertyDefinition> {
+
+    // Flag indicating whether values of this property are
+    // case-insensitive.
+    private boolean isCaseInsensitive = true;
+
+    // Optional pattern which values of this property must match.
+    private Pattern pattern = null;
+
+    // Pattern usage which provides a user-friendly summary of the
+    // pattern if present.
+    private String patternUsage = null;
+
+
+
+    // Private constructor
+    private Builder(AbstractManagedObjectDefinition<?, ?> d,
+        String propertyName) {
+      super(d, propertyName);
+    }
+
+
+
+    /**
+     * Set a flag indicating whether values of this property are
+     * case-insensitive.
+     *
+     * @param value
+     *          <code>true</code> if values are case-insensitive, or
+     *          <code>false</code> otherwise.
+     */
+    public final void setCaseInsensitive(boolean value) {
+      isCaseInsensitive = value;
+    }
+
+
+
+    /**
+     * Set the regular expression pattern which values of this
+     * property must match. By default there is no pattern defined.
+     *
+     * @param pattern
+     *          The regular expression pattern string, or
+     *          <code>null</code> if there is no pattern.
+     * @param patternUsage
+     *          A user-friendly usage string representing the pattern
+     *          which can be used in error messages and help (e.g. for
+     *          patterns which match a host/port combination, the
+     *          usage string "HOST:PORT" would be appropriate).
+     * @throws PatternSyntaxException
+     *           If the provided regular expression pattern has an
+     *           invalid syntax.
+     */
+    public final void setPattern(String pattern, String patternUsage)
+        throws PatternSyntaxException {
+      if (pattern == null) {
+        this.pattern = null;
+        this.patternUsage = null;
+      } else {
+        this.pattern = Pattern.compile(pattern);
+        this.patternUsage = patternUsage;
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected StringPropertyDefinition buildInstance(
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
+        EnumSet<PropertyOption> options,
+        AdministratorAction adminAction,
+        DefaultBehaviorProvider<String> defaultBehavior) {
+      return new StringPropertyDefinition(d, propertyName, options,
+          adminAction, defaultBehavior, isCaseInsensitive, pattern,
+          patternUsage);
+    }
+
+  }
+
+
+
+  /**
+   * Create a string property definition builder.
+   *
+   * @param d
+   *          The managed object definition associated with this
+   *          property definition.
+   * @param propertyName
+   *          The property name.
+   * @return Returns the new string property definition builder.
+   */
+  public static Builder createBuilder(AbstractManagedObjectDefinition<?, ?> d,
+      String propertyName) {
+    return new Builder(d, propertyName);
+  }
+
+  // Flag indicating whether values of this property are
+  // case-insensitive.
+  private final boolean isCaseInsensitive;
+
+  // Optional pattern which values of this property must match.
+  private final Pattern pattern;
+
+  // Pattern usage which provides a user-friendly summary of the
+  // pattern if present.
+  private final String patternUsage;
+
+
+
+  // Private constructor.
+  private StringPropertyDefinition(AbstractManagedObjectDefinition<?, ?> d,
+      String propertyName, EnumSet<PropertyOption> options,
+      AdministratorAction adminAction,
+      DefaultBehaviorProvider<String> defaultBehavior,
+      boolean isCaseInsensitive, Pattern pattern, String patternUsage) {
+    super(d, String.class, propertyName, options, adminAction,
+        defaultBehavior);
+    this.isCaseInsensitive = isCaseInsensitive;
+    this.pattern = pattern;
+    this.patternUsage = patternUsage;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
+    return v.visitString(this, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
+    return v.visitString(this, value, p);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String decodeValue(String value)
+      throws IllegalPropertyValueStringException {
+    ensureNotNull(value);
+
+    try {
+      validateValue(value);
+    } catch (IllegalPropertyValueException e) {
+      throw new IllegalPropertyValueStringException(this, value);
+    }
+
+    return value;
+  }
+
+
+
+  /**
+   * Gets the optional regular expression pattern which values of this
+   * property must match.
+   *
+   * @return Returns the optional regular expression pattern which
+   *         values of this property must match, or <code>null</code>
+   *         if there is no pattern.
+   */
+  public Pattern getPattern() {
+    return pattern;
+  }
+
+
+
+  /**
+   * Gets the pattern synopsis of this string property definition in
+   * the default locale.
+   *
+   * @return Returns the pattern synopsis of this string property
+   *         definition in the default locale, or <code>null</code>
+   *         if there is no pattern synopsis (which is the case when
+   *         there is no pattern matching defined for this string
+   *         property definition).
+   */
+  public Message getPatternSynopsis() {
+    return getPatternSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the optional pattern synopsis of this string property
+   * definition in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the pattern synopsis of this string property
+   *         definition in the specified locale, or <code>null</code>
+   *         if there is no pattern synopsis (which is the case when
+   *         there is no pattern matching defined for this string
+   *         property definition).
+   */
+  public Message getPatternSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "property." + getName()
+        + ".syntax.string.pattern.synopsis";
+    try {
+      return resource
+          .getMessage(getManagedObjectDefinition(), property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Gets a user-friendly usage string representing the pattern which
+   * can be used in error messages and help (e.g. for patterns which
+   * match a host/port combination, the usage string "HOST:PORT" would
+   * be appropriate).
+   *
+   * @return Returns the user-friendly pattern usage string, or
+   *         <code>null</code> if there is no pattern.
+   */
+  public String getPatternUsage() {
+    return patternUsage;
+  }
+
+
+
+  /**
+   * Query whether values of this property are case-insensitive.
+   *
+   * @return Returns <code>true</code> if values are
+   *         case-insensitive, or <code>false</code> otherwise.
+   */
+  public boolean isCaseInsensitive() {
+    return isCaseInsensitive;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String normalizeValue(String value)
+      throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    if (isCaseInsensitive()) {
+      return value.trim().toLowerCase();
+    } else {
+      return value.trim();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void validateValue(String value) throws IllegalPropertyValueException {
+    ensureNotNull(value);
+
+    if (pattern != null) {
+      Matcher matcher = pattern.matcher(value);
+      if (!matcher.matches()) {
+        throw new IllegalPropertyValueException(this, value);
+      }
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/Tag.java b/opendj-admin/src/main/java/org/opends/server/admin/Tag.java
new file mode 100644
index 0000000..fcc4166
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/Tag.java
@@ -0,0 +1,212 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+import org.opends.messages.Message;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * An interface for querying the properties of a tag.
+ * <p>
+ * Tags are used to group related managed objects together into
+ * categories.
+ */
+public final class Tag implements Comparable<Tag> {
+
+  // All the tags.
+  private static final Map<String, Tag> tags = new HashMap<String, Tag>();
+
+
+
+  /**
+   * Defines a new tag with the specified name.
+   *
+   * @param name
+   *          The name of the new tag.
+   */
+  public static void define(String name) {
+    Tag tag = new Tag(name);
+
+    // Register the tag.
+    tags.put(name, tag);
+  }
+
+
+
+  /**
+   * Returns the tag associated with the specified name.
+   *
+   * @param name
+   *          The name of the tag.
+   * @return Returns the tag associated with the specified name.
+   * @throws IllegalArgumentException
+   *           If the tag name was not recognized.
+   */
+  public static Tag valueOf(String name) throws IllegalArgumentException {
+    Validator.ensureNotNull(name);
+
+    // Hack to force initialization of the tag definitions.
+    RootCfgDefn.getInstance();
+
+    Tag tag = tags.get(name.toLowerCase());
+
+    if (tag == null) {
+      throw new IllegalArgumentException("Unknown tag \"" + name + "\"");
+    }
+
+    return tag;
+  }
+
+
+
+  /**
+   * Returns an unmodifiable collection view of the set of registered
+   * tags.
+   *
+   * @return Returns an unmodifiable collection view of the set of
+   *         registered tags.
+   */
+  public static Collection<Tag> values() {
+    // Hack to force initialization of the tag definitions.
+    RootCfgDefn.getInstance();
+
+    return Collections.unmodifiableCollection(tags.values());
+  }
+
+  // The name of the tag.
+  private final String name;
+
+
+
+  // Private constructor.
+  private Tag(String name) {
+    this.name = name;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final int compareTo(Tag o) {
+    return name.compareTo(o.name);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public final boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+
+    if (obj instanceof Tag) {
+      Tag other = (Tag) obj;
+      return other.name.equals(this.name);
+    }
+
+    return false;
+  }
+
+
+
+  /**
+   * Gets the name of this tag.
+   *
+   * @return Returns the name of this tag.
+   */
+  public final String getName() {
+    return name;
+  }
+
+
+
+  /**
+   * Gets the synopsis of this tag in the default locale.
+   *
+   * @return Returns the synopsis of this tag in the default locale.
+   */
+  public final Message getSynopsis() {
+    return getSynopsis(Locale.getDefault());
+  }
+
+
+
+  /**
+   * Gets the synopsis of this tag in the specified locale.
+   *
+   * @param locale
+   *          The locale.
+   * @return Returns the synopsis of this tag in the specified locale.
+   */
+  public final Message getSynopsis(Locale locale) {
+    ManagedObjectDefinitionI18NResource resource =
+      ManagedObjectDefinitionI18NResource.getInstance();
+    String property = "tag." + name + ".synopsis";
+    try {
+      return resource.getMessage(RootCfgDefn.getInstance(), property, locale);
+    } catch (MissingResourceException e) {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public final int hashCode() {
+    return name.hashCode();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public final String toString() {
+    return name;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/TopCfgDefn.java b/opendj-admin/src/main/java/org/opends/server/admin/TopCfgDefn.java
new file mode 100644
index 0000000..240b55a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/TopCfgDefn.java
@@ -0,0 +1,74 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * Configuration definition <code>TopCfgDefn</code> is the root of
+ * the configuration definition hierarchy. Every configuration has
+ * <code>TopCfgDefn</code> as a superclass.
+ * <p>
+ * The <code>TopCfgDefn</code> has no properties or relations.
+ * However, it can be used to determine all the configuration
+ * definitions currently available to the administration framework
+ * using the {@link #getAllChildren()}.
+ * <p>
+ * <b>NOTE:</b> it is not possible to retrieve I18N related
+ * information or profile information for this managed object
+ * definition. In particular, calls to the methods
+ * {@link #getSynopsis()}, {@link #getDescription()},
+ * {@link #getUserFriendlyName()}, and
+ * {@link #getUserFriendlyPluralName()} will not work.
+ */
+public final class TopCfgDefn extends
+    AbstractManagedObjectDefinition<ConfigurationClient, Configuration> {
+
+  // The singleton configuration definition instance.
+  private static final TopCfgDefn INSTANCE = new TopCfgDefn();
+
+
+
+  /**
+   * Get the Top configuration definition singleton.
+   *
+   * @return Returns the Top configuration definition singleton.
+   */
+  public static TopCfgDefn getInstance() {
+    return INSTANCE;
+  }
+
+
+
+  /**
+   * Private constructor.
+   */
+  private TopCfgDefn() {
+    super("top", null);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/UndefinedDefaultBehaviorProvider.java b/opendj-admin/src/main/java/org/opends/server/admin/UndefinedDefaultBehaviorProvider.java
new file mode 100644
index 0000000..f9d695f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/UndefinedDefaultBehaviorProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin;
+
+
+
+/**
+ * A default behavior provider which indicates undefined behavior. It should
+ * be used by properties which have no default values or behavior as such. For
+ * example, a description property, when left unset, has no default value and no
+ * side-effects.
+ *
+ * @param <T>
+ *          The type of values represented by this provider.
+ */
+public final class UndefinedDefaultBehaviorProvider<T> extends
+    DefaultBehaviorProvider<T> {
+
+  /**
+   * Create an undefined default behavior provider.
+   */
+  public UndefinedDefaultBehaviorProvider() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <R, P> R accept(DefaultBehaviorProviderVisitor<T, R, P> v, P p) {
+    return v.visitUndefined(this, p);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/UnknownPropertyDefinitionException.java b/opendj-admin/src/main/java/org/opends/server/admin/UnknownPropertyDefinitionException.java
new file mode 100644
index 0000000..a58eda5
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/UnknownPropertyDefinitionException.java
@@ -0,0 +1,78 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+
+
+/**
+ * Indicates that an unknown type of property definition was
+ * encountered. This can occur as the management prototype develops
+ * and new kinds of property definitions are added.
+ */
+public final class UnknownPropertyDefinitionException
+    extends PropertyException {
+
+  // Generated serialization ID.
+  private static final long serialVersionUID = 7042646409131322385L;
+
+  // The visitor parameter if there was one.
+  private Object parameter;
+
+
+
+  /**
+   * Creates a new unknown property definition exception.
+   *
+   * @param pd
+   *          The unknown property definition.
+   * @param p
+   *          The visitor parameter if there was one.
+   */
+  public UnknownPropertyDefinitionException(PropertyDefinition<?> pd,
+      Object p) {
+    super(pd, ERR_UNKNOWN_PROPERTY_DEFINITION_EXCEPTION.get(pd.getName(), pd
+        .getClass().getName()));
+    this.parameter = p;
+  }
+
+
+
+  /**
+   * Get the visitor parameter if there was one.
+   *
+   * @return Returns the visitor parameter if there was one.
+   */
+  public Object getParameter() {
+    return parameter;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/AdminClientException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/AdminClientException.java
new file mode 100644
index 0000000..b596166
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/AdminClientException.java
@@ -0,0 +1,80 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import org.opends.messages.Message;
+
+import org.opends.server.admin.AdminException;
+
+
+
+/**
+ * Administration client exceptions represent non-operational problems
+ * which occur whilst interacting with the administration framework.
+ * They provide clients with a transport independent interface for
+ * handling transport related exceptions.
+ * <p>
+ * Client exceptions represent communications problems, security
+ * problems, and service related problems.
+ */
+public abstract class AdminClientException extends AdminException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 4044747533980824456L;
+
+
+
+  /**
+   * Create an administration client exception with a message and
+   * cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected AdminClientException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an administration client exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected AdminClientException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/AdminSecurityException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/AdminSecurityException.java
new file mode 100644
index 0000000..2f92e71
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/AdminSecurityException.java
@@ -0,0 +1,74 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * This exception is thrown when a security related problem occurs
+ * whilst interacting with the Directory Server. These fall broadly
+ * into two categories: authentication problems and authorization
+ * problems.
+ */
+public abstract class AdminSecurityException extends AdminClientException {
+
+  /**
+   * Fake serialization ID.
+   */
+  private static final long serialVersionUID = 1L;
+
+
+
+  /**
+   * Create a security exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  protected AdminSecurityException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create a security exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  protected AdminSecurityException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationException.java
new file mode 100644
index 0000000..c5f6ab7
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationException.java
@@ -0,0 +1,97 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * This exception is thrown when an authentication error occurs while
+ * connecting to the Directory Server. An authentication error can
+ * happen, for example, when the client credentials are invalid.
+ */
+public class AuthenticationException extends AdminSecurityException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 3544797197747686958L;
+
+
+
+  /**
+   * Creates an authentication exception with a default message.
+   */
+  public AuthenticationException() {
+    super(ERR_AUTHENTICATION_EXCEPTION_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Create an authentication exception with a cause and a default
+   * message.
+   *
+   * @param cause
+   *          The cause.
+   */
+  public AuthenticationException(Throwable cause) {
+    super(ERR_AUTHENTICATION_EXCEPTION_DEFAULT.get(), cause);
+  }
+
+
+
+  /**
+   * Create an authentication exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  public AuthenticationException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an authentication exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  public AuthenticationException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationNotSupportedException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationNotSupportedException.java
new file mode 100644
index 0000000..7f66a59
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthenticationNotSupportedException.java
@@ -0,0 +1,99 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * This exception is thrown when the particular flavor of
+ * authentication requested is not supported by the Directory Server.
+ */
+public class AuthenticationNotSupportedException
+    extends AdminSecurityException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 7387834052676291793L;
+
+
+
+  /**
+   * Creates an authentication not supported exception with a default
+   * message.
+   */
+  public AuthenticationNotSupportedException() {
+    super(ERR_AUTHENTICATION_NOT_SUPPORTED_EXCEPTION_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Creates an authentication not supported exception with a cause
+   * and a default message.
+   *
+   * @param cause
+   *          The cause.
+   */
+  public AuthenticationNotSupportedException(Throwable cause) {
+    super(ERR_AUTHENTICATION_NOT_SUPPORTED_EXCEPTION_DEFAULT.get(), cause);
+  }
+
+
+
+  /**
+   * Create an authentication not supported exception with a message
+   * and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  public AuthenticationNotSupportedException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an authentication not supported exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  public AuthenticationNotSupportedException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/AuthorizationException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthorizationException.java
new file mode 100644
index 0000000..5f8b3ef
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/AuthorizationException.java
@@ -0,0 +1,98 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * This exception is thrown when an authorization error occurs while
+ * interacting with the Directory Server. Authorization errors can
+ * occur when a client attempts to perform an administrative operation
+ * which they are not permitted to perform.
+ */
+public class AuthorizationException extends AdminSecurityException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 8414248362572933814L;
+
+
+
+  /**
+   * Create an authorization exception with a default message.
+   */
+  public AuthorizationException() {
+    super(ERR_AUTHORIZATION_EXCEPTION_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Create an authorization exception with a cause and a default
+   * message.
+   *
+   * @param cause
+   *          The cause.
+   */
+  public AuthorizationException(Throwable cause) {
+    super(ERR_AUTHORIZATION_EXCEPTION_DEFAULT.get(), cause);
+  }
+
+
+
+  /**
+   * Create an authorization exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  public AuthorizationException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create an authorization exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  public AuthorizationException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ClientConstraintHandler.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ClientConstraintHandler.java
new file mode 100644
index 0000000..fac52ab
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ClientConstraintHandler.java
@@ -0,0 +1,165 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.client;
+
+
+
+import java.util.Collection;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.ManagedObjectPath;
+
+
+
+/**
+ * An interface for performing client-side constraint validation.
+ * <p>
+ * Constraints are evaluated immediately before the client performs a
+ * write operation. If one or more constraints fails, the write
+ * operation is refused and fails with an
+ * {@link OperationRejectedException}.
+ * <p>
+ * A client constraint handler must override at least one of the
+ * provided methods.
+ *
+ * @see org.opends.server.admin.Constraint
+ */
+public abstract class ClientConstraintHandler {
+
+  /**
+   * Creates a new client constraint handler.
+   */
+  protected ClientConstraintHandler() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Determines whether or not the newly created managed object which
+   * is about to be added to the server configuration satisfies this
+   * constraint.
+   * <p>
+   * If the constraint is not satisfied, the implementation must
+   * return <code>false</code> and add a message describing why the
+   * constraint was not satisfied.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @param managedObject
+   *          The new managed object.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this constraint is
+   *         satisfied, or <code>false</code> if it is not.
+   * @throws AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isAddAcceptable(ManagementContext context,
+      ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    return true;
+  }
+
+
+
+  /**
+   * Determines whether or not the changes to an existing managed
+   * object which are about to be committed to the server
+   * configuration satisfies this constraint.
+   * <p>
+   * If the constraint is not satisfied, the implementation must
+   * return <code>false</code> and add a message describing why the
+   * constraint was not satisfied.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @param managedObject
+   *          The modified managed object.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this modify is satisfied,
+   *         or <code>false</code> if it is not.
+   * @throws AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isModifyAcceptable(ManagementContext context,
+      ManagedObject<?> managedObject, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    return true;
+  }
+
+
+
+  /**
+   * Determines whether or not the existing managed object which is
+   * about to be deleted from the server configuration satisfies this
+   * constraint.
+   * <p>
+   * If the constraint is not satisfied, the implementation must
+   * return <code>false</code> and add a message describing why the
+   * constraint was not satisfied.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param context
+   *          The management context.
+   * @param path
+   *          The path of the managed object which is about to be
+   *          deleted.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this constraint is
+   *         satisfied, or <code>false</code> if it is not.
+   * @throws AuthorizationException
+   *           If an authorization failure prevented this constraint
+   *           from being evaluated.
+   * @throws CommunicationException
+   *           If a communications problem prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isDeleteAcceptable(ManagementContext context,
+      ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons)
+      throws AuthorizationException, CommunicationException {
+    return true;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/CommunicationException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/CommunicationException.java
new file mode 100644
index 0000000..45815bd
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/CommunicationException.java
@@ -0,0 +1,100 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+
+
+
+/**
+ * This exception is thrown when a communications related problem
+ * occurs whilst interacting with the Directory Server. This may be
+ * caused by problems such as network partitioning, the unavailability
+ * of the Directory Server, or other failures on the client or server
+ * side.
+ */
+public class CommunicationException extends AdminClientException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 9093195928501281027L;
+
+
+
+  /**
+   * Create a communication exception with a default message.
+   */
+  public CommunicationException() {
+    super(ERR_COMMUNICATION_EXCEPTION_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Create a communication exception with a cause and a default
+   * message.
+   *
+   * @param cause
+   *          The cause.
+   */
+  public CommunicationException(Throwable cause) {
+    super(ERR_COMMUNICATION_EXCEPTION_DEFAULT_CAUSE.get(cause.getMessage()),
+        cause);
+  }
+
+
+
+  /**
+   * Create a communication exception with a message and cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  public CommunicationException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create a communication exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  public CommunicationException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ConcurrentModificationException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ConcurrentModificationException.java
new file mode 100644
index 0000000..3e6c3df
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ConcurrentModificationException.java
@@ -0,0 +1,100 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.OperationsException;
+
+
+
+/**
+ * This exception is thrown when a critical concurrent modification is
+ * detected by the client. This may be caused by another client
+ * application removing a managed object whilst it is being managed.
+ */
+public class ConcurrentModificationException extends OperationsException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -1467024486347612820L;
+
+
+
+  /**
+   * Create a concurrent modification exception with a default
+   * message.
+   */
+  public ConcurrentModificationException() {
+    super(ERR_CONCURRENT_MODIFICATION_EXCEPTION_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Create a concurrent modification exception with a cause and a
+   * default message.
+   *
+   * @param cause
+   *          The cause.
+   */
+  public ConcurrentModificationException(Throwable cause) {
+    super(ERR_CONCURRENT_MODIFICATION_EXCEPTION_DEFAULT.get(), cause);
+  }
+
+
+
+  /**
+   * Create a concurrent modification exception with a message and
+   * cause.
+   *
+   * @param message
+   *          The message.
+   * @param cause
+   *          The cause.
+   */
+  public ConcurrentModificationException(Message message, Throwable cause) {
+    super(message, cause);
+  }
+
+
+
+  /**
+   * Create a concurrent modification exception with a message.
+   *
+   * @param message
+   *          The message.
+   */
+  public ConcurrentModificationException(Message message) {
+    super(message);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/IllegalManagedObjectNameException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/IllegalManagedObjectNameException.java
new file mode 100644
index 0000000..d8957f1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/IllegalManagedObjectNameException.java
@@ -0,0 +1,143 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.OperationsException;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyDefinitionUsageBuilder;
+
+
+
+/**
+ * Thrown when an attempt is made to create a new managed object with
+ * an illegal name.
+ * <p>
+ * This exception can occur when a new managed object is given a name
+ * which is either an empty string, a string containing just
+ * white-spaces, or a string which is invalid according to the managed
+ * object's naming property (if it has one).
+ */
+public class IllegalManagedObjectNameException extends OperationsException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 7491748228684293291L;
+
+
+
+  // Create the message
+  private static Message createMessage(String illegalName,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    if (illegalName.length() == 0) {
+      return ERR_ILLEGAL_MANAGED_OBJECT_NAME_EXCEPTION_EMPTY.get();
+    } else if (illegalName.trim().length() == 0) {
+      return ERR_ILLEGAL_MANAGED_OBJECT_NAME_EXCEPTION_BLANK.get();
+    } else if (namingPropertyDefinition != null) {
+      try {
+        namingPropertyDefinition.decodeValue(illegalName);
+      } catch (IllegalPropertyValueStringException e) {
+        PropertyDefinitionUsageBuilder builder =
+          new PropertyDefinitionUsageBuilder(true);
+        return ERR_ILLEGAL_MANAGED_OBJECT_NAME_EXCEPTION_SYNTAX.get(
+            illegalName, namingPropertyDefinition.getName(), builder
+                .getUsage(namingPropertyDefinition));
+      }
+    }
+
+    return ERR_ILLEGAL_MANAGED_OBJECT_NAME_EXCEPTION_OTHER.get(illegalName);
+  }
+
+  // The illegal name.
+  private final String illegalName;
+
+  // The naming property definition if applicable.
+  private final PropertyDefinition<?> namingPropertyDefinition;
+
+
+
+  /**
+   * Create a new illegal name exception and no naming property
+   * definition.
+   *
+   * @param illegalName
+   *          The illegal managed object name.
+   */
+  public IllegalManagedObjectNameException(String illegalName) {
+    this(illegalName, null);
+  }
+
+
+
+  /**
+   * Create a new illegal name exception and a naming property
+   * definition.
+   *
+   * @param illegalName
+   *          The illegal managed object name.
+   * @param namingPropertyDefinition
+   *          The naming property definition.
+   */
+  public IllegalManagedObjectNameException(String illegalName,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    super(createMessage(illegalName, namingPropertyDefinition));
+
+    this.illegalName = illegalName;
+    this.namingPropertyDefinition = namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * Get the illegal managed object name.
+   *
+   * @return Returns the illegal managed object name.
+   */
+  public String getIllegalName() {
+    return illegalName;
+  }
+
+
+
+  /**
+   * Get the naming property definition if applicable.
+   *
+   * @return Returns naming property definition, or <code>null</code>
+   *         if none was specified.
+   */
+  public PropertyDefinition<?> getNamingPropertyDefinition() {
+    return namingPropertyDefinition;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObject.java
new file mode 100644
index 0000000..3b16697
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObject.java
@@ -0,0 +1,938 @@
+/*
+ * 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 2007-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import java.util.Collection;
+import java.util.SortedSet;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectAlreadyExistsException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsReadOnlyException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyProvider;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+
+
+
+/**
+ * A generic interface for accessing client-side managed objects.
+ * <p>
+ * A managed object comprises of zero or more properties. A property
+ * has associated with it three sets of property value(s). These are:
+ * <ul>
+ * <li><i>default value(s)</i> - these value(s) represent the
+ * default behavior for the property when it has no active values.
+ * When a property inherits its default value(s) from elsewhere (i.e.
+ * a property in another managed object), the default value(s)
+ * represent the active value(s) of the inherited property at the time
+ * the managed object was retrieved
+ * <li><i>active value(s)</i> - these value(s) represent the state
+ * of the property at the time the managed object was retrieved
+ * <li><i>pending value(s)</i> - these value(s) represent any
+ * modifications made to the property's value(s) since the managed
+ * object object was retrieved and before the changes have been
+ * committed using the {@link #commit()} method, the pending values
+ * can be empty indicating that the property should be modified back
+ * to its default values.
+ * </ul>
+ * In addition, a property has an <i>effective state</i> defined by
+ * its <i>effective values</i> which are derived by evaluating the
+ * following rules in the order presented:
+ * <ul>
+ * <li>the <i>pending values</i> if defined and non-empty
+ * <li>or, the <i>default values</i> if the pending values are
+ * defined but are empty
+ * <li>or, the <i>active values</i> if defined and non-empty
+ * <li>or, the <i>default values</i> if there are no active values
+ * <li>or, an empty set of values, if there are no default values.
+ * </ul>
+ *
+ * @param <T>
+ *          The type of client configuration represented by the client
+ *          managed object.
+ */
+public interface ManagedObject<T extends ConfigurationClient> extends
+    PropertyProvider {
+
+  /**
+   * Adds this managed object to the server or commits any changes
+   * made to it depending on whether or not the managed object already
+   * exists on the server. Pending property values will be committed
+   * to the managed object. If successful, the pending values will
+   * become active values.
+   * <p>
+   * See the class description for more information regarding pending
+   * and active values.
+   *
+   * @throws ManagedObjectAlreadyExistsException
+   *           If the managed object cannot be added to the server
+   *           because it already exists.
+   * @throws MissingMandatoryPropertiesException
+   *           If the managed object contains some mandatory
+   *           properties which have been left undefined.
+   * @throws ConcurrentModificationException
+   *           If the managed object is being added to the server but
+   *           its parent has been removed by another client, or if
+   *           this managed object is being modified but it has been
+   *           removed from the server by another client.
+   * @throws OperationRejectedException
+   *           If this managed object cannot be added or modified due
+   *           to some client-side or server-side constraint which
+   *           cannot be satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to add or modify this managed
+   *           object because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  void commit() throws ManagedObjectAlreadyExistsException,
+      MissingMandatoryPropertiesException, ConcurrentModificationException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException;
+
+
+  /**
+   * Determines whether or not this managed object has been modified since it
+   * was constructed.
+   * In other words, whether or not the set of pending values differs from
+   * the set of active values.
+   *
+   * @return Returns <code>true</code> if this managed object has been
+   *         modified since it was constructed.
+   */
+  boolean isModified();
+
+
+  /**
+   * Creates a new child managed object bound to the specified
+   * instantiable relation. The new managed object will initially not
+   * contain any property values (including mandatory properties).
+   * Once the managed object has been configured it can be added to
+   * the server using the {@link #commit()} method.
+   *
+   * @param <C>
+   *          The expected type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The expected type of the child managed object
+   *          server configuration.
+   * @param <CC>
+   *          The actual type of the added managed object
+   *          configuration client.
+   * @param r
+   *          The instantiable relation definition.
+   * @param d
+   *          The definition of the managed object to be created.
+   * @param name
+   *          The name of the child managed object.
+   * @param exceptions
+   *          A collection in which to place any
+   *          {@link DefaultBehaviorException}s that occurred whilst
+   *          attempting to determine the managed object's default
+   *          values.
+   * @return Returns a new child managed object bound to the specified
+   *         instantiable relation.
+   * @throws IllegalManagedObjectNameException
+   *           If the name of the child managed object is invalid.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration, CC extends C>
+  ManagedObject<CC> createChild(InstantiableRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d, String name,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalManagedObjectNameException, IllegalArgumentException;
+
+
+
+  /**
+   * Creates a new child managed object bound to the specified
+   * optional relation. The new managed object will initially not
+   * contain any property values (including mandatory properties).
+   * Once the managed object has been configured it can be added to
+   * the server using the {@link #commit()} method.
+   *
+   * @param <C>
+   *          The expected type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The expected type of the child managed object
+   *          server configuration.
+   * @param <CC>
+   *          The actual type of the added managed object
+   *          configuration client.
+   * @param r
+   *          The optional relation definition.
+   * @param d
+   *          The definition of the managed object to be created.
+   * @param exceptions
+   *          A collection in which to place any
+   *          {@link DefaultBehaviorException}s that occurred whilst
+   *          attempting to determine the managed object's default
+   *          values.
+   * @return Returns a new child managed object bound to the specified
+   *         optional relation.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration, CC extends C>
+  ManagedObject<CC> createChild(OptionalRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Creates a new child managed object bound to the specified
+   * set relation. The new managed object will initially not
+   * contain any property values (including mandatory properties).
+   * Once the managed object has been configured it can be added to
+   * the server using the {@link #commit()} method.
+   *
+   * @param <C>
+   *          The expected type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The expected type of the child managed object
+   *          server configuration.
+   * @param <CC>
+   *          The actual type of the added managed object
+   *          configuration client.
+   * @param r
+   *          The set relation definition.
+   * @param d
+   *          The definition of the managed object to be created.
+   * @param exceptions
+   *          A collection in which to place any
+   *          {@link DefaultBehaviorException}s that occurred whilst
+   *          attempting to determine the managed object's default
+   *          values.
+   * @return Returns a new child managed object bound to the specified
+   *         set relation.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   */
+  <C extends ConfigurationClient, S extends Configuration, CC extends C>
+  ManagedObject<CC> createChild(SetRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Retrieves an instantiable child managed object.
+   *
+   * @param <C>
+   *          The requested type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns the instantiable child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(InstantiableRelationDefinition<C, S> r,
+      String name) throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Retrieves an optional child managed object.
+   *
+   * @param <C>
+   *          The requested type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The optional relation definition.
+   * @return Returns the optional child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(OptionalRelationDefinition<C, S> r)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Retrieves a singleton child managed object.
+   *
+   * @param <C>
+   *          The requested type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The singleton relation definition.
+   * @return Returns the singleton child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(SingletonRelationDefinition<C, S> r)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Retrieves a set child managed object.
+   *
+   * @param <C>
+   *          The requested type of the child managed object
+   *          configuration client.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The set relation definition.
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns the set child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(SetRelationDefinition<C, S> r,
+      String name) throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Creates a client configuration view of this managed object.
+   * Modifications made to this managed object will be reflected in
+   * the client configuration view and vice versa.
+   *
+   * @return Returns a client configuration view of this managed
+   *         object.
+   */
+  T getConfiguration();
+
+
+
+  /**
+   * Gets the definition associated with this managed object.
+   *
+   * @return Returns the definition associated with this managed
+   *         object.
+   */
+  ManagedObjectDefinition<T, ? extends Configuration>
+    getManagedObjectDefinition();
+
+
+
+  /**
+   * Gets the path of this managed object.
+   *
+   * @return Returns the path of this managed object.
+   */
+  ManagedObjectPath<T, ? extends Configuration> getManagedObjectPath();
+
+
+
+  /**
+   * Gets a mutable copy of the set of default values for the
+   * specified property.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's default values, or an empty set if
+   *         there are no default values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  <PD> SortedSet<PD> getPropertyDefaultValues(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Gets the effective value of the specified property.
+   * <p>
+   * See the class description for more information about how the
+   * effective property value is derived.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> if there is no effective value
+   *         defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Gets a mutable copy of the set of effective values for the
+   * specified property.
+   * <p>
+   * See the class description for more information about how the
+   * effective property values are derived.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no effective values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Determines whether or not the specified property is set. If the
+   * property is unset, then any default behavior associated with the
+   * property applies.
+   *
+   * @param pd
+   *          The property definition.
+   * @return Returns <code>true</code> if the property has been set,
+   *         or <code>false</code> if it is unset and any default
+   *         behavior associated with the property applies.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  boolean isPropertyPresent(PropertyDefinition<?> pd)
+      throws IllegalArgumentException;
+
+
+
+  /**
+   * Determines whether or not the optional managed object associated
+   * with the specified optional relations exists.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional managed
+   *         object exists, <code>false</code> otherwise.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  boolean hasChild(OptionalRelationDefinition<C, S> r)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified
+   * instantiable relation.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(InstantiableRelationDefinition<C, S> r)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified
+   * instantiable relation which are a sub-type of the specified
+   * managed object definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The instantiable relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(InstantiableRelationDefinition<C, S> r,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified set
+   * relation.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The set relation definition.
+   * @return Returns the names of the child managed objects which for
+   *         set relations are the definition names of each managed
+   *         object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(SetRelationDefinition<C, S> r)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified set
+   * relation which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The set relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which for
+   *         set relations are the definition names of each managed
+   *         object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(SetRelationDefinition<C, S> r,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Removes the named instantiable child managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the managed object could not be removed because it
+   *           could not found on the server.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(InstantiableRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Removes an optional child managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The optional relation definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the managed object could not be removed because it
+   *           could not found on the server.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(OptionalRelationDefinition<C, S> r)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Removes s set child managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param r
+   *          The set relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the managed object could not be removed because it
+   *           could not found on the server.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(SetRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Sets a new pending value for the specified property.
+   * <p>
+   * See the class description for more information regarding pending
+   * values.
+   *
+   * @param <PD>
+   *          The type of the property to be modified.
+   * @param pd
+   *          The property to be modified.
+   * @param value
+   *          The new pending value for the property, or
+   *          <code>null</code> if the property should be reset to
+   *          its default behavior.
+   * @throws IllegalPropertyValueException
+   *           If the new pending value is deemed to be invalid
+   *           according to the property definition.
+   * @throws PropertyIsReadOnlyException
+   *           If this is not a new managed object and the property is
+   *           read-only or for monitoring purposes.
+   * @throws PropertyIsMandatoryException
+   *           If an attempt was made to remove a mandatory property.
+   * @throws IllegalArgumentException
+   *           If the specified property definition is not associated
+   *           with this managed object.
+   */
+  <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
+      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException;
+
+
+
+  /**
+   * Sets a new pending values for the specified property.
+   * <p>
+   * See the class description for more information regarding pending
+   * values.
+   *
+   * @param <PD>
+   *          The type of the property to be modified.
+   * @param pd
+   *          The property to be modified.
+   * @param values
+   *          A non-<code>null</code> set of new pending values for
+   *          the property (an empty set indicates that the property
+   *          should be reset to its default behavior). The set will
+   *          not be referenced by this managed object.
+   * @throws IllegalPropertyValueException
+   *           If a new pending value is deemed to be invalid
+   *           according to the property definition.
+   * @throws PropertyIsSingleValuedException
+   *           If an attempt was made to add multiple pending values
+   *           to a single-valued property.
+   * @throws PropertyIsReadOnlyException
+   *           If this is not a new managed object and the property is
+   *           read-only or for monitoring purposes.
+   * @throws PropertyIsMandatoryException
+   *           If an attempt was made to remove a mandatory property.
+   * @throws IllegalArgumentException
+   *           If the specified property definition is not associated
+   *           with this managed object.
+   */
+  <PD> void setPropertyValues(PropertyDefinition<PD> pd, Collection<PD> values)
+      throws IllegalPropertyValueException, PropertyIsSingleValuedException,
+      PropertyIsReadOnlyException, PropertyIsMandatoryException,
+      IllegalArgumentException;
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObjectDecodingException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObjectDecodingException.java
new file mode 100644
index 0000000..7e042fe
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagedObjectDecodingException.java
@@ -0,0 +1,146 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+import org.opends.server.admin.DecodingException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * The requested managed object was found but one or more of its
+ * properties could not be decoded successfully.
+ */
+public class ManagedObjectDecodingException extends DecodingException {
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = -4268510652395945357L;
+
+
+
+  // Create the message.
+  private static Message createMessage(ManagedObject<?> partialManagedObject,
+      Collection<PropertyException> causes) {
+    Validator.ensureNotNull(causes);
+    Validator.ensureTrue(!causes.isEmpty());
+
+    ManagedObjectDefinition<?, ?> d = partialManagedObject
+        .getManagedObjectDefinition();
+    if (causes.size() == 1) {
+      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_SINGLE.get(d
+          .getUserFriendlyName(), causes.iterator().next().getMessageObject());
+    } else {
+      MessageBuilder builder = new MessageBuilder();
+
+      boolean isFirst = true;
+      for (PropertyException cause : causes) {
+        if (!isFirst) {
+          builder.append("; ");
+        }
+        builder.append(cause.getMessageObject());
+        isFirst = false;
+      }
+
+      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_PLURAL.get(d
+          .getUserFriendlyName(), builder.toMessage());
+    }
+  }
+
+  // The exception(s) that caused this decoding exception.
+  private final Collection<PropertyException> causes;
+
+  // The partially created managed object.
+  private final ManagedObject<?> partialManagedObject;
+
+
+
+  /**
+   * Create a new property decoding exception.
+   *
+   * @param partialManagedObject
+   *          The partially created managed object containing
+   *          properties which were successfully decoded and empty
+   *          properties for those which were not (this may include
+   *          empty mandatory properties).
+   * @param causes
+   *          The exception(s) that caused this decoding exception.
+   */
+  public ManagedObjectDecodingException(ManagedObject<?> partialManagedObject,
+      Collection<PropertyException> causes) {
+    super(createMessage(partialManagedObject, causes));
+
+    this.partialManagedObject = partialManagedObject;
+    this.causes = Collections
+        .unmodifiableList(new LinkedList<PropertyException>(causes));
+  }
+
+
+
+  /**
+   * Get an unmodifiable collection view of the causes of this
+   * exception.
+   *
+   * @return Returns an unmodifiable collection view of the causes of
+   *         this exception.
+   */
+  public Collection<PropertyException> getCauses() {
+    return causes;
+  }
+
+
+
+  /**
+   * Get the partially created managed object containing properties
+   * which were successfully decoded and empty properties for those
+   * which were not (this may include empty mandatory properties).
+   *
+   * @return Returns the partially created managed object containing
+   *         properties which were successfully decoded and empty
+   *         properties for those which were not (this may include
+   *         empty mandatory properties).
+   */
+  public ManagedObject<?> getPartialManagedObject() {
+    return partialManagedObject;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ManagementContext.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagementContext.java
new file mode 100644
index 0000000..0a1d047
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ManagementContext.java
@@ -0,0 +1,528 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import java.util.Set;
+import java.util.SortedSet;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.admin.std.client.RootCfgClient;
+
+
+
+/**
+ * Client management connection context.
+ */
+public abstract class ManagementContext {
+
+  /**
+   * Creates a new management context.
+   */
+  protected ManagementContext() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Deletes the named instantiable child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    return getDriver().deleteManagedObject(parent, rd, name);
+  }
+
+
+
+  /**
+   * Deletes the optional child managed object from the named parent
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional child managed
+   *         object was found, or <code>false</code> if it was not
+   *         found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    return getDriver().deleteManagedObject(parent, rd);
+  }
+
+
+
+  /**
+   * Deletes s set child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The set relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the set
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    return getDriver().deleteManagedObject(parent, rd, name);
+  }
+
+
+
+  /**
+   * Gets the named managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The path of the managed object.
+   * @return Returns the named managed object.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  @SuppressWarnings("unchecked")
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    // Be careful to handle the root configuration.
+    if (path.isEmpty()) {
+      return (ManagedObject<C>) getRootConfigurationManagedObject();
+    }
+
+    return getDriver().getManagedObject(path);
+  }
+
+
+
+  /**
+   * Gets the effective value of a property in the named managed
+   * object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      DefinitionDecodingException, AuthorizationException,
+      ManagedObjectNotFoundException, CommunicationException,
+      PropertyException {
+    Set<PD> values = getPropertyValues(path, pd);
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.iterator().next();
+    }
+  }
+
+
+
+  /**
+   * Gets the effective values of a property in the named managed
+   * object.
+   *
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <PD> SortedSet<PD> getPropertyValues(
+      ManagedObjectPath<?, ?> path, PropertyDefinition<PD> pd)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      AuthorizationException, ManagedObjectNotFoundException,
+      CommunicationException, PropertyException {
+    return getDriver().getPropertyValues(path, pd);
+  }
+
+
+
+  /**
+   * Gets the root configuration client associated with this
+   * management context.
+   *
+   * @return Returns the root configuration client associated with
+   *         this management context.
+   */
+  public final RootCfgClient getRootConfiguration() {
+    return getRootConfigurationManagedObject().getConfiguration();
+  }
+
+
+
+  /**
+   * Gets the root configuration managed object associated with this
+   * management context.
+   *
+   * @return Returns the root configuration managed object associated
+   *         with this management context.
+   */
+  public final
+  ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
+    return getDriver().getRootConfigurationManagedObject();
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return listManagedObjects(parent, rd, rd.getChildDefinition());
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return getDriver().listManagedObjects(parent, rd, d);
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The set relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    return getDriver().listManagedObjects(parent, rd, rd.getChildDefinition());
+  }
+
+
+
+  /**
+   * Determines whether or not the named managed object exists.
+   *
+   * @param path
+   *          The path of the named managed object.
+   * @return Returns <code>true</code> if the named managed object
+   *         exists, <code>false</code> otherwise.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final boolean managedObjectExists(ManagedObjectPath<?, ?> path)
+      throws ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    return getDriver().managedObjectExists(path);
+  }
+
+
+
+  /**
+   * Gets the driver associated with this management context.
+   *
+   * @return Returns the driver associated with this management
+   *         context.
+   */
+  protected abstract Driver getDriver();
+
+
+
+  /**
+   * Closes this management context.
+   */
+  public final void close() {
+    this.getDriver().close();
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/MissingMandatoryPropertiesException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/MissingMandatoryPropertiesException.java
new file mode 100644
index 0000000..4d9b598
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/MissingMandatoryPropertiesException.java
@@ -0,0 +1,173 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.OperationsException;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * This exception is thrown when an attempt is made to add or modify a
+ * managed object when one or more of its mandatory properties are
+ * undefined.
+ */
+public class MissingMandatoryPropertiesException extends OperationsException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 6342522125252055588L;
+
+
+
+  // Create the message.
+  private static Message createMessage(
+      Collection<PropertyIsMandatoryException> causes) {
+    Validator.ensureNotNull(causes);
+    Validator.ensureTrue(!causes.isEmpty());
+
+    if (causes.size() == 1) {
+      return ERR_MISSING_MANDATORY_PROPERTIES_EXCEPTION_SINGLE.get(causes
+          .iterator().next().getPropertyDefinition().getName());
+    } else {
+      MessageBuilder builder = new MessageBuilder();
+
+      boolean isFirst = true;
+      for (PropertyIsMandatoryException cause : causes) {
+        if (!isFirst) {
+          builder.append(", ");
+        }
+        builder.append(cause.getPropertyDefinition().getName());
+        isFirst = false;
+      }
+
+      return ERR_MISSING_MANDATORY_PROPERTIES_EXCEPTION_PLURAL.get(builder
+          .toMessage());
+    }
+  }
+
+  // The causes of this exception.
+  private final Collection<PropertyIsMandatoryException> causes;
+
+  // Indicates whether the exception occurred during managed object
+  // creation.
+  private final boolean isCreate;
+
+  // The user friendly name of the component that caused this
+  // exception.
+  private final Message ufn;
+
+
+
+  /**
+   * Creates a new missing mandatory properties exception with the
+   * provided causes.
+   *
+   * @param ufn
+   *          The user friendly name of the component that caused this
+   *          exception.
+   * @param causes
+   *          The causes of this exception (must be non-<code>null</code>
+   *          and non-empty).
+   * @param isCreate
+   *          Indicates whether the exception occurred during managed
+   *          object creation.
+   */
+  public MissingMandatoryPropertiesException(Message ufn,
+      Collection<PropertyIsMandatoryException> causes, boolean isCreate) {
+    super(createMessage(causes));
+
+    this.causes = new ArrayList<PropertyIsMandatoryException>(causes);
+    this.ufn = ufn;
+    this.isCreate = isCreate;
+  }
+
+
+
+  /**
+   * Gets the first exception that caused this exception.
+   *
+   * @return Returns the first exception that caused this exception.
+   */
+  @Override
+  public PropertyIsMandatoryException getCause() {
+    return causes.iterator().next();
+  }
+
+
+
+  /**
+   * Gets an unmodifiable collection view of the causes of this
+   * exception.
+   *
+   * @return Returns an unmodifiable collection view of the causes of
+   *         this exception.
+   */
+  public Collection<PropertyIsMandatoryException> getCauses() {
+    return Collections.unmodifiableCollection(causes);
+  }
+
+
+
+  /**
+   * Gets the user friendly name of the component that caused this
+   * exception.
+   *
+   * @return Returns the user friendly name of the component that
+   *         caused this exception.
+   */
+  public Message getUserFriendlyName() {
+    return ufn;
+  }
+
+
+
+  /**
+   * Indicates whether or not this exception was thrown during managed
+   * object creation or during modification.
+   *
+   * @return Returns <code>true</code> if this exception was thrown
+   *         during managed object creation.
+   */
+  public boolean isCreate() {
+    return isCreate;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/OperationRejectedException.java b/opendj-admin/src/main/java/org/opends/server/admin/client/OperationRejectedException.java
new file mode 100644
index 0000000..73030e0
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/OperationRejectedException.java
@@ -0,0 +1,243 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * This exception is thrown when the client or server refuses to
+ * create, delete, or modify a managed object due to one or more
+ * constraints that cannot be satisfied.
+ * <p>
+ * Operations can be rejected either by a client-side constraint
+ * violation triggered by {@link ClientConstraintHandler}, or by a
+ * server-side error.
+ * <p>
+ * For example, the Directory Server might not be able perform an
+ * operation due to some OS related problem, such as lack of disk
+ * space, or missing files.
+ */
+public class OperationRejectedException extends AdminClientException {
+
+  /**
+   * The type of operation that caused this exception.
+   */
+  public enum OperationType {
+    /**
+     * A managed object could not be created.
+     */
+    CREATE,
+
+    /**
+     * A managed object could not be deleted.
+     */
+    DELETE,
+
+    /**
+     * A managed object could not be modified.
+     */
+    MODIFY;
+  }
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = 8547688890613079044L;
+
+
+
+  // Gets the default message.
+  private static Message getDefaultMessage(Collection<Message> messages) {
+    Validator.ensureNotNull(messages);
+    Validator.ensureTrue(!messages.isEmpty());
+
+    if (messages.size() == 1) {
+      return ERR_OPERATION_REJECTED_EXCEPTION_SINGLE.get(messages.iterator()
+          .next());
+    } else {
+      return ERR_OPERATION_REJECTED_EXCEPTION_PLURAL
+          .get(getSingleMessage(messages));
+    }
+  }
+
+
+
+  // Merge the messages into a single message.
+  private static Message getSingleMessage(Collection<Message> messages) {
+    if (messages.size() == 1) {
+      return messages.iterator().next();
+    } else {
+      MessageBuilder builder = new MessageBuilder();
+
+      boolean isFirst = true;
+      for (Message m : messages) {
+        if (!isFirst) {
+          builder.append(";  ");
+        }
+        builder.append(m);
+        isFirst = false;
+      }
+
+      return builder.toMessage();
+    }
+  }
+
+  // The messages describing the constraint violations that occurred.
+  private final Collection<Message> messages;
+
+  // The type of operation that caused this exception.
+  private final OperationType type;
+
+  // The user friendly name of the component that caused this
+  // exception.
+  private final Message ufn;
+
+
+
+  /**
+   * Creates a new operation rejected exception with a default
+   * message.
+   *
+   * @param type
+   *          The type of operation that caused this exception.
+   * @param ufn
+   *          The user friendly name of the component that caused this
+   *          exception.
+   */
+  public OperationRejectedException(OperationType type, Message ufn) {
+    this(type, ufn, ERR_OPERATION_REJECTED_DEFAULT.get());
+  }
+
+
+
+  /**
+   * Creates a new operation rejected exception with the provided
+   * messages.
+   *
+   * @param type
+   *          The type of operation that caused this exception.
+   * @param ufn
+   *          The user friendly name of the component that caused this
+   *          exception.
+   * @param messages
+   *          The messages describing the constraint violations that
+   *          occurred (must be non-<code>null</code> and
+   *          non-empty).
+   */
+  public OperationRejectedException(OperationType type, Message ufn,
+      Collection<Message> messages) {
+    super(getDefaultMessage(messages));
+
+    this.messages = new ArrayList<Message>(messages);
+    this.type = type;
+    this.ufn = ufn;
+  }
+
+
+
+  /**
+   * Creates a new operation rejected exception with the provided
+   * message.
+   *
+   * @param type
+   *          The type of operation that caused this exception.
+   * @param ufn
+   *          The user friendly name of the component that caused this
+   *          exception.
+   * @param message
+   *          The message describing the constraint violation that
+   *          occurred.
+   */
+  public OperationRejectedException(OperationType type, Message ufn,
+      Message message) {
+    this(type, ufn, Collections.singleton(message));
+  }
+
+
+
+  /**
+   * Gets an unmodifiable collection view of the messages describing
+   * the constraint violations that occurred.
+   *
+   * @return Returns an unmodifiable collection view of the messages
+   *         describing the constraint violations that occurred.
+   */
+  public Collection<Message> getMessages() {
+    return Collections.unmodifiableCollection(messages);
+  }
+
+
+
+  /**
+   * Creates a single message listing all the messages combined into a
+   * single list separated by semi-colons.
+   *
+   * @return Returns a single message listing all the messages
+   *         combined into a single list separated by semi-colons.
+   */
+  public Message getMessagesAsSingleMessage() {
+    return getSingleMessage(messages);
+  }
+
+
+
+  /**
+   * Gets the type of operation that caused this exception.
+   *
+   * @return Returns the type of operation that caused this exception.
+   */
+  public OperationType getOperationType() {
+    return type;
+  }
+
+
+
+  /**
+   * Gets the user friendly name of the component that caused this
+   * exception.
+   *
+   * @return Returns the user friendly name of the component that
+   *         caused this exception.
+   */
+  public Message getUserFriendlyName() {
+    return ufn;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/JNDIDirContextAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/JNDIDirContextAdaptor.java
new file mode 100644
index 0000000..7764f2a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/JNDIDirContextAdaptor.java
@@ -0,0 +1,337 @@
+/*
+ * 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 2008-2010 Sun Microsystems, Inc.
+ *      Portions Copyright 2013 ForgeRock AS
+ */
+package org.opends.server.admin.client.ldap;
+
+
+
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.opends.admin.ads.util.BlindTrustManager;
+import org.opends.admin.ads.util.ConnectionUtils;
+import org.opends.admin.ads.util.TrustedSocketFactory;
+import org.opends.server.admin.client.AuthenticationException;
+import org.opends.server.admin.client.AuthenticationNotSupportedException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.schema.SchemaConstants;
+
+
+
+/**
+ * An LDAP connection adaptor which maps LDAP requests onto an
+ * underlying JNDI connection context.
+ */
+public final class JNDIDirContextAdaptor extends LDAPConnection {
+
+  /**
+   * Adapts the provided JNDI <code>DirContext</code>.
+   *
+   * @param dirContext
+   *          The JNDI connection.
+   * @return Returns a new JNDI connection adaptor.
+   */
+  public static JNDIDirContextAdaptor adapt(DirContext dirContext) {
+    return new JNDIDirContextAdaptor(dirContext);
+  }
+
+
+
+  /**
+   * Creates a new JNDI connection adaptor by performing a simple bind
+   * operation to the specified LDAP server.
+   *
+   * @param host
+   *          The host.
+   * @param port
+   *          The port.
+   * @param name
+   *          The LDAP bind DN.
+   * @param password
+   *          The LDAP bind password.
+   * @return Returns a new JNDI connection adaptor.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   * @throws AuthenticationNotSupportedException
+   *           If the server does not support simple authentication.
+   * @throws AuthenticationException
+   *           If authentication failed for some reason, usually due
+   *           to invalid credentials.
+   */
+  public static JNDIDirContextAdaptor simpleBind(String host, int port,
+      String name, String password) throws CommunicationException,
+      AuthenticationNotSupportedException, AuthenticationException {
+    Hashtable<String, Object> env = new Hashtable<String, Object>();
+    env
+        .put(Context.INITIAL_CONTEXT_FACTORY,
+            "com.sun.jndi.ldap.LdapCtxFactory");
+    String hostname = ConnectionUtils.getHostNameForLdapUrl(host) ;
+    env.put(Context.PROVIDER_URL, "ldap://" + hostname + ":" + port);
+    env.put(Context.SECURITY_PRINCIPAL, name);
+    env.put(Context.SECURITY_CREDENTIALS, password);
+
+    DirContext ctx;
+    try {
+      ctx = new InitialLdapContext(env, null);
+    } catch (javax.naming.CommunicationException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.AuthenticationException e) {
+      throw new AuthenticationException(e);
+    } catch (javax.naming.AuthenticationNotSupportedException e) {
+      throw new AuthenticationNotSupportedException(e);
+    } catch (NamingException e) {
+      // Assume some kind of communication problem.
+      throw new CommunicationException(e);
+    }
+
+    return new JNDIDirContextAdaptor(ctx);
+  }
+
+  /**
+   * Creates a new JNDI connection adaptor by performing a simple bind
+   * operation to the specified LDAP server.
+   *
+   * @param host
+   *          The host.
+   * @param port
+   *          The port.
+   * @param name
+   *          The LDAP bind DN.
+   * @param password
+   *          The LDAP bind password.
+   * @return Returns a new JNDI connection adaptor.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   * @throws AuthenticationNotSupportedException
+   *           If the server does not support simple authentication.
+   * @throws AuthenticationException
+   *           If authentication failed for some reason, usually due
+   *           to invalid credentials.
+   */
+  public static JNDIDirContextAdaptor simpleSSLBind(String host, int port,
+      String name, String password) throws CommunicationException,
+      AuthenticationNotSupportedException, AuthenticationException {
+    Hashtable<String, Object> env = new Hashtable<String, Object>();
+    env.put(Context.INITIAL_CONTEXT_FACTORY,
+            "com.sun.jndi.ldap.LdapCtxFactory");
+    String hostname = ConnectionUtils.getHostNameForLdapUrl(host) ;
+    env.put(Context.PROVIDER_URL, "ldaps://" + hostname + ":" + port);
+    env.put(Context.SECURITY_PRINCIPAL, name);
+    env.put(Context.SECURITY_CREDENTIALS, password);
+    env.put(Context.SECURITY_AUTHENTICATION, "simple");
+    // Specify SSL
+    env.put(Context.SECURITY_PROTOCOL, "ssl");
+    env.put("java.naming.ldap.factory.socket",
+        org.opends.admin.ads.util.TrustedSocketFactory.class.getName());
+    TrustedSocketFactory.setCurrentThreadTrustManager(new BlindTrustManager(),
+              null);
+    DirContext ctx;
+    try {
+      ctx = new InitialLdapContext(env, null);
+    } catch (javax.naming.CommunicationException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.AuthenticationException e) {
+      throw new AuthenticationException(e);
+    } catch (javax.naming.AuthenticationNotSupportedException e) {
+      throw new AuthenticationNotSupportedException(e);
+    } catch (NamingException e) {
+      // Assume some kind of communication problem.
+      throw new CommunicationException(e);
+    }
+
+    return new JNDIDirContextAdaptor(ctx);
+  }
+
+
+  // The JNDI connection context.
+  private final DirContext dirContext;
+
+
+
+  // Create a new JNDI connection adaptor using the provider JNDI
+  // DirContext.
+  private JNDIDirContextAdaptor(DirContext dirContext) {
+    this.dirContext = dirContext;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void createEntry(LdapName dn, Attributes attributes)
+      throws NamingException {
+    dirContext.createSubcontext(dn, attributes).close();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void deleteSubtree(LdapName dn) throws NamingException {
+    // Delete the children first.
+    for (LdapName child : listEntries(dn, null)) {
+      deleteSubtree(child);
+    }
+
+    // Delete the named entry.
+    dirContext.destroySubcontext(dn);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean entryExists(LdapName dn) throws NamingException {
+    boolean entryExists = false;
+    String filter = "(objectClass=*)";
+    SearchControls controls = new SearchControls();
+    controls.setSearchScope(SearchControls.OBJECT_SCOPE);
+    controls
+        .setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES });
+    try {
+      NamingEnumeration<SearchResult> results = dirContext.search(dn, filter,
+          controls);
+      try
+      {
+        while (results.hasMore()) {
+          // To avoid having a systematic abandon in the server.
+          results.next();
+          entryExists = true;
+        }
+      }
+      finally
+      {
+        results.close();
+      }
+    } catch (NameNotFoundException e) {
+      // Fall through - entry not found.
+    }
+    return entryExists;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Collection<LdapName> listEntries(LdapName dn, String filter)
+      throws NamingException {
+    if (filter == null) {
+      filter = "(objectClass=*)";
+    }
+
+    SearchControls controls = new SearchControls();
+    controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+
+    List<LdapName> children = new LinkedList<LdapName>();
+    NamingEnumeration<SearchResult> results = dirContext.search(dn, filter,
+        controls);
+    try
+    {
+      while (results.hasMore()) {
+        SearchResult sr = results.next();
+        LdapName child = new LdapName(dn.getRdns());
+        child.add(new Rdn(sr.getName()));
+        children.add(child);
+      }
+    }
+    finally
+    {
+      results.close();
+    }
+
+    return children;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void modifyEntry(LdapName dn, Attributes mods) throws NamingException {
+    ModificationItem[] modList = new ModificationItem[mods.size()];
+    NamingEnumeration<? extends Attribute> ne = mods.getAll();
+    for (int i = 0; ne.hasMore(); i++) {
+      ModificationItem modItem = new ModificationItem(
+          DirContext.REPLACE_ATTRIBUTE, ne.next());
+      modList[i] = modItem;
+    }
+    dirContext.modifyAttributes(dn, modList);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Attributes readEntry(LdapName dn, Collection<String> attrIds)
+      throws NamingException {
+    String[] attrIdList = attrIds.toArray(new String[attrIds.size()]);
+    return dirContext.getAttributes(dn, attrIdList);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void unbind() {
+    try {
+      dirContext.close();
+    } catch (NamingException e) {
+      // nothing to do
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPConnection.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPConnection.java
new file mode 100644
index 0000000..943d82e
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPConnection.java
@@ -0,0 +1,149 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.client.ldap;
+
+
+import java.util.Collection;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+
+
+/**
+ * An LDAP connection adaptor interface which is used to perform LDAP
+ * client operations.
+ * <p>
+ * This interface is provided in order to make it easier to keep track
+ * of which JNDI DirContext methods we require and also to facilitate
+ * implementation of mock JNDI contexts for unit-testing.
+ */
+public abstract class LDAPConnection {
+
+  /**
+   * Create a new LDAP connection.
+   */
+  protected LDAPConnection() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Creates a new entry with the specified set of attributes.
+   *
+   * @param dn
+   *          The name of the entry to be created.
+   * @param attributes
+   *          The set of attributes.
+   * @throws NamingException
+   *           If an error occurred whilst creating the entry.
+   */
+  public abstract void createEntry(LdapName dn, Attributes attributes)
+      throws NamingException;
+
+
+
+  /**
+   * Deletes the named subtree.
+   *
+   * @param dn
+   *          The name of the subtree to be deleted.
+   * @throws NamingException
+   *           If an error occurred whilst deleting the subtree.
+   */
+  public abstract void deleteSubtree(LdapName dn) throws NamingException;
+
+
+
+  /**
+   * Determines whether or not the named entry exists.
+   *
+   * @param dn
+   *          The name of the entry.
+   * @return Returns <code>true</code> if the entry exists.
+   * @throws NamingException
+   *           If an error occurred whilst making the determination.
+   */
+  public abstract boolean entryExists(LdapName dn) throws NamingException;
+
+
+
+  /**
+   * Lists the children of the named entry.
+   *
+   * @param dn
+   *          The name of the entry to list.
+   * @param filter
+   *          An LDAP filter string, or <code>null</code> indicating
+   *          the default filter of <code>(objectclass=*)</code>.
+   * @return Returns the names of the children.
+   * @throws NamingException
+   *           If an error occurred whilst listing the children.
+   */
+  public abstract Collection<LdapName> listEntries(LdapName dn, String filter)
+      throws NamingException;
+
+
+
+  /**
+   * Modifies the attributes of the named entry.
+   *
+   * @param dn
+   *          The name of the entry to be modified.
+   * @param mods
+   *          The list of attributes which need replacing.
+   * @throws NamingException
+   *           If an error occurred whilst applying the modifications.
+   */
+  public abstract void modifyEntry(LdapName dn, Attributes mods)
+      throws NamingException;
+
+
+
+  /**
+   * Reads the attributes of the named entry.
+   *
+   * @param dn
+   *          The name of the entry to be read.
+   * @param attrIds
+   *          The list of attributes to be retrievd.
+   * @return Returns the attributes of the requested entry.
+   * @throws NamingException
+   *           If an error occurred whilst reading the entry.
+   */
+  public abstract Attributes readEntry(LdapName dn, Collection<String> attrIds)
+      throws NamingException;
+
+
+
+  /**
+   * Closes the LDAP connection.
+   */
+  public abstract void unbind();
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java
new file mode 100644
index 0000000..4adebb5
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -0,0 +1,706 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ *      Portions Copyright 2013 ForgeRock, AS.
+ */
+package org.opends.server.admin.client.ldap;
+
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AggregationPropertyDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.DefinitionResolver;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyDefinitionVisitor;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.Reference;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.UnknownPropertyDefinitionException;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.OperationRejectedException;
+import org.opends.server.admin.client.OperationRejectedException.OperationType;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.admin.client.spi.PropertySet;
+import org.opends.server.admin.std.client.RootCfgClient;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+
+
+
+/**
+ * The LDAP management context driver implementation.
+ */
+final class LDAPDriver extends Driver {
+
+  /**
+   * A visitor which is used to decode property LDAP values.
+   */
+  private static final class ValueDecoder extends
+      PropertyDefinitionVisitor<Object, String> {
+
+    /**
+     * Decodes the provided property LDAP value.
+     *
+     * @param <PD>
+     *          The type of the property.
+     * @param pd
+     *          The property definition.
+     * @param value
+     *          The LDAP string representation.
+     * @return Returns the decoded LDAP value.
+     * @throws IllegalPropertyValueStringException
+     *           If the property value could not be decoded because it
+     *           was invalid.
+     */
+    public static <PD> PD decode(PropertyDefinition<PD> pd, Object value)
+        throws IllegalPropertyValueStringException {
+      String s = String.valueOf(value);
+      return pd.castValue(pd.accept(new ValueDecoder(), s));
+    }
+
+
+
+    // Prevent instantiation.
+    private ValueDecoder() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration>
+    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
+      // Aggregations values are stored as full DNs in LDAP, but
+      // just their common name is exposed in the admin framework.
+      try {
+        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
+            .getRelationDefinition(), p);
+        return reference.getName();
+      } catch (IllegalArgumentException e) {
+        throw new IllegalPropertyValueStringException(d, p);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
+        throws UnknownPropertyDefinitionException {
+      // By default the property definition's decoder will do.
+      return d.decodeValue(p);
+    }
+  }
+
+
+
+  // The LDAP connection.
+  private final LDAPConnection connection;
+
+  // The LDAP management context.
+  private final LDAPManagementContext context;
+
+  // The LDAP profile which should be used to construct LDAP
+  // requests and decode LDAP responses.
+  private final LDAPProfile profile;
+
+
+
+  /**
+   * Creates a new LDAP driver using the specified LDAP connection and
+   * profile.
+   *
+   * @param context
+   *          The LDAP management context.
+   * @param connection
+   *          The LDAP connection.
+   * @param profile
+   *          The LDAP profile.
+   */
+  public LDAPDriver(LDAPManagementContext context, LDAPConnection connection,
+      LDAPProfile profile) {
+    this.context = context;
+    this.connection = connection;
+    this.profile = profile;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void close() {
+    connection.unbind();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    if (!managedObjectExists(path)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    try {
+      // Read the entry associated with the managed object.
+      LdapName dn = LDAPNameBuilder.create(path, profile);
+      AbstractManagedObjectDefinition<C, S> d = path
+          .getManagedObjectDefinition();
+      ManagedObjectDefinition<? extends C, ? extends S> mod =
+        getEntryDefinition(d, dn);
+
+      ArrayList<String> attrIds = new ArrayList<String>();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrId = profile.getAttributeName(mod, pd);
+        attrIds.add(attrId);
+      }
+
+      Attributes attributes = connection.readEntry(dn, attrIds);
+
+      // Build the managed object's properties.
+      List<PropertyException> exceptions = new LinkedList<PropertyException>();
+      PropertySet newProperties = new PropertySet();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrID = profile.getAttributeName(mod, pd);
+        Attribute attribute = attributes.get(attrID);
+        try {
+          decodeProperty(newProperties, path, pd, attribute);
+        } catch (PropertyException e) {
+          exceptions.add(e);
+        }
+      }
+
+      // If there were no decoding problems then return the object,
+      // otherwise throw an operations exception.
+      ManagedObject<? extends C> mo = createExistingManagedObject(mod, path,
+          newProperties);
+      if (exceptions.isEmpty()) {
+        return mo;
+      } else {
+        throw new ManagedObjectDecodingException(mo, exceptions);
+      }
+    } catch (NameNotFoundException e) {
+      throw new ManagedObjectNotFoundException();
+    } catch (NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @SuppressWarnings("unchecked")
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration, PD>
+  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      DefinitionDecodingException, AuthorizationException,
+      ManagedObjectNotFoundException, CommunicationException,
+      PropertyException {
+    // Check that the requested property is from the definition
+    // associated with the path.
+    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
+    if (tmp != pd) {
+      throw new IllegalArgumentException("The property " + pd.getName()
+          + " is not associated with a " + d.getName());
+    }
+
+    if (!managedObjectExists(path)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    try {
+      // Read the entry associated with the managed object.
+      LdapName dn = LDAPNameBuilder.create(path, profile);
+      ManagedObjectDefinition<? extends C, ? extends S> mod;
+      mod = getEntryDefinition(d, dn);
+
+      // Make sure we use the correct property definition, the
+      // provided one might have been overridden in the resolved
+      // definition.
+      pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
+
+      String attrID = profile.getAttributeName(mod, pd);
+      Attributes attributes = connection.readEntry(dn, Collections
+          .singleton(attrID));
+      Attribute attribute = attributes.get(attrID);
+
+      // Decode the values.
+      SortedSet<PD> values = new TreeSet<PD>(pd);
+      if (attribute != null) {
+        NamingEnumeration<?> ldapValues = attribute.getAll();
+        while (ldapValues.hasMore()) {
+          Object obj = ldapValues.next();
+          if (obj != null) {
+            PD value = ValueDecoder.decode(pd, obj);
+            values.add(value);
+          }
+        }
+      }
+
+      // Sanity check the returned values.
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new PropertyIsSingleValuedException(pd);
+      }
+
+      if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+        throw new PropertyIsMandatoryException(pd);
+      }
+
+      if (values.isEmpty()) {
+        // Use the property's default values.
+        values.addAll(findDefaultValues(path.asSubType(mod), pd, false));
+      }
+
+      return values;
+    } catch (NameNotFoundException e) {
+      throw new ManagedObjectNotFoundException();
+    } catch (NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
+    return new LDAPManagedObject<RootCfgClient>(this,
+        RootCfgDefn.getInstance(), ManagedObjectPath.emptyPath(),
+        new PropertySet(), true, null);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!managedObjectExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    // Get the search base DN.
+    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
+
+    // Retrieve only those entries which are sub-types of the
+    // specified definition.
+    StringBuilder builder = new StringBuilder();
+    builder.append("(objectclass=");
+    builder.append(profile.getObjectClass(d));
+    builder.append(')');
+    String filter = builder.toString();
+
+    List<String> children = new ArrayList<String>();
+    try {
+      for (LdapName child : connection.listEntries(dn, filter)) {
+        children.add(child.getRdn(child.size() - 1).getValue().toString());
+      }
+    } catch (NameNotFoundException e) {
+      // Ignore this - it means that the base entry does not exist
+      // (which it might not if this managed object has just been
+      // created.
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+
+    return children.toArray(new String[children.size()]);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+
+    if (!managedObjectExists(parent)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    // Get the search base DN.
+    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
+
+    // Retrieve only those entries which are sub-types of the
+    // specified definition.
+    StringBuilder builder = new StringBuilder();
+    builder.append("(objectclass=");
+    builder.append(profile.getObjectClass(d));
+    builder.append(')');
+    String filter = builder.toString();
+
+    List<String> children = new ArrayList<String>();
+    try {
+      for (LdapName child : connection.listEntries(dn, filter)) {
+        children.add(child.getRdn(child.size() - 1).getValue().toString());
+      }
+    } catch (NameNotFoundException e) {
+      // Ignore this - it means that the base entry does not exist
+      // (which it might not if this managed object has just been
+      // created.
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+
+    return children.toArray(new String[children.size()]);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean managedObjectExists(ManagedObjectPath<?, ?> path)
+      throws ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    if (path.isEmpty()) {
+      return true;
+    }
+
+    ManagedObjectPath<?, ?> parent = path.parent();
+    LdapName dn = LDAPNameBuilder.create(parent, profile);
+    if (!entryExists(dn)) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    dn = LDAPNameBuilder.create(path, profile);
+    return entryExists(dn);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected <C extends ConfigurationClient, S extends Configuration>
+  void deleteManagedObject(
+      ManagedObjectPath<C, S> path) throws OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    // Delete the entry and any subordinate entries.
+    LdapName dn = LDAPNameBuilder.create(path, profile);
+    try {
+      connection.deleteSubtree(dn);
+    } catch (OperationNotSupportedException e) {
+      // Unwilling to perform.
+      AbstractManagedObjectDefinition<?, ?> d =
+        path.getManagedObjectDefinition();
+      if (e.getMessage() == null) {
+        throw new OperationRejectedException(OperationType.DELETE, d
+            .getUserFriendlyName());
+      } else {
+        Message m = Message.raw("%s", e.getMessage());
+        throw new OperationRejectedException(OperationType.DELETE, d
+            .getUserFriendlyName(), m);
+      }
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected LDAPManagementContext getManagementContext() {
+    return context;
+  }
+
+
+
+  /**
+   * Adapts a naming exception to an appropriate admin client
+   * exception.
+   *
+   * @param ne
+   *          The naming exception.
+   * @throws CommunicationException
+   *           If the naming exception mapped to a communication
+   *           exception.
+   * @throws AuthorizationException
+   *           If the naming exception mapped to an authorization
+   *           exception.
+   */
+  void adaptNamingException(NamingException ne) throws CommunicationException,
+      AuthorizationException {
+    try {
+      throw ne;
+    } catch (javax.naming.CommunicationException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.ServiceUnavailableException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      // Just treat it as a communication problem.
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
+  /**
+   * Determines whether the named LDAP entry exists.
+   *
+   * @param dn
+   *          The LDAP entry name.
+   * @return Returns <code>true</code> if the named LDAP entry
+   *         exists.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  boolean entryExists(LdapName dn) throws CommunicationException,
+      AuthorizationException {
+    try {
+      return connection.entryExists(dn);
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
+    return false;
+  }
+
+
+
+  /**
+   * Gets the LDAP connection used for interacting with the server.
+   *
+   * @return Returns the LDAP connection used for interacting with the
+   *         server.
+   */
+  LDAPConnection getLDAPConnection() {
+    return connection;
+  }
+
+
+
+  /**
+   * Gets the LDAP profile which should be used to construct LDAP
+   * requests and decode LDAP responses.
+   *
+   * @return Returns the LDAP profile which should be used to
+   *         construct LDAP requests and decode LDAP responses.
+   */
+  LDAPProfile getLDAPProfile() {
+    return profile;
+  }
+
+
+
+  // Create a managed object which already exists on the server.
+  private <M extends ConfigurationClient, N extends Configuration>
+  ManagedObject<M> createExistingManagedObject(
+      ManagedObjectDefinition<M, N> d,
+      ManagedObjectPath<? super M, ? super N> p, PropertySet properties) {
+    RelationDefinition<?, ?> rd = p.getRelationDefinition();
+    PropertyDefinition<?> pd = null;
+    if (rd instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<?, ?> ird =
+        (InstantiableRelationDefinition<?, ?>) rd;
+      pd = ird.getNamingPropertyDefinition();
+    }
+    return new LDAPManagedObject<M>(this, d, p.asSubType(d), properties, true,
+        pd);
+  }
+
+
+
+  // Create a property using the provided string values.
+  private <PD> void decodeProperty(PropertySet newProperties,
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd,
+      Attribute attribute) throws PropertyException,
+      NamingException {
+    PropertyException exception = null;
+
+    // Get the property's active values.
+    SortedSet<PD> activeValues = new TreeSet<PD>(pd);
+    if (attribute != null) {
+      NamingEnumeration<?> ldapValues = attribute.getAll();
+      while (ldapValues.hasMore()) {
+        Object obj = ldapValues.next();
+        if (obj != null) {
+          PD value = ValueDecoder.decode(pd, obj);
+          activeValues.add(value);
+        }
+      }
+    }
+
+    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+      // This exception takes precedence over previous exceptions.
+      exception = new PropertyIsSingleValuedException(pd);
+      PD value = activeValues.first();
+      activeValues.clear();
+      activeValues.add(value);
+    }
+
+    // Get the property's default values.
+    Collection<PD> defaultValues;
+    try {
+      defaultValues = findDefaultValues(p, pd, false);
+    } catch (DefaultBehaviorException e) {
+      defaultValues = Collections.emptySet();
+      exception = e;
+    }
+
+    newProperties.addProperty(pd, defaultValues, activeValues);
+
+    if (activeValues.isEmpty() && defaultValues.isEmpty()
+        && pd.hasOption(PropertyOption.MANDATORY)) {
+      // The active values maybe empty because of a previous
+      // exception.
+      if (exception == null) {
+        exception = new PropertyIsMandatoryException(pd);
+      }
+    }
+
+    if (exception != null) {
+      throw exception;
+    }
+  }
+
+
+
+  // Determine the type of managed object associated with the named
+  // entry.
+  private <C extends ConfigurationClient, S extends Configuration>
+  ManagedObjectDefinition<? extends C, ? extends S> getEntryDefinition(
+      AbstractManagedObjectDefinition<C, S> d, LdapName dn)
+      throws NamingException, DefinitionDecodingException {
+    Attributes attributes = connection.readEntry(dn, Collections
+        .singleton("objectclass"));
+    Attribute oc = attributes.get("objectclass");
+
+    if (oc == null) {
+      // No object classes.
+      throw new DefinitionDecodingException(d, Reason.NO_TYPE_INFORMATION);
+    }
+
+    final Set<String> objectClasses = new HashSet<String>();
+    NamingEnumeration<?> values = oc.getAll();
+    while (values.hasMore()) {
+      Object value = values.next();
+      if (value != null) {
+        objectClasses.add(value.toString().toLowerCase().trim());
+      }
+    }
+
+    if (objectClasses.isEmpty()) {
+      // No object classes.
+      throw new DefinitionDecodingException(d, Reason.NO_TYPE_INFORMATION);
+    }
+
+    // Resolve the appropriate sub-type based on the object classes.
+    DefinitionResolver resolver = new DefinitionResolver() {
+
+      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+        String objectClass = profile.getObjectClass(d);
+        return objectClasses.contains(objectClass);
+      }
+
+    };
+
+    return d.resolveManagedObjectDefinition(resolver);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagedObject.java
new file mode 100644
index 0000000..70a46b1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -0,0 +1,386 @@
+/*
+ * 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 2007-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.ldap;
+
+
+
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.AggregationPropertyDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectAlreadyExistsException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.PropertyValueVisitor;
+import org.opends.server.admin.Reference;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.UnknownPropertyDefinitionException;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ConcurrentModificationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.OperationRejectedException;
+import org.opends.server.admin.client.OperationRejectedException.OperationType;
+import org.opends.server.admin.client.spi.AbstractManagedObject;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.admin.client.spi.Property;
+import org.opends.server.admin.client.spi.PropertySet;
+
+
+
+/**
+ * A managed object bound to an LDAP connection.
+ *
+ * @param <T>
+ *          The type of client configuration represented by the client
+ *          managed object.
+ */
+final class LDAPManagedObject<T extends ConfigurationClient> extends
+    AbstractManagedObject<T> {
+
+  /**
+   * A visitor which is used to encode property LDAP values.
+   */
+  private static final class ValueEncoder extends
+      PropertyValueVisitor<Object, Void> {
+
+    // Prevent instantiation.
+    private ValueEncoder() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration>
+    Object visitAggregation(
+        AggregationPropertyDefinition<C, S> pd, String v, Void p) {
+      // Aggregations values are stored as full DNs in LDAP, but
+      // just their common name is exposed in the admin framework.
+      Reference<C, S> reference = Reference.parseName(pd.getParentPath(), pd
+          .getRelationDefinition(), v);
+      return reference.toDN().toString();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <PD> Object visitUnknown(PropertyDefinition<PD> pd, PD v, Void p)
+        throws UnknownPropertyDefinitionException {
+      return pd.encodeValue(v);
+    }
+  }
+
+
+
+  // The LDAP management driver associated with this managed object.
+  private final LDAPDriver driver;
+
+
+
+  /**
+   * Creates a new LDAP managed object instance.
+   *
+   * @param driver
+   *          The underlying LDAP management driver.
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object already
+   *          exists.
+   * @param namingPropertyDefinition
+   *          The managed object's naming property definition if there
+   *          is one.
+   */
+  LDAPManagedObject(LDAPDriver driver,
+      ManagedObjectDefinition<T, ? extends Configuration> d,
+      ManagedObjectPath<T, ? extends Configuration> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    super(d, path, properties, existsOnServer, namingPropertyDefinition);
+    this.driver = driver;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void addNewManagedObject() throws AuthorizationException,
+      CommunicationException, OperationRejectedException,
+      ConcurrentModificationException, ManagedObjectAlreadyExistsException {
+    // First make sure that the parent managed object still exists.
+    ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
+    ManagedObjectPath<?, ?> path = getManagedObjectPath();
+    ManagedObjectPath<?, ?> parent = path.parent();
+
+    try {
+      if (!driver.managedObjectExists(parent)) {
+        throw new ConcurrentModificationException();
+      }
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    // We may need to create the parent "relation" entry if this is a
+    // child of an instantiable or set relation.
+    RelationDefinition<?, ?> r = path.getRelationDefinition();
+    if (r instanceof InstantiableRelationDefinition
+        || r instanceof SetRelationDefinition) {
+
+      // TODO: this implementation does not handle relations which
+      // comprise of more than one RDN arc (this will probably never
+      // be required anyway).
+      LdapName dn;
+      if (r instanceof InstantiableRelationDefinition) {
+        dn = LDAPNameBuilder.create(parent,
+            (InstantiableRelationDefinition) r, driver.getLDAPProfile());
+      } else {
+        dn = LDAPNameBuilder.create(parent,
+            (SetRelationDefinition) r, driver.getLDAPProfile());
+      }
+
+      if (!driver.entryExists(dn)) {
+        // We need to create the entry.
+        Attributes attributes = new BasicAttributes();
+
+        // Create the branch's object class attribute.
+        Attribute oc = new BasicAttribute("objectClass");
+        for (String objectClass : driver.getLDAPProfile()
+            .getRelationObjectClasses(r)) {
+          oc.add(objectClass);
+        }
+        attributes.put(oc);
+
+        // Create the branch's naming attribute.
+        Rdn rdn = dn.getRdn(dn.size() - 1);
+        attributes.put(rdn.getType(), rdn.getValue().toString());
+
+        // Create the entry.
+        try {
+          driver.getLDAPConnection().createEntry(dn, attributes);
+        } catch (OperationNotSupportedException e) {
+          // Unwilling to perform.
+          if (e.getMessage() == null) {
+            throw new OperationRejectedException(OperationType.CREATE, d
+                .getUserFriendlyName());
+          } else {
+            Message m = Message.raw("%s", e.getMessage());
+            throw new OperationRejectedException(OperationType.CREATE, d
+                .getUserFriendlyName(), m);
+          }
+        } catch (NamingException e) {
+          driver.adaptNamingException(e);
+        }
+      }
+    }
+
+    // Now add the entry representing this new managed object.
+    LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
+    Attributes attributes = new BasicAttributes(true);
+
+    // Create the object class attribute.
+    Attribute oc = new BasicAttribute("objectclass");
+    ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
+    for (String objectClass : driver.getLDAPProfile().getObjectClasses(
+        definition)) {
+      oc.add(objectClass);
+    }
+    attributes.put(oc);
+
+    // Create the naming attribute if there is not naming property.
+    PropertyDefinition<?> npd = getNamingPropertyDefinition();
+    if (npd == null) {
+      Rdn rdn = dn.getRdn(dn.size() - 1);
+      attributes.put(rdn.getType(), rdn.getValue().toString());
+    }
+
+    // Create the remaining attributes.
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      String attrID = driver.getLDAPProfile().getAttributeName(definition, pd);
+      Attribute attribute = new BasicAttribute(attrID);
+      encodeProperty(attribute, pd);
+      if (attribute.size() != 0) {
+        attributes.put(attribute);
+      }
+    }
+
+    try {
+      // Create the entry.
+      driver.getLDAPConnection().createEntry(dn, attributes);
+    } catch (NameAlreadyBoundException e) {
+      throw new ManagedObjectAlreadyExistsException();
+    } catch (OperationNotSupportedException e) {
+      // Unwilling to perform.
+      if (e.getMessage() == null) {
+        throw new OperationRejectedException(OperationType.CREATE, d
+            .getUserFriendlyName());
+      } else {
+        Message m = Message.raw("%s", e.getMessage());
+        throw new OperationRejectedException(OperationType.CREATE, d
+            .getUserFriendlyName(), m);
+      }
+    } catch (NamingException e) {
+      driver.adaptNamingException(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected Driver getDriver() {
+    return driver;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void modifyExistingManagedObject()
+      throws ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    // Build the list of modified attributes.
+    Attributes mods = new BasicAttributes();
+    ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      Property<?> p = getProperty(pd);
+      if (p.isModified()) {
+        String attrID = driver.getLDAPProfile().getAttributeName(d, pd);
+        Attribute attribute = new BasicAttribute(attrID);
+        encodeProperty(attribute, pd);
+        mods.put(attribute);
+      }
+    }
+
+    // Perform the LDAP modification if something has changed.
+    if (mods.size() > 0) {
+      try {
+        ManagedObjectPath<?, ?> path = getManagedObjectPath();
+        LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
+        driver.getLDAPConnection().modifyEntry(dn, mods);
+      } catch (NoPermissionException e) {
+        throw new AuthorizationException(e);
+      } catch (OperationNotSupportedException e) {
+        // Unwilling to perform.
+        if (e.getMessage() == null) {
+          throw new OperationRejectedException(OperationType.MODIFY, d
+              .getUserFriendlyName());
+        } else {
+          Message m = Message.raw("%s", e.getMessage());
+          throw new OperationRejectedException(OperationType.MODIFY, d
+              .getUserFriendlyName(), m);
+        }
+      } catch (NamingException e) {
+        // Just treat it as a communication problem.
+        throw new CommunicationException(e);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected <M extends ConfigurationClient> ManagedObject<M> newInstance(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    return new LDAPManagedObject<M>(driver, d, path, properties,
+        existsOnServer, namingPropertyDefinition);
+  }
+
+
+
+  // Encode a property into LDAP string values.
+  private <PD> void encodeProperty(Attribute attribute,
+      PropertyDefinition<PD> pd) {
+    PropertyValueVisitor<Object, Void> visitor = new ValueEncoder();
+    Property<PD> p = getProperty(pd);
+    if (pd.hasOption(PropertyOption.MANDATORY)) {
+      // For mandatory properties we fall-back to the default values
+      // if defined which can sometimes be the case e.g when a
+      // mandatory property is overridden.
+      for (PD value : p.getEffectiveValues()) {
+        attribute.add(pd.accept(visitor, value, null));
+      }
+    } else {
+      for (PD value : p.getPendingValues()) {
+        attribute.add(pd.accept(visitor, value, null));
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isModified() {
+    ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      Property<?> p = getProperty(pd);
+      if (p.isModified()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagementContext.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagementContext.java
new file mode 100644
index 0000000..76052d1
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPManagementContext.java
@@ -0,0 +1,77 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.ldap;
+
+
+
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.client.spi.Driver;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * An LDAP management connection context.
+ */
+public final class LDAPManagementContext extends ManagementContext {
+
+  /**
+   * Create a new LDAP management context using the provided LDAP
+   * connection.
+   *
+   * @param connection
+   *          The LDAP connection.
+   * @return Returns the new management context.
+   */
+  public static ManagementContext createFromContext(LDAPConnection connection) {
+    Validator.ensureNotNull(connection);
+    return new LDAPManagementContext(connection, LDAPProfile.getInstance());
+  }
+
+  // The LDAP management context driver.
+  private final LDAPDriver driver;
+
+
+
+  // Private constructor.
+  private LDAPManagementContext(LDAPConnection connection,
+      LDAPProfile profile) {
+    this.driver = new LDAPDriver(this, connection, profile);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected Driver getDriver() {
+    return driver;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPNameBuilder.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
new file mode 100644
index 0000000..0bbc756
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
@@ -0,0 +1,246 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.ldap;
+
+
+
+import java.util.LinkedList;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.ManagedObjectPathSerializer;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+
+
+
+/**
+ * A strategy for creating <code>LdapName</code>s from managed object paths.
+ */
+final class LDAPNameBuilder implements ManagedObjectPathSerializer {
+
+  /**
+   * Creates a new LDAP name representing the specified managed object
+   * path.
+   *
+   * @param path
+   *          The managed object path.
+   * @param profile
+   *          The LDAP profile which should be used to construct LDAP
+   *          names.
+   * @return Returns a new LDAP name representing the specified
+   *         managed object path.
+   */
+  public static LdapName create(ManagedObjectPath<?, ?> path,
+      LDAPProfile profile) {
+    LDAPNameBuilder builder = new LDAPNameBuilder(profile);
+    path.serialize(builder);
+    return builder.getInstance();
+  }
+
+
+
+  /**
+   * Creates a new LDAP name representing the specified managed object
+   * path and instantiable relation.
+   *
+   * @param path
+   *          The managed object path.
+   * @param relation
+   *          The child instantiable relation.
+   * @param profile
+   *          The LDAP profile which should be used to construct LDAP
+   *          names.
+   * @return Returns a new LDAP name representing the specified
+   *         managed object path and instantiable relation.
+   */
+  public static LdapName create(ManagedObjectPath<?, ?> path,
+      InstantiableRelationDefinition<?, ?> relation, LDAPProfile profile) {
+    LDAPNameBuilder builder = new LDAPNameBuilder(profile);
+    path.serialize(builder);
+    builder.appendManagedObjectPathElement(relation);
+    return builder.getInstance();
+  }
+
+
+
+  /**
+   * Creates a new LDAP name representing the specified managed object
+   * path and set relation.
+   *
+   * @param path
+   *          The managed object path.
+   * @param relation
+   *          The child set relation.
+   * @param profile
+   *          The LDAP profile which should be used to construct LDAP
+   *          names.
+   * @return Returns a new LDAP name representing the specified
+   *         managed object path and set relation.
+   */
+  public static LdapName create(ManagedObjectPath<?, ?> path,
+      SetRelationDefinition<?, ?> relation, LDAPProfile profile) {
+    LDAPNameBuilder builder = new LDAPNameBuilder(profile);
+    path.serialize(builder);
+    builder.appendManagedObjectPathElement(relation);
+    return builder.getInstance();
+  }
+
+  // The list of RDNs in big-endian order.
+  private final LinkedList<Rdn> rdns;
+
+  // The LDAP profile.
+  private final LDAPProfile profile;
+
+
+
+  /**
+   * Create a new JNDI LDAP name builder.
+   *
+   * @param profile
+   *          The LDAP profile which should be used to construct LDAP
+   *          names.
+   */
+  public LDAPNameBuilder(LDAPProfile profile) {
+    this.rdns = new LinkedList<Rdn>();
+    this.profile = profile;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          InstantiableRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d, String name) {
+    // Add the RDN sequence representing the relation.
+    appendManagedObjectPathElement(r);
+
+    // Now add the single RDN representing the named instance.
+    String type = profile.getRelationChildRDNType(r);
+    try {
+      Rdn rdn = new Rdn(type, name.trim());
+      rdns.add(rdn);
+    } catch (InvalidNameException e1) {
+      // Should not happen.
+      throw new RuntimeException(e1);
+    }
+  }
+
+
+
+  /**
+   * Appends the RDN sequence representing the provided relation.
+   *
+   * @param r
+   *          The relation definition.
+   */
+  public void appendManagedObjectPathElement(RelationDefinition<?, ?> r) {
+    // Add the RDN sequence representing the relation.
+    try {
+      LdapName tmp = new LdapName(profile.getRelationRDNSequence(r));
+      rdns.addAll(tmp.getRdns());
+    } catch (InvalidNameException e1) {
+      // Should not happen.
+      throw new RuntimeException(e1);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          OptionalRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
+    // Add the RDN sequence representing the relation.
+    appendManagedObjectPathElement(r);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          SingletonRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
+    // Add the RDN sequence representing the relation.
+    appendManagedObjectPathElement(r);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+      void appendManagedObjectPathElement(
+          SetRelationDefinition<? super C, ? super S> r,
+          AbstractManagedObjectDefinition<C, S> d) {
+    // Add the RDN sequence representing the relation.
+    appendManagedObjectPathElement(r);
+
+    // Now add the single RDN representing the named instance.
+    String type = profile.getRelationChildRDNType(r);
+    try {
+      Rdn rdn = new Rdn(type, d.getName());
+      rdns.add(rdn);
+    } catch (InvalidNameException e1) {
+      // Should not happen.
+      throw new RuntimeException(e1);
+    }
+  }
+
+
+
+  /**
+   * Create a new JNDI LDAP name using the current state of this LDAP name
+   * builder.
+   *
+   * @return Returns the new JNDI LDAP name instance.
+   */
+  public LdapName getInstance() {
+    return new LdapName(rdns);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/package-info.java
new file mode 100644
index 0000000..5a9f05d
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/package-info.java
@@ -0,0 +1,40 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * LDAP configuration transport implementation.
+ * <p>
+ * This implementation uses JNDI for all communication. It is expected
+ * that, at some point in the future, we will replace this implementation
+ * with our own LDAP client SDK based implementation.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.client.ldap;
+
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/client/package-info.java
new file mode 100644
index 0000000..6bfb68b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * Common client-side administration classes.
+ * <p>
+ * This package contains classes which client applications are
+ * expected to use.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.client;
+
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/spi/AbstractManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/AbstractManagedObject.java
new file mode 100644
index 0000000..a9c6085
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/AbstractManagedObject.java
@@ -0,0 +1,1048 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.client.spi;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefaultManagedObject;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectAlreadyExistsException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsReadOnlyException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.RelationDefinitionVisitor;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ConcurrentModificationException;
+import org.opends.server.admin.client.IllegalManagedObjectNameException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.client.MissingMandatoryPropertiesException;
+import org.opends.server.admin.client.OperationRejectedException;
+import org.opends.server.admin.client.OperationRejectedException.OperationType;
+
+
+
+/**
+ * An abstract managed object implementation.
+ *
+ * @param <T>
+ *          The type of client configuration represented by the client
+ *          managed object.
+ */
+public abstract class AbstractManagedObject<T extends ConfigurationClient>
+    implements ManagedObject<T> {
+
+  /**
+   * Creates any default managed objects associated with a relation
+   * definition.
+   */
+  private final class DefaultManagedObjectFactory implements
+      RelationDefinitionVisitor<Void, Void> {
+
+    // Possible exceptions.
+    private AuthorizationException ae = null;
+
+    private ManagedObjectAlreadyExistsException moaee = null;
+
+    private MissingMandatoryPropertiesException mmpe = null;
+
+    private ConcurrentModificationException cme = null;
+
+    private OperationRejectedException ore = null;
+
+    private CommunicationException ce = null;
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+        Void visitInstantiable(
+        InstantiableRelationDefinition<C, S> rd, Void p) {
+      for (String name : rd.getDefaultManagedObjectNames()) {
+        DefaultManagedObject<? extends C, ? extends S> dmo = rd
+            .getDefaultManagedObject(name);
+        ManagedObjectDefinition<? extends C, ? extends S> d = dmo
+            .getManagedObjectDefinition();
+        ManagedObject<? extends C> child;
+        try {
+          child = createChild(rd, d, name, null);
+        } catch (IllegalManagedObjectNameException e) {
+          // This should not happen.
+          throw new RuntimeException(e);
+        }
+        createDefaultManagedObject(d, child, dmo);
+      }
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+        Void visitOptional(
+        OptionalRelationDefinition<C, S> rd, Void p) {
+      if (rd.getDefaultManagedObject() != null) {
+        DefaultManagedObject<? extends C, ? extends S> dmo = rd
+            .getDefaultManagedObject();
+        ManagedObjectDefinition<? extends C, ? extends S> d = dmo
+            .getManagedObjectDefinition();
+        ManagedObject<? extends C> child = createChild(rd, d, null);
+        createDefaultManagedObject(d, child, dmo);
+      }
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+        Void visitSingleton(
+        SingletonRelationDefinition<C, S> rd, Void p) {
+      // Do nothing - not possible to create singletons
+      // dynamically.
+      return null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public <C extends ConfigurationClient, S extends Configuration>
+        Void visitSet(
+        SetRelationDefinition<C, S> rd, Void p) {
+      for (String name : rd.getDefaultManagedObjectNames()) {
+        DefaultManagedObject<? extends C, ? extends S> dmo = rd
+            .getDefaultManagedObject(name);
+        ManagedObjectDefinition<? extends C, ? extends S> d = dmo
+            .getManagedObjectDefinition();
+        ManagedObject<? extends C> child = createChild(rd, d, null);
+        createDefaultManagedObject(d, child, dmo);
+      }
+      return null;
+    }
+
+
+
+    // Create the child managed object.
+    private void createDefaultManagedObject(ManagedObjectDefinition<?, ?> d,
+        ManagedObject<?> child, DefaultManagedObject<?, ?> dmo) {
+      for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+        setPropertyValues(child, pd, dmo);
+      }
+
+      try {
+        child.commit();
+      } catch (AuthorizationException e) {
+        ae = e;
+      } catch (ManagedObjectAlreadyExistsException e) {
+        moaee = e;
+      } catch (MissingMandatoryPropertiesException e) {
+        mmpe = e;
+      } catch (ConcurrentModificationException e) {
+        cme = e;
+      } catch (OperationRejectedException e) {
+        ore = e;
+      } catch (CommunicationException e) {
+        ce = e;
+      }
+    }
+
+
+
+    /**
+     * Creates the default managed objects associated with the
+     * provided relation definition.
+     *
+     * @param rd
+     *          The relation definition.
+     */
+    private void createDefaultManagedObjects(RelationDefinition<?, ?> rd)
+        throws AuthorizationException, CommunicationException,
+        ConcurrentModificationException, MissingMandatoryPropertiesException,
+        ManagedObjectAlreadyExistsException, OperationRejectedException {
+      rd.accept(this, null);
+
+      if (ae != null) {
+        throw ae;
+      } else if (ce != null) {
+        throw ce;
+      } else if (cme != null) {
+        throw cme;
+      } else if (mmpe != null) {
+        throw mmpe;
+      } else if (moaee != null) {
+        throw moaee;
+      } else if (ore != null) {
+        throw ore;
+      }
+    }
+
+
+
+    // Set property values.
+    private <PD> void setPropertyValues(ManagedObject<?> mo,
+        PropertyDefinition<PD> pd, DefaultManagedObject<?, ?> dmo) {
+      mo.setPropertyValues(pd, dmo.getPropertyValues(pd));
+    }
+  }
+
+
+
+  // The managed object definition associated with this managed
+  // object.
+  private final ManagedObjectDefinition<T, ? extends Configuration> definition;
+
+  // Indicates whether or not this managed object exists on the server
+  // (false means the managed object is new and has not been
+  // committed).
+  private boolean existsOnServer;
+
+  // Optional naming property definition.
+  private final PropertyDefinition<?> namingPropertyDefinition;
+
+  // The path associated with this managed object.
+  private ManagedObjectPath<T, ? extends Configuration> path;
+
+  // The managed object's properties.
+  private final PropertySet properties;
+
+
+
+  /**
+   * Creates a new abstract managed object.
+   *
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object exists on
+   *          the server (false means the managed object is new and
+   *          has not been committed).
+   * @param namingPropertyDefinition
+   *          Optional naming property definition.
+   */
+  protected AbstractManagedObject(
+      ManagedObjectDefinition<T, ? extends Configuration> d,
+      ManagedObjectPath<T, ? extends Configuration> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition) {
+    this.definition = d;
+    this.path = path;
+    this.properties = properties;
+    this.existsOnServer = existsOnServer;
+    this.namingPropertyDefinition = namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void commit() throws ManagedObjectAlreadyExistsException,
+      MissingMandatoryPropertiesException, ConcurrentModificationException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    // First make sure all mandatory properties are defined.
+    List<PropertyIsMandatoryException> exceptions =
+      new LinkedList<PropertyIsMandatoryException>();
+
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      Property<?> p = getProperty(pd);
+      if (pd.hasOption(PropertyOption.MANDATORY)
+          && p.getEffectiveValues().isEmpty()) {
+        exceptions.add(new PropertyIsMandatoryException(pd));
+      }
+    }
+
+    if (!exceptions.isEmpty()) {
+      throw new MissingMandatoryPropertiesException(definition
+          .getUserFriendlyName(), exceptions, !existsOnServer);
+    }
+
+    // Now enforce any constraints.
+    List<Message> messages = new LinkedList<Message>();
+    boolean isAcceptable = true;
+    ManagementContext context = getDriver().getManagementContext();
+
+    for (Constraint constraint : definition.getAllConstraints()) {
+      for (ClientConstraintHandler handler : constraint
+          .getClientConstraintHandlers()) {
+        if (existsOnServer) {
+          if (!handler.isModifyAcceptable(context, this, messages)) {
+            isAcceptable = false;
+          }
+        } else {
+          if (!handler.isAddAcceptable(context, this, messages)) {
+            isAcceptable = false;
+          }
+        }
+      }
+      if (!isAcceptable) {
+        break;
+      }
+    }
+
+    if (!isAcceptable) {
+      if (existsOnServer) {
+        throw new OperationRejectedException(OperationType.MODIFY, definition
+            .getUserFriendlyName(), messages);
+      } else {
+        throw new OperationRejectedException(OperationType.CREATE, definition
+            .getUserFriendlyName(), messages);
+      }
+    }
+
+    // Commit the managed object.
+    if (existsOnServer) {
+      modifyExistingManagedObject();
+    } else {
+      addNewManagedObject();
+    }
+
+    // Make all pending property values active.
+    properties.commit();
+
+    // If the managed object was created make sure that any default
+    // subordinate managed objects are also created.
+    if (!existsOnServer) {
+      DefaultManagedObjectFactory factory = new DefaultManagedObjectFactory();
+      for (RelationDefinition<?, ?> rd :
+          definition.getAllRelationDefinitions()) {
+        factory.createDefaultManagedObjects(rd);
+      }
+
+      existsOnServer = true;
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration,
+                CC extends C>
+  ManagedObject<CC> createChild(
+      InstantiableRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d, String name,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalManagedObjectNameException, IllegalArgumentException {
+    validateRelationDefinition(r);
+
+    // Empty names are not allowed.
+    if (name.trim().length() == 0) {
+      throw new IllegalManagedObjectNameException(name);
+    }
+
+    // If the relation uses a naming property definition then it must
+    // be a valid value.
+    PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
+    if (pd != null) {
+      try {
+        pd.decodeValue(name);
+      } catch (IllegalPropertyValueStringException e) {
+        throw new IllegalManagedObjectNameException(name, pd);
+      }
+    }
+
+    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d, name);
+    return createNewManagedObject(d, childPath, pd, name, exceptions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient,
+                S extends Configuration, CC extends C>
+  ManagedObject<CC> createChild(
+      OptionalRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException {
+    validateRelationDefinition(r);
+    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
+    return createNewManagedObject(d, childPath, null, null, exceptions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration,
+                CC extends C>
+  ManagedObject<CC> createChild(
+      SetRelationDefinition<C, S> r,
+      ManagedObjectDefinition<CC, ? extends S> d,
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException {
+    validateRelationDefinition(r);
+
+    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
+    return createNewManagedObject(d, childPath, null, null, exceptions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      InstantiableRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r, name));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      SingletonRelationDefinition<C, S> r) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+    return ctx.getManagedObject(path.child(r));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getChild(
+      SetRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    ensureThisManagedObjectExists();
+    Driver ctx = getDriver();
+
+    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
+    AbstractManagedObjectDefinition<? extends C, ? extends S> cd;
+
+    try
+    {
+      cd = d.getChild(name);
+    }
+    catch (IllegalArgumentException e)
+    {
+      // Unrecognized definition name - report this as a decoding
+      // exception.
+      throw new DefinitionDecodingException(d,
+          Reason.WRONG_TYPE_INFORMATION);
+    }
+
+    return ctx.getManagedObject(path.child(r, cd));
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final T getConfiguration() {
+    return definition.createClientConfiguration(this);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final ManagedObjectDefinition<T, ? extends Configuration>
+  getManagedObjectDefinition() {
+    return definition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final ManagedObjectPath<T, ? extends Configuration>
+  getManagedObjectPath() {
+    return path;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> SortedSet<PD> getPropertyDefaultValues(
+      PropertyDefinition<PD> pd) throws IllegalArgumentException {
+    return new TreeSet<PD>(getProperty(pd).getDefaultValues());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    Set<PD> values = getProperty(pd).getEffectiveValues();
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.iterator().next();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    return new TreeSet<PD>(getProperty(pd).getEffectiveValues());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean hasChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    try {
+      return ctx.managedObjectExists(path.child(r));
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isPropertyPresent(PropertyDefinition<?> pd)
+      throws IllegalArgumentException {
+    return !getProperty(pd).isEmpty();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      InstantiableRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    return listChildren(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      InstantiableRelationDefinition<C, S> r,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    try {
+      return ctx.listManagedObjects(path, r, d);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      SetRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    return listChildren(r, r.getChildDefinition());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  String[] listChildren(
+      SetRelationDefinition<C, S> r,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    try {
+      return ctx.listManagedObjects(path, r, d);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(
+      InstantiableRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    boolean found;
+
+    try {
+      found = ctx.deleteManagedObject(path, r, name);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    if (!found) {
+      throw new ManagedObjectNotFoundException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(
+      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    boolean found;
+
+    try {
+      found = ctx.deleteManagedObject(path, r);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    if (!found) {
+      throw new ManagedObjectNotFoundException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  void removeChild(
+      SetRelationDefinition<C, S> r, String name)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(r);
+    Driver ctx = getDriver();
+    boolean found;
+
+    try {
+      found = ctx.deleteManagedObject(path, r, name);
+    } catch (ManagedObjectNotFoundException e) {
+      throw new ConcurrentModificationException();
+    }
+
+    if (!found) {
+      throw new ManagedObjectNotFoundException();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
+      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException {
+    if (value == null) {
+      setPropertyValues(pd, Collections.<PD> emptySet());
+    } else {
+      setPropertyValues(pd, Collections.singleton(value));
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final <PD> void setPropertyValues(PropertyDefinition<PD> pd,
+      Collection<PD> values) throws IllegalPropertyValueException,
+      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException {
+    if (pd.hasOption(PropertyOption.MONITORING)) {
+      throw new PropertyIsReadOnlyException(pd);
+    }
+
+    if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
+      throw new PropertyIsReadOnlyException(pd);
+    }
+
+    properties.setPropertyValues(pd, values);
+
+    // If this is a naming property then update the name.
+    if (pd.equals(namingPropertyDefinition)) {
+      // The property must be single-valued and mandatory.
+      String newName = pd.encodeValue(values.iterator().next());
+      path = path.rename(newName);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+
+    builder.append("{ TYPE=");
+    builder.append(definition.getName());
+    builder.append(", PATH=\"");
+    builder.append(path);
+    builder.append('\"');
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      builder.append(", ");
+      builder.append(pd.getName());
+      builder.append('=');
+      builder.append(getPropertyValues(pd));
+    }
+    builder.append(" }");
+
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Adds this new managed object.
+   *
+   * @throws ManagedObjectAlreadyExistsException
+   *           If the managed object cannot be added to the server
+   *           because it already exists.
+   * @throws ConcurrentModificationException
+   *           If the managed object's parent has been removed by
+   *           another client.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be added due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to add this managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract void addNewManagedObject() throws AuthorizationException,
+      CommunicationException, OperationRejectedException,
+      ConcurrentModificationException, ManagedObjectAlreadyExistsException;
+
+
+
+  /**
+   * Gets the management context driver associated with this managed
+   * object.
+   *
+   * @return Returns the management context driver associated with
+   *         this managed object.
+   */
+  protected abstract Driver getDriver();
+
+
+
+  /**
+   * Gets the naming property definition associated with this managed
+   * object.
+   *
+   * @return Returns the naming property definition associated with
+   *         this managed object, or <code>null</code> if this
+   *         managed object does not have a naming property.
+   */
+  protected final PropertyDefinition<?> getNamingPropertyDefinition() {
+    return namingPropertyDefinition;
+  }
+
+
+
+  /**
+   * Gets the property associated with the specified property
+   * definition.
+   *
+   * @param <PD>
+   *          The underlying type of the property.
+   * @param pd
+   *          The Property definition.
+   * @return Returns the property associated with the specified
+   *         property definition.
+   * @throws IllegalArgumentException
+   *           If this property provider does not recognize the
+   *           requested property definition.
+   */
+  protected final <PD> Property<PD> getProperty(PropertyDefinition<PD> pd)
+      throws IllegalArgumentException {
+    return properties.getProperty(pd);
+  }
+
+
+
+  /**
+   * Applies changes made to this managed object.
+   *
+   * @throws ConcurrentModificationException
+   *           If this managed object has been removed from the server
+   *           by another client.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be added due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied.
+   * @throws AuthorizationException
+   *           If the server refuses to modify this managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract void modifyExistingManagedObject()
+      throws ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Creates a new managed object.
+   *
+   * @param <M>
+   *          The type of client configuration represented by the
+   *          client managed object.
+   * @param d
+   *          The managed object's definition.
+   * @param path
+   *          The managed object's path.
+   * @param properties
+   *          The managed object's properties.
+   * @param existsOnServer
+   *          Indicates whether or not the managed object exists on
+   *          the server (false means the managed object is new and
+   *          has not been committed).
+   * @param namingPropertyDefinition
+   *          Optional naming property definition.
+   * @return Returns the new managed object.
+   */
+  protected abstract <M extends ConfigurationClient>
+  ManagedObject<M> newInstance(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
+      PropertySet properties, boolean existsOnServer,
+      PropertyDefinition<?> namingPropertyDefinition);
+
+
+
+  // Creates a new managed object with no active values, just default
+  // values.
+  private <M extends ConfigurationClient, PD> ManagedObject<M>
+  createNewManagedObject(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p,
+      PropertyDefinition<PD> namingPropertyDefinition, String name,
+      Collection<DefaultBehaviorException> exceptions) {
+    PropertySet childProperties = new PropertySet();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      try {
+        createProperty(childProperties, p, pd);
+      } catch (DefaultBehaviorException e) {
+        // Add the exception if requested.
+        if (exceptions != null) {
+          exceptions.add(e);
+        }
+      }
+    }
+
+    // Set the naming property if there is one.
+    if (namingPropertyDefinition != null) {
+      PD value = namingPropertyDefinition.decodeValue(name);
+      childProperties.setPropertyValues(namingPropertyDefinition, Collections
+          .singleton(value));
+    }
+
+    return newInstance(d, p, childProperties, false, namingPropertyDefinition);
+  }
+
+
+
+  // Create an empty property.
+  private <PD> void createProperty(PropertySet properties,
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd)
+      throws DefaultBehaviorException {
+    try {
+      Driver context = getDriver();
+      Collection<PD> defaultValues = context.findDefaultValues(p, pd, true);
+      properties.addProperty(pd, defaultValues, Collections.<PD> emptySet());
+    } catch (DefaultBehaviorException e) {
+      // Make sure that we have still created the property.
+      properties.addProperty(pd, Collections.<PD> emptySet(), Collections
+          .<PD> emptySet());
+      throw e;
+    }
+  }
+
+
+
+  // Makes sure that this managed object exists.
+  private void ensureThisManagedObjectExists()
+      throws ConcurrentModificationException, CommunicationException,
+      AuthorizationException {
+    if (!path.isEmpty()) {
+      Driver ctx = getDriver();
+
+      try {
+        if (!ctx.managedObjectExists(path)) {
+          throw new ConcurrentModificationException();
+        }
+      } catch (ManagedObjectNotFoundException e) {
+        throw new ConcurrentModificationException();
+      }
+    }
+  }
+
+
+
+  // Validate that a relation definition belongs to this managed
+  // object.
+  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
+      throws IllegalArgumentException {
+    ManagedObjectDefinition<T, ?> d = getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Driver.java b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Driver.java
new file mode 100644
index 0000000..99621ec
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Driver.java
@@ -0,0 +1,809 @@
+/*
+ * 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.spi;
+
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedSet;
+
+import org.opends.messages.Message;
+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.ConfigurationClient;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectNotFoundException;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyNotFoundException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.client.OperationRejectedException;
+import org.opends.server.admin.client.OperationRejectedException.OperationType;
+import org.opends.server.admin.std.client.RootCfgClient;
+
+
+
+/**
+ * An abstract management connection context driver which should form
+ * the basis of driver implementations.
+ */
+public abstract class Driver {
+
+  /**
+   * A default behavior visitor used for retrieving the default values
+   * of a property.
+   *
+   * @param <T>
+   *          The type of the property.
+   */
+  private class DefaultValueFinder<T> implements
+      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
+
+    // Any exception that occurred whilst retrieving inherited default
+    // values.
+    private DefaultBehaviorException exception = null;
+
+    // The path of the managed object containing the first property.
+    private final ManagedObjectPath<?, ?> firstPath;
+
+    // Indicates whether the managed object has been created yet.
+    private final boolean isCreate;
+
+    // The path of the managed object containing the next property.
+    private ManagedObjectPath<?, ?> nextPath = null;
+
+    // The next property whose default values were required.
+    private PropertyDefinition<T> nextProperty = null;
+
+
+
+    // Private constructor.
+    private DefaultValueFinder(ManagedObjectPath<?, ?> p, boolean isCreate) {
+      this.firstPath = p;
+      this.isCreate = isCreate;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAbsoluteInherited(
+        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      Collection<String> stringValues = d.getDefaultValues();
+      List<T> values = new ArrayList<T>(stringValues.size());
+
+      for (String stringValue : stringValues) {
+        try {
+          values.add(nextProperty.decodeValue(stringValue));
+        } catch (IllegalPropertyValueStringException e) {
+          exception = new DefaultBehaviorException(nextProperty, e);
+          break;
+        }
+      }
+
+      return values;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitRelativeInherited(
+        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    // Find the default values for the next path/property.
+    private Collection<T> find(ManagedObjectPath<?, ?> p,
+        PropertyDefinition<T> pd) throws DefaultBehaviorException {
+      this.nextPath = p;
+      this.nextProperty = pd;
+
+      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
+          this, null);
+
+      if (exception != null) {
+        throw exception;
+      }
+
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new DefaultBehaviorException(pd,
+            new PropertyIsSingleValuedException(pd));
+      }
+
+      return values;
+    }
+
+
+
+    // Get an inherited property value.
+    @SuppressWarnings("unchecked")
+    private Collection<T> getInheritedProperty(ManagedObjectPath target,
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
+        throws DefaultBehaviorException {
+      // First check that the requested type of managed object
+      // corresponds to the path.
+      AbstractManagedObjectDefinition<?, ?> supr = target
+          .getManagedObjectDefinition();
+      if (!supr.isParentOf(d)) {
+        throw new DefaultBehaviorException(
+            nextProperty, new DefinitionDecodingException(supr,
+                Reason.WRONG_TYPE_INFORMATION));
+      }
+
+      // Save the current property in case of recursion.
+      PropertyDefinition<T> pd1 = nextProperty;
+
+      try {
+        // Determine the requested property definition.
+        PropertyDefinition<T> pd2;
+        try {
+          // FIXME: we use the definition taken from the default
+          // behavior here when we should really use the exact
+          // definition of the component being created.
+          PropertyDefinition<?> pdTmp = d.getPropertyDefinition(propertyName);
+          pd2 = pd1.getClass().cast(pdTmp);
+        } catch (IllegalArgumentException e) {
+          throw new PropertyNotFoundException(propertyName);
+        } catch (ClassCastException e) {
+          // FIXME: would be nice to throw a better exception here.
+          throw new PropertyNotFoundException(propertyName);
+        }
+
+        // If the path relates to the current managed object and the
+        // managed object is in the process of being created it won't
+        // exist, so we should just use the default values of the
+        // referenced property.
+        if (isCreate && firstPath.equals(target)) {
+          // Recursively retrieve this property's default values.
+          Collection<T> tmp = find(target, pd2);
+          Collection<T> values = new ArrayList<T>(tmp.size());
+          for (T value : tmp) {
+            pd1.validateValue(value);
+            values.add(value);
+          }
+          return values;
+        } else {
+          // FIXME: issue 2481 - this is broken if the referenced property
+          // inherits its defaults from the newly created managed object.
+          return getPropertyValues(target, pd2);
+        }
+      } catch (DefaultBehaviorException e) {
+        // Wrap any errors due to recursion.
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (DefinitionDecodingException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (PropertyNotFoundException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (AuthorizationException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (ManagedObjectNotFoundException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (CommunicationException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (PropertyException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      }
+    }
+  };
+
+
+
+  /**
+   * Creates a new abstract management context.
+   */
+  protected Driver() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Closes any context associated with this management context
+   * driver.
+   */
+  public void close() {
+    // do nothing by default
+  }
+
+
+
+  /**
+   * Deletes the named instantiable child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+    ManagedObjectPath<?, ?> child = parent.child(rd, name);
+    return doDeleteManagedObject(child);
+  }
+
+
+
+  /**
+   * Deletes the optional child managed object from the named parent
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional child managed
+   *         object was found, or <code>false</code> if it was not
+   *         found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    validateRelationDefinition(parent, rd);
+    ManagedObjectPath<?, ?> child = parent.child(rd);
+    return doDeleteManagedObject(child);
+  }
+
+
+
+  /**
+   * Deletes the named instantiable child managed object from the
+   * named parent managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object to be removed.
+   * @return Returns <code>true</code> if the named instantiable
+   *         child managed object was found, or <code>false</code>
+   *         if it was not found.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           client-side or server-side constraint which cannot be
+   *           satisfied (for example, if it is referenced by another
+   *           managed object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public final <C extends ConfigurationClient, S extends Configuration>
+  boolean deleteManagedObject(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
+      String name) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    validateRelationDefinition(parent, rd);
+    ManagedObjectPath<?, ?> child = parent.child(rd, name);
+    return doDeleteManagedObject(child);
+  }
+
+
+
+  /**
+   * Gets the named managed object. The path is guaranteed to be
+   * non-empty, so implementations do not need to worry about handling
+   * this special case.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The non-empty path of the managed object.
+   * @return Returns the named managed object.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws ManagedObjectDecodingException
+   *           If the managed object was found but one or more of its
+   *           properties could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  ManagedObject<? extends C> getManagedObject(
+      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Gets the effective values of a property in the named managed
+   * object.
+   * <p>
+   * Implementations MUST NOT not use
+   * {@link #getManagedObject(ManagedObjectPath)} to read the
+   * referenced managed object in its entirety. Specifically,
+   * implementations MUST only attempt to resolve the default values
+   * for the requested property and its dependencies (if it uses
+   * inherited defaults). This is to avoid infinite recursion where a
+   * managed object contains a property which inherits default values
+   * from another property in the same managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws DefinitionDecodingException
+   *           If the managed object was found but its type could not
+   *           be determined.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ManagedObjectNotFoundException
+   *           If the requested managed object could not be found on
+   *           the server.
+   * @throws AuthorizationException
+   *           If the server refuses to retrieve the managed object
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration, PD>
+  SortedSet<PD> getPropertyValues(
+      ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd)
+      throws IllegalArgumentException, DefinitionDecodingException,
+      AuthorizationException, ManagedObjectNotFoundException,
+      CommunicationException, PropertyException;
+
+
+
+  /**
+   * Gets the root configuration managed object associated with this
+   * management context driver.
+   *
+   * @return Returns the root configuration managed object associated
+   *         with this management context driver.
+   */
+  public abstract
+  ManagedObject<RootCfgClient> getRootConfigurationManagedObject();
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object which are a sub-type of the specified managed object
+   * definition.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The set relation definition.
+   * @param d
+   *          The managed object definition.
+   * @return Returns the names of the child managed objects which are
+   *         a sub-type of the specified managed object definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to list the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
+      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Determines whether or not the named managed object exists.
+   * <p>
+   * Implementations should always return <code>true</code> when the
+   * provided path is empty.
+   *
+   * @param path
+   *          The path of the named managed object.
+   * @return Returns <code>true</code> if the named managed object
+   *         exists, <code>false</code> otherwise.
+   * @throws ManagedObjectNotFoundException
+   *           If the parent managed object could not be found.
+   * @throws AuthorizationException
+   *           If the server refuses to make the determination because
+   *           the client does not have the correct privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  public abstract boolean managedObjectExists(ManagedObjectPath<?, ?> path)
+      throws ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException;
+
+
+
+  /**
+   * Deletes the named managed object.
+   * <p>
+   * Implementations do not need check whether the named managed
+   * object exists, nor do they need to enforce client constraints.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param path
+   *          The path of the managed object to be deleted.
+   * @throws OperationRejectedException
+   *           If the managed object cannot be removed due to some
+   *           server-side constraint which cannot be satisfied (for
+   *           example, if it is referenced by another managed
+   *           object).
+   * @throws AuthorizationException
+   *           If the server refuses to remove the managed objects
+   *           because the client does not have the correct
+   *           privileges.
+   * @throws CommunicationException
+   *           If the client cannot contact the server due to an
+   *           underlying communication problem.
+   */
+  protected abstract <C extends ConfigurationClient, S extends Configuration>
+  void deleteManagedObject(
+      ManagedObjectPath<C, S> path) throws OperationRejectedException,
+      AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Gets the default values for the specified property.
+   *
+   * @param <PD>
+   *          The type of the property.
+   * @param p
+   *          The managed object path of the current managed object.
+   * @param pd
+   *          The property definition.
+   * @param isCreate
+   *          Indicates whether the managed object has been created
+   *          yet.
+   * @return Returns the default values for the specified property.
+   * @throws DefaultBehaviorException
+   *           If the default values could not be retrieved or decoded
+   *           properly.
+   */
+  protected final <PD> Collection<PD> findDefaultValues(
+      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd, boolean isCreate)
+      throws DefaultBehaviorException {
+    DefaultValueFinder<PD> v = new DefaultValueFinder<PD>(p, isCreate);
+    return v.find(p, pd);
+  }
+
+
+
+  /**
+   * Gets the management context associated with this driver.
+   *
+   * @return Returns the management context associated with this
+   *         driver.
+   */
+  protected abstract ManagementContext getManagementContext();
+
+
+
+  /**
+   * Validate that a relation definition belongs to the managed object
+   * referenced by the provided path.
+   *
+   * @param path
+   *          The parent managed object path.
+   * @param rd
+   *          The relation definition.
+   * @throws IllegalArgumentException
+   *           If the relation definition does not belong to the
+   *           managed object definition.
+   */
+  protected final void validateRelationDefinition(ManagedObjectPath<?, ?> path,
+      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+
+
+
+  // Remove a managed object, first ensuring that the parent exists,
+  // then ensuring that the child exists, before ensuring that any
+  // constraints are satisfied.
+  private <C extends ConfigurationClient, S extends Configuration>
+  boolean doDeleteManagedObject(
+      ManagedObjectPath<C, S> path) throws ManagedObjectNotFoundException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    // First make sure that the parent exists.
+    if (!managedObjectExists(path.parent())) {
+      throw new ManagedObjectNotFoundException();
+    }
+
+    // Make sure that the targeted managed object exists.
+    if (!managedObjectExists(path)) {
+      return false;
+    }
+
+    // The targeted managed object is guaranteed to exist, so enforce
+    // any constraints.
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    List<Message> messages = new LinkedList<Message>();
+    boolean isAcceptable = true;
+
+    for (Constraint constraint : d.getAllConstraints()) {
+      for (ClientConstraintHandler handler : constraint
+          .getClientConstraintHandlers()) {
+        ManagementContext context = getManagementContext();
+        if (!handler.isDeleteAcceptable(context, path, messages)) {
+          isAcceptable = false;
+        }
+      }
+      if (!isAcceptable) {
+        break;
+      }
+    }
+
+    if (!isAcceptable) {
+      throw new OperationRejectedException(OperationType.DELETE, d
+          .getUserFriendlyName(), messages);
+    }
+
+    deleteManagedObject(path);
+    return true;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Property.java b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Property.java
new file mode 100644
index 0000000..de4d731
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/Property.java
@@ -0,0 +1,140 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.spi;
+
+
+
+import java.util.SortedSet;
+
+import org.opends.server.admin.PropertyDefinition;
+
+
+
+/**
+ * A managed object property comprising of the property's definition
+ * and its set of values.
+ * <p>
+ * The property stores the values in a sorted set in which values are
+ * compared using the comparator defined by the property definition.
+ * <p>
+ * The property keeps track of whether or not its pending set of
+ * values differs from its active values.
+ *
+ * @param <T>
+ *          The type of the property.
+ */
+public interface Property<T> {
+
+  /**
+   * Get an immutable set view of this property's active values.
+   *
+   * @return Returns an immutable set view of this property's active
+   *         values. An empty set indicates that there are no active
+   *         values, and any default values are applicable.
+   */
+  SortedSet<T> getActiveValues();
+
+
+
+  /**
+   * Get an immutable set view of this property's default values.
+   *
+   * @return Returns an immutable set view of this property's default
+   *         values. An empty set indicates that there are no default
+   *         values.
+   */
+  SortedSet<T> getDefaultValues();
+
+
+
+  /**
+   * Get an immutable set view of this property's effective values.
+   *
+   * @return Returns an immutable set view of this property's
+   *         effective values.
+   */
+  SortedSet<T> getEffectiveValues();
+
+
+
+  /**
+   * Get an immutable set view of this property's pending values.
+   * <p>
+   * Immediately after construction, the pending values matches the
+   * active values.
+   *
+   * @return Returns an immutable set view of this property's pending
+   *         values. An empty set indicates that there are no pending
+   *         values, and any default values are applicable.
+   */
+  SortedSet<T> getPendingValues();
+
+
+
+  /**
+   * Get the property definition associated with this property.
+   *
+   * @return Returns the property definition associated with this
+   *         property.
+   */
+  PropertyDefinition<T> getPropertyDefinition();
+
+
+
+  /**
+   * Determines whether or not this property contains any pending
+   * values.
+   *
+   * @return Returns <code>true</code> if this property does not
+   *         contain any pending values.
+   */
+  boolean isEmpty();
+
+
+
+  /**
+   * Determines whether or not this property has been modified since
+   * it was constructed. In other words, whether or not the set of
+   * pending values differs from the set of active values.
+   *
+   * @return Returns <code>true</code> if this property has been
+   *         modified since it was constructed.
+   */
+  boolean isModified();
+
+
+
+  /**
+   * Determines whether or not this property contains any active
+   * values.
+   *
+   * @return Returns <code>true</code> if this property does not
+   *         contain any active values.
+   */
+  boolean wasEmpty();
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/spi/PropertySet.java b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/PropertySet.java
new file mode 100644
index 0000000..2c9116e
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/PropertySet.java
@@ -0,0 +1,371 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.client.spi;
+
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyOption;
+
+
+
+/**
+ * A set of properties. Instances of this class can be used as the
+ * core of a managed object implementation.
+ */
+public final class PropertySet {
+
+  /**
+   * Internal property implementation.
+   *
+   * @param <T>
+   *          The type of the property.
+   */
+  private static final class MyProperty<T> implements Property<T> {
+
+    // The active set of values.
+    private final SortedSet<T> activeValues;
+
+    // The definition associated with this property.
+    private final PropertyDefinition<T> d;
+
+    // The default set of values (read-only).
+    private final SortedSet<T> defaultValues;
+
+    // The pending set of values.
+    private final SortedSet<T> pendingValues;
+
+
+
+    /**
+     * Create a property with the provided sets of pre-validated
+     * default and active values.
+     *
+     * @param pd
+     *          The property definition.
+     * @param defaultValues
+     *          The set of default values for the property.
+     * @param activeValues
+     *          The set of active values for the property.
+     */
+    public MyProperty(PropertyDefinition<T> pd, Collection<T> defaultValues,
+        Collection<T> activeValues) {
+      this.d = pd;
+
+      SortedSet<T> sortedDefaultValues = new TreeSet<T>(pd);
+      sortedDefaultValues.addAll(defaultValues);
+      this.defaultValues = Collections
+          .unmodifiableSortedSet(sortedDefaultValues);
+
+      this.activeValues = new TreeSet<T>(pd);
+      this.activeValues.addAll(activeValues);
+
+      // Initially the pending values is the same as the active
+      // values.
+      this.pendingValues = new TreeSet<T>(this.activeValues);
+    }
+
+
+
+    /**
+     * Makes the pending values active.
+     */
+    public void commit() {
+      activeValues.clear();
+      activeValues.addAll(pendingValues);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<T> getActiveValues() {
+      return Collections.unmodifiableSortedSet(activeValues);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<T> getDefaultValues() {
+      return defaultValues;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<T> getEffectiveValues() {
+      SortedSet<T> values = getPendingValues();
+
+      if (values.isEmpty()) {
+        values = getDefaultValues();
+      }
+
+      return values;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public SortedSet<T> getPendingValues() {
+      return Collections.unmodifiableSortedSet(pendingValues);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public PropertyDefinition<T> getPropertyDefinition() {
+      return d;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+      return pendingValues.isEmpty();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isModified() {
+      if (activeValues.size() == pendingValues.size()
+          && activeValues.containsAll(pendingValues)) {
+        return false;
+      }
+      return true;
+    }
+
+
+
+    /**
+     * Replace all pending values of this property with the provided
+     * values.
+     *
+     * @param c
+     *          The new set of pending property values.
+     */
+    public void setPendingValues(Collection<T> c) {
+      pendingValues.clear();
+      pendingValues.addAll(c);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      return getEffectiveValues().toString();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean wasEmpty() {
+      return activeValues.isEmpty();
+    }
+  }
+
+  // The properties.
+  private final Map<PropertyDefinition<?>, MyProperty<?>> properties;
+
+
+
+  /**
+   * Creates a new empty property set.
+   */
+  public PropertySet() {
+    this.properties = new HashMap<PropertyDefinition<?>, MyProperty<?>>();
+  }
+
+
+
+  /**
+   * Creates a property with the provided sets of pre-validated
+   * default and active values.
+   *
+   * @param <T>
+   *          The type of the property.
+   * @param pd
+   *          The property definition.
+   * @param defaultValues
+   *          The set of default values for the property.
+   * @param activeValues
+   *          The set of active values for the property.
+   */
+  public <T> void addProperty(PropertyDefinition<T> pd,
+      Collection<T> defaultValues, Collection<T> activeValues) {
+    MyProperty<T> p = new MyProperty<T>(pd, defaultValues, activeValues);
+    properties.put(pd, p);
+  }
+
+
+
+  /**
+   * Get the property associated with the specified property
+   * definition.
+   *
+   * @param <T>
+   *          The underlying type of the property.
+   * @param d
+   *          The Property definition.
+   * @return Returns the property associated with the specified
+   *         property definition.
+   * @throws IllegalArgumentException
+   *           If this property provider does not recognise the
+   *           requested property definition.
+   */
+  @SuppressWarnings("unchecked")
+  public <T> Property<T> getProperty(PropertyDefinition<T> d)
+      throws IllegalArgumentException {
+    if (!properties.containsKey(d)) {
+      throw new IllegalArgumentException("Unknown property " + d.getName());
+    }
+
+    return (Property<T>) properties.get(d);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append('{');
+    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties
+        .entrySet()) {
+      builder.append(entry.getKey().getName());
+      builder.append('=');
+      builder.append(entry.getValue().toString());
+      builder.append(' ');
+    }
+    builder.append('}');
+    return builder.toString();
+  }
+
+
+
+
+
+
+  /**
+   * Makes all pending values active.
+   */
+  void commit() {
+    for (MyProperty<?> p : properties.values()) {
+      p.commit();
+    }
+  }
+
+
+
+  /**
+   * Set a new pending values for the specified property.
+   * <p>
+   * See the class description for more information regarding pending
+   * values.
+   *
+   * @param <T>
+   *          The type of the property to be modified.
+   * @param d
+   *          The property to be modified.
+   * @param values
+   *          A non-<code>null</code> set of new pending values for
+   *          the property (an empty set indicates that the property
+   *          should be reset to its default behavior). The set will
+   *          not be referenced by this managed object.
+   * @throws IllegalPropertyValueException
+   *           If a new pending value is deemed to be invalid
+   *           according to the property definition.
+   * @throws PropertyIsSingleValuedException
+   *           If an attempt was made to add multiple pending values
+   *           to a single-valued property.
+   * @throws PropertyIsMandatoryException
+   *           If an attempt was made to remove a mandatory property.
+   * @throws IllegalArgumentException
+   *           If the specified property definition is not associated
+   *           with this managed object.
+   */
+  <T> void setPropertyValues(PropertyDefinition<T> d,
+      Collection<T> values) throws IllegalPropertyValueException,
+      PropertyIsSingleValuedException, PropertyIsMandatoryException,
+      IllegalArgumentException {
+    MyProperty<T> property = (MyProperty<T>) getProperty(d);
+
+    if (values.size() > 1 && !d.hasOption(PropertyOption.MULTI_VALUED)) {
+      throw new PropertyIsSingleValuedException(d);
+    }
+
+    if (values.isEmpty() && d.hasOption(PropertyOption.MANDATORY)) {
+      // But only if there are no default values.
+      if (property.getDefaultValues().isEmpty()) {
+        throw new PropertyIsMandatoryException(d);
+      }
+    }
+
+    // Validate each value.
+    for (T e : values) {
+      if (e == null) {
+        throw new NullPointerException();
+      }
+
+      d.validateValue(e);
+    }
+
+    // Update the property.
+    property.setPendingValues(values);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/spi/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/package-info.java
new file mode 100644
index 0000000..49ac44f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/spi/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * Client side driver implementation interfaces.
+ * <p>
+ * This package contains classes which client-side driver
+ * implementations are expected to use.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.client.spi;
+
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/ANDCondition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/ANDCondition.java
new file mode 100644
index 0000000..272214e
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/ANDCondition.java
@@ -0,0 +1,112 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * A condition which evaluates to <code>true</code> if and only if
+ * all of its sub-conditions are <code>true</code>.
+ */
+public final class ANDCondition implements Condition {
+
+  // The list of sub-conditions.
+  private final List<Condition> conditions;
+
+
+
+  /**
+   * Creates a new logical AND condition with the provided
+   * sub-conditions.
+   *
+   * @param conditions
+   *          The sub-conditions which will be combined using a
+   *          logical AND.
+   */
+  public ANDCondition(Condition... conditions) {
+    Validator.ensureNotNull(conditions);
+    this.conditions = Arrays.asList(conditions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ManagementContext context,
+      ManagedObject<?> managedObject) throws AuthorizationException,
+      CommunicationException {
+    for (Condition condition : conditions) {
+      if (!condition.evaluate(context, managedObject)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    for (Condition condition : conditions) {
+      if (!condition.evaluate(managedObject)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+      throws Exception {
+    for (Condition condition : conditions) {
+      condition.initialize(d);
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/Condition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/Condition.java
new file mode 100644
index 0000000..78d9254
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/Condition.java
@@ -0,0 +1,94 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * An interface for evaluating conditions.
+ */
+public interface Condition {
+
+  /**
+   * Initializes this condition.
+   *
+   * @param d
+   *          The abstract managed object definition associated with
+   *          this condition.
+   * @throws Exception
+   *           If this condition could not be initialized.
+   */
+  void initialize(AbstractManagedObjectDefinition<?, ?> d) throws Exception;
+
+
+
+  /**
+   * Evaluates this condition against the provided client managed
+   * object.
+   *
+   * @param context
+   *          The client management context.
+   * @param managedObject
+   *          The client managed object.
+   * @return Returns <code>true</code> if this condition is
+   *         satisfied.
+   * @throws AuthorizationException
+   *           If the condition could not be evaluated due to an
+   *           authorization problem.
+   * @throws CommunicationException
+   *           If the condition could not be evaluated due to an
+   *           communication problem.
+   */
+  boolean evaluate(ManagementContext context, ManagedObject<?> managedObject)
+      throws AuthorizationException, CommunicationException;
+
+
+
+  /**
+   * Evaluates this condition against the provided server managed
+   * object.
+   *
+   * @param managedObject
+   *          The server managed object.
+   * @return Returns <code>true</code> if this condition is
+   *         satisfied.
+   * @throws ConfigException
+   *           If the condition could not be evaluated due to an
+   *           unexpected configuration exception.
+   */
+  boolean evaluate(ServerManagedObject<?> managedObject) throws ConfigException;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/Conditions.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/Conditions.java
new file mode 100644
index 0000000..4f057ab
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/Conditions.java
@@ -0,0 +1,237 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * This class consists exclusively of static methods that operate on
+ * or return conditions.
+ */
+public final class Conditions {
+
+  /**
+   * A condition which always evaluates to <code>false</code>.
+   */
+  public static final Condition FALSE = new Condition() {
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ManagementContext context,
+        ManagedObject<?> managedObject) throws AuthorizationException,
+        CommunicationException {
+      return false;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      return false;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+        throws Exception {
+      // No implementation required.
+    }
+
+  };
+
+  /**
+   * A condition which always evaluates to <code>true</code>.
+   */
+  public static final Condition TRUE = new Condition() {
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ManagementContext context,
+        ManagedObject<?> managedObject) throws AuthorizationException,
+        CommunicationException {
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      return true;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+        throws Exception {
+      // No implementation required.
+    }
+
+  };
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>true</code> if and
+   * only if all of its sub-conditions are <code>true</code>.
+   *
+   * @param conditions
+   *          The sub-conditions which be combined using a logical
+   *          AND.
+   * @return Returns a condition which evaluates to <code>true</code>
+   *         if and only if all of its sub-conditions are
+   *         <code>true</code>.
+   */
+  public static Condition and(Condition... conditions) {
+    return new ANDCondition(conditions);
+  }
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>true</code> if and
+   * only if a property contains a particular value.
+   *
+   * @param propertyName
+   *          The property name.
+   * @param propertyStringValue
+   *          The string representation of the required property
+   *          value.
+   * @return Returns a condition which evaluates to <code>true</code>
+   *         if and only if a property contains a particular value.
+   */
+  public static Condition contains(String propertyName,
+      String propertyStringValue) {
+    return new ContainsCondition(propertyName, propertyStringValue);
+  }
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>false</code> if
+   * and only if the first sub-condition evaluates to
+   * <code>true</code> and the second sub-condition evaluates to
+   * <code>false</code>. This can be used to represent if-then
+   * relationships.
+   *
+   * @param premise
+   *          The sub-condition which, when <code>true</code>
+   *          implies that the implication sub-condition must also be
+   *          <code>true</code>.
+   * @param implication
+   *          The sub-condition which, must be <code>true</code>
+   *          when the premise is <code>true</code>.
+   * @return Returns a condition which evaluates to <code>false</code>
+   *         if and only if the first sub-condition evaluates to
+   *         <code>true</code> and the second sub-condition
+   *         evaluates to <code>false</code>.
+   */
+  public static Condition implies(Condition premise, Condition implication) {
+    return or(not(premise), implication);
+  }
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>true</code> if and
+   * only if a particular property has any values specified.
+   *
+   * @param propertyName
+   *          The property name.
+   * @return Returns a condition which evaluates to <code>true</code>
+   *         if and only if a particular property has any values
+   *         specified.
+   */
+  public static Condition isPresent(String propertyName) {
+    return new IsPresentCondition(propertyName);
+  }
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>true</code> if the
+   * sub-condition is <code>false</code>, or <code>false</code>
+   * if the sub-condition is <code>true</code>.
+   *
+   * @param condition
+   *          The sub-condition which will be inverted.
+   * @return Returns a condition which evaluates to <code>true</code>
+   *         if the sub-condition is <code>false</code>, or
+   *         <code>false</code> if the sub-condition is
+   *         <code>true</code>.
+   */
+  public static Condition not(Condition condition) {
+    return new NOTCondition(condition);
+  }
+
+
+
+  /**
+   * Creates a condition which evaluates to <code>false</code> if
+   * and only if all of its sub-conditions are <code>false</code>.
+   *
+   * @param conditions
+   *          The sub-conditions which be combined using a logical OR.
+   * @return Returns a condition which evaluates to <code>false</code>
+   *         if and only if all of its sub-conditions are
+   *         <code>false</code>.
+   */
+  public static Condition or(Condition... conditions) {
+    return new ORCondition(conditions);
+  }
+
+
+
+  // Prevent instantiation.
+  private Conditions() {
+    // No implementation required.
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/ContainsCondition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/ContainsCondition.java
new file mode 100644
index 0000000..12035b6
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/ContainsCondition.java
@@ -0,0 +1,214 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import java.util.SortedSet;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * A condition which evaluates to <code>true</code> if and only if a
+ * property contains a particular value.
+ */
+public final class ContainsCondition implements Condition {
+
+  /**
+   * The strongly typed underlying implementation.
+   *
+   * @param <T>
+   *          The type of the property value being tested.
+   */
+  private static final class Impl<T> implements Condition {
+
+    // The property.
+    final PropertyDefinition<T> pd;
+
+    // The required property value.
+    final T value;
+
+
+
+    // Private constructor.
+    private Impl(PropertyDefinition<T> pd, T value)
+        throws IllegalPropertyValueStringException {
+      this.pd = pd;
+      this.value = value;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ManagementContext context,
+        ManagedObject<?> managedObject) throws AuthorizationException,
+        CommunicationException {
+      SortedSet<T> values = managedObject.getPropertyValues(pd);
+      return values.contains(value);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean evaluate(ServerManagedObject<?> managedObject)
+        throws ConfigException {
+      SortedSet<T> values = managedObject.getPropertyValues(pd);
+      return values.contains(value);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+        throws Exception {
+      // Not used.
+    }
+
+
+
+    // Private implementation of fix() method.
+    private void setPropertyValue(ManagedObject<?> managedObject) {
+      managedObject.setPropertyValue(pd, value);
+    }
+
+  }
+
+  // The strongly typed private implementation.
+  private Impl<?> impl = null;
+
+  // The property name.
+  private final String propertyName;
+
+  // The string representation of the required property value.
+  private final String propertyStringValue;
+
+
+
+  /**
+   * Creates a new contains value condition.
+   *
+   * @param propertyName
+   *          The property name.
+   * @param stringValue
+   *          The string representation of the required property
+   *          value.
+   */
+  public ContainsCondition(String propertyName, String stringValue) {
+    Validator.ensureNotNull(propertyName, stringValue);
+    this.propertyName = propertyName;
+    this.propertyStringValue = stringValue;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ManagementContext context,
+      ManagedObject<?> managedObject) throws AuthorizationException,
+      CommunicationException {
+    return impl.evaluate(context, managedObject);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    return impl.evaluate(managedObject);
+  }
+
+
+
+  /**
+   * Modifies the provided managed object so that it has the property
+   * value associated with this condition.
+   *
+   * @param managedObject
+   *          The managed object.
+   */
+  public void setPropertyValue(ManagedObject<?> managedObject) {
+    impl.setPropertyValue(managedObject);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+      throws Exception {
+    // Decode the property.
+    buildImpl(d.getPropertyDefinition(propertyName));
+  }
+
+
+
+  // Creates the new private implementation.
+  private <T> void buildImpl(PropertyDefinition<T> pd)
+      throws IllegalPropertyValueStringException {
+    T value = pd.decodeValue(propertyStringValue);
+    this.impl = new Impl<T>(pd, value);
+  }
+
+  /**
+   * Returns the property definition associated with this condition.
+   * @return the property definition associated with this condition.
+   */
+  public PropertyDefinition<?> getPropertyDefinition()
+  {
+    return impl.pd;
+  }
+
+  /**
+   * Returns the value that must be set for this condition to be fulfilled.
+   * @return the value that must be set for this condition to be fulfilled.
+   */
+  public Object getValue()
+  {
+    return impl.value;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/IsPresentCondition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/IsPresentCondition.java
new file mode 100644
index 0000000..17649d8
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/IsPresentCondition.java
@@ -0,0 +1,104 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import java.util.SortedSet;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * A condition which evaluates to <code>true</code> if and only if a
+ * particular property has any values specified.
+ */
+public final class IsPresentCondition implements Condition {
+
+  // The property name.
+  private final String propertyName;
+
+  // The property definition.
+  private PropertyDefinition<?> pd;
+
+
+
+  /**
+   * Creates a new is present condition.
+   *
+   * @param propertyName
+   *          The property name.
+   */
+  public IsPresentCondition(String propertyName) {
+    Validator.ensureNotNull(propertyName);
+    this.propertyName = propertyName;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ManagementContext context,
+      ManagedObject<?> managedObject) throws AuthorizationException,
+      CommunicationException {
+    SortedSet<?> values = managedObject.getPropertyValues(pd);
+    return !values.isEmpty();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    SortedSet<?> values = managedObject.getPropertyValues(pd);
+    return !values.isEmpty();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+      throws Exception {
+    // Decode the property.
+    this.pd = d.getPropertyDefinition(propertyName);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/NOTCondition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/NOTCondition.java
new file mode 100644
index 0000000..6cf0689
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/NOTCondition.java
@@ -0,0 +1,97 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * A condition which evaluates to <code>true</code> if the
+ * sub-condition is <code>false</code>, or <code>false</code> if
+ * the sub-condition is <code>true</code>.
+ */
+public final class NOTCondition implements Condition {
+
+  // The single sub-condition.
+  private final Condition condition;
+
+
+
+  /**
+   * Creates a new logical NOT condition with the provided
+   * sub-condition.
+   *
+   * @param condition
+   *          The sub-condition which will be inverted.
+   */
+  public NOTCondition(Condition condition) {
+    Validator.ensureNotNull(condition);
+    this.condition = condition;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ManagementContext context,
+      ManagedObject<?> managedObject) throws AuthorizationException,
+      CommunicationException {
+    return !condition.evaluate(context, managedObject);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    return !condition.evaluate(managedObject);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+      throws Exception {
+    condition.initialize(d);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/ORCondition.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/ORCondition.java
new file mode 100644
index 0000000..d10869b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/ORCondition.java
@@ -0,0 +1,112 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.condition;
+
+
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.client.AuthorizationException;
+import org.opends.server.admin.client.CommunicationException;
+import org.opends.server.admin.client.ManagedObject;
+import org.opends.server.admin.client.ManagementContext;
+import org.opends.server.admin.server.ServerManagedObject;
+import org.opends.server.config.ConfigException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * A condition which evaluates to <code>false</code> if and only if
+ * all of its sub-conditions are <code>false</code>.
+ */
+public final class ORCondition implements Condition {
+
+  // The list of sub-conditions.
+  private final List<Condition> conditions;
+
+
+
+  /**
+   * Creates a new logical OR condition with the provided
+   * sub-conditions.
+   *
+   * @param conditions
+   *          The sub-conditions which will be combined using a
+   *          logical OR.
+   */
+  public ORCondition(Condition... conditions) {
+    Validator.ensureNotNull(conditions);
+    this.conditions = Arrays.asList(conditions);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ManagementContext context,
+      ManagedObject<?> managedObject) throws AuthorizationException,
+      CommunicationException {
+    for (Condition condition : conditions) {
+      if (condition.evaluate(context, managedObject)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean evaluate(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    for (Condition condition : conditions) {
+      if (condition.evaluate(managedObject)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initialize(AbstractManagedObjectDefinition<?, ?> d)
+      throws Exception {
+    for (Condition condition : conditions) {
+      condition.initialize(d);
+    }
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/condition/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/condition/package-info.java
new file mode 100644
index 0000000..af0571f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/condition/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+/**
+ * Logical conditions for defining constraints.
+ * <p>
+ * This package contains interfaces for building and evaluating
+ * arbitrary logical conditions which can be used with constraints.
+ */
+@org.opends.server.types.PublicAPI(
+    stability = org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.condition;
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java b/opendj-admin/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java
new file mode 100644
index 0000000..7330ba4
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java
@@ -0,0 +1,1627 @@
+/*
+ * 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 2007-2010 Sun Microsystems, Inc.
+ *      Portions Copyright 2011-2013 ForgeRock AS
+ */
+package org.opends.server.admin.doc;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.opends.messages.Message;
+import org.opends.server.admin.ACIPropertyDefinition;
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AdministratorAction.Type;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.admin.*;
+import org.opends.server.types.InitializationException;
+import org.opends.server.util.EmbeddedUtils;
+import org.opends.server.util.DynamicConstants;
+
+/**
+ *  This class allow Configuration Guide documentation generation (html format).
+ * It is based on the Admin Framework Introspection API
+ *
+ */
+public class ConfigGuideGeneration {
+
+  // Note : still to be done :
+  // I18n support. Today all the strings are hardcoded in this file
+
+  private final static String ACI_SYNTAX_REL_URL =
+    "/doc/admin-guide/#about-acis";
+  private final static String DURATION_SYNTAX_REL_URL =
+    "duration-syntax.html";
+  private final String CSS_FILE = "opendj-config.css";
+
+  private final String MAIN_FILE = "index.html";
+  private final String INHERITANCE_TREE_FILE =
+    "ManagedObjectInheritanceTree.html";
+  private final String RELATION_TREE_FILE = "ManagedObjectRelationTree.html";
+  private final String MO_LIST_FILE = "ManagedObjectList.html";
+  private final String PROPERTIES_INDEX_FILE = "PropertiesIndex.html";
+  private final String WELCOME_FILE = "welcome.html";
+  private final String MAINTOP_FILE = "maintop.html";
+  private final String INDEX_FILE = "index.html";
+  private final String FAVICON = "http://forgerock.org/favicon.ico";
+
+  private static final String CONFIG_GUIDE_DIR = "opendj_config_guide";
+  private final String MAIN_FRAME = "mainFrame";
+
+  /**
+   * Entry point for documentation generation.
+   *
+   * Properties:
+   * GenerationDir - The directory where the doc is generated
+   *              (default is /var/tmp/[CONFIG_GUIDE_DIR>])
+   * LdapMapping - Presence means that the LDAP mapping section is to be
+   *               generated (default is no)
+   * OpenDJWiki - The URL of the OpenDJ Wiki
+   *              (default is
+   *              "http://wikis.forgerock.org/confluence/display/OPENDJ")
+   * OpenDJHome - The URL of the OpenDJ project Home page
+   *              (default is "http://opendj.forgerock.org")
+   *
+   * @param args none.
+   */
+  public static void main(String[] args) {
+    Properties properties = System.getProperties();
+    generationDir = properties.getProperty("GenerationDir");
+    if (generationDir == null) {
+      // Default dir is prefixed by the system-dependent default temporary dir
+      generationDir = System.getProperty("java.io.tmpdir") + File.separator +
+        CONFIG_GUIDE_DIR;
+    }
+    // Create new dir if necessary
+    try {
+      (new File(generationDir)).mkdir();
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+    System.out.println("Generation directory is : " + generationDir);
+
+    if (properties.getProperty("LdapMapping") != null) {
+      ldapMapping = true;
+    }
+
+    OpenDJWiki = properties.getProperty("OpenDJWiki");
+    if (OpenDJWiki == null) {
+      // Default is current wiki
+      OpenDJWiki = "http://wikis.forgerock.org/confluence/display/OPENDJ";
+    }
+
+    OpenDJHome = properties.getProperty("OpenDJHome");
+    if (OpenDJHome == null) {
+      // Default is current OpenDJ project home
+      OpenDJHome = "http://opendj.forgerock.org";
+    }
+
+    aciSyntaxPage = OpenDJHome + ACI_SYNTAX_REL_URL;
+    durationSyntaxPage = DURATION_SYNTAX_REL_URL;
+
+    ConfigGuideGeneration myGen = new ConfigGuideGeneration();
+    myGen.generate();
+  }
+
+  private void generate() {
+    init();
+
+    // Generate the relation tree of all the managed objects
+    genManagedObjectRelationTree(catTopRelList);
+
+    // Generate the inheritance tree of all the managed objects
+    genManagedObjectInheritanceTree(catTopMoList);
+
+    // Generate all the managed objects and their children
+    genAllManagedObject(topMoList);
+
+    // Generate a list of managed objects
+    genManagedObjectList(moList);
+
+    // Generate an index of properties
+    genPropertiesIndex();
+
+    // Generate the Index page
+    genIndexPage();
+
+    // Generate the Main Top page
+    genMainTopPage();
+
+    // Generate the Welcome page
+    genWelcomePage();
+   }
+
+  private void init() {
+
+    // Build a list of top relations
+    RootCfgDefn rootCfg = RootCfgDefn.getInstance();
+    for (RelationDefinition rel : rootCfg.getAllRelationDefinitions()) {
+      topRelList.put(rel.getChildDefinition().getName(), rel);
+    }
+
+    // Enable the client-side class loader to explicitly load classes
+    // which are not directly reachable from the root configuration
+    EmbeddedUtils.initializeForClientUse();
+    // Bootstrap definition classes.
+    try {
+      ClassLoaderProvider.getInstance().enable();
+    } catch (InitializationException e) {
+      System.err.println("ERROR : Cannot enable the client-side class loader.");
+      e.printStackTrace();
+      System.exit(1);
+    }
+    // Switch off class name validation in client.
+    ClassPropertyDefinition.setAllowClassValidation(false);
+    // Switch off attribute type name validation in client.
+    AttributeTypePropertyDefinition.setCheckSchema(false);
+
+    // Build a sorted list of top managed objects
+    TopCfgDefn topCfg = TopCfgDefn.getInstance();
+    Collection<AbstractManagedObjectDefinition<?, ?>> topObjects =
+      topCfg.getChildren();
+    for (AbstractManagedObjectDefinition topObject : topObjects) {
+      if (topObject.getName().equals("")) {
+        // root
+        continue;
+      }
+      if (topObject.hasOption(ManagedObjectOption.HIDDEN))
+      {
+        continue;
+      }
+      topMoList.put(topObject.getName(), topObject);
+    }
+
+
+    // Build a list of top relations by category (core, database, ...)
+    for (RelationDefinition rel : topRelList.values()) {
+      AbstractManagedObjectDefinition<?, ?> mo = rel.getChildDefinition();
+      Collection<Tag> tags = mo.getAllTags();
+      for (Tag tag : tags) {
+        TreeMap<String, RelationDefinition> catMap =
+          catTopRelList.get(tag.getName());
+        if (catMap == null) {
+          catMap = new TreeMap<String, RelationDefinition>();
+          catTopRelList.put(tag.getName(), catMap);
+        }
+        catMap.put(mo.getName(), rel);
+      }
+    }
+
+    // Build a list of top managed objects by category (core, database, ...)
+    for (AbstractManagedObjectDefinition<?, ?> topObject : topMoList.values()) {
+      Collection<Tag> tags = topObject.getAllTags();
+      for (Tag tag : tags) {
+        TreeMap<String, AbstractManagedObjectDefinition> catMap =
+          catTopMoList.get(tag.getName());
+        if (catMap == null) {
+          catMap = new TreeMap<String, AbstractManagedObjectDefinition>();
+          catTopMoList.put(tag.getName(), catMap);
+        }
+        catMap.put(topObject.getName(), topObject);
+      }
+    }
+
+  }
+
+  /**
+   * Generate the inheritance tree of all the managed objects.
+   */
+  @SuppressWarnings("unchecked")
+  private void genManagedObjectInheritanceTree(
+    TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>> list) {
+
+    htmlHeader(DynamicConstants.PRODUCT_NAME + " " +
+            "Configuration Reference - Inheritance View");
+    tabMenu(INHERITANCE_TREE_FILE);
+    viewHelp("This view represents the inheritance relationships between " +
+      "configuration components.");
+    jumpSection();
+
+    for (String catName : list.keySet()) {
+      heading3(getFriendlyName(catName));
+      // Get the list of the category
+      TreeMap<String, AbstractManagedObjectDefinition> catList =
+        list.get(catName);
+      for (AbstractManagedObjectDefinition mo : catList.values()) {
+        if ((relList.get(mo.getName()) != null) &&
+          (relList.get(mo.getName()).hasOption(RelationOption.HIDDEN))) {
+          continue;
+        }
+        paragraph(
+          getLink(mo.getUserFriendlyName().toString(),
+          mo.getName() + ".html", MAIN_FRAME));
+        if (mo.hasChildren()) {
+          genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
+        }
+      }
+    }
+
+    htmlFooter();
+    generateFile(INHERITANCE_TREE_FILE);
+  }
+
+  @SuppressWarnings("unchecked")
+  private void genMoInheritanceTree(
+    TreeMap<String, AbstractManagedObjectDefinition> catList) {
+
+    beginList();
+    for (AbstractManagedObjectDefinition mo : catList.values()) {
+      link(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
+        MAIN_FRAME);
+      if (mo.hasChildren()) {
+        genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
+      }
+    }
+    endList();
+  }
+
+   private void jumpSection() {
+     htmlBuff.append("<p class=\"category-index\">" +
+       "<strong>Jump To:</strong><br>\n");
+
+     String[] catNames = catTopMoList.keySet().toArray(new String[0]);
+    for (int ii=0; ii < catNames.length; ii++) {
+      if (ii != 0) {
+        htmlBuff.append(", ");
+      }
+      String catFriendlyName = getFriendlyName(catNames[ii]);
+      htmlBuff.append(getLink(catFriendlyName, "#" + catFriendlyName));
+    }
+    htmlBuff.append("</p>\n");
+   }
+
+
+  /**
+   * Generate the relation tree of all the managed objects.
+   */
+  private void genManagedObjectRelationTree(
+    TreeMap <String, TreeMap<String, RelationDefinition>> list) {
+
+    htmlHeader(DynamicConstants.PRODUCT_NAME +
+            " Configuration Reference - Structure View");
+    tabMenu(RELATION_TREE_FILE);
+    viewHelp("This view represents the structural relationships between " +
+      "components and indicates how certain components can exist only within " +
+      "container components.");
+    jumpSection();
+
+    for (String catName : list.keySet()) {
+      heading3(getFriendlyName(catName));
+      // Get the list of the category
+      TreeMap<String, RelationDefinition> catList = list.get(catName);
+      genMORelationTree(catList);
+    }
+
+    htmlFooter();
+    generateFile(RELATION_TREE_FILE);
+  }
+
+
+  @SuppressWarnings("unchecked")
+  private void genMORelationTree(TreeMap<String, RelationDefinition> list) {
+    for (RelationDefinition rel : list.values()) {
+      AbstractManagedObjectDefinition childMo = rel.getChildDefinition();
+      AbstractManagedObjectDefinition parentMo = rel.getParentDefinition();
+      // Does not generate several entry for the same relation
+      if (relList.put(childMo.getName(), rel) != null) {
+       continue;
+      }
+      if (rel.hasOption(RelationOption.HIDDEN)) {
+        continue;
+      }
+      String linkStr = getLink(childMo.getUserFriendlyName().toString(),
+        childMo.getName() + ".html", MAIN_FRAME);
+      String fromStr = "";
+      if (!parentMo.getName().equals("")) {
+        fromStr = " (from " +
+          getLink(parentMo.getUserFriendlyName().toString(),
+          parentMo.getName() + ".html", MAIN_FRAME) + ")";
+      }
+      if (!inList) {
+        paragraph(linkStr + fromStr);
+      } else {
+        bullet(linkStr + fromStr);
+      }
+      genMORelationSubTree(makeRelTreeMap(childMo.getAllRelationDefinitions()));
+      if (childMo.hasChildren()) {
+        for (Iterator<AbstractManagedObjectDefinition> it =
+          childMo.getChildren().iterator(); it.hasNext();) {
+
+          AbstractManagedObjectDefinition mo = it.next();
+          if (mo.hasOption(ManagedObjectOption.HIDDEN))
+          {
+            continue;
+          }
+          genMORelationSubTree(makeRelTreeMap(mo.getAllRelationDefinitions()));
+        }
+      }
+    }
+  }
+
+
+  private void genMORelationSubTree(TreeMap<String, RelationDefinition> list) {
+    if (!list.values().isEmpty()) {
+      beginList();
+      genMORelationTree(list);
+      endList();
+    }
+  }
+
+
+  /**
+   * Generate all the managed objects HTML pages.
+   */
+  @SuppressWarnings("unchecked")
+  private void genAllManagedObject(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    for (AbstractManagedObjectDefinition mo : list.values()) {
+      if ((relList.get(mo.getName()) != null) &&
+        (relList.get(mo.getName()).hasOption(RelationOption.HIDDEN))) {
+        continue;
+      }
+      moList.put(mo.getName(), mo);
+      genManagedObject(mo);
+      if (mo.hasChildren()) {
+        genAllManagedObject(makeMOTreeMap(mo.getChildren()));
+      }
+    }
+  }
+
+  private void genManagedObject(AbstractManagedObjectDefinition mo) {
+    //------------------------------------------------------------------------
+    // Header
+    //------------------------------------------------------------------------
+
+    homeLink();
+    String title = mo.getUserFriendlyName().toString();
+    htmlHeader(DynamicConstants.PRODUCT_NAME + " - " + title);
+
+    // title
+    heading2(title);
+
+    // Abstract notice
+    if (mo.hasChildren()) {
+      paragraph(
+        "Note: this is an abstract component, that cannot be instantiated.",
+        TextStyle.ITALIC);
+    }
+
+    // description
+    paragraph(mo.getSynopsis());
+    paragraph(mo.getDescription());
+
+    // sub-components
+    if (mo.hasChildren()) {
+      heading3("Direct Subcomponents");
+      paragraph("The following " + mo.getUserFriendlyPluralName() +
+        " are available in the server :");
+      beginList();
+      @SuppressWarnings("unchecked")
+      TreeMap<String, AbstractManagedObjectDefinition> children =
+        makeMOTreeMap(mo.getChildren());
+      for ( AbstractManagedObjectDefinition child : children.values()) {
+        link(child.getUserFriendlyName().toString(), child.getName() + ".html");
+      }
+      endList();
+
+      paragraph("These " + mo.getUserFriendlyPluralName() +
+        " inherit from the properties described below.");
+    }
+
+    // Parent
+    if (!mo.getParent().isTop()) {
+      heading3("Parent Component");
+      paragraph("The " + mo.getUserFriendlyName() +
+        " component inherits from the " +
+        getLink(mo.getParent().getUserFriendlyName().toString(),
+        mo.getParent().getName() + ".html"));
+    }
+
+    // Relations
+    generateRelationsSection(mo);
+
+    // Page links in case of LDAP mapping
+    if (ldapMapping) {
+      newline();
+      horizontalLine();
+      newline();
+      paragraph("This page describes the " + mo.getUserFriendlyName() + ":");
+      beginList();
+      link("Properties", "#Properties");
+      link("LDAP Mapping", "#LDAP Mapping");
+      endList();
+      newline();
+    }
+
+
+    //------------------------------------------------------------------------
+    // Properties
+    //------------------------------------------------------------------------
+
+    heading3("Properties");
+
+    paragraph("A description of each property follows.");
+    newline();
+
+    TreeMap<String, PropertyDefinition> basicProps =
+      new TreeMap<String, PropertyDefinition>();
+    TreeMap<String, PropertyDefinition> advancedProps =
+      new TreeMap<String, PropertyDefinition>();
+    // Properties actually defined in this managed object
+    @SuppressWarnings("unchecked")
+    Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
+    for ( PropertyDefinition prop : props) {
+      if (prop.hasOption(PropertyOption.ADVANCED)) {
+        advancedProps.put(prop.getName(), prop);
+      } else {
+        basicProps.put(prop.getName(), prop);
+      }
+    }
+
+    propertiesLinkTable(basicProps, advancedProps);
+
+    // basic properties
+    if (basicProps.size() > 0) {
+      heading4("Basic Properties");
+      for ( PropertyDefinition prop : basicProps.values()) {
+        generateProperty(mo, prop);
+        newline();
+      }
+      newline();
+    }
+
+    // advanced properties
+    if (advancedProps.size() > 0) {
+      heading4("Advanced Properties");
+      for ( PropertyDefinition prop : advancedProps.values()) {
+        generateProperty(mo, prop);
+        newline();
+      }
+      newline();
+    }
+
+    if (ldapMapping) {
+      genLdapMapping(mo);
+    }
+
+    htmlFooter();
+
+    generateFile(mo.getName() + ".html");
+  }
+
+
+  private TreeMap<String, PropertyDefinition>
+    getPropertyList(AbstractManagedObjectDefinition mo) {
+
+    @SuppressWarnings("unchecked")
+    Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
+    return makePropTreeMap(props);
+  }
+
+  private void homeLink() {
+    htmlBuff.append("<div style=\"font-size:11px;margin-top:-10px;" +
+      "margin-bottom:-10px; text-align:right\"><a href=\"" +
+      MAIN_FILE +
+      "\" target=\"_top\">Configuration Reference Home</a></div>");
+  }
+
+
+  private void generateRelationsSection(AbstractManagedObjectDefinition mo) {
+    // Composition relations
+    @SuppressWarnings("unchecked")
+    Collection<RelationDefinition> compRels = mo.getRelationDefinitions();
+    @SuppressWarnings("unchecked")
+    Collection<RelationDefinition> reverseCompRels =
+      mo.getReverseRelationDefinitions();
+    // Aggregation properties
+    @SuppressWarnings("unchecked")
+    Collection<AggregationPropertyDefinition> aggregProps =
+      mo.getAggregationPropertyDefinitions();
+    @SuppressWarnings("unchecked")
+    Collection<AggregationPropertyDefinition> reverseAggregProps =
+      mo.getReverseAggregationPropertyDefinitions();
+
+
+    // Check if something to print in composition relations
+    // (even if the list not empty, it may contain only hidden relations)
+    boolean isCompRelsEmpty = true;
+    if (!compRels.isEmpty()) {
+      for (RelationDefinition rel : compRels) {
+        if (rel.hasOption(RelationOption.HIDDEN)) {
+          continue;
+        }
+        isCompRelsEmpty = false;
+      }
+    }
+    boolean isReverseCompRelsEmpty = true;
+    if (!reverseCompRels.isEmpty()) {
+      for (RelationDefinition rel : reverseCompRels) {
+        if (rel.hasOption(RelationOption.HIDDEN)) {
+          continue;
+        }
+        // check if it is not root
+        if (rel.getParentDefinition().getName().equals("")) {
+          continue;
+        }
+        isReverseCompRelsEmpty = false;
+      }
+    }
+
+    // Check if something to print in reverse aggregation relations
+    // (even if the list not empty, it may contain only relations from
+    // hidden component)
+    boolean isReverseAggregPropsEmpty = true;
+    if (!reverseAggregProps.isEmpty()) {
+      for (AggregationPropertyDefinition agg : reverseAggregProps) {
+        AbstractManagedObjectDefinition fromMo =
+          agg.getManagedObjectDefinition();
+        @SuppressWarnings("unchecked")
+        Collection<RelationDefinition> rels =
+          fromMo.getAllReverseRelationDefinitions();
+        for (RelationDefinition rel : rels) {
+          if (rel.hasOption(RelationOption.HIDDEN)) {
+            continue;
+          }
+          isReverseAggregPropsEmpty = false;
+        }
+      }
+    }
+
+
+    //
+    // Relations FROM this component
+    //
+
+    if (!isCompRelsEmpty || !aggregProps.isEmpty()) {
+        heading3("Relations From this Component");
+    }
+
+    if (!isCompRelsEmpty) {
+      paragraph(
+        "The following components have a direct COMPOSITION relation FROM " +
+        mo.getUserFriendlyPluralName() + " :");
+      for ( RelationDefinition rel : compRels) {
+        if (rel.hasOption(RelationOption.HIDDEN)) {
+          continue;
+        }
+        beginList();
+        AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
+        link(childRel.getUserFriendlyName().toString(), childRel.getName() +
+          ".html");
+        endList();
+      }
+    }
+    if (!aggregProps.isEmpty()) {
+      paragraph(
+        "The following components have a direct AGGREGATION relation FROM " +
+        mo.getUserFriendlyPluralName() + " :");
+      TreeMap<String, AbstractManagedObjectDefinition> componentList =
+        new TreeMap<String, AbstractManagedObjectDefinition>();
+      for ( AggregationPropertyDefinition agg : aggregProps) {
+        RelationDefinition rel = agg.getRelationDefinition();
+        AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
+        componentList.put(childRel.getName(), childRel);
+      }
+      for (AbstractManagedObjectDefinition component : componentList.values()) {
+        beginList();
+        link(component.getUserFriendlyName().toString(), component.getName() +
+          ".html");
+        endList();
+      }
+    }
+
+
+    //
+    // Relations TO this component
+    //
+
+    if (!isReverseCompRelsEmpty || !isReverseAggregPropsEmpty) {
+        heading3("Relations To this Component");
+    }
+
+    if (!mo.getReverseRelationDefinitions().isEmpty()) {
+      if (!isReverseCompRelsEmpty) {
+        paragraph(
+          "The following components have a direct COMPOSITION relation TO " +
+          mo.getUserFriendlyPluralName() + " :");
+        for ( RelationDefinition rel : reverseCompRels) {
+          beginList();
+          AbstractManagedObjectDefinition childRel = rel.getParentDefinition();
+          link(childRel.getUserFriendlyName().toString(), childRel.getName() +
+            ".html");
+          endList();
+        }
+      }
+    }
+    if (!isReverseAggregPropsEmpty) {
+      paragraph(
+        "The following components have a direct AGGREGATION relation TO " +
+        mo.getUserFriendlyPluralName() + " :");
+      TreeMap<String, AbstractManagedObjectDefinition> componentList =
+        new TreeMap<String, AbstractManagedObjectDefinition>();
+      for ( AggregationPropertyDefinition agg : reverseAggregProps) {
+        AbstractManagedObjectDefinition fromMo =
+          agg.getManagedObjectDefinition();
+        componentList.put(fromMo.getName(), fromMo);
+      }
+      for (AbstractManagedObjectDefinition component : componentList.values()) {
+        beginList();
+        link(component.getUserFriendlyName().toString(), component.getName() +
+          ".html");
+        endList();
+
+      }
+    }
+
+  }
+
+  private void generateProperty(
+    AbstractManagedObjectDefinition mo, PropertyDefinition prop) {
+
+    // Property name
+    paragraph(getAnchor(prop.getName()) + prop.getName(), TextStyle.STANDARD,
+      "propertyname");
+
+    // Property table
+    startTable();
+    tableRow("Description",
+      ((prop.getSynopsis() != null) ? prop.getSynopsis().toString()+ " " : "") +
+      ((prop.getDescription() != null) ?
+        prop.getDescription().toString() : ""));
+
+    // Default value
+    String defValueStr = getDefaultBehaviorString(prop);
+    tableRow("Default Value", defValueStr);
+
+    tableRow("Allowed Values", getSyntaxStr(prop));
+
+    tableRow("Multi-valued",
+      (prop.hasOption(PropertyOption.MULTI_VALUED) ? "Yes" : "No"));
+
+    if (prop.hasOption(PropertyOption.MANDATORY)) {
+      tableRow("Required", "Yes");
+    } else {
+      tableRow("Required", "No");
+    }
+
+    String action = "None";
+    if (prop.getAdministratorAction() != null) {
+      Message synopsis = prop.getAdministratorAction().getSynopsis();
+      Type actionType = prop.getAdministratorAction().getType();
+      String actionStr = "";
+      if (actionType == Type.COMPONENT_RESTART) {
+        actionStr = "The " + mo.getUserFriendlyName() +
+          " must be disabled and re-enabled for changes to this setting " +
+          "to take effect";
+      } else if (actionType == Type.SERVER_RESTART) {
+        actionStr = "Restart the server";
+      } else if (actionType == Type.NONE) {
+        actionStr = "None";
+      }
+      String dot = (actionStr.equals("") ? "" : ". ");
+      action = actionStr +
+        ((synopsis != null) ? dot + synopsis : "");
+    }
+    tableRow("Admin Action Required", action);
+
+    if (prop.hasOption(PropertyOption.ADVANCED)) {
+      tableRow("Advanced Property", "Yes");
+    } else {
+      tableRow("Advanced Property", "No");
+    }
+
+    if (prop.hasOption(PropertyOption.READ_ONLY)) {
+      tableRow("Read-only", "Yes");
+    } else {
+      tableRow("Read-only", "No");
+    }
+
+    endTable();
+
+  }
+
+
+  private void propertiesLinkTable(TreeMap<String,
+    PropertyDefinition> basicProps,
+    TreeMap<String, PropertyDefinition> advancedProps) {
+    htmlBuff.append(
+      "<table border=\"0\" cellspacing=\"0\" class=\"jump-table\">\n" +
+      "  <tr>\n" +
+      "    <th>Basic Properties:</th>\n" +
+      "    <th>Advanced Properties:</th>\n" +
+      "  </tr>\n");
+
+    PropertyDefinition[] basicPropsArray =
+      basicProps.values().toArray(new PropertyDefinition[0]);
+    PropertyDefinition[] advancedPropsArray =
+      advancedProps.values().toArray(new PropertyDefinition[0]);
+
+    for (int ii=0;
+        (ii < basicPropsArray.length) || (ii < advancedPropsArray.length);
+        ii++) {
+      String basicPropName =
+        ii < basicPropsArray.length ? basicPropsArray[ii].getName() : null;
+      String advancedPropName =
+        ii < advancedPropsArray.length ?
+          advancedPropsArray[ii].getName() : null;
+
+      String basicHtmlCell = "";
+      if (basicPropName != null) {
+        basicHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + basicPropName + "\">"
+          + basicPropName + "</a></td>\n";
+      } else if ((basicPropsArray.length == 0) && (ii == 0)) {
+        basicHtmlCell = "  <td>&nbsp;None</td>\n";
+      } else if (ii >= basicPropsArray.length) {
+        // Case of nb of basic props < nb of advanced props
+        basicHtmlCell = "  <td></td>\n";
+      }
+
+      String advancedHtmlCell = "";
+      if (advancedPropName != null) {
+        advancedHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + advancedPropName +
+          "\">" + advancedPropName + "</a></td>\n";
+      } else if ((advancedPropsArray.length == 0) && (ii == 0)) {
+        advancedHtmlCell = "  <td>&nbsp;None</td>\n";
+      }
+
+      htmlBuff.append("<tr>\n");
+      htmlBuff.append(basicHtmlCell + advancedHtmlCell);
+      htmlBuff.append("</tr>\n");
+    }
+    htmlBuff.append("</table>\n");
+  }
+
+
+  private void genLdapMapping(AbstractManagedObjectDefinition mo) {
+    //------------------------------------------------------------------------
+    // LDAP mapping
+    //------------------------------------------------------------------------
+
+    heading3("LDAP Mapping");
+    paragraph(
+      "Each configuration property can be mapped to a specific " +
+      "LDAP attribute under the \"cn=config\" entry. " +
+      "The mappings that follow are provided for information only. " +
+      "In general, you should avoid changing the server configuration " +
+      "by manipulating the LDAP attributes directly.");
+
+    // Managed object table
+    startTable();
+
+    LDAPProfile ldapProfile = LDAPProfile.getInstance();
+    tableRow("Base DN", getBaseDN(mo, ldapProfile));
+
+    tableRow("objectclass name", ldapProfile.getObjectClass(mo));
+    if (mo.getParent().getName() != null) {
+      String superior = "";
+      if (mo.getParent().getName().equals("top")) {
+        superior = "top";
+      } else {
+        if (moList.get(mo.getParent().getName()) != null) {
+          superior =
+            ldapProfile.getObjectClass(moList.get(mo.getParent().getName()));
+        } else {
+          System.err.println(
+            "Error: managed object " + mo.getName() + " not found.");
+        }
+      }
+      tableRow("objectclass superior", superior);
+    } else {
+      System.err.println(
+        "Error: objectclass superior not found for " + mo.getName());
+    }
+    endTable();
+
+    newline();
+    // Properties table
+    startTable();
+    tableRow("Property", "LDAP attribute");
+    for ( PropertyDefinition prop : getPropertyList(mo).values()) {
+      tableRow(prop.getName(), ldapProfile.getAttributeName(mo, prop));
+    }
+
+    endTable();
+
+  }
+
+  private void genManagedObjectList(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    htmlHeader(DynamicConstants.PRODUCT_NAME
+            + " Configuration Reference - Components View");
+    tabMenu(MO_LIST_FILE);
+    viewHelp("This view provides a list of all configuration components, " +
+      "in alphabetical order.");
+
+    newline();
+    StringBuffer moPointers = new StringBuffer();
+    String lettersPointers = "";
+    String firstChar = ".";
+    for (AbstractManagedObjectDefinition mo : list.values()) {
+      if (!mo.getName().startsWith(firstChar)) {
+        firstChar = mo.getName().substring(0, 1);
+        String letter = firstChar.toUpperCase();
+        moPointers.append(getAnchor(letter) + getHeading2(letter));
+        lettersPointers += getLink(letter, "#" + letter) + " ";
+      }
+      moPointers.append(
+        "<p> " +
+        getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
+        MAIN_FRAME) +
+        "</p>\n");
+    }
+    paragraph(lettersPointers);
+    htmlBuff.append(moPointers);
+    htmlFooter();
+    generateFile(MO_LIST_FILE);
+  }
+
+  private void genPropertiesIndex() {
+
+    // Build a sorted list of (property name + its managed object name)
+    TreeSet<String> propMoList = new TreeSet<String>();
+    for (AbstractManagedObjectDefinition<?, ?> mo : moList.values()) {
+      for (PropertyDefinition<?> prop : mo.getPropertyDefinitions()) {
+        propMoList.add(
+          prop.getName() + "," + prop.getManagedObjectDefinition().getName());
+      }
+    }
+
+    String lettersPointers = "";
+    String firstChar = ".";
+    for (String propMoStr : propMoList) {
+      String[] propMoArray = propMoStr.split(",");
+      String propName = propMoArray[0];
+      AbstractManagedObjectDefinition mo = moList.get(propMoArray[1]);
+      if (!propName.startsWith(firstChar)) {
+        firstChar = propName.substring(0, 1);
+        String letter = firstChar.toUpperCase();
+        htmlBuff.append(getAnchor(letter) + getHeading2(letter));
+        lettersPointers += getLink(letter, "#" + letter) + " ";
+      }
+      String propLink = getLink(propName,
+        mo.getName() + ".html" + "#" + propName, MAIN_FRAME);
+      String moLink =
+        getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
+        MAIN_FRAME, "#666");
+      paragraph(propLink + "  [ " + moLink + " ]");
+    }
+
+    String indexBody = htmlBuff.toString();
+    htmlBuff = new StringBuffer();
+    htmlHeader(DynamicConstants.PRODUCT_NAME +
+            " Configuration Reference - Properties View");
+    tabMenu(PROPERTIES_INDEX_FILE);
+    viewHelp("This view provides a list of all configuration properties, " +
+      "in alphabetical order, and indicates the configuration component to " +
+      "which each property applies.");
+
+    newline();
+    paragraph(lettersPointers);
+    htmlBuff.append(indexBody);
+    htmlFooter();
+    generateFile(PROPERTIES_INDEX_FILE);
+  }
+
+    private void genWelcomePage() {
+    htmlHeader(DynamicConstants.PRODUCT_NAME +
+            " Configuration Reference - Welcome");
+    heading2("About This Reference");
+    paragraph("This reference " +
+      "describes the " + DynamicConstants.PRODUCT_NAME +
+      " configuration properties that can be manipulated " +
+      "with the dsconfig command.");
+    paragraph("Configuration components are grouped according to the area of " +
+      "the server in which they are used, as follows:");
+
+    beginList();
+    for (String catName : catTopMoList.keySet()) {
+      bullet(getFriendlyName(catName));
+    }
+    endList();
+
+    paragraph(
+      "For ease of reference, the configuration is described on multiple " +
+      "tabs. These tabs provide alternative views of the configuration " +
+      "components:");
+    beginList();
+    bullet("The <strong>Inheritance</strong> view represents the inheritance " +
+      "relationships between configuration components. A sub-component " +
+      "inherits all of the properties of its parent component.");
+    bullet("The <strong>Structure</strong> view represents the structural " +
+      "relationships between components and indicates how certain components " +
+      "can exist only within container components. When a container " +
+      "component is deleted, all of the components within it are also " +
+      "deleted.");
+    bullet(
+      "The <strong>Components</strong> view provides an alphabetical list " +
+      "of all configuration components.");
+    bullet(
+      "The <strong>Properties</strong> view provides an alphabetical list " +
+      "of all configuration properties, and indicates the configuration " +
+      "component to which each property applies.");
+    endList();
+
+    newline();
+    paragraph("When you set up " +
+            DynamicConstants.PRODUCT_NAME +
+            ", certain components are created in the " +
+      "configuration by default. These components are configured with " +
+      "specific values, which are not necessarily the same as the " +
+      "\"default values\" of new components that you create using dsconfig. " +
+      "The \"default values\" listed in this document refer to the values " +
+      "of the new components that you create using dsconfig.");
+
+    htmlFooter();
+    generateFile(WELCOME_FILE);
+
+  }
+
+  private void genMainTopPage() {
+    htmlHeader(DynamicConstants.PRODUCT_NAME +
+            " Configuration Reference - Main Top");
+    htmlBuff.append("<div class=\"breadcrumb\"><span class=\"pageactions\">" +
+      "<a href=\"" + OpenDJHome + "\" target=\"_parent\">" +
+      "<span style=\"font-size: 12px;\">&laquo;&nbsp;&nbsp;</span>" +
+      "Back to " +
+      DynamicConstants.PRODUCT_NAME + " Home</a></span>&nbsp;&nbsp;</div>\n");
+    htmlBuff.append("<table class=\"titletable\" cellspacing=\"0\" " +
+      "width=\"100%\">\n");
+    htmlBuff.append("<tbody><tr>\n");
+    htmlBuff.append("  <td><h2>"+
+            DynamicConstants.PRODUCT_NAME +
+            " Configuration Reference</h2></td>\n");
+    htmlBuff.append("  <td valign=\"bottom\" width=\"10%\">" +
+      "<a href=\"" + OpenDJHome + "\" target=\"_parent\">" +
+      "<img src=\"opendj_logo_sm.png\" alt=\"OpenDJ Logo\" align=\"bottom\" " +
+      "border=\"0\" height=\"33\" width=\"114\"></a></td>\n");
+    htmlBuff.append("</tr>\n");
+    htmlBuff.append("</tbody></table>\n");
+
+    htmlFooter();
+    generateFile(MAINTOP_FILE);
+
+  }
+
+  private void genIndexPage() {
+    htmlBuff.append(getHtmlHeader(
+            DynamicConstants.PRODUCT_NAME + " Configuration Reference"));
+
+    htmlBuff.append("<frameset rows=\"80,*\" framespacing=\"1\" " +
+      "frameborder=\"yes\" border=\"1\" bordercolor=\"#333333\">\n");
+    htmlBuff.append("  <frame src=\"" + MAINTOP_FILE + "\" name=\"topFrame\" " +
+      "id=\"topFrame\" border=\"1\" title=\"topFrame\" scrolling=\"no\">\n");
+    htmlBuff.append("  <frameset cols=\"375,*\" frameborder=\"yes\" " +
+      "border=\"1\" " +
+      "framespacing=\"1\">\n");
+    htmlBuff.append("     <frame src=\"" + INHERITANCE_TREE_FILE + "\" " +
+      "name=\"leftFrame\" id=\"leftFrame\" title=\"leftFrame\" " +
+      "scrolling=\"auto\">\n");
+    htmlBuff.append("     <frame src=\"" + WELCOME_FILE +
+      "\" name=\"mainFrame\" " +
+      "id=\"mainFrame\" title=\"mainFrame\" scrolling=\"auto\">\n");
+    htmlBuff.append("   </frameset>\n");
+    htmlBuff.append("</frameset>\n");
+    htmlBuff.append("<noframes><body>\n");
+    htmlBuff.append("</body>\n");
+    htmlBuff.append("</noframes>\n");
+    htmlBuff.append("</html>\n");
+
+    generateFile(INDEX_FILE);
+  }
+
+  private String getBaseDN(
+    AbstractManagedObjectDefinition mo, LDAPProfile ldapProfile) {
+
+    RelationDefinition rel = relList.get(mo.getName());
+    if (rel != null) {
+      String baseDn = ldapProfile.getRelationRDNSequence(rel);
+      if (!baseDn.equals("")) {
+        return baseDn;
+      } else {
+        // Check the parent relation
+        return getBaseDN(rel.getParentDefinition(), ldapProfile);
+      }
+    } else if (moList.get(mo.getParent().getName()) != null) {
+      // check its superior
+      return getBaseDN(moList.get(mo.getParent().getName()), ldapProfile);
+    } else {
+      System.err.println("Error: Base DN not found for " + mo.getName());
+    }
+    return null;
+  }
+
+  @SuppressWarnings("unchecked")
+  private String getSyntaxStr(PropertyDefinition prop) {
+    // Create a visitor for performing syntax specific processing.
+    PropertyDefinitionVisitor<String, Void> visitor =
+      new PropertyDefinitionVisitor<String, Void>() {
+
+      @Override
+      public String visitACI(ACIPropertyDefinition prop, Void p) {
+        return getLink("An ACI Syntax", aciSyntaxPage);
+      }
+
+      @Override
+      public String visitAggregation(
+        AggregationPropertyDefinition prop, Void p) {
+
+        RelationDefinition rel = prop.getRelationDefinition();
+        String linkStr = getLink(rel.getUserFriendlyName().toString(),
+          rel.getName() + ".html");
+      return "The DN of any " +  linkStr + ". " +
+        ((prop.getSourceConstraintSynopsis() != null) ?
+          prop.getSourceConstraintSynopsis().toString() : "");
+      }
+
+      @Override
+      public String visitAttributeType(
+        AttributeTypePropertyDefinition prop, Void p) {
+        return "The name of an attribute type defined in the server schema.";
+      }
+
+      @Override
+      public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
+        return "true" + getNewLine() + "false";
+      }
+
+      @Override
+      public String visitClass(ClassPropertyDefinition prop, Void p) {
+        String classStr =
+          "A java class that implements or extends the class(es) :";
+        for (String clazz : prop.getInstanceOfInterface()) {
+          classStr += getNewLine() + clazz;
+        }
+        return classStr;
+      }
+
+      @Override
+      public String visitDN(DNPropertyDefinition prop, Void p) {
+        String retStr = "A valid DN.";
+        if (prop.getBaseDN() != null) {
+          retStr += prop.getBaseDN().toString();
+        }
+        return retStr;
+      }
+
+      @Override
+      public String visitDuration(DurationPropertyDefinition prop, Void p) {
+        String durationStr = "";
+
+        durationStr += getLink("A duration Syntax", durationSyntaxPage) +
+          ". ";
+        if (prop.isAllowUnlimited()) {
+          durationStr += "A value of \"-1\" or \"unlimited\" for no limit. ";
+        }
+        if (prop.getMaximumUnit() != null) {
+          durationStr += "Maximum unit is \"" +
+            prop.getMaximumUnit().getLongName() + "\". ";
+        }
+        long lowerLimitStr = new Double(prop.getBaseUnit().
+          fromMilliSeconds(prop.getLowerLimit())).longValue();
+        durationStr += "Lower limit is " + lowerLimitStr +
+          " " + prop.getBaseUnit().getLongName() + ". ";
+        if (prop.getUpperLimit() != null) {
+          long upperLimitStr = new Double(prop.getBaseUnit().
+            fromMilliSeconds(prop.getUpperLimit())).longValue();
+          durationStr += "Upper limit is " + upperLimitStr +
+            " " + prop.getBaseUnit().getLongName() + ". ";
+        }
+
+        return durationStr;
+      }
+
+      @Override
+      public String visitEnum(EnumPropertyDefinition prop, Void p) {
+        String enumStr = "";
+        Class en = prop.getEnumClass();
+        for (Object cst : en.getEnumConstants()) {
+          enumStr += cst.toString();
+          if (prop.getValueSynopsis((Enum) cst) != null) {
+            enumStr += " - " + prop.getValueSynopsis((Enum) cst).toString();
+          }
+          enumStr += getNewLine() + getNewLine();
+        }
+        return enumStr;
+      }
+
+      @Override
+      public String visitInteger(IntegerPropertyDefinition prop, Void p) {
+        String intStr = "An integer value.";
+        intStr += " Lower value is " + prop.getLowerLimit() + ".";
+        if (prop.getUpperLimit() != null) {
+          intStr += " Upper value is " + prop.getUpperLimit() + " .";
+        }
+        if (prop.isAllowUnlimited()) {
+          intStr += " A value of \"-1\" or \"unlimited\" for no limit.";
+        }
+        if (prop.getUnitSynopsis() != null) {
+          intStr += " Unit is " + prop.getUnitSynopsis() + ".";
+        }
+        return intStr;
+      }
+
+      @Override
+      public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
+        return "An IP address";
+      }
+
+      @Override
+      public String visitIPAddressMask(
+        IPAddressMaskPropertyDefinition prop, Void p) {
+
+        return "An IP address mask";
+      }
+
+      @Override
+      public String visitSize(SizePropertyDefinition prop, Void p) {
+        String sizeStr = "A positive integer representing a size.";
+        if (prop.getLowerLimit() != 0) {
+          sizeStr += " Lower value is " + prop.getLowerLimit() + ".";
+        }
+        if (prop.getUpperLimit() != null) {
+          sizeStr += " Upper value is " + prop.getUpperLimit() + " .";
+        }
+        if (prop.isAllowUnlimited()) {
+          sizeStr += " A value of \"-1\" or \"unlimited\" for no limit.";
+        }
+        return sizeStr;
+      }
+
+      @Override
+      public String visitString(StringPropertyDefinition prop, Void p) {
+        String retStr = "A String";
+        if (prop.getPatternSynopsis() != null) {
+          retStr = prop.getPatternSynopsis().toString();
+        }
+        return retStr;
+      }
+
+      @Override
+      public String visitUnknown(PropertyDefinition prop, Void p) {
+        return "Unknown";
+      }
+    };
+
+    // Invoke the visitor against the property definition.
+    return (String) prop.accept(visitor, null);
+
+  }
+
+  @SuppressWarnings("unchecked")
+  private String getDefaultBehaviorString(PropertyDefinition prop) {
+    DefaultBehaviorProvider defaultBehav = prop.getDefaultBehaviorProvider();
+    String defValueStr = "";
+    if (defaultBehav instanceof UndefinedDefaultBehaviorProvider) {
+      defValueStr = "None";
+    } else if (defaultBehav instanceof DefinedDefaultBehaviorProvider) {
+      DefinedDefaultBehaviorProvider defBehav =
+        (DefinedDefaultBehaviorProvider) defaultBehav;
+      for (Iterator<String> it = defBehav.getDefaultValues().iterator();
+      it.hasNext();) {
+
+        String str = it.next();
+        defValueStr += str + (it.hasNext() ? "\n" : "");
+      }
+    } else if (defaultBehav instanceof AliasDefaultBehaviorProvider) {
+      AliasDefaultBehaviorProvider aliasBehav = (
+        AliasDefaultBehaviorProvider) defaultBehav;
+      defValueStr = aliasBehav.getSynopsis().toString();
+    } else if
+      (defaultBehav instanceof RelativeInheritedDefaultBehaviorProvider) {
+      RelativeInheritedDefaultBehaviorProvider relativBehav =
+        (RelativeInheritedDefaultBehaviorProvider) defaultBehav;
+      defValueStr = getDefaultBehaviorString(
+        relativBehav.getManagedObjectDefinition().
+        getPropertyDefinition(relativBehav.getPropertyName()));
+    } else if
+      (defaultBehav instanceof AbsoluteInheritedDefaultBehaviorProvider) {
+      AbsoluteInheritedDefaultBehaviorProvider absoluteBehav =
+        (AbsoluteInheritedDefaultBehaviorProvider) defaultBehav;
+      defValueStr = getDefaultBehaviorString(
+        absoluteBehav.getManagedObjectDefinition().
+        getPropertyDefinition(absoluteBehav.getPropertyName()));
+    }
+    return defValueStr;
+  }
+
+  private TreeMap<String, AbstractManagedObjectDefinition> makeMOTreeMap(
+    Collection<AbstractManagedObjectDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, AbstractManagedObjectDefinition> map =
+      new TreeMap<String, AbstractManagedObjectDefinition>();
+    for (AbstractManagedObjectDefinition mo : coll) {
+      if (mo.hasOption(ManagedObjectOption.HIDDEN))
+      {
+        continue;
+      }
+      map.put(mo.getName(), mo);
+    }
+    return map;
+  }
+
+  private TreeMap<String, RelationDefinition> makeRelTreeMap(
+    Collection<RelationDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, RelationDefinition> map =
+      new TreeMap<String, RelationDefinition>();
+    for (RelationDefinition rel : coll) {
+      map.put(rel.getChildDefinition().getName(), rel);
+    }
+    return map;
+  }
+
+  private TreeMap<String, PropertyDefinition> makePropTreeMap(
+    Collection<PropertyDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, PropertyDefinition> map =
+      new TreeMap<String, PropertyDefinition>();
+    for (PropertyDefinition prop : coll) {
+      map.put(prop.getName(), prop);
+    }
+    return map;
+  }
+
+  private void horizontalLine() {
+    htmlBuff.append("<hr style=\"width: 100%; height: 2px;\">");
+  }
+
+  private void endTable() {
+    htmlBuff.append("</tbody>\n");
+    htmlBuff.append("</table>\n");
+  }
+
+  private void bullet(String str) {
+    htmlBuff.append(
+      "<li>" +
+      str +
+      "</li>\n");
+  }
+
+  private void heading2(String string) {
+    heading(string, 2);
+  }
+
+  private void heading3(String string) {
+    heading(string, 3);
+  }
+
+  private void heading4(String string) {
+    heading(string, 4);
+  }
+
+  private void heading(String str, int level) {
+    htmlBuff.append(getHeading(str, level));
+  }
+
+  private String getHeading(String str, int level) {
+    String strLevel = (new Integer(level)).toString();
+    return "<h" + strLevel + ">" +
+      "<a name=\"" + str + "\"></a>" +
+      str +
+      "</h" + strLevel + ">\n";
+  }
+
+  private String getHeading2(String str) {
+    return getHeading(str, 2);
+  }
+
+  private String getAnchor(String str) {
+    return "<a name=\"" + str + "\"></a>";
+  }
+
+  private void htmlHeader(String pageTitle) {
+    htmlBuff.append(getHtmlHeader(pageTitle) +
+      "<body>\n");
+
+  }
+
+  private final String Now = new Date().toString();
+  private String getHtmlHeader(String pageTitle) {
+    return ("<html>\n" +
+      "<head>\n" +
+      "<meta http-equiv=\"content-type\"\n" +
+      "content=\"text/html; charset=ISO-8859-1\">\n" +
+      "<title>" + pageTitle + "</title>\n" +
+      "<link rel=\"stylesheet\" type=\"text/css\"\n" +
+      "href=\"" + CSS_FILE + "\">\n" +
+      "<link rel=\"shortcut icon\" href=\"" + FAVICON + "\">\n" +
+      "<meta name=\"date generated\" content=\"" + Now + "\">\n" +
+      "</head>\n");
+  }
+
+  // Add a Tab Menu, the active tab is the one given as parameter
+  private void tabMenu(String activeTab) {
+    htmlBuff.append(
+      "<div class=\"tabmenu\"> " +
+
+      "<span><a " +
+      (activeTab.equals(INHERITANCE_TREE_FILE) ? "class=\"activetab\" " : "") +
+      "href=\"" + INHERITANCE_TREE_FILE + "\"" +
+      " title=\"Inheritance View of Components\">Inheritance</a></span> " +
+
+      "<span><a " +
+      (activeTab.equals(RELATION_TREE_FILE) ? "class=\"activetab\" " : "") +
+      "href=\"" + RELATION_TREE_FILE + "\"" +
+      " title=\"Relational View of Components\">Structure</a></span> " +
+
+      "<span><a " +
+      (activeTab.equals(MO_LIST_FILE) ? "class=\"activetab\" " : "") +
+      "href=\"" + MO_LIST_FILE + "\"" +
+      " title=\"Alphabetical Index of Components\">Components</a></span> " +
+
+      "<span><a " +
+      (activeTab.equals(PROPERTIES_INDEX_FILE) ? "class=\"activetab\" " : "") +
+      "href=\"" + PROPERTIES_INDEX_FILE + "\"" +
+      " title=\"Alphabetical Index of Properties\" >Properties</a></span>" +
+
+      "</div>" +
+      "\n"
+      );
+  }
+
+  private String getLink(String str, String link) {
+    return getLink(str, link, null, null);
+  }
+
+  private String getLink(String str, String link, String target) {
+    return getLink(str, link, target, null);
+  }
+
+  private String getLink(String str, String link, String target, String color) {
+    return "<a " +
+      (color != null ? "style=\"color:" + color + "\" " : "") +
+      "href=\"" + link + "\"" +
+      (target == null ? "" : " target=\"" + target + "\"") +
+      ">"
+      + str + "</a>";
+  }
+
+  private void link(String str, String link) {
+    link(str, link, null, null);
+  }
+
+  private void link(String str, String link, String target) {
+    link(str, link, target, null);
+  }
+
+  private void link(String str, String link, String target, String color) {
+    String htmlStr = "";
+    if (!inList && getIndentPixels() > 0) {
+      htmlStr += "<div style=\"margin-left: " + getIndentPixels() + "px;\">";
+    } else if (inList) {
+      htmlStr += "<li>";
+    }
+    htmlStr += getLink(str, link, target, color);
+    if (!inList && getIndentPixels() > 0) {
+      htmlStr += "</div>";
+    } else if (inList) {
+      htmlStr += "</li>";
+    }
+    if (!inList) {
+      htmlStr += "<br>";
+    }
+    htmlBuff.append(htmlStr + "\n");
+  }
+
+  private void newline() {
+    htmlBuff.append(
+      getNewLine());
+  }
+
+  private String getNewLine() {
+    return "<br>\n";
+  }
+
+  private void paragraph(Message description) {
+    if (description != null) {
+      paragraph(description.toString());
+    }
+  }
+
+  private void paragraph(String description) {
+    paragraph(description, TextStyle.STANDARD, null);
+  }
+
+  private void paragraph(String description, TextStyle style) {
+    paragraph(description, style, null);
+  }
+
+  private void paragraph(String description, TextStyle style, String pClass) {
+    String indentStr = "";
+    String styleStr = "";
+    String classStr = "";
+    if (getIndentPixels() > 0) {
+      indentStr = "style=\"margin-left: " + getIndentPixels() + "px;\"";
+    }
+    if (style == TextStyle.BOLD) {
+      styleStr = "style=\"font-weight: bold;\"";
+    } else if (style == TextStyle.ITALIC) {
+      styleStr = "style=\"font-style: italic;\"";
+    }
+    if (pClass != null) {
+      classStr = "class=" + pClass;
+    }
+
+    htmlBuff.append(
+      "<p " +
+      indentStr + " " +
+      styleStr + " " +
+      classStr +
+      ">" +
+      description +
+      "</p>\n");
+  }
+
+  private int getIndentPixels() {
+    return (ind * 40);
+  }
+
+  private void startTable() {
+    htmlBuff.append(
+      "<table " +
+      "style=\"width: 100%; text-align: left;\"" +
+      "border=\"1\"" +
+      "cellpadding=\"1\"" +
+      "cellspacing=\"0\"" +
+      ">\n");
+
+    htmlBuff.append("<tbody>\n");
+  }
+
+  /*
+   * Generate a "friendly" name from a string :
+   * '-' and '_' replaced by space
+   * first letter of a word in uppercase
+   */
+  private String getFriendlyName(String str) {
+    String retStr = "";
+    String[] words = str.split("\\p{Punct}");
+    for (int ii = 0; ii < words.length; ii++) {
+      if (ii>0) {
+        retStr += " ";
+      }
+      String word = words[ii];
+       String firstChar = word.substring(0, 1).toUpperCase();
+       retStr += firstChar + word.substring(1, word.length());
+    }
+    return retStr;
+  }
+
+  private void tableRow(String... strings) {
+    htmlBuff.append(
+      "<tr>\n");
+    for (int ii = 0; ii < strings.length; ii++) {
+      String string = strings[ii];
+      htmlBuff.append(
+        "<td style=\"" +
+        "vertical-align: top; " +
+        ((ii == 0) ? "width: 20%;" : "") +
+        "\">" +
+        string +
+        "<br></td>");
+    }
+    htmlBuff.append(
+      "</tr>\n");
+  }
+
+  /**
+   * Text style.
+   */
+  private enum TextStyle {
+
+    STANDARD, BOLD, ITALIC, UNDERLINE, FIXED_WIDTH
+  }
+
+  private void beginList() {
+    inList = true;
+    listLevel++;
+    htmlBuff.append(
+      "<ul>\n");
+  }
+
+  private void endList() {
+    listLevel--;
+    if (listLevel == 0) {
+      inList = false;
+    }
+    htmlBuff.append(
+      "</ul>\n");
+  }
+
+  private void htmlFooter() {
+    htmlBuff.append(
+      "</body>\n" +
+      "</html>\n");
+  }
+
+  private void viewHelp(String helpStr) {
+    htmlBuff.append(
+      "<p class=\"view-help\" >" +
+      helpStr +
+      "</p>" +
+      "\n"
+      );
+  }
+
+  private void generateFile(String fileName) {
+    // Write the html buffer in a file
+    try {
+      PrintWriter file = new java.io.PrintWriter(
+        new java.io.FileWriter(generationDir + File.separator + fileName));
+      file.write(htmlBuff.toString());
+      file.close();
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+    // re-init html buffer
+    htmlBuff = new StringBuffer();
+  }
+
+  // Relation List from RootConfiguration
+  private final TreeMap<String, RelationDefinition> topRelList =
+    new TreeMap<String, RelationDefinition>();
+  private final TreeMap<String, RelationDefinition> relList =
+    new TreeMap<String, RelationDefinition>();
+  private final TreeMap<String, TreeMap<String, RelationDefinition>>
+    catTopRelList = new TreeMap<String, TreeMap<String, RelationDefinition>>();
+  // managed object list
+  private final TreeMap<String, AbstractManagedObjectDefinition> moList =
+    new TreeMap<String, AbstractManagedObjectDefinition>();
+  private final TreeMap<String, AbstractManagedObjectDefinition> topMoList =
+    new TreeMap<String, AbstractManagedObjectDefinition>();
+  private final TreeMap<String,
+                        TreeMap<String, AbstractManagedObjectDefinition>>
+    catTopMoList =
+      new TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>();
+  private final int ind = 0;
+  private StringBuffer htmlBuff = new StringBuffer();
+  private static String generationDir;
+  private static boolean ldapMapping = false;
+  private static String OpenDJWiki;
+  private static String OpenDJHome;
+  private static String aciSyntaxPage;
+  private static String durationSyntaxPage;
+  private boolean inList = false;
+  private int listLevel = 0;
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/doc/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/doc/package-info.java
new file mode 100644
index 0000000..0b78245
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/doc/package-info.java
@@ -0,0 +1,38 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * Administration documentation classes.
+ * <p>
+ * This package contains classes used to generate administration documentation.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.doc;
+
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/package-info.java
new file mode 100644
index 0000000..e97e137
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * Common administration classes.
+ * <p>
+ * This package contains administration related classes and interfaces
+ * common to both the client and server.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin;
+
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
new file mode 100644
index 0000000..fc1e3ea
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/AbstractConfigListenerAdaptor.java
@@ -0,0 +1,72 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.Collection;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+
+
+
+/**
+ * Common features of config listener adaptors.
+ */
+abstract class AbstractConfigListenerAdaptor {
+
+  /**
+   * Create a new config listener adaptor.
+   */
+  protected AbstractConfigListenerAdaptor() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Concatenate a list of messages into a single message.
+   *
+   * @param reasons
+   *          The list of messages to concatenate.
+   * @param unacceptableReason
+   *          The single message to which messages should be appended.
+   */
+  protected final void generateUnacceptableReason(Collection<Message> reasons,
+      MessageBuilder unacceptableReason) {
+    boolean isFirst = true;
+    for (Message reason : reasons) {
+      if (isFirst) {
+        isFirst = false;
+      } else {
+        unacceptableReason.append("  ");
+      }
+      unacceptableReason.append(reason);
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
new file mode 100644
index 0000000..9a8c29b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -0,0 +1,281 @@
+/*
+ * 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 2007-2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.DecodingException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.api.ConfigAddListener;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
+
+
+
+/**
+ * An adaptor class which converts {@link ConfigAddListener} callbacks
+ * to {@link ServerManagedObjectAddListener} callbacks.
+ *
+ * @param <S>
+ *          The type of server configuration handled by the add
+ *          listener.
+ */
+final class ConfigAddListenerAdaptor<S extends Configuration> extends
+    AbstractConfigListenerAdaptor implements ConfigAddListener {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // Cached managed object between accept/apply callbacks.
+  private ServerManagedObject<? extends S> cachedManagedObject;
+
+  // The instantiable relation.
+  private final InstantiableRelationDefinition<?, S> instantiableRelation;
+
+  // The set relation.
+  private final SetRelationDefinition<?, S> setRelation;
+
+  // The underlying add listener.
+  private final ServerManagedObjectAddListener<S> listener;
+
+  // The optional relation.
+  private final OptionalRelationDefinition<?, S> optionalRelation;
+
+  // The managed object path of the parent.
+  private final ManagedObjectPath<?, ?> path;
+
+
+
+  /**
+   * Create a new configuration add listener adaptor for an
+   * instantiable relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The instantiable relation.
+   * @param listener
+   *          The underlying add listener.
+   */
+  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
+      InstantiableRelationDefinition<?, S> relation,
+      ServerManagedObjectAddListener<S> listener) {
+    this.path = path;
+    this.instantiableRelation = relation;
+    this.optionalRelation = null;
+    this.setRelation = null;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * Create a new configuration add listener adaptor for an optional
+   * relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The optional relation.
+   * @param listener
+   *          The underlying add listener.
+   */
+  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
+      OptionalRelationDefinition<?, S> relation,
+      ServerManagedObjectAddListener<S> listener) {
+    this.path = path;
+    this.optionalRelation = relation;
+    this.instantiableRelation = null;
+    this.setRelation = null;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * Create a new configuration add listener adaptor for a
+   * set relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The set relation.
+   * @param listener
+   *          The underlying add listener.
+   */
+  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
+      SetRelationDefinition<?, S> relation,
+      ServerManagedObjectAddListener<S> listener) {
+    this.path = path;
+    this.instantiableRelation = null;
+    this.optionalRelation = null;
+    this.setRelation = relation;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) {
+    if (optionalRelation != null) {
+      // Optional managed objects are located directly beneath the
+      // parent and have a well-defined name. We need to make sure
+      // that we are handling the correct entry.
+      ManagedObjectPath<?, ?> childPath = path.child(optionalRelation);
+      DN expectedDN = DNBuilder.create(childPath);
+      if (!configEntry.getDN().equals(expectedDN)) {
+        // Doesn't apply to us.
+        return new ConfigChangeResult(ResultCode.SUCCESS, false);
+      }
+    }
+
+    // Cached objects are guaranteed to be from previous acceptable
+    // callback.
+    ConfigChangeResult result = listener
+        .applyConfigurationAdd(cachedManagedObject);
+
+    // Now apply post constraint call-backs.
+    if (result.getResultCode() == ResultCode.SUCCESS) {
+      ManagedObjectDefinition<?, ?> d = cachedManagedObject
+          .getManagedObjectDefinition();
+      for (Constraint constraint : d.getAllConstraints()) {
+        for (ServerConstraintHandler handler : constraint
+            .getServerConstraintHandlers()) {
+          try {
+            handler.performPostAdd(cachedManagedObject);
+          } catch (ConfigException e) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+          }
+        }
+      }
+    }
+
+    return result;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean configAddIsAcceptable(ConfigEntry configEntry,
+      MessageBuilder unacceptableReason) {
+    DN dn = configEntry.getDN();
+    AttributeValue av = dn.getRDN().getAttributeValue(0);
+    String name = av.getValue().toString().trim();
+
+    try {
+      ManagedObjectPath<?, ? extends S> childPath;
+      if (instantiableRelation != null) {
+        childPath = path.child(instantiableRelation, name);
+      } else if (setRelation != null) {
+        try {
+          childPath = path.child(setRelation, name);
+        } catch (IllegalArgumentException e) {
+          throw new DefinitionDecodingException(setRelation
+              .getChildDefinition(), Reason.WRONG_TYPE_INFORMATION);
+        }
+      } else {
+        // Optional managed objects are located directly beneath the
+        // parent and have a well-defined name. We need to make sure
+        // that we are handling the correct entry.
+        childPath = path.child(optionalRelation);
+        DN expectedDN = DNBuilder.create(childPath);
+        if (!dn.equals(expectedDN)) {
+          // Doesn't apply to us.
+          return true;
+        }
+      }
+
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      cachedManagedObject = context.decode(childPath, configEntry, configEntry);
+    } catch (DecodingException e) {
+      unacceptableReason.append(e.getMessageObject());
+      return false;
+    }
+
+    // Give up immediately if a constraint violation occurs.
+    try {
+      cachedManagedObject.ensureIsUsable();
+    } catch (ConstraintViolationException e) {
+      generateUnacceptableReason(e.getMessages(), unacceptableReason);
+      return false;
+    }
+
+    // Let the add listener decide.
+    List<Message> reasons = new LinkedList<Message>();
+    if (listener.isConfigurationAddAcceptable(cachedManagedObject, reasons)) {
+      return true;
+    } else {
+      generateUnacceptableReason(reasons, unacceptableReason);
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Get the server managed object add listener associated with this
+   * adaptor.
+   *
+   * @return Returns the server managed object add listener associated
+   *         with this adaptor.
+   */
+  ServerManagedObjectAddListener<S> getServerManagedObjectAddListener() {
+    return listener;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
new file mode 100644
index 0000000..467f403
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigChangeListenerAdaptor.java
@@ -0,0 +1,492 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.messages.AdminMessages;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+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.Constraint;
+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.ManagedObjectDefinition;
+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.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.ErrorLogger;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.ConfigChangeResult;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
+import org.opends.server.util.StaticUtils;
+
+
+
+/**
+ * An adaptor class which converts {@link ConfigChangeListener}
+ * call-backs to {@link ServerManagedObjectChangeListener}
+ * call-backs.
+ *
+ * @param <S>
+ *          The type of server configuration handled by the change
+ *          listener.
+ */
+final class ConfigChangeListenerAdaptor<S extends Configuration> extends
+    AbstractConfigListenerAdaptor implements ConfigChangeListener {
+
+  /**
+   * 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 delete listener which is used to remove this listener and any
+  // dependencies.
+  private final ConfigDeleteListener cleanerListener;
+
+  // 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 ConfigChangeListener dependencyListener;
+
+  // The DN associated with this listener.
+  private final DN dn;
+
+  // The underlying change listener.
+  private final ServerManagedObjectChangeListener<? super S> listener;
+
+  // The managed object path.
+  private final ManagedObjectPath<?, S> path;
+
+
+
+  /**
+   * Create a new configuration change listener adaptor.
+   *
+   * @param path
+   *          The managed object path.
+   * @param listener
+   *          The underlying change listener.
+   */
+  public ConfigChangeListenerAdaptor(ManagedObjectPath<?, S> path,
+      ServerManagedObjectChangeListener<? super S> listener) {
+    this.path = path;
+    this.dn = DNBuilder.create(path);
+    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 ConfigChangeListener() {
+
+      public ConfigChangeResult applyConfigurationChange(
+          ConfigEntry configEntry) {
+        ConfigEntry dependentConfigEntry = getConfigEntry(dn);
+        if (dependentConfigEntry != null) {
+          return ConfigChangeListenerAdaptor.this
+              .applyConfigurationChange(dependentConfigEntry);
+        } else {
+          // The dependent entry was not found.
+          configEntry.deregisterChangeListener(this);
+          return new ConfigChangeResult(ResultCode.SUCCESS, false);
+        }
+      }
+
+
+
+      public boolean configChangeIsAcceptable(ConfigEntry configEntry,
+          MessageBuilder unacceptableReason) {
+        ConfigEntry dependentConfigEntry = getConfigEntry(dn);
+        if (dependentConfigEntry != null) {
+          return ConfigChangeListenerAdaptor.this.configChangeIsAcceptable(
+              dependentConfigEntry, unacceptableReason, configEntry);
+        } else {
+          // The dependent entry was not found.
+          configEntry.deregisterChangeListener(this);
+          return true;
+        }
+      }
+
+    };
+
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      Visitor.find(path, pd, dependencies);
+    }
+
+    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);
+        }
+      }
+    }
+
+    // Register a delete listener against the parent which will
+    // finalize this change listener when the monitored configuration
+    // entry is removed.
+    this.cleanerListener = new ConfigDeleteListener() {
+
+      public ConfigChangeResult applyConfigurationDelete(
+          ConfigEntry configEntry) {
+        // Perform finalization if the deleted entry is the monitored
+        // entry.
+        if (configEntry.getDN().equals(dn)) {
+          finalizeChangeListener();
+        }
+        return new ConfigChangeResult(ResultCode.SUCCESS, false);
+      }
+
+
+
+      public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
+          MessageBuilder unacceptableReason) {
+        // Always acceptable.
+        return true;
+      }
+
+    };
+
+    DN parent = dn.getParent();
+    if (parent != null) {
+      ConfigEntry configEntry = getConfigEntry(dn.getParent());
+      if (configEntry != null) {
+        configEntry.registerDeleteListener(cleanerListener);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) {
+    // Looking at the ConfigFileHandler implementation reveals
+    // that this ConfigEntry will actually be a different object to
+    // 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);
+
+    ConfigChangeResult result = listener
+        .applyConfigurationChange(cachedManagedObject);
+
+    // Now apply post constraint call-backs.
+    if (result.getResultCode() == ResultCode.SUCCESS) {
+      ManagedObjectDefinition<?, ?> d = cachedManagedObject
+          .getManagedObjectDefinition();
+      for (Constraint constraint : d.getAllConstraints()) {
+        for (ServerConstraintHandler handler : constraint
+            .getServerConstraintHandlers()) {
+          try {
+            handler.performPostModify(cachedManagedObject);
+          } catch (ConfigException e) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+          }
+        }
+      }
+    }
+
+    return result;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean configChangeIsAcceptable(ConfigEntry configEntry,
+      MessageBuilder 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,
+      MessageBuilder unacceptableReason, ConfigEntry newConfigEntry) {
+    try {
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      cachedManagedObject = context.decode(path, configEntry, newConfigEntry);
+    } catch (DecodingException e) {
+      unacceptableReason.append(e.getMessageObject());
+      return false;
+    }
+
+    // Give up immediately if a constraint violation occurs.
+    try {
+      cachedManagedObject.ensureIsUsable();
+    } catch (ConstraintViolationException e) {
+      generateUnacceptableReason(e.getMessages(), unacceptableReason);
+      return false;
+    }
+
+    // Let the change listener decide.
+    List<Message> reasons = new LinkedList<Message>();
+    if (listener.isConfigurationChangeAcceptable(cachedManagedObject,reasons)) {
+      return true;
+    } else {
+      generateUnacceptableReason(reasons, unacceptableReason);
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Finalizes this configuration change listener adaptor. This method
+   * must be called before this change listener is removed.
+   */
+  public void finalizeChangeListener() {
+    // Remove the dependency listeners.
+    for (DN dependency : dependencies) {
+      ConfigEntry listenerConfigEntry = getConfigEntry(dependency);
+      if (listenerConfigEntry != null) {
+        listenerConfigEntry.deregisterChangeListener(dependencyListener);
+      }
+    }
+
+    // Now remove the cleaner listener as it will no longer be
+    // needed.
+    ConfigEntry parentConfigEntry = getConfigEntry(dn.getParent());
+    if (parentConfigEntry != null) {
+      parentConfigEntry.deregisterDeleteListener(cleanerListener);
+    }
+
+  }
+
+
+
+  /**
+   * Get the server managed object change listener associated with
+   * this adaptor.
+   *
+   * @return Returns the server managed object change listener
+   *         associated with this adaptor.
+   */
+  ServerManagedObjectChangeListener<? super S>
+  getServerManagedObjectChangeListener() {
+    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 {
+        Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
+            .get(String.valueOf(dn));
+        ErrorLogger.logError(message);
+      }
+    } catch (ConfigException e) {
+      // The dependent entry could not be retrieved.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
+          String.valueOf(dn), StaticUtils.getExceptionMessage(e));
+      ErrorLogger.logError(message);
+    }
+
+    return null;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
new file mode 100644
index 0000000..10b8246
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -0,0 +1,302 @@
+/*
+ * 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 2007-2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.DecodingException;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.api.ConfigDeleteListener;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConfigChangeResult;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
+
+
+
+/**
+ * An adaptor class which converts {@link ConfigDeleteListener}
+ * callbacks to {@link ServerManagedObjectDeleteListener} callbacks.
+ *
+ * @param <S>
+ *          The type of server configuration handled by the delete
+ *          listener.
+ */
+final class ConfigDeleteListenerAdaptor<S extends Configuration> extends
+    AbstractConfigListenerAdaptor implements ConfigDeleteListener {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // Cached managed object between accept/apply callbacks.
+  private ServerManagedObject<? extends S> cachedManagedObject;
+
+  // The instantiable relation.
+  private final InstantiableRelationDefinition<?, S> instantiableRelation;
+
+  // The set relation.
+  private final SetRelationDefinition<?, S> setRelation;
+
+  // The underlying delete listener.
+  private final ServerManagedObjectDeleteListener<S> listener;
+
+  // The optional relation.
+  private final OptionalRelationDefinition<?, S> optionalRelation;
+
+  // The managed object path of the parent.
+  private final ManagedObjectPath<?, ?> path;
+
+
+
+  /**
+   * Create a new configuration delete listener adaptor for an
+   * instantiable relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The instantiable relation.
+   * @param listener
+   *          The underlying delete listener.
+   */
+  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
+      InstantiableRelationDefinition<?, S> relation,
+      ServerManagedObjectDeleteListener<S> listener) {
+    this.path = path;
+    this.optionalRelation = null;
+    this.instantiableRelation = relation;
+    this.setRelation = null;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * Create a new configuration delete listener adaptor for an
+   * optional relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The optional relation.
+   * @param listener
+   *          The underlying delete listener.
+   */
+  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
+      OptionalRelationDefinition<?, S> relation,
+      ServerManagedObjectDeleteListener<S> listener) {
+    this.path = path;
+    this.optionalRelation = relation;
+    this.instantiableRelation = null;
+    this.setRelation = null;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * Create a new configuration delete listener adaptor for an
+   * set relation.
+   *
+   * @param path
+   *          The managed object path of the parent.
+   * @param relation
+   *          The set relation.
+   * @param listener
+   *          The underlying delete listener.
+   */
+  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
+      SetRelationDefinition<?, S> relation,
+      ServerManagedObjectDeleteListener<S> listener) {
+    this.path = path;
+    this.optionalRelation = null;
+    this.instantiableRelation = null;
+    this.setRelation = relation;
+    this.listener = listener;
+    this.cachedManagedObject = null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) {
+    if (optionalRelation != null) {
+      // Optional managed objects are located directly beneath the
+      // parent and have a well-defined name. We need to make sure
+      // that we are handling the correct entry.
+      ManagedObjectPath<?, ?> childPath = path.child(optionalRelation);
+      DN expectedDN = DNBuilder.create(childPath);
+      if (!configEntry.getDN().equals(expectedDN)) {
+        // Doesn't apply to us.
+        return new ConfigChangeResult(ResultCode.SUCCESS, false);
+      }
+    }
+
+    // Cached objects are guaranteed to be from previous acceptable
+    // callback.
+    ConfigChangeResult result = listener
+        .applyConfigurationDelete(cachedManagedObject);
+
+    // Now apply post constraint call-backs.
+    if (result.getResultCode() == ResultCode.SUCCESS) {
+      ManagedObjectDefinition<?, ?> d = cachedManagedObject
+          .getManagedObjectDefinition();
+      for (Constraint constraint : d.getAllConstraints()) {
+        for (ServerConstraintHandler handler : constraint
+            .getServerConstraintHandlers()) {
+          try {
+            handler.performPostDelete(cachedManagedObject);
+          } catch (ConfigException e) {
+            if (debugEnabled()) {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+          }
+        }
+      }
+    }
+
+    return result;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean configDeleteIsAcceptable(ConfigEntry configEntry,
+      MessageBuilder unacceptableReason) {
+    DN dn = configEntry.getDN();
+    AttributeValue av = dn.getRDN().getAttributeValue(0);
+    String name = av.getValue().toString().trim();
+
+    try {
+      ManagedObjectPath<?, ? extends S> childPath;
+      if (instantiableRelation != null) {
+        childPath = path.child(instantiableRelation, name);
+      } else if (setRelation != null) {
+        try {
+          childPath = path.child(setRelation, name);
+        } catch (IllegalArgumentException e) {
+          throw new DefinitionDecodingException(setRelation
+              .getChildDefinition(), Reason.WRONG_TYPE_INFORMATION);
+        }
+      } else {
+        // Optional managed objects are located directly beneath the
+        // parent and have a well-defined name. We need to make sure
+        // that we are handling the correct entry.
+        childPath = path.child(optionalRelation);
+        DN expectedDN = DNBuilder.create(childPath);
+        if (!dn.equals(expectedDN)) {
+          // Doesn't apply to us.
+          return true;
+        }
+      }
+
+      ServerManagementContext context = ServerManagementContext.getInstance();
+      cachedManagedObject = context.decode(childPath, configEntry);
+    } catch (DecodingException e) {
+      unacceptableReason.append(e.getMessageObject());
+      return false;
+    }
+
+    List<Message> reasons = new LinkedList<Message>();
+
+    // Enforce any constraints.
+    boolean isDeleteAllowed = true;
+    ManagedObjectDefinition<?, ?> d = cachedManagedObject
+        .getManagedObjectDefinition();
+    for (Constraint constraint : d.getAllConstraints()) {
+      for (ServerConstraintHandler handler : constraint
+          .getServerConstraintHandlers()) {
+        try {
+          if (!handler.isDeleteAllowed(cachedManagedObject, reasons)) {
+            isDeleteAllowed = false;
+          }
+        } catch (ConfigException e) {
+          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
+              .getMessageObject());
+          reasons.add(message);
+          isDeleteAllowed = false;
+        }
+      }
+    }
+
+    // Give up immediately if a constraint violation occurs.
+    if (!isDeleteAllowed) {
+      generateUnacceptableReason(reasons, unacceptableReason);
+      return false;
+    }
+
+    // Let the delete listener decide.
+    if (listener.isConfigurationDeleteAcceptable(cachedManagedObject,
+        reasons)) {
+      return true;
+    } else {
+      generateUnacceptableReason(reasons, unacceptableReason);
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Get the server managed object delete listener associated with
+   * this adaptor.
+   *
+   * @return Returns the server managed object delete listener
+   *         associated with this adaptor.
+   */
+  ServerManagedObjectDeleteListener<S> getServerManagedObjectDeleteListener() {
+    return listener;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java
new file mode 100644
index 0000000..4e71cd9
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigExceptionFactory.java
@@ -0,0 +1,148 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+import org.opends.messages.Message;
+
+
+
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.config.ConfigException;
+import org.opends.messages.AdminMessages;
+import org.forgerock.opendj.ldap.DN;
+
+
+
+/**
+ * A utility class for converting admin exceptions to config exceptions.
+ */
+final class ConfigExceptionFactory {
+
+  // The singleton instance.
+  private static final ConfigExceptionFactory INSTANCE =
+    new ConfigExceptionFactory();
+
+
+
+  // Prevent instantiation.
+  private ConfigExceptionFactory() {
+    // Do nothing.
+  }
+
+
+
+  /**
+   * Get the configuration exception factory instance.
+   *
+   * @return Returns the configuration exception factory instance.
+   */
+  public static ConfigExceptionFactory getInstance() {
+    return INSTANCE;
+  }
+
+
+
+  /**
+   * Create a configuration exception from a definition decoding exception.
+   *
+   * @param dn
+   *          The dn of the configuration entry that could not be decoded.
+   * @param e
+   *          The definition decoding exception
+   * @return Returns the configuration exception.
+   */
+  public ConfigException createDecodingExceptionAdaptor(DN dn,
+      DefinitionDecodingException e) {
+    Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.
+        get(String.valueOf(dn), stackTraceToSingleLineString(e));
+    return new ConfigException(message, e);
+  }
+
+
+
+  /**
+   * Create a configuration exception from a server managed object decoding
+   * exception.
+   *
+   * @param e
+   *          The server managed object decoding exception.
+   * @return Returns the configuration exception.
+   */
+
+  public ConfigException createDecodingExceptionAdaptor(
+      ServerManagedObjectDecodingException e) {
+    DN dn = e.getPartialManagedObject().getDN();
+    Message message =
+            AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM.get(
+                    String.valueOf(dn),
+        stackTraceToSingleLineString(e));
+    return new ConfigException(message, e);
+  }
+
+
+
+  /**
+   * Create a configuration exception from a constraints violation
+   * decoding exception.
+   *
+   * @param e
+   *          The constraints violation decoding exception.
+   * @return Returns the configuration exception.
+   */
+  public ConfigException createDecodingExceptionAdaptor(
+      ConstraintViolationException e) {
+    DN dn = e.getManagedObject().getDN();
+    Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DECODING_PROBLEM
+        .get(String.valueOf(dn), stackTraceToSingleLineString(e));
+    return new ConfigException(message, e);
+  }
+
+
+
+  /**
+   * Create an exception that describes a problem that occurred when
+   * attempting to load and instantiate a class.
+   *
+   * @param dn
+   *          The dn of the configuration entry was being processed.
+   * @param className
+   *          The name of the class that could not be loaded or
+   *          instantiated.
+   * @param e
+   *          The exception that occurred.
+   * @return Returns the configuration exception.
+   */
+
+  public ConfigException createClassLoadingExceptionAdaptor(DN dn,
+      String className, Exception e) {
+    Message message = AdminMessages.ERR_ADMIN_CANNOT_INSTANTIATE_CLASS.
+        get(String.valueOf(className), String.valueOf(dn),
+            stackTraceToSingleLineString(e));
+    return new ConfigException(message, e);
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationAddListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationAddListener.java
new file mode 100644
index 0000000..b38ebe5
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationAddListener.java
@@ -0,0 +1,76 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+import org.opends.messages.Message;
+
+
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when a new configuration is added.
+ *
+ * @param <T>
+ *          The type of configuration that this listener should be
+ *          notified about.
+ */
+public interface ConfigurationAddListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed addition of a new configuration is
+   * acceptable to this add listener.
+   *
+   * @param configuration
+   *          The configuration that will be added.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided configuration is not acceptable.
+   * @return Returns <code>true</code> if the proposed addition is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationAddAcceptable(T configuration,
+      List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Adds a new configuration to this add listener.
+   *
+   * @param configuration
+   *          The configuration that will be added.
+   * @return Returns information about the result of adding the
+   *         configuration.
+   */
+  public ConfigChangeResult applyConfigurationAdd(T configuration);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java
new file mode 100644
index 0000000..b2c7abe
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationChangeListener.java
@@ -0,0 +1,77 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+import org.opends.messages.Message;
+
+
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when a its associated configuration is
+ * changed.
+ *
+ * @param <T>
+ *          The type of configuration that this listener should be
+ *          notified about.
+ */
+public interface ConfigurationChangeListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed change to the configuration is
+   * acceptable to this change listener.
+   *
+   * @param configuration
+   *          The new configuration containing the changes.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided configuration is not acceptable.
+   * @return Returns <code>true</code> if the proposed change is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationChangeAcceptable(T configuration,
+      List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Applies the configuration changes to this change listener.
+   *
+   * @param configuration
+   *          The new configuration containing the changes.
+   * @return Returns information about the result of changing the
+   *         configuration.
+   */
+  public ConfigChangeResult applyConfigurationChange(T configuration);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java
new file mode 100644
index 0000000..aaa4b3f
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConfigurationDeleteListener.java
@@ -0,0 +1,76 @@
+/*
+ * 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 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+import org.opends.messages.Message;
+
+
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when an existing configuration is deleted.
+ *
+ * @param <T>
+ *          The type of configuration that this listener should be
+ *          notified about.
+ */
+public interface ConfigurationDeleteListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed deletion of an existing
+   * configuration is acceptable to this delete listener.
+   *
+   * @param configuration
+   *          The configuration that will be deleted.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided configuration is not acceptable.
+   * @return Returns <code>true</code> if the proposed deletion is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationDeleteAcceptable(T configuration,
+      List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Deletes an existing configuration from this delete listener.
+   *
+   * @param configuration
+   *          The existing configuration that will be deleted.
+   * @return Returns information about the result of deleting the
+   *         configuration.
+   */
+  public ConfigChangeResult applyConfigurationDelete(T configuration);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java
new file mode 100644
index 0000000..e630cd5
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ConstraintViolationException.java
@@ -0,0 +1,179 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.DecodingException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * This exception is thrown when the server refuses to use or delete a
+ * managed object due to one or more constraints that cannot be
+ * satisfied.
+ */
+public class ConstraintViolationException extends DecodingException {
+
+  /**
+   * Serialization ID.
+   */
+  private static final long serialVersionUID = -4902443848460011875L;
+
+  // The server managed object.
+  private final ServerManagedObject<?> managedObject;
+
+
+
+  // Gets the default message.
+  private static Message getDefaultMessage(Collection<Message> messages) {
+    Validator.ensureNotNull(messages);
+    Validator.ensureTrue(!messages.isEmpty());
+
+    if (messages.size() == 1) {
+      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_SINGLE.get(messages.iterator()
+          .next());
+    } else {
+      return ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL
+          .get(getSingleMessage(messages));
+    }
+  }
+
+
+
+  // Merge the messages into a single message.
+  private static Message getSingleMessage(Collection<Message> messages) {
+    if (messages.size() == 1) {
+      return messages.iterator().next();
+    } else {
+      MessageBuilder builder = new MessageBuilder();
+
+      boolean isFirst = true;
+      for (Message m : messages) {
+        if (!isFirst) {
+          builder.append(";  ");
+        }
+        builder.append(m);
+        isFirst = false;
+      }
+
+      return builder.toMessage();
+    }
+  }
+
+  // The messages describing the constraint violations that occurred.
+  private final Collection<Message> messages;
+
+
+
+  /**
+   * Creates a new constraint violation exception with the provided
+   * messages.
+   *
+   * @param managedObject
+   *          The server managed object which caused the constraint
+   *          violations.
+   * @param messages
+   *          The messages describing the constraint violations that
+   *          occurred (must be non-<code>null</code> and
+   *          non-empty).
+   */
+  public ConstraintViolationException(ServerManagedObject<?> managedObject,
+      Collection<Message> messages) {
+    super(getDefaultMessage(messages));
+
+    this.managedObject = managedObject;
+    this.messages = new ArrayList<Message>(messages);
+  }
+
+
+
+  /**
+   * Creates a new constraint violation exception with the provided
+   * message.
+   *
+   * @param managedObject
+   *          The server managed object which caused the constraint
+   *          violations.
+   * @param message
+   *          The message describing the constraint violation that
+   *          occurred.
+   */
+  public ConstraintViolationException(ServerManagedObject<?> managedObject,
+      Message message) {
+    this(managedObject, Collections.singleton(message));
+  }
+
+
+
+  /**
+   * Gets an unmodifiable collection view of the messages describing
+   * the constraint violations that occurred.
+   *
+   * @return Returns an unmodifiable collection view of the messages
+   *         describing the constraint violations that occurred.
+   */
+  public Collection<Message> getMessages() {
+    return Collections.unmodifiableCollection(messages);
+  }
+
+
+
+  /**
+   * Creates a single message listing all the messages combined into a
+   * single list separated by semi-colons.
+   *
+   * @return Returns a single message listing all the messages
+   *         combined into a single list separated by semi-colons.
+   */
+  public Message getMessagesAsSingleMessage() {
+    return getSingleMessage(messages);
+  }
+
+
+
+  /**
+   * Gets the server managed object which caused the constraint
+   * violations.
+   *
+   * @return Returns the server managed object which caused the
+   *         constraint violations.
+   */
+  public ServerManagedObject<?> getManagedObject() {
+    return managedObject;
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java b/opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java
new file mode 100644
index 0000000..b3c25c6
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/DNBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.server;
+
+
+
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.types.DirectoryException;
+
+
+
+/**
+ * A factory class for creating <code>DN</code>s from managed
+ * object paths.
+ */
+final class DNBuilder {
+
+  /**
+   * Creates a new DN representing the specified managed object path.
+   *
+   * @param path
+   *          The managed object path.
+   * @return Returns a new DN representing the specified managed
+   *         object path.
+   */
+  public static DN create(ManagedObjectPath<?, ?> path) {
+    return path.toDN();
+  }
+
+
+
+  /**
+   * Creates a new DN representing the specified managed object path
+   * and relation.
+   *
+   * @param path
+   *          The managed object path.
+   * @param relation
+   *          The child relation.
+   * @return Returns a new DN representing the specified managed
+   *         object path and relation.
+   */
+  public static DN create(ManagedObjectPath<?, ?> path,
+      RelationDefinition<?, ?> relation) {
+    DN dn = path.toDN();
+
+    try {
+      LDAPProfile profile = LDAPProfile.getInstance();
+      DN localName = DN.decode(profile.getRelationRDNSequence(relation));
+      return dn.concat(localName);
+    } catch (DirectoryException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+
+
+  // Prevent instantiation.
+  private DNBuilder() {
+    // No implementation required.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java
new file mode 100644
index 0000000..5bedaec
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/DelayedConfigAddListener.java
@@ -0,0 +1,191 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import org.opends.server.loggers.debug.DebugTracer;
+
+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.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.ResultCode;
+import org.opends.messages.MessageBuilder;
+
+
+/**
+ * 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 tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // 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 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 child, ConfigAddListener addListener) {
+    this.parent = child.getParent();
+    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 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 child,
+      ConfigDeleteListener deleteListener) {
+    this.parent = child.getParent();
+    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()) {
+          TRACER.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,
+      MessageBuilder unacceptableReason) {
+    // Always acceptable.
+    return true;
+  }
+
+
+
+  /**
+   * Gets the delayed add listener.
+   * <p>
+   * This method is provided for unit-testing.
+   *
+   * @return Returns the delayed add listener, or <code>null</code>
+   *         if this listener is delaying a delete listener.
+   */
+  ConfigAddListener getDelayedAddListener() {
+    return delayedAddListener;
+  }
+
+
+
+  /**
+   * Gets the delayed delete listener.
+   * <p>
+   * This method is provided for unit-testing.
+   *
+   * @return Returns the delayed delete listener, or <code>null</code>
+   *         if this listener is delaying a add listener.
+   */
+  ConfigDeleteListener getDelayedDeleteListener() {
+    return delayedDeleteListener;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java
new file mode 100644
index 0000000..944a17b
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerConstraintHandler.java
@@ -0,0 +1,193 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.Collection;
+
+import org.opends.messages.Message;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * An interface for performing server-side constraint validation.
+ * <p>
+ * Constraints are evaluated immediately before and after write
+ * operations are performed. Server-side constraints are evaluated in
+ * two phases: the first phase determines if the proposed add, delete,
+ * or modification is acceptable according to the constraint. If one
+ * or more constraints fails, the write write operation is refused,
+ * and the client will receive an
+ * <code>OperationRejectedException</code> exception. The second
+ * phase is invoked once the add, delete, or modification request has
+ * been allowed and any changes applied. The second phase gives the
+ * constraint handler a chance to register listener call-backs if
+ * required.
+ * <p>
+ * A server constraint handler must override at least one of the
+ * provided methods.
+ *
+ * @see org.opends.server.admin.Constraint
+ */
+public abstract class ServerConstraintHandler {
+
+  /**
+   * Creates a new server constraint handler.
+   */
+  protected ServerConstraintHandler() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Determines whether or not the existing managed object can be
+   * deleted from the server's configuration. For example, an
+   * implementation might enforce referential integrity by preventing
+   * referenced managed objects from being deleted.
+   * <p>
+   * If the constraint is not satisfied, the implementation must
+   * return <code>false</code> and add a message describing why the
+   * managed object cannot be deleted.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param managedObject
+   *          The managed object which is about to be deleted.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this constraint is
+   *         satisfied, or <code>false</code> if it is not and the
+   *         managed object cannot be deleted.
+   * @throws ConfigException
+   *           If an configuration exception prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isDeleteAllowed(ServerManagedObject<?> managedObject,
+      Collection<Message> unacceptableReasons) throws ConfigException {
+    return true;
+  }
+
+
+
+  /**
+   * Determines whether or not the provided managed object can be used
+   * by the server. This method is invoked each time a managed object
+   * is decoded by the administration framework: when an attempt is
+   * made to add a new configuration, modify an existing
+   * configuration, or during server initialization. If the constraint
+   * is not satisfied the managed object will be rejected.
+   * <p>
+   * If the constraint is not satisfied, the implementation must
+   * return <code>false</code> and add a message describing why the
+   * managed object is not usable.
+   * <p>
+   * The default implementation is to return <code>true</code>.
+   *
+   * @param managedObject
+   *          The new managed object.
+   * @param unacceptableReasons
+   *          A list of messages to which error messages should be
+   *          added.
+   * @return Returns <code>true</code> if this constraint is
+   *         satisfied, or <code>false</code> if it is not and the
+   *         managed object cannot be used.
+   * @throws ConfigException
+   *           If an configuration exception prevented this constraint
+   *           from being evaluated.
+   */
+  public boolean isUsable(ServerManagedObject<?> managedObject,
+      Collection<Message> unacceptableReasons) throws ConfigException {
+    return true;
+  }
+
+
+
+  /**
+   * Performs any post-add processing required by this constraint.
+   * This method is invoked after a new managed object has been
+   * accepted for use by the administration framework. This might
+   * occur during initialization or when a managed object is added at
+   * run-time.
+   * <p>
+   * The default implementation is to do nothing.
+   *
+   * @param managedObject
+   *          The managed object which has just been added to the
+   *          server's configuration.
+   * @throws ConfigException
+   *           If the post-add processing fails due to a configuration
+   *           exception.
+   */
+  public void performPostAdd(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    // Do nothing.
+  }
+
+
+
+  /**
+   * Performs any post-delete processing required by this constraint.
+   * This method is invoked after a managed object has been accepted
+   * for deletion from the server's configuration.
+   * <p>
+   * The default implementation is to do nothing.
+   *
+   * @param managedObject
+   *          The managed object which was deleted.
+   * @throws ConfigException
+   *           If the post-delete processing fails due to a
+   *           configuration exception.
+   */
+  public void performPostDelete(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    // Do nothing.
+  }
+
+
+
+  /**
+   * Performs any post-modify processing required by this constraint.
+   * This method is invoked after changes to an existing managed
+   * object have been accepted.
+   * <p>
+   * The default implementation is to do nothing.
+   *
+   * @param managedObject
+   *          The managed object which was modified.
+   * @throws ConfigException
+   *           If the post-modify processing fails due to a
+   *           configuration exception.
+   */
+  public void performPostModify(ServerManagedObject<?> managedObject)
+      throws ConfigException {
+    // Do nothing.
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
new file mode 100644
index 0000000..7ad7b9c
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObject.java
@@ -0,0 +1,1692 @@
+/*
+ * 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 2006-2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.server;
+
+
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.Constraint;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.OptionalRelationDefinition;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyProvider;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.SingletonRelationDefinition;
+import org.opends.server.api.ConfigAddListener;
+import org.opends.server.api.ConfigChangeListener;
+import org.opends.server.api.ConfigDeleteListener;
+import org.opends.server.config.ConfigException;
+
+
+
+/**
+ * A server-side managed object.
+ *
+ * @param <S>
+ *          The type of server configuration represented by the server
+ *          managed object.
+ */
+public final class ServerManagedObject<S extends Configuration> implements
+    PropertyProvider {
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // The configuration entry associated with this server managed
+  // object (null if root).
+  private ConfigEntry configEntry;
+
+  // The management context.
+  private final ServerManagementContext context = ServerManagementContext
+      .getInstance();
+
+  // The managed object's definition.
+  private final ManagedObjectDefinition<?, S> definition;
+
+  // The managed object path identifying this managed object's
+  // location.
+  private final ManagedObjectPath<?, S> path;
+
+  // The managed object's properties.
+  private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
+
+
+
+  /**
+   * Creates an new server side managed object.
+   *
+   * @param path
+   *          The managed object path.
+   * @param d
+   *          The managed object definition.
+   * @param properties
+   *          The managed object's properties.
+   * @param configEntry
+   *          The configuration entry associated with the managed
+   *          object.
+   */
+  ServerManagedObject(ManagedObjectPath<?, S> path,
+      ManagedObjectDefinition<?, S> d,
+      Map<PropertyDefinition<?>, SortedSet<?>> properties,
+      ConfigEntry configEntry) {
+    this.definition = d;
+    this.path = path;
+    this.properties = properties;
+    this.configEntry = configEntry;
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      InstantiableRelationDefinition<?, M> d,
+      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      InstantiableRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      OptionalRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      SetRelationDefinition<?, M> d,
+      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object add listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterAddListener(
+      SetRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterAddListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration change listener.
+   *
+   * @param listener
+   *          The configuration change listener.
+   */
+  public void deregisterChangeListener(
+      ConfigurationChangeListener<? super S> listener) {
+    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
+      if (l instanceof ConfigChangeListenerAdaptor) {
+        ConfigChangeListenerAdaptor<?> adaptor =
+          (ConfigChangeListenerAdaptor<?>) l;
+        ServerManagedObjectChangeListener<?> l2 = adaptor
+            .getServerManagedObjectChangeListener();
+        if (l2 instanceof ServerManagedObjectChangeListenerAdaptor<?>) {
+          ServerManagedObjectChangeListenerAdaptor<?> adaptor2 =
+            (ServerManagedObjectChangeListenerAdaptor<?>) l2;
+          if (adaptor2.getConfigurationChangeListener() == listener) {
+            adaptor.finalizeChangeListener();
+            configEntry.deregisterChangeListener(adaptor);
+          }
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object change listener.
+   *
+   * @param listener
+   *          The server managed object change listener.
+   */
+  public void deregisterChangeListener(
+      ServerManagedObjectChangeListener<? super S> listener) {
+    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
+      if (l instanceof ConfigChangeListenerAdaptor) {
+        ConfigChangeListenerAdaptor<?> adaptor =
+          (ConfigChangeListenerAdaptor<?>) l;
+        if (adaptor.getServerManagedObjectChangeListener() == listener) {
+          adaptor.finalizeChangeListener();
+          configEntry.deregisterChangeListener(adaptor);
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      InstantiableRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The server managed object delete listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      InstantiableRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      OptionalRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The server managed object delete listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      OptionalRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing configuration delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      SetRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Deregisters an existing server managed object delete listener.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The server managed object delete listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   */
+  public <M extends Configuration> void deregisterDeleteListener(
+      SetRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+
+    DN baseDN = DNBuilder.create(path, d);
+    deregisterDeleteListener(baseDN, listener);
+  }
+
+
+
+  /**
+   * Retrieve an instantiable child managed object.
+   *
+   * @param <M>
+   *          The requested type of the child server managed object
+   *          configuration.
+   * @param d
+   *          The instantiable relation definition.
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns the instantiable child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConfigException
+   *           If the child managed object could not be found or if it
+   *           could not be decoded.
+   */
+  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
+      InstantiableRelationDefinition<?, M> d, String name)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    return context.getManagedObject(path.child(d, name));
+  }
+
+
+
+  /**
+   * Retrieve an optional child managed object.
+   *
+   * @param <M>
+   *          The requested type of the child server managed object
+   *          configuration.
+   * @param d
+   *          The optional relation definition.
+   * @return Returns the optional child managed object.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   * @throws ConfigException
+   *           If the child managed object could not be found or if it
+   *           could not be decoded.
+   */
+  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
+      OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
+      ConfigException {
+    validateRelationDefinition(d);
+    return context.getManagedObject(path.child(d));
+  }
+
+
+
+  /**
+   * Retrieve a set child managed object.
+   *
+   * @param <M>
+   *          The requested type of the child server managed object
+   *          configuration.
+   * @param d
+   *          The set relation definition.
+   * @param name
+   *          The name of the child managed object.
+   * @return Returns the set child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition or if {@code name} specifies
+   *           a managed object definition which is not a sub-type of
+   *           the relation's child definition.
+   * @throws ConfigException
+   *           If the child managed object could not be found or if it
+   *           could not be decoded.
+   */
+  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
+      SetRelationDefinition<?, M> d, String name)
+      throws IllegalArgumentException, ConfigException
+  {
+    validateRelationDefinition(d);
+
+    return context.getManagedObject(path.child(d, name));
+  }
+
+
+
+  /**
+   * Retrieve a singleton child managed object.
+   *
+   * @param <M>
+   *          The requested type of the child server managed object
+   *          configuration.
+   * @param d
+   *          The singleton relation definition.
+   * @return Returns the singleton child managed object.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   * @throws ConfigException
+   *           If the child managed object could not be found or if it
+   *           could not be decoded.
+   */
+  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
+      SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
+      ConfigException {
+    validateRelationDefinition(d);
+    return context.getManagedObject(path.child(d));
+  }
+
+
+
+  /**
+   * Creates a server configuration view of this managed object.
+   *
+   * @return Returns the server configuration view of this managed
+   *         object.
+   */
+  public S getConfiguration() {
+    return definition.createServerConfiguration(this);
+  }
+
+
+
+  /**
+   * Get the DN of the LDAP entry associated with this server managed
+   * object.
+   *
+   * @return Returns the DN of the LDAP entry associated with this
+   *         server managed object, or an null DN if this is the root
+   *         managed object.
+   */
+  public DN getDN() {
+    if (configEntry != null) {
+      return configEntry.getDN();
+    } else {
+      return DN.nullDN();
+    }
+  }
+
+
+
+  /**
+   * Get the definition associated with this server managed object.
+   *
+   * @return Returns the definition associated with this server
+   *         managed object.
+   */
+  public ManagedObjectDefinition<?, S> getManagedObjectDefinition() {
+    return definition;
+  }
+
+
+
+  /**
+   * Get the path of this server managed object.
+   *
+   * @return Returns the path of this server managed object.
+   */
+  public ManagedObjectPath<?, S> getManagedObjectPath() {
+    return path;
+  }
+
+
+
+  /**
+   * Get the effective value of the specified property. If the
+   * property is multi-valued then just the first value is returned.
+   * If the property does not have a value then its default value is
+   * returned if it has one, or <code>null</code> indicating that
+   * any default behavior is applicable.
+   *
+   * @param <T>
+   *          The type of the property to be retrieved.
+   * @param d
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> indicating that any default behavior
+   *         is applicable.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  public <T> T getPropertyValue(PropertyDefinition<T> d)
+      throws IllegalArgumentException {
+    Set<T> values = getPropertyValues(d);
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.iterator().next();
+    }
+  }
+
+
+
+  /**
+   * Get the effective values of the specified property. If the
+   * property does not have any values then its default values are
+   * returned if it has any, or an empty set indicating that any
+   * default behavior is applicable.
+   *
+   * @param <T>
+   *          The type of the property to be retrieved.
+   * @param d
+   *          The property to be retrieved.
+   * @return Returns an unmodifiable set containing the property's
+   *         effective values. An empty set indicates that the
+   *         property has no default values defined and any default
+   *         behavior is applicable.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with this
+   *           managed object's definition.
+   */
+  @SuppressWarnings("unchecked")
+  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
+      throws IllegalArgumentException {
+    if (!properties.containsKey(d)) {
+      throw new IllegalArgumentException("Unknown property " + d.getName());
+    }
+    return Collections.unmodifiableSortedSet((SortedSet<T>) properties.get(d));
+  }
+
+
+
+  /**
+   * Determines whether or not the optional managed object associated
+   * with the specified optional relations exists.
+   *
+   * @param d
+   *          The optional relation definition.
+   * @return Returns <code>true</code> if the optional managed
+   *         object exists, <code>false</code> otherwise.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   */
+  public boolean hasChild(OptionalRelationDefinition<?, ?> d)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+    return context.managedObjectExists(path.child(d));
+  }
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified
+   * instantiable relation.
+   *
+   * @param d
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   */
+  public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+    return context.listManagedObjects(path, d);
+  }
+
+
+
+  /**
+   * Lists the child managed objects associated with the specified
+   * set relation.
+   *
+   * @param d
+   *          The set relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with this
+   *           managed object's definition.
+   */
+  public String[] listChildren(SetRelationDefinition<?, ?> d)
+      throws IllegalArgumentException {
+    validateRelationDefinition(d);
+    return context.listManagedObjects(path, d);
+  }
+
+
+
+  /**
+   * Register to be notified when new child configurations are added
+   * beneath an instantiable relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           instantiable relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      InstantiableRelationDefinition<?, M> d,
+      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
+      ConfigException {
+    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when new child server managed object are
+   * added beneath an instantiable relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           instantiable relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      InstantiableRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d);
+    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
+        listener);
+    registerAddListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * Register to be notified when a new child configurations is added
+   * beneath an optional relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the optional
+   *           relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when a new child server managed object is
+   * added beneath an optional relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the optional
+   *           relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      OptionalRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
+        listener);
+    registerAddListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * Register to be notified when new child configurations are added
+   * beneath a set relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The configuration add listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           set relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      SetRelationDefinition<?, M> d,
+      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
+      ConfigException {
+    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when new child server managed object are
+   * added beneath a set relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The server managed object add listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           set relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerAddListener(
+      SetRelationDefinition<?, M> d,
+      ServerManagedObjectAddListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d);
+    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
+        listener);
+    registerAddListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * Register to be notified when this server managed object is
+   * changed.
+   *
+   * @param listener
+   *          The configuration change listener.
+   */
+  public void registerChangeListener(
+      ConfigurationChangeListener<? super S> listener) {
+    registerChangeListener(new ServerManagedObjectChangeListenerAdaptor<S>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when this server managed object is
+   * changed.
+   *
+   * @param listener
+   *          The server managed object change listener.
+   */
+  public void registerChangeListener(
+      ServerManagedObjectChangeListener<? super S> listener) {
+    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
+        listener);
+    configEntry.registerChangeListener(adaptor);
+
+    // Change listener registration usually signifies that a managed
+    // object has been accepted and added to the server configuration
+    // during initialization post-add.
+
+    // FIXME: we should prevent multiple invocations in the case where
+    // multiple change listeners are registered for the same object.
+    for (Constraint constraint : definition.getAllConstraints()) {
+      for (ServerConstraintHandler handler : constraint
+          .getServerConstraintHandlers()) {
+        try {
+          handler.performPostAdd(this);
+        } catch (ConfigException e) {
+          if (debugEnabled()) {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * Register to be notified when existing child configurations are
+   * deleted beneath an instantiable relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           instantiable relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      InstantiableRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
+      ConfigException {
+    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when existing child server managed
+   * objects are deleted beneath an instantiable relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The instantiable relation definition.
+   * @param listener
+   *          The server managed objects delete listener.
+   * @throws IllegalArgumentException
+   *           If the instantiable relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           instantiable relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      InstantiableRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d);
+    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
+        listener);
+    registerDeleteListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * Register to be notified when an existing child configuration is
+   * deleted beneath an optional relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the optional
+   *           relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      OptionalRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
+      ConfigException {
+    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when an existing child server managed
+   * object is deleted beneath an optional relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The optional relation definition.
+   * @param listener
+   *          The server managed object delete listener.
+   * @throws IllegalArgumentException
+   *           If the optional relation definition is not associated
+   *           with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the optional
+   *           relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      OptionalRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d).getParent();
+    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
+        listener);
+    registerDeleteListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * Register to be notified when existing child configurations are
+   * deleted beneath a set relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The configuration delete listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           set relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      SetRelationDefinition<?, M> d,
+      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
+      ConfigException {
+    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
+        listener));
+  }
+
+
+
+  /**
+   * Register to be notified when existing child server managed
+   * objects are deleted beneath a set relation.
+   *
+   * @param <M>
+   *          The type of the child server configuration object.
+   * @param d
+   *          The set relation definition.
+   * @param listener
+   *          The server managed objects delete listener.
+   * @throws IllegalArgumentException
+   *           If the set relation definition is not
+   *           associated with this managed object's definition.
+   * @throws ConfigException
+   *           If the configuration entry associated with the
+   *           set relation could not be retrieved.
+   */
+  public <M extends Configuration> void registerDeleteListener(
+      SetRelationDefinition<?, M> d,
+      ServerManagedObjectDeleteListener<M> listener)
+      throws IllegalArgumentException, ConfigException {
+    validateRelationDefinition(d);
+    DN baseDN = DNBuilder.create(path, d);
+    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
+        listener);
+    registerDeleteListener(baseDN, adaptor);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+
+    builder.append("{ TYPE=");
+    builder.append(definition.getName());
+    builder.append(", DN=\"");
+    builder.append(getDN());
+    builder.append('\"');
+    for (Map.Entry<PropertyDefinition<?>, SortedSet<?>> value : properties
+        .entrySet()) {
+      builder.append(", ");
+      builder.append(value.getKey().getName());
+      builder.append('=');
+      builder.append(value.getValue());
+    }
+    builder.append(" }");
+
+    return builder.toString();
+  }
+
+
+
+  /**
+   * Determines whether or not this managed object can be used by the
+   * server.
+   *
+   * @throws ConstraintViolationException
+   *           If one or more constraints determined that this managed
+   *           object cannot be used by the server.
+   */
+  void ensureIsUsable() throws ConstraintViolationException {
+    // Enforce any constraints.
+    boolean isUsable = true;
+    List<Message> reasons = new LinkedList<Message>();
+    for (Constraint constraint : definition.getAllConstraints()) {
+      for (ServerConstraintHandler handler : constraint
+          .getServerConstraintHandlers()) {
+        try {
+          if (!handler.isUsable(this, reasons)) {
+            isUsable = false;
+          }
+        } catch (ConfigException e) {
+          Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
+              .getMessageObject());
+          reasons.add(message);
+          isUsable = false;
+        }
+      }
+    }
+
+    if (!isUsable) {
+      throw new ConstraintViolationException(this, reasons);
+    }
+  }
+
+
+
+  /**
+   * Update the config entry associated with this server managed
+   * object. This is only intended to be used by change listener call
+   * backs in order to update the managed object with the correct
+   * config entry.
+   *
+   * @param configEntry
+   *          The configuration entry.
+   */
+  void setConfigEntry(ConfigEntry configEntry) {
+    this.configEntry = configEntry;
+  }
+
+
+
+  // Deregister an add listener.
+  private <M extends Configuration> void deregisterAddListener(DN baseDN,
+      ConfigurationAddListener<M> listener) {
+    try {
+      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+      if (configEntry != null) {
+        for (ConfigAddListener l : configEntry.getAddListeners()) {
+          if (l instanceof ConfigAddListenerAdaptor) {
+            ConfigAddListenerAdaptor<?> adaptor =
+              (ConfigAddListenerAdaptor<?>) l;
+            ServerManagedObjectAddListener<?> l2 = adaptor
+                .getServerManagedObjectAddListener();
+            if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
+              ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
+                (ServerManagedObjectAddListenerAdaptor<?>) l2;
+              if (adaptor2.getConfigurationAddListener() == listener) {
+                configEntry.deregisterAddListener(adaptor);
+              }
+            }
+          }
+        }
+      }
+      else
+      {
+        // The relation entry does not exist so check for and deregister
+        // delayed add listener.
+        deregisterDelayedAddListener(baseDN, listener);
+      }
+    } catch (ConfigException e) {
+      // Ignore the exception since this implies deregistration.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+  }
+
+
+
+  // Deregister an add listener.
+  private <M extends Configuration> void deregisterAddListener(DN baseDN,
+      ServerManagedObjectAddListener<M> listener) {
+    try {
+      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+      if (configEntry != null) {
+        for (ConfigAddListener l : configEntry.getAddListeners()) {
+          if (l instanceof ConfigAddListenerAdaptor) {
+            ConfigAddListenerAdaptor<?> adaptor =
+              (ConfigAddListenerAdaptor<?>) l;
+            if (adaptor.getServerManagedObjectAddListener() == listener) {
+              configEntry.deregisterAddListener(adaptor);
+            }
+          }
+        }
+      }
+      else
+      {
+        // The relation entry does not exist so check for and deregister
+        // delayed add listener.
+        deregisterDelayedAddListener(baseDN, listener);
+      }
+    } catch (ConfigException e) {
+      // Ignore the exception since this implies deregistration.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+  }
+
+
+
+  // Deregister a delete listener.
+  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
+      ConfigurationDeleteListener<M> listener) {
+    try {
+      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+      if (configEntry != null) {
+        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
+          if (l instanceof ConfigDeleteListenerAdaptor) {
+            ConfigDeleteListenerAdaptor<?> adaptor =
+              (ConfigDeleteListenerAdaptor<?>) l;
+            ServerManagedObjectDeleteListener<?> l2 = adaptor
+                .getServerManagedObjectDeleteListener();
+            if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
+              ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
+                (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
+              if (adaptor2.getConfigurationDeleteListener() == listener) {
+                configEntry.deregisterDeleteListener(adaptor);
+              }
+            }
+          }
+        }
+      }
+      else
+      {
+        // The relation entry does not exist so check for and deregister
+        // delayed add listener.
+        deregisterDelayedDeleteListener(baseDN, listener);
+      }
+    } catch (ConfigException e) {
+      // Ignore the exception since this implies deregistration.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+  }
+
+
+
+  // Deregister a delete listener.
+  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
+      ServerManagedObjectDeleteListener<M> listener) {
+    try {
+      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
+      if (configEntry != null) {
+        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
+          if (l instanceof ConfigDeleteListenerAdaptor) {
+            ConfigDeleteListenerAdaptor<?> adaptor =
+              (ConfigDeleteListenerAdaptor<?>) l;
+            if (adaptor.getServerManagedObjectDeleteListener() == listener) {
+              configEntry.deregisterDeleteListener(adaptor);
+            }
+          }
+        }
+      }
+      else
+      {
+        // The relation entry does not exist so check for and deregister
+        // delayed add listener.
+        deregisterDelayedDeleteListener(baseDN, listener);
+      }
+    } catch (ConfigException e) {
+      // Ignore the exception since this implies deregistration.
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+  }
+
+
+
+  // Gets a config entry required for a listener and throws a config
+  // 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.
+    ConfigEntry configEntry;
+    try {
+      configEntry = DirectoryServer.getConfigEntry(dn);
+    } catch (ConfigException e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
+          String.valueOf(dn), stackTraceToSingleLineString(e));
+      throw new ConfigException(message, e);
+    }
+
+    return configEntry;
+  }
+
+
+
+  // Register an instantiable or optional relation add listener.
+  private void registerAddListener(DN baseDN, ConfigAddListener adaptor)
+      throws IllegalArgumentException, ConfigException {
+    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
+
+    if (relationEntry != null) {
+      relationEntry.registerAddListener(adaptor);
+    } else {
+      // The relation entry does not exist yet so register a delayed
+      // add listener.
+      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
+          adaptor);
+      registerDelayedListener(baseDN, delayedListener);
+    }
+  }
+
+
+
+  // Register a delayed listener with the nearest existing parent
+  // entry to the provided base DN.
+  private void registerDelayedListener(DN baseDN,
+      ConfigAddListener delayedListener) throws ConfigException {
+    DN parentDN = baseDN.getParent();
+    while (parentDN != null) {
+      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+      if (relationEntry == null) {
+        delayedListener = new DelayedConfigAddListener(parentDN,
+            delayedListener);
+        parentDN = parentDN.getParent();
+      } else {
+        relationEntry.registerAddListener(delayedListener);
+        return;
+      }
+    }
+
+    // No parent entry could be found.
+    Message message = ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
+        .get(String.valueOf(baseDN));
+    throw new ConfigException(message);
+  }
+
+  // Deregister a delayed listener with the nearest existing parent
+  // entry to the provided base DN.
+  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
+      ConfigurationAddListener<M> listener) throws ConfigException {
+    DN parentDN = baseDN.getParent();
+    int delayWrappers = 0;
+    while (parentDN != null) {
+      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+      if (relationEntry == null) {
+        parentDN = parentDN.getParent();
+        delayWrappers++;
+      } else {
+        for (ConfigAddListener l : relationEntry.getAddListeners()) {
+          if(l instanceof DelayedConfigAddListener)
+          {
+            DelayedConfigAddListener delayListener =
+                (DelayedConfigAddListener) l;
+            ConfigAddListener wrappedListener;
+
+            int i = delayWrappers;
+            for(; i > 0; i--)
+            {
+              wrappedListener = delayListener.getDelayedAddListener();
+              if(wrappedListener != null &&
+                  wrappedListener instanceof DelayedConfigAddListener)
+              {
+                delayListener = (DelayedConfigAddListener) l;
+              }
+              else
+              {
+                break;
+              }
+            }
+
+            if(i > 0)
+            {
+              // There are not enough level of wrapping so this can't be
+              // the listener we are looking for.
+              continue;
+            }
+
+            ConfigAddListener delayedListener =
+                delayListener.getDelayedAddListener();
+
+            if (delayedListener != null &&
+                 delayedListener instanceof ConfigAddListenerAdaptor) {
+              ConfigAddListenerAdaptor<?> adaptor =
+                  (ConfigAddListenerAdaptor<?>) delayedListener;
+              ServerManagedObjectAddListener<?> l2 = adaptor
+                  .getServerManagedObjectAddListener();
+              if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
+                ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
+                    (ServerManagedObjectAddListenerAdaptor<?>) l2;
+                if (adaptor2.getConfigurationAddListener() == listener) {
+                  relationEntry.deregisterAddListener(l);
+                }
+              }
+            }
+          }
+        }
+        return;
+      }
+    }
+  }
+
+
+  // Deregister a delayed listener with the nearest existing parent
+  // entry to the provided base DN.
+  private <M extends Configuration> void deregisterDelayedDeleteListener(
+      DN baseDN, ConfigurationDeleteListener<M> listener)
+      throws ConfigException {
+    DN parentDN = baseDN.getParent();
+    int delayWrappers = 0;
+    while (parentDN != null) {
+      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+      if (relationEntry == null) {
+        parentDN = parentDN.getParent();
+        delayWrappers++;
+      } else {
+        for (ConfigAddListener l : relationEntry.getAddListeners()) {
+          if(l instanceof DelayedConfigAddListener)
+          {
+            DelayedConfigAddListener delayListener =
+                (DelayedConfigAddListener) l;
+            ConfigAddListener wrappedListener;
+
+            int i = delayWrappers;
+            for(; i > 0; i--)
+            {
+              wrappedListener = delayListener.getDelayedAddListener();
+              if(wrappedListener != null &&
+                  wrappedListener instanceof DelayedConfigAddListener)
+              {
+                delayListener = (DelayedConfigAddListener) l;
+              }
+              else
+              {
+                break;
+              }
+            }
+
+            if(i > 0)
+            {
+              // There are not enough level of wrapping so this can't be
+              // the listener we are looking for.
+              continue;
+            }
+
+            ConfigDeleteListener delayedListener =
+                delayListener.getDelayedDeleteListener();
+
+            if (delayedListener != null &&
+                delayedListener instanceof ConfigDeleteListenerAdaptor) {
+              ConfigDeleteListenerAdaptor<?> adaptor =
+                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
+              ServerManagedObjectDeleteListener<?> l2 = adaptor
+                  .getServerManagedObjectDeleteListener();
+              if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
+                ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
+                    (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
+                if (adaptor2.getConfigurationDeleteListener() == listener) {
+                  relationEntry.deregisterAddListener(l);
+                }
+              }
+            }
+          }
+        }
+        return;
+      }
+    }
+  }
+
+  // Deregister a delayed listener with the nearest existing parent
+  // entry to the provided base DN.
+  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
+      ServerManagedObjectAddListener<M> listener) throws ConfigException {
+    DN parentDN = baseDN.getParent();
+    int delayWrappers = 0;
+    while (parentDN != null) {
+      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+      if (relationEntry == null) {
+        parentDN = parentDN.getParent();
+        delayWrappers++;
+      } else {
+        for (ConfigAddListener l : relationEntry.getAddListeners()) {
+          if(l instanceof DelayedConfigAddListener)
+          {
+            DelayedConfigAddListener delayListener =
+                (DelayedConfigAddListener) l;
+            ConfigAddListener wrappedListener;
+
+            int i = delayWrappers;
+            for(; i > 0; i--)
+            {
+              wrappedListener = delayListener.getDelayedAddListener();
+              if(wrappedListener != null &&
+                  wrappedListener instanceof DelayedConfigAddListener)
+              {
+                delayListener = (DelayedConfigAddListener) l;
+              }
+              else
+              {
+                break;
+              }
+            }
+
+            if(i > 0)
+            {
+              // There are not enough level of wrapping so this can't be
+              // the listener we are looking for.
+              continue;
+            }
+
+            ConfigAddListener delayedListener =
+                delayListener.getDelayedAddListener();
+
+            if (delayedListener != null &&
+                 delayedListener instanceof ConfigAddListenerAdaptor) {
+              ConfigAddListenerAdaptor<?> adaptor =
+                  (ConfigAddListenerAdaptor<?>) delayedListener;
+              if (adaptor.getServerManagedObjectAddListener() == listener) {
+                relationEntry.deregisterAddListener(l);
+              }
+            }
+          }
+        }
+        return;
+      }
+    }
+  }
+
+
+  // Deregister a delayed listener with the nearest existing parent
+  // entry to the provided base DN.
+  private <M extends Configuration> void deregisterDelayedDeleteListener(
+      DN baseDN, ServerManagedObjectDeleteListener<M> listener)
+      throws ConfigException {
+    DN parentDN = baseDN.getParent();
+    int delayWrappers = 0;
+    while (parentDN != null) {
+      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
+      if (relationEntry == null) {
+        parentDN = parentDN.getParent();
+        delayWrappers++;
+      } else {
+        for (ConfigAddListener l : relationEntry.getAddListeners()) {
+          if(l instanceof DelayedConfigAddListener)
+          {
+            DelayedConfigAddListener delayListener =
+                (DelayedConfigAddListener) l;
+            ConfigAddListener wrappedListener;
+
+            int i = delayWrappers;
+            for(; i > 0; i--)
+            {
+              wrappedListener = delayListener.getDelayedAddListener();
+              if(wrappedListener != null &&
+                  wrappedListener instanceof DelayedConfigAddListener)
+              {
+                delayListener = (DelayedConfigAddListener) l;
+              }
+              else
+              {
+                break;
+              }
+            }
+
+            if(i > 0)
+            {
+              // There are not enough level of wrapping so this can't be
+              // the listener we are looking for.
+              continue;
+            }
+
+            ConfigDeleteListener delayedListener =
+                delayListener.getDelayedDeleteListener();
+
+            if (delayedListener != null &&
+                 delayedListener instanceof ConfigDeleteListenerAdaptor) {
+              ConfigDeleteListenerAdaptor<?> adaptor =
+                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
+              if (adaptor.getServerManagedObjectDeleteListener() == listener) {
+                relationEntry.deregisterAddListener(l);
+              }
+            }
+          }
+        }
+        return;
+      }
+    }
+  }
+
+
+  // Register an instantiable or optional relation delete listener.
+  private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor)
+      throws ConfigException {
+    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
+
+    if (relationEntry != null) {
+      relationEntry.registerDeleteListener(adaptor);
+    } else {
+      // The relation entry does not exist yet so register a delayed
+      // add listener.
+      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
+          adaptor);
+      registerDelayedListener(baseDN, delayedListener);
+    }
+  }
+
+
+
+  // Validate that a relation definition belongs to this managed
+  // object.
+  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
+      throws IllegalArgumentException {
+    RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd
+        .getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + definition.getName());
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListener.java
new file mode 100644
index 0000000..c3bd1d0
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListener.java
@@ -0,0 +1,78 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import org.opends.messages.Message;
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when a new server managed object is added.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+public interface ServerManagedObjectAddListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed addition of a new server managed
+   * object is acceptable to this add listener.
+   *
+   * @param mo
+   *          The server managed object that will be added.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided server managed object is not acceptable.
+   * @return Returns <code>true</code> if the proposed addition is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationAddAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Adds a new server managed object to this add listener.
+   *
+   * @param mo
+   *          The server managed object that will be added.
+   * @return Returns information about the result of adding the server
+   *         managed object.
+   */
+  public ConfigChangeResult applyConfigurationAdd(
+      ServerManagedObject<? extends T> mo);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListenerAdaptor.java
new file mode 100644
index 0000000..35a1cee
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectAddListenerAdaptor.java
@@ -0,0 +1,100 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * An adaptor class which converts
+ * {@link ServerManagedObjectAddListener} callbacks to
+ * {@link ConfigurationAddListener} callbacks.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+final class ServerManagedObjectAddListenerAdaptor<T extends Configuration>
+    implements ServerManagedObjectAddListener<T> {
+
+  // The underlying add listener.
+  private final ConfigurationAddListener<T> listener;
+
+
+
+  /**
+   * Creates a new server managed object add listener adaptor.
+   *
+   * @param listener
+   *          The underlying add listener.
+   */
+  public ServerManagedObjectAddListenerAdaptor(
+      ConfigurationAddListener<T> listener) {
+    this.listener = listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationAdd(
+      ServerManagedObject<? extends T> mo) {
+    return listener.applyConfigurationAdd(mo.getConfiguration());
+  }
+
+
+
+  /**
+   * Gets the configuration add listener associated with this adaptor.
+   *
+   * @return Returns the configuration add listener associated with
+   *         this adaptor.
+   */
+  public ConfigurationAddListener<T> getConfigurationAddListener() {
+    return listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationAddAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons) {
+    return listener.isConfigurationAddAcceptable(mo.getConfiguration(),
+        unacceptableReasons);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListener.java
new file mode 100644
index 0000000..4ca64f4
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListener.java
@@ -0,0 +1,80 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import org.opends.messages.Message;
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when a its associated server managed object
+ * is changed.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+public interface ServerManagedObjectChangeListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed change to the server managed
+   * object is acceptable to this change listener.
+   *
+   * @param mo
+   *          The new server managed object containing the changes.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided server managed object is not acceptable.
+   * @return Returns <code>true</code> if the proposed change is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationChangeAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Applies the server managed object changes to this change
+   * listener.
+   *
+   * @param mo
+   *          The new server managed object containing the changes.
+   * @return Returns information about the result of changing the
+   *         server managed object.
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      ServerManagedObject<? extends T> mo);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListenerAdaptor.java
new file mode 100644
index 0000000..0539f6a
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectChangeListenerAdaptor.java
@@ -0,0 +1,102 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * An adaptor class which converts
+ * {@link ServerManagedObjectChangeListener} callbacks to
+ * {@link ConfigurationChangeListener} callbacks.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+final class ServerManagedObjectChangeListenerAdaptor<T extends Configuration>
+    implements ServerManagedObjectChangeListener<T> {
+
+  // The underlying change listener.
+  private final ConfigurationChangeListener<? super T> listener;
+
+
+
+  /**
+   * Creates a new server managed object change listener adaptor.
+   *
+   * @param listener
+   *          The underlying change listener.
+   */
+  public ServerManagedObjectChangeListenerAdaptor(
+      ConfigurationChangeListener<? super T> listener) {
+    this.listener = listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      ServerManagedObject<? extends T> mo) {
+    return listener.applyConfigurationChange(mo.getConfiguration());
+  }
+
+
+
+  /**
+   * Gets the configuration change listener associated with this
+   * adaptor.
+   *
+   * @return Returns the configuration change listener associated with
+   *         this adaptor.
+   */
+  public ConfigurationChangeListener<? super T>
+  getConfigurationChangeListener() {
+    return listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons) {
+    return listener.isConfigurationChangeAcceptable(mo.getConfiguration(),
+        unacceptableReasons);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java
new file mode 100644
index 0000000..aa36dbf
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDecodingException.java
@@ -0,0 +1,148 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.admin.server;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
+import org.opends.server.admin.DecodingException;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.util.Validator;
+
+
+
+/**
+ * The requested server managed object was found but one or more of
+ * its properties could not be decoded successfully.
+ */
+public class ServerManagedObjectDecodingException extends DecodingException {
+
+  /**
+   * Version ID required by serializable classes.
+   */
+  private static final long serialVersionUID = 1598401431084729853L;
+
+
+
+  // Create the message.
+  private static Message createMessage(
+      ServerManagedObject<?> partialManagedObject,
+      Collection<PropertyException> causes) {
+    Validator.ensureNotNull(causes);
+    Validator.ensureTrue(!causes.isEmpty());
+
+    ManagedObjectDefinition<?, ?> d = partialManagedObject
+        .getManagedObjectDefinition();
+    if (causes.size() == 1) {
+      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_SINGLE.get(d
+          .getUserFriendlyName(), causes.iterator().next().getMessageObject());
+    } else {
+      MessageBuilder builder = new MessageBuilder();
+
+      boolean isFirst = true;
+      for (PropertyException cause : causes) {
+        if (!isFirst) {
+          builder.append("; ");
+        }
+        builder.append(cause.getMessageObject());
+        isFirst = false;
+      }
+
+      return ERR_MANAGED_OBJECT_DECODING_EXCEPTION_PLURAL.get(d
+          .getUserFriendlyName(), builder.toMessage());
+    }
+  }
+
+  // The exception(s) that caused this decoding exception.
+  private final Collection<PropertyException> causes;
+
+  // The partially created server managed object.
+  private final ServerManagedObject<?> partialManagedObject;
+
+
+
+  /**
+   * Create a new property decoding exception.
+   *
+   * @param partialManagedObject
+   *          The partially created server managed object containing
+   *          properties which were successfully decoded and empty
+   *          properties for those which were not (this may include
+   *          empty mandatory properties).
+   * @param causes
+   *          The exception(s) that caused this decoding exception.
+   */
+  public ServerManagedObjectDecodingException(
+      ServerManagedObject<?> partialManagedObject,
+      Collection<PropertyException> causes) {
+    super(createMessage(partialManagedObject, causes));
+
+    this.partialManagedObject = partialManagedObject;
+    this.causes = Collections
+        .unmodifiableList(new LinkedList<PropertyException>(causes));
+  }
+
+
+
+  /**
+   * Get an unmodifiable collection view of the causes of this
+   * exception.
+   *
+   * @return Returns an unmodifiable collection view of the causes of
+   *         this exception.
+   */
+  public Collection<PropertyException> getCauses() {
+    return causes;
+  }
+
+
+
+  /**
+   * Get the partially created server managed object containing
+   * properties which were successfully decoded and empty properties
+   * for those which were not (this may include empty mandatory
+   * properties).
+   *
+   * @return Returns the partially created server managed object
+   *         containing properties which were successfully decoded and
+   *         empty properties for those which were not (this may
+   *         include empty mandatory properties).
+   */
+  public ServerManagedObject<?> getPartialManagedObject() {
+    return partialManagedObject;
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListener.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListener.java
new file mode 100644
index 0000000..ac238a6
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListener.java
@@ -0,0 +1,80 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import org.opends.messages.Message;
+
+import java.util.List;
+
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * This interface defines the methods that a Directory Server
+ * configurable component should implement if it wishes to be able to
+ * receive notifications when an existing server managed object is
+ * deleted.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+public interface ServerManagedObjectDeleteListener<T extends Configuration> {
+
+  /**
+   * Indicates whether the proposed deletion of an existing server
+   * managed object is acceptable to this delete listener.
+   *
+   * @param mo
+   *          The server managed object that will be deleted.
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          provided server managed object is not acceptable.
+   * @return Returns <code>true</code> if the proposed deletion is
+   *         acceptable, or <code>false</code> if it is not.
+   */
+  public boolean isConfigurationDeleteAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons);
+
+
+
+  /**
+   * Deletes an existing server managed object from this delete
+   * listener.
+   *
+   * @param mo
+   *          The existing server managed object that will be deleted.
+   * @return Returns information about the result of deleting the
+   *         server managed object.
+   */
+  public ConfigChangeResult applyConfigurationDelete(
+      ServerManagedObject<? extends T> mo);
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListenerAdaptor.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListenerAdaptor.java
new file mode 100644
index 0000000..7f9dca6
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagedObjectDeleteListenerAdaptor.java
@@ -0,0 +1,101 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.server;
+
+
+
+import java.util.List;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.Configuration;
+import org.opends.server.types.ConfigChangeResult;
+
+
+
+/**
+ * An adaptor class which converts
+ * {@link ServerManagedObjectDeleteListener} callbacks to
+ * {@link ConfigurationDeleteListener} callbacks.
+ *
+ * @param <T>
+ *          The type of server managed object that this listener
+ *          should be notified about.
+ */
+final class ServerManagedObjectDeleteListenerAdaptor<T extends Configuration>
+    implements ServerManagedObjectDeleteListener<T> {
+
+  // The underlying delete listener.
+  private final ConfigurationDeleteListener<T> listener;
+
+
+
+  /**
+   * Creates a new server managed object delete listener adaptor.
+   *
+   * @param listener
+   *          The underlying delete listener.
+   */
+  public ServerManagedObjectDeleteListenerAdaptor(
+      ConfigurationDeleteListener<T> listener) {
+    this.listener = listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(
+      ServerManagedObject<? extends T> mo) {
+    return listener.applyConfigurationDelete(mo.getConfiguration());
+  }
+
+
+
+  /**
+   * Gets the configuration delete listener associated with this
+   * adaptor.
+   *
+   * @return Returns the configuration delete listener associated with
+   *         this adaptor.
+   */
+  public ConfigurationDeleteListener<T> getConfigurationDeleteListener() {
+    return listener;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationDeleteAcceptable(
+      ServerManagedObject<? extends T> mo, List<Message> unacceptableReasons) {
+    return listener.isConfigurationDeleteAcceptable(mo.getConfiguration(),
+        unacceptableReasons);
+  }
+
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
new file mode 100644
index 0000000..045d3ad
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
@@ -0,0 +1,986 @@
+/*
+ * 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.admin.server;
+
+
+
+import static org.opends.messages.AdminMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AggregationPropertyDefinition;
+import org.opends.server.admin.AliasDefaultBehaviorProvider;
+import org.opends.server.admin.Configuration;
+import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DefinitionDecodingException;
+import org.opends.server.admin.DefinitionResolver;
+import org.opends.server.admin.IllegalPropertyValueException;
+import org.opends.server.admin.IllegalPropertyValueStringException;
+import org.opends.server.admin.InstantiableRelationDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.ManagedObjectDefinition;
+import org.opends.server.admin.ManagedObjectPath;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyDefinitionVisitor;
+import org.opends.server.admin.PropertyException;
+import org.opends.server.admin.PropertyIsMandatoryException;
+import org.opends.server.admin.PropertyIsSingleValuedException;
+import org.opends.server.admin.PropertyNotFoundException;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.Reference;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.SetRelationDefinition;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
+import org.opends.server.admin.UnknownPropertyDefinitionException;
+import org.opends.server.admin.DefinitionDecodingException.Reason;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.api.AttributeValueDecoder;
+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.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.forgerock.opendj.ldap.DN;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+
+
+
+/**
+ * Server management connection context.
+ */
+public final class ServerManagementContext {
+
+  /**
+   * A default behavior visitor used for retrieving the default values
+   * of a property.
+   *
+   * @param <T>
+   *          The type of the property.
+   */
+  private class DefaultValueFinder<T> implements
+      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
+
+    // Any exception that occurred whilst retrieving inherited default
+    // values.
+    private DefaultBehaviorException exception = null;
+
+    // Optional new configuration entry which does not yet exist in
+    // the configuration back-end.
+    private final ConfigEntry newConfigEntry;
+
+    // The path of the managed object containing the next property.
+    private ManagedObjectPath<?, ?> nextPath = null;
+
+    // The next property whose default values were required.
+    private PropertyDefinition<T> nextProperty = null;
+
+
+
+    // Private constructor.
+    private DefaultValueFinder(ConfigEntry newConfigEntry) {
+      this.newConfigEntry = newConfigEntry;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAbsoluteInherited(
+        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      Collection<String> stringValues = d.getDefaultValues();
+      List<T> values = new ArrayList<T>(stringValues.size());
+
+      for (String stringValue : stringValues) {
+        try {
+          values.add(nextProperty.decodeValue(stringValue));
+        } catch (IllegalPropertyValueStringException e) {
+          exception = new DefaultBehaviorException(nextProperty, e);
+          break;
+        }
+      }
+
+      return values;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitRelativeInherited(
+        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+        return Collections.emptySet();
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+        Void p) {
+      return Collections.emptySet();
+    }
+
+
+
+    // Find the default values for the next path/property.
+    private Collection<T> find(ManagedObjectPath<?, ?> p,
+        PropertyDefinition<T> pd) throws DefaultBehaviorException {
+      nextPath = p;
+      nextProperty = pd;
+
+      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
+          this, null);
+
+      if (exception != null) {
+        throw exception;
+      }
+
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new DefaultBehaviorException(pd,
+            new PropertyIsSingleValuedException(pd));
+      }
+
+      return values;
+    }
+
+
+
+    // Get an inherited property value.
+    @SuppressWarnings("unchecked")
+    private Collection<T> getInheritedProperty(ManagedObjectPath target,
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
+        throws DefaultBehaviorException {
+      // First check that the requested type of managed object
+      // corresponds to the path.
+      AbstractManagedObjectDefinition<?, ?> supr = target
+          .getManagedObjectDefinition();
+      if (!supr.isParentOf(d)) {
+        throw new DefaultBehaviorException(
+            nextProperty, new DefinitionDecodingException(supr,
+                Reason.WRONG_TYPE_INFORMATION));
+      }
+
+      // Save the current property in case of recursion.
+      PropertyDefinition<T> pd1 = nextProperty;
+
+      try {
+        // Get the actual managed object definition.
+        DN dn = DNBuilder.create(target);
+        ConfigEntry configEntry;
+        if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
+          configEntry = newConfigEntry;
+        } else {
+          configEntry = getManagedObjectConfigEntry(dn);
+        }
+
+        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+        ManagedObjectDefinition<?, ?> mod = d
+            .resolveManagedObjectDefinition(resolver);
+
+        PropertyDefinition<T> pd2;
+        try {
+          PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
+          pd2 = pd1.getClass().cast(pdTmp);
+        } catch (IllegalArgumentException e) {
+          throw new PropertyNotFoundException(propertyName);
+        } catch (ClassCastException e) {
+          // FIXME: would be nice to throw a better exception here.
+          throw new PropertyNotFoundException(propertyName);
+        }
+
+        List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
+        if (values.isEmpty()) {
+          // Recursively retrieve this property's default values.
+          Collection<T> tmp = find(target, pd2);
+          Collection<T> pvalues = new ArrayList<T>(tmp.size());
+          for (T value : tmp) {
+            pd1.validateValue(value);
+            pvalues.add(value);
+          }
+          return pvalues;
+        } else {
+          Collection<T> pvalues = new ArrayList<T>(values.size());
+          for (AttributeValue value : values) {
+            pvalues.add(ValueDecoder.decode(pd1, value));
+          }
+          return pvalues;
+        }
+      } catch (DefinitionDecodingException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (PropertyNotFoundException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (IllegalPropertyValueException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (IllegalPropertyValueStringException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      } catch (ConfigException e) {
+        throw new DefaultBehaviorException(pd1, e);
+      }
+    }
+  }
+
+
+
+  /**
+   * A definition resolver that determines the managed object
+   * definition from the object classes of a ConfigEntry.
+   */
+  private class MyDefinitionResolver implements DefinitionResolver {
+
+    // The config entry.
+    private final ConfigEntry entry;
+
+
+
+    // Private constructor.
+    private MyDefinitionResolver(ConfigEntry entry) {
+      this.entry = entry;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+      String oc = LDAPProfile.getInstance().getObjectClass(d);
+      return entry.hasObjectClass(oc);
+    }
+  }
+
+
+
+  /**
+   * A visitor which is used to decode property LDAP values.
+   */
+  private static final class ValueDecoder extends
+      PropertyDefinitionVisitor<Object, String> {
+
+    /**
+     * Decodes the provided property LDAP value.
+     *
+     * @param <PD>
+     *          The type of the property.
+     * @param pd
+     *          The property definition.
+     * @param value
+     *          The LDAP string representation.
+     * @return Returns the decoded LDAP value.
+     * @throws IllegalPropertyValueStringException
+     *           If the property value could not be decoded because it
+     *           was invalid.
+     */
+    public static <PD> PD decode(PropertyDefinition<PD> pd,
+        AttributeValue value) throws IllegalPropertyValueStringException {
+      String s = value.getValue().toString();
+      return pd.castValue(pd.accept(new ValueDecoder(), s));
+    }
+
+
+
+    // Prevent instantiation.
+    private ValueDecoder() {
+      // No implementation required.
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration>
+    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
+      // Aggregations values are stored as full DNs in LDAP, but
+      // just their common name is exposed in the admin framework.
+      try {
+        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
+            .getRelationDefinition(), p);
+        return reference.getName();
+      } catch (IllegalArgumentException e) {
+        throw new IllegalPropertyValueStringException(d, p);
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
+        throws UnknownPropertyDefinitionException {
+      // By default the property definition's decoder will do.
+      return d.decodeValue(p);
+    }
+  }
+
+
+
+  // Singleton instance.
+  private final static ServerManagementContext INSTANCE =
+    new ServerManagementContext();
+
+  /**
+   * The root server managed object.
+   */
+  private static final ServerManagedObject<RootCfg> ROOT =
+    new ServerManagedObject<RootCfg>(
+      ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
+          .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
+
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
+  /**
+   * Get the single server-side management context.
+   *
+   * @return Returns the single server-side management context.
+   */
+  public static ServerManagementContext getInstance() {
+    return INSTANCE;
+  }
+
+
+
+  // Private constructor.
+  private ServerManagementContext() {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * Gets the named managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The path of the managed object.
+   * @return Returns the named managed object.
+   * @throws ConfigException
+   *           If the named managed object could not be found or if it
+   *           could not be decoded.
+   */
+  @SuppressWarnings("unchecked")
+  public <C extends ConfigurationClient, S extends Configuration>
+  ServerManagedObject<? extends S> getManagedObject(
+      ManagedObjectPath<C, S> path) throws ConfigException {
+    // Be careful to handle the root configuration.
+    if (path.isEmpty()) {
+      return (ServerManagedObject<S>) getRootConfigurationManagedObject();
+    }
+
+    // Get the configuration entry.
+    DN targetDN = DNBuilder.create(path);
+    ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
+    try {
+      ServerManagedObject<? extends S> managedObject;
+      managedObject = decode(path, configEntry);
+
+      // Enforce any constraints.
+      managedObject.ensureIsUsable();
+
+      return managedObject;
+    } catch (DefinitionDecodingException e) {
+      throw ConfigExceptionFactory.getInstance()
+          .createDecodingExceptionAdaptor(targetDN, e);
+    } catch (ServerManagedObjectDecodingException e) {
+      throw ConfigExceptionFactory.getInstance()
+          .createDecodingExceptionAdaptor(e);
+    } catch (ConstraintViolationException e) {
+      throw ConfigExceptionFactory.getInstance()
+          .createDecodingExceptionAdaptor(e);
+    }
+  }
+
+
+
+  /**
+   * Gets the effective value of a property in the named managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective value, or
+   *         <code>null</code> if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ConfigException
+   *           If the named managed object could not be found or if it
+   *           could not be decoded.
+   */
+  public <C extends ConfigurationClient, S extends Configuration, PD>
+  PD getPropertyValue(ManagedObjectPath<C, S> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      ConfigException, PropertyException {
+    SortedSet<PD> values = getPropertyValues(path, pd);
+    if (values.isEmpty()) {
+      return null;
+    } else {
+      return values.first();
+    }
+  }
+
+
+
+  /**
+   * Gets the effective values of a property in the named managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param <PD>
+   *          The type of the property to be retrieved.
+   * @param path
+   *          The path of the managed object containing the property.
+   * @param pd
+   *          The property to be retrieved.
+   * @return Returns the property's effective values, or an empty set
+   *         if there are no values defined.
+   * @throws IllegalArgumentException
+   *           If the property definition is not associated with the
+   *           referenced managed object's definition.
+   * @throws PropertyException
+   *           If the managed object was found but the requested
+   *           property could not be decoded.
+   * @throws ConfigException
+   *           If the named managed object could not be found or if it
+   *           could not be decoded.
+   */
+  @SuppressWarnings("unchecked")
+  public <C extends ConfigurationClient, S extends Configuration, PD>
+  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
+      PropertyDefinition<PD> pd) throws IllegalArgumentException,
+      ConfigException, PropertyException {
+    // Check that the requested property is from the definition
+    // associated with the path.
+    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
+    if (tmp != pd) {
+      throw new IllegalArgumentException("The property " + pd.getName()
+          + " is not associated with a " + d.getName());
+    }
+
+    // Determine the exact type of managed object referenced by the
+    // path.
+    DN dn = DNBuilder.create(path);
+    ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
+
+    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+    ManagedObjectDefinition<? extends C, ? extends S> mod;
+
+    try {
+      mod = d.resolveManagedObjectDefinition(resolver);
+    } catch (DefinitionDecodingException e) {
+      throw ConfigExceptionFactory.getInstance()
+          .createDecodingExceptionAdaptor(dn, e);
+    }
+
+    // Make sure we use the correct property definition, the
+    // provided one might have been overridden in the resolved
+    // definition.
+    pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
+
+    List<AttributeValue> values = getAttribute(mod, pd, configEntry);
+    return decodeProperty(path.asSubType(mod), pd, values, null);
+  }
+
+
+
+  /**
+   * Get the root configuration manager associated with this
+   * management context.
+   *
+   * @return Returns the root configuration manager associated with
+   *         this management context.
+   */
+  public RootCfg getRootConfiguration() {
+    return getRootConfigurationManagedObject().getConfiguration();
+  }
+
+
+
+  /**
+   * Get the root configuration server managed object associated with
+   * this management context.
+   *
+   * @return Returns the root configuration server managed object
+   *         associated with this management context.
+   */
+  public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
+    return ROOT;
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The instantiable relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
+      throws IllegalArgumentException {
+    validateRelationDefinition(parent, rd);
+
+    // Get the target entry.
+    DN targetDN = DNBuilder.create(parent, rd);
+    ConfigEntry configEntry;
+    try {
+      configEntry = DirectoryServer.getConfigEntry(targetDN);
+    } catch (ConfigException e) {
+      return new String[0];
+    }
+
+    if (configEntry == null) {
+      return new String[0];
+    }
+
+    // Retrieve the children.
+    Set<DN> children = configEntry.getChildren().keySet();
+    ArrayList<String> names = new ArrayList<String>(children.size());
+    for (DN child : children) {
+      // Assume that RDNs are single-valued and can be trimmed.
+      AttributeValue av = child.getRDN().getAttributeValue(0);
+      names.add(av.getValue().toString().trim());
+    }
+
+    return names.toArray(new String[names.size()]);
+  }
+
+
+
+  /**
+   * Lists the child managed objects of the named parent managed
+   * object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          relation definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          relation definition refers to.
+   * @param parent
+   *          The path of the parent managed object.
+   * @param rd
+   *          The set relation definition.
+   * @return Returns the names of the child managed objects.
+   * @throws IllegalArgumentException
+   *           If the relation definition is not associated with the
+   *           parent managed object's definition.
+   */
+  public <C extends ConfigurationClient, S extends Configuration>
+  String[] listManagedObjects(
+      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
+      throws IllegalArgumentException {
+    validateRelationDefinition(parent, rd);
+
+    // Get the target entry.
+    DN targetDN = DNBuilder.create(parent, rd);
+    ConfigEntry configEntry;
+    try {
+      configEntry = DirectoryServer.getConfigEntry(targetDN);
+    } catch (ConfigException e) {
+      return new String[0];
+    }
+
+    if (configEntry == null) {
+      return new String[0];
+    }
+
+    // Retrieve the children.
+    Set<DN> children = configEntry.getChildren().keySet();
+    ArrayList<String> names = new ArrayList<String>(children.size());
+    for (DN child : children) {
+      // Assume that RDNs are single-valued and can be trimmed.
+      AttributeValue av = child.getRDN().getAttributeValue(0);
+      names.add(av.toString().trim());
+    }
+
+    return names.toArray(new String[names.size()]);
+  }
+
+
+
+  /**
+   * Determines whether or not the named managed object exists.
+   *
+   * @param path
+   *          The path of the named managed object.
+   * @return Returns <code>true</code> if the named managed object
+   *         exists, <code>false</code> otherwise.
+   */
+  public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
+    // Get the configuration entry.
+    DN targetDN = DNBuilder.create(path);
+    try {
+      return (getManagedObjectConfigEntry(targetDN) != null);
+    } catch (ConfigException e) {
+      // Assume it doesn't exist.
+      return false;
+    }
+  }
+
+
+
+  /**
+   * Decodes a configuration entry into the required type of server
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The location of the server managed object.
+   * @param configEntry
+   *          The configuration entry that should be decoded.
+   * @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.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ServerManagedObject<? extends S> decode(
+      ManagedObjectPath<C, S> path, ConfigEntry configEntry)
+      throws DefinitionDecodingException, ServerManagedObjectDecodingException {
+    return decode(path, configEntry, null);
+  }
+
+
+
+  /**
+   * Decodes a configuration entry into the required type of server
+   * managed object.
+   *
+   * @param <C>
+   *          The type of client managed object configuration that the
+   *          path definition refers to.
+   * @param <S>
+   *          The type of server managed object configuration that the
+   *          path definition refers to.
+   * @param path
+   *          The location of the server managed object.
+   * @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.
+   */
+  <C extends ConfigurationClient, S extends Configuration>
+  ServerManagedObject<? extends S> decode(
+      ManagedObjectPath<C, S> path, 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.
+    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+    ManagedObjectDefinition<? extends C, ? extends S> mod = d
+        .resolveManagedObjectDefinition(resolver);
+
+    // Build the managed object's properties.
+    List<PropertyException> exceptions = new LinkedList<PropertyException>();
+    Map<PropertyDefinition<?>, SortedSet<?>> properties =
+      new HashMap<PropertyDefinition<?>, SortedSet<?>>();
+    for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+      List<AttributeValue> values = getAttribute(mod, pd, configEntry);
+      try {
+        SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
+        properties.put(pd, pvalues);
+      } catch (PropertyException e) {
+        exceptions.add(e);
+      }
+    }
+
+    // If there were no decoding problems then return the managed
+    // object, otherwise throw an operations exception.
+    ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
+        configEntry);
+    if (exceptions.isEmpty()) {
+      return mo;
+    } else {
+      throw new ServerManagedObjectDecodingException(mo, exceptions);
+    }
+  }
+
+
+
+  // Decode helper method required to avoid generics warning.
+  private <C extends ConfigurationClient, S extends Configuration>
+  ServerManagedObject<S> decodeAux(
+      ManagedObjectPath<? super C, ? super S> path,
+      ManagedObjectDefinition<C, S> d,
+      Map<PropertyDefinition<?>, SortedSet<?>> properties,
+      ConfigEntry configEntry) {
+    ManagedObjectPath<C, S> newPath = path.asSubType(d);
+    return new ServerManagedObject<S>(newPath, d, properties, configEntry);
+  }
+
+
+
+  // Create a property using the provided string values.
+  private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
+      PropertyDefinition<T> pd, List<AttributeValue> values,
+      ConfigEntry newConfigEntry) throws PropertyException {
+    PropertyException exception = null;
+    SortedSet<T> pvalues = new TreeSet<T>(pd);
+
+    if (!values.isEmpty()) {
+      // The property has values defined for it.
+      for (AttributeValue value : values) {
+        try {
+          pvalues.add(ValueDecoder.decode(pd, value));
+        } catch (IllegalPropertyValueStringException e) {
+          exception = e;
+        }
+      }
+    } else {
+      // No values defined so get the defaults.
+      try {
+        pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
+      } catch (DefaultBehaviorException e) {
+        exception = e;
+      }
+    }
+
+    if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+      // This exception takes precedence over previous exceptions.
+      exception = new PropertyIsSingleValuedException(pd);
+      T value = pvalues.first();
+      pvalues.clear();
+      pvalues.add(value);
+    }
+
+    if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+      // The values maybe empty because of a previous exception.
+      if (exception == null) {
+        exception = new PropertyIsMandatoryException(pd);
+      }
+    }
+
+    if (exception != null) {
+      throw exception;
+    } else {
+      return pvalues;
+    }
+  }
+
+
+
+  // Gets the attribute associated with a property from a ConfigEntry.
+  private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d,
+      PropertyDefinition<?> pd, ConfigEntry configEntry) {
+    // TODO: we create a default attribute type if it is
+    // undefined. We should log a warning here if this is the case
+    // since the attribute should have been defined.
+    String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
+    AttributeType type = DirectoryServer.getAttributeType(attrID, true);
+    AttributeValueDecoder<AttributeValue> decoder =
+      new AttributeValueDecoder<AttributeValue>() {
+
+      public AttributeValue decode(AttributeValue value)
+          throws DirectoryException {
+        return value;
+      }
+    };
+
+    List<AttributeValue> values = new LinkedList<AttributeValue>();
+    try {
+      configEntry.getEntry().getAttributeValues(type, decoder, values);
+    } catch (DirectoryException e) {
+      // Should not happen.
+      throw new RuntimeException(e);
+    }
+    return values;
+  }
+
+
+
+  // Get the default values for the specified property.
+  private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
+      PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
+      throws DefaultBehaviorException {
+    DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
+    return v.find(p, pd);
+  }
+
+
+
+  // Gets a config entry required for a managed object and throws a
+  // config exception on failure.
+  private ConfigEntry getManagedObjectConfigEntry(
+      DN dn) throws ConfigException {
+    ConfigEntry configEntry;
+    try {
+      configEntry = DirectoryServer.getConfigEntry(dn);
+    } catch (ConfigException e) {
+      if (debugEnabled()) {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
+          String.valueOf(dn), stackTraceToSingleLineString(e));
+      throw new ConfigException(message, e);
+    }
+
+    // The configuration handler is free to return null indicating
+    // that the entry does not exist.
+    if (configEntry == null) {
+      Message message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
+          .get(String.valueOf(dn));
+      throw new ConfigException(message);
+    }
+
+    return configEntry;
+  }
+
+
+
+  // Validate that a relation definition belongs to the path.
+  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
+      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
+    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+    if (tmp != rd) {
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
+    }
+  }
+}
diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/package-info.java b/opendj-admin/src/main/java/org/opends/server/admin/server/package-info.java
new file mode 100644
index 0000000..45f84ff
--- /dev/null
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/package-info.java
@@ -0,0 +1,41 @@
+/*
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+
+
+/**
+ * Server-side administration interface.
+ * <p>
+ * This package contains classes and interfaces which internal
+ * directory server components are expected to use in order to
+ * access the server's current configuration and register
+ * to be notified when the configuration changes.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.PRIVATE)
+package org.opends.server.admin.server;
+

--
Gitblit v1.10.0