NDS provides a schema that governs the relationships among NDS objects and the valid attributes and values for each object. You can extend, or modify, the schema, by defining and adding your own object classes and attributes, or modifying existing ones.
You can manage the NDS schema from ConsoleOne, making any schema changes necessary for your snap-ins to run. ConsoleOne allows you to manage the schema by:
NDS object classes and attributes are defined using specific syntaxes. For information about NDS attribute syntaxes, see Attribute Syntax Definitions.
As mentioned earlier, the NDS schema defines the directory and its objects and attributes. To access a schema associated with a given NDS implementation, you need to retrieve that namespace.
//Get the namespace from the given object entry in ConsoleOne. NDSNamespace ns = (NDSNamespace)objEntry.getObjectType().getNamespace();
You can retrieve the namespace as follows if you are not using ConsoleOne:
//If not using ConsoleOne the namespace is created like this: NDSNamespaceImpl ns = new NDSNamespaceImpl();
Given the namespace, you can use the following method to retrieve its schema.
//Get a handle to the schema. SchemaDefinition schemaDef = nameSpace.getSchemaDefinition(objEntry);
The schema defines all valid object classes, their attributes, inheritance, and containment. To retrieve an object class definition, use the following method.
//Get the class definitions. ClassDefinition[] classDefs = schemaDef.getClassDefinitions();
The following example shows how you would retrieve the definition for the User object class.
First you specify the User object name when you call getClassDefinition.
//Example for class User.
NDSClassDefinition classDef = (NDSClassDefinition)schema.Def.getClassDefinition("User");
The isAttributeMandatory method returns a boolean for each attribute indicating if the attribute is mandatory.
//Is an attribute mandatory
boolean isMandatory = classDef.isAttributeMandatory("Object Class");
Next, the isNamedBy method returns a boolean for each attribute indicating if the attribute is a naming attribute.
//Is an attribute naming
boolean isNaming = classDef.isNamedBy("CN");
The isAttributeOptional method returns a boolean for each attribute indicating if the attribute is an optional attribute.
//Is an attribute optional
boolean isOptional = classDef.isAttributeOptional("Last Name");
The getSuperClasses method returns the super classes of the object.
//Get the super classes String[] superClasses = classDef.getSuperClasses();
The getContainmentClasses method returns an array of the object classes this class can contain. In this case, the User class cannot contain other classes.
//Get the containment classes String[] containmentClasses = classDef.getContainmentClasses()
Then, the canBeContainedBy method returns a boolean for every class that can contain the class being checked.
//See if the class can be contained by a particular class.
boolean canBeContained = classDef.canBeContainedBy("Organization");
//See if the class can be inherited from a particular class.
boolean isInheritedFrom = classDef.isInheritedFrom("Top");
The isContainerClass method returns a boolean indicating if the class is a container class.
//See if class is a container class. boolean isContainer = classDef.isContainerClass();
The isEffectiveClass method returns a boolean indicating if the class is an effective class, meaning that it can be instantiated in the directory. Some super classes are used for inheritance only and cannot be instantiated.
//See if the class can be instantiated directly boolean isEffective = classDef.isEffectiveClass();
The getMandatoryAttributes method returns an array containing the mandatory attributes, or those returning a boolean of true when it was checked by isMandatory.
//Get the mandatory attributes. AttributeDefinition[] mandatoryAttrs = classDef.getMandatoryAttributes();
The getNamingAt method returns an array containing the naming attributes for the class.
//Get the naming attributes. AttributeDefinition[] namingAttrs = classDef.getNamingAttributes();
The getOptionalAttributes method returns an array of optional attributes for the class.
//Get the optional attributes. AttributeDefinition[] optionalAttrs = classDef.getOptionalAttributes();
The schema also defines valid attributes and values, including access control and naming attributes. To retrieve attribute definitions, use the following method:
//Get the attribute definitions. AttributeDefinition[] attrDefs = schemaDef.getAttributeDefinitions();
The following code example shows how attribute definitions are retrieved, in this case the CN attribute, which is a naming attribute.
First, given the name of the attribute, you can retrieve the attribute definition.
//Example for attribute CN.
NDSAttributeDefinition attrDef = (NDSAttributeDefinition)schemaDef.getAttributeDefinition("CN");
Then, the isSized method returns a boolean indicating whether the the attribute is sized.
boolean isSized = attrDef.isSized();
Then the lower and upper bounds of the attribute in the database are retrieved.
long lower = attrDef.getLowerBound(); long upper = attrDef.getUpperBound();
Then the operation returns a boolean indicating whether the attribute:
boolean isHid = attrDef.isHidden(); boolean isRO = attrDef.isReadOnly(); boolean isSV = attrDef.isSingleValued(); boolean isN = attrDef.isNaming(); boolean isPRead = attrDef.isPublicRead(); boolean isSR = attrDef.isServerRead(); boolean isPReplica = attrDef.isPerReplica(); boolean isSI = attrDef.isSyncImmediate(); boolean isR = attrDef.isRemovable();
You can extend the schema by creating a new object class definition that meets the needs of your snap-in. As an object class is created from a combination of data, the following example will show how to create a new class:
First, you must name the class and flag it as an effective class. Effective classes are those whose objects can be instantiated in the directory.
//Put the class definition together. String className = new String("class1"); NDSClassFlags flags = new NDSClassFlags(NDSClassFlags.EFFECTIVE_CLASS);
Next, you build the containment names, or the array of object classes that this class be contained by.
//Build the class containment names. String[] classContainmentNames = null;
Next, String arrays are created that contain:
//Build the super class names.
String[] superClassNames = new String[1];
superClassNames[0] = new String("User");
//Build the mandatory attributes.
AttributeDefinition[] mandatoryAttributes = null;
//Put the class definition together.
String className = new String("class1");
NDSClassFlags flags = new NDSClassFlags(NDSClassFlags.EFFECTIVE_CLASS);
//Build the class containment names.
String[] classContainmentNames = null;
//Build the super class names.
String[] superClassNames = new String[1];
superClassNames[0] = new String("User");
//Build the mandatory attributes.
AttributeDefinition[] mandatoryAttributes =
//Build the naming attributes.
AttributeDefinition[] namingAttributes = null;
//Build the optional attributes.
AttributeDefinition[] optionalAttributes = null;
Next, the object class asn1 name is created.
//Build the asn1 name. byte[] asn1 = new byte[1];
The object class is created by passing in all of the created attributes.
classDef = new NDSClassDefinition(className, flags, classContainmentNames, superClassNames, mandatoryAttributes, namingAttributes, optionalAttributes, asn1);
Finally the class is created in the NDS database.
//Create the class definition. ((NDSSchemaDefinition)schemaDef).putClassDefinition(classDef);
An NDS attribute is a combination of data governing how the attribute can be used.
To create a valid NDS attribute, first you must name the new attribute, in this case "attr1."
//Put the attribute definition together.
String attrName = new String("attr1");
Then you assign a syntax, in this case SYN_CLASS_NAME, to the attribute.
NDSSyntax syntax = NDSSyntax.SYN_CLASS_NAME;
Next you can assign flags that govern the attributes and behavior of the new attribute. In this case we assigned the SIZED flag to indicate that the attribute is sized.
NDSAttributeFlags flags = new NDSAttributeFlags(NDSAttributeFlags.SIZED);
Then you assign the upper and lower bounds of the attribute in the database.
long lBound = 0; long uBound = 10;
Next you create the asn1 name of the attribute.
byte[] asn1 = new byte[1];
With all of this information, the attribute can now be assembled.
attributeDef = new NDSAttributeDefinition(attrName, syntax, flags, lBound, uBound, asn1);
Finally, the attribute is created in the NDS database.
//Create the attribute definition. ((NDSSchemaDefinition)schemaDef).putAttributeDefinition(attributeDef);
To remove an object class, you must specify the object class name as a parameter in the following method.
//Remove class definition
(NDSSchemaDefinition)schemaDef).removeClassDefinition("class1");
An attribute can be removed from the schema by specifying its name as a parameter in the following method .
//Remove attribute definition
(NDSSchemaDefinition)schemaDef).removeAttributeDefinition("attr1");
This operation modifies an existing object class definition. First, you must retrieve the existing definition.
//Get the old class definition
NDSClassDefinition oldClassDef = (NDSClassDefinition)(schemaDef.getUnexpandedClassDefinition("class1"))
Next, you retrieve the new attribute definition.
//Get the attribute definition for the attribute to add
NDSAttributeDefinition attrDef = (NDSAttributeDefinition)schemaDef.getAttributeDefinition("attr1");
AttributeDefinition[] addOptionalAttrSet = new AttributeDefinition[1];
addOptionalAttrSet[0] = attrDef;
Then you can add the optional attributes. Mandatory attributes cannot be modified.
//Add the new optional attributes NDSClassDefinition newClassDef = new NDSClassDefinition(oldClassDef.getName(), oldClassDef.getClassFlags(), oldClassDef.getContainmentClasses(), oldClassDef.getSuperClasses(), oldClassDef.getMandatoryAttributes(), oldClassDef.getNamingAttributes(), addOptionalAttrSet, oldClassDef.getASN1Data());
Finally, the new definition is added to NDS.
//Modify the class definition. ((NDSSchemaDefinition)schemaDef).putClassDefinition(newClassDef);
The NDS schema dictates the hierarchy of the directory and the relationships between objects. Effective schema classes are those classes that can be instantiated under a given container. For example, a User object can be created subordinate to an Organizational Unit, but an Organizational Unit cannot be created subordinate to a User object. You must provide the object entry and name associated with the given container object.
//Get the classes that can be instantiated for the given parent container (OU). ClassDefinition[] classDef = ((NDSSchemaDefinition)ns.getSchemaDefinition(objEntry)).getEffectiveClasses(OU);
You can use the following methods to determine if a definition exists in the directory for a given object class or attribute.
//Check to see if the class and attribute are defined in the schema.
boolean isAttributeDefined = ((NDSSchemaDefinition)schemaDef).isAttributeDefined("CN");
boolean isClassDefined = ((NDSSchemaDefinition)schemaDef).isClassDefined("User");
The following method returns an array containing the class definitions that use a given attribute definition.
//Get the class definitions.
ClassDefinition[] classDefs = ((NDSSchemaDefinition)schemaDef).getClassesUsingAttribute("CN");
This method returns a list of classes that are derived from a given object class. The following example shows how to return the classes that derive from the super class "Top."
ClassDefinition[] subClassDefs = ((NDSSchemaDefinition)schemaDef).getSubClasses("Top");
This class allows you to register as a listener for changes to the schema. When a schema modification occurs, all listeners are notified of the change.
If an attribute is changed, the following information is returned:
private class MyChangeListener implements PropertyChangeListener
{
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getNewValue() instanceof NDSAttributeDefinition)
{
//Attribute got changed. Here are the values.
NDSAttributeDefinition newAttrDef = (NDSAttributeDefinition)evt.getNewValue();
NDSAttributeDefinition oldAttrDef = (NDSAttributeDefinition)evt.getOldValue();
String attributeName = evt.getPropertyName();
}
If the object class has been changed, the following information is returned:
else
{
//Class got changed. Here are the values.
NDSClassDefinition newAttrDef = (NDSClassDefinition)evt.getNewValue();
NDSClassDefinition oldAttrDef = (NDSClassDefinition)evt.getOldValue();
String className = evt.getPropertyName();
}
}
}
The following method does the actual listening.
//Listen to any changes to schema definition.
private void listenToChangesToSchemaDefinition()
{
MyChangeListener changeListener = new MyChangeListener();
//Get schema object
ObjectEntry objEntry = nameSpace.getObjectEntry(null,"NOVELL_INC");
SchemaDefinition schemaDef = nameSpace.getSchemaDefinition(objEntry);
The addPropertyChangeListener method registers an change listener that will listen for property changes.
//Register as a change listener. The method propertyChange //will be called in the class //MyChangeListener if change to schema definition is made. ((NDSSchemaDefinition)schemaDef).addPropertyChangeListener( changeListener);
The removePropertyChangeListener(changeListener) method removes a listener for property changes. After being removed, the listener will no longer be notified of changes.
//To remove yourself from being a change listener do this
((NDSSchemaDefinition)schemaDef).
removePropertyChangeListener(changeListener);
}
Some listeners for schema changes can veto certain changes before they happen. For example, if an application wanted to modify an object class that your application needs, if you are registered as a vetoable listener, you would be notified of an attempt to modify the schema, and then you could veto, or prevent the change.
private class MyVetoChangeListener implements VetoableChangeListener
{
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException
{
if (evt.getNewValue() instanceof NDSAttributeDefinition)
{
NDSAttributeDefinition newAttrDef = (NDSAttributeDefinition)evt.getNewValue();
NDSAttributeDefinition oldAttrDef = (NDSAttributeDefinition)evt.getOldValue();
String attributeName = evt.getPropertyName();
}
else
{
NDSClassDefinition newAttrDef = (NDSClassDefinition)evt.getNewValue();
NDSClassDefinition oldAttrDef = (NDSClassDefinition)evt.getOldValue();
String className = evt.getPropertyName();
}
//Throw PropertyVetoException if you don't want changes to
//be allowed.
}
}
private void vetoChangesToSchemaDefinition()
{
MyVetoChangeListener vetoListener = new MyVetoChangeListener();
//Get schema definition object
ObjectEntry objEntry = nameSpace.getObjectEntry(null,"NOVELL_INC");
SchemaDefinition schemaDef = nameSpace.getSchemaDefinition(objEntry);
//Register as a vetoable listener.
//The method vetoableChange will be called in the class
//MyVetoChangeListener if an attempt to modify the schema
//definition is made.
((NDSSchemaDefinition)schemaDef).addVetoableChangeListener(vetoListener);
//To remove yourself from being a vetoable listener do this
((NDSSchemaDefinition)schemaDef).
removeVetoableChangeListener(vetoListener);
}