This is the final version of the jsh project for the semester. You will be developing it in a sequence of steps, and this web page will be updated as new steps are introduced. The assignment will be due on midnight of the last day of exams, May 23.
The goal of the assignment is to allow a user of jsh to load Java class files into a "Persistent Java Virtual Machine" (PJVM) and to invoke methods within those classes by typing commands on the command line. The name PJVM refers to the fact that the JVM gets created once when jsh starts running, and continues to run (persists) until jsh exits. This is in contrast with a conventional JVM such as the one invoked by the java command, which is loaded and started for every Java program the user runs.
When the user types in a command line, jsh will read and tokenize it in the usual way. If it turns out that the command name is neither a shell builtin (alias, echo, exit), nor the name of an executable file, jsh is to write the command to a separate process, which will interpret the command line and load a class into a running jvm, and optionally invoke one of the class' methods.
If you have not done so yet, implement the alias builtin command and alias substitution. Use an STL map to implement the alias table. An alias command with no arguments should cause all aliases and their values to be displayed. The syntax for defining a new alias (or to replace an existing alias) can be either "alias name value" or "alias name=value" as you prefer.
Before starting on the remainder of the project, bring all project files up to RCS revision 2.1. You do this by checking each file out with the lock option, and then checking them back in with the "-f" and "-r2" options. The -r2 option (or -r2.1) checks the file in with the new version number. Normally, you can't check in a file if you haven't modified it in any way, but the -f option forces RCS to check the file in even though nothing changed. An appropriate log message would be something like, "Starting work on Version 2." Note that you can use a single ci command to check in all the files in the project. If you do, you will need to enter the log message just once for all of them.
If you add new files to the project after this, be sure to use the -r2 option the first time you check them in.
When you have this working, you will be able to make either version of the project by making the environment variable (or
makevariable) COFLAGS equal to -r1 or -r2. For example, in a clean project directory, you could type, "make COFLAGS=-r1" to build version 1 of the project. (Thought question: What is the difference between "make COFLAGS=-r1" and COFLAGS=r1 make"?)
For this step, have the main process fork a child process when it first starts up. We'll call this new process the jvm process and the main process the jsh process from now on. Before creating the jvm process, create a pipe for communicating between it and the jsh process. The purpose of this step is to manage interprocess communciation (IPC) between the jsh and jvm processes.
Your code is to implement the following IPC features:
Here is an example of what I mean by the last item in the above list:
jsh> da JVM: This is a message from the jvm ... jsh>date (output of the date command) jsh>
In this example, the user was in the middle of typing a "date" command (green characters), when a message arrived from the jvm process and was displayed on the user's screen. The red text is what the jsh printed on the screen before continuing to read the command the user was entering.
To test this version of your program, code the jvm process so it reads an initial message from the jsh process when it starts up, and sends this same message back to the jsh process at 5 second intervals for as long as it runs. Either the jsh or jvm process should display the jvm process' pid so you can experiment with killing the jvm process with a kill command from a second console window to see if that is handled properly.
If the user enters a command that is not a builtin command, the jsh process searches through the user's PATH, using the access() system call to determine whether an executable file can be found. (If the command name starts with . or / don't search the PATH, just test the explicit pathname the user entered.) If the command name fails the access() test, send the entire command as a message to the jvm process. The jvm process waits 5 seconds, and sends back a message saying "JVM: <user's command>"
Well, you know, the user types in executable Java statements, and the jsh process sends them to the jvm process, which executes them in the jvm. There are also commands for determining which classes are currently loaded into the jvm, for listing the methods available for each of those classes, and stuff like that. And a command for seeing how much memory the jvm is using. You know, like, other stuff too ...