3.1 How to Create a JNDI Shell

This solution describes how to create a JNDI shell that accepts commands from an input stream and executes these commands. A demo JNDIShell browser, with accompanying source code, is provided to show the functionality of this JNDI shell solution.

3.1.1 Creating a JNDI Shell

The JNDI shell implementation consists of two major components (JNDIShell class and shell class) with other supporting interfaces and classes.

JNDIShell Class

The JNDIShell class contains the JNDI specific shell implementations. Sample source code is provided by the JNDIShell.java file. The primary purpose of the JNDIShell class is to control the shell state. The specific tasks included in this class are as follows:

  1. Defines the input stream from which commands are read and the output stream to which results are written.
  2. Defines the continueWStdIn flag and the verbose flag as described in the sample source code.
  3. Sets up a constructor for a child shell with the parent shell, initial context, input stream, and output stream as parameters.
  4. Sets up constructors for several JNDIShell objects with different parameters, such as initial context, input stream, output stream, defined flags, and so forth.
  5. Sets the current JNDI context associated with the shell to facilitate relative context operations.
  6. Sets up a loop in the main run method for reading in the command line from the input stream, checking and processing the command line, registering and de-registering commands, and writing the results to the output stream.
  7. Provides the following methods for streams operations, resolving names, registering and de-registering commands, and performing relative context operations:
    • addCommand(String, Command) - Binds a command to a string command name in the shell.
    • removeCommand(String) - Unbinds a command from a string command name in the shell.
    • getCommand(String) - Returns a command bound to the specific command name.
    • resolve(String) - Resolves a name relative to the initial context specified in the constructor.
    • resolveRelative(NamedContext, String) - Resolves a name relative to a provided context.
    • resolveRelativeAtomic(NamedContext, String) - Resolves an atomic name relative to a provided context.
    • getCurrCtx - Returns the current context as a NamedContext object.
    • setCurrCtx(NamedContext) - Sets the current context to the context specified.
    • setPrompt(String) - Sets the prompt to that specified.
    • getVerbose - Returns the verbose state.
    • setVerbose(boolean) - Sets the verbose state to TRUE or FALSE.
    • getInputStream - Returns the input stream attached to the shell.
    • getOutputStream - Returns the output stream attached to the shell.

shell Class

The shell class is a generic shell that does general initialization, parses the command line, and controls input and output. Its primary purpose is to set up the environment in which the JNDIShell class can operate. This generic shell contains two principal methods (main and runShell) and 23 internal classes, which define the different default commands that are implemented.

Sample source code is provided by the shell.java file.

main Method

The primary purpose of the main method is to interpret any strings passed to it as command line parameters. More specifically it performs the following functions:

  • Takes as input a string array of arguments and sets up a command line parser for interpreting the strings passed in.
  • Initializes optional variables and flags, as well as input and output streams.
  • Looks for the initial context factory property, which is necessary for the class java.naming.InitialContext to function correctly. The InitialContext gets this system property and treats the contents as a fully distinguished class name, which should implement java.naming.spi.InitialContextFactory.
  • Loads and runs the shell with the default command bindings by calling the runShell method.

runShell Method

The primary purpose of the runShell method is to run the shell given a set of minimal options and the necessary input and output streams. More specifically it performs the following functions:

  • Sets up the environment for and spawns a JNDIShell object according to the options specified.
  • Defines the default commands and binds the command-name pairs to the shell. These commands may be loaded and unloaded during the course of the shell execution. You can implement your own commands by creating a class that implements either the Command interface or the CommandWithHelp interface.
  • Sets the default initialContext to be
       com.novell.service.nw.NetWareInitialContetFactory
       

    if no other initial context is provided.

  • Runs the shell given a set of minimal options and the necessary input and output streams.

runShell Command Classes

A command class is included for each of the 23 default shell commands. Because each command class implements the CommandWithHelp interface, each has a getHelpStrs method for printing the command help information. These commands are bound to the shell, and because of the load and unload commands, all of them can be changed during the course of shell execution.

  • Load Command (load) adds a command class to the shell under a command name.
  • Unload Command (unload) removes a command from the shell.
  • Dir Command (dir) lists bindings of the current context or relative context.
  • Cd Command (cd) changes the current context to a relative context.
  • CdAtomic Command (cda) changes the current context to a literal relative context. It tracks the name using the current context's name parser.
  • CdSchemaRoot Command (cds) starts a new shell with the initial context at the schema root of the specified relative DirContext.
  • CdSchemaClassDef Command (cdcdef) starts a new shell with the initial context at the schema class definition of the specified relative DirContext.
  • CdSchemaAttributeDef Command (cdadef) starts a new shell with the initial context at the schema attribute definition of the specified attribute of a relative DirContext.
  • CdSchemaSyntaxDef Command (cdsdef) starts a new shell with the initial context at the schema syntax definition of the specified attribute of a relative DirContext.
  • Attr Command (attr) dumps all or specified attributes of a DirContext.
  • Show Command (show) prints the toString representation of a bound object.
  • Rename Command (ren) renames a bound object.
  • Bind Command (bind) binds a relative object to the current context under a specified name.
  • Rebind Command (rebind) rebinds a relative object to the current context under a specified name.
  • Unbind Command (unbind) unbinds a relative object to the current context under a specified name.
  • Create Command (create) creates a subcontext relative to the current context.
  • Destroy Command (del) destroys a subcontext relative to the current context.
  • Env Command (env) displays the environment of the current context, allowing modifications.
  • Shell Command (shell) executes a shell script.
  • Exec Command (exec) executes an OS-specific command.
  • VerboseToggle Command (v) toggles the verbose flag in the shell.
  • Exit Command (exit, quit, q) exits the shell.
  • Help Command (help, h, ?) prints help for all of the default bound commands.

Other Supporting Interfaces and Classes

The JNDIShell class and the shell class use the following additional interfaces and classes.

  • Command interface - Provides a generic interface for a command. A class that implements this interface must be registered with the shell. When the command is entered, all arguments are passed to the command through the execute method. This interface is used by both the JNDIShell and shell classes.
  • CommandWithHelp interface - Provides a generic interface for a command that supports help through extending Help by adding the getHelpStrs method. A class that implements this interface must be registered with the shell. When the command is entered, all arguments are passed to the command through the execute method. This interface is used by only the shell class.
  • LineReader class - Provides support for reading text input lines from an arbitrary input stream or reader. This class is used only in the JNDIShell class.
  • NamedContext class - Contains a Context and the name that can be used to look up that context from a specified initial context. This class is used by both the JNDIShell and shell classes.
  • NamedObject class - Contains an object and the name required to look it up. This demonstrates functionality that can be used to look up an object from the specified name. This class is used by both the JNDIShell and shell classes.
  • NullOutputStream class - Extends OutputStream and acts as a sink for bytes that should disappear. This class is used only by the JNDIShell class.
  • ShellException class - Used to pass error messages from a command to the shell. If an error occurs in a command, a ShellException can be thrown with a message. The name of the command, along with the message, is then printed, and execution continues. If the exitShell flag is set, then the shell will exit. This class is used by both the JNDIShell and shell classes.
  • shell.ini file - Contains the initial environment settings. This configuration file can be edited to use JNDI providers other than those supplied by NovellĀ® by setting the environment key 'java.naming.factory.initial' to the class name of the desired initial context factory.

3.1.2 Executing the JNDI Shell Demo

The JNDI Shell demo is a text-based browser that allows you to explore JNDI providers. It is designed for users who have a basic knowledge of the JNDI Context and DirContext interfaces. JNDIShell can call basic JNDI Context and DirContext methods.

Some possible uses for JNDIShell are:

  • As an exploratory tool to discover how different JNDI providers work.
  • As a debugging tool, allowing you to save debugging time by providing calls to basic NJCL Context and DirContext methods. To test specific situations, run JNDIShell and execute the command you desire.

To run the JNDIShell demo utility, follow the procedures below.

Setting up JNDIShell locally

  1. Go to the c:\novell\Ndk\samples\njcl_sample\jndishell directory (default download).
  2. Compile JNDIShell by entering the following on the command line:
       javac shell.java
       
  3. Run JNDIShell by entering the following on the command line:
       java shell
       
  4. Get command line help by adding the '-?' option. For example, enter
       java shell -?
       

Implementing Your Own Commands

  1. Create a class that implements either the Command or the CommandWithHelp interfaces.
  2. Load the command into the shell by typing
       load <cmdclass> <cmdname>
       

    where <cmdclass> is the name of the class you created and <cmdname> is the command name to which you want to bind the class.

Changing the Initial Environment

Edit the shell.ini file to change the initial environment. To use the JNDI providers other than those supplied by Novell, set the environment key 'java.naming.factory.initial' to the class name of the desired initial context factory.

Getting Help on Commands

For all commands, at the command line, enter

   ?
   

For one particular command, at the command line, enter

   ? <cmd>
   

3.1.3 JNDIShell Source Code

This source code demonstrates how to create the functionality of a generic shell that accepts commands from an input stream and executes these commands.