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

matthew_swift
06.26.2007 07541fa5b77a51759b23eac2308255651eb5cd93
Add partial support for performing server-side referential integrity. This change adds a constraint which prevents configuration of dangling references. A subsequent change will add a constraint which will prevent removal of referenced components.
5 files modified
193 ■■■■ changed files
opends/resource/admin/property-types/aggregation.xsl 11 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/admin.properties 2 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java 120 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java 7 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java 53 ●●●●● patch | view | raw | blame | history
opends/resource/admin/property-types/aggregation.xsl
@@ -29,7 +29,8 @@
  <!-- 
    Templates for processing aggregation properties.
  -->
  <xsl:template match="adm:aggregation" mode="java-definition-imports">
  <xsl:template match="adm:aggregation"
    mode="java-definition-imports">
    <xsl:element name="import">
      <xsl:call-template name="get-definition-package" />
      <xsl:value-of select="'.client.'" />
@@ -99,6 +100,14 @@
                       normalize-space(@target-enabled-property-name), '&quot;);&#xa;')" />
    </xsl:if>
  </xsl:template>
  <xsl:template match="adm:aggregation"
    mode="java-definition-post-ctor">
    <xsl:value-of select="'      INSTANCE.registerConstraint(PD_'" />
    <xsl:call-template name="name-to-java-constant">
      <xsl:with-param name="value" select="../../@name" />
    </xsl:call-template>
    <xsl:value-of select="');&#xa;'" />
  </xsl:template>
  <!--
    Gets the Java client configuration interface for the referenced type.
  -->
opends/src/messages/messages/admin.properties
@@ -259,3 +259,5 @@
 constraint violation occurred: %s
SEVERE_ERR_CONSTRAINT_VIOLATION_EXCEPTION_PLURAL_113=The following \
 constraint violations occurred: %s
SEVERE_ERR_SERVER_REFINT_DANGLING_REFERENCE_114=The value "%s" in \
 property "%s" in the %s in entry "%s" refers to a non-existent %s at "%s"
opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -28,10 +28,20 @@
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.util.Validator.*;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.SortedSet;
import org.opends.messages.Message;
import org.opends.server.admin.client.ClientConstraintHandler;
import org.opends.server.admin.server.ServerConstraintHandler;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.config.ConfigException;
import org.opends.server.types.DN;
@@ -78,7 +88,7 @@
 */
public final class AggregationPropertyDefinition
    <C extends ConfigurationClient, S extends Configuration>
    extends PropertyDefinition<String> {
    extends PropertyDefinition<String> implements Constraint {
  /**
   * An interface for incrementally constructing aggregation property
@@ -95,6 +105,9 @@
      <C extends ConfigurationClient, S extends Configuration>
      extends AbstractBuilder<String, AggregationPropertyDefinition<C, S>> {
    // The type of referenced managed objects.
    private AbstractManagedObjectDefinition<?, ?> cd = null;
    // The name of the managed object which is the parent of the
    // aggregated managed objects.
    private ManagedObjectPath<?, ?> p = null;
@@ -103,9 +116,6 @@
    // contains the aggregated managed objects.
    private String rdName = null;
    // The type of referenced managed objects.
    private AbstractManagedObjectDefinition<?, ?> cd = null;
    // The optional name of a boolean "enabled" property in this
    // managed object. When this property is true, the enabled
    // property in the aggregated managed object must also be true.
@@ -127,6 +137,23 @@
    /**
     * Sets the definition of the type of referenced managed objects.
     * <p>
     * This must be defined before the property definition can be
     * built.
     *
     * @param d
     *          The definition of the type of referenced managed
     *          objects.
     */
    public final void setManagedObjectDefinition(
        AbstractManagedObjectDefinition<C, S> d) {
      this.cd = d;
    }
    /**
     * Sets the name of the managed object which is the parent of the
     * aggregated managed objects.
     * <p>
@@ -161,23 +188,6 @@
    /**
     * Sets the definition of the type of referenced managed objects.
     * <p>
     * This must be defined before the property definition can be
     * built.
     *
     * @param d
     *          The definition of the type of referenced managed
     *          objects.
     */
    public final void setManagedObjectDefinition(
        AbstractManagedObjectDefinition<C, S> d) {
      this.cd = d;
    }
    /**
     * Sets the optional boolean "enabled" property in this managed
     * object. When this property is true, the enabled property in the
     * aggregated managed object must also be true.
@@ -275,6 +285,54 @@
  /**
   * The server-side constraint handler implementation.
   */
  private static class ServerHandler
      <C extends ConfigurationClient, S extends Configuration>
      extends ServerConstraintHandler {
    // The associated property definition.
    private final AggregationPropertyDefinition<C, S> pd;
    // Creates a new server-side constraint handler.
    private ServerHandler(AggregationPropertyDefinition<C, S> pd) {
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isUsable(ServerManagedObject<?> managedObject,
        Collection<Message> unacceptableReasons) throws ConfigException {
      SortedSet<String> names = managedObject.getPropertyValues(pd);
      ServerManagementContext context = ServerManagementContext.getInstance();
      boolean isUsable = true;
      for (String name : names) {
        ManagedObjectPath<C, S> path = pd.getChildPath(name);
        if (!context.managedObjectExists(path)) {
          Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name, pd
              .getName(), managedObject.getManagedObjectDefinition()
              .getUserFriendlyName(), managedObject.getDN().toString(), pd
              .getRelationDefinition().getUserFriendlyName(), path.toDN()
              .toString());
          unacceptableReasons.add(msg);
          isUsable = false;
        }
      }
      return isUsable;
    }
  }
  /**
   * Creates an aggregation property definition builder.
   *
   * @param <C>
@@ -405,6 +463,16 @@
  /**
   * {@inheritDoc}
   */
  public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
    // TODO: not yet implemented.
    return Collections.emptyList();
  }
  /**
   * Gets the name of the managed object which is the parent of the
   * aggregated managed objects.
   *
@@ -431,6 +499,16 @@
  /**
   * {@inheritDoc}
   */
  public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
    ServerConstraintHandler handler = new ServerHandler<C, S>(this);
    return Collections.singleton(handler);
  }
  /**
   * Gets the optional boolean "enabled" property in this managed
   * object. When this property is true, the enabled property in the
   * aggregated managed object must also be true.
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
@@ -48,15 +48,15 @@
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.std.client.ConnectionHandlerCfgClient;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.std.client.ConnectionHandlerCfgClient;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.ConnectionHandlerCfg;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -119,6 +119,7 @@
      builder.setManagedObjectDefinition(ConnectionHandlerCfgDefn.getInstance());
      PD_AGGREGATION_PROPERTY = builder.getInstance();
      INSTANCE.registerPropertyDefinition(PD_AGGREGATION_PROPERTY);
      INSTANCE.registerConstraint(PD_AGGREGATION_PROPERTY);
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
@@ -34,6 +34,7 @@
import javax.naming.ldap.LdapName;
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.IllegalPropertyValueStringException;
@@ -150,6 +151,23 @@
    assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler",
        "LDAP Connection Handler");
  }
  // Test child 5 LDIF.
  private static final String[] TEST_CHILD_5 = new String[] {
      "dn: cn=test child 5,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 5",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "ds-cfg-backend-base-dn: cn=BAD Connection Handler 1, cn=connection handlers, cn=config",
      "ds-cfg-backend-base-dn: cn=BAD Connection Handler 2, cn=connection handlers, cn=config",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config"
  };
  // Test LDIF.
  private static final String[] TEST_LDIF = new String[] {
@@ -332,6 +350,41 @@
  /**
   * Tests that aggregation is rejected by a constraint violation when
   * the DN values are dangling.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationDanglingReference() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_5);
    try {
      TestParentCfg parent = getParent("test parent 1");
      parent.getTestChild("test child 5");
      Assert
          .fail("Unexpectedly added test child 5 when it had a dangling reference");
    } catch (ConfigException e) {
      // Check that we have a constraint violation as the cause.
      Throwable cause = e.getCause();
      if (cause instanceof ConstraintViolationException) {
        ConstraintViolationException cve = (ConstraintViolationException) cause;
        Collection<Message> causes = cve.getMessages();
        Assert.assertEquals(causes.size(), 2);
      } else {
        // Got an unexpected cause.
        throw e;
      }
    } finally {
      deleteSubtree("cn=test child 5,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }
  // Asserts that the actual set of DNs contains the expected values.
  private void assertSetEquals(SortedSet<String> actual, String... expected) {
    SortedSet<String> values = new TreeSet<String>(TestChildCfgDefn