by Mark Sienkiewicz
Mark Sienkiewicz has worked on many projects including software complexity tools, a physics experiment, traffic control systems, a firewall, etc. He is currently principal software engineer with Network Flight Recorder.
Write Once, Run . . . Where?
Java is intended to be a portable language. Sun makes so much of the phrase "Write Once, Run Anywhere" that they claim it as a trademark. But is Java really that portable? The answer turns out to be "almost." This article describes my experiences working on the Network Flight Recorder user interface.
The first question you have to ask when dealing with Java is "what version?" When we began in June 1997, the prevalent version of Java was 1.0.2. We considered whether we should write in Java 1.0 or Java 1.1 because we knew 1.1 would be available "real soon now."
Since then (only eight months ago as I write this), two new versions of Java have been released, and both made substantial changes to the standard libraries. In Java 1.1, they changed the GUI event-handling model. Java 1.2 is making still more changes to the user interface components.
Our user interface was intended to be Web-based, which meant we had to have a browser that could execute whatever version of Java we picked. At the time, Netscape 3 and Internet Explorer 3 were both widely used. They executed Java 1.0, so we wrote to Java 1.0.
We discussed writing to Java 1.1 to be used in Netscape 4, which was also due out "real soon now," but decided it would be difficult to develop for a platform that did not yet exist. Now Netscape 4 and Internet Explorer 4 are available, and they can run Java 1.0 or Java 1.1 code. So how do you choose a Java version?
There are really two choices:
Even though these newer browsers are now available for many systems, not everyone is going to rush out and upgrade immediately. Some people have things to do other than try to keep up with the latest version of everything. Some people have well-considered policies to be cautious about upgrades, based on the theory that upgrading means trading the bugs you know about for all new bugs you have never seen before.
NFR chose to stay with Java 1.0, rather than trying to sell a product while saying, "Yes, it's Web based, but it won't work with your browser."
Following this reasoning, the rest of this article relates to code written for Java 1.0, though it also applies to 1.0 code run in 1.1 environments.
How Portable Is It?
Java is much more portable than C. Assuming that you write "pure Java," the core features of the language really do live up to the promise of "Write once, run anywhere." I've never seen any instance of basic functionality that is different from platform to platform.
The real portability problems show up when you try to write something graphical. Because Java is being pitched primarily as a language for GUI-like programming, I found this somewhat surprising.
The problem occurs because Java does not implement GUI components directly. Instead, it uses "peer objects." These peer objects are constructed from the native GUI components for the system you are running on. For Netscape, somebody wrote a peer object that uses Motif buttons. For Internet Explorer, somebody wrote a peer object that uses Microsoft Windows buttons. Initially, this looks like a very elegant solution, but in practice it turned out to be a mistake because it did not work well when it was handed off to third parties to implement.
In your program, you just use java.awt.Button to get a button, so your interface is portable. In principle, you never have to know that there is a Microsoft Windows button that implements that object. In practice, though, the objects do not all behave the same. The incompatibilities fall into two different categories that are sometimes hard to distinguish.
Some of Java's portability problems come from the poor documentation. The AWT documentation does not always clearly state what an object promises to do.
For example, consider java.awt.Scrollbar. According to the documentation, AWT sends a "scroll absolute event if the user drags the bubble." But when? It doesn't say. In fact, the Netscape version of java.awt.Scrollbar sends a scroll event for every mouse move event. The Internet Explorer version of java.awt.Scrollbar does not send a scroll event until after you release the mouse button.
Each author could make a reasonable case that his or her implementation complies with the documentation. After all, the differences exist only in the unspecified area. Only the user who looks at both implementations will get confused.
The other major problem is that some things just don't work. To demonstrate a few examples of the problems, I put together a small applet that creates a window with some various objects on it..
Here are a few examples that you can demonstrate with this applet:
I have also encountered some other odd problems. For example, the NFR user interface displays only an empty window when run in HotJava. This was surprising, because I thought that HotJava would be the one place where everything would work. I believe I could have found the problem, but at the time I was also evaluating HotJava and found that it was incredibly slow as a browser. I did not try very hard to find the problem, assuming that not many people were using it.
What To Do?
With the current set of runtime environments, writing portable Java is very much the same as writing portable code in other languages: there are lots of little annoying things that differ from platform to platform. You have to write code that works on all of them.
It is important to remember that it does not matter which environment "works" and which is "broken." What matters is that there are differences between them, and you have to account for that.
Java has no conditional compilation, so you can't easily compile shared code with minor differences for different platforms. It doesn't make sense to the Java mindset you don't know the target platform at compile time. You might have your Web server send different class files based on the user's browser type, but that seems likely to result in its own set of problems.
java.lang.System contains a method to ask about system properties such as the Java version number, a vendor-specific string, and the operating system name. You would think you could ask for the vendor-specific string and implement workarounds based on the environment you are running in, but that doesn't work. It is a security violation to read the system properties.
You can get much of the same information by having your Web server tell you. As part of HTTP transactions, the browser sends a version string that is available to cgi-bin programs in the environment variable HTTP_USER_AGENT. If you really wanted to, you could fetch that data out and then pass it in to the applet as a parameter.
The easiest solution, though, is to stick to a minimal feature set that works in all your target environments. For example, you don't have problems with setting colors if you always use the default colors.
In any other language, you would write a portable program by testing it on all of your desired target platforms. The same rule applies to Java.
This is the solution we used in the NFR user interface. The original design had an interesting color scheme. After finding out all the different ways that objects fail on different platforms, it became apparent that the best solution was to use the default color scheme.
Of course, it would always be possible to write your own Button, your own Scrollbar, etc. This is a viable solution if your time is not valuable, but it doesn't seem appropriate in a commercial development effort.
Keep in Mind the Alternatives
We want "Write Once, Run Anywhere" to reduce work for the programmer. Ultimately, the question is less whether "Write Once, Run Anywhere" is a perfect reality as it is whether it is better than the alternatives.
The NFR user interface consists of about 20,000 lines of Java. It could easily take substantially more to write an equivalent user interface in a more conventional language like C.
If I were to write in C, I would have to write the user interface twice once for UNIX (with all the normal UNIX-UNIX portability issues) and again for Microsoft Windows. Surely I could share some code between the two with careful design, but most of the work would be in the nonportable GUI area.
By comparison, the same Java code implements the same GUI on both platforms. There are some minor portability issues in the GUI area, but these are not nearly as severe as the difference between the X and Microsoft programming models.
Apart from the GUI components, Java is highly portable. I have not found any fundamental features that behave oddly on different platforms. The GUI components themselves have problems, but you can still write highly portable programs if you are willing to test on your various target platforms and avoid or work around portability issues. It has not yet reached the ideal, but the amount of work attributed to "porting" is substantially less than common alternatives.