Book Home Java Enterprise in a Nutshell Search this book

6.6. Listing the Children of a Context

A 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 to see if one is running or list the names of children of an InitialContext in order to populate a Swing JTree component. 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, where 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. The 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 6-6 shows the implementation of a list command for 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 6-6. The list Command

import 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 where 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)
...

6.6.1. How Names Work

The list() method 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, which, in the case of the filesystem provider, can be either a Unix-style forward slash (/) or a Windows-style backward slash (\). 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 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 backward slash interchangeably, most service providers are very picky about the separator character used for that service. Unfortunately, the JNDI API does not 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 cannot 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.

6.6.2. Browsing a Naming System

So 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, you 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 does not implement Context, however, you obviously cannot 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 6-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, since 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 cannot use cd .., as Context objects do not know about their parents, and therefore, we cannot go up the Context hierarchy.

Example 6-7. The cd Command

import 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 we demonstrated the list command, I asked you to assume that the current Context for NamingShell was the naming subdirectory. Now we 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 we are starting from the initial context and that the naming directory is available in the filesystem at /temp/javax/naming.

6.6.3. Listing the Bindings of a Context

The listBindings() method of Context provides an alternative means of accessing 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 where 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.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.