Running the J-Kernel and Using Resolvers
To start the J-Kernel, use your favorite JDK1.1 compatible Java virtual machine to run the program cornell.slk.jkernel.std.Main. For example, if your virtual machine is called "java", then type:
java cornell.slk.jkernel.std.Main
In order for this to work, your class path needs to include a directory where cornell/slk/jkernel/std/Main.class can be found. The easiest way to ensure this is to run the java virtual machine from the directory jk-0.91/bin.
When Main runs with no arguments, it prints out a list of the command line arguments that it can accept. The most important command line argument is the name of the class that Main should run once it has started. This class should be a public class with a public, static method named main, taking an array of Strings as an argument. Let's look at an example. The following class prints a message to System.out and then exits:
// Hello.java public class Hello { public static void main(String[] args) { System.out.println("Hello."); } }
Cut and paste this code into a file named Hello.java, and compile it to produce a file Hello.class. Now you can run this class under the J-Kernel as follows:
java cornell.slk.jkernel.std.Main Hello
Main searches the class path to find the file Hello.class that contains the bytecode for Hello. Therefore, when you run the command line above, both cornell/slk/jkernel/std/Main.class and Hello.class must be in your class path.
So far, this looks simple enough. But there's more going on than meets the eye. When Main starts, it creates a task for itself, and then spawns a child task to run the Hello class. Each task gets its own version of every class that it uses (although some classes may be shared among tasks; we'll say more about this later). In particular, Main's task and Hello's task each get their own System class. So the field System.out referred to by Hello is not the field as System.out in Main's task. For instance, if Hello decides to change its System.out to something else:
// Hello.java public class Hello { public static void main(String[] args) { System.setOut(System.err); System.out.println("Hello."); } }
this won't affect the System.out in any other tasks.
This magic is made possible by Java's dynamic class loading facilities. Each task gets its own class loader, which turns Java bytecode into living, breathing Java classes at run-time. The J-Kernel provides a simplified interface to Java's class loading system that hides some of the difficulties (and dangers) of Java's own ClassLoader class. Each J-Kernel task contains one or more resolvers which are queried any time the task needs to know what bytecode to use to create a particular class. A resolver is anything that implements the Resolver interface; you can write your own resolvers that fetch bytecode from a particular location, or you can use some of the built-in resolver classes such as FileResolver.
In the example above, the resolver for Hello's task is queried for the class names "Hello" and "java.lang.System" (as well as some other classes). Main sets up a resolver that looks through directories based on the class path to find these bytecode for these classes. However, you can easily add your own resolvers to the task while it is running. Let's dynamically load the following class into Hello's task:
// Friend.java public class Friend { public Friend() { System.out.println("You've got a friend in Pennsylvania."); } }
Compile this class and move the class file (Friend.class) to some directory that's not in the class path, so that Hello's initial resolver won't be able to find it. Now recompile a new version of Hello and run it:
// Hello.java public class Hello { public static void main(String[] args) throws Exception { System.out.println("Hello."); Class c = Class.forName("Friend"); Object o = c.newInstance(); } }
When you run this, Class.forName looks for a class named "Friend", which it cannot find. Therefore it throws a ClassNotFoundException which may look something like this (depending on your virtual machine):
Hello. J-Kernel class loader exception: >>> java.lang.ClassNotFoundException: class Friend not found in task "task_2": resolver returned null at cornell.slk.jkernel.core.TaskClassNodeLoader.getClassNode(TaskClassNodeLoader.java:146) at cornell.slk.jkernel.core.DClassLoader.loadClass(DClassLoader.java:163) at cornell.slk.jkernel.core.DClassLoaderStandard.loadClass(DClassLoaderStandard.java:24) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313) at Hello.main(Hello.java:7) at cornell.slk.jkernel.std.StartImpl.runMain(StartImpl.java:37) at cornell.slk.jkernel.std.StartImpl$STUB$LRMI.runMain(Unknown Source) at cornell.slk.jkernel.std.RootTask.start(RootTask.java:229) at cornell.slk.jkernel.std.RootTask.run(RootTask.java:37) at cornell.slk.jkernel.core.CrossTaskThread.run(CrossTaskThread.java:44) <<<
In fact, this exception may show up many times on the screen. By default, the J-Kernel prints an exception's stack trace in several different places to ensure that the necessary information gets out at least once. Don't be alarmed at the volume of output here. There are a few things to note about the exception:
Of course, this example would be more satisfactory if it succeeded, instead of throwing a ClassNotFoundException. Therefore we need to add a resolver to the task which is able to find the bytecode for the class Friend. There are a number of off-the-shelf resolvers available in jkernel.util. For this example, a FileResolver, which looks for bytecode in a particular path in the file system, is useful. The following code adds a FileResolver to Hello's task (you should substitute the path where Friend.class can be found on your system for "yourpath"):
// Hello.java import cornell.slk.jkernel.core.Task; import cornell.slk.jkernel.util.FileResolver; public class Hello { public static void main(String[] args) throws Exception { System.out.println("Hello."); Task.addResolver(new FileResolver("yourpath")); Class c = Class.forName("Friend"); Object o = c.newInstance(); } }
If all goes well, the new resolver will find Friend.class, and the expression "c.newInstance()" will instantiate a new Friend object, which causes Friend's constructor to run:
Hello. You've got a friend in Pennsylvania.
Next: Creating New Tasks