From 2ba8e81b3d42cdacb4bf15d77ca681660595ee46 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Tue, 11 Sep 2007 23:37:29 +0000
Subject: [PATCH] Partial fix for issue 1449: improve server-side referential integrity support.
---
opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java | 307 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 297 insertions(+), 10 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java b/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
index 77b4046..f06b6c3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -34,14 +34,21 @@
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.SortedSet;
import org.opends.messages.Message;
import org.opends.server.admin.client.ClientConstraintHandler;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+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.ServerManagementContext;
import org.opends.server.config.ConfigException;
+import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
@@ -285,11 +292,124 @@
/**
+ * A change listener which prevents the named component from being
+ * disabled.
+ */
+ private class ReferentialIntegrityChangeListener implements
+ ConfigurationChangeListener<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(S configuration) {
+ throw new IllegalStateException("Attempting to disable a referenced "
+ + configuration.definition().getUserFriendlyName());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(S configuration,
+ List<Message> unacceptableReasons) {
+ // Always prevent the referenced component from being
+ // disabled.
+ PropertyProvider provider = configuration.properties();
+ Collection<Boolean> values = provider
+ .getPropertyValues(getTargetEnabledPropertyDefinition());
+ if (values.iterator().next() == false) {
+ unacceptableReasons.add(message);
+ return false;
+ }
+ return true;
+ }
+
+
+
+ // 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) {
+ throw new IllegalStateException("Attempting to delete a referenced "
+ + configuration.definition().getUserFriendlyName());
+ }
+
+
+
+ /**
+ * {@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 static class ServerHandler
- <C extends ConfigurationClient, S extends Configuration>
- extends ServerConstraintHandler {
+ private class ServerHandler extends ServerConstraintHandler {
// The associated property definition.
private final AggregationPropertyDefinition<C, S> pd;
@@ -311,23 +431,178 @@
Collection<Message> unacceptableReasons) throws ConfigException {
SortedSet<String> names = managedObject.getPropertyValues(pd);
ServerManagementContext context = ServerManagementContext.getInstance();
- boolean isUsable = true;
+ BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
+ BooleanPropertyDefinition spd = pd.getSourceEnabledPropertyDefinition();
+ Message thisUFN = managedObject.getManagedObjectDefinition()
+ .getUserFriendlyName();
+ String thisDN = managedObject.getDN().toString();
+ Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+ boolean isUsable = true;
for (String name : names) {
ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ String thatDN = path.toDN().toString();
+
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());
+ .getName(), thisUFN, thisDN, thatUFN, thatDN);
unacceptableReasons.add(msg);
isUsable = false;
+ } else if (tpd != null) {
+ // Check that the referenced component is enabled.
+ ServerManagedObject<? extends S> ref = context.getManagedObject(path);
+
+ if (spd != null) {
+ // Target must be enabled but only if the source is
+ // enabled.
+ if (managedObject.getPropertyValue(spd)
+ && !ref.getPropertyValue(tpd)) {
+ Message msg = ERR_SERVER_REFINT_SOURCE_ENABLED_TARGET_DISABLED
+ .get(name, pd.getName(), thisUFN, thisDN, thatUFN, thatDN);
+ unacceptableReasons.add(msg);
+ isUsable = false;
+ }
+ } else {
+ // Target must always be enabled.
+ if (!ref.getPropertyValue(tpd)) {
+ Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name, pd
+ .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.
+ BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
+ BooleanPropertyDefinition spd = pd.getSourceEnabledPropertyDefinition();
+ Message thisUFN = managedObject.getManagedObjectDefinition()
+ .getUserFriendlyName();
+ String thisDN = managedObject.getDN().toString();
+ Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+
+ // Referenced managed objects will only need a change listener
+ // if they have can be disabled.
+ boolean needsChangeListeners;
+ if (tpd != null) {
+ if (spd == null) {
+ needsChangeListeners = true;
+ } else {
+ needsChangeListeners = managedObject.getPropertyValue(spd);
+ }
+ } else {
+ needsChangeListeners = false;
+ }
+
+ // Delete listeners need to be registered against the parent
+ // entry of the referenced components.
+ ServerManagementContext context = ServerManagementContext.getInstance();
+ ManagedObjectPath<?, ?> parentPath = pd.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(pd)) {
+ ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ DN dn = path.toDN();
+ String thatDN = dn.toString();
+
+ // Register the delete listener.
+ Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN, pd
+ .getName(), thisUFN, thisDN);
+ ReferentialIntegrityDeleteListener dl =
+ new ReferentialIntegrityDeleteListener(dn, msg);
+ parent.registerDeleteListener(pd.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, pd
+ .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 = pd.getParentPath();
+ ServerManagedObject<?> parent = context.getManagedObject(parentPath);
+ if (deleteListeners.containsKey(dn)) {
+ for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
+ parent.deregisterDeleteListener(pd.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);
+ }
}
@@ -349,11 +624,23 @@
* @return Returns the new aggregation property definition builder.
*/
public static <C extends ConfigurationClient, S extends Configuration>
- Builder<C, S> createBuilder(
+ 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 final ManagedObjectPath<?, ?> parentPath;
@@ -502,7 +789,7 @@
* {@inheritDoc}
*/
public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
- ServerConstraintHandler handler = new ServerHandler<C, S>(this);
+ ServerConstraintHandler handler = new ServerHandler(this);
return Collections.singleton(handler);
}
--
Gitblit v1.10.0