Section 9.9. Accessing Directory Services


9.9. Accessing Directory Services

So far, we've discussed JNDI only in the context of naming services. Now it's time to turn to directory services. At its root, a directory is merely a naming service whose objects have attributes as well as names. Programming for a directory service, such as an LDAP directory, is roughly as hard as programming for a relational database.

As we've seen, a binding in JNDI is an association between a name and an object. While this association is sufficient for some naming services, a directory service needs to be able to associate more than just a name with an object. Attributes associate specialized data with an object. In JNDI, an object with attributes as well as a name is called a directory entry.

We've been talking about the filesystem as though it were a naming system because that is how Sun's filesystem provider implements it. But if you think about it, a filesystem is really a directory system; files and directories have attributes like permissions, user IDs, and group IDs (we just can't get at these attributes using Sun's filesystem provider).

9.9.1. The DirContext Interface

javax.naming.directory.DirContext is JNDI's directory services interface. It extends Context and provides modified methods that support operations involving attributes. Like a Context, a DirContext encapsulates a set of name-to-object bindings. In addition, a DirContext contains a javax.naming.directory.Attributes object for each bound object that holds the attributes and values for that object.

The names of objects in X.500-based directories (like LDAP) look a little different from the names we've seen so far for filesystems. If you've worked with an LDAP directory, you've probably seen names like "cn=Billy Roberts, o=Acme Products". This name is actually a compound name while something like "o=Acme Products" is an atomic name. By convention, in an LDAP directory, the part of the name before the equals sign (e.g., "cn", "o") is stored as an attribute of the directory entry, and the rest of the name (e.g., "Billy Roberts", "Acme Products") is stored as its value. This attribute is called the key attribute. Table 9-2 lists some commonly used key attributes. Note that when a DirContext is used with an LDAP directory, it knows its name, unlike a Context.

Table 9-2. Common key X.500 attributes

Attribute

Meaning

"c"

A country, such as the United States or Lithuania

"o"

An organization or corporation, such as the Humane Society or Omni Consumer Products

"ou"

A division of an organization, such as the Public Relations Department or the Robotic Peace Officer Division

"cn"

The common name of an entity (often a user, in which case it can be a first name or a full name)

"sn"

The surname (last name) of a user


The key attribute is closely tied to the directory entry's object class definition, otherwise known as its type. For example, in an LDAP directory, an entry that has an object class of "user" has a key attribute of "cn" while an object with a class of "organization" has a key attribute of "o".

The schema for a directory defines the object classes that can be used in the directory, analogous to the way that the schema for a relational database defines the data tables that are available in the database. The object class of a directory entry is stored as an attribute of the entry. Note that the values used for object classes are directory-dependent, so a user entry from one directory might have a different object class than a user entry from another directory.

9.9.2. The Attributes Interface

The Attributes interface represents the set of attributes for a directory entry. It has accessor methods that enable access to the entire set, as well as to specific attributes. In X.500-based directories, the name of an attribute (also called an attribute ID), such as "name", "address", or "telephonenumber", determines the type of the attribute and is called the attribute type definition. An attribute type definition is part of a directory's schema; the corresponding attribute syntax definition specifies the syntax for the attribute's value and whether it can have multiple values, among other things.

You can retrieve all attributes of a directory entry by calling the getAttributes( ) method of DirContext, followed by the getAll( ) method of Attributes. getAttributes( ) returns an Attributes object. Calling the getAll( ) method of this object returns a NamingEnumeration of javax.naming.directory.Attribute objects, one for each attribute of the directory entry.

Example 9-10 shows the implementation of a listattrs command for NamingShell. This command prints the attributes of a directory entry, as well as string representations of the attribute values.

Example 9-10. The listattrs command
 import java.util.Vector; import javax.naming.*; import javax.naming.directory.*;   class listattrs implements Command {   public void execute(Context c, Vector v) throws CommandException {       String name = "";       // An empty string is OK for a listattrs operation     // as it means list attributes of the current context     if (!(v.isEmpty(  )))       name = (String)v.firstElement(  );       if (NamingShell.getCurrentContext(  ) == null)       throw new CommandException(new Exception(  ), "No current context");       try {       // Get the Attributes and then get enumeration of Attribute objects       Attributes attrs = ((DirContext)c).getAttributes(name);       NamingEnumeration allAttr = attrs.getAll(  );       while (allAttr.hasMore(  )) {         Attribute attr = (Attribute)allAttr.next(  );         System.out.println("Attribute: " + attr.getID(  ));           // Note that this can return human-unreadable garbage         NamingEnumeration values = attr.getAll(  );         while (values.hasMore(  ))           System.out.println("Value: " + values.next(  ));       }     }     catch (NamingException e) {       throw new CommandException(e, "Couldn't list attributes of " + name);     }     catch (ClassCastException cce) {       throw new CommandException(cce, "Not a directory context");     }   }     public void help(  ) { System.out.println("Usage: listattrs [name]"); } }

To use the listattrs command, you need access to a live directory server. To experiment with a live LDAP directory server, you might try the University of Michigan's server at ldap://ldap.itd.umich.edu or Novell's test server at ldap://nldap.com. Another option is to download and compile the OpenLDAP source code from http://www.openldap.org and get an LDAP server running on your local network. To use the University of Michigan's LDAP server with NamingShell, you need to create a properties file that contains the following properties:

 java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory java.naming.provider.url=ldap://ldap.itd.umich.edu/

Make sure that the jar file for the LDAP service provider is in the CLASSPATH of NamingShell when you use this initial context information. If you are using JDK 1.3 or later, this LDAP provider is built into the runtime.

Once you have NamingShell set up to use a directory server, here's how you might use the listattrs command:

 o=NOVELL% listattrs cn=admin Attribute: groupMembership Value: cn=DEVNET SYSOP,ou=Groups,o=NOVELL Attribute: revision Value: 235 Attribute: uid Value: admin Attribute: objectClass Value: top Value: person Value: organizationalPerson Value: inetOrgPerson Attribute: sn Value: admin Attribute: cn Value: admin

The following code in listattrs retrieves the Attributes object of the named directory context and enumerates the individual Attribute objects:

 Attributes attrs = ((DirContext)c).getAttributes(name); NamingEnumeration allAttr = attrs.getAll(  );

Calling getAttributes( ) with the name of a directory entry returns an Attributes object that contains all the attributes for that entry. Another variation of getAttributes( ) allows you to pass the name of a directory entry and an array of attribute names (as String objects). This method returns an Attributes object that contains only the specified attributes. For example:

 String[] attrIDs = {"name", "telephonenumber"}; Attributes partialAttrs = dirContext.getAttributes(name, attrIDs);

In listattrs, we used the getAll( ) method of Attributes to return an enumeration of Attribute objects. The Attributes interface also provides a getIDs( ) method that returns an enumeration of just the attribute names (or IDs) for the directory entry. If you know the attribute you want, you can specify the attribute name in a call to the get( ) method, which returns a single Attribute object. For example:

 Attribute addr = attrs.get("address");

9.9.3. The Attribute Interface

The Attribute interface represents a single directory attribute. We've already seen this interface in the listattrs command, where we used it to print the names and values of all the attributes of a directory context.

An attribute can have a single value or multiple values, as specified in the schema for the directory. For example, a "name" attribute might have a single value (e.g., "Billy") while a "telephonenumber" attribute might have multiple values (e.g., "800 555 1212" and "303 444 6633").

JNDI provides several methods for working with values in an attribute. For instance, we can get one or more values, add or remove a single value, remove all values, or determine if a particular value is present.

The get( ) method of Attribute returns a single attribute value as a java.lang.Object. If the attribute has only a single value, get( ) returns that value. If the attribute has multiple values, the service provider determines the value that is returned. The following code shows how to get a single value from an attribute:

 DirContext user ... ; // Created somewhere else in the program Attributes attrs = user.getAttributes(""); Attribute attr = attrs.get("telephonenumber"); Object onePhoneNumber = attr.get(  );

The getAll( ) method returns multiple attribute values as a NamingEnumeration of objects, as we saw in listattrs. Here's how to print all values stored in an attribute:

 Attribute attr = attrs.get("telephonenumber"); NamingEnumeration phoneNumbers = attr.getAll(  ); while (phoneNumbers.hasMore(  ))   System.out.println(phoneNumbers.next(  ));

The add( ) method of Attribute enables us to add another value to an attribute:

 Attribute attr = attrs.get("telephonenumber"); attr.add("520 765 4321"); // Add a new number

If we try to add a value to an attribute that doesn't support multiple values, the method doesn't throw an exception. The attribute simply doesn't accept the new value. By the same token, you can use the remove( ) method to remove a value from an attribute.

 Attribute attr = attrs.get("telephonenumber"); attr.remove("303 444 6633"); // Remove the old number

To remove all the values from an attribute, you can call the clear( ) method. Note that none of these method calls actually affect the directory entry; they simply modify the local Attribute object. To make a permanent change, you have to call the modifyAttributes( ) method of DirContext and provide it with a modified Attribute object, as discussed in the next section.

The contains( ) method lets you determine whether an attribute has a certain value, while size( ) returns the number of values the attribute has:

 Attribute attr = attrs.get("telephonenumber"); // Check for certain value boolean itsThere = attr.contains("800 555 1212"); int valuesItHas = attr.size(  ); // Check how many values it has



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net