| By Michael Birken | Article Rating: |
|
| April 11, 2005 12:00 AM EDT | Reads: |
27,904 |
Delegate's primary purpose is to serve as a bridge between an event-driven front-end and a multithreaded middle tier. In Swing, when an event is triggered, it does not execute right away. Instead, it joins the event queue (java.awt.EventQueue) where it waits along with other events to be executed by the Event-Dispatch Thread (EDT). The EDT pulls events off the queue and services them one-by-one.
An event that requires a significant time to complete - either because it's computationally intensive or because it makes blocking calls - holds up the rest of the queue, causing the user interface to seem sluggish. In fact, since repaint requests traverse the queue as well, expensive events typically cause components to appear as lifeless solid-colored rectangles.
If an event can't be processed in a timely fashion, it should forward the request to a worker thread. That's the reason why Delegate provides invokeAsync(). invokeAsync() is called just like invoke(), but it dispatches a java.util.concurrent.ThreadPoolExecutor thread to execute the method. Since it's an asynchronous call, it returns immediately without a return value. To see this in use, here's an indirect call to println() on a different thread:
Delegate delegate = new Delegate(
System.out, "println");
delegate.invokeAsync("Hello World!");
Now, Swing components aren't thread-safe; they were designed to be accessed exclusively by the EDT. To safely call Swing methods from worker threads, Delegate provides invokeUI(). If invokeUI() is called by the EDT, the method is invoked directly. Otherwise, a request to execute the method is placed in the event queue. Since the call is potentially asynchronous, invokeUI() doesn't return a value. On the other hand, Delegate.invokeUIAndWait() enqueues the request, blocks until the EDT fulfills it, and then returns the result. The following example demonstrates how to obtain a JTextField value safely from any thread:
Delegate delegate = new Delegate( textField, "getText"); String text = (String)delegate .invokeUIAndWait();
A Listener Delegate
To join Swing events with methods, I combined delegates with a dynamic proxy. The dynamic proxy is an often-overlooked concept that's been available since Java 1.3. Before I discuss it, let's quickly review the proxy pattern.
A proxy acts as a middleman, serving as a stand-in for another object. Typically that other object and the proxy share a common interface. In the case of Remote Method Invocation (RMI), for example, you access a remote object living in a JVM on a different machine across the network via a local proxy. Internally, the local proxy forwards all method calls to the remote counterparts. The proxy effectively creates the illusion that the remote class is accessible locally.
For Swing components, I needed a proxy with the ability to stand-in for any event listener. I could have created a class that implements all of the listener interfaces in java.awt.event and forwards the calls to Delegate.invoke() accordingly, but that would have required way too much typing.
Luckily, java.lang.reflect.Proxy provides a static newProxyInstance() method for generating a proxy class on-the-fly that implements a specified set of interfaces - hence the term "dynamic proxy." newProxyInstance() requires a ClassLoader, a list of interfaces as a Class[], and a reference to an InvocationHandler implementation. InvocationHandler contains a single method with this signature:
public Object invoke(Object proxy,
Method method, Object[] args)
throws Throwable
All calls on the dynamically created proxy funnel down to InvocationHandler.invoke() where the Method parameter contains a description of the invoked proxy method. It's a simple matter to use it along with args to make a Delegate.invoke() call.
To generate dynamic proxies, I created a factory class called UIDelegate. Listing 2 shows one of its static create() methods. The return type, UIDelegateListener, is an interface that extends all of the interfaces in java.awt.event. UIDelegateProxy is an inner class that implements InvocationHandler.
The create() method accepts an arbitrary number of arguments in groups of four, each corresponding to a method of an event listener interface. A group consists of the object with a handler method, the name of the event method, the name of the handler method, and the technique to call the handler. The last parameter is a boolean: true indicates that the handler is invoked asynchronously and false causes the EDT to invoke it directly. The following example shows how to associate a handleOK() method with a button:
okButton.addActionListener(UIDelegate
.create(this, "actionPerformed",
"handleOK", true));
The referenced handleOK() method must accept an ActionEvent.
For those listeners with multiple methods, you only need to specify the methods that you're interested in. For example, MouseListener provides five methods, but if you only need to respond to enter and exit events on the EDT, you can do it like this (see Figure 2):
panel.addMouseListener(new UIDelegate( this, "mouseEntered", "handleEnter", false, this, "mouseExited", "handleExit", false));
Published April 11, 2005 Reads 27,904
Copyright © 2005 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Michael Birken
Michael Birken is actively involved in the design and research of emerging trading technologies at a Manhattan-based financial software company. He's a Sun Certified Java programmer and developer. Michael holds a BS in computer engineering from Columbia University.
![]() |
Black007_pl 05/20/08 10:28:41 AM EDT | |||
Hi! Have a nice day |
||||
![]() |
Tom 04/13/05 09:10:30 AM EDT | |||
Oh, another suggestion is to use a more Microsoft alike approach. For example when instead of: container.add(button) the following is writen: container.add(SwingEventDispatcher.wrap(button, "store", this)); This line still adds the button to the container, but also names the button "store" and binds it to "this" by searching for methods with a particular pattern, for example: public void store_actionPerformed(ActionEvent e) Note the "store_" prefix. To make the source more readable this method may also be written as: public void store_clicked(ActionEvent e) And some intelligence is added also: public void store_singleClick(ActionEvent e) It makes sure that on a double click only xxx_doubleClick is called and not xxx_singleClick also. The same trick works with all events: mybean_propertyChanged(PropertyChangeEvent e) |
||||
![]() |
Tom 04/13/05 08:46:16 AM EDT | |||
A cleaner solution (IMHO) are closures. Using closures would result in something like: JButton button = new JButton(); Closures basically are inner classes that can access its context without having to use final (the local variable button is used in the inner class). Groovy provides a (hopefully soon) standard way to do this, but at least Groovy is fully Java compatible. |
||||
![]() |
A Wolfe 04/12/05 11:21:22 AM EDT | |||
Maybe I'm not following the argument here, but I guess I wonder why someone would prefer to pass around function pointers rather than interfaces. Can't the dispatch code just as easily invoke a method through an interface as through a Delegate class? I guess I am uncomfortable with the modularity here. First of all, actual implementation classes have a sort of rigidity from the standpoint of the client and the programmer. So I like having interfaces anyway. But the other issue is that a method may start out being 'standalone', but then it may need to be called as part of a sequence of other methods. If you delegate through Interfaces, then it would seem you could add those other methods very easily. Finally, variable argument lists are prone to incorrect use in all circumstances; but in this one there is no commonalties among the arguments. The classic 'printf' variety of variable argument lists was very clear -- each of the variable arguments is intended to be rendered as text. In this case it can be results of embedded function calls or anything. I guess I don't understand why one would want to suppress type checking. Anyway I have really not gotten the idea or the benefit of this technique. Sometimes it's worthwhile to emulate one language's constructs in another language, but I don't see it here. |
||||
![]() |
Ross Judson 04/12/05 09:08:06 AM EDT | |||
There's nothing wrong with inner classes per se; they represent a superset of the functionality of delegates or method pointers. The problem is the Java language itself, and the lack of lightweight anonymous function/class declaration, and the lack the type inferencing. Fix those two things and the ugly part of inner classes (the declaration syntax) goes away. Scala does lightweight functional syntax very nicely. If func takes something like an action listener, you can call it like this (for arrow read greater-equals): func(event "arrow" event.getComponent().setSelected(true)); Scala knows the expected type for the call and builds the scaffolding necessary. Under the covers you end up with: func(new ActionListener() { |
||||
![]() |
Michael Abernethy 04/11/05 01:01:16 PM EDT | |||
The Buoy package contains some things similar to what you've outlined in your article. Although I have not worked with, reading the examples makes it appear that your goals of making the event handling code easier exists already in Buoy. |
||||
![]() |
Kevin Timm 04/11/05 10:16:11 AM EDT | |||
One of the things I miss the most about C is the lack of pointers to functions. Used correctly, these are fantastically useful. Hopefully, some of that can be added back (or implemented as is shown here) for the greater good of all. Remember too, the 'Evil Empire' did not invent this desire (as noted by a previous respondent) they only implemented a shortcoming that they saw. This is not a bad thing. |
||||
![]() |
Javanaut 04/11/05 07:15:38 AM EDT | |||
### Dennis Farr commented ... The idea is certain to provoke widepsread discussion - it reminds how close C# and Java are. |
||||
![]() |
Dennis Farr 04/10/05 01:23:31 PM EDT | |||
Great article. I have been haunted by the thought that not all differences between C# and Java were abberations, and your article shows how to take advantage of the good ideas coming out of the evil empire and use them inside Java. Java is at least open enough to discuss and use ideas from outside Sun, as I hope yours will be. |
||||
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- Confessions of a Ulitzer Addict
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- It's the Java vs. C++ Shootout Revisited!
- Cloud Computing Can Revitalize Your Career as Software Developer
- IBM Could "Reinvent" Java: Mills
- Oracle & Cloud Computing: Exclusive Q&A with SVP Richard Sarwal
- A Brief History of Cloud Computing
- Kindle 2 vs Nook
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- i-Technology Predictions for 2007: Where's It All Headed?





































