As I mentioned in my LDAPCon presentation, I’ve been working recently on updating the UnboundID LDAP SDK for Java to provide a framework that makes it easy to store and retrieve Java objects in an LDAP directory server. The majority of this framework is complete and has been committed into the SourceForge repository (although it’s currently in the branches/persist directory rather than in the trunk), so you can check it out and build it for yourself if you’d like to give it a shot. It provides support for add, delete, modify, get, and search operations, as well as for encoding and decoding LDAP entries to and from Java objects, and for validating the set of annotations contained in an object. The primary components that have not yet been implemented are the tools for generating LDAP schema from a properly-annotated Java object, or for generating a Java source file from LDAP schema, but that will hopefully be added in the near future.
At the heart of the persistence framework is a set of annotations that can be used to mark your source code with information about how the object should be represented in an LDAP directory server. In this post, I’ll describe those annotations and how they can be used.
The LDAPObject Annotation
The @LDAPObject
annotation is used to mark the class for an object that is to be stored in an LDAP directory server. The class must include a constructor that takes zero arguments, although it may have any or no access modifier. This annotation may include the following elements:
structuralClass
— The name of the LDAP structural object class to use for entries created from objects of this type. If this is not provided, then it will be assumed that the name of the LDAP structural object class is the same as the unqualified Java class name.auxiliaryClass
— The name(s) of any LDAP auxiliary object classes to use for entries created from objects of this type. If this is not provided, then no auxiliary object classes will be included in generated entries.defaultParentDN
— The DN of the entry below which objects of this type are typically located in the directory. This is optional, and even if it is provided, you can override this value on a per-operation basis.postDecodeMethod
— The name of a method in the associated Java class that should be invoked after all other processing has been performed when decoding an LDAP entry to a Java object. If this is provided, then the specified method must exist in the entry and must not take any arguments. That method can be used to perform any additional processing not directly provided by the LDAP SDK persistence framework, or to perform custom validation of the resulting object (optionally throwing an exception if a problem is found).postEncodeMethod
— The name of a method in the associated Java class that should be invoked after all other processing has been performed to encode the object to an LDAP entry. If this is provided, then the specified method must exist in the entry and must take exactly one argument with a type ofcom.unboundid.ldap.sdk.Entry
. This method can be used to alter the resulting entry, or to perform further validation of that entry (optionally throwing an exception if a problem is found).
The LDAPField Annotation
The @LDAPField
annotation is used to mark fields in a class containing the @LDAPObject
annotation. Each field marked with this annotation will be associated with a different attribute in an LDAP entry. The field may be marked with any or no access modifier (i.e., it can be public, private, protected, or have package-level access), and must not be declared either static or final. Any fields included in the class which are not marked with the @LDAPField
annotation will not be used when encoding or decoding objects.
This annotation may include the following elements:
attribute
— The name of the LDAP attribute used to hold the value(s) for this field. If this is not provided, then it will be assumed that the name of the LDAP attribute is the same as the name of the Java field.objectClass
— The name(s) of the object class(es) in which this attribute is referenced. This will primarily be used in the process of generating LDAP schema that may be used to store the associated type of object. If this is not provided, then it will be assumed that the attribute is included in the structural object class for the object. Only object classes named as structural or auxiliary classes in the@LDAPObject
annotation may be specified.inRDN
— Indicates whether this field should be included in the RDN for entries created from the object. If this is not provided, then it will be assigned a default value offalse
. At least one field or getter method in the class must be marked for inclusion in the entry RDN.inAdd
— Indicates whether this field should be included in the entry created from the object as part of an LDAP add operation. If this is not provided, then it will be assigned a default value oftrue
. Note that ifinRDN
istrue
, then the field will always be included in add operations regardless of the value of theinAdd
element.inModify
— Indicates whether this field should be included in the set of modifications when performing an LDAP modify operation for the object. If this is not provided, then it will be assigned a default value oftrue
. Note that ifinRDN
istrue
, then the field will never be included in the modify operations regardless of the value of theinModify
element.inFilter
— Indicates whether this field may be included in the filter that is generated when trying to search for objects matching the provided object. If this is not provided, then it will be assigned a default value offalse
.encoderClass
— The fully-qualified name of the Java class that provides the logic for encoding and decoding the value(s) of the field to and from an LDAP attribute. If this is not provided, then a default ofcom.unboundid.ldap.sdk.persist.DefaultLDAPFieldEncoder
will be used (more information about this class and the field encoder API will be provided in an upcoming post).defaultDecodeValue
— A default value (or set of values) that will be assigned to the field when decoding an LDAP entry to a Java object if the entry does not contain the associated attribute. If this is not provided, then the field will be assigned a value ofnull
(or a default value for primitives) if the attribute is missing from the entry being decoded.defaultEncodeValue
— A default value (or set of values) that will be used for the attribute generated from the associated field if it has anull
value. If this is not provided, then no attribute will be included in the generated entry if the field has anull
value.requiredForDecode
— Indicates whether the process of decoding an LDAP entry to a Java object should fail if the associated attribute is not included in the entry and no default decode values are defined. If this is not provided, then it will be assigned a default value offalse
.requiredForEncode
— Indicates whether the process of encoding the field to an LDAP attribute should fail if the field has anull
value and no default encode values are defined. If this is not provided, then it will be assigned a default value offalse
.failOnInvalidValue
— Indicates whether the process of decoding an LDAP entry to a Java object should fail if the associated attribute contains a value that cannot be assigned to the field (e.g., because the value cannot be parsed in a way compatible with the type of the Java field). If this is not provided, then it will be assigned a default value oftrue
.failOnTooManyValues
— Indicates whether the process of decoding an LDAP entry to a Java object should fail if the associated attribute has multiple values when only a single value can be assigned to the Java field. If this is not provided, then it will be assigned a default value oftrue
.
The LDAPFieldGetter Annotation
The @LDAPFieldGetter
annotation may be used to mark a method which may be used to generate an attribute to include in entries or modifications generated from the object. It can be used for cases in which a value should only be used when encoding and not decoding, or if custom processing is needed to prepare the value for storing in the directory. Any methods marked with this annotation must not take any arguments, and must not be declared static
. They can have any or no access modifier, including public
, private
, or protected
.
This annotation may include the following elements:
attribute
— The name of the LDAP attribute in which the value(s) returned from the method should be stored. This is a required element.objectClass
— The name(s) of the LDAP object class(es) in which this attribute is referenced. It has the same use as theobjectClass
element of the@LDAPField
annotation.inRDN
— Indicates whether the value returned from this method should be included in the RDN for entries created from the object. If this is not provided, then it will be assigned a default value offalse
. At least one field or getter method in the class must be marked for inclusion in the entry RDN.inAdd
— Indicates whether the value(s) of this method should be included in the entry created from the object as part of an LDAP add operation. It has the same behavior as theinAdd
element of the@LDAPField
annotation.inModify
— Indicates whether the value(s) of this method should be included in the set of modifications when performing an LDAP modify operation for the object. It has the same behavior as theinModify
element of the@LDAPField
annotation.inFilter
— Indicates whether this field may be included in the filter that is generated when trying to search for objects matching the provided object. It has the same behavior as theinFilter
element of the@LDAPField
annotation.encoderClass
— The fully-qualified name of the Java class that provides the logic for encoding the method value(s) to an LDAP attribute. It has the same behavior as theencoderClass
element of the@LDAPField
annotation.
The LDAPFieldSetter Annotation
The @LDAPFieldSetter
annotation may be used to mark a method that can be used to update a Java object based with information from an attribute in an LDAP entry. It can be used for cases in which an attribute should be read from but not written to an entry, or for cases in which custom processing is required to update the object from an attribute. Any methods marked with this annotation must take exactly one argument and must not be declared static
. The methods can have any or no access modifier.
This annotation may include the following elements:
attribute
— The name of the LDAP attribute that should be used to provide the argument used when invoking the method. This is a required element.encoderClass
— The fully-qualified name of the Java class that provides the logic for decoding the attribute value(s) to the argument used to invoke the method. It has the same behavior as theencoderClass
element of the@LDAPField
annotation.failOnInvalidValue
— Indicates whether the process of decoding an LDAP entry to a Java object should fail if the associated attribute contains a value that cannot be converted to the argument used to invoke the method. It has the same behavior as thefailOnInvalidValue
element of the@LDAPField
annotation.failOnTooManyValues
— Indicates whether the process of decoding an LDAP entry to a Java object should fail if the associated attribute has multiple values when only a single value can be held in the argument used to invoke the method. It has the same behavior as thefailOnTooManyValues
element of the@LDAPField
annotation.
The LDAPDNField Annotation
The @LDAPDNField
annotation may be used to mark at most one field in the class which will be assigned the DN of the entry with which the object is associated. If a field with this annotation is included in the object, then it must have a type of java.lang.String
, and it must not be declared static
or final
. It may have any or no access modifier. This annotation must not be used to mark a field that is also marked with the @LDAPField
annotation.
If a field with this annotation is present in the object, then it will be assigned in the course of initializing the object with information from an LDAP entry, or in the course of generating an LDAP entry from the object contents. If set, the value of this field may be used to obtain the appropriate entry DN for add, delete, or modify operations.
Neither the @LDAPDNField
nor the @LDAPEntryField
annotations are required to be used in an object marked with the @LDAPObject
annotation, but it is strongly recommended that at least one of them be included so that the correct entry DN will be available for LDAP operations involving the object.
The LDAPEntryField Annotation
The @LDAPEntryField
annotation may be used to mark at most one field in the class which will be assigned a read-only copy of the entry with which the object is associated. If a field with this annotation is included in the object, then it must have a type of com.unboundid.ldap.sdk.ReadOnlyEntry
, and it must not be declared static
or final
. It may have any or no access modifier. This annotation must not be used to mark a field that is also marked with the @LDAPField
annotation.
If a field with this annotation is present in the object, then it will be assigned in the course of initializing the object with information from an LDAP entry, or in the course of generating an LDAP entry from the object contents. If set, the value of this field may be used to obtain the appropriate entry DN for add, delete, or modify operations. It may also be used to refine the set of modifications generated from an object for inclusion in a modify operation, so that modifications do not include changes to attributes that have not changed since the object was retrieved.
As mentioned above, although neither is required, it is strongly recommended that either or both the @LDAPDNField
or @LDAPEntryField
annotations should be included in objects to be persisted in the directory.