Check out the new USENIX Web site.
Using JavaUSENIX

 

by Rik Farrow
rik@spirit.com

April, 1997

As I hop aboard my time machine (I write this in February, you read it in April or later), the JDK, version 1.1, is still in Beta. I have had a chance to look more at the changes and must say that in general I am pleased with what I see.

Not that I have learned everything new yet. There are teams of programmers working on Internet time updating Java and individuals out in the hinterlands, like myself, to discover what's new. One of the obvious ways is to download the JDK API documentation and begin to pore over it. There are a lot of changes.

I did get a chance to look into the internationalization features. The java.text package includes classes for describing messages, numbers, dates, times, and currencies in a portable format. Doing so adds complexity to your application (which should not be surprising). The text package also includes support for collation (sorting) under different languages and determining word boundaries, for text formatting, which may also be different. The examples provided are copyrighted by Taligent, a wholly owned subsidiary of IBM. You begin to see why I said "teams of programmers."

Java Beans

Java Beans, a method for creating controls à la ActiveX, but in a portable manner, has also become part of the 1.1 release. ActiveX is what was once called OLE 2 by Microsoft and relies on Microsoft applications to work properly. ActiveX controls are not portable between architectures, making them poor competition for Java Beans. Well, there is the problem that many desktops already include Microsoft applications.

But there is another problem with ActiveX, and that is security. An ActiveX control has the same privilege as the person who activates the control, say by uploading it or selecting it. Microsoft has handled the security of ActiveX by creating a mechanism for digitally signing each control, so if you trust the signing principle, and the certificate authority, you can trust the control.

If this sounds too complex to work, well, I agree. And to make things more interesting, some hackers have demonstrated using a signed, ActiveX control that made deposits through Quicken. In other words, just signing an ActiveX control won't stop it from transferring all the money in your checking account to some third party.

The JDK 1.1 has added some security features. Java Bean controls would execute in the "sandbox" created by the SecurityManager. But JDK 1.1 has also added methods for signing classes, encryption, and even access control lists (ACLs). The java.security package contains 13 new classes, plus a set of interfaces for digital signatures and the ACL package. The NIST digital signature standard is used (and implemented in a Windows and Solaris application named javakey), and X.509 key certificates are used to verify public keys.

Naturally, Microsoft and Sun have created their own "standards" for signing ActiveX controls or Java classes. Java and ActiveX digital signature mechanism do not interoperate, and JDK 1.1 does not attempt to deal with setting up certificate authority hierarchies. But, in the future, it will be possible to upload an applet or class and have the virtual machine grant it greater or lesser access to your system based on ACLs authenticated with digital signatures. ActiveX does not have a comparable level of control over access to the system. Hmm, I wonder if Bill Gates uses Quicken . . . .

The Main Event

There's more, of course. Rather than rambling on, I have decided to focus on an area that I needed to learn pronto, and that is the new way that events are handled.

Events are generated by a user interacting with components, the windowing system (frames and associated controls), or the keyboard and mouse. In the JDK 1.0, event handling was a mess, and I am happy to say things got better (rather than worse).

Let's start with the past. Every Java component (most of the AWT package except layout managers) has or inherits the handleEvent() method. This default method does very little, except for calling subevent handlers, such as action() or mouseDown(); and if you wanted your application to exit when the user selected the Quit ornament on the window frame, you had to override handleEvent(). You could put off handling a particular event (leaving it for a super class to handle) by simply returning false from handleEvent(). This technique made it difficult to know where in the code events would be handled.

The old event-handling mechanism played havoc with GUI design tools, too. And it wouldn't fit in with the goals of Java Beans, which include the ability to be queried for attributes, configured, and connected to events handlers in a portable way, without source code.

In many windowing systems, event handling is done by informing the component which functions need to be notified when an event occurs. As Java is object oriented, this approach is the wrong way to do things. Instead, you now register interest in an event by adding event listeners. Any object can be an event listener, which makes it possible to create a class that does nothing but handle and dispatch events.

To demonstrate, I've created a short, working demo that shows off two types of listeners. As in the old model, where there were many different events generated by different components (Buttons, Lists, etc.), the new model has a dozen different event listeners. For this example, I'll use just two of the more popular ones, an ActionListener and a WindowListener. Click here for the complete example.

import java.awt.*; 
import java.awt.event.*;

public class Timer implements Runnable { 
    Thread timer; 
    int counter = 0;
    TextField text; 

    public Timer() { 
        Frame f = new Frame("Timer demo");
        f.setLayout(new GridLayout(2,0));
        f.setResizable(false); 
        // Create three event listening objects
        Command startCmd = new Command(Command.START, this);
        Command stopCmd = new Command(Command.STOP, this);
        Command resetCmd = new Command(Command.RESET, this);
        // Assign a listener to the frame (window)
        f.addWindowListener(stopCmd); 
        Panel p = new Panel();
        Button b; 
        // Create three buttons, assigning each a listener 
        p.add(b = new Button("Start"));
        b.addActionListener(startCmd);
        p.add(b = new Button("Stop"));
        b.addActionListener(stopCmd);
        p.add(b = new Button("Reset")); 
        b.addActionListener(resetCmd);
        f.add(p); 
        // Set up a TextField for displaying results 
        text = new TextField(" 00 ", 4); 
        text.setEditable(false); 
        text.setFont(new Font"Helvetica", Font.BOLD, 40));
        f.add(text);
        f.pack(); 
        f.show(); 
}
The constructor for Timer class creates the frame (window), sets up a Grid layout of two rows, and disables resizing. Next, three Command objects are created, which will be used as listeners. One of the Command objects is added as a WindowListener for the frame. Then three buttons (stop, start, and reset) are added to a panel, and the panel is added to the frame. A TextField, initialized to "00", is added to the second Grid element in the frame.
public void Do (int id) { 
    switch (id) { 
        case Command.START: 
           timer.resume(); 
           break; 
        case Command.STOP: 
           timer.suspend(); 
           break; 
        case Command.RESET: 
           counter = 0; 
           text.setText(" 00 "); 
           break; 
    } 
}
The Do() method actually changes the state of the application, based on the integer id passed from the Command class methods. Although rather simple, it represents the ability to have all the application control in one class, while having a separate class handle events.
public void run() 
    { while (true) { 
        try {Thread.sleep(500);} // Should be 1000 
           catch (InterruptedException e) {}; 
        counter += 1; 
        if (counter == 100) counter = 0;
         // Trick way to format string as two digits 
        text.setText(" "+ counter/10 + counter%10 + " "); 
    } 
}
 static public void main(String args[]) { 
    Timer app = new Timer(); 
    app.timer = new Thread(app); 
    app.timer.start(); 
}
The run() method makes the Timer work, just so this application does something. I had wanted to combine an internationalization example here, but it was too much for one column. The main() method calls the Timer contructor, creates a Thread, and starts it. Notice that the Thread.sleep() is only for 500 milliseconds. In the Beta3 for Win32 version, this works out to about 1 second. Maybe I have a slow Pentium?
class Command extends WindowAdapter 
    implements ActionListener {
    static final int START = 0; 
    static final int STOP = 1; 
    static final int RESET = 2; 
    int id; 
    Timer s;

     public Command(int id, Timer s) {
        this.id = id; 
        this.s = s; 
    }

     public void actionPerformed(ActionEvent e) { 
        s.Do(id); 
    }

     public void windowClosing(WindowEvent e) { 
        System.exit(0); 
    } 
}
The Command class is the real guts of this example. It both extends WindowAdapter and implements ActionListener. To implement ActionListener, I must define the actionPerformed() method, which gets called by Components that generate ActionEvents, such as Buttons. This method passes control back to the Timer class method Do().

The windowClosing() method overrides a method in the WindowAdapter class. The windowAdapter() class has empty methods for seven different window events and permits me to define just the one I am interested in, windowClosing(). All this does is exit the application.

In the past, the Timer class would have included a handleEvent() method, with many comparisons so I could determine exactly what event had occurred. The new event model permits me to assign objects for each event, or generalize, with an all purpose class such as Command, which captures two types of events, and "knows" which button was selected because each Command object has a different id.

Not Backward Compatible

You must have JDK 1.1 to build this example. You can run classes built using JDK 1.0 using the virtual machine in JDK 1.1. But you can't run JDK 1.1 classes on older virtual machines. This also means that if you build applets with JDK 1.1, most browsers won't be able to execute these applets until they support JDK 1.1. At the time I write this, only HotJava Beta2 and Netscape 3.0b could handle 1.1 applets.

Although I am cheered by the improvements (there are also performance improvements), the amount of new material to learn is daunting. I had felt like I had a good grip on the class hierarchy, only to have it doubled in size (well, the distribution has almost doubled in size, not the number of classes).

Familiar classes have been "deprecated," a fancy term meaning "made smaller," or more precisely, soon to be unsupported. "Bugs," which I had thought were really the way things worked, have been fixed, making a couple of my existing programs fail.

Still, no complaints. Just more work to be done with a 1.x release of a product, and one I don't have to pay for while I study it at that.

First published in ;login:, Volume 22, No. 2, April 1997.

 

?Need help? Use our Contacts page.
Last changed: May 16, 1997 pc
Java index
Publications index
USENIX home