From 07541fa5b77a51759b23eac2308255651eb5cd93 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Thu, 06 Sep 2007 00:26:13 +0000
Subject: [PATCH] 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.

---
 opends/src/messages/messages/admin.properties                                                 |    2 
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java |   53 +++++++++++++
 opends/resource/admin/property-types/aggregation.xsl                                          |   11 ++
 opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java                  |  120 ++++++++++++++++++++++++-----
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java       |    7 +
 5 files changed, 168 insertions(+), 25 deletions(-)

diff --git a/opends/resource/admin/property-types/aggregation.xsl b/opends/resource/admin/property-types/aggregation.xsl
index 158609c..b0c2a6a 100644
--- a/opends/resource/admin/property-types/aggregation.xsl
+++ b/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.
   -->
diff --git a/opends/src/messages/messages/admin.properties b/opends/src/messages/messages/admin.properties
index af43615..5089f2e 100644
--- a/opends/src/messages/messages/admin.properties
+++ b/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"
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java b/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
index 56bbccc..35f235f 100644
--- a/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
+++ b/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.
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
index 4a5780c..44dafc4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
+++ b/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);
   }
 
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
index 3641a92..78048c0 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
+++ b/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

--
Gitblit v1.10.0