opends/resource/admin/admin.xsd
@@ -115,6 +115,21 @@ </xsd:documentation> </xsd:annotation> </xsd:element> <xsd:element name="property-override" type="tns:property-override-type"> <xsd:annotation> <xsd:documentation> Overrides a property definition inherited from a parent managed object definition. Using a property override it is possible to modify the behavior of an inherited property definition in a non-critical way. For example, a managed object definition might override the default behavior of an inherited Java implementation class property so that new instances are created with the correct default implementation class. </xsd:documentation> </xsd:annotation> </xsd:element> <xsd:element name="property-reference" type="tns:property-reference-type"> <xsd:annotation> @@ -407,6 +422,58 @@ </xsd:annotation> </xsd:attribute> </xsd:complexType> <xsd:complexType name="property-override-type"> <xsd:annotation> <xsd:documentation> Overrides a property definition inherited from a parent managed object definition. Using a property override it is possible to modify the behavior of an inherited property definition in a non-critical way. For example, a managed object definition might override the default behavior of an inherited Java implementation class property so that new instances are created with the correct default implementation class. </xsd:documentation> </xsd:annotation> <xsd:sequence> <xsd:element name="requires-admin-action" type="tns:admin-action-type" minOccurs="0"> <xsd:annotation> <xsd:documentation> Optionally override the administrative action defined in the overridden property definition. An administrative action defines an optional action which administators must perform after they have modified this 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. </xsd:documentation> </xsd:annotation> </xsd:element> <xsd:element name="default-behavior" type="tns:default-type" minOccurs="0"> <xsd:annotation> <xsd:documentation> Optionally override the default behavior defined in the overridden property definition. The default behavior is applicable when the property has no values specified. All properties must have a default behavior defined unless they are mandatory. </xsd:documentation> </xsd:annotation> </xsd:element> </xsd:sequence> <xsd:attribute name="name" type="tns:name-type" use="required"> <xsd:annotation> <xsd:documentation> The name of the overridden property. </xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:complexType> <xsd:complexType name="relation-type"> <xsd:annotation> <xsd:documentation> opends/resource/admin/metaMO.xsl
@@ -594,13 +594,8 @@ <xsl:value-of select="' builder.setOption(PropertyOption.HIDDEN);
'" /> </xsl:if> <xsl:choose> <xsl:when test="@mandatory='true'"> <xsl:value-of select="concat(' builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<', $value-type,'>());
')" /> </xsl:when> <xsl:otherwise> <xsl:if test="not(adm:default-behavior)"> <xsl:if test="not(@mandatory='true') and not(adm:default-behavior)"> <xsl:message terminate="yes"> <xsl:value-of select="concat('No default behavior defined for non-mandatory property "', @name, @@ -608,7 +603,7 @@ </xsl:message> </xsl:if> <xsl:choose> <xsl:when test="adm:default-behavior/adm:undefined"> <xsl:when test="not(adm:default-behavior) or adm:default-behavior/adm:undefined"> <xsl:value-of select="concat(' builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<', $value-type,'>());
')" /> </xsl:when> @@ -674,8 +669,6 @@ </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> <xsl:call-template name="get-property-definition-ctor" /> <xsl:value-of select="concat(' PD_', $java-prop-name, ' = builder.getInstance();
')" /> @@ -1066,7 +1059,8 @@ </xsl:choose> <xsl:value-of select="' value)'" /> <xsl:if test="@read-only='true'"> <xsl:value-of select="' throws PropertyIsReadOnlyException'" /> <xsl:value-of select="' throws PropertyIsReadOnlyException'" /> </xsl:if> <xsl:value-of select="concat(' {
' , @@ -1781,7 +1775,9 @@ <import>java.util.Collection</import> </xsl:if> <xsl:if test="$this-all-properties[@read-only='true']"> <import>org.opends.server.admin.PropertyIsReadOnlyException</import> <import> org.opends.server.admin.PropertyIsReadOnlyException </import> </xsl:if> </xsl:otherwise> </xsl:choose> opends/resource/admin/preprocessor.xsl
@@ -208,11 +208,15 @@ <!-- Copy all inherited properties. --> <xsl:copy-of select="$hierarchy/adm:managed-object/adm:property" /> <xsl:variable name="property-overrides" select="adm:property-override" /> <xsl:copy-of select="$hierarchy/adm:managed-object/adm:property[not(@name=$property-overrides/@name)]" /> <!-- Copy all local properties. --> <xsl:apply-templates select="adm:property|adm:property-reference" <xsl:apply-templates select="adm:property|adm:property-reference|adm:property-override" mode="pre-process"> <xsl:with-param name="moname" select="@name" /> <xsl:with-param name="mopackage" select="@package" /> @@ -463,6 +467,113 @@ </xsl:element> </xsl:template> <!-- Pre-process a property override pulling in the inherited property definition and by adding a "preprocessor" profile which contains information about where the property was redefined. --> <xsl:template match="adm:property-override" mode="pre-process"> <xsl:param name="mopackage" select="/.." /> <xsl:param name="moname" select="/.." /> <xsl:param name="hierarchy" /> <!-- Make sure that this property override does not have the same name as another property override in this managed object. --> <xsl:variable name="name" select="@name" /> <xsl:if test="../adm:property-override[@name=$name][2]"> <xsl:message terminate="yes"> <xsl:value-of select="concat('Property override ', @name, ' is already overridden in this managed object')" /> </xsl:message> </xsl:if> <!-- Make sure that this property overrides an existing property. --> <xsl:if test="not($hierarchy/adm:managed-object/adm:property[@name=$name])"> <xsl:message terminate="yes"> <xsl:value-of select="concat('Cannot find inherited property ', @name, ' for property override')" /> </xsl:message> </xsl:if> <!-- Copy the inherited property definition taking care to override the default behavior and admin action if required. --> <xsl:variable name="property" select="$hierarchy/adm:managed-object/adm:property[@name=$name]" /> <xsl:element name="adm:property"> <xsl:copy-of select="$property/@*" /> <xsl:apply-templates select="$property/adm:TODO | $property/adm:synopsis | $property/adm:description" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> <xsl:choose> <xsl:when test="adm:requires-admin-action"> <xsl:apply-templates select="adm:requires-admin-action" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$property/adm:requires-admin-action" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> <xsl:choose> <xsl:when test="adm:default-behavior"> <xsl:apply-templates select="adm:default-behavior" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$property/adm:default-behavior" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> <xsl:apply-templates select="$property/adm:syntax | $property/adm:profile[@name!='preprocessor']" mode="pre-process"> <xsl:with-param name="mopackage" select="$mopackage" /> <xsl:with-param name="moname" select="$moname" /> <xsl:with-param name="hierarchy" select="$hierarchy" /> </xsl:apply-templates> <!-- Now append the preprocessor profile. --> <xsl:element name="adm:profile"> <xsl:attribute name="name"> <xsl:value-of select="'preprocessor'" /> </xsl:attribute> <xsl:element name="admpp:managed-object"> <xsl:attribute name="name"> <xsl:value-of select="$moname" /> </xsl:attribute> <xsl:attribute name="package"> <xsl:value-of select="$mopackage" /> </xsl:attribute> </xsl:element> </xsl:element> </xsl:element> </xsl:template> <!-- Pre-process a relation, merging information from the referenced managed object where required, and by adding a "preprocessor" profile which contains information about where the relation was defined. opends/src/admin/defn/org/opends/server/admin/std/JMXConnectionHandlerConfiguration.xml
@@ -25,7 +25,6 @@ ! ! Portions Copyright 2007 Sun Microsystems, Inc. ! --> <adm:managed-object name="jmx-connection-handler" plural-name="jmx-connection-handlers" package="org.opends.server.admin.std" extends="connection-handler" @@ -44,6 +43,15 @@ <ldap:superior>ds-cfg-connection-handler</ldap:superior> </ldap:object-class> </adm:profile> <adm:property-override name="java-implementation-class"> <adm:default-behavior> <adm:defined> <adm:value> org.opends.server.protocols.jmx.JmxConnectionHandler </adm:value> </adm:defined> </adm:default-behavior> </adm:property-override> <adm:property-reference name="listen-port" /> <adm:property-reference name="use-ssl" /> <adm:property-reference name="ssl-cert-nickname" /> opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml
@@ -25,7 +25,6 @@ ! ! Portions Copyright 2007 Sun Microsystems, Inc. ! --> <adm:managed-object name="ldap-connection-handler" plural-name="ldap-connection-handlers" package="org.opends.server.admin.std" extends="connection-handler" @@ -44,6 +43,15 @@ <ldap:superior>ds-cfg-connection-handler</ldap:superior> </ldap:object-class> </adm:profile> <adm:property-override name="java-implementation-class"> <adm:default-behavior> <adm:defined> <adm:value> org.opends.server.protocols.ldap.LDAPConnectionHandler </adm:value> </adm:defined> </adm:default-behavior> </adm:property-override> <adm:property-reference name="listen-port" /> <adm:property-reference name="use-ssl" /> <adm:property-reference name="ssl-cert-nickname" /> opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
@@ -73,6 +73,14 @@ // definition. private final Map<String, RelationDefinition<?, ?>> relationDefinitions; // 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 managed object definitions which inherit from this definition. private final Map<String, AbstractManagedObjectDefinition<? extends C, ? extends S>> children; @@ -94,12 +102,23 @@ this.parent = parent; this.propertyDefinitions = new HashMap<String, PropertyDefinition<?>>(); this.relationDefinitions = new HashMap<String, RelationDefinition<?,?>>(); this.allPropertyDefinitions = new HashMap<String, PropertyDefinition<?>>(); this.allRelationDefinitions = new HashMap<String, RelationDefinition<?, ?>>(); this.children = new HashMap<String, AbstractManagedObjectDefinition<? extends C, ? extends S>>(); // If we have a parent definition then inherit its features. if (parent != null) { parent.children.put(name, this); for (PropertyDefinition<?> pd : parent.getAllPropertyDefinitions()) { allPropertyDefinitions.put(pd.getName(), pd); } for (RelationDefinition<?, ?> rd : parent.getAllRelationDefinitions()) { allRelationDefinitions.put(rd.getName(), rd); } } } @@ -139,14 +158,7 @@ * object. */ public final Collection<PropertyDefinition<?>> getAllPropertyDefinitions() { if (parent == null) { return getPropertyDefinitions(); } else { List<PropertyDefinition<?>> list = new ArrayList<PropertyDefinition<?>>( propertyDefinitions.values()); list.addAll(parent.getAllPropertyDefinitions()); return Collections.unmodifiableCollection(list); } return Collections.unmodifiableCollection(allPropertyDefinitions.values()); } @@ -162,14 +174,7 @@ */ public final Collection<RelationDefinition<?, ?>> getAllRelationDefinitions() { if (parent == null) { return getRelationDefinitions(); } else { List<RelationDefinition<?, ?>> list = new ArrayList<RelationDefinition<?, ?>>(relationDefinitions.values()); list.addAll(parent.getAllRelationDefinitions()); return Collections.unmodifiableCollection(list); } return Collections.unmodifiableCollection(allRelationDefinitions.values()); } @@ -314,16 +319,11 @@ throw new IllegalArgumentException("null or empty property name"); } PropertyDefinition d = propertyDefinitions.get(name); PropertyDefinition d = allPropertyDefinitions.get(name); if (d == null) { if (parent != null) { return parent.getPropertyDefinition(name); } else { throw new IllegalArgumentException("property definition \"" + name + "\" not found"); } } return d; } @@ -365,16 +365,11 @@ throw new IllegalArgumentException("null or empty relation name"); } RelationDefinition d = relationDefinitions.get(name); RelationDefinition d = allRelationDefinitions.get(name); if (d == null) { if (parent != null) { return parent.getRelationDefinition(name); } else { throw new IllegalArgumentException("relation definition \"" + name + "\" not found"); } } return d; } @@ -497,30 +492,6 @@ /** * Determine whether this type of managed object has any property definitions. * * @return Returns <code>true</code> if this type of managed object has any * property definitions, <code>false</code> otherwise. */ public final boolean hasPropertyDefinitions() { return !propertyDefinitions.isEmpty(); } /** * Determine whether this type of managed object has any relation definitions. * * @return Returns <code>true</code> if this type of managed object has any * relation definitions, <code>false</code> otherwise. */ public final boolean hasRelationDefinitions() { return !relationDefinitions.isEmpty(); } /** * 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 @@ -579,6 +550,7 @@ String name = d.getName(); propertyDefinitions.put(name, d); allPropertyDefinitions.put(name, d); } @@ -596,6 +568,7 @@ String name = d.getName(); relationDefinitions.put(name, d); allRelationDefinitions.put(name, d); } opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -424,7 +424,8 @@ for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) { Property<?> p = properties.getProperty(pd); if (pd.hasOption(PropertyOption.MANDATORY) && p.isEmpty()) { if (pd.hasOption(PropertyOption.MANDATORY) && p.getEffectiveValues().isEmpty()) { exceptions.add(new PropertyIsMandatoryException(pd)); } } @@ -941,10 +942,19 @@ private <T> void encodeProperty(Attribute attribute, PropertyDefinition<T> pd, PropertySet properties) { Property<T> p = properties.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 (T value : p.getEffectiveValues()) { attribute.add(pd.encodeValue(value)); } } else { for (T value : p.getPendingValues()) { attribute.add(pd.encodeValue(value)); } } } opends/tests/unit-tests-testng/src/server/org/opends/server/admin/AbstractManagedObjectDefinitionTest.java
@@ -32,7 +32,13 @@ import static org.testng.Assert.*; import java.util.Collection; import java.util.Collections; import org.opends.server.TestCaseUtils; import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn; import org.opends.server.admin.std.meta.JMXConnectionHandlerCfgDefn; import org.opends.server.admin.std.meta.LDAPConnectionHandlerCfgDefn; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -78,6 +84,21 @@ /** * Sets up tests * * @throws Exception * If the server could not be initialized. */ @BeforeClass public void setUp() throws Exception { // This test suite depends on having the schema available, so // we'll start the server. TestCaseUtils.startServer(); } /** * @return data for testIsChildOf. */ @DataProvider(name = "testIsChildOf") @@ -212,4 +233,69 @@ assertEquals(children.size(), 0); } /** * Tests that overridden properties work properly. FIXME: should not * use Connection Handlers - should define our own definitions. * <p> * Check that the generic connection handler definition does not * have a default behavior defined for the * java-implementation-class. */ @Test public void testPropertyOverride1() { AbstractManagedObjectDefinition<?, ?> d = ConnectionHandlerCfgDefn .getInstance(); PropertyDefinition<?> pd = d .getPropertyDefinition("java-implementation-class"); DefaultBehaviorProvider<?> dbp = pd.getDefaultBehaviorProvider(); assertEquals(dbp.getClass(), UndefinedDefaultBehaviorProvider.class); } /** * Tests that overridden properties work properly. FIXME: should not * use Connection Handlers - should define our own definitions. * <p> * Check that the LDAP connection handler definition does have a * default behavior defined for the java-implementation-class. */ @Test public void testPropertyOverride2() { AbstractManagedObjectDefinition<?, ?> d = LDAPConnectionHandlerCfgDefn .getInstance(); PropertyDefinition<?> pd = d .getPropertyDefinition("java-implementation-class"); DefaultBehaviorProvider<?> dbp = pd.getDefaultBehaviorProvider(); assertEquals(dbp.getClass(), DefinedDefaultBehaviorProvider.class); DefinedDefaultBehaviorProvider<?> ddbp = (DefinedDefaultBehaviorProvider<?>) dbp; assertEquals(ddbp.getDefaultValues(), Collections .singleton("org.opends.server.protocols.ldap.LDAPConnectionHandler")); } /** * Tests that overridden properties work properly. FIXME: should not * use Connection Handlers - should define our own definitions. * <p> * Check that the JMX connection handler definition does have a * default behavior defined for the java-implementation-class. */ @Test public void testPropertyOverride3() { AbstractManagedObjectDefinition<?, ?> d = JMXConnectionHandlerCfgDefn .getInstance(); PropertyDefinition<?> pd = d .getPropertyDefinition("java-implementation-class"); DefaultBehaviorProvider<?> dbp = pd.getDefaultBehaviorProvider(); assertEquals(dbp.getClass(), DefinedDefaultBehaviorProvider.class); DefinedDefaultBehaviorProvider<?> ddbp = (DefinedDefaultBehaviorProvider<?>) dbp; assertEquals(ddbp.getDefaultValues(), Collections .singleton("org.opends.server.protocols.jmx.JmxConnectionHandler")); } }