|
|
||
David Walend's BlogSwing ArchivesAffine Frustration Transformed - New! Fewer Bugs!Posted by dwalend on January 26, 2006 at 06:05 PM | Permalink | Comments (6)
I put together a generic ZoomPane that holds other Swing components. Hand ZomePane's I set up ZoomButtonPane as a simple interface. (I want to play with something more complex and transparent. It's been a while since I've worked with Swing, so I wanted to warm up with some easy buttons.) ZoomButtonPane hands off AffineTransforms to a ZoomPane. Panning worked great. Zooming in and out was a pain to get right. I found similar old questions in some forums, with no answers. I asked for help in other forums, and got a few responses that pointed to example code. Unfortunately, none of the examples solved the whole problem; in each case, some controller held on to some of the state information used by ZoomPane. I figured out how to get those details from just the old transform and size of the window.
Here's an applet showing it working: Kick the code around yourself at https://tokenarranger.dev.java.net. SomnifugiJMS for User Interfaces, Now With Example CodePosted by dwalend on June 23, 2004 at 04:03 AM | Permalink | Comments (6)Somnifugi JMS is a lightweight, single-JVM implementation of the JMS interface. I've used Somnifugi to simplify the threading architecture on a few user interfaces for clients over the years. The architecture has worked well, especially for developers who weren't familiar with Java's multi-threading. I've been meaning to follow up on an article by Johnathan Simon where he hinted that one could use it to handle threads efficiently in a Swing interface. Last month, Brian Goetz wrote an article about using parts of J2EE for non-enterprise systems. He mentioned that I provided example code for how to use Swing in the Somnifugi JMS download. He asked me to read through the article before publishing it, but the reference slipped through. After he published, the email requests for the example code started. After too many promises, I finally took advantage of a sit on an airplane to code up examples of Queues and Topics for separating work from the awt thread. I based the examples on Johnathan's original event listener example. Our coding styles are different enough that you can assume that any bugs are mine. Look in the CVS repository" to see the full examples. Like most examples that scale up well, the code I'm presenting will seem a little awkward in the smallest, simplest case. I found ways to apply two general metaphors for using JMS behind a GUI; a command queue and an event publisher. Spin may offer a third metaphor, more like request/reply. I haven't had time to investigate Spin yet. I hope to look into it on my way to JavaOne next week. Command Queue Metaphor A system can use a JMS Queue as a command queue for receiving threads. It works like this: A user causes the GUI to construct commands on the view thread. If the command will take a while to run, the GUI sends the command to the command Queue. Other threads receive commands from the Queue and carry out the user's wishes. The receiving threads make changes back to the GUI through calls to SwingUtilities.invokeLater(). Relevant code from CommandQueueExample.java:
Event Publisher Metaphor The event publisher metaphor uses a Topic to publish events from the user interface to subscriber threads. It works like this: A user causes the GUI to construct events. The GUI publishes the events to a Topic. Various subscriber threads subscribe to this Topic. Each subscriber reacts to the event to carry out part of the work required. Again, the subscribing threads make changes to the GUI through calls to SwingUtilities.invokeLater(). Relevant code from EventTopicExample.java:
Each subscriber specializes in maintaining particular facets of the system. The resulting design keeps the facets separated. The event publisher metaphor is a great fit for complex user interfaces assembled by several small teams, and for user interfaces with rapidly changing requirements. All teams publish events about what's going on in their part of the system. Any team can create new subscribers to these events. The metaphor scales up remarkably, handles rapidly changing requirements very well, and encourages teams to create isolated unit tests. Beware that this looser coupling has a cost. Debugging involves tracking flow through Java code as well as watching events in the Topic. (Always have a subscriber that logs the events in your back pocket.) The cognitive leap for the developers is much greater; many senior architects have trouble with the Zen of Topics. Also, if your project needs to preserve the feel of commands executed in order, you will need to create one subscriber that treats events like commands (easy) and insure the other subscribers don't treat events like commands (more difficult). No Escape From SwingUtilities.invokeLater(Runnable) Java's GUI kit comes with its own EventQueue, which follows the command queue metaphor. We developers can only change what the user sees on the view Thread using the EventQueue. The only good way I found to do this was to use SwingUtilities.invokeLater(Runnable). I could have set up a separate MessageListener to act as a proxy for the EventQueue. However, spinning up an extra thread just to move events from one queue to another seems excessive. Plus someone still has to define Runnables somewhere. I wound up putting
in my code whenever I needed to change something in the UI. I expect most Swing programmers at least know to use SwingUtilities.invokeLater() for GUI changes (Johnathan's experience differs), but it takes discipline. Public forums seem especially good at catching these mistakes.
Reading: The Visual Display of Quantitative Information, Tufte
SomnifugiJMS for User Interfaces and Simple-Enough APIsPosted by dwalend on September 11, 2003 at 04:56 AM | Permalink | Comments (2)Somnifugi JMS is an implementation of the Java Messaging Service built on top of Doug Lea's Channels. This JMS implementation runs inside a single JVM, quickly delivering messages between java Threads. A few years back, I created Somnifugi JMS to speed up a project where the architects had gone overboard with messaging. I used Somnifugi JMS to prototype and test the next project, and left it in place to keep things fast. The project after that, I used a Somnifugi JMS Topic to communicate from the AWT Thread to other Threads where the controllers and models live. It worked great; I've used it in every Swing project since then, and a few others have started using it this way, too. I have trouble tracking all the issues in building a Swing interface: Swing's API has about a hundred top-level classes, each with a few dozen methods, plus nine subpackages, AWT, and Java2D to get things just right. Getting a group of people to all use MVC, JavaBeans and Threads the same way is hard. The model always has its own dynamics, and usually changes a few times a day. Add users unfamiliar with the new UI to make the project even harder. The "Use Topics to communicate from the view to the controllers" pattern is a nice complement to the "Use SwingUtils.invokeLater() to communicate from the controllers to the view" pattern. Extending the pattern to place model handling in separate Threads is just a matter of adding extra Topics or Queues for the controller to consume. Plus if I need to make the application fit a client-server model, all I have to do is swap the model's Somnifugi JMS Topics for distributed JMS Topics. I think the power in this pattern comes from breaking up the task into small, easy-to-grasp pieces. Using a Topic to communicate from the view to the controller simplifies both by decoupling them. The view generates messages whenever any user action happens. The controller digests those messages. The JMS API is small and easy to learn and use. Using Topics simplifies performance decisions about handling Threads and shared Objects. The projects become more predictable, easier to decouple and test, and more pleasant to work on. I think a lot of this gain is because a JMS Topic is easier to use than Java's threading support. The JMS specification is one of the best written specifications to come out of the JCP. The underlying ideas work without bending the universe to match. Each interface fills an outlined role and I don't have to do anything weird to my own code to use them. There's no requiring me to inheriting from someone else's superclass for example. (See Allen Holub's article, "The fragile base-class problem".) I needed a single afternoon to read the spec, and I understood without strain. I spoke briefly with Joseph Fialli (one of the authors of the JMS spec) after he gave an invited talk (on JaxB) at a local user group. I described how I was using JMS behind Swing interfaces. He thought the pattern was pretty slick. He was the only person ever to compare it to InfoBus.* After the talk, I found my notes from reading the InfoBus spec in 1999. "InfoBus API looks more complex than just using the Thread API. We'd need to fill in most of the JavaBeans event spec. Not a good fit for our problem." Over the years, I've never seen a project that uses InfoBus, despite the hype it received in the late 90's. A JSR to update the InfoBus specification was started in 1998 but withdrawn in 1999. I think the InfoBus spec has been abandoned, but some ideas live on in the JavaBeans spec. I think there's a strong correlation between simple APIs and how likely developers are to use those APIs. JMS survives and thrives because it meets a need that developers recognize with relatively little overhead. JMS exemplifies a "Simple-Enough Principle" for API design. Developers never adopted InfoBus in large numbers because we didn't think InfoBus was any better than what we had before. Because all the code I write becomes library code, I try to keep this principle in mind when I define APIs. I want to create an API rich enough to do the job, but simple enough for another developer to be able to use without expanding the problem he's working on. Perhaps the advances in Aspect-Oriented code will reduce the typing overhead for JavaBeans to the point where developers can meet the specification. But that's a different blog.
* As I added mark up to this article, I got email from Ted Shab asking about Somnifugi JMS, "We are looking for InfoBus-like functionality, as well as some other related concepts."
| ||
|
|