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 "equal to" 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
+ * "compatible" 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>↓ <a href=\"#" + basicPropName + "\">"
+ + basicPropName + "</a></td>\n";
+ } else if ((basicPropsArray.length == 0) && (ii == 0)) {
+ basicHtmlCell = " <td> 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>↓ <a href=\"#" + advancedPropName +
+ "\">" + advancedPropName + "</a></td>\n";
+ } else if ((advancedPropsArray.length == 0) && (ii == 0)) {
+ advancedHtmlCell = " <td> 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;\">« </span>" +
+ "Back to " +
+ DynamicConstants.PRODUCT_NAME + " Home</a></span> </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