WELCOME to the Java Developer ConnectionSM
(JDC) Tech Tips, July 11, 2000. This issue covers:
- Using Shutdown Hooks
- Automating GUI Programs with
java.awt.Robot
This issue of the JDC Tech Tips is written by Glen McCluskey.
These tips were developed using JavaTM 2 SDK,
Standard Edition, v 1.3.
USING SHUTDOWN HOOKS
Suppose that you're writing an application, and you'd like to gain control when
the application shuts down. You might want to do this in order to close files
that are open, for example, close a log file that the application has written to.
One way to gain control is simply to have a shutdown method that you call before
calling exit:
- callShutdown ();
- System.exit (0);
This approach works if your application terminates only in one place, that is,
System.exit
is called only one place, or if you specify callShutdown everywhere
that you exit. This approach also requires that you catch all exceptions that are
thrown, which otherwise would terminate the program abnormally.
In JDK 1.3 there's another way to handle shutdown: using shutdown hooks. A
shutdown hook is an initialized thread that has not yet been executed. In other
words, a shutdown hook is an object of a class derived from the Thread
class, with
a run method that is called to perform whatever actions you want. You register this
object with the JavaTM Virtual Machine (JVM)*.
Here's an example:
import java.io.*;
public class ShutdownDemo {
private FileWriter fw_log;
private BufferedWriter bw_log;
// constructor that opens the log file
public ShutdownDemo() throws IOException {
fw_log = new FileWriter("log.txt");
bw_log = new BufferedWriter(fw_log);
// register the shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
endApp();
}
});;
}
// do some application processing and write to the log file
public void processApp1() throws IOException {
bw_log.write("testing");
bw_log.newLine();
}
// do some application processing resulting in an exception
public void processApp2() {
throw new RuntimeException();
}
// close the log file
public void endApp() {
try {
bw_log.close();
}
catch (IOException e) {
System.err.println(e);
}
}
public static void main(String args[]) throws IOException {
// create an application object
ShutdownDemo demo = new ShutdownDemo();
// do some processing
demo.processApp1();
// do some more processing that results in an exception
demo.processApp2();
}
}
This application creates an instance of the ShutdownDemo
class, representing
an application. The constructor for the class opens a log file, using FileWriter
and BufferedWriter
.
ShutdownDemo
then calls processApp1
. In its processing,
processApp1
writes an entry to the log file. Then processApp2
is
called. It throws an exception that is not caught by the application. Normally, this
exception would terminate the application; the log file entry previously written would be
lost because the output is sitting in a buffer which has not been flushed to disk.
But in this demo the output is not lost. This is because the application registers a shutdown
hook by saying:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
endApp();
}
});;
Notice the use of an anonymous inner class. Here an instance of an unnamed class derived
from Thread
is created, and a run method that calls endApp
is defined for the
class.
All of this means that when the application is about to terminate, the JVM starts the thread
representing by the passed-in thread object. When the thread starts, the run method is
called. The run method calls endApp
, which closes the log file. This flushes
the output buffer.
To underscore the effect of the shutdown hook, comment out the addShutdownHook
lines in ShutdownDemo
. You'll see that the log file is empty when the program
terminates.
You can register multiple shutdown hooks. In this case, each thread that represents a hook
is started in an unspecified order, and the various threads run simultaneously. You cannot
register or unregister a shutdown hook after the shutdown sequence has started. Doing so
results in an IllegalStateException
.
Because shutdown hooks run as threads, you must use thread-safe programming techniques.
Otherwise you risk having threads interfere with each other. Also, it's wise to design your
application for simple and fast shutdown processing. For example, you might run into trouble
if your application uses services during shutdown that are themselves in the processing of
being shut down.
There are cases where shutdown processing does not happen even if you have registered shutdown
hooks. One example is corrupted native methods, for example, when you dereference a null
pointer in C code.
This feature is somewhat similar to the atexit library function in C/C++.
For further information about shutdown hooks, see:
http://java.sun.com/j2se/1.3/docs/guide/lang/enhancements.html#hooks
AUTOMATING GUI PROGRAMS WITH JAVA.AWT.ROBOT
Imagine that you have a Java GUI program written using the AWT and Swing libraries, and
you'd like to automate the program. For example, you'd like to automatically supply GUI
input events from the keyboard and mouse to the program. In this way, the program could
operate without user intervention. This type of automation might be useful for testing
purposes, or to produce a self-running demo program.
java.awt.Robot
is a new class in JDK 1.3, designed to handle this type of automation. It's
a way to automatically feed input events to a program. The events are generated in the
native input queue of the platform, as if they had actually been generated by the user. For
example, using java.awt.Robot
, you can generate an event that is equivalent to a user moving
the mouse to particular (X,Y) coordinates on the screen.
Here's an example of how you can use this class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class RobotDemo {
public static void main(String args[]) throws AWTException {
// set up frames and panels
JFrame frame = new JFrame("RobotDemo");
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 1));
// set up fields, labels, and buttons
final JTextField field = new JTextField(10);
final JLabel lab = new JLabel();
field.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String s = "Length: " +
field.getText().length();
lab.setText(s);
}
});
JButton button = new JButton("Exit");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
// add components to panel and display
panel.add(field);
panel.add(lab);
panel.add(button);
frame.getContentPane().add(panel);
frame.setSize(200, 150);
frame.setLocation(200, 200);
frame.setVisible(true);
// create a robot to feed in GUI events
Robot rob = new Robot();
// enter some keystrokes
int keyinput[] = {
KeyEvent.VK_T,
KeyEvent.VK_E,
KeyEvent.VK_S,
KeyEvent.VK_T,
KeyEvent.VK_I,
KeyEvent.VK_N,
KeyEvent.VK_G
};
rob.delay(1000);
rob.keyPress(KeyEvent.VK_SHIFT);
field.requestFocus();
for (int i = 0; i < keyinput.length; i++) {
rob.keyPress(keyinput[i]);
rob.delay(1000);
}
rob.keyRelease(KeyEvent.VK_SHIFT);
rob.keyPress(KeyEvent.VK_ENTER);
// move cursor to Exit button
Point p = button.getLocationOnScreen();
rob.mouseMove(p.x + 5, p.y + 5);
rob.delay(2000);
// press and release left mouse button
rob.mousePress(InputEvent.BUTTON1_MASK);
rob.delay(2000);
rob.mouseRelease(InputEvent.BUTTON1_MASK);
}
}
The demo sets up a panel containing an input field, a label, and an Exit button. This part
of the demo is typical of many Swing applications. Then the demo creates a Robot object,
and feeds into it a series of keystrokes. These keystrokes mimic keys typed by a user, that
is, the keys T, E, S, T, I, N, and G. There is a delay of 1000 milliseconds between
keystrokes; this helps present the animation more clearly. The shift key is held down
throughout, so that the letters are entered as capitals. At the end of the text input,
Enter is specified. This causes the length of the input to be echoed in the label field.
Then the mouse cursor is moved to the Exit button, and the left mouse button is pressed
and released. This terminates the program.
Notice that virtual keycodes are used to enter keystrokes; keycodes
are not the same as Java characters. KeyEvent.VK_A
corresponds to
pressing the unshifted 'A' key on a keyboard. If you specify 'A' or
'a' instead of KeyEvent.VK_A
, you get unexpected results.
Also note that the documentation for Robot says that some platforms
require special privileges to access low-level input control. One
specific case is X Windows, which requires the XTEST 2.2 standard
extension.
For further information about java.awt.Robot, see
http://java.sun.com/j2se/1.3/docs/api/java/awt/Robot.html
For further information about key events, see
http://java.sun.com/j2se/1.3/docs/api/java/awt/event/KeyEvent.html
For further information about mouse events, see
http://java.sun.com/j2se/1.3/docs/api/java/awt/event/MouseEvent.html
Note
The names on the JDCSM
mailing list
are used for internal Sun MicrosystemsTM
purposes only. To remove your name from the list, see
Subscribe/Unsubscribe
below.
Feedback
Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster
Subscribe/Unsubscribe
The JDC Tech Tips are sent to you because you elected to subscribe when you registered as a
JDC member. To unsubscribe from JDC email, go to the following address and enter the email
address you wish to remove from the mailing list:
http://developer.java.sun.com/unsubscribe.html
To become a JDC member and subscribe to this newsletter go to:
http://java.sun.com/jdc/
Copyright
Copyright 2000 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.
This Document is protected by copyright. For more information, see:
http://developer.java.sun.com/developer/copyright.html
This issue of the JDC Tech Tips is written by Glen McCluskey.
JDC Tech Tips
July 11, 2000
* As used in this document, the terms "Java virtual machine" or "JVM" mean a virtual machine
for the Java platform.