Find out how to build programs that you can extend dynamically -- even while they execute
Most Java programs are written in this (albeit grossly over-simplified) fashion:
- A programmer writes, compiles, and debugs a bunch of code.
- Then he (or, increasingly, she) distributes the completed program.
The point Iโm trying to illustrate is that once the program is written, it contains all of the features it will ever have. If new features are ever necessary, the programmer must go back and edit the source code to include the new features, recompile the code, and redistribute the updated program.
The fact that most Java programs are written in this fashion is the result of Javaโs C and C++ heritage: Both C and C++ are statically linked languages, which means that all of the pieces that make up a C or C++ program must be present at the link step. Couple this with the fact that many Java programmers migrated from a C and/or C++ environment, and you can see why this type of programming is the norm. The thing is, in Java, we are not limited by the language in this way. Java, as you will soon see, allows programs to be dynamically extensible โ that is, to dynamically load in and execute new code, including code that wasnโt in existence when the program was written. Sound far-fetched? Itโs not. Whatโs more, Iโm betting you use at least one program like this every day.
To make the distinction clear, Iโll call programs written as described at the beginning of the article as โnot dynamically extensible.โ Now, I donโt want you to think that not being dynamically extensible is some sort of a flaw. That is not necessarily so. Some programs shouldnโt be dynamically extensible. However, many programs, including most shrink-wrapped applications, would benefit users greatly if they were.
Just try to imagine a Web browser written in Java that could not load and execute Java applets, which are dynamically loaded into the browser. The thought is almost comical โ thatโs essentially the whole reason for writing a browser in Java. A browser, therefore, is a good example of a program that should be dynamically extensible. (Didnโt I tell you that you probably used a dynamically extensible program daily?)
I bet youโre wondering why this feature is important for programs other than browsers. Well for one thing, if a program is dynamically extensible, you donโt need to modify the main body of source code in order to add new features. This is a great blessing for two reasons. First, editing working code can introduce bugs. Second, end users donโt need access to the original source code.
Dynamic extensibility is important for another reason, as well. If the program is written correctly (and Iโll show you how), you donโt even have to restart the program to pick up the additions. Consider a browser once again. If it encounters an applet tag in an HTML document, it locates the applet code, loads it, and executes it without the user ever having to exit the browser. This is a good thing.
The technique Iโm about to describe will work under both Java 1.1 and Java 1.0. It doesnโt use JavaBeans, class introspection, or class serialization (APIs provided only with Java 1.1). Instead, it makes use of features that have been in the language and the class library since the beginning.
The tools youโll need
Letโs stop for a moment and look at the relevant pieces in the Java class library.
java.lang.Class
Instances of class Class represent classes and interfaces in a Java application. The Java virtual machine (JVM) usually manipulates classes behind the scenes; however, a program can directly manipulate classes via instances of the Class class.
Here is the public interface to class Class.
public final class java.lang.Class extends java.lang.Object
{
public static Class forName(String className);
public ClassLoader getClassLoader();
public Class [] getInterfaces();
public String getName();
public Class getSuperclass();
public boolean isInterface();
public Object newInstance();
public String toString();
}
We only need to look at the forName() and newInstance() methods. Weโll deal with the forName() method first.
static Class forName(String className)
The forName() method returns the Class instance associated with the class with the specified class name. It loads the class into the executing program via the currently active class loader. The className parameter can name any class โ even one that does not exist currently. If the forName() method cannot find the named class, it throws a ClassNotFoundException.
Now letโs take a look at the newInstance() method.
Object newInstance()
The newInstance() method creates a newly allocated instance of the class represented by the Class instance. Because this is done exactly as if by a new expression with an empty argument list, the class must define a no-arg constructor. If the newInstance() method cannot instantiate the class for any reason, it throws an InstantiationException. If the class or constructor is not accessible, it throws an IllegalAccessException.
Putting the tools to use
I stated earlier that this technique is easy to use. Now I intend to prove it. Hereโs all of the code necessary to dynamically load a class file into an executing application and create an instance of the class:
Object voila(String str)
{
Object o = null;
try
{
Object o = Class.forName(str).newInstance();
}
catch (Exception e)
{
System.out.println(e);
}
return o;
}
The try..catch statement is necessary in order to catch any errors or exceptions that are raised during loading and instantiation.
Notice how the string is passed down. Any string is possible as long as it names a valid Java class. The string can be obtained in any number of ways: The user can input it, the program can read it from a configuration file, or the user can pass it in on the command line. In short, the program doesnโt have to know its value at compilation time.
If you want to interact with the string, youโll probably want to cast it to an instance of a class of the appropriate type. The following code shows how:
Foo f = null;
if (o instanceof Foo)
{
f = (Foo)o;
}
This last step illustrates one of the limitations of this technique: The new class must either be a subclass of a class known to the program at compile time, or it must implement an interface known to the program at compile time. Although itโs possible to load any class (because all classes technically are subclasses of the Object class), itโs not particularly useful; while classes can be dynamically specified, their methods cannot. This limitation is one of the reasons the Introspection API was introduced as part of Java 1.1.
How does it stack up?
Iโve shown you the technique for extending your applications as they execute. Now itโs time for me to put my money where my mouth is and show you a functional demo. Luckily, I am prepared. Take a look at the applet below.
This applet is an implementation of a simple stack-based machine, which consists of a stack of items โ in this case, a collection of zero or more numbers stacked one on top of another, just like in the figure below.
There are only two valid operations you can perform on this stack of numbers. You can either push a number on the top of the stack or you can pop a number off the top of the stack. The figure below illustrates these operations.
Take some time to peruse Stack.java, the source code for the stack. Stack.java is a subclass of Panel, and it displays the top five numbers in the stack.
The stack is embedded within a control panel. The control panel includes a field in which commands can be entered and a selector from which the previously entered command can be selected.
Believe it or not, this simple stack forms the basis of a very powerful calculating machine. All we need to complete the program are a few operators (add, subtract, and so on) to operate on the numbers in the stack. However, the operators can only operate on the numbers via the two operations mentioned earlier โ push and pop. Let me give you an example.
Consider an operator called add. This operator pops the top two numbers off the stack, adds them, and then pushes the result back on the stack. When the operation is complete, the stack will be one item shorter and the top two numbers will have been replaced by their sum.
Now add is only one of many different operators. I can think of several more off hand โ subtract, divide, factor, truncate, and convert to francs, to name a few. There are plenty more where those came from, but I wouldnโt want to have to sit down and think of them all, much less code them.
Which brings me to my next point. Rather than trying to think up and code all possible operators, Iโd like to build the framework and perhaps provide a few common operators. Iโll leave the fancy operators โ such as converting to francs โ for you to develop and add into the program as you need them.
With that in mind, take a look at a method that accepts a string, locates the correct class, loads it, and invokes the proper method:
private void invoke(String str) throws Exception
{
Double d = null;
// parse the string
try
{
d = new Double(str);
}
catch (NumberFormatException nfexp)
{
;
}
// if the string is not a number, it must be
// a operator... let's try it...
if (d == null)
{
_ch.addItem(str);
// load the class...
Class c = Class.forName("howto.operators." + str);
// create a new instance...
Object o = c.newInstance();
// cast it...
Operator op = (Operator)o;
// do it...
op.operate(_s);
}
else
{
_s.push(d.doubleValue());
}
}
Pretty simple, isnโt it? The complete code for the control panel is in Main.java.
Let me tell you a bit more about the applet before I conclude. You can push numbers on the stack by typing them in the text field. Once youโve pushed a few numbers, try out the following operators: add, subtract, clone, pop, rotate, and swap. I bet you can you guess what they do.
Conclusion
Consider for a moment more what a powerful tool Iโve left you with. Even though I didnโt write every conceivable operator, Iโve made it possible for you to add those you need, when you need them, without ever having to touch the original source code. Thatโs not a convenience to be lightly dismissed. Itโs certainly something Iโd like to see more of. Application developers, I hope youโre listening.


