Sunday, August 19th, 2001
Friday, September 28th 2001
Comet Way Jerminal v0.2a
Copyright 2001©, Comet Way Inc.
jonlin@tesuji.org
The Comet Way Jerminal

The Comet Way Jerminal is a Java terminal which provides basic shell functionality as well as the ability to create classes and invoke methods. A Jerminal session provides an environment that allows you to set environment variables to arbitrary objects that already exist or are created. Any given Jerminal session can be given a set of Input and Output Streams for recieving commands and reporting the results of those commands. The basic Jerminal session assumes two actions: prompting for a command and evaluating a given command. The prompt process updates the current runtime session and provides a prompt for a command. The evaluation process involves parsing and interpreting a String given through the Jerminal session input. In order to use the Jerminal effectively, one must understand the process of the interpretation.

 

SECTION 1: The Jerminal Session

The Interpreter

Each command is delimited by newlines; thus, multple commands to be interpreted are separated by hitting the [ENTER] key. Each given command is processed as follows: The entire command String is tokenized by delimiting the tokens using whitespace (all whitespace except the newline/carriage return, since that delimits the different commands given to the interpreter. The first token is the name of the command, the rest of the tokens are given to that command as operands or arguments. A token can be a collection of tokens if enclosed by double quotes. Backslashes are used to escape characters and reserved characters.

For example:

prompt> command "this is all one token" token2 token3

prompt> command \"token1 token2 token3

The reserved characters are double quotes ("), percent (%), dollar sign ($), and backslash (\). The quotes are used to group tokens together. The percent is used by putting it in front of a token. That token will be evaluated as a command. The dollar sign is used in front of a token and denotes that that token should be dereferenced as an environment variable. The blackslash is used to escape characters.

prompt> command %"command2 token1 token2" token2 $token3 %$token4

When this command is interpreted by the interpreter, the command2 will be evaluated first. The result of that command is passed to the first command as its first argument. The token3 token will be dereferenced as an environment variable; for example, if I have the variable VAR_NAME set to VAR_VALUE, then the interpreter will see $VAR_NAME and replace it with VAR_VALUE. The token4 token is being dereferenced and evaluated. In other words, if I have an environment variable set to 'command token1 token2', by using both the % and the $, I can dereference that variable and evaluate the command. The % and $ characters will still be applied even if they are in a double quoted expression. In order for them to be ignored, they need to be escaped. The ! is a special character when used as the first character of a command string. This tells the interpreter to search for the last command in its history.

 

The Enviroment Variables

Environment variables can be set using the setenv command. This command takes as many as 2 arguments. With no arguments, it lists all the environment variables. With one, it only shows the value of the variable given by the first argument. If both arguments are given, the first argument is the variable name and the second is the value and the command will set that environment variable.

Certain variables affect how the shell works. The prompt variable determines what the prompt will look like. There are a few special characters that the prompt will recognize: @T is the current time on 24 hour clock, @t is the current time on a 12 hour clock, @d is the current directory with ~ as your home directory, @D is the absolute path of the current directory, @c is a counter for how many commands where issued to the shell. The home_directory variable denotes what your home directory will be. The ignore_string variable denotes what string will be used to denote a comment. The interpreter will ignore anything that starts with this string. The path_separator_char variable tells the interpreter what the character that separates path names are. The pwd variable points to the java.io.File Object which represents the current working directory. Note that changing the pwd variable to something that isn't a java.io.File object will cause the jerminal to throw exceptions. The history_size variable tells the Jerminal Session how large to make the command history. The jerminal environment variable is a reference to the Jerminal session itself. This provides the user access to the Jerminal Object.

For example:

setenv prompt "[@t][@d] > "
Will give you a prompt that will look something like this:

[12:35am][~/work/classes] > 

 

The Commands

The jerminal commands are instantiated when used and cached. The name of the command is the class name of the Object which represents the functionality of that command. All commands subclass the com.cometway.jerminal.command.JerminalCommand abstract class. These subclasses will need to implement the Object evaluate(Vector) method. The Vector parameter is the tokens which were passed to that command and the Object that the method returns is the result of the evaluation. There is another abstract sub class of called UnixFileCommand. This abstract subclass provides a bit of functionality for resolving UNIX style file handles into the Canonical path name. Commands which require this functionality needs to subclass the UnixFileCommand class. If you wish to use your own commands, the jerminal needs to know where to look for them. Upon startup, the jerminal will look for a comma delimited list of classname prefixes under the property command_prefix_list. This list is the package names of where your commands are.

The UnixFileCommands can be disabled if the Jerminal Session is instantiated with the file access flag set to false. Attempts to evaluate a UnixFileCommand command with the file access set to false will result in a UnauthorizedAccessException being thrown. When command classes can't be found for a given command, the Jerminal Session attempts to execute that command natively. It creates a runtime and a process to execute it. The Jerminal Session will try to link the Input and Output streams to the process. If the binary access flag is set to false upon the instantiation of a JerminalSession, the session will not attempt to execute commands if a class for the command is not found. (NOTE: Although the Java APIs allow you to create a Runtime and execute a process, the ability to do this across multiple platforms and Java VMs is very unpredictable. You will recieve an Exception in the native code if you attempt to executing something under Windows 95/98. On Linux and SunOS, commands that don't require input from stdin will usually work.)

The JerminalSession commands

These commands deal with the jerminal environment. You can get this help listing for each command (except a few special commands like exit) if you include the operand --help with the command. For example: invoke --help will print out the description and the synopsis for the invoke command.

aliasClass
This command adds a classname alias to the current jerminal session

Synopsis:

aliasClass
Lists all the classname aliases.

aliasClass [alias]
Lists the classname alias who's name is [alias]

aliasClass [alias] [classname]
Adds an alias which sets the alias [alias] to the classname [classname].

unaliasClass
This command removes a classname alias

Synopsis:

unaliasClass [alias]
Removes the classname alias who's name is [alias].

clearHistory
This command clears the command history in the current jerminal session. The history will start anew after the interpreter has evaluated the clearHistory command.

Synopsis:

clearHistory

history
This command displays what is in the current history.

Synopsis:

history

rehash
This command clears the list of cached commands. Since jerminal commands are Objects and need to be instantiated in order to use them, the jerminal keeps a cache of the command Objects. The rehash command will clear that cache and the command Objects are recached when they are issued to the jerminal.

Synopsis:

rehash
This clears the entire cache.

rehash [command name]
This clears the cached command [command name] from the cache.

rehash [command name] [command Object]
This clears the cached command [command name] and recaches it using the command Object [command Object]. This command assumes that you are passing a JerminalCommand of the appropriate class, otherwise, problems may result.

setenv
This command displays and sets the environment variables of a jerminal session. Synopsis:
setenv
This displays all the environment variables (in case sensitive alpha-numeric order).

setenv [variable name]
This displays the environment variable who's name is [variable name].

setenv [variable name] [value]
This sets the environment variable [variable name] to the value [value]. The old value is returned by this call.

source
This command reads a file from the local file system and evaluates it line by line. The evaluated command is printed out by the source command before it is evaluated. Synopsis:

source [source file]

unsetenv
This command removes an environment variable. The only parameter that this command accepts is the variable name which to remove. Synopsis:

unsetenv [variable name]

exit
This command causes the jerminal to stop accepting commands and send the notifyJerminalExiting() notification to its listeners. System.exit() is not called, the Thread that reads commands from jerminal's InputStream is stopped. Synopsis:

exit

 

The File commands

The commands help you navigate and negotiate with the file system. Note that these commands are unavailable if the jerminal is started with file access set to false. These UnixFileCommands will resolve partial pathnames based off the home_directory and pwd environment variables. Thus, these commands will resolve pathnames when they are expecting them. Possible pathnames: /usr/local/java/lib/classes/java/../../i586/, ~/java

cat
This command displays the contents of a file or files.

Synopsis:

cat [files]
This will list the contents of the file or files in the [files] list
cd
This command allows you to change your current working directory.

Synopsis:

cd
This will change to your home directory, or what your home_directory variable is set to.

cd [pathname]
This will change your current working directory to the one specified by [pathname].

ls
This command lists files and directories. There are two flags which you can use: the l and the a flags (flags are preceded by a '-' character). The l flag will cause this command to give to a long (detailed) listing of the files and directories. The a flag will display hidden files as well (files starting with the '.' character).

Synopsis:

ls (flags)
Displays the contents of your current working directory.

ls (flags) [pathname1] (pathname2) (pathname3) ...
Displays the contents of [pathname1], and if they exist, the contents of (pathname2) and (pathname3). All the arguments given to the ls command will be interpreted as pathnames.

mkdir
This command will create a directory on the local file system.

Synopsis:

mkdir [directory name]

mkdirs
This command will create a directory tree on the local file system.

Synopsis:

mkdirs [directory name]

rm
This command removes files and directories from the local file system. There are two flags which the rm command will accept: the r flag and the i flag. The r flag tells the rm command to recurse through directories and subdirectories when removing (this could be dangerous!). The i flag will make the rm command ask for a confirmation before deleting anything.

Synopsis:

rm (flags) [pathname1] (pathname2) (pathname3) ...
This removes [pathname1] and if they exist, (pathname2) and (pathname3). The rm command will assume all the arguments are pathnames and are to be removed.

 

The Java construct commands

These commands provide you the ability to create and manipulate Objects. The commands that accept classnames as arguments will check with the class name aliases to see if any of the classnames are aliases.

getClass
This command takes a java.lang.Object as its only parameter. This command returns a java.lang.Class object which represents the Class of the argument.

Synopsis:

getClass [Object]

getField
This command takes a java.lang.Object and a field name (java.lang.String). This command returns the field of the Object that was given as an argument.

Synopsis:

getField [Object] [field name]
The field [field name] of the object [Object] is returned. It will be the same type (the same Class) as the field.

instantiate
This command instantiates a class and returns the new instance of that class.

Synopsis:

instantiate [classname] [constructor param1 type] (constructor param2 type)... [constructor param1 value] (constructor param2 value)...
The class [classname] is to be instantiated. The constructor to use will have as its first argument an Object of type [constructor param1 type] and if exists, the second argument will be of type (constructor param2 type) and so on. There can be as many types as required to describe the constructor. The returned object will be of type [classname] and be instantiated with the arguments: [constructor param1 value] and if exists, (constructor param2 value) and so on. The number of constructor values must equal the number of constructor types. The constructor values can be casted into the primitive Object representations. For example [constructor param1 value] could be '(Integer)57'.

invoke
This command invokes a method on an existing Object.

Synopsis:

invoke [Target Object] [method name] [method param1 type] (method param2 type)... [method param1 value] (method param2 value)...
The Object [Target Object] will have the method [method name] invoked. The Method will require as its arguments the types [method param1 type] and if exists, (method param2 type) and so on. There can be as many types as needed to describe the method. The command returns the Object returned by the method invocation when given the arguments [method param1 value] and if exists (method param2 type) and so on. The number of method values must equal to the number of method types. The method values can be casted into primitive Object representations. For example, [method param1 value] could be '(Character)c'. If the [Target Object] is a classname, the method invoked is assumed to be a static method.

main
This command invokes the public static void main(String[]) method for the given class.

Synopsis:

main [class] (String arguments)
The public static void main(String[]) static method is invoked on the class [class]. If any other operands are given, they are assumed to be Strings and will make up the String array that is passed into the main method as arguments.

javac
This command compiles a file on the local file system. This command works exactly like the javac command in the java distribution.

javap
This command takes classnames as its arguments and lists the class information, including its subclasses, interfaces, constructors, fields, and methods.

loadClass
This command is similar to the instantiate command. But instead of using the default ClassLoader to load the Class, the loadClass command uses its own ClassLoader and reloads the class data. Synopsis:
loadClass [pathname] (constructor param1 type)... (constructor param1 value)...
The [pathname] is the path name of the .class file of the Class to load. This class is loaded and the constructor that mathces the (constructor param1 type) types is loaded. There can be as many types as needed to describe the constructor. The command returns the new instance of the Object instantiated from the class data loaded from the [pathname]. The (constructor param1 value) values are used as arguments for the constructor. The number of types must equal the number of values.

setField
This command sets a field of a java.lang.Object.

Synopsis:

setField [Target Object] [field name] [Field Value Object] (field type)
The Object [Target Object]'s field, [field name] will be set to [Field Value Object]. In the case that the field is a primitive, the (field type) is used to indicate which primitive to set the field to.

 

Miscellenous commands

echo
This command will echo whatever arguments you pass it.

eval
This command will evaluate whatever arguments you pass it.

nop
This command will do nothing. The interpreter will, however, process any $ or % tokens it finds.

 

Examples

Here are some examples of using a Jerminal Session:
Simple Example
Welcome to Jerminal v0.1

Jerminal(0)> setenv
history_size=50
home_directory=/usr/tmp
ignore_string=#
jerminal=com.cometway.jerminal.JerminalSession@80cc60d
path_separator_char=/
prompt=Jerminal(@c)> 
pwd=/usr/tmp
Jerminal(1)> ls -l
Jerminal(2)> mkdir test_directory
Jerminal(3)> ls -l
drw     1024    Sun Aug 19 03:13:18 EDT 2001    test_directory
Jerminal(4)> setenv TEST_VARIABLE "This is a test variable"
Jerminal(5)> setenv prompt "[@t][@d] > "
Jerminal(@c)> [3:14am][~] > 
[3:14am][~] > 
[3:14am][~] > cd test_directory
[3:14am][/var/tmp/test_directory] > cd
[3:14am][~] > !ls
! -> ls -l
drw     1024    Sun Aug 19 03:13:18 EDT 2001    test_directory
[3:14am][~] > 
The 3rd command issued (ls -l) listed the currect working directory, which from the 0th command (setenv), is /usr/tmp. The 5th command changes the prompt, and the last command uses the ! character to evaluate a command in the history. In this case, it is the last command in the history that begins with 'ls'.

Examples using the $ and % directives
Welcome to Jerminal v0.1

Jerminal(0)> setenv COMMAND1 "echo \"This is what I want to ECHO\" "
Jerminal(1)> setenv
COMMAND1=echo "This is what I want to ECHO" 
history_size=50
home_directory=/usr/tmp
ignore_string=#
jerminal=com.cometway.jerminal.JerminalSession@80cc60d
path_separator_char=/
prompt=Jerminal(@c)> 
pwd=/usr/tmp
Jerminal(2)> 
Jerminal(3)> 
Jerminal(4)> echo $COMMAND1
echo "This is what I want to ECHO"  
Jerminal(5)> 
Jerminal(6)> setenv ECHO $%COMMAND1
Jerminal(7)> setenv
COMMAND1=echo "This is what I want to ECHO" 
ECHO=This is what I want to ECHO 

history_size=50
home_directory=/usr/tmp
ignore_string=#
jerminal=com.cometway.jerminal.JerminalSession@80cc60d
path_separator_char=/
prompt=Jerminal(@c)> 
pwd=/usr/tmp
Jerminal(8)> 
Jerminal(9)> echo $ECHO
This is what I want to ECHO 
 
Jerminal(10)> setenv ECHO %"echo \"I can also echo like this\" "
This is what I want to ECHO 
Jerminal(11)> echo $ECHO
I can also echo like this 
 
Jerminal(12)> 
The 0th command sets the String 'echo "This is what I want to ECHO"' to the variable COMMAND1. The 6th command sets the variable ECHO to the evaluated contents of the COMMAND1 variable (using the combination of % and $ directives). Since the value of COMMAND1 is 'echo "This is what I want to ECHO"', when this is evaluated, the String 'This is what I want to ECHO' is returned. Thus, the value of ECHO is the String 'This is what I want to ECHO'. Command 10 shows you an alternate way of doing the same thing.

Example with java Objects
Welcome to Jerminal v0.1

Jerminal(0)> setenv BUFFER %"instantiate java.lang.StringBuffer"  
Jerminal: instantiate: Created new instance of java.lang.StringBuffer
Jerminal(1)> echo $BUFFER
 
Jerminal(2)> invoke $BUFFER append java.lang.String "THis is the first line\n"
THis is the first line
Jerminal(3)> invoke $BUFFER append java.lang.String "This is the second line\n"
THis is the first line
This is the second line
Jerminal(4)> 
Jerminal(5)> echo $BUFFER
THis is the first line
This is the second line
 
Jerminal(6)> echo %"invoke \$BUFFER length"
47 
Jerminal(7)> setenv BUFFER %"instantiate java.lang.StringBuffer java.lang.String \"This is a new StringBuffer\""
Jerminal: instantiate: Created new instance of java.lang.StringBuffer
THis is the first line
This is the second line
Jerminal(8)> echo $BUFFER
This is a new StringBuffer 
Jerminal(9)> 
The first command sets the variable BUFFER to a newly instantiated java.lang.StringBuffer object. The instantiated StringBuffer has no arguments thus the StringBuffer is empty. The 2nd and 3rd commands invokes the java.lang.StringBuffer.append(java.lang.String) method on the StringBuffer stored in the variable BUFFER. The 6th command invokes the length() method and echos the length of the StringBuffer. The 7th command instantiates a new StringBuffer with constructor arguments and stored the value under the BUFFER variable. The setenv command returned the old value of the BUFFER variable which was 'THis is the first line\nThis is the second line\n'.

Example using a script and loading and manipulating classes
This script creates an instance of the Comet Way Java Agent Kernel and starts up several Agents. The first thing this script does is it defines what the comment string will be. In this case '//', which is what the Java comment String is. Then several class name aliases are set. This allows the script to say 'String' when it really means 'java.lang.String'. Then the JAK is instantiated and stored in the variable JAK. The AgentKernel is instantiated and stored in the variable AGENT_KERNEL. It's initialized and started. With the AgentKernel running, other Agents can be easily created and loaded. Since order is important, the com.cometway.jak.ServiceManager is created first, then the com.cometway.httpd.HTTPFileServerAgent, and finally the com.cometway.httpd.WebServer. The WebServer properties needed to be set before it was created.
setenv ignore_string "//"

// This jerminal script creates a JAK, starts a ServiceManager, then starts a
// HTTPFileServer and a WebServer
//

// Classname aliases for convenience
aliasClass String java.lang.String
aliasClass Props kinetoscope.util.Props
aliasClass Object java.lang.Object
aliasClass JAK com.cometway.jak.JAK
aliasClass Agent com.cometway.jak.AgentInterface

// Lets start by instantiating a JAK
//
setenv JAK %"instantiate JAK"

// And we will need an AgentKernel as well
//
javap com.cometway.jak.AgentKernel

// You see that we need a Props object to pass into its constructor
setenv TMP_PROPS %"instantiate Props"
setenv AGENT_KERNEL %"instantiate com.cometway.jak.AgentKernel Props \$TMP_PROPS"

// give the Agent Kernel to our instance of the JAK
setField $JAK instance $AGENT_KERNEL

// initialize the AgentKernel
invoke $AGENT_KERNEL initProps

// Create the default reporter
setField $JAK reporter %"instantiate com.cometway.jak.Reporter"

// Start the Agent Kernel
invoke $AGENT_KERNEL startAgent Agent $AGENT_KERNEL

// Now we can start loading in some agents, we will start with the ServiceManager
setenv TMP %"instantiate Props"
invoke $TMP setProperty String Object classname com.cometway.jak.ServiceManager
setenv SERVICE_MANAGER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
invoke $AGENT_KERNEL startAgent Agent $SERVICE_MANAGER

// With the service manager loaded, we can try to load some services now
setenv TMP %"instantiate Props"
invoke $TMP setProperty String Object classname com.cometway.httpd.HTTPFileServerAgent
setenv inet %"invoke java.net.InetAddress getLocalHost"
setenv inet %"invoke \$inet getHostName"
setenv tmp %"instantiate java.lang.StringBuffer String http://"
invoke $tmp append String $inet
invoke $tmp append String :9876/
invoke $TMP setProperty String Object service_url %"invoke \$tmp toString"
unsetenv inet
unsetenv tmp
setenv FILE_SERVER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
invoke $AGENT_KERNEL startAgent Agent $FILE_SERVER

// And now we start the web server
setenv TMP %"instantiate Props"
invoke $TMP setProperty String Object classname com.cometway.httpd.WebServer
invoke $TMP setProperty String Object bind_port 9876
setenv inet %"invoke java.net.InetAddress getLocalHost"
setenv inet %"invoke \$inet getHostName"
setenv tmp %"instantiate java.lang.StringBuffer String http://"
invoke $tmp append String $inet
invoke $tmp append String :9876/
invoke $TMP setProperty String Object service_url %"invoke \$tmp toString"
unsetenv inet
unsetenv tmp
setenv WEB_SERVER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
invoke $AGENT_KERNEL startAgent Agent $WEB_SERVER
This is what it looks like when you source it.
Welcome to Jerminal v0.1

Jerminal(0)> # First, I want to set the prompt to something a little more legible
Jerminal(1)> setenv prompt "###### > "
Jerminal(@c)> ###### > 
###### > setenv home_directory /usr/home/jonlin/java
/usr/tmp###### > 
###### > cd   
###### > source ./com/cometway/jerminal/example1.jerm
Jerminal: source: setenv ignore_string "//"
Jerminal: source: // This jerminal script creates a JAK, starts a ServiceManager, then starts a
Jerminal: source: // HTTPFileServer and a WebServer
Jerminal: source: //
Jerminal: source: // Classname aliases for convenience
Jerminal: source: aliasClass String java.lang.String
Jerminal: source: aliasClass Props kinetoscope.util.Props
Jerminal: source: aliasClass Object java.lang.Object
Jerminal: source: aliasClass JAK com.cometway.jak.JAK
Jerminal: source: aliasClass Agent com.cometway.jak.AgentInterface
Jerminal: source: // Lets start by instantiating a JAK
Jerminal: source: //
Jerminal: source: setenv JAK %"instantiate JAK"
Jerminal: instantiate: Created new instance of com.cometway.jak.JAK
Jerminal: source: // And we will need an AgentKernel as well
Jerminal: source: //
Jerminal: source: javap com.cometway.jak.AgentKernel
Jerminal: source: // You see that we need a Props object to pass into its constructor
Jerminal: source: setenv TMP_PROPS %"instantiate Props"
Jerminal: instantiate: Created new instance of kinetoscope.util.Props
Jerminal: source: setenv AGENT_KERNEL %"instantiate com.cometway.jak.AgentKernel Props \$TMP_PROPS"
Jerminal: instantiate: Created new instance of com.cometway.jak.AgentKernel
Jerminal: source: // give the Agent Kernel to our instance of the JAK
Jerminal: source: setField $JAK instance $AGENT_KERNEL
Jerminal: source: // initialize the AgentKernel
Jerminal: source: invoke $AGENT_KERNEL initProps
Jerminal: source: // Create the default reporter
Jerminal: source: setField $JAK reporter %"instantiate com.cometway.jak.Reporter"
Jerminal: instantiate: Created new instance of com.cometway.jak.Reporter
Jerminal: source: // Start the Agent Kernel
Jerminal: source: invoke $AGENT_KERNEL startAgent Agent $AGENT_KERNEL
[001_StartupAgent] Starting on 19-Aug-01 3:56:25 AM
[001_StartupAgent] Loading agents from ./
Jerminal: source: // Now we can start loading in some agents, we will start with the ServiceManager
Jerminal: source: setenv TMP %"instantiate Props"
Jerminal: instantiate: Created new instance of kinetoscope.util.Props
Jerminal: source: invoke $TMP setProperty String Object classname com.cometway.jak.ServiceManager
Jerminal: source: setenv SERVICE_MANAGER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
Jerminal: source: invoke $AGENT_KERNEL startAgent Agent $SERVICE_MANAGER
[1003_ServiceManager] Starting on 19-Aug-01 3:56:25 AM
Jerminal: source: // With the service manager loaded, we can try to load some services now
Jerminal: source: setenv TMP %"instantiate Props"
Jerminal: instantiate: Created new instance of kinetoscope.util.Props
Jerminal: source: invoke $TMP setProperty String Object classname com.cometway.httpd.HTTPFileServerAgent
Jerminal: source: setenv inet %"invoke java.net.InetAddress getLocalHost"
Jerminal: source: setenv inet %"invoke \$inet getHostName"
Jerminal: source: setenv tmp %"instantiate java.lang.StringBuffer String http://"
Jerminal: instantiate: Created new instance of java.lang.StringBuffer
Jerminal: source: invoke $tmp append String $inet
Jerminal: source: invoke $tmp append String :9876/
Jerminal: source: invoke $TMP setProperty String Object service_url %"invoke \$tmp toString"
Jerminal: source: unsetenv inet
Jerminal: source: unsetenv tmp
Jerminal: source: setenv FILE_SERVER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
Jerminal: source: invoke $AGENT_KERNEL startAgent Agent $FILE_SERVER
[1004_HTTPFileServerAgent] Starting on 19-Aug-01 3:56:25 AM
[1003_ServiceManager] Registered com.cometway.httpd.HTTPFileServerAgent as HTTPFileServerAgent
Jerminal: source: // And now we start the web server
Jerminal: source: setenv TMP %"instantiate Props"
Jerminal: instantiate: Created new instance of kinetoscope.util.Props
Jerminal: source: invoke $TMP setProperty String Object classname com.cometway.httpd.WebServer
Jerminal: source: invoke $TMP setProperty String Object bind_port 9876
Jerminal: source: setenv inet %"invoke java.net.InetAddress getLocalHost"
Jerminal: source: setenv inet %"invoke \$inet getHostName"
Jerminal: source: setenv tmp %"instantiate java.lang.StringBuffer String http://"
Jerminal: instantiate: Created new instance of java.lang.StringBuffer
Jerminal: source: invoke $tmp append String $inet
Jerminal: source: invoke $tmp append String :9876/
Jerminal: source: invoke $TMP setProperty String Object service_url %"invoke \$tmp toString"
Jerminal: source: unsetenv inet
Jerminal: source: unsetenv tmp
Jerminal: source: setenv WEB_SERVER %"invoke \$AGENT_KERNEL createAgent Object \$TMP"
Jerminal: source: invoke $AGENT_KERNEL startAgent Agent $WEB_SERVER
[1005_WebServer] Starting on 19-Aug-01 3:56:25 AM
[1005_WebServer] Comet Way Web Server 1.1 Final 2-14-2001
[1005_WebServer] Starting WebServer and listening on port 9876...
[1005_WebServer] Server bound successfully.
(1003_ServiceManager) Requested: HTTPFileServerAgent 19-Aug-01 3:56:25 AM
(1003_ServiceManager) Not found: logger_agent 19-Aug-01 3:56:25 AM
###### > 
[1005_WebServer] Processing request for: comet/216.29.185.162 GET /
[1005_WebServer] Processing request for: comet/216.29.185.162 GET /projects
[1005_WebServer] Processing request for: comet/216.29.185.162 GET /projects/
[1005_WebServer] Processing request for: comet/216.29.185.162 GET /projects/httpd
[1005_WebServer] Processing request for: comet/216.29.185.162 GET /projects/httpd/

 

 

SECTION 2: The Jerminal Classes

The JerminalAgent

The JerminalSession class allows you to arbitrarily choose your input and output. Replacing the input stream changes where the JerminalSession recieves its commands. Replacing the output stream changes where the JerminalSession outputs the results of those commands. The JerminalAgent class is a JavaAgentKernel (JAK) Agent that starts a JerminalSession and opens a socket. Incoming connections are authenticated and they are connected to a JerminalSession. There is only one Jerminal session started by the JerminalAgent and it is persistent; future connections to that socket will result in being connected to the same Jerminal session.

The JerminalAgent takes these properties:
listen_port tells the JerminalAgent what port to open the socket on.
password is the password authentication in order to access the session.
allow_file_access is a flag that sets whether local file access is allowed in the JerminalSession.
allow_binary_access is a flag that sets whether local binary access is allowed in the JerminalSession.
default_environment allows you to load in a set of environment variables the first time the JerminalSession is created.

The default listen_port is 7870 and the default password is qazmko'68qwerty, and a quick way to start a jerminal agent is to create a file that looks like this:

classname=com.cometway.jerminal.JerminalAgent
listen_port=7870
password=my_new_password

Then run the Comet Way JAK and pass it the file:
> java com.cometway.jak.JAK [path of the file I just created]

This will start a JerminalAgent which you can connect to (via telnet) on port 7870 and with your password. NOTE: I highly suggest you change the port and password before you run this Agent, by default the JerminalSession that is started allows file and binary access. That means whoever that connects and enters in the correct password has access to your files and the ability to run programs on your machine. YOU HAVE BEEN WARNED.

Screenshots of starting the JerminalAgent and connecting to it:
The JerminalAgent.

Welcome to Jerminal v0.2


Jerminal(0)> 
Jerminal(1)> main com.cometway.jak.JAK /tmp/1000_JerminalAgent.startup
Executing JAK's static main method
Jerminal(2)> 
[JAK] Comet Way JAK 1.3 Preview 7-10-2001
[001_StartupAgent] Starting on 28-Sep-01 11:03:30 AM
[001_StartupAgent] Loading agents from ./
[1003_JerminalAgent] Starting on 28-Sep-01 11:03:30 AM

The connection.
[11:02am][comet-ttyp7][/tmp]> \telnet localhost 7870
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
access code: qazmko'68qwerty
Welcome to Jerminal v0.2

Jerminal(0)> 
Jerminal(1)> 
Jerminal(2)> 
Jerminal(3)> ls
test_directory
Jerminal(4)> setenv
TERM=vt100
history_size=50
home_directory=/usr/tmp
ignore_string=#
jerminal=com.cometway.jerminal.JerminalSession@80ae1dd
path_separator_char=/
prompt=Jerminal(@c)> 
pwd=/usr/tmp
Jerminal(5)> ^]
telnet> close
Connection closed.
[11:04am][comet-ttyp7][/tmp]> 
[11:04am][comet-ttyp7][/tmp]> 
[11:04am][comet-ttyp7][/tmp]> \telnet localhost 7870
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
access code: qazmko'68qwerty
Welcome to Jerminal v0.2

Jerminal(6)> 
Jerminal(7)> 

 

The JerminalWindow, JerminalKeyListeners, and the DumbTerminal

These classes provide a windowed terminal with an interpreted input and output. The JerminalWindow class is subclass of java.awt.Frame which supports a Text terminal with many features that windowed text terminals have. This window has an array of Strings which it paints in the window starting from the bottom of the window and the end of the array. Lines that are longer than the window width are automatically wrapped. This window keeps track of a cursor and provides a number of methods which allow insertion of text at the cursor as well as the ability to arbitrarily move the cursor. There is also functionality to allow scrolling through the rest of the Strings in the array which are not drawn in the window. Keyboard input from this window are read and interpreted by a JerminalKeyListener, an interface which defines a set of methods that provides basic interpreter functionality. One example of a JerminalKeyListener that is provided in this build is the EmacsKeyListener which interprets keystrokes similiar to emacs style keys (i.e. ^N, ^P, ^B, ^F for up, down, left, right). The KeyListener is responsible for filling the JerminalWindow with the text that the user types in, the editing that the user may use, and sending the user typed commands to the JerminalSession for them to be interpreted. The EmacsKeyListener provides a full featured command line editing tools as well as command and pathname completion. It speaks to the JerminalSession to implement a set of command history keys. The final class in this connection is the DumbTerminal. This class is responsible for reading the output of the JerminalSession and displaying them in the JerminalWindow. The actual terminal emulation happens in this piece of the model. The DumbTerminal emulates a dumb terminal, in other words, simple streamed output with no ability to manipulate the cursor. In order to display TN2320 or VT100 terminal emulation, a subclass or the DumbTerminal can be created which will interpret the output of the JerminalSession in the proper terminal emulation.

 

The JerminalWindow

The JerminalWindow provides a set of basic Text Window methods:

public void insertTab()
This method inserts a tab in the String array at the cursor. The length of the tab stop is given by the tabWidth field.

public void insertAtCursor(String s)
This method inserts the String into the String array at the cursor.

public void insertAtCursor(char c)
This method inserts the character into the String array at the cursor.

public void overwriteAtCursor(String s)
This method replaces the text at the cursor with the String passed in as a parameter.

public void overwriteAtCursor(char c)
This method replaces the character at the cursor with the character passed in as a parameter.

public void moveCursor(int x, int y)
This method moves the cursor to the given coordinates. If the Y cursor is out of the array bounds, it is moved back into the end/beginning of the array. Then if the X cursor is out of the String's bounds, it is moved to the end/beginning of the String.

public void moveCursorX(int xdiff)
This method offsets the X cursor xdiff number of characters. If it leaves the bounds of the String, it will be moved to the end/beginning of the String.

public void moveCursorY(int ydiff)
This method offsets the Y cursor xdiff number of array elements. If it leaves the bounds of the String array, it will be moved to the end/beginning of the array.

public void makeNewLine()
This method inserts a new String (new line) at the Y cursor. All the previous Strings from the Y cursor on are moved one less array index. The String at array index 0 is discarded.

public void visualBeep()
This method causes the window to flash, indicating a 'beep'.

public int getMaxCharWidth(int advance)
This method returns the maximum number of characters that will fit horizontally across the current window based on the given font advance.

public int getMaxCharHeight(int fontheight)
This method returns the maximum number of characters that will fit vertically across the current window based on the given font height.

public void scrollDown()
This method causes the window to scroll down, thus viewing Strings of a higher array index. If the String at the end of the array can already be viewed, nothing will happen. This only really does anything if you have scrolled up first.

public void scrollUp()
This method causes the window to scroll up, thus viewing Strings of a lower array index. If the String at the beginning of the array can already be viewed, nothing will happend.

As you can see, these methods provide the basic functionality of a text window and the JerminalWindow is not limited to being used with only the JerminalSession/ JerminalKeyListener/ DumbTerminal classes. Any application can use a JerminalWindow to display windowed text in a neat and organized fashion. These methods are the same methods that the JerminalKeyListener and the DumbTerminal uses to display its input.

 

The JerminalKeyListener/EmacsKeyListener

The JerminalKeyListener interface defines these methods:

	/**
	 * This sets the INSERT flag of the key listener. If set to true, it is expected
	 * that keystrokes read from the JerminalWindow are INSERTed at the cursor.
	 */
	public void setInsert(boolean insert);

	/**
	 * This method returns the state of the INSERT flag.
	 */
	public boolean getInsert();

	/**
	 * This sets the INTERPRET flag of the key listener. If set to false, the key
	 * listener is expected not to interpret the input.
	 */
	public void setInterpret(boolean interpret);
	
	/**
	 * This method returns the state of the INTERPRET flag.
	 */
	public boolean getInterpret();

	/**
	 * This sets the copy/paste buffer of the key listener.
	 */
	public void setKillBuffer(String buffer);

	/**
	 * This method returns the copy/paste buffer.
	 */
	public String getKillBuffer();

	/**
	 * This method sets the input buffer of the key listener. This is the buffer that
	 * will be sent as a command to the jerminal.
	 */
	public void setInputBuffer(String buffer);

	/**
	 * This method returns what is currently in the input buffer.
	 */
	public String getInputBuffer();

	/**
	 * This method sets the cursor index of the input buffer.
	 */
	public void setBufferIndex(int index);

	/**
	 * This method returns the cursor index of the input buffer.
	 */
	public int getBufferIndex();

	/**
	 * This method sets the JerminalWindow which this key listener should be reading
	 * keyboard input from.
	 */
	public void setJerminalWindow(JerminalWindow window);

	/**
	 * This method returns the JerminalWindow which this key listener is reading from.
	 */
	public JerminalWindow getJerminalWindow();

 

The EmacsKeyListener currently interprets the follow keystrokes when the interpret flag is set to true (The interpret flag is set to false in the case that the JerminalSession is executing a binary, where the keyboard input may not want to be interpreted). NOTE: a word is defined to be characters separated by a whitespace or a path separator character.
CTRL+A Move the cursor to the beginning of the line
CTRL+B Move the cursor back one character
ALT+B Move the cursor back one word
CTRL+C Keyboard break, this flushes whatever was in the current line
CTRL+D Delete the character at the cursor
ALT+D Delete the word in front of the cursor
CTRL+E Move the cursor to the end of the line
CTRL+F Move the cursor forward one character
ALT+F Move the cursor forward one word
CTRL+K Delete all the characters from the cursor to the end of the line and store them in the Kill buffer
CTRL+L Refresh the screen (redraw the JerminalWindow)
CTRL+N Display the next command in the command history
CTRL+P Display the previous command in the command history
CTRL+Y Yank the characters in the Kill buffer and insert them at the cursor
UP arrow Display the previous command in the command history
DOWN arrow Display the next command in the command history
LEFT arrow Moves the cursor back one character
CTRL+LEFT arrow Moves the cursor back one word
ALT+LEFT arrow Delete the word behind the cursor
RIGHT arrow Moves the cursor forward one character
CTRL+RIGHT arrow Moves the cursor forward one word
ALT+RIGHT arrow Delete the word in front of the cursor
INSERT Toggles the insert flag. If the insert flag is turned off, characters typed will overwrite the contents at the cursor instead of inserting them
END Move the cursor to the end of the line
ENTER (Return) This key takes whatever is in the current line and flushes it to the JerminalSession as well as inserting a newline at the end of the line so that it is displayed in the JerminalWindow
HOME Move the cursor to the beginning of the line
DELETE Deletes the character behind the cursor
BACKSPACE Deletes the character behind the cursor
ALT+DELETE Deletes the word behind the cursor
ALT_BACKSPACE Deletes the word behind the cursor
TAB This key will cause the EmacsKeyListener to attempt to complete the current characters (the word) that the cursor is on. It will complete filenames, pathnames, and command names (if the cursor is on the first word of the line)
ESCAPE Insert a '\' at the cursor (The escape character)
SHIFT+PAGE DOWN Scroll down in the window
SHIFT+PAGE UP Scroll up in the window
Everything else All other keystrokes read will be interpreted as a character to be inserted at the cursor

 

The DumbTerminal class

The DumbTerminal class provides a static void main(String[]) method that initializes a JerminalWindow, JerminalSession, and an EmacsKeyListener. The arguments given to the main method are expected to be name=value pairs. All the name=value pairs are stored as properties in a Props object that is used to initialize the JerminalWindow. When the JerminalWindow is initialized with its startup properties, a JerminalSession is initialized and given to the JerminalWindow. Then the EmacsKeyListener is added to the JerminalWindow as its KeyListener. The JerminalSession is initialized with PipedInput/ PipedOutput streams, one that allows the JerminalKeyListener (EmacsKeyListener) to write key events to and one for the DumbTerminal to read output from. If the remote_host and remote_port properties are given, the DumbTerminal will connect the streams to a Socket that is opened to the specified host and port. The original intent of this functionality is for the DumbTerminal to connect to a remote JerminalSession, but nothing is to prevent you from connecting to any tcp socket.

Here is an example of starting the DumbTerminal class from a command line:

[3:57am][comet-ttyp7][/tmp]> j com.cometway.jerminal.DumbTerminal font=Monospaced font_size=10 use_std_out 
tab_width=20 scroll_back_size=1000 jerminal_props=.jerminal 
commands_directory=./classes/com/cometway/jerminal/commands/
This example will start a DumbTerminal, which opens a window with a JerminalSession inside. The properties that the JerminalWindow recieves are: font (set to Monospace), font_size (set to 10), use_std_out (set to nothing), tab_width (set to 20), scroll_back_lines (set to 1000), jerminal_props (set to the file .jerminal), and commands_directory (set to the directory ./classes/com/cometway/jerminal/commands/). Here is a list of all the valid properties (at the writing of this document) and what part of the Jerminal uses it. All these properties are accepted by the DumbTerminal as name=value pairs on the command line.

Properties used by the JerminalWindow
font This property is the Font that the JerminalWindow will use. The default font is DialogInput.
font_size This is the size of the font that is used. The default font size is 9.
bg The background color represented by 3 sets of 3 digit integers (no spaces) corresponding to the RED, GREEN, and BLUE values of the color. The default color is 000000000.
fg The foreground color represended by 3 sets of 3 digit integers (no spaces) corresponding to the RED, GREEN, and BLUE values of the color. The default foreground color is 255000000.
x_offset This is the offset in the positive X direction that everything will be drawn from. Increasing this number will move the contents of the window to the right. This property is used to adjust for difference in the drawing origin (0,0) when using java AWT under different platforms and different java VMs. The default x_offset is 6
y_offset This is the offset in the positive Y direction that everything will be drawn from. Increasing this number will move the contents of the window to the down. This property is used to adjust for difference in the drawing origin (0,0) when using java AWT under different platforms and different java VMs. The default y_offset is 23
xCharOffset This is the offset of pixels on the left and right of the text drawn in the window. Increasing this will increase the space left of the text and right of the text. The default value of this property is 3.
yCharOffset This is the offset of pixels on the top and bottom of the text drawn in the window. Increasing this will increase the space above of the text and below of the text. The default value of this property is 4.
xFrame This is dynamically calculated offset of the pixels to the left of the text in the window. This value is used when calculating the number of columns of characters that will fit in the window. The default value is 10.
yFrame This is dynamically calculated offset of the pixels to the above of the text in the window. This value is used when calculating the number of rows of characters that will fit in the window. The default value is 30.
initial_width This is the initial width of the window in terms of how many columns of characters the width of the window will accomodate. The default value of this property is 80.
initial_height This is the initial height of the window in terms of how many rows of characters the height of the window will accomodate. The default value of this property is 45.
scroll_back_lines This is the number of lines the String array of the JerminalWindow will have. This is the maximum number of lines that will be saved. The default value of this property is 250.
scroll_lines This is the number of lines up or down each successive scroll up or scroll down will move. The default value is 25; however, when the window is resized, this value is changed to two thirds the maximum numbers of rows of characters the window will accomodate.
tab_length This sets the length of a tab. The tabs themselves are not stored in the window's String array, they are replaced with the appropriate number of spaces.
fixed_font_width In the case that the platform or java VM does not support FontMetrics, these properties can be used to specify the width in pixels of the font that you are using. If this property is not provided, the window will attempt to find the width using the FontMetrics.
fixed_font_height In the case that the platform or java VM does not support FontMetrics, these properties can be used to specify the height in pixels of the font that you are using. If this property is not provided, the window will attempt to find the height using the FontMetrics.
 

Properties used by the DumbTerminal

use_std_out This property does not need a value. If this property exists, the DumbTerminal will set the System.out OutputStream to the PipedOutputStream that is being read from. Thus, whatever is written to System.out will be displayed in the JerminalWindow.
use_std_error This property does not need a value. If this property exists, the DumbTerminal will set the System.err OutputStream to the PipedOutputStream that is being read from. Thus, whatever is written to System.err will be displayed in the JerminalWindow.
remote_host If this property exists, the DumbTerminal will open a Socket to this host and connect the Jerminal to the Socket instead of a local JerminalSession. This property is used in conjunction with the remote_port property.
remote_port This property is used in conjunction with the remote_host property. It will be the port that the DumbTerminal will open a socket to.
jerminal_props The value of this property should be a filename on the local file system which has a list of name=value pairs separated by newlines. These properties are read in and used to initialize the JerminalSession that is started. If the remote_host and remote_port properties are used, this property is ignored.

The list of valid JerminalSession properties (as of the writing of this document) are:
home_directory, prompt, ignore_string, path_separator_char, TERM, command_prefix_list, use_default_aliases, history_size

use_default_aliases This property is passed to the properties used to initialize the JerminalSession that the DumbTerminal creates. The session will load a set of default classname aliases. They are:
classAliases.put("String","java.lang.String");
classAliases.put("Object","java.lang.Object");
classAliases.put("StringBuffer","java.lang.StringBuffer");

classAliases.put("Date","java.util.Date");
classAliases.put("Vector","java.util.Vector");
classAliases.put("Hashtable","java.util.Hashtable");

classAliases.put("Props","kinetoscope.util.Props");
classAliases.put("Pair","kinetoscope.util.Pair");
classAliases.put("StringTools","kinetoscope.util.StringTools");
classAliases.put("DebugTools","kinetoscope.util.DebugTools");
classAliases.put("HTTPLoader","kinetoscope.net.HTTPLoader");

classAliases.put("JAK","com.cometway.jak.JAK");
classAliases.put("JerminalSession","com.cometway.jerminal.JerminalSession");
 

Properties used by the EmacsKeyListener

commands_directory This is the directory that contains the classes for the JerminalCommand classes. These classes are used by the EmacsKeyListener to do completion on commands, since commands used by the Jerminal are dynamically loaded into memory. If this property is not provided, or invalid (they do not contain the class files), the keyboard interpreter will not be able to complete on commands.

 

 

SECTION 3: Extensions and other applications.

The Jerminal classes are broken down into extensible classes and classes where the functionality was not meant to be changed. The JerminalWindow and JerminalSession can be subclassed for specific purposes but they may not be able to interact with the other Jerminal classes the same way. However, the interfaces JerminalListenerInterface and JerminalKeyListener provide abstractions for other implementations. And finally, the DumbTerminal class plugs everything together.

The JerminalListenerInterface interface is implemented by classes that want to listen to JerminalSession events. When the session is executing a binary, the DumbTerminal must set the JerminalKeyListener's interpret flag to false so that keyboard input is no longer interpreted and should be sent to the session as is. When an invalid command is given to the session, the DumbTerminal will make the JerminalWindow visually beep (flash). When the JerminalSession is exiting (it recieved an exit command), the DumbTerminal must stop reading the output of the session and dispose of the window. Here is an example of a simple class that implements the JerminalListenerInterface. The output of the JerminalSession that is created is sent to System.out and the input is read from System.in.

import java.io.*;
import kinetoscope.util.Props;
import com.cometway.jerminal.*;

public class Listener implements JerminalListenerInterface, Runnable
{
	boolean stopRunning;
	Thread runThread;
	
	public Listener()
	{
		runThread = new Thread(this);
		runThread.start();
	}
	
	public void notifyRunningBinary()
	{
		System.out.println("Running binary...");
	}
	
	public void notifyFinishedRunningBinary()
	{
		System.out.println("Finished running binary...");
	}
	
	public void notifyInvalidCommand()
	{
		System.out.println("BEEP");
	}
	
	public void notifyExecutingCommand(String command)
	{
		System.out.println("Executing command: "+command);
	}
	
	public void notifyFinishedExecutingCommand(String command)
	{
		System.out.println("Finished executing command: "+command);
	}
	
	public void notifyJerminalExiting()
	{
		stopRunning = true;
		System.out.println("EXITING");
	}
	
	
	public void run()
	{
		try {
			OutputStream out = null;
			InputStream jIn = null;
			
			jIn = new PipedInputStream();
			out = new PipedOutputStream((PipedInputStream)jIn);
			
			JerminalSession jerminal = new JerminalSession(jIn,System.out,new Props(),true,true,true);
			jerminal.addListener(this);
			jerminal.start();
			
			while(!stopRunning) {
				out.write((char)System.in.read());
				out.flush();
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args)
	{
		new Listener();
	}
}
If we wanted to use a JerminalWindow and a JerminalKeyListener for the output and input, we would change the run() method to look like this:
	public void run()
	{
		try {
			InputStream in = null;
			OutputStream jOut = null;
			OutputStream out = null;
			InputStream jIn = null;
			
			in = new PipedInputStream();
			jOut = new PipedOutputStream((PipedInputStream)in);
			jIn = new PipedInputStream();
			out = new PipedOutputStream((PipedInputStream)jIn);

			// create the window and key listener
			JerminalWindow window = new JerminalWindow(new Props());
			JerminalKeyListener keys = new EmacsKeyListener(window);
			window.keyListener = keys;
			window.addKeyListener(keys);

			// create the session and start it
			JerminalSession jerminal = new JerminalSession(jIn,jOut,new Props(),true,true,true);
			window.jerminal = jerminal;
			window.jerminalIn = in;
			window.jerminalOut = out;			
			jerminal.addListener(this);
			jerminal.start();

			
			// read from the key listener (in)
			while(!stopRunning) {
				int i = in.read();
				char c = (char)i;

				// make a new line if we read a newline
				if(i==10 || i==13) {
					window.makeNewLine();
				}
				// insert a tab if we read a tab
				else if(i==9) {
					window.insertTab();
				}
				// anything else, write it to the window and move the cursor
				else {
					window.insertAtCursor(c);
					window.moveCursorX(1);
				}
				// redraw the window
				window.refresh(true);
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}

The JerminalKeyListener interface is implemented by KeyListeners to interpret the keyboard input from the JerminalWindow. This allows any prefered editing style to be implemented (vi keys, MS Word keys, etc...) and shell functionality to be mapped to keys. Keys can even be mapped to JerminalSession commands.

The JerminalWindow can be used in many different ways without the rest of the Jerminal classes. It can be used by applications to display various levels of debugging output, where no keyboard input is read and the windows can be arbitrarily closed or opened. It can be used to implement a text editor or a console. Here is an example of using the JerminalWindow where the OutputStreams debug0-9, are various streams used to output various levels of debugging (NOTE: this will not compile):

public class DebugManager
{
	JerminalWindow window;

	// The magic class provides me the debug OutputStreams used by my application
	public DebugManager(SomeMagicClass someClass)
	{
		Props windowProperties = new Props();
		window = new JerminalWindow(windowProperties);
		new StreamReader(new PipedInputStream(someClass.debug0),"DEBUG[0]");
		new StreamReader(new PipedInputStream(someClass.debug1),"DEBUG[1]");
		new StreamReader(new PipedInputStream(someClass.debug2),"DEBUG[2]");
		new StreamReader(new PipedInputStream(someClass.debug3),"DEBUG[3]");
		new StreamReader(new PipedInputStream(someClass.debug4),"DEBUG[4]");
	}

	// This Threaded inner class reads from the debug streams and gives them to the window to be displayed
	class StreamReader extends Thread
	{
		String prefix;
		InputStream in;

		public StreamReader(InputStream readStream, String outputPrefix)
		{
			super();
			prefix = outputPrefix;
			in = readStream;
			start();
		}

		public void run()
		{
			try {
				BufferedReader reader = new BufferedReader(new InputStreamReader(in));
				String line = reader.readLine();
				while(line!=null) {
					window.insertAtCursor(line);
					window.makeNewLine();
					window.refresh(true);
					line = reader.readLine();
				}
			} catch(Exception e) {;}
		}
	}

}

You can view the javadocs here.


Last modified: Sat Sep 29 11:17:45 EEST 2001