6.5. The NamingShell Application
Earlier, we discussed how we might modify the
Lookup example to make it more general, allowing
us to look up Enterprise JavaBeans and remote objects. The rest of
the examples in this chapter are going to be based on the
NamingShell code shown in Example 6-2. NamingShell is an extensible JNDI shell that
enables us to perform naming operations in any JNDI-accessible
naming system. The shell provides methods for getting and setting
the current object and other shell-related details, and it also
keeps track of the name of the current object, something a
Context cannot do for itself.
Once you have loaded NamingShell, you can use the
shell to execute JNDI-related commands, just as you would use a
regular shell to execute operating-system commands. I encourage you to
download the code for NamingShell right now, so
that you can experiment with it as we proceed through the rest of the
chapter. NamingShell uses the name you type to
locate a command dynamically from the filesystem. The shell has no
interpreter; however, NamingShell expects a command
to implement the Command interface and its
execute() method. This means a command really
interprets itself. A command throws a
CommandException when execution fails.
As you can see, NamingShell itself contains
very little real JNDI code. All the JNDI functionality is implemented
in the various Command classes we create to handle
particular JNDI operations. The shell simply supports the loading of
commands and keeps track of various shell-related details.
Example 6-2. The NamingShell Class
import java.io.*;
import java.util.*;
import javax.naming.*;
class NamingShell {
// Private variables
private static Hashtable COMMAND_TABLE = new Hashtable();
private static String JNDIPROPS_FILENAME = ".jndienv";
private static String PROMPT = "[no initial context]";
private static String VERSION = "1.0";
private static Context CURRENT_CONTEXT, INITIAL_CONTEXT;
private static String CURRENT_NAME, INITIAL_NAME;
private static boolean RUNNING = true;
// Shell operations
private static void exit(int status) { System.exit(status); }
// Accessor methods
public static Hashtable getCommands() { return COMMAND_TABLE; }
public static Context getCurrentContext() { return CURRENT_CONTEXT; }
public static String getCurrentName() { return CURRENT_NAME; }
public static String getDefaultPropsFilename() { return JNDIPROPS_FILENAME; }
public static Context getInitialContext() { return INITIAL_CONTEXT; }
public static String getInitialName() { return INITIAL_NAME; }
public static String getPrompt() { return PROMPT; }
public static void setCurrentContext(Context ctx) { CURRENT_CONTEXT = ctx; }
public static void setInitialContext(Context ctx) { INITIAL_CONTEXT = ctx; }
public static void setInitialName(String name) { INITIAL_NAME = name; }
public static void setPrompt(String prompt) { PROMPT = prompt; }
public static void setCurrentName(String name) {
CURRENT_NAME = name;
setPrompt(name);
}
// Executes a preinstantiated command we are sure is already
// present in the table
private static void execute(Command c, Vector v) {
if (c == null) {
System.out.println("No command was loaded; cannot execute the command.");
return;
}
try {
c.execute(CURRENT_CONTEXT, v);
}
catch (CommandException ce) {
System.out.println(ce.getMessage());
}
}
// Another private method that enables us to specify a command
// by its string name and that loads the command first
private static void execute(String s, Vector v) {
execute(loadCommand(s), v);
}
// Loads the command specified in commandName; the help command
// relies on this method
public static Command loadCommand(String commandName) {
// The method returns a null command unless some of its
// internal logic assigns a new reference to it
Command theCommand = null;
// First see if the command is already present in the hashtable
if (COMMAND_TABLE.containsKey(commandName)) {
theCommand = (Command)COMMAND_TABLE.get(commandName);
return theCommand;
}
try {
// Here we use a little introspection to see if a class
// implements Command before we instantiate it
Class commandInterface = Class.forName("Command");
Class commandClass = Class.forName(commandName);
// Check to see if the class is assignable from Command
// and if so, put the instance in the command table
if (!(commandInterface.isAssignableFrom(commandClass)))
System.out.println("[" + commandName + "]: Not a command");
else {
theCommand = (Command)commandClass.newInstance();
COMMAND_TABLE.put(commandName, theCommand);
return theCommand;
}
}
catch (ClassNotFoundException cnfe) {
System.out.println("[" + commandName + "]: command not found");
}
catch (IllegalAccessException iae) {
System.out.println("[" + commandName + "]: illegal acces");
}
catch (InstantiationException ie) {
System.out.println("["+commandName+"]: command couldn't be instantiated");
}
finally {
return theCommand; // theCommand is null if we get here
}
}
// This method reads a line of input, gets the command and arguments
// within the line of input, and then dynamically loads the command
// from the current directory of the running shell
private static void readInput() {
// Get the input from System.in
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Begin reading input
try {
while (RUNNING) {
System.out.print(PROMPT + "% ");
// Tokenize the line, read each token, and pass the token
// into a convenient remaining arguments Vector that we
// pass into the Command
StringTokenizer tokenizer = new StringTokenizer(br.readLine());
Vector remainingArgs = new Vector();
String commandToken = "";
if (tokenizer.hasMoreTokens()) {
commandToken = tokenizer.nextToken();
while (tokenizer.hasMoreTokens())
remainingArgs.addElement(tokenizer.nextToken());
}
// Dynamically load the class for the appropriate command
// based upon the case-sensitive name of the first token,
// which is the command token
if (!(commandToken.equals("")))
execute(commandToken, remainingArgs);
}
}
catch (java.io.IOException ioe) {
System.out.println("Caught an IO exception reading a line of input");
}
}
// Constructor
NamingShell(String[] args) {
}
// Main method that reads input until the user exits
public static void main(String[] args) {
System.out.println("NamingShell " + VERSION);
System.out.println("Type help for more information or exit to quit");
shell.readInput();
System.out.println("Exiting");
}
}
6.5.1. The Command Interface
The Command interface (shown in Example 6-3)
describes a standard interface for a shell command. It has an
execute() method that contains the command logic
and a help() method for displaying online help for
the command. If execute() encounters a naming
exception (or some other exception), it throws a
CommandException (shown in Example 6-4), which
stores the first exception as an instance variable so that the shell
can display the exception appropriately.
Example 6-3. The Command Interface
import java.util.Vector;
import javax.naming.Context;
public interface Command {
public void execute(Context c, Vector v)
throws CommandException;
public void help();
}
Example 6-4. The CommandException Class
public class CommandException extends Exception {
Exception e; // root exception
CommandException(Exception e, String message) {
super(message);
this.e = e;
}
public Exception getRootException() {
return e;
}
}
6.5.2. Loading an Initial Context
As I said earlier, to use JNDI to look up an object in a naming system
(or, in fact, to do anything with the naming system), you first have
to create an InitialContext for that naming
system. So, the first command we need to implement is
initctx, for loading an initial context into
NamingShell. Example 6-5 shows an implementation
of this command.
Example 6-5. The initctx Command
import java.io.*;
import java.util.*;
import javax.naming.*;
public class initctx implements Command {
public void execute(Context c, Vector v) {
String jndiPropsFilename;
// If no properties file is specified, use the default file;
// otherwise use the specified file
if (v.isEmpty())
jndiPropsFilename = NamingShell.getDefaultPropsFilename();
else
jndiPropsFilename = (String)v.firstElement();
try {
Properties props = new Properties();
File jndiProps = new File(jndiPropsFilename);
props.load(new FileInputStream(jndiProps));
NamingShell.setInitialContext(new InitialContext(props));
NamingShell.setInitialName("/");
NamingShell.setCurrentContext(NamingShell.getInitialContext());
NamingShell.setCurrentName(NamingShell.getInitialName());
System.out.print("Created initial context using ");
System.out.println(jndiProps.getAbsolutePath());
}
catch (NamingException ne) {
System.out.println("Couldn't create the initial context");
}
catch (FileNotFoundException fnfe) {
System.out.print("Couldn't find properties file: ");
System.out.println(jndiPropsFilename);
}
catch (IOException ioe) {
System.out.print("Problem loading the properties file: ");
System.out.println(jndiPropsFilename);
}
catch (Exception e) {
System.out.println("There was a problem starting the shell");
}
}
public void help() { System.out.println("Usage: initctx [filename]"); }
}
The initctx command accepts an argument that specifies the name of a properties file to use in creating the Properties object that is passed to the InitialContext constructor. If no filename is specified, initctx looks for the default properties file specified by NamingShell. So, with NamingShell, all you have to do to use a particular naming service is create an appropriate properties file for that service.
6.5.3. Running the Shell
With NamingShell and
initctx, we have enough functionality to actually
run the shell. Before you try running the shell, make sure that the
JNDI libraries (in jndi.jar) and any other
specialized providers are specified in your classpath. Here's
how we might start NamingShell and establish an
initial context, once the classpath is set appropriately:
% java NamingShell
NamingShell 1.0
Type help for more information or exit to quit
[no initial context]% initctx
Created initial context using C:\temp\samples\book\.jndienv
/%
In this case, since we didn't specify a properties file,
NamingShell looks for the
.jndienv file in the current directory. For the
purpose of our next few examples, let's assume that this file
contains property settings that allow us to use the filesystem
provider from Sun. You can change initial contexts at any time
during the shell session by running initctx
with a new filename. After you have created an initial context, you
can begin performing naming operations by typing in commands. To
exit the shell, simply use the exit
command.[2]
If you are not sure how a command works, you can get help for that command by typing:
/% help command
 |  |  |
| 6.4. Looking Up Objects in a Context |  | 6.6. Listing the Children of a Context |

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