The TKJavaRunner Application
What would such a class launcher look like? Below we have a trivial
implementation called TKJavaRunner. For your convenience, I have provided
an archive with all listings, class files, and executables in the Resources section below. To compile TKJavaRunner.exe I used the excellent lcc-win32 IDE and compiler package.
/*********************************
* *
* TKJavaRunner *
* *
* This program invokes the Java *
* runtime with a class name *
* which is built from the first *
* argument; further arguments *
* are passed to the Java *
* application *
* *
*********************************/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXLEN 256
int main(int argc, char **argv) {
char *path;
char *classname;
char *extension;
int result = 0;
char cmd[MAXLEN];
int i, len;
if (argc < 2) {
fprintf(stderr,
"TKJavaRunner [.class file} [argument1] [argument...]\n");
result = 1;
} else {
path = argv[1];
classname = strrchr(path, '\\');
if (classname == NULL)
classname = path;
else
*(classname++) = 0;
extension = strrchr(classname, '.');
if (extension == NULL)
result = 1;
else {
*(extension++) = 0;
sprintf(cmd, "java -cp \"%s\" %s",
path, classname);
len = strlen(cmd);
for (i = 2; i < argc; i++) {
len += (strlen(argv[i]) + 1);
if (len >= MAXLEN)
break;
strcat(cmd, " ");
strcat(cmd, argv[i]);
}
// fprintf(stderr, "%s\n", cmd);
result = system(cmd);
}
}
return result;
}
Let us have a look at the program. It expects at least one argument,
the complete path and name of the class file to launch. Additional
arguments will be passed to the class upon startup of the Java virtual
machine. Since java.exe expects a class name, we remove the path and the
extension. Then a new command line is constructed that contains the name
of the Java runtime launcher (java.exe) with several arguments:
- The
-cp option to add the directory that contains our class file to the class path.
- The class name without its extension.
- Any additional arguments.
The program assumes that Windows knows where to find java.exe; its
directory therefore has to be mentioned in PATH. If you work with Java at
the command-line prompt now and then, this is quite a good idea anyway.
But how do we use TKJavaRunner? We need to assign .class files to it.
Though the specifics may vary depending on your version of Windows, the basic
steps are as follows. First, open the Folder Options dialog, as shown in Figure 1.

Figure 1. Opening the Folder Options dialog
After that, check to see if .class is already a registered file type.
If this is the case, you will find an entry similar to the one shown in
Figure 2. If not, create an entry by selecting the New button.

Figure 2. Browsing the list of known file types
Once you have made sure .class is a registered file type, hit the
Advanced button to check the settings. Please note that in Figure 3 we
pass up to nine arguments to the program. Since %1 refers to the class
file itself, we need to make sure that additional arguments are passed to
our program. TKJavaRunner is a simple console-based program;
therefore, the other settings in this dialog box do not apply.

Figure 3. Configuring the .class association
There is one more problem we need to fix. Since we want our classes
to behave as native executables, we want to be able to launch them
without their .class extensions. Windows maintains a list of
extensions to be treated as executables. To modify this list,
right-click your My Computer icon on the desktop and select Properties from the contextual
menu. A dialog box will appear. Move to the Advanced tab. You
will notice a button labelled Environment. If you select that, another
dialog box will pop up. As you can see in Figure 4, there is a variable
called PATHEXT, which contains a semicolon-delimited list of extensions
of executable files. We just need to add .class to this list. After that
we can run our classes just like native executables.

Figure 4. Modifying the PATHEXT variable
Some Examples
Now that we have configured Windows to recognize .class files, let us do
some testing with cmd.exe. I have written two sample programs,
named FileSize and Sum. You can get the source and class files in the Resources
section at the bottom of this article. Put
at least the class files to your previously created bin folder,
so that cmd.exe can find them.
FileSize expects filenames as its arguments. For each given filename, it prints the size of the
file to the standard output. Sum does not take arguments, but reads
lines from standard input. Each line is treated as a decimal number.
Sum sums all numbers it has read up until an end of file signal is received.
It then prints the result to standard output.
Neither of these programs is particularly useful on its own, but if we combine them, we
can, for example, compute the size of a certain directory. Within cmd.exe type FileSize * > sizes.txt and hit Enter. You can check the result by executing type sizes.txt. After that, type Sum < sizes.txt. What happens here is that cmd.exe expands * to a list of all file (and directory) names in the current directory. These names are passed to
FileSize, which prints the sizes of the files. Since we redirected the
standard output of FileSize, we did not see anything on screen. The values were
written to a file named sizes.txt instead. With Sum we do the opposite; we
force it not to read from the keyboard but from the previously created file.
We could write a small batch file that takes one argument, the pathname of a
directory, and computes the sum of all file sizes within this directory.
Limitations of TKJavaRunner
So far, TKJavaRunner seems to do a good job. However, it has some
significant drawbacks, which I am going to cover in this section.
The first one is obvious: it does not handle .jar files. This is a minor
issue, since .jar files usually contain more complex, GUI-based applications.
On the other hand, the Java runtime usually registers .jar
as a file type, so if this .jar archive contains a manifest file that
specifies the class containing the main method, it can be launched with a
double click. And if we add .jar to the PATHEXT environment
variable, we can run the archive from cmd.exe as well.
Another minor drawback is that Windows launches TKJavaRunner,
which in turn starts the Java runtime. We might consider this a
performance issue, since it takes place each time a Java class has to
be launched (from cmd.exe). My assumption is, however, that we can ignore the
time needed to launch TKJavaRunner, especially when compared to
the time needed to bring up the Java runtime.
The biggest drawback is that TKJavaRunner cannot handle classes that contain a
package statement. In order to find packaged classes, the virtual
machine must know the base directory of the package, not the directory that
contains the class. But that is exactly what TKJavaRunner does. It determines
the path of the class and adds that directory to the classpath. To illustrate this, imagine you have put Sum.class into C:\bin. If you run it from cmd.exe,
C:\bin\Sum.class is passed to TKJavaRunner, which sets the classpath to C:\bin using the -cp option and determines Sum as the class name. Since Sum has no package statement, TKJavaRunner is right with its assumption. However, if we assign it to the package com.oreilly.tkuenneth, the class file should be in a directory
named some path\com\oreilly\kuenneth. To work correctly,
TKJavaRunner would need to add some path to the classpath
and determine com.oreilly.tkuenneth.Sum as the (fully
qualified) class name, which it, as we have already seen, does not.
The fact that packages produce a directory tree presents another problem.
Remember that cmd.exe finds class files because we added a
bin folder to PATH. To find a class nested deep inside of a
package hierarchy, we would need to add each directory that is part of this
tree to PATH. This is not practical, for the following reasons:
PATH becomes difficult to maintain.
- Each time a filename has to be resolved, all
PATH entries are scanned. To a certain extent, this might affect the performance of your machine.
Consequently, it seems reasonable not to package the main
class of programs you wish to access from the command line.
Of course, they may reference classes of other packages
if the Java runtime knows the path of the package base
directory. This, for example, applies to Java extensions,
which are put in the ext directory.
This is particularly convenient, since you can put
any .jar file there. Another option is to modify TKJavaRunner
to add another directory to the classpath using the -cp
option.
Conclusion
Java provides everything necessary to write console-based programs.
Since class files contain byte code, they cannot be invoked directly.
Instead, the Java runtime has to be launched first. To mimic the
behavior of native executables in Windows, we have used file associations
and an appropriate wrapper program. This allows us to treat class files
almost as .exe files. As I have mentioned earlier,
the shell plays a very important role in Unix-like environments.
Therefore it seems desirable to use Java classes in shell scripts
just like native executables in these environments, too.
Unfortunately shells do not know file associations, so there is no
common approach we could use for all Unix-like operating systems.
For Linux, there is a kernel module called binfmt_misc,
which adds support for the execution of non-native binary formats, which is based on a magic number or a file extension. This mechanism is similar to our approach using a wrapper
program. A file that contains a magic number or has an appropriate extension
is passed to its corresponding launcher program. I hope to discuss this Linux feature in a later article.
In Windows, the approach of using a launcher program does not
necessarily require a native program. What we could do instead is
assign .class files to java.exe with
a command line as follows: java.exe TKClassLauncher
"%1" "%2" ... "%9". The TKClassLauncher
class would act as our launcher program, so it determines the class
to be started, loads it, and invokes its main method.
Doing so has several advantages:
- We need no extra time to launch a native wrapper program; instead, the Java runtime is brought up immediately.
- We could even solve the problem regarding packages by implementing an appropriate
ClassLoader that does not depend upon a previously defined classpath.
Since dealing with the ClassLoader class is an interesting topic,
it might be worth a further article. In any case, I greatly
appreciate your feedback.
Resources
Source code and binaries for TKJavaRunner, FileSize, and Sum
Thomas Künneth works as a software architect at the
German authorities, specializing in Java-based rich clients.