From f2886f88f7ebebea3a925d0e69bbb8f971ffbb3d Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 24 Sep 2007 20:38:02 +0000
Subject: [PATCH] Fix issue 1449: aggregation support.
---
opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java | 473 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 405 insertions(+), 68 deletions(-)
diff --git a/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java b/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
index fd15e40..c1e7b96 100644
--- a/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
+++ b/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
@@ -41,7 +41,12 @@
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.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerConstraintHandler;
@@ -143,6 +148,27 @@
/**
+ * Registers a boolean "enabled" property in this managed object.
+ * When all the registered properties are true, the enabled
+ * property in the aggregated managed object must also be true.
+ * <p>
+ * By default no source properties are defined which indicates
+ * that the target property must always be true. When there is one
+ * or more source properties defined, a target property must also
+ * be defined.
+ *
+ * @param sourceEnabledPropertyName
+ * The optional boolean "enabled" property in this
+ * managed object.
+ */
+ public final void addSourceEnabledPropertyName(
+ String sourceEnabledPropertyName) {
+ this.sourceEnabledPropertyNames.add(sourceEnabledPropertyName);
+ }
+
+
+
+ /**
* Sets the name of the managed object which is the parent of the
* aggregated managed objects.
* <p>
@@ -178,27 +204,6 @@
/**
- * Registers a boolean "enabled" property in this managed object.
- * When all the registered properties are true, the enabled
- * property in the aggregated managed object must also be true.
- * <p>
- * By default no source properties are defined which indicates
- * that the target property must always be true. When there is one
- * or more source properties defined, a target property must also
- * be defined.
- *
- * @param sourceEnabledPropertyName
- * The optional boolean "enabled" property in this
- * managed object.
- */
- public final void addSourceEnabledPropertyName(
- String sourceEnabledPropertyName) {
- this.sourceEnabledPropertyNames.add(sourceEnabledPropertyName);
- }
-
-
-
- /**
* Sets the optional boolean "enabled" property in the aggregated
* managed object. This property must not be false while the
* aggregated managed object is referenced.
@@ -240,8 +245,8 @@
if (!sourceEnabledPropertyNames.isEmpty()
&& targetEnabledPropertyName == null) {
throw new IllegalStateException(
- "One or more source properties defined but " +
- "target property is undefined");
+ "One or more source properties defined but "
+ + "target property is undefined");
}
return new AggregationPropertyDefinition<C, S>(d, propertyName, options,
@@ -393,42 +398,31 @@
*/
private class ServerHandler 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);
+ SortedSet<String> names = managedObject
+ .getPropertyValues(AggregationPropertyDefinition.this);
ServerManagementContext context = ServerManagementContext.getInstance();
- BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
- List<BooleanPropertyDefinition> spdlist = pd
- .getSourceEnabledPropertyDefinitions();
+ BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
+ List<BooleanPropertyDefinition> spdlist =
+ getSourceEnabledPropertyDefinitions();
Message thisUFN = managedObject.getManagedObjectDefinition()
.getUserFriendlyName();
String thisDN = managedObject.getDN().toString();
- Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+ Message thatUFN = getRelationDefinition().getUserFriendlyName();
boolean isUsable = true;
for (String name : names) {
- ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ ManagedObjectPath<C, S> path = getChildPath(name);
String thatDN = path.toDN().toString();
if (!context.managedObjectExists(path)) {
- Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name, pd
- .getName(), thisUFN, thisDN, thatUFN, thatDN);
+ Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name,
+ getName(), thisUFN, thisDN, thatUFN, thatDN);
unacceptableReasons.add(msg);
isUsable = false;
} else if (tpd != null) {
@@ -448,15 +442,15 @@
if (isRequired && !ref.getPropertyValue(tpd)) {
Message msg = ERR_SERVER_REFINT_SOURCE_ENABLED_TARGET_DISABLED
- .get(name, pd.getName(), thisUFN, thisDN, thatUFN, thatDN);
+ .get(name, 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);
+ Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name,
+ getName(), thisUFN, thisDN, thatUFN, thatDN);
unacceptableReasons.add(msg);
isUsable = false;
}
@@ -484,13 +478,13 @@
// Add change and delete listeners against all referenced
// components.
- BooleanPropertyDefinition tpd = pd.getTargetEnabledPropertyDefinition();
- List<BooleanPropertyDefinition> spdlist = pd
- .getSourceEnabledPropertyDefinitions();
+ BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
+ List<BooleanPropertyDefinition> spdlist =
+ getSourceEnabledPropertyDefinitions();
Message thisUFN = managedObject.getManagedObjectDefinition()
.getUserFriendlyName();
String thisDN = managedObject.getDN().toString();
- Message thatUFN = pd.getRelationDefinition().getUserFriendlyName();
+ Message thatUFN = getRelationDefinition().getUserFriendlyName();
// Referenced managed objects will only need a change listener
// if they have can be disabled.
@@ -510,7 +504,7 @@
// Delete listeners need to be registered against the parent
// entry of the referenced components.
ServerManagementContext context = ServerManagementContext.getInstance();
- ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
+ ManagedObjectPath<?, ?> parentPath = getParentPath();
ServerManagedObject<?> parent = context.getManagedObject(parentPath);
// Create entries in the listener tables.
@@ -522,24 +516,25 @@
new LinkedList<ReferentialIntegrityChangeListener>();
changeListeners.put(managedObject.getDN(), clist);
- for (String name : managedObject.getPropertyValues(pd)) {
- ManagedObjectPath<C, S> path = pd.getChildPath(name);
+ 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, pd
- .getName(), thisUFN, thisDN);
+ Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN,
+ getName(), thisUFN, thisDN);
ReferentialIntegrityDeleteListener dl =
new ReferentialIntegrityDeleteListener(dn, msg);
- parent.registerDeleteListener(pd.getRelationDefinition(), dl);
+ 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, pd
- .getName(), thisUFN, thisDN);
+ msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN,
+ getName(), thisUFN, thisDN);
ReferentialIntegrityChangeListener cl =
new ReferentialIntegrityChangeListener(path, msg);
ref.registerChangeListener(cl);
@@ -562,11 +557,11 @@
// Delete listeners need to be deregistered against the parent
// entry of the referenced components.
- ManagedObjectPath<?, ?> parentPath = pd.getParentPath();
+ ManagedObjectPath<?, ?> parentPath = getParentPath();
ServerManagedObject<?> parent = context.getManagedObject(parentPath);
if (deleteListeners.containsKey(dn)) {
for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) {
- parent.deregisterDeleteListener(pd.getRelationDefinition(), dl);
+ parent.deregisterDeleteListener(getRelationDefinition(), dl);
}
deleteListeners.remove(dn);
}
@@ -601,6 +596,323 @@
/**
+ * 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 = true;
+ for (BooleanPropertyDefinition spd :
+ getSourceEnabledPropertyDefinitions()) {
+ if (!managedObject.getPropertyValue(spd)) {
+ needsEnabling = false;
+ }
+ }
+
+ // Check the referenced managed objects exist and, if required,
+ // are enabled.
+ boolean isAcceptable = true;
+ BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
+ 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 (tpd != null && needsEnabling) {
+ if (!ref.getPropertyValue(tpd)) {
+ 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 {
+
+ /**
+ * Instances of this class are used to search for all managed
+ * objects that contain a reference to the named managed object.
+ */
+ private class Finder implements
+ RelationDefinitionVisitor<Void, ManagedObject<?>> {
+
+ // Any authorization exceptions that were encountered.
+ private AuthorizationException ae = null;
+
+ // Any communication exceptions that were encountered.
+ private CommunicationException ce = null;
+
+ // The name of the managed object being deleted or modified.
+ private final String name;
+
+ // The collected list of referencing managed objects.
+ private final Collection<ManagedObject<?>> references;
+
+
+
+ // Private constructor.
+ private Finder(String name, Collection<ManagedObject<?>> references) {
+ this.name = name;
+ this.references = references;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Void visitInstantiable(InstantiableRelationDefinition<?, ?> rd,
+ ManagedObject<?> p) {
+ try {
+ for (String childName : p.listChildren(rd)) {
+ find(p.getChild(rd, childName));
+ }
+ } catch (AuthorizationException e) {
+ ae = e;
+ } catch (CommunicationException e) {
+ ce = e;
+ } catch (OperationsException e) {
+ // Ignore all other types of exception.
+ }
+ return null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Void visitOptional(OptionalRelationDefinition<?, ?> rd,
+ ManagedObject<?> p) {
+ try {
+ find(p.getChild(rd));
+ } catch (AuthorizationException e) {
+ ae = e;
+ } catch (CommunicationException e) {
+ ce = e;
+ } catch (OperationsException e) {
+ // Ignore all other types of exception.
+ }
+ return null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Void visitSingleton(SingletonRelationDefinition<?, ?> rd,
+ ManagedObject<?> p) {
+ try {
+ find(p.getChild(rd));
+ } catch (AuthorizationException e) {
+ ae = e;
+ } catch (CommunicationException e) {
+ ce = e;
+ } catch (OperationsException e) {
+ // Ignore all other types of exception.
+ }
+ return null;
+ }
+
+
+
+ private void find(ManagedObject<?> current)
+ throws AuthorizationException, CommunicationException {
+ // First check the current managed object to see if it
+ // contains a reference.
+ ManagedObjectDefinition<?, ?> mod = current
+ .getManagedObjectDefinition();
+ if (mod.isChildOf(getManagedObjectDefinition())) {
+ for (String value : current
+ .getPropertyValues(AggregationPropertyDefinition.this)) {
+ if (compare(value, name) == 0) {
+ references.add(current);
+ }
+ }
+ }
+
+ // Now check its children.
+ for (RelationDefinition<?, ?> rd : mod.getAllRelationDefinitions()) {
+ rd.accept(this, current);
+
+ if (ae != null) {
+ throw ae;
+ }
+
+ if (ce != null) {
+ throw ce;
+ }
+ }
+ }
+
+ }
+
+
+
+ /**
+ * {@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, 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.
+ BooleanPropertyDefinition tpd = getTargetEnabledPropertyDefinition();
+
+ // The referenced managed object cannot be disabled: always ok.
+ if (tpd == null) {
+ return true;
+ }
+
+ // The referenced managed object is enabled: always ok.
+ if (managedObject.getPropertyValue(tpd)) {
+ return true;
+ }
+
+ // The referenced managed object is disabled. Need to check for
+ // active references.
+ boolean isAcceptable = true;
+ for (ManagedObject<?> mo : findReferences(context, managedObject
+ .getManagedObjectPath().getName())) {
+ boolean needsEnabling = true;
+ for (BooleanPropertyDefinition spd :
+ getSourceEnabledPropertyDefinitions()) {
+ if (!mo.getPropertyValue(spd)) {
+ needsEnabling = false;
+ break;
+ }
+ }
+
+ if (needsEnabling) {
+ 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 Collection<ManagedObject<?>> findReferences(
+ ManagementContext context, String name) throws AuthorizationException,
+ CommunicationException {
+ List<ManagedObject<?>> references = new LinkedList<ManagedObject<?>>();
+ Finder finder = new Finder(name, references);
+ finder.find(context.getRootConfigurationManagedObject());
+ return references;
+ }
+ }
+
+
+
+ /**
* Creates an aggregation property definition builder.
*
* @param <C>
@@ -617,7 +929,7 @@
* @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);
}
@@ -760,8 +1072,8 @@
* {@inheritDoc}
*/
public Collection<ClientConstraintHandler> getClientConstraintHandlers() {
- // TODO: not yet implemented.
- return Collections.emptyList();
+ ClientConstraintHandler handler = new SourceClientHandler();
+ return Collections.singleton(handler);
}
@@ -796,7 +1108,7 @@
* {@inheritDoc}
*/
public Collection<ServerConstraintHandler> getServerConstraintHandlers() {
- ServerConstraintHandler handler = new ServerHandler(this);
+ ServerConstraintHandler handler = new ServerHandler();
return Collections.singleton(handler);
}
@@ -923,18 +1235,43 @@
sourceEnabledProperties.add(BooleanPropertyDefinition.class.cast(pd));
}
+ d = relationDefinition.getChildDefinition();
if (targetEnabledPropertyName == null) {
targetEnabledProperty = null;
} else {
- PropertyDefinition<?> pd;
-
- d = relationDefinition.getChildDefinition();
- pd = d.getPropertyDefinition(targetEnabledPropertyName);
+ PropertyDefinition<?> pd = d
+ .getPropertyDefinition(targetEnabledPropertyName);
// Runtime cast is required to workaround a
// bug in JDK versions prior to 1.5.0_08.
targetEnabledProperty = BooleanPropertyDefinition.class.cast(pd);
}
+
+ // 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();
+ }
+
+ };
+
+ d.registerConstraint(constraint);
}
}
--
Gitblit v1.10.0