|
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.
| |