9.6. Listing the Children of a ContextA common JNDI operation is retrieving the list of names of an object's children. For example, an application might get the names of Enterprise JavaBeans in a Java application server or list the names of user profile information in an LDAP server in order to populate a Swing JTRee component in an address book application. You list the names of an object's children using the list( ) method of Context: NamingEnumeration children = initialContext.list(""); The list( ) method returns a javax.naming.NamingEnumeration of javax.naming.NameClassPair objects; each NameClassPair contains the name and class of a single child of the Context. Note that the NameClassPair is not the child itself. Its getName( ) method, however, enables us to learn the name of the child while getClassName( ) lets us access the child's class name. NamingEnumeration implements the java.util.Enumeration interface, so it allows us to loop through the results of calling list( ) using the familiar enumeration methods. JNDI actually uses NamingEnumeration as the return type of a number of naming operations; the actual objects in the enumeration vary depending on the operation. Example 9-6 shows the implementation of a list command for our NamingShell. Because executing list( ) requires a current Context, the execute( ) method queries the shell to determine whether one exists. If there is no current Context, the method throws an exception. Example 9-6. The list commandimport java.util.Vector; import javax.naming.*; public class list implements Command { public void execute(Context c, Vector v) throws CommandException { String name = ""; // An empty string is OK for a list operation as it means // list children of the current context if (!(v.isEmpty( ))) name = (String)v.firstElement( ); // Check for current context; throw an exception if there isn't one if (NamingShell.getCurrentContext( ) == null) throw new CommandException(new Exception( ), "Error: no current context."); // Call list( ) and then loop through the results, printing the names // and class names of the children try { NamingEnumeration enum = c.list(name); while (enum.hasMore( )) { NameClassPair ncPair = (NameClassPair)enum.next( ); System.out.print(ncPair.getName( ) + " (type "); System.out.println(ncPair.getClassName( ) + ")"); } } catch (NamingException e) { throw new CommandException(e, "Couldn't list " + name); } } public void help( ) { System.out.println("Usage: list [name]"); } } Let's continue with our example of using NamingShell with the filesystem provider. Say that we are accessing a filesystem in which we have unpacked a jar file that contains, among others, a javax directory and a naming subdirectory. If the current Context is the naming directory (ignoring for a moment how we set the current Context; we'll see how to do that shortly), we can use the list command with the following results: naming% list AuthenticationException.class (type java.io.File) AuthenticationNotSupportedException.class (type java.io.File) BinaryRefAddr.class (type java.io.File) Binding.class (type java.io.File) CannotProceedException.class (type java.io.File) CommunicationException.class (type java.io.File) CompositeName.class (type java.io.File) CompoundName.class (type java.io.File) ConfigurationException.class (type java.io.File) Context.class (type java.io.File) ContextNotEmptyException.class (type java.io.File) directory (type javax.naming.Context) ... 9.6.1. How Names WorkThe list( ) method on Context allows us to list the names of the children of any arbitrary child of a Context. We just saw that we can list the names of the children of a Context itself (in this case, the naming directory) by calling its list( ) method using an empty string as a parameter. Again, let's assume we have a Context object for the naming subdirectory under javax. Here's how a call to get the names of the children of this Context might look: NamingEnumeration childrenOfNaming = namingContext.list(""); The result is a NamingEnumeration that contains NameClassPair objects representing all the children of naming (i.e., the classes and subpackages of javax.naming), including the directory directory (i.e., the javax.naming.directory subpackage). To list the names of the children of an arbitrary child of a Context, we have to pass a name to list( ). For example, we can list the children of directory by specifying the String "directory" as a parameter to list( ): NamingEnumeration childrenOfDirectory = namingContext.list("directory"); The result here is a NamingEnumeration that contains NameClassPair objects representing all the children of directory (i.e., the classes of javax.naming.directory, such as DirContext). You can also specify a name using something called a compound name. A compound name is composed of atomic names, like "naming" and "directory," that are separated by separator characters. In the case of the filesystem provider, these can be either a Unix-style forward slash (/) or a Windows-style backslash (\). Any JNDI method that takes a name as a parameter can accept a compound name. Say we have a Context object for the javax directory. We can get a list of the children of that directory as follows: NamingEnumeration childrenOfDirectory = javaxContext.list( "naming/directory"); This call returns the same NamingEnumeration we got earlier. Now consider the following call: NamingEnumeration childrenOfContext = javaxContext.list( "naming/Context"); The compound name here specifies an object that is not a Context, so it has no children. In this case, the call to list( ) throws a NamingException. The separator character used in JNDI compound names varies across naming and directory services; the separator is analogous to the separator used in java.io.File. Although the Sun filesystem provider allows us to use the Unix-style forward slash and the Windows-style backslash interchangeably, most service providers are very picky about the separator character used for that service. Unfortunately, the JNDI API doesn't provide a way to get the separator character programmatically the way java.io.File does. Although the javax.naming.CompoundName class reads a property called jndi.syntax.separator that contains the separator character, this property can't be accessed outside the service provider. So, to find out the separator character for a particular service provider, you have to consult the documentation or some sample code for that provider. 9.6.2. Browsing a Naming SystemSo far, we know how to look up an object in a Context using lookup( ) and list the children of that Context with list( ). Browsing is a composite operation that involves repeated calls to list( ) and lookup( ) to see what objects are available in the naming system and to move around in those objects. Context objects are the key to browsing . You start with a current Context and list the children of that Context to see which child you (or, more likely, the user) are interested in. Once you have selected an interesting child, look up that child to get the actual child object. If the object implements Context, you can use this new Context object to continue browsing, by calling list( ) again, selecting a child, and looking up its object. If the object doesn't implement Context, however, you obviously can't continue browsing down that branch of the naming system. Once you have a Context object, it is always possible to list its children and look up objects within it. So, for example, you can always use the InitialContext for a naming system to go back and start browsing at the entry point to the naming system. Example 9-7 shows an implementation of a cd command for NamingShell. The cd command changes the current context of NamingShell to the specified context; you use it in conjunction with the list command to browse the naming system. The name of this command comes from the Unix cd command for changing directories, because changing the directory on a Unix system is an analogous operation to changing the current context when NamingShell is used with the filesystem provider. To change the current context back to the initial context, use either cd / or cd \. Note, however, that you can't use cd.. because Context objects don't know about their parents, and so you can't go up the Context hierarchy. Example 9-7. The cd commandimport java.util.Vector; import javax.naming.*; class cd implements Command { public void execute(Context ctx, Vector v) throws CommandException { if (NamingShell.getCurrentContext( ) == null) throw new CommandException(new Exception( ), "No current context"); else if (v.isEmpty( )) throw new CommandException(new Exception( ), "No name specified"); // Get args[0] and throw away the other args else { String name = (String)v.firstElement( ); try { if (name.equals("..")) { throw new CommandException(new Exception( ), "Contexts don't know about their parents."); } else if (((name.equals("/")) || (name.equals("\\"))) { NamingShell.setCurrentContext(NamingShell.getInitialContext( )); NamingShell.setCurrentName(NamingShell.getInitialName( )); System.out.println("Current context now " + name); } else { Context c = (Context) (NamingShell.getCurrentContext( )).lookup(name); NamingShell.setCurrentContext(c); NamingShell.setCurrentName(name); System.out.println("Current context now " + name); } } catch (NamingException ne) { throw new CommandException(ne, "Couldn't change to context " + name); } catch (ClassCastException cce) { throw new CommandException(cce, name + " not a Context"); } } } public void help( ) { System.out.println("Usage: cd [name]"); } } Earlier, when demonstrating the list command, we asked you to assume that the current Context for NamingShell was the naming subdirectory. Now you can see just how to change the current Context to that directory: initctx% cd temp Current context now temp temp% cd javax Current context now javax javax% cd naming Current context now naming Of course, these commands assume you are starting from the initial context and that the naming directory is available in the filesystem at /temp/javax/naming. 9.6.3. Listing the Bindings of a ContextThe listBindings( ) method of Context provides an alternative way to access the children of a Context. We've seen that list( ) returns a NamingEnumeration of NameValuePair objects, where each NameValuePair provides access to the name and class name of a single child of the Context. listBindings( ) also returns a NamingEnumeration, but, in this case, the enumeration contains Binding objects. Binding is a subclass of NameValuePair that contains the actual child object in addition to its name and class. You can use the getObject( ) method of Binding to get the child object. Just as with list( ), we can pass an empty string to listBindings( ) to return the bindings for a Context: NamingEnumeration bindings = initialContext.listBindings(""); listBindings( ) is designed for situations in which you need to perform some sort of operation on all the children of a Context, and you want to save yourself the time and trouble of looking up each child individually. Be aware, however, that listBindings( ) is potentially a very expensive operation, as it has to get each child object from the underlying naming system. If you don't need all the objects, you are better off using list( ) to get the names of the children and then just looking up the objects you need. |