UnboundID LDAP SDK for Java 1.1.2

UnboundID has released version 1.1.2 of the UnboundID LDAP SDK for Java. It primarily includes bug fixes and minor enhancements over the previous 1.1.1 release. The release notes for this version are available online, but the changes for this release are as follows:

  • We have made it easier to work with attribute type definitions in which the SYNTAX element includes an optional minimum upper bound element. New methods have been provided which make it possible to obtain the base OID and minimum upper bound components separately, and attempts to retrieve an attribute syntax with a specified OID will ignore any minimum upper bound component that may be included in the provided OID.
  • When identifying differences between two entries, you now have the option to indicate whether you want the resulting list of modifications to be reversible. If you indicate that the modifications should be reversible (which has always been the default behavior in the past), then the modifications will only use the ADD and DELETE modification types, and they could be applied in reverse to revert back to the original entry, although attempts to apply these modifications are more likely to fail if the entry has been altered since it was retrieved. If the modifications should not be reversible, then they will all contain the REPLACE modification type, which will not be reversible but are less likely to fail if the entry has been altered.
  • We have made improvements in the way that you can obtain the string representations of DNs and RDNs. When creating a DN from a set of RDNs (or from an RDN and a parent DN), then the string representations of the existing RDNs will be used rather than constructing them all from scratch. This will better preserve the original string representation, including any escaping for special characters. In addition, new toMinimallyEncodedString methods have been added to retrieve a string representation with escaping used only for the minimal set of special characters.
  • We have fixed a bug in the LDIF reader that prevented it from being used to read entries containing only a DN and no attributes (as might be the case when reading the LDIF representation of a search result entry for which no attributes were returned).
  • We fixed a bug in the code used to generate the string representation of an LDAP URL using the subordinate subtree search scope. Previously, it would use the value “subord” instead of the correct “subordinates”. When parsing a string representation of a URL, it will still accept either form.
  • We have fixed a bug in the LDAPCommandLineTool class (including tools written using it like searchrate, modrate, and authrate) which prevented the “--bindPasswordFile” argument from being used to specify the password from a file in some cases.

LDAP SDK Persistence Updates

Over the Christmas break, I was able to spend some time working on the LDAP SDK persistence framework and I believe that it is now in a very usable state. I do plan on talking it over with a couple of people before merging it into the trunk (at which point it would be included in the next release), but if you’re interested in possibly using it then I would strongly encourage you to try it for yourself. You can do that by checking the source code out of the public Subversion repository using the following command:

svn checkout \
https://ldap-sdk.svn.sourceforge.net/svnroot/ldap-sdk/branches/persist \
ldap-sdk-persist

Then, you can build the LDAP SDK using the command ./build-se.sh on UNIX-based systems, or build-se.bat on Windows. The resulting build/package directory will include zipped-up and extracted versions of the LDAP SDK.

Some of the most significant changes that I made to the LDAP SDK persistence framework over the break include:

  • I added a generate-source-from-schema tool that can be used to read a directory server schema and generate Java source code for a properly-annotated class that can be used to interact with entries of a specified type, based on the structural object class (and optionally auxiliary classes) contained in that entry. If you want to work with existing data in a directory, or data for which the LDAP schema is already defined, then this is a very easy way to get source code to interact with entries of that type.

  • I added a generate-schema-from-source tool that can be used to generate LDAP attribute type and object class definitions from a properly-annotated source file.

  • I updated the default LDAP field encoder so that it supports interacting with multivalued attributes using Java lists and sets. Previously, this was only possible using arrays, and that option is still available and is still the mechanism used by the generate-source-from-schema tool.

  • I added the initial set of documentation for the persistence framework, including schema and code examples.

UnboundID LDAP SDK for Java 1.1.1

UnboundID has released version 1.1.1 of the UnboundID LDAP SDK for Java. This release adds a number of enhancements and bug fixes over the previous 1.1.0 release. The release notes provide a more complete overview of the changes in this release, but some of the most significant changes include:

  • We have found and corrected a bug that could cause the LDAP SDK to encounter an error and terminate the client connection if the server returned a response message in multiple parts, where the first part did not contain at least the complete BER type and length and there was a delay before the next packet(s) containing the remainder of the length.
  • There have been a number of significant updates to the searchrate, modrate, and authrate tools. The accuracy of the recent rates has been improved, and they have been updated to provide information about any non-success results returned by the server. The searchrate and modrate tools have been updated to support the use of the proxied authorization v2 control to request that operations be processed using an alternate authorization identity.
  • The ValuePattern class has been updated so that it supports obtaining data from files (either on the local filesystem or remotely via HTTP) rather than using numeric values within a specified range.
  • The behavior that the LDAP SDK exhibits when attempting to interact with a completely unresponsive server (e.g., for a system that is powered off, has the network cable disconnected, or has the server process forcibly stopped). Previously, the connect timeout may not have always been honored, and it was not possible to define operation timeouts for bind or extended operations. Further, the error message included in the exception resulting from a client-side timeout has been updated to include the type of operation and the length of time the client had waited for a response to the operation.
  • Client-side support for matching rule evaluation has been improved. It is now possible to select a matching rule by OID, and it is possible to make use of a matching rule ID in a sort key used for client-side sorting.
  • Schema parsing code has been updated to trim any leading or trailing spaces that may be contained in schema definitions. Even though leading or trailing spaces are technically not allowed, some servers don’t enforce this restriction, and the LDAP SDK has been updated to properly handle definitions containing them.

Thank you, Don

This weekend, Don Bowen passed away, nearly two years after being diagnosed with brain cancer. His battle certainly had its ups and downs, but his faith in God remained strong and was truly an inspiration to me, and I’m sure to many others. He has long been one of the men I admire most, and although it was sad to see him go, we can take comfort in the knowledge that he is truly in a better place, free of pain and full of praise.

I first met Don about ten years ago when we both worked at Caterpillar. I was fresh out of college and inherited responsibility for the Corporate Web Security (CWS) system, a web-based single sign-on environment built from scratch using a collection of Perl scripts, a web server plugin, and (at the time) Netscape Directory Server 3.11. Don had previously overseen the CWS project, and someone suggested that I talk to him to learn more about how it was put together. He gave me a pretty good grilling, and actually I came out of it a little scared of him. I can’t imagine what he must have done to the guys brave enough to try to date one of his daughters. Nevertheless, I must have done well enough because within a few months he asked me to join him at a startup in Baltimore (B2B Communications, later renamed to TidePoint Corporation), which I did.

Unfortunately, TidePoint fared about as well as many of the other startups around that time, and when it came to an end we separated for a bit when he went to the Burton Group and I joined Netscape Communications. However, we were reunited after only a few months when we both came to Sun Microsystems at about the same time (he actually called me and told me he was probably going to be joining Sun while I was there for my interview). And most recently, we helped to found UnboundID Corp., where despite his diagnosis days after forming the company, he was a tremendous asset to the company in many ways and I know we certainly wouldn’t be where we are today without him. Even after he was no longer able to work, he continued to stay involved and the last time I was able to have a meaningful conversation with him (about two weeks ago), he wanted to know what I was working on so he could continue to pray for me.

Please pray for Don’s wife, their four daughters, his parents, and their myriad friends. I can’t imagine how exhausting things must have been for them, especially within the last couple of weeks, but it is a blessing to see and to experience the strong support network they have. We will miss his presence, but his memory will live on, and his eternity is secure.

LDAP SDK persistence framework part 2: Supported data types

Another very important aspect of the persistence framework being developed for the UnboundID LDAP SDK for Java is the set of data types that it supports. In order to be able to correctly store and retrieve Java objects in a directory server, the LDAP SDK has to now how to encode Java values to LDAP attributes, and then decode the values of those attributes back to their Java representations. The LDAP SDK provides an abstract LDAPFieldEncoder class which defines the API needed to achieve this, as well as other things like generating LDAP attribute type definitions for a particular field or method. The @LDAPField, @LDAPFieldGetter, and @LDAPFieldSetter annotation types all include elements that allow you to specify the type of encoder that should be used when interacting with the associated field or method.

If the encoderClass element isn’t provided in the annotation, then the LDAP SDK will use an instance of the DefaultLDAPFieldEncoder class. This class supports a pretty broad range of data types, including:

  • boolean primitives
  • double primitives
  • float primitives
  • int primitives
  • long primitives
  • short primitives
  • byte[] objects
  • char[] objects
  • java.lang.Boolean objects
  • java.lang.Double objects
  • java.lang.Float objects
  • java.lang.Integer objects
  • java.lang.Long objects
  • java.lang.Short objects
  • java.lang.String objects
  • java.lang.StringBuffer objects
  • java.lang.StringBuilder objects
  • java.math.BigDecimal objects
  • java.math.BigInteger objects
  • java.util.Date objects
  • java.util.UUID objects
  • java.util.concurrent.atomic.AtomicInteger objects
  • java.util.concurrent.atomic.AtomicLong objects
  • com.unboundid.ldap.sdk.DN objects
  • com.unboundid.ldap.sdk.Filter objects
  • com.unboundid.ldap.sdk.LDAPURL objects
  • com.unboundid.ldap.sdk.RDN objects

In addition, it also supports arrays of all of the above types for dealing with multi-valued attributes. In the future, I intend to add add support for using java.util.Lists and java.util.Sets for all of the above types as well, but that’s not in the current implementation, and generics make that a bit tricky.

For most of the above data types, the value stored in the directory server is simply the string representation of the associated primitive or object. However, there are a few special cases:

  • boolean primitives and java.lang.Boolean objects are always stored using values of “TRUE” or “FALSE” (in all uppercase), as per the LDAP Boolean syntax.
  • byte[] objects are used to represent the raw bytes that comprise the attribute value
  • char[] objects are used to represent the raw characters that comprise the attribute value
  • java.util.Date objects are encoded using the LDAP generalized time syntax

Even though the default field encoder supports a number of primitive types (although it doesn’t support either byte or char in order to avoid confusion), I would generally recommend that you use the corresponding objects in the java.lang package instead. The reason for this is that primitives can’t have a null value, so it’s not possible to distinguish between cases in which there is no value and cases where there is a value that happens to be the same as the default value for that primitive (zero for numeric types and false for boolean). If you use the corresponding object types instead, then they can have null values, and you can use that to determine if a value has been assigned.

If you need to store some other kind of object in the directory that isn’t in this list, then you’ll need to create your own subclass of LDAPFieldEncoder with the appropriate logic for encoding and decoding values. If this is necessary, then I’d strongly recommend using DefaultLDAPFieldEncoder as a starting point.

LDAP SDK persistence framework part 1: Annotations

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 of com.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 of false. 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 of true. Note that if inRDN is true, then the field will always be included in add operations regardless of the value of the inAdd 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 of true. Note that if inRDN is true, then the field will never be included in the modify operations regardless of the value of the inModify 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 of false.
  • 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 of com.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 of null (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 a null value. If this is not provided, then no attribute will be included in the generated entry if the field has a null 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 of false.
  • requiredForEncode — Indicates whether the process of encoding the field to an LDAP attribute should fail if the field has a null value and no default encode values are defined. If this is not provided, then it will be assigned a default value of false.
  • 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 of true.
  • 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 of true.

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 the objectClass 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 of false. 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 the inAdd 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 the inModify 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 the inFilter 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 the encoderClass 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 the encoderClass 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 the failOnInvalidValue 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 the failOnTooManyValues 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.

LDAPCon recorded presentation posted

I have now updated the UnboundID website to include a recorded version of the presentation that I gave at LDAPCon. The total presentation is right at 45 minutes, but I’ve broken it up into a few pieces so that you don’t have to try to digest it all at once.

You can find the recorded presentation, as well as a copy of the slides, at http://www.unboundid.com/products/ldapsdk/LDAPCon-2009-presentation/index.php.

LDAPCon slides posted

The LDAP SDK presentation that I gave Monday at LDAPCon went pretty smoothly and seemed to be pretty well-received. When I got home yesterday afternoon, I recorded a version of the presentation and we’ll hopefully be making that available online in the near future. However, a version of the slides that I used for the presentation is now available online at http://www.symas.com/ldapcon2009/papers/ely1.shtml.

Slides and/or papers for some of the other presentations are also available online. You can see them at http://www.symas.com/ldapcon2009/papers.shtml.

LDAP SDK now in Maven Central Repository

Just in time for my talk at LDAPCon on Monday, it looks like the UnboundID LDAP SDK for Java is now available in the Maven Central Repository.

You can get it with the following information

  • groupId — com.unboundid
  • artifactId — unboundid-ldapsdk
  • version — 1.1.0

The link to the POM file for this release is at http://repo1.maven.org/maven2/com/unboundid/unboundid-ldapsdk/1.1.0/unboundid-ldapsdk-1.1.0.pom.

UnboundID LDAP SDK for Java 1.1.0

UnboundID has released version 1.1.0 of the UnboundID LDAP SDK for Java. This is a significant update over the previous 1.0.0 release, both in terms of functionality and project policies. You can see the release notes for more detailed information about the changes, but some of the most significant updates include:

  • The LDAP SDK is now covered by the GNU Lesser General Public License (LGPL) version 2.1. This is an addition to the existing GPLv2 and UnboundID LDAP SDK Free Use Licenses that were already in use for the project. LGPLv2.1 is an OSI-approved open source license that is very friendly for use in both open source and proprietary applications.
  • As previously mentioned, the LDAP SDK is now available on SourceForge, including a public source repository, mailing lists, forums, and download files.
  • The LDAP SDK has been updated to make it easier for use with the Apache Maven build system. It should be included in the Central Repository in the near future, but in the meantime, you can use a repository URL of http://ldap-sdk.sourceforge.net/maven/ with a groupId of “com.unboundid”, an artifactId of “unboundid-ldapsdk”, and a version of “1.1.0”.
  • The LDAP SDK jar file has been updated so that its manifest includes the appropriate headers to make it easier to use with the OSGi module system.
  • We have added code to help make it easier to migrate applications written using the Netscape Directory SDK for Java or JNDI to the UnboundID LDAP SDK for Java. The LDAP SDK contains a number of analogs for classes in the Netscape Directory SDK for Java, and in many cases you can simply change import statements from “netscape.ldap” to “com.unboundid.ldap.sdk.migrate.ldapjdk” and recompile your code. If you’re currently using JNDI, then you can use classes in the “com.unboundid.ldap.sdk.migrate.jndi” package to aid in the conversion process.
  • The LDAP SDK has been updated to make it easier to have server schema information taken into account when reading responses. A new “use schema” connection option has been defined, and when it is enabled then the LDAP SDK will use the server schema to select appropriate matching rules for attributes in search result entries. A number of other schema-related improvements have also been made.
  • The searchrate, modrate, and authrate tools have been updated to provide the ability to load-balance requests across multiple servers, to include timestamps in the output, and to run for a specified number of warm-up intervals before beginning overall statistics collection.