HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

We all know what AWT is. We know that AWT 1.0 is simple and easy to use. It's compatible with the old versions of the Web browsers, but has an inefficient event-handling model. The events are received by the components, which handle or ignore them. Worse, if you want to add a small functionality to a component, you have to create a new class of components. AWT 1.1 corrects these defects. It's a little more complex, but has many advantages. The new delegation-based event model plays a key role in the JavaBeans architecture as it facilitates the communications between beans. Design patterns allow the development tools to use reflection to become smarter. With AWT 1.1 you can create lightweight components (like those of Swing) that are serializable so you can create persistent user interfaces. The delegation-based event model allows programmers to isolate the application logic from the user interface and improve UI performances of the applications.

AWT 1.1 has to struggle to offer compatibility with AWT 1.0. A component may use the old event model or the new one, but not both at the same time. The documents from Sun advise you not to mix event models in the same application. How can you believe that advice if all AWT 1.1 components are born as AWT 1.0s? (I'm talking about instances, not classes.) The event model of AWT 1.0 is the default event model of the AWT 1.1 components. Each component has a flag - newEventsOnly - that indicates the type of event model used by the respective component. The flag is initialized false when the component is created. When you register listeners to a component, the newEventsOnly flag is switched to true. Only after that occurs will the component use the AWT 1.1 event model. The transformation is irreversible (see Figure 1).

Figure 1
Figure 1:

If you're using Java 1.1 or a later version, your components have the potential to become AWT 1.1 components. Most of them die as AWT 1.0 components, however, because the programmers don't register listeners to them. Why register a listener to a TextField when you have a getText() method? Why register a listener to a Checkbox when you have a getState() method?

If you're using Java 1.1 or 1.2 and you don't call deprecated methods of AWT 1.0, then you can benefit from all the advantages of AWT 1.1. If you're not careful, however, many of your components will use the AWT 1.0 event model. Your app will work fine but won't run at maximum speed. This article shows you how to force all the components to use the AWT 1.1 event model.

How to Improve UI Performance
You should ask yourself first whether your apps need improved UI performance. Most of the simple applets run at the same speed no matter what event model they use. But suppose you're working on a complex application that places components in a slow container - to create compound documents, for example. You'll want all of the events addressed to the components not to be passed to the container.

Let's consider a simple example. I wrote a small applet whose area is covered by only one component (a java.awt.Canvas instance). The init() method of the applet creates the Canvas object and stores a reference to it in the c member variable.

public void init() {
setBackground(Color.red);
c = new Canvas();
setLayout(new BorderLayout());
add(c, "Center");
}

This applet is a slow container. Each time the mouse is moved within the applet's area, the status bar is modified a hundredfold. This operation proved slow enough for most of my browsers (HotJava 1.1, Navigator 4.05 and AppletViewer). SystemMonitor of Win95 showed that my processor, a Pentium MMX 166, was 100% busy. Still, Internet Explorer 4.0 was very fast at the redraw of the status bar, so I had to increase the limit of the i counter at 1,000. If you have a very fast machine you may grow to this limit, but be careful because the applet might freeze your browser.

public boolean mouseMove(Event evt, int x, int y) {
for (int i = 0; i <100; i++)
showStatus(Integer.toString(x * y + i));
return true;
}

The applet's entire area was covered by the Canvas object, and the mouse events received by this component were sent further - to the applet. This means that the Canvas instance uses the AWT 1.0 event model. However, if I click on the canvas, the status bar won't modify anymore (mouseMove() isn't called anymore) because the applet intercepts the mouse click and calls the addComponentListener() method of the Canvas instance from the mouseDown() method. Although undocumented, this is the only way to force the components to use the AWT 1.1 event model.

public boolean mouseDown(Event evt, int x, int y) {
c.addComponentListener(null);
return true;
}

No listeners are registered to the Canvas object because the parameter of the addComponentListener() method is null. However, this method will set the internal newEventsOnly flag of the component to true. From this moment the applet won't receive any mouse events because its entire area is covered with an AWT 1.1 component.

The Canvas object was born as an AWT 1.0 component and became an AWT 1.1 component after the "c.addComponentListener(null)" call. This is a good trick that allows you to force the AWT components to use the delegation event model even if no listeners are registered to them. There is no correct way to transform an AWT 1.1 component into an AWT 1.0 component. The applet not only uses the AWT 1.0 event model for its entire existence, but it also overrides deprecated methods.

Find Out What Event Model Your Components Use
The only way to determine what event model is used by a particular component is to examine the newEventsOnly flag, which isn't public. How can you do that in the absence of a getNewEventsOnly() method? Fortunately, the flag is not private. It's friendly, so you may write your own AWT class whose methods can read newEventsOnly. You can do this without altering your Java Virtual Machine (JVM), as long as you write stand-alone applications. The trick doesn't work with Navigator and Internet Explorer without breaking their security mechanisms, and I don't know how to do that. After all, I'm just a Java developer, not a professional hacker. You also must know that Java 1.2 has a better security mechanism than 1.1. If you want to run the DemoCompTree application with JDK 1.2 beta 4, you'll have to use the -Xverify:none option of the Java interpreter. Otherwise, an IllegalAccess Error will be thrown (source code will be explained later).

I named the new AWT class CompTree (see Listing 1). You don't need to place CompTree.class in the classes.zip file. Just insert the "package java.awt;" declaration at the beginning of the source file and, after you compile it, copy the classfile in your_dir/java/awt. Then add your_dir directory at CLASSPATH and you'll be able to use the CompTree class like any other AWT class (your_dir might be the current directory -- "."). This is possible because the JVM is locking after system classes in all of the directories and .zip files from CLASSPATH. The JVM makes no distinction between java/awt/Component.class from classes.zip and java/awt/CompTree.class from your_dir. There's nothing to stop you from splitting the classes of a package into more than one directory.

The CompTree class has only one public method (printTree()) that takes a java.awt.Container (c) as argument and prints (to console) the entire component hierarchy, whose top is referenced by c. Note that this hierarchy has nothing to do with the class hierarchy of the java.awt package. The public printTree() method calls the private printInfo() and printTree() methods.

The printInfo() method (see Listing 1) shows information about the component whose reference is passed as parameter. It prints the name of the class of the component, the internal name of the component (which is returned by the getName() method) and the value of the newEventsOnly flag. True means that the component uses the AWT 1.1 event model. False indicates that the component uses the old event model (of AWT 1.0). If a component is a container (java.awt.Container is a subclass of java.awt.Component), then the name of the LayoutManager class is printed. More information about the state of the component could have been obtained with the toString() method.

The private printTree() method (see Listing 1) gets the list of the components of the c container (c is a parameter variable) with the help of getComponents(). The printInfo() method is called for each component. The printTree() method is called recursively for those components that are also containers. This way, the entire component tree is printed. An integer parameter is used for indenting.

What Is the CompTree Class Good For?
You already know the answer. You can use it to identify the components of a container that doesn't use the AWT 1.1 event model because they don't have listeners, and then force them to use it. You can do that, for example, by calling the addComponentListener() method with a null parameter, as I described earlier.

Next I'll show you how to use the CompTree class as a tool. In my "Persistent User Interface for Multiuser Apps" article (JDJ, Vol. 3, Issue 8), I presented an app, SmartLogin, whose window was serialized into a file. I'm not interested right now in persistence. What I really want to find out is what event model was used by the components of the SmartLogin window (two Panels, two Buttons, three Checkboxes, two Labels and two TextFields). Therefore, I wrote a small app that creates an instance of the SmartLogin class (which inherits from java.awt.Frame) and calls the printTree() method of the CompTree class. The main() method has only three lines.

SmartLogin sl = new SmartLogin("");
CompTree.printTree(sl);
System.exit(0);

To use CompTree in this app, I had to include an import directive in the source file.

import java.awt.CompTree;

Listing 2 shows the results after the app is run. Most of the components (nine of 12) use the AWT 1.0 event model. Note that SmartLogin application doesn't use deprecated APIs. The delegation event model is used only by the SmartLogin container and the two buttons. This is typical. You usually register listeners to the push buttons in order to intercept the user's actions. You also register a listener to the app's window so that it can close. Most of the components don't need listeners, which helps the programmer control the number of the classes of the application.

The SmartLogin app is fast enough because it's simple. If I force the nine components that use the old event model to work with the new one, the increase in speed is imperceptible so the source code should remain unchanged. For a real-world, complex application, however, the techniques presented in this article may be useful.

Note that in last month's article the constructor of the SmartLogin class was private. This made sense because the app was creating only one SmartLogin instance in the main() method (SmartLogin was a singleton). For this article I had to delete the private keyword to be able to study the component tree of the SmartLogin container with the help of CompTree.

CompTree can also be used as a utility class to verify or study the component hierarchies that you coded manually or built visually with a development tool. You don't necessarily need CompTree to do such an operation. If you have JDK 1.1/1.2, you can press Ctrl+Shift+F1 to dump the component tree of a Frame to the console. However, you still need CompTree to show the newEventsOnly flags.

When Wouldn't You Use CompTree in Your Apps?
The CompTree class can't be downloaded with applets (it must be placed in CLASSPATH), but that isn't a major problem. When I wrote the CompTree class, I saw it as a tool to use at design time. You could include it in your stand-alone apps to be used at runtime, of course, but it's not a good idea because the CompTree class is already included in the java.awt package. What would happen if you deployed more than one version of this class in different .jar archives and another person wanted to use them together? Another architectural change to AWT (beyond JDK 1.2) might cause the deletion of the newEventsOnly flag. Until something is modified, use CompTree as a tool to tune your apps, and don't forget the -Xverify:none option if you use JDK 1.2.

Request for Enchantment
Let's take a short look inside AWT and Swing. The constructor of the java.awt.Component class contains no instructions.

protected Component() {
}

The constructor of the Swing's JComponent class calls the enableEvents() method, which is inherited from java.awt.Component via java.awt.Container (JComponent extends Container). The enableEvents() method will set the newEventsOnly flag to true. The conclusion is that all of the Swing components use the delegation event model since they're created. They're also lightweight components; they don't have native peer classes.

public JComponent() {
super();
enableEvents(AWTEvent.FOCUS_EVENT_MASK);
}

Let's go back to AWT. Wouldn't it be nice to have a static flag - defaultNewEventsOnly - that could be used by the Component() constructor?

// This class variable doesn't exist
static boolean defaultNewEventsOnly = false;

// This constructor doesn't exist
protected Component() {
newEventsOnly = defaultNewEventsOnly;
}

If you were sure you used only AWT 1.1 in an application, then you could call, right from the start, a method like this:

// This method doesn't exist
public static newAWTOnly() {
defaultNewEventsOnly = true;
}

This way you wouldn't need to call addComponentListener(null) to force the components to use the AWT 1.1 event model.

Summary
For compatibility reasons the AWT 1.0 event model is the default event model of the AWT 1.1 components. You usually have to register listeners to components to convince them to use the delegation event model.

This article teaches you two things: how to force the components that don't need listeners to use the AWT 1.1 event model, and how to discover the components that use the AWT 1.0 event model. You also learned a hacking trick: how to access a friendly variable of a java.xxx.Yyy class without altering the JVM.

References

  1. Sun Microsystems, "The AWT Home Page," http://java.sun.com/products/jdk/awt/index.html.
  2. Sun Microsystems, "Delegation Event Model,'' http://java.sun.com/products/jdk/1.1/docs/guide/awt/designspec/events.html.
  3. Sun Microsystems, "Deprecated Methods in the 1.1 AWT," http://java.sun.com/products/jdk/1.1/docs/guide/awt/DeprecatedMethods.html.
  4. Andrei Cioroianu, "Persistent User Interface for Multiuser Applications" http://www.javadevelopersjournal.com/.
  5. Andrei Cioroianu, "Inside AWT," http://www.geocities.com/SiliconValley/Horizon/6481/InsideAWT.html.
About the Author
Andrei Cioroianu is an independent Java developer. He has a BS in mathematics-computer science and an MS in artificial intelligence. His focus is on 3D graphics (Java 3D), software components (JavaBeans) and user interface (AWT, JFC). You can reach Andrei for questions or comments at [email protected]

	

Listing 1: The CompTree class.
 
// CompTree.java 

package java.awt; 

public class CompTree { 

public static void printTree(Container c) { 
   // Prints the component tree, whose top is c 
   printInfo(c, 0); 
   printTree(c, 4); 
} 

private static void printTree(Container c, int l) { 
   // Gets the component list of the c container 
   Component a[] = c.getComponents(); 
   if (a == null) return; 
   // Prints the information about each component 
   for (int i = 0; i < a.length; i++) { 
      printInfo(a[i], l); 
      if (a[i] instanceof Container) 
         printTree((Container) a[i], l+4); 
   } 
} 

private static void printInfo(Component c, int l) { 
   // This string computing is not optimized 
   String s = new String(); 
   for (int i = 0; i < l; i++) 
      s += ' '; 
   // Prints the information about the c component 
   System.out.print(s); 
   System.out.print(c.getClass().getName()); 
   System.out.print(" -- "); 
   System.out.print(c.getName()); 
   if (c instanceof Container) { 
      LayoutManager m = ((Container) c).getLayout(); 
      System.out.print(" ("); 
      if (m != null) 
         System.out.print(m.getClass().getName()); 
      System.out.print(")"); 
   } 
   System.out.print(" -- "); 
   System.out.print(c.newEventsOnly); 
   System.out.println(); 
   // Uncomment next line for more information 
   // System.out.println(s + "+ " + c); 
} 

} 
  

Listing 2: The component hierarchy of SmartLogin.

SmartLogin -- frame0 (java.awt.GridLayout) -- true 
    java.awt.Label -- label0 -- false 
    java.awt.TextField -- textfield0 -- false 
    java.awt.Panel -- panel0 (java.awt.GridLayout) 
                                          -- false 
        java.awt.Checkbox -- checkbox0 -- false 
        java.awt.Checkbox -- checkbox1 -- false 
        java.awt.Checkbox -- checkbox2 -- false 
    java.awt.Label -- label1 -- false 
    java.awt.TextField -- textfield1 -- false 
    java.awt.Panel -- panel1 (java.awt.FlowLayout) 
                                          -- false 
        java.awt.Button -- button0 -- true 
        java.awt.Button -- button1 -- true 


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.