Received: from SOUTH-STATION-ANNEX.MIT.EDU by po10 (5.61/4.7) id AA10175; Wed, 31 May 00 00:26:57 EDT Received: from hermes.javasoft.com by MIT.EDU with SMTP id AA26762; Wed, 31 May 00 00:23:26 EDT Received: (from nobody@localhost) by hermes.java.sun.com (8.9.3+Sun/8.9.1) id EAA29076; Wed, 31 May 2000 04:20:00 GMT Date: Wed, 31 May 2000 04:20:00 GMT Message-Id: <200005310420.EAA29076@hermes.java.sun.com> X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_39/mqueue7 X-Mailing: 213 From: JDCTechTips@sun.com Subject: JDC Tech Tips May 30, 2000 To: JDCMember@sun.com Reply-To: JDCTechTips@sun.com Errors-To: bounced_mail@hermes.java.sun.com Precedence: junk Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Mailer: Beyond Email 2.2 J D C T E C H T I P S TIPS, TECHNIQUES, AND SAMPLE CODE WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, May 30, 2000. This issue celebrates the public release of the Java(tm) 2 Platform, Standard Edition (J2SE) v1.3 for Windows platforms. (J2SE v1.3 for Solaris and Linux platforms will be available soon.) Today's tips cover two features that are new in J2SE v1.3: dynamic proxies and timer classes. These features are discussed below in: * Using Dynamic Proxies to Layer New Functionality Over Existing Code * Using Timers to Run Recurring or Future Tasks on a Background Thread You can view this issue of the Tech Tips on the Web at http://developer.java.sun.com/developer/TechTips/2000/tt0530.html. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING DYNAMIC PROXIES TO LAYER NEW FUNCTIONALITY OVER EXISTING CODE Dynamic proxies allow you to implement new interfaces at runtime by forwarding all calls to an InvocationHandler. This tip shows you how to use dynamic proxies to add new capabilities without modifying existing code. Consider the following program. The program includes an interface named Explorer. The interface models the movement of an "explorer" around a Cartesian grid. The explorer can travel in any compass direction, and can report its current location. The class ExplorerImpl is a simple implementation of the Explorer interface. It uses two integer values to track the explorer's progress around the grid. The TestExplorer class sends the explorer on 100 random steps, and then logs the explorer's position. import java.lang.reflect.Method; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; interface Explorer { public int getX(); public int getY(); public void goNorth(); public void goSouth(); public void goEast(); public void goWest(); } class ExplorerImpl implements Explorer { private int x; private int y; public int getX() {return x;} public int getY() {return y;} public void goNorth() {y++;} public void goSouth() {y--;} public void goEast() {x++;} public void goWest() {x--;} } public class TestExplorer { public static void test(Explorer e) { for (int n=0; n<100; n++) { switch ((int)(Math.random() * 4)) { case 0: e.goNorth(); break; case 1: e.goSouth(); break; case 2: e.goEast(); break; case 3: e.goWest(); break; } } System.out.println("Explorer ended at " + e.getX() + "," + e.getY()); } public static void main(String[] args) { Explorer e = new ExplorerImpl(); test(e); } } Try running the TestExplorer class. You should get one line of output, similar to this: Explorer ended at -2,8 Now, imagine that the requirements for the system change, and you need to log the explorer's movement at each step. Because the client programmed against an interface, this is straightforward; you could simply create a LoggedExplorer wrapper class that logs each method call before delegating to the original Explorer implementation. This is a nice solution because it does not require any changes to ExplorerImpl. Here's the new LoggingExplorer wrapper class: class LoggingExplorer implements Explorer { Explorer realExplorer; public LoggingExplorer(Explorer realExplorer) { this.realExplorer = realExplorer; } public int getX() { return realExplorer.getX(); } public int getY() { return realExplorer.getY(); } public void goNorth() { System.out.println("goNorth"); realExplorer.goNorth(); } public void goSouth() { System.out.println("goSouth"); realExplorer.goSouth(); } public void goEast() { System.out.println("goEast"); realExplorer.goEast(); } public void goWest() { System.out.println("goWest"); realExplorer.goWest(); } } The LoggingExplorer class delegates to an underlying realExplorer interface, which allows you to add logging to any existing Explorer implementation. The only change clients of the Explorer interface need to make is to construct the LoggingExplorer so that it wraps the Explorer interface. To do this, modify TestExplorer's main method as follows: public static void main(String[] args) { Explorer real = new ExplorerImpl(); Explorer wrapper = new LoggingExplorer(real); test(wrapper); } Now your output should be similar to goWest goNorth ...several of these... goWest goNorth Explorer ended at 2,2 By delegating to an underlying interface, you added a new layer of function without changing the ExplorerImpl code. And you did it with only a trivial change to the test client. The LoggingExplorer wrapper class is a good start to using delegation, but this "by-hand" approach has two major drawbacks. First, it's tedious. Each individual method of the Explorer interface must be reimplemented in the LoggingExplorer implementation. The second drawback is that the problem (that is, logging) is generic, but the solution is not. If you want to log some other interface, you need to write a separate wrapper class. The Dynamic Proxy Class API can solve both of these problems. A dynamic proxy is a special class created at runtime by the Java(tm) virtual machine*. You can request a proxy class that implements any interface, or even a group of interfaces, by calling: Proxy.newProxyInstance(ClassLoader classLoaderToUse, Class[] interfacesToImplement, InvocationHandler objToDelegateTo) The JVM manufactures a new class that implements the interfaces you request, forwarding all calls to InvocationHandler's single method: public Object invoke(Object proxy, Method meth, Object[] args) throws Throwable; All you have to do is implement the invoke method in a class that implements the InvocationHandler interface. The Proxy class then forwards all calls to you. Let's make this work for the Explorer interface. Replace the LoggingExplorer wrapper class with the following Logger class. class Logger implements InvocationHandler { private Object delegate; public Logger(Object o) { delegate = o; } public Object invoke(Object proxy, Method meth, Object[] args) throws Throwable { System.out.println(meth.getName()); return meth.invoke(delegate, args); } } This implementation of the invoke method can log any method call on any interface. It uses reflective invocation on the Method object to delegate to the real object. Now modify the TestExplorer main method as follows to create a dynamic proxy class: public static void main(String[] args) { Explorer real = new ExplorerImpl(); Explorer wrapper = (Explorer) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[] {Explorer.class}, new Logger(real)); test(wrapper); } The static method Proxy.newProxyInstance creates a new proxy that implements the array of interfaces passed as its second parameter. In this example, the proxy implements the Explorer interface. All invocations of Explorer methods are then handed off to the InvocationHandler that is passed as the third parameter. Try running the updated code. You should see that each step of the Explorer is logged to System.out. The dynamic proxy class solves both of the problems of the "by-hand" approach. There is no tedious copying and pasting of methods because invoke can handle all methods. Also, the logger presented here can be used to log calls to any interface in the Java(tm) language. Try inserting some loggers in your own code to trace program flow. Notice that the logging operation is method generic, that is, logging does not require any decision making based on the specifics of the method being called. Dynamic proxies are at their best when adding method-generic services. Logging is one area where dynamic proxies can be used to advantage; others include generic stubs for RMI, automatic parameter validation, transaction enlistment, authentication and access control, and rule-based parameter, modification, and error handling. Dynamic proxies, like all reflective code, are somewhat slower than "normal" code. In many situations this performance difference is not crucial. If you want to evaluate the performance of dynamic proxies for delegation, download the benchmarking code from http://staff.develop.com/halloway/JavaTools.html and execute the TimeMethodInvoke.cmd script. This script measures times for various styles of method invocation in the Java language. For more info on dynamic proxies, see http://java.sun.com/j2se/1.3/docs/guide/reflection/proxy.html. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING TIMERS TO RUN RECURRING OR FUTURE TASKS ON A BACKGROUND THREAD Many applications need to schedule tasks for future execution, or to schedule them to recur at a regular interval. J2SE v1.3 meets this need with the addition of two Timer classes: java.util.Timer and java.util.TimerTask. This tip demonstrates various scheduling strategies for using these Timer classes. The tip also shows you how to handle poorly-behaved tasks, that is, tasks that run too long or that crash. The java.util.Timer and java.util.TimerTask classes are simple to use. As with many things threaded, the TimerTask class implements the Runnable interface. To use the class, simply write a subclass with a run method that does the work; then plug the subclass into a Timer instance. Here's an example: import java.util.*; import java.io.*; public class TestTimers { public static void doMain() throws Exception { Timer t = new Timer(true); t.schedule(new Ping("Fixed delay"), 0, 1000); Thread.currentThread().sleep(12000); } public static void main(String[] args) { try { doMain(); } catch (Exception e) { e.printStackTrace(); } } } class Ping extends TimerTask { private String name; public Ping(String name) { this.name = name; } public void run() { System.out.println(name + " Ping at " + new Date()); } } The class TestTimers creates a Timer. By passing the Timer the boolean value true, TestTimers forces the Timer to use a daemon thread. The main thread then sleeps, allowing you see the Timer at work. However you never actually see any thread classes or instances; those details are encapsulated by the Timer class. In the statement, t.schedule(new Ping("Fixed delay"), 0, 1000); the parameters to the schedule method cause a Ping object's run method to be invoked after an initial delay of 0 milliseconds; the method is repeatedly invoked every 1000 milliseconds. Ping's run method logs output to System.out. (In your own applications, you would use the run method to do something more interesting.) If you run TestTimers, you will see output similar to this: Fixed delay ping at Thu May 18 14:18:56 EDT 2000 Fixed delay ping at Thu May 18 14:18:57 EDT 2000 Fixed delay ping at Thu May 18 14:18:58 EDT 2000 Fixed delay ping at Thu May 18 14:18:59 EDT 2000 Fixed delay ping at Thu May 18 14:19:00 EDT 2000 Fixed delay ping at Thu May 18 14:19:01 EDT 2000 Fixed delay ping at Thu May 18 14:19:02 EDT 2000 Fixed delay ping at Thu May 18 14:19:03 EDT 2000 Fixed delay ping at Thu May 18 14:19:04 EDT 2000 Fixed delay ping at Thu May 18 14:19:05 EDT 2000 Fixed delay ping at Thu May 18 14:19:06 EDT 2000 Fixed delay ping at Thu May 18 14:19:07 EDT 2000 Fixed delay ping at Thu May 18 14:19:08 EDT 2000 The output confirms that Ping is running about once per second, exactly as requested. Better still, the Timer can handle multiple TimerTasks, each with different start times and repeat periods. This leads to an interesting question: If a TimerTask takes a very long time to complete, will other tasks in the list be thrown off? To answer this question, you need to understand how the Timer uses threads. Each Timer instance has a single dedicated thread that all the TimerTasks share. So, if one task takes a long time, all the other tasks wait for it to complete. Consider this long-running task: class PainstakinglySlowTask extends TimerTask { public void run() { //simulate some very slow activity by sleeping try { Thread.currentThread().sleep(6000); System.out.println("Painstaking task ran at " + new Date()); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } The PainstakinglySlowTask class sleeps for six full seconds. It prevents any other tasks from executing during that time. What happens if you add the painstakingly slow task to TestTimers? Let's see. public static void doMain() throws Exception { Timer t = new Timer(true); t.schedule(new Ping("Fixed delay"), 0, 1000); t.schedule(new PainstakinglySlowTask(), 2000); Thread.currentThread().sleep(12000); } If you recompile and run TestTimers, you will see output like this: Fixed delay Ping at Thu May 18 15:41:33 EDT 2000 Fixed delay Ping at Thu May 18 15:41:34 EDT 2000 Fixed delay Ping at Thu May 18 15:41:35 EDT 2000 Painstaking task ran at Thu May 18 15:41:41 EDT 2000 Fixed delay Ping at Thu May 18 15:41:41 EDT 2000 Fixed delay Ping at Thu May 18 15:41:42 EDT 2000 Fixed delay Ping at Thu May 18 15:41:43 EDT 2000 Fixed delay Ping at Thu May 18 15:41:44 EDT 2000 Fixed delay Ping at Thu May 18 15:41:45 EDT 2000 During the time that PainstakinglySlowTask runs (from 15:41:35 to 15:41:41), no pings occur. This is what is meant by a "fixed delay". The Timer tries to make the delay between Pings as precise as possible, even if that means that some Pings are lost during the running time of another, long-running task. A scheduling alternative is "fixed rate." With fixed rate scheduling, the Timer tries to make the processing rate as accurate as possible over time. So, if one task runs for a long time, other tasks can instantaneously run several times in order to catch up. You can specify fixed rate scheduling by using the scheduleAtFixedRate method: //commented out the fixed delay version //t.schedule(new Ping("Fixed delay"), 0, 1000); t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000); t.schedule(new PainstakinglySlowTask(), 2000); If you run TestTimers with a fixed rate ping, you should see output like this: Fixed rate Ping at Thu May 18 15:48:33 EDT 2000 Fixed rate Ping at Thu May 18 15:48:34 EDT 2000 Fixed rate Ping at Thu May 18 15:48:35 EDT 2000 Painstaking task ran at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:41 EDT 2000 Fixed rate Ping at Thu May 18 15:48:42 EDT 2000 Fixed rate Ping at Thu May 18 15:48:43 EDT 2000 Fixed rate Ping at Thu May 18 15:48:44 EDT 2000 Fixed rate Ping at Thu May 18 15:48:45 EDT 2000 This time, several Pings run right after PainstakinglySlowTask finishes; the Pings all run at 15:48:41. This keeps the rate of Pings as close as possible to the desired 1000 msec average. The price paid is occasionally having Pings run at approximately the same time. Both fixed-rate and fixed-delay scheduling have their uses. However, neither totally eliminates the interference caused by long-running tasks. If you have different tasks that might run for a very long time, you might want to minimize the interference between the tasks. This is especially true if you need to take advantage of multiple CPUs. A single Timer provides no obvious way to do this. You cannot control the Timer thread, because it is encapsulated as a private field of the Timer class. Instead, you can create multiple Timers, or have one Timer call notify() and have other threads do the actual work. Tasks that throw exceptions pose more of a problem than long-running tasks. Here's an example. Replace the PainstakinglySlowTask class with the following CrashingTask class: class CrashingTask extends TimerTask { public void run() { throw new Error("CrashingTask"); } } //new version of TestTimers public static void doMain() throws Exception { Timer t = new Timer(true); t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000); t.schedule(new CrashingTask(), 5000, 1000); Thread.currentThread().sleep(12000); } If you run TestTimers with CrashingTask, you should see output that looks something like this: Fixed rate Ping at Thu May 18 15:58:53 EDT 2000 Fixed rate Ping at Thu May 18 15:58:54 EDT 2000 Fixed rate Ping at Thu May 18 15:58:55 EDT 2000 Fixed rate Ping at Thu May 18 15:58:56 EDT 2000 Fixed rate Ping at Thu May 18 15:58:57 EDT 2000 Fixed rate Ping at Thu May 18 15:58:58 EDT 2000 java.lang.Error: CrashingTask at CrashingTask.run(TestTimers.java:37) at java.util.TimerThread.mainLoop(Timer.java:435) at java.util.TimerThread.run(Timer.java:385) After CrashingTask throws an exception, it never runs again. This should come as no surprise. What may surprise you is that no other task on the same Timer will run again, either. A wayward exception will cancel the Timer, causing any future attempt to schedule a task to throw an exception. However, there is no mechanism to notify your existing tasks that they have been brutally de-scheduled. It is up to you to make sure that errant TimerTasks do not destroy your Timers. One strategy is to guarantee that your TimerTasks never throw exceptions back into the Timer. You can do this by enclosing the TimerTasks in a try block that catches the exception. If you need to be notified of the exception, you can create a simple mechanism to notify the program that a failure occurred. Here's an example: import java.util.*; import java.io.*; interface ExceptionListener { public void exceptionOccurred(Throwable t); } class ExceptionLogger implements ExceptionListener { public void exceptionOccurred(Throwable t) { System.err.println("Exception on Timer thread!"); t.printStackTrace(); } } public class TestTimers { public static void doMain() throws Exception { Timer t = new Timer(true); //t.schedule(new Ping("Fixed delay"), 0, 1000); t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000); t.schedule(new CrashingTask(new ExceptionLogger()), 5000, 5000); //t.schedule(new PainstakinglySlowTask(), 2000); Thread.currentThread().sleep(12000); } public static void main(String[] args) { try { doMain(); } catch (Exception e) { e.printStackTrace(); } } } class Ping extends TimerTask { private String name; public Ping(String name) { this.name = name; } public void run() { System.out.println(name + " Ping at " + new Date()); } } class CrashingTask extends TimerTask { ExceptionListener el; public CrashingTask(ExceptionListener el) { this.el = el; } public void run() { try { throw new Error("CrashingTask"); } catch (Throwable t) { cancel(); el.exceptionOccurred(t); } } } This code is very similar to the previous version, except that this time CrashingTask's run method never propagates exceptions of any type. Instead, it uses a catch block to catch all Throwables and then uses a callback interface to report the exception. Here's the output: Fixed rate Ping at Thu May 18 16:41:03 EDT 2000 Fixed rate Ping at Thu May 18 16:41:04 EDT 2000 Fixed rate Ping at Thu May 18 16:41:05 EDT 2000 Fixed rate Ping at Thu May 18 16:41:06 EDT 2000 Fixed rate Ping at Thu May 18 16:41:07 EDT 2000 Fixed rate Ping at Thu May 18 16:41:08 EDT 2000 Exception on Timer thread! java.lang.Error: CrashingTask at CrashingTask.run(TestTimers.java:54) at java.util.TimerThread.mainLoop(Timer.java:435) at java.util.TimerThread.run(Timer.java:385) Fixed rate Ping at Thu May 18 16:41:09 EDT 2000 Fixed rate Ping at Thu May 18 16:41:10 EDT 2000 Fixed rate Ping at Thu May 18 16:41:11 EDT 2000 Fixed rate Ping at Thu May 18 16:41:12 EDT 2000 Fixed rate Ping at Thu May 18 16:41:13 EDT 2000 Fixed rate Ping at Thu May 18 16:41:14 EDT 2000 Fixed rate Ping at Thu May 18 16:41:15 EDT 2000 When CrashingTask throws an exception, it calls cancel on itself to remove itself from the Timer. It then logs the exception by calling an implementation of the ExceptionListener interface. Because the exception never propagates back into the Timer thread, the Pings continue to function even after CrashingTask fails. In a production system, a more robust implementation of ExceptionListener could take action to deal with the exception instead of simply logging it. There is another Timer class in the Java Platform, javax.swing.Timer. Which Timer should you use? The Swing Timer is designed for a very specific purpose. It does work on the AWT event thread. Because much of the Swing package code must execute on the AWT event thread, you should use the Swing Timer if you are manipulating the user interface. For other tasks, use the java.util.Timer for its flexible scheduling. For more info on the Timer classes, see http://java.sun.com/j2se/1.3/docs/api/java/util/Timer.html. . . . . . . . . . . . . . . . . . . . . . . . - NOTE The names on the JDC mailing list are used for internal Sun Microsystems(tm) 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@sun.com - 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/ - ARCHIVES You'll find the JDC Tech Tips archives at: http://developer.java.sun.com/developer/TechTips/index.html - 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 Stuart Halloway, a Java specialist at DevelopMentor (http://www.develop.com/java). JDC Tech Tips May 30, 2000 * As used in this document, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.