|
|
|||
Hans Muller's BlogCommunity: JavaDesktop ArchivesProperty Syntax for Java? A Constructive AlternativePosted by hansmuller on January 10, 2007 at 05:57 AM | Permalink | Comments (29)Having written, by conservative estimates, about a jillion Java Beans classes over the years, I have to say that I'm amazed that we'd seriously consider changing the Java language to trivialize this kind of Java Bean property. It certainly is a property per the spec, a read/write property at that, but - as a Swing developer - it's the kind of property I almost never write. And if repetitive boilerplate is what we're hunting with this language change, then we're shooting at rabbits while a herd of buffalo thunders by. The mighty buffalo of the Java Beans boilerplate animal kingdom are bound properties. They're the kind of properties we write so that our beans can be automatically and dynamically synchronized with a GUI or with each other. As a desktop developer, I almost always write bound properties. To write a bound property properly you've got to ensure that your class defines or inherits support for a PropertyChangeListener. That's about 20 lines of code just to get started:
class FooBean {
private final java.beans.PropertyChangeSupport pcs;
public FooBean () {
pcs = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public PropertyChangeListener[] getPropertyChangeListeners() {
return pcs.getPropertyChangeListeners();
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
pcs.firePropertyChange(propertyName, oldValue, newValue);
}
}
And then there's the definition of each read/write property which should: check the validity of new values in its set method as well as calling firePropertyChange to notify PropertyChangeListeners, and defensively copy the return value (if necessary) in its get method. I suppose one could concoct syntax that would simplify all of this, at least a little, as well as allowing for read-only/write-only variants. But that's not the proposal I wanted to make here. If you consider the property keyword proposal in light of Java's origins in the C language, then it's pretty clear what the proposal's proponents are really after: structs. It's not about defining properties, it's about simplifying defining a Java class that's comparable to a struct in the C language. So perhaps the proposal should really focus on allowing one to write classes, not properties. Where this:
struct FooBean { Foo foo; }
Would by equivalant to this (as before):
class FooBean {
private Foo foo;
public Foo getFoo() { return foo; }
public void setFoo(Foo foo) { this.foo = foo; }
}
If you admit that the focus of the property proposal is really adding support for defining structs in Java, then using "->" to refer to struct properties feels like coming home again.
struct Point { int x, y; }
Point p = new Point();
p->x = p->y = 0; // oh joy
I'm not a language design expert however I would think that I would be among the target developers for Java language feature designed to support properties. In my humble (ha) opinion, the current proposal serves the needs of Java Beans developers poorly by targeting a special case that doesn't warrant language support. Although I would welcome a proposal that also simplified defining bound properties, I would guess that it would be hard to invent syntax that would handle the general case without being obscure. If there is a consituency for the current proposal, I say: give them structs instead. Javapolis Session Interrupted by Marriage ProposalPosted by hansmuller on December 17, 2006 at 04:34 PM | Permalink | Comments (11)Javapolis 2006I spent most of last week at the Javapolis conference in Antwerp Belgium. With nearly 3000 attendees, it's the second biggest Java conference after JavaOne, and it's held in a movie cineplex complex called "Metropolis". The venue delivers large halls, comfortable seats (with beverage caddies!), and dramatic lighting. Half of each enormous movie screen is devoted to a live image of the presenter and the camerawork is quite good, pausing for a dramatic closeup now and then. My laptop wasn't working correctly so I had to turn around periodically, to visually sync up with my slides. That meant that the audience was treated to six linear feet of my balding hemisphere glowing in the klieg lights. I was thankful that everyone put up with that, and also with my raspy voice. I'd managed to pick up a cold the day before leaving, and only hours before I was scheduled to speak, I could do little more than croak and wheeze. Things were somewhat better by the showtime, however from the sound of my voice you might think I'd spent the day smoking heavily and shrieking. My presentation topic was JSR-296 "Swing Application Framework". If you're interested, there's a copy here. Within a few months you'll find a nicely edited slides-and-video version of the presentation on parleys.com, thanks to Stephan Janssen, the tireless Javapolis organizer. And when it appears, do not miss the finale. As Richard Bair and I finished up a demo of a prototype of the Application Framework in NetBeans, Stephan appeared and asked to interrupt the proceedings for a special announcment. He picked up the microphone and asked a woman who's name I didn't catch to come forward. Sure enough, a tall young blonde woman came to the front, looking nonplussed. After a brief pause, a young man wearing a suit came loping down the aisle and in a moment he was facing the woman and making a declaration of some kind in Dutch. She looked pretty nervous and, not knowing where this was all going, we started feeling a bit anxious too. Until he dropped to one knee and assumed the international marriage proposal position. Then we really started to worry, because here was a situation that could only end really well, or really badly. The woman was crying (uh oh) and smiling (whew!), and then nodding her head, and to everyone's relief, we had a happy betrothal. The couple bounded up the steps to a nice round of applause, and Richard and I tried to remember what it was we'd been talking about. The Javapolis conference was memorable as always. One evening the organizers arranged for enough french fries and mayonaise to serve 3000 (that's a lot), they showed the new James Bond movie, Mark Fleury performed his keynote dressed up like Flavor Flav, Sun raffled away a Nintendo Wii, the technical sessions were solid, and, mine was interrupted by a proposal of marriage. With a happy ending. Merry Christmas!
Update: Stephan Janssen has published the video, you'll find it here: Dialog DiatribePosted by hansmuller on October 27, 2006 at 05:48 PM | Permalink | Comments (23)I've been writing the occasional small application recently and now and then I blunder into a problem with Java SE that's, uh..., well, annoying. I realize that I'm not the only one who's had this experience and I'm probably not the only one who seeks relief by writing a lengthy diatribe and then sending it to whomever might be guilty of creating the situation. Of course, in my case that's often me, and since relief usually doesn't come from berating oneself, I'm guilty of sending the occasional long crabby missive to the people who are currently responsible for maintaining things that I'm probably responsible for bollocksing up in the first place. It's not a particularly endearing habit. I sent the following to Swing's technical lead, Shannon Hickey, and he confirmed that the details, though twisted with bile, are essentially correct. So in the interest of furthering my own therapy, and also to ensure that some record of this will be stored away in Google's indices till the end of time, I thought I'd share. I would think that a fairly common idiom in a Swing application would be to popup a dialog in response to selecting a menu item. Given Matisse, we'll assume that the JDialog has been created with the IDE, rather than some JOptionPane convenience method, and given rudimentary aesthetics, assume the dialog should be centered over the menu item's frame. Accomplishing this seems to be much too difficult:
public void showMyDialog(ActionEvent e) {
// How to find the Dialog's Frame owner?
Window dialogOwner = null;
JDialog dialog = new MyDialog(dialogOwner, true); // true => modal Dialog
dialog.pack();
// How to center the Dialog?
dailog.setVisible(true);
}
The first problem to deal with is mapping from the menu item's ActionEvent to the frame that contains the menu item. The frame will be the dialog's owner as well as the component we're going to center the dialog relative to. There seems to be an overabundance of SwingUtilities methods that address this trivial problem:
To find the Frame that owns a JMenuItem, we have to follow the JPopupMenu's "invoker" property, which gets us back into the component hierarchy. So to find the frame that corresponds to an ActionEvent one must write (!):
Frame frameForActionEvent(ActionEvent e) {
if (e.getSource() instanceof Component) {
Component c = (Component)e.getSource();
while(c != null) {
if (c instanceof Frame) {
return (Frame)c;
}
c = (c instanceof JPopupMenu) ? ((JPopupMenu)c).getInvoker() : c.getParent();
}
}
return null;
}
It would more useful to have written
But we're still not done, because we must also center the dialog over the frame. Naturally there are other useful positions for the dialog. Centering a dialog over its frame happens to be what started me on this quest. I have it on good authority that Windows.setLocationRelativeTo() is the handy method for this job. The javadoc for this method isn't promising:
OK so far. Except it sounds like I'm going to have to compute the relative origin of my dialog and deal with edge (of the screen) conditions. Yech.
Huh? What does "the component" refer to in this sentence? I
assume they're not referring to this Window and I have to
wonder what "showing" means in this context. Is is the same
thing as
This, no doubt, means that the method will endeavor to find a location for my dialog that respects the relative location I've specified, without making part of the dialog appear off-screen. Good, I think. So I still appear to be stuck with computing an origin for my dialog that centers it relative to its owner. Before I code that, I try leaving the origin of the new dialog at 0,0, which is the default:
public void showMyDialog(ActionEvent e) {
Window dialogOwner = frameForActionEvent(e);
JDialog dialog = new MyDialog(dialogOwner, true);
dialog.pack();
aboutBox.setLocationRelativeTo(dialogOwner);
dialog.setVisible(true);
}
Miraculously, this works. The dialog appears centered over the dialogOwner unless that would cause the dialog to appear off-screen. I have no idea why it works, since according to the "spec" (and the name of the method) I should have had to compute an appropriate relative origin for the dialog. But I guess I don't. Frankly, I think this whole mess is a mini-travesty. If I'm going to show a dialog, I should be able to do so without writing code that digs around the component hierarchy and without experimentally determining what something as simple (and not terribly useful) as Window.setLocationRelativeTo() does. There, that feels a little better. A seven year old bug that covers the menu item to frame lookup problem is still open. Given the fact that it's accumulated exactly 0 votes in that time, perhaps no one has ever cared about the problem quite as much as I do at this moment. I would think that a cleaner way to handle this case would be some static methods that handled the entire idiom, for example:
public void showMyDialog(AWTEvent event) {
Window dialogOwner = Window.eventToWindow(event);
JDialog dialog = new MyDialog(dialogOwner, true);
Window.showModalDialog(dialog); // Center dialog relative to its owner
}
And Shannon suggested that the method name might be rationalized
as implying that the Window is be moved to a location that makes
its relationship to the component parameter obvious.
Typically that means centering the Window relative to the
component. In return for that tortured explanation, I had to
agree to file an RFE about the
Thanks for listening. Fire Marshall Extinguishes Dukelele PerformancePosted by hansmuller on May 19, 2006 at 06:52 PM | Permalink | Comments (2)Tuesday morning this week, I was seated in the vast Moscone keynote cavern, with 15,000 other Java developers, taking in the start of another JavaOne conference. The keynotes and demos were entertaining and I hope you didn't miss the HUGE Swing Aerith demo at the conclusion of the morning. Sadly I did, although I've seen quite a lot of it over last few weeks. I had to dart out early, because my first-ever JavaOne musical gig started at about 10:30 and I had to get my bass and set up in time for the big Dukelele show. That's right, Dukelele. A Ukelele painted like so:
A group of us played music in front of the JavaOne store at one end of the corridor that connects the Moscone's North and South subterranean chambers. In addition to me, the band was Hideya Kawahara and Yuichi Sakuraba playing Ukeleles (Dukeleles!) and singing, Mark Anenberg on a guitar-shaped drum synthesizer, Chet Haase on a laptop powered keyboard, and Kaoru Nakamura playing a keyboard/harmonica hybrid called a Pianaca. And to top if off, Duke danced and hugged people. We started playing as the keynote audience began flooding past and to our delight, many of them stopped to listen. Tragically, the Moscone Fire Marshall did not share our joy. After about 10 minutes he swooped in and put a stop to the show. I guess we were a fire hazard, or at least Hideya was. He was really putting his heart into singing and playing and I suspect that the Fire Marshall was afraid that he might suddenly burst into flames. Another rock and roll show, shut down by the man. Not exactly Altamont Speedway, but definitely a strange and abrupt ending to our little performance. Fortunately one of our colleagues videotaped the whole thing and so now you can check it out on YouTube.com. No animals were harmed in the making of this video. We played again, in the afternoon, outside on the sidewalk. It was a bit breezy and most of the people who drifted by gave us an odd look and then slipped indoors. One was exception was Tim Boudreau, who took in the entire set and then threw a quarter in our tip jar. Except we didn't have a tip jar. Our friend the Fire Marshall seemed to be pleased that we finished without spontaneously combusting. Cautious man that he is, he kept a sharp eye on the proceedings from a safe distance, clutching his fire extinguisher in one hand, and a fistful of swag from the Motorola booth in the other. Next year we'll bring our own fire protection. A Reusable BuddyList ComponentPosted by hansmuller on February 27, 2006 at 03:30 PM | Permalink | Comments (13)Every now and then someone drops by to ask about the slick chat/IM demo components that were shown in the Extreme GUI Makeover JavaOne session last year. The Swing components created for those demos where hacked together in order to show what's possible and sadly, they're not available as production quality components just yet. I certainly like the idea of resuable, configurable/extensible, chat client GUI parts. If I were building that kind of application I'd be happy to avoid starting from scratch. This blog is a brief look at one such part. You can try it out by pressing the launch button.
BuddyCellRenderer is an attempt to build a somewhat reusable JList CellRenderer for Chat/IM buddy lists. It's job is to render an object that represents a Buddy roughly like this: screen name [status] short message [icon] Here "status" is one of online, offline, or away. Away status means that the user is online but busy. The "screen name" is the Buddy's name, "short message" is an optional short message from the Buddy, and icon is a picture that represents the Buddy. All of this is quite conventional. These elements appear in most chat/IM application buddy-lists in one form or another. If a "short message" isn't provided we change the layout just slightly: [status] screen name [icon] The BuddyListCellRenderer must also provide a Buddy-specific tooltip that's displayed if the user lingers over one BuddyList element. JList renders list elements or "cells" by delegating to an implementation of ListCellRenderer. ListCellRenderers have only one method, getListCellRendererComponent(), which returns a Component that the JList uses to paint a single list element. The JList really just uses the cell renderer component's paint() method to draw or "rubber stamp" a list element. The getListCellRendererComponent() method is passed the JList model's "value" for each list element, and its responsibility is to return a component that's been configured to display that value. The default ListCellRenderer is quite simple. It just uses the same JLabel for every list element, roughly like this:
JLabel label = new JLabel();
Component getListCellRendererComponent(JList l, Object value, ...) {
jLabel.setText(value.toString());
return jLabel;
}
To display the properties of a Buddy in the way we've layed out above will require more than just a JLabel. BuddyCellRenderer uses a JPanel with subcomponents for the various properties and GridBagLayout to define the layout. A generic ListCellRenderer that configures our JPanel composite to display a Buddy value is difficult because we don't want to dictate the type of the Buddy object but we do need to extract its status, screen name, icon, and message. What's needed is an adapter that extracts the properties needed by the BuddyCellRenderer from the app-specific Buddy object. The BuddyListCellRenderer.Adapter class does this. The way it works is easiest to explain with an example. Lets assume that our chat/IM application has a Buddy class that looks like this:
class MyBuddy {
boolean isOnline() { ... }
boolean isAway() { ... }
String getScreenName() { ... }
ImageIcon getIcon() { ... }
}
A JList ListModel that encapsulated the list of MyBuddy objects would have to be created; I will not delve into that here. The adapter for MyBuddy objects could be defined and used like this:
class MyBuddyAdapter extends BuddyCellRenderer.Adapter {
private MyBuddy getBuddy() { return (MyBuddy)getValue(); }
public String getName() { return getBuddy().getScreenName(); }
public String getMessage() { return getBuddy().getMessage(); }
public ImageIcon getBuddyIcon() { return getBuddy().getIcon(); }
public Status getStatus() {
if (getBuddy().isAway()) {
return Status.AWAY;
}
else if (getBuddy().isOnline()) {
return Status.ONLINE;
}
else {
return Status.OFFLINE;
}
}
}
BuddyCellRenderer cellRenderer = new BuddyCellRenderer();
cellRenderer.setAdapter(new MyBuddyAdapter());
myBuddyJList.setCellRenderer(cellRenderer);
That's pretty much all there is to it. The BuddyCellRenderer
scales (and caches) the Icons provided by the Adapter if they're
bigger than If you'd like to try making some changes to BuddyCellRenderer and the demo, you can download a NetBeans project with the source code and the jar files here: Download BuddyList NetBeans Project . Using Java Web Start to Launch NetBeansPosted by hansmuller on January 09, 2006 at 04:21 PM | Permalink | Comments (24)About six months ago I had a dream. Not the sort of dream that makes you wake up shrieking or smiling, and not the kind that brings you down from the mountain top or even gets you off the couch. Mine was the kind of dream programmers have. The kind of sloth inspired idea that comes to you while staring at the screen, wondering if there's a way to eliminate all of the mouse clicking and key pressing effort that makes you weary without actually burning calories. I spend quite a bit of time looking at Java blogs and articles that incorporate lots of source code. Usually there's a link for zip file that contains all of the files the document refers to, and maybe a jar file with a build. Sometimes articles include direct links to source files, however scanning a pile of source code with the web browser isn't terribly appealing. The nicest way to look at code and try out APIs, is to just load everything into a Java IDE like NetBeans. Once that's done it's possible to use the editor and debugger and all of the other IDE features to explore the code. Like a real programmer should. And just like a real programmer, I'm usually too lazy to bother. So the big idea was to write a web started app that would download a complete NetBeans project, launch NetBeans, and open the key source files in the editor. That way, if someone was reading a blog or an article about some Java project, they could click on a JNLP link - and with no additional effort! - peruse the code from within the IDE. To me, this seems like a civilized way to do business. Sadly, I wasn't able to con one of my colleagues into building such a web started app. Towards the end of last year, as Sun began to slow down in anticipation of the Christmas break, I took a crack at building a NetBeans launcher. You can try it now, by clicking on the handy launch button below. It's a signed application, because it creates a temp file and launches a (NetBeans) process on your machine, so you'll have to click through a security dialog. To give the example launcher a try, just click the Launch button: The launch app depends on the NetBeans OpenFile module to start the NetBeans IDE, if necessary. Sadly (at the moment) there isn't an "OpenProject" module, so the best I can do is to show a single Java source file. If the launch app is unable to locate an installed copy of NetBeans, it displays a little form that allows choosing the install directory, or downloading the current NetBeans release. I haven't tested launch very carefully (works on my machine :-) which is unwise, since part of the code to find and launch NetBeans is platform specific. It should work on Windows XP and it might work on Linux or the Mac or Solaris. I'd be happy to hear from anyone who's tried it. The example file downloaded by the launcher, NetBeans.java, deals with figuring out if and where NetBeans is installed. I didn't pick this one file out of pride, it's incomplete and contains some moderately embarrassing hacks. On the other hand, it does all of the important work. The class is used like this:
NetBeans nb = new NetBeans();
nb.initialize();
if (!nb.isInstalled()) {
// give the user the opportunity to choose
// the install directory or download NetBeans
}
else {
File file = new File("C:\MyProject\src\pkg\MyFile.java");
try {
nb.openFile(file);
}
catch (NetBeans.Failure e) {
// report the problem to the user
}
}
The initialize method uses some shameful heuristics to try and figure out where NetBeans was installed. Check out the source code from within the IDE to see what I mean. It would certainly be much nicer to be able to look in a well known (per platform) place to find out what versions of NetBeans were installed and where; maybe in the future that kind of support will emerge. You can use the launch app on you own web site, just by making a copy of the JNLP file file and replace the URL in argument element at the bottom. The current version links to the example NetBeans.java file: <argument>http://download.java.net/javadesktop/blogs/hansmuller/launch/NetBeans.java</argument> You don't need to copy the launch jar files or anything else. Just create a link to your version of launch.jnlp on your site, and make sure your web server is configured to support the JNLP MIME type. This was originally intended to be a quick project that I'd finish on on the long flight home from Prague back in December. It didn't turn out that way, in part because building (usable) GUIs is always more work than you'd think. It's also because I took the opportunity to get introduced to Matisse. It's been a long time since I've been comfortable writing Swing GUIs with a tool. Using Matisse turned out to be pretty inspiring: it worked well and it greatly simplified the task of evolving a GUI. Using it was a constant reminder of all of the additional desktop app building support that should be in NetBeans. More about that in another blog. I have a long laundry list of worthy improvements for the NetBeans launcher. I'd be interested to hear what other developers think of it and what you-all think should be changed/improved. Here are a few of TODO items from the top of my list:
If you'd like to look at the complete NetBeans launch project, you'll find a zip file of the complete project here. I realize that it's more than a little ironic to publish such a link. Hopefully I'll be able to support loading entire projects (not just files) in round two. JFrame.add() contentPane Pain: The Complete StoryPosted by hansmuller on November 16, 2005 at 11:35 AM | Permalink | Comments (10)It's my fault. The fact that adding a component to a JFrame required one to explicitly add it to the JFrame's "contentPane" is my fault. Early on in Swing's evolution we added a runtime exception that warned developers not to write JFrame.add(myComponent) and it has been raising hackles ever since. Graham Hamilton covered my transgression in his My Favorite (Dead) Java Boilerplate blog and I thought I'd explain the rationale behind it's birth and eventual demise. It turns out that I did not create this trip-wire to incite violence or to "educate developers about the choices" within the JFrame container. JFrame's automatically created rootPane, layeredPane, and contentPane substructure was designed to enable popup effects that appear on top of the main GUI. The original motivation for JFrame's substructure was we to support lightweight menus and tooltips and even dialogs that appeared within a top level window. It's also possible to use the substructure to produce novel GUI effects, like translucent full-window progress monitors. So why did JFrame.add() generate an exceptional slap in the face for the developer who's not schooled in all of this? The 1.0 and 1.1 releases of Swing were delivered on the original Java 1.x platform. Our audience was AWT developers who typically wrote small apps by subclassing java.awt.Frame and overriding paint() or setting its layout manager and adding children. When we decided to create JFrame's substructure there was a debate about the wisdom of automatically mapping JFrame.add() to JFrame.getContentPane().add(). The reason I rejected that approach is that this "convenience" is a shallow illusion. To complete the illusion one would have to redirect get/setLayout(), and addComponentListener(), and getComponent() and getChildren() and so on. In addition to making it tough to actually get inside the JFrame itself, the complete illusion would be asymmetrical since the source of events or a layout manager's container wouldn't match what a developer would expect. So in the interest of consistency, not education, we did not automatically redirect JFrame.add() to the content pane. Time has passed and the number of AWT developers who's expectations might have been violated by making JFrame.add() convenient has become pretty small. It's also true that the merits of providing a simple trouble-free out of the box experience, even if it depends on an imperfect illusion, are increasingly important. So, in Tiger, JFrame's add and setLayout (and addLayout) methods have changed to "do what you [probably] mean". The other JFrame methods, like getComponent(), do not redirect, so if you use them, be careful. And if you don't use them, well, ignorance is bliss. Now that we've got that out of the way, anyone have a nomination for a new Swing boilerplate hall of shame candidate? As far as I know, the rest of the API is perfect. A Brief Report from JavaOne JapanPosted by hansmuller on November 09, 2005 at 07:32 AM | Permalink | Comments (5)
This year, JavaOne Japan is during the week of November 7th in a jaw dropping venue called Tokyo International Forum. On Tuesday Scott Violet, Josh Marianacci, and I made two presentations based on Desktop Java talks from the San Francisco JavaOne: Extreme GUI Makeover, Episode 1: Lookin' Good, and Extreme GUI Makeover, Episode 2: Runnin' Fast. Sessions at JavaOne Japan are shorter, just 45 minutes, and the presentations are translated into Japanese in real-time. Before each session we met with the translators to go over technical terms and other jargon. The translators work in pairs because the job is a bit of a mental sprint, so they shift the work back and forth. They always advise us to follow two simple rules: speak slowly, pausing between slides to allow them catch up, and don't tell jokes. Not being funny just comes naturally, however it's tough to speak slowly when you're trying to cram 60 minutes of material into a 45 minute session. We tended to start out at a sensible pace and then gradually accelerate to the point where the last slide sounded like it was delivered by Alvin and the Chipmunks. After the first talk we met the translators in the corridor. The culture here is polite and friendly and I think they abhor violence. Still, I was glad that the translators didn't have a club handy. Here are the important sites and documents we referred to during the talk. The San Francisco versions of both talks are available online at developers.sun.com: Romain Guy developed some of the special effects shown in the "Lookin' Good" presentation and he's been writing about them in his jroller blog as well as his blog on java.net. The code for some of the special effects is available now from the SwingFX project . The animation framework used to animate the button and window backgrounds was written by Chet Haase and is the basis of an open source project at timingframework.dev.java.net The Mustang splash screen API is documented in this java.sun.com tutorial And you can download an early access build of Mustang from mustang.dev.java.net. Scott Violet's in-depth article about performance tuning applications with large JTables is available on java.sun.com: Christmas Tree Applications The SwingWorker API, for moving work from the event dispatching thread to a worker thread, has been extensively documented. The latest documents and downloads can be found at swingworker.dev.java.net The latest beta release of NetBeans includes support for performance tuning and the new Matisse Swing GUI designer. You can download NetBeans from netbeans.org. Information about configuring the GC, for example to reduce startup time by making the heap big enough, can be also found on the netbeans site: performance.netbeans.org/howto/jvmswitches/ Three other bloggers have covered the Extreme GUI talks at JavaOne Japan: Charles Ditzel: Live From Tokyo: Extreme UI Makeover - A Great Talk on Developing Powerful Desktop Apps and John O'Conner: JavaOne Tokyo '05: Extreme GUI Makeover and Greg Sporar from the NetBeans team: JavaOne Tokyo, Day One. Scott, Josh and I appreciate the coverage and the photos (thanks John)! Official: Swing is the Dominant GUI ToolkitPosted by hansmuller on October 18, 2005 at 12:31 PM | Permalink | Comments (33)I've been trying to think of a way to humbly announce that no lesser authority than Evans Data Corporation has reported that Swing is the dominant GUI Toolkit for Northern American developers. It's difficult to present this new statistic with the grace and humility of good sportsmanship because, after nearly 8 years of steady growth: "Java Swing with 47% use, has surpassed WinForms as the dominant GUI development toolkit, an increase of 27% since fall 2004." That's a direct quote from the Spring 2005 report. You may want to read it again (I have). There are more developers building applications using Swing and Java SE than WinForms and .NET. Despite the titanic resources marshalled by Microsoft to assert dominance over their own desktop platform, the Swing community has grown into an unstoppable force. Microsoft has often been referred to as an "eight hundred pound gorilla". Thanks to the persistence and enthusiasm of Swing developers everywhere, we've thrown the gorilla and the cage off the island. We're the new alpha male, we're the King Kong of GUI toolkits. We are the force to be reckoned with. We are number one!. I realize that was a little over the top. I'm supposed to be humble and quietly confident about our success and not indulge in all of this vulgar gloating and boasting and jumping up and down on the desk shouting, we're number one, we're number one, we're number ... Sorry about that. I'll just remain calm from here on in. You'll have to trust me when I say that I'm reporting the following from a peaceful and serene perspective. The use of both Swing and AWT have grown dramatically in the last year and, quoting from the report, "Java GUI development is clearly experiencing substantial growth". So it is. I would guess that there are at least two trends at work here. People are writing Swing clients to augment or replace browser clients for network services, and developers really do care about platform portability. Sometimes portability is just about spanning different versions of Windows but more often than not, it's about covering the growing "alternative" desktop market. Users want applications that provide entertainment or communication or educational experiences that are worthy of the fine computer hardware they're seated in front of, and the zippy internet service they're connected to. Developers are choosing Swing to deliver those experiences and here, at camp Swing headquarters, we couldn't be happier. It's good to be king and it's hard to be humble. I feel a T-shirt coming.
Thanks to Jeff Dinkins for another bit of just-in-time artwork! Open Source BluegrassPosted by hansmuller on October 05, 2005 at 01:23 PM | Permalink | Comments (6)This past Saturday morning, a friend and I were in San Francisco at Golden Gate park, walking out of the Speedway Meadow. It was cool and overcast and the fog blanketed the tree tops and hung over our heads and gave the surroundings the blurry hazy look of an old newsreel. We were walking down into another hollow, listed on the map as Marx meadow, and drifting toward us was the sound of Joan Baez singing "The Night They Drove Old Dixie Down". The long narrow meadow is flanked by steep hills and tall trees. With its foggy ceiling and dim light it felt like a cathedral. There were thousands of people there, listening, shuffling toward the stage for a better view or just sitting in the grass listening to the story about Virgil Caine and the tragedy of the American Civil War. We joined them and listened to Joan Baez sing more old American songs, songs by Woody Guthrie and Bob Dylan and folk songs that have been around so long they're just attributed to "traditional". I'll never forget being the same place last year listening to John Prine sing "Angel from Montgomery" or this year, hearing anti-war and protest songs inspired by conflicts from the distant past and from right now. It's tough to write about how great it was spending a day listening to music. It was entertaining, it was moving, it was fun, sometimes it was beautiful. In the evening it was really cold. You should have been there. If you live in the area, you should have stepped away from the keyboard and taken your head out of the network for a while, and plugged it into the music. At least that's what I did and I couldn't have been happier. But that's not why I'm writing this. There were five big stages in Golden Park on Saturday and Sunday, different bluegrass and folk music performers every hour, from 11AM till 7PM, and it was all free. Free as in Free Beer. A (very) wealthy man named Warren Hellman has been putting on the "Not Strictly Bluegrass Festival" show on his own dime for the past five years. I can't tell you exactly why he does this, although if I had to guess I'd say he's motivated by the same spirit that inspires software engineers to join open source projects and build things just for the pure joy of it. That spirit confounds economists looking for a profit motive and capitalists looking for a profit. There were about a bajillion people in Golden Gate Park taking in the music over the weekend, and they weren't confused at all. So I just wanted to say thank you. Thank you Warren Hellman for sharing two days of music with the Bay Area, again. If you've actually read this far and have made a mental note to delete me and my moon-eyed ravings from your RSS feed, here's a non-sequitur that I hope you'll find reassuring. It doesn't have anything to do with free music, but it's a genuine Java code sample that you might find useful.
Examples from the java.util canon are usually short and tidy. In theory, now that we have type parameters, the can be even shorter and tidier. For example if you have a TreeMap that defines the number of occurrences of each word in a document, you can print them all like this:
Map<String, Integer> countMap = new TreeMap<String, Integer>();
...
for (Map.Entry<String, Integer> e: countMap.entries()) {
System.out.println(e.getKey() + ":" + e.getValue());
}
All well and good, unless you wanted to see the word/count entries sorted by word count, not by word. TreeMap keeps the entries sorted by key, that's the word String in this case. To sort the word/count Map.Entry entries by word count you must convert the entries to a List, define a Comparator that compares Map.Entry values, and then use Collections.sort to sort the list.
/* Sort the entries in countMap by count. This code has
* me wondering about just going back to AWK.
*/
Comparator<Map.Entry<String, Integer>> compareEntries =
new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer>e1, Map.Entry<String, Integer>e2) {
return e2.getValue().compareTo(e1.getValue());
}
};
ArrayList<Map.Entry<String, Integer>> entries =
new ArrayList<Map.Entry<String, Integer>>(countMap.entrySet());
Collections.sort(entries, compareEntries);
Maybe it's just me, but yech. Sometimes I'd really rather just listen to Earl Scruggs play Foggy Mountain Breakdown. Using Swing's JFormattedTextField for integers is not as trivial as it should be.Posted by hansmuller on August 25, 2005 at 02:18 PM | Permalink | Comments (2)Earlier this year I was fiddling around with the new J2SE network ProxySelector APIs as part of a small demo-project. Sadly, the project just wouldn't stay small and I didn't have time for something big. So after a few days it disappeared into one of the many corners of my laptop's hard disk, where it's been quietly moldering away. One part of the old demo was a small GUI for collecting network proxy host names and port numbers. I'd used JFormattedTextFields for the latter. You might think that doing so would have been trivial, since port numbers are just integers between 0 and 65534 and JFormattedTextField is very, well, flexible. It turns out to have been not so trivial and at the time I was inspired to write a blog-sized article about exactly what I'd done. That article would have remained buried with everything else from the project if it hadn't been for some interesting JFormattedTextField threads on the javadesktop.org JDNC forum that cropped up recently. The problem that inspired the JDNC JFormattedTextField thread had to do with decimals (like 123.45). Since I'd spent some time in the trenches with a similar problem, I thought it might be fun to exhume my old article and toss it on the pyre. So here it is.
ProxyPanel: A Swing Component for Network ProxiesWarning: if you're looking for the material about JFormattedTextField you can skip the first couple of paragraphs. I've left the first couple of paragraphs the way they were out of respect for the dead. Plus, I'm too lazy to edit them out. Before writing more than a few lines of code I considered structuring the ProxyPanel component conventionally: with careful separation of model and view, and with great flexibility for all dimensions of both. The model would be a Java Bean that included all of the data required to completely specify the usual set of networking proxies along with all of the secondary data like overrides and user names and passwords. The bean's API would be specified as an interface, so that the ProxyPanel could operate directly on application data, and an abstract class would provide a simple backing store for the data along with all of the change listener machinery required to keep the GUI view in sync. The view would be equally overdesigned. It would be configurable, to accommodate applications that wanted a compact or subsetted presentation. And just before I awoke from my second system syndrome induced reveries, I imagined providing an XML schema that could be used to completely configure and (cue the Mormon Tabernacle choir) even localize the GUI. This was supposed to be a tiny project aimed at highlighting the new ProxySelector APIs and providing a small coding diversion for yours truly. So, after I'd calmed down, I decided to write a simple GUI that wasn't terribly configurable and that lacked a pluggable model. That's right: no model/view separation here. If there are MVC gods, I'm sure I'll be in for some smiting. And if the gods can't be bothered, then I'm confident that my more dogmatic brethren will take up the slack. Please don't send your self-righteous segregationist rantings about the merits of MVC to me. I know, I know. My first cut at structuring the code for the four pairs of proxy host/port fields that correspond to the bulk of the GUI was to create a little internal class that defined the GUI for just one proxy, in terms of four components:
public class ProxyPanel extends JPanel {
private ProxyUI httpUI;
private ProxyUI httpsUI;
// ProxyPanel constructor initializes httpUI etc ...
private static class ProxyUI {
private final JLabel hostLabel;
private final JTextField hostField;
private final JLabel portLabel;
private final JFormattedTextField portField;
ProxyUI (ProxyPanel panel, String hostTitle, String host, String portTitle, int port) {
// create labels, fields, and update the GridBagLayout
}
String getHostName() {
return hostField.getText();
}
// ...
}
}
The ProxyPanel created four ProxyUI instances and squirreled them away in four private ProxyPanel ivars. The ProxyUI class did encapsulate the details of how one proxy was presented to the user. On the down side, had to assume that the ProxyPanel had a GridBagLayout (no encapsulation there) and it felt gratuitously complicated. One lesson I learned as part of building this first revision of ProxyPanel was how to configure a JFormattedTextField that accepted either a integer between 0 and 65534 or an empty string. The latter indicated that the user hadn't provided a valid value. It seemed like it would a little less surprising for users to map no-value or invalid values to a blank than to insert a valid default value like 0. JFormattedTextFields are eminently configurable and if you'd like to get acquainted with the API I'd recommend the Java Tutorial. The specific problem I was trying to solve isn't covered there however with a little help from the local cognoscenti I was able to work things out. The Swing class that takes care of converting to and from strings as well as validating same, is called a formatter and the subclass needed for numbers is called NumberFormatter. A separate java.text class called DecimalFormat is delegated the job of doing the actual string conversions and it provides its own myriad of options for specifying exactly how our decimal is to be presented. Fortunately in this case we don't need to avail ourselves of much of that, in fact we're going to defeat DecimalFormat's very capable features for rendering numbers in a locale specific way. What we need is just a geek friendly 16 bit unsigned integer. Or a blank. Here's the code for our JFormattedTextField instance. We override NumberFormatter's stringToValue method to map "" (empty string) to null. The ProxyPanel.getPort() method that reads this field will map null to -1, to indicate that the user hasn't provided a valid value.
DecimalFormat df = new DecimalFormat("#####");
NumberFormatter nf = new NumberFormatter(df) {
public String valueToString(Object iv) throws ParseException {
if ((iv == null) || (((Integer)iv).intValue() == -1)) {
return "";
}
else {
return super.valueToString(iv);
}
}
public Object stringToValue(String text) throws ParseException {
if ("".equals(text)) {
return null;
}
return super.stringToValue(text);
}
};
nf.setMinimum(0);
nf.setMaximum(65534);
nf.setValueClass(Integer.class);
portField = new JFormattedTextField(nf);
portField.setColumns(5);
It occurred to me that perhaps an IntegerTextField would be worthwhile. That way one could write: IntegerTextField inf = new IntegerTextField(); itf.setMinimum(0); itf.setMaximum(65534); itf.setEmptyOK(true); itf.setEmptyValue(-1); // new feature, "" => -1 itf.setValue(0); I don't think that's a vast improvement however developers might have an easier time sorting out how to create an IntegerTextField than assembling the right combination of DecimalFormat, NumberFormatter, and FormattedTextField. Of course, having gone so far as to create IntegerTextField we'd want similar classes for currency values, real numbers, dates, and so on. Some of this is already covered by JSpinner although spinners are better suited to cycling through relatively small sets of values. Eight months later ...It's been a long time since I wrote all of that. Looking back I'd have to say that a set of classes, like IntegerTextField, would certainly make life more straightforward for Swing developers. Hopefully the SwingLabs project will take up the cause and maybe in the future a collection of battle-hardened special purpose text fields will find their way into the JDK. If they do, I'll use them. JavaOne Desktop Report: 20M Brazilians File Tax Returns with Swing ApplicationPosted by hansmuller on July 15, 2005 at 03:23 PM | Permalink | Comments (0)JavaOne 2005, Moscone Hall in San Francisco, Thursday at 2:30. It was the very end of a very long week. A week that ended on June 29th and began in March. Chet Haase and I had just debuted the GUI Puzzlers technical session and despite some lunch and wandering around, we were both exhausted. We took our seats for the last desktop session on the last day of the conference. Weaker men would have nodded off to sleep. If only those Moscone Hall seats had been a little more comfortable. Late last year I attended the JavaPolis conference in Belgium. It's held in a large cinema multiplex and sessions are held in the individual theaters. It's really the perfect venue for holding a conference save for one tragic flaw: the seats are really comfortable. The perfect storm of jet-lag, soft lighting, a reclining seat (with a beverage caddy!), and a speaker who's beginning to drone a little, can have tragic consequences. At one point, I woke up to discover that my entire row had nodded off. At any rate. While waiting for the session to begin, I might have submitted to the urge to just catnap, just for a second, if I hadn't been distracted by a strange vision. Striding purposefully towards the stage, wearing a green cape and cradling what appeared to be a large parrot puppet, was Bruno Souza. I was probably the only person on the Java planet who didn't know Bruno. He's part of the contingent of Java developers from Brazil; the contingent that conference keynoters relied on for a rousing shout, whenever a burst of enthusiasm was called for. The session wasn't due to start for a few minutes and Bruno filled the time by talking to the crowd with the puppet. It was a little surreal and very entertaining. I stayed awake. Bruno is a good speaker and he launched the "Desktop Java Technology on a Massive Scale: Brazil's Swing-Based Income Tax Application" session. Bruno pointed out that Brazil is larger than the continental US, however there are only 170M inhabitants. About 42M Brazilians paid tax electronically last year, which is 95% of eligible individuals and 100% of corporations. The IRPF desktop Tax Reporting application was created in Java/Swing by a company called Serpro that handles data processing for the Brazilian government. It is used by all Brazilian tax payers to file their returns electronically - over the internet. Brazil has been supporting online tax returns for some time:
The current version of tax reporting app was built entirely in Java, with a Swing GUI. Development started in September 2003, beta release was December 2004, and the app was deployed and used by about 20 million tax payers in April 2005. Next year the old Win32 app will be retired and all tax returns will be delivered with Java. All of the work, from UI to docs, was done by about a dozen developers. Linux, Windows, and OSX are officially supported although, ironically, most of the development was done on Windows! As it turns out the app actually landed on more than 15 different platforms. That explains the need for platform independence and it's a nice indication that Java really is succeeding at "run anywhere". The Java platform deployed in Brazil is J2SE 1.4 and JGoodies FormLayout was used extensively for the GUI. Bruno's colleague Serge delivered the second half of the session, which focused on the architecture of the application. The engineering team created an app framework called PGD that supports data binding and forms and so on. Completed tax returns are compressed and encrypted and transmitted back to the Secretaria de Receita SRF tax collectors over the internet. The server "Receitanet" that handles incoming returns is also written in Java. Futures:
In conclusion Serge reported that their experience building the application had been very good:
JavaOne Desktop Report: A BIG web started deployment of a BIG banking app.Posted by hansmuller on June 29, 2005 at 03:20 PM | Permalink | Comments (4)It's JavaOne 2005, Monday afternoon, and I'm drifting around Sun's booths on the tradeshow floor. Moscone's meeting halls are two stories tall and underground; it's a basement palace. The cavernous space used by the tradeshow Pavillion must be a quarter of a mile long and a hundred feet high and it's abundantly and artificially lit. It's like being in an aircraft hanger that's inexplicably buried two stories below street level. So I'm ambling around with the same glazed expression I wear in casinos and supermarkets, when one of my colleagues buttonholes me. She's been talking to a developer/architect who's been thinking about building and deploying a large web started Swing banking application. It was to be deployed to about 5000 desktops and naturally the developer was interested to know about other financial institutions who'd already done the same thing. I've always tried to pay attention to what desktop developers are doing with Java and so this was, in theory, my moment to shine. Unfortunately the accumlated stress of preparing for JavaOne and my lack of Moscone underworld acclimatization had drained most of the charge from my brain. There are many examples of large financial institutions with web started desktop app deployments but sadly I was unable fire enough neurons to deliver the message. If I'd had a full charge, I would have directed the developer to the technical session on Wednesday called "Large Scale Client Deployment Using Java Web Start Software". I attended this session myself and made some notes. The presenter was Matthias Schorer who's the Technical Chief Architect at FIDUCIA IT AG in Germany. The presentation was an excellent introduction to using Java Web Start for big deployments of big real-world applications. I wanted to record a few of the highlights so that the next time I'm dumbstruck, I can point developers to my blog. FIDUCIA is the largest IT-Fullservice provider for the German cooperative Banks. They provide comprehensive software solutions to 920 banks. The service includes both desktop and server side applications and they run data centers for the latter. Managing about 38 million accounts (2.3 billion transactions/year!) requires lots of big iron. They've got nearly 1000 server machines and 138 terabytes of (SAN) storage. The desktop software is deployed on 106,500 PC desktops. And it's all Java. Desktop and server. All Java. Fiducia's web started desktop banking application, "Banking Work Place", is a big one. It's 10 million lines of Java, developed by 640 engineers, and it provides 553 user accessible functions. The complete set of application jars weighs in at a healthy 120M. The app runs on desktops with Windows, Linux, OSX, and even OS/2. It's also a great looking Swing application! I've appended some low resolution screenshots from Matthias's talk below. We'll get a Swing Sightings item with a bigger set of full resolution screenshots out shortly. The focus of the talk was the practical aspects of deploying such a large application to over 100,000 desktops. The developers at Fiducia have extensive experience with desktop Java. They deployed their first home banking applet in 1996, and they've been delivering web started apps based on the Java Banking Framework (JBF) since 2002. One of the biggest obstacles the developers had to overcome was that although branch banks typically have high speed LANS, they often have relatively low speed internet connections. The other problem was that individual banks wanted to be able to control what version of the app was deployed and when. All web started applications are cached and updates can be delivered as small deltas (jardiff). So, in many cases even very large web started apps can be deployed on the internet with a minimum of fuss, once the initial app download is taken care of. To accomodate low bandwidth connections, Fiducia puts a caching proxy on each bank's LAN, in between the Fiducia IT-Center download servers and the bank's desktop users. Download proxy servers (a single Java process) run on a bank file server and cache application jars as well as all of the jardiff differential downloads. Fiducia updates the proxy servers at night time, "because at night, nobody is in the bank". The proxy download server also helps with deploying different versions of the app. The application's JNLP file is actually a template that's expanded at request time to create a custom app definition based on the source of the request. The expanded JNLP file is passed along to the standard JNLPDownloadServlet which takes care of delivering the jar files and deltas. Interestingly, not only is the proxy download server written entirely in Java, it's also web started! The "Large Scale Client Deployment Using Java Web Start" technical session was inspiring and informative (many developers asked about getting the proxy download softare for their own applications!). Here are some low resolution screenhots of the app in action, look for more in a Swing Sighting preview on javadesktop.org soon.
If You've Got a Name - Check out this AppletPosted by hansmuller on June 17, 2005 at 01:27 PM | Permalink | Comments (4)I was in a technical meeting recently, with about a dozen developers, and the discussion topic turned to AJAX. We were seated at tables in the usual presenter-in-the-center horsehoe configuration. Each participant was ensconced in a defensive posture, behind a big laptop. The tenor of technical meetings has changed over the years, thanks to technology. Complex adjustable office chairs facilitate slouching behind a laptop screen's protective wall, so that it's only necessary to expose one's eyes and forehead to the rest of the room. When someone ventured a point about AJAX, it was like ringing Pavlov's bell. A dozen foreheads tilted upwards, eyes wide open and shining from the reflected glint of email messages and web pages. AJAX is the hot technology du jour, the saving grace for developers who've toiled for years, trying to make browser applications palatable. This was a sophisticated crowd, so the usual boosterism and swooning was quickly put aside in favor of some sober discussion about AJAX's shortcomings. Meeting participants often use laptops and wireless internet connections as a way to multitask, snapping from one context to another just like music videos have trained them to. This can be a very disturbing experience for a presenter, since it's very easy to judge how well your material is getting across in real time. If you're not more interesting than email, IM, and the web, your message will reach little more than the gray plastic backs of laptops. Your words will just bounce off the lowered foreheads, while the keyboards quietly crackle. Ringing the AJAX bell roused the entire group for a moment. If we'd all been wearing miner's helmets, the dramatic shift in attention would have lit up the speaker's face like a spotlight. Sometimes, web surfing in the middle of a discussion can have a very positive effect. When the web content you're scanning is related to the topic, a quick dose of the details can lead to a more informed discussion. Many people are much more effective at this than I am, so I've taken to watching what's appearing on the screens that flank mine. So, while the AJAX discussion lumbered along (apparently the developer documentation is inadequate) I kept an eye on my neighbors' screens. On my left, an interesting looking animated application appeared. The user typed names at what appeared to be a graph of the geological record and then a new version of the graph swooped into place. A closer look revealed that the graphs showed the relative popularity of people's names over the last 120 years. Given the discussion, I assumed that the application was some AJAXian miracle, like Google maps. I didn't get around to trying the app until the next day. That's when I discovered (insert trumpet fanfare here) that what I'd been watching was an applet called Name Voyager. This applet is very cool and it's appealing to anyone who has a name. You will not be able to resist typing your appellation at the top to see how its popularity as fared since 1900. Although I was born and raised in the (great state) of New Jersey I have a pretty ethnic German name. The results from the Name Voyager for "Hans" are interesting. There's a big dip in the name's popularity in the 1940s. I wonder why.
Apology
Name Voyager Applet Screenshot: Hans's Popularity since 1880 Grokker Java applet makes Monday's New York Times business section.Posted by hansmuller on May 10, 2005 at 03:56 PM | Permalink | Comments (5)Would it be shameless to plug a new release of a interesting new search visualization product just because it happens to be featured in Monday's New York Times (May 9th)? In the Business section, on page C-3, with a nice color screenshot and a teaser at the top of page C-1? Perhaps it would. But only if I failed to mention a former colleague, who used to manage the J2SE client group, now works at Groxis, the company who produced this press-worthy Java client software. So far he hasn't agreed to provide any incentives in return for promoting the new free Grokker Applet front-end for Yahoo search. If the Groxis news page is any indication, they don't really need my help.
Grokker application screenshot with highbrow art search results. Grokker is a visualization for search results that organizes matches into hierarchical groups, where the members of each group all belong to a category. As you can see in the screenshot above, categories are displayed as circles, membership by containment, relevance by scale, and so on. An enormous amount of information can be put on the screen at once, thanks in no small part to some expert use of Java2D. There's also support for dynamically filtering the results. So if you want exclude matches that are newer than 1997, you can just grab a slider and interactively exclude just as much of the past as suits your purposes. Grokker is available now as an applet for everyone, you'll find it on http://www.grokker.com/. It's also available as a stand-alone application. I've used screenshots of the stand-alone version from an somewhat dated Swing Sightings column because I'm lazy. The library here at Sun provides a Grokker interface for our entire collection, as does the Stanford University library. I've talked to the librarian for Sun about it. Apparently Sun library patrons have found Grokker to be really effective; sometimes saving hours of time relative to conventional list-of-text search systems. The feedback from Stanford researchers has also been really positive. As a Java client developer I can honestly say that I'm pleased to hear that this Java applet is a useful tool; but did anyone notice the GUI's awesome rollover highlights? And the animated segues? And the snappy performance? Grokker is a great example of what's possible with Java2D and Swing and the complete Java platform. And, it's a pretty good search visualization tool too.
Grokker Application Screenshot: drilling into the roll-over highlights. Data Binding in Laszlo - Lessons for JDNCPosted by hansmuller on April 07, 2005 at 05:05 PM | Permalink | Comments (8)For the past two months or so, I've been working with some of the JDNC developers on the databinding problem. After some false starts, the approach we've taken is to define "data aware" components and some special encapsulation classes for relational data. The overall goal is to make forms and master/detail applications relatively easy to build by automating most of the donkey work involved in interconnecting data sources with Swing GUI components and their attendant data and selection models. All in all not a terribly novel quest, however it's an important one and it's long overdue. Data binding is an old problem and there are plenty of worthy examples to learn from. The support in ADO.NET and the newer version of that in Avalon certainly qualifies, although the complexity of Microsoft's solution should serve as a warning about the dangers of trading simplicity for generality. The JGoodies data binding system , which is based on the SmallTalk ValueModel idea, is another fine example. The Laszlo platform, which is popular among Flash aficionados and can boast some very beautiful demos, also supports basic data binding. I'd written a short review of Laszlo's databinding support recently and the JDNC developers with whom I'd been working suggested publishing it here. In case you're already bored I'll provide the punchline first. We've observed that data binding systems that bury the natural syntax for specifying bindings with abstractions can be more trouble than they're worth. If I'm binding to JavaBeans then strings using the usual "bean.property" expression language notation are a nice way to specify bindings. Similarly, if you're binding to an XML document, then an XPath expression is a natural way to define a component's data source. Laszlo's data binding system is designed just for XML data and they employ an XPath subset for specifying bindings. Here's how. What follows is based on a quick study of the data binding part of the Laszlo developer documentation, chapters 28-30 . The Laszlo designers had about as much flexibility as anyone could want, since they designed a new "language" (an XML schema) for defining applications that targeted Macromedia's Flash player. Laszlo applications are defined by an XML document. Trivially simple GUIs survive the encoding and remain simple:
<canvas width="500" height="350">
<text>Hello World</text>
</canvas>
Complex GUIs are probably more compact than a Java version however in my (biased) opinion, one ends up writing too much cryptic JavaScript/unix-shell style line noise like:
<tree datapath="*" text="$path{'@name'}"
isleaf="${this.datapath.xpathQuery('@type') == 'file'}"/>
The Laszlo platform includes a modest number of basic GUI controls; behavior is defined with JavaScript. There are a handful of very primitive layout managers however if the documentation is any indication, absolute layout is the norm. Platform look and feel fidelity is not a goal. Laszlo supports XML data binding with XPath (a small subset) expressions. The XML data can be embedded in the application source code, which is good for examples, or it can be loaded from a URL. The binding system works nicely for simple things however once the world of "hello world" is left behind, Laszlo gets ugly in a hurry. There doesn't appear to be any support for master/detail applications with their pesky connections to selection. Apparently master/detail relationships must be defined at the level of writing "onClick" handlers. Updating a relational database is a similarly do-it-yourself operation. That said, the basic XML data binding support is tidy enough, particularly if you're familiar with XPath syntax. To bind to an XML document you have to give it a name, and to do that you use the dataset tag:
<canvas>
<dataset name="customerData">
<customers>
<customer firstName="Fred" lastName="Mertz"/>
<customer firstName="Ethel" lastName="Mertz"/>
</customers>
</dataset>
<simplelayout axis="x"/>
<text datapath="customerData:/customers/customer[1]/@firstName"/>
<text datapath="customerData:/customers/customer[2]/@firstName"/>
</canvas>
The dataset tag has a "src" attribute, like the HTML anchor or image tags, whose value is a URL. There's adequate support for programatically reconfiguring and and reloading a dataset and one can write handlers that get notified each time the dataset has been completely loaded. Thanks to the "simplelayout" (like AWT flow) layout tag, this GUI just displays "Fred Ethel". The data binding for each text component is specified by the "datapath" attribute which names the dataset and, on the RHS of the ":", the XPath expression of the data that the component it bound to. Note: if you're an XPath novice as I am, you might find the datapath expressions in the example a little bit confusing. They look like filesystem paths (and they're called "paths") but their semantics are subtly different. They're really patterns that match XML tag names and properties of XML elements and attributes. The latter are enclosed in square brackets, e.g. "[1]" means the element whose one-based index (relative to its siblings) is 1. So the XPath "/customers" means: all of the elements whose tag is "customers" and "/customers/customer[1]" selects the first child whose tag is "customer", and "/customers/customer[1].@firstName" selects its "firstName" attribute. It took me a while to stop trying to write "/customers[1]/@firstName". I'm over that now. The Laszlo generic container class is called "view" and you can bind it to data and then use relative data binding paths on the components it contains. This example produces the same output as the previous one:
<canvas>
<dataset name="customerData">
<customers>
<customer firstName="Fred" lastName="Mertz"/>
<customer firstName="Ethel" lastName="Mertz"/>
</customers>
</dataset>
<view datapath="customerData:/customers/">
<simplelayout axis="x"/>
<text datapath="customer[1]/@firstName"/>
<text datapath="customer[2]/@firstName"/>
</view>
</canvas>
Where Laszlo data binding gets interesting and ugly is in producing tables. There's no explicit table construct, you just bind a view to a list of elements and the GUI components contained by the view are "replicated", once for each list element.
<canvas>
<dataset name="customerData">
<customers>
<customer firstName="Fred" lastName="Mertz"/>
<customer firstName="Ethel" lastName="Mertz"/>
</customers>
</dataset>
<simplelayout/>
<view datapath="customerData:/customers/customer">
<simplelayout axis="x"/>
<text datapath="@firstName"/>
<text datapath="@lastName"/>
</view>
</canvas>
The XML above produces a GUI with four elements, like this: Fred Mertz Ethel Mertz In other words, we're not binding firstName and lastName to columns in a special table component. What we've got is just short hand for creating one row of textfields for each customer element in the dataset. There is no selection support and watch out for large data sets. To deal with the latter you can try and configure the special "replication manager" which provides pools of components that can be reused for rows, and the "onclone" JavaScript callback that alerts you when a view has been replicated, and, well, yecch. There are several other interesting features of the Laszlo databinding system that I don't think are worth diving into here. For example one can bind component attributes to XPath expressions (but be prepared to think hard about when those bindings are evaluated). There are "datapointers" which can be used to create bindings that get moved around with methods like datapointer.selectNext(). What's good about all of this is that, assuming you've got XML data, you can bind to that data using moderately intuitive XPath expressions. Returning to JDNC and given our canonical Customers, Orders, and Parts example, and a quick and dirty schema, you could write the bindings for the three tables (roughly) like this:
customersTable.setDataPath("/customers/customer");
ordersTable.setDataPath("/customers/customer[@selected]/order");
partsTable.setDataPath("/customers/customer[@selected]/order[@selected]/part");
<customers>
<customer firstName="Fred" lastName="Mertz">
<order id="o1">
<part id="p1"/>
<part id="p2"/>
</order>
<order id="o2">
<part id="p3"/>
</order>
</customer>
<customer firstName="Ethel" lastName="Mertz"/>
<order id="o3">
<part id="p4"/>
<part id="p5"/>
</order>
</customer>
</customers>
Here I've created a synthetic attribute called "selected" that's implicitly defined for all tags. If it's present then the corresponding XML DOM node is selected. The implication is that the binding engine that interpreted this binding would the customers JTable's selection model at run time. What's nice about this is that the developer gets to define the bindings using the same syntax as the (XML) data they're binding to. A Desktop Java "Killer Application"Posted by hansmuller on March 30, 2005 at 10:35 AM | Permalink | Comments (9)It's been a while since I've contributed a blog. It's not for lack of thinking about it. The inspiration for pounding out some purple prose about developments around java desktop software has flowed from my brain to the tips of my fingers half a dozen times of the past month or so. For one reason or another I've set aside the the urge to write each time. It gets easier and easier to do so, until there's some twisted satisfaction in just coming up with a topic that might have made a good blog entry. There's no need to actually write anything, now that my brain has begun equating a brief personal visit with a promising idea with the satisfaction of actually writing about it. This is starting feel dangerously close to solipsism and so, in an effort to ward off incipient madness, I'm going to wade back into the blogosphere. Here in the Java client department, we used to have to put up with the occasional harangue about the lack of a "killer application". The harangue would always begin in a patronizing tone, conceding that, oh yes, there are tens of thousands of desktop Java applications for all kinds of specialized tasks and specialized people. What we're not seeing out there, from our elevated perspective (they'd continue), is just one gigantically successful, widely used, everyone has just got to have it, killer application. The usual response to this kind of tirade was to tick off a list of applet games, or collaboration tools like QNext or Elluminate, or - and this was always the coup de grace - Limewire. A year ago, at about 150 thousand downloads a week, the Limewire (GNUtella) file sharing application was a pretty respectable killer application. It's been listed on download.com for about 18 months and for most of that time it's been among the top 10 or so. Now it's number two. Limewire's popularity, measured in terms of number downloads or the number of active GNUtella nodes, has been growing steadily. In terms of downloads, the application is now up to about 1.4 million a week. That's a lot. At the moment (per the stats on download.com), it's about 3X WinZip, 10X RealPlayer, and about 20X Windows XP Service Pack 2. Limewire's popularity can be attributed to a variety of things, including great performance, an easy to use Swing GUI, and the lack of any kind of co-bundled spyware or ham-fisted registry busting installer. I think it's fair to guess that the demise of BitTorrent listing sites like SuperNova didn't hurt either. However the BitTorrent "network" has hardly gone away and our own Supreme Court is puzzling over the merits of file sharing as I write this. I hope that they'll find that the good force balances the dark side. If you haven't given this application a try, check it out at http://www.limewire.com or download.com. The very usable GUI features a custom Swing look and feel that supports skins and has been localized for more than a dozen languages. Startup is fast, and the app automatically "swarm" downloads from multiple GNUtella nodes when that's possible. Limewire is open source so if you're a developer you may want to visit http://www.limewire.org/. Or just join the millions of people who just use it. Inside TiVo's new Java SDKPosted by hansmuller on February 01, 2005 at 03:09 PM | Permalink | Comments (2)On Monday, TiVo announced a Java SDK called the Home Media Engine (HME) and a corresponding simulation tool, all for writing PC applications that target their digital recorder box. The announcement has been heralded in many forums, notably slashdot , the New York Times, EWeek, Yahoo News, etc. You'd be hard pressed to discover the fact that TiVo's new SDK is a set of Java APIs and a new J2SE-based tool in most of the coverage, so naturally I'll try to fill that embarrassing lacuna here. The TiVo SDK and simulator tool run on J2SE 1.4.2 or 1.5 and they're available for free on http://tivohme.sourceforge.net now. The documentation appears to be comprehensive enough and there are enough examples to kick-start development. Plus the source code for the entire SDK is provided. The only thing lacking for yours truly is a TiVo box at home to really complete the experience. Sadly, that will have to wait. Fortunately one can obtain a reasonable TiVo developer experience facsimile by running applications against the simulator (check out a screenshot here ). Just for the record, I wasn't able to run the samples "right out of the box" on my Java Desktop System laptop due to some confusion about the IP address for my machine's ethernet port. The symptom was a "java.net.MalformedURLException". The fix was to lookup the eth0 address using ifconfig and pass that as well as a port number to the sample app on the command line:
> /sbin/ifconfig eth0 | fgrep "inet addr"
inet addr:129.145.161.189 Bcast:129.145.161.255 Mask:255.255.254.0
> java -cp simulator.jar:samples/samples.jar
com.tivo.hme.sim.Simulator com.tivo.hme.samples.hello.HelloWorld
-i "129.145.161.189" -port 7288
TiVo's developer forum on sourceforge.net was helpful for sorting this little snafu out. What may be becoming clear from all of this is that TiVO HME applications are actually servers that the TiVo box or the simulator tool discover and connect to. You don't download applications into the box, the application or application service running on your PC sends commands to the box and receives events, roughly like an X11 client would. Unlike an X11 client, an HME service includes a factory that generates a new instance of your application for each TiVo box that connects to it. This aspect of the HME SDK will not be much of a concern while you're prototyping an app for the TiVo box in your living room. If you wanted to turn your creation into a service for all, then you'd have to think carefully about the sorts of scaling issues that application server folks worry about. The HME SDK is a small API. It makes it possible for an app running on your PC to map data from the internet to a visualization that's appropriate for your TV. HME applications build hierarchies of translucent rectangular View objects where each View contains one image, text, or color resource. A View corresponds to the essence of a GUI component, it just defines a 2D coordinate system and a clip rectangle. There's an HME Application class which defines a root view and you override its init() method to add your own views to that, e.g.:
public class MyApplication extends Application {
protected void init(Context context) {
View v1 = new View(this.root, 0, 100, this.width, 100);
View v2 = new View(this.root, 0, 200, this.width, 200);
Resource text = createText("default-36.ttf", Color.white, "Hello"));
v1.setResource(text);
ImageIcon icon = new ImageIcon("myImageFile.png");
Resource image = createImage(icon.getImage());
v2.setResource(image);
}
}
In this example the Resource createXXX methods create text and image resources on the TiVo box synchronously. It's also possible to stream images resources from an arbitrary URI to the box. At the moment there's no support for streaming or playing videos, an unusual limitation for a TV device. There's no way to gain access to the TiVo's archive of recorded material either; of course this is only an "early access" release of the SDK. Event dispatching is a similarly no-frills affair. The TiVo box sends events for everything from the TV keyboard and remote control to progress reports per downloading a streamed resource, back to the application. The application can deal with them by overriding an Application method called handleEvent(). One novel feature of the HME SDK is support for animation. Some of the methods that set View properties take an extra string argument that specifies how to animate the transition from the properties current value to the new one. For example to "fade in" a View over a period of 500 milliseconds one could write: myView.setTransparency(1.0f); // initially transparent myView.setTransparency(0.0f, "*500"); // 500ms, linear ramp One can animate changes in a View's bounds, origin, scale, and transparency. One can also specify a second animation parameter (also part of the string) that defines how fast the animation accelerates and decelerates to/from the linear ramp. I suspect that's enough of an introduction to this new SDK. I didn't cover the support for loading/streaming audio clips however it was nice to see that the TiVo folks picked up the JLayerME MP3 decoder (featured in the Swing Sightings column back in April 2002) from JavaZoom, see http://www.javazoom.net/javalayer/javalayerme.html for the latest information about that. And then there were more than 50: More Swing Component LibrariesPosted by hansmuller on October 14, 2004 at 01:13 PM | Permalink | Comments (9)The javadesktop.org Swing Component Depot column has a backlog of more than 50 component suites. They'll all appear there eventually however we thought you'd like to see the queue now, in all its unadorned glory. So, here for your delectation is a brief summary of all 50+ of them. I appreciated the comments in response to the original version of this blog ("Another 40 Swing Component Libraries") that have appeared here on javadesktop.org and on JavaLobby.org, particularly the many suggestions about libraries that deserved to be added to the list. Some of the feedback pointed out that it wasn't entirely clear where I was drawing the line, not every library listed here was a Swing Component suite. I have to admit that didn't religously hew to that line. Looking at web sites devoted to Swing component suites often lead to products that were relevant to Swing app developers like spell checkers, PDF generators, and so on. A few of these have made it into the list because I couldn't help but inadvertantly vacuum them up in the process of collecting vital statistics about mainline Swing component libraries. In this update I've added another 14 libraries to the list, nearly all of the ones suggested by people who've offered feedback:
One suggestion that led to to a few extra entries that probably don't, strictly speaking, belong was Hypergraph. Hypergraph is an impressive open source project that has produced a J2SE component for visualizing hyperbolic trees. There are several commercial projects in this space: Inxight, Thinkmap, and TheBrain.com. All of these products provide applet viewers for hyperbolic trees and all of them have eye-popping demos on their respective web sites (go ahead and look, you will not be disappointed). Both Inxight and Thinkmap provide SDKs, however based on my cursory look at their sites, the SDKs are for server side work or they're custom JDK1.1 viewers based on AWT. In fact I didn't list TheBrain.com at all, despite the inspiring name, since there didn't appear to be a Java SDK there at all. Still it didn't seem right not mention them since they're great examples of what desktop Java is capable of and they could probably become part of a modern Swing application. Plus, the Hypergraph site provided pointers to all of them and I just couldn't stop myself. Warning: in many cases I've lifted or crudely paraphrased vendor product descriptions in the list below, rather than craft my own pithy summary. It's not because I'm incapable of doing a good job of that, it's because I'm lazy and life is short. It's also worth pointing out that I'm not trying promote the relative merits of these packages, just their existence. There are more than 50 entries in the list below and that's more than the most devoted fan of Swing component libraries is going to want to sift through. I've attempted to break the entries into categories to ease the burden a bit: General Purpose, Database, Graphs and Charts, Special Purpose (for physics, astronomy, oil exploration, etc, components look here first), Calendars, and "More". Naturally not every library fits nicely into one of these categories, for example nearly every general purpose library contains calendar or database parts. The "More" category contains libraries that didn't fit into a category. There are two (obvious) additional categories that are missing because they're big enough to deserve their own roll-up: layout managers and look and feels. Those categories will have to wait until a future blog; I've got blisters on my fingers. General Purpose A Java Swing library for creating configurable menus, toolbars and popups. Applications built with Configure can be reconfigured by end-users through direct manipulation of the GUI itself. JAPISoft provides everything from EditX, v an XML Editor and XSLT Debugger, to most of the components you'd need to build a software engineering tool, like an IDE. Among the Swing parts you'll find on the site are JXMLPad, a Swing component/framework for editing XML/XHTML documents, JDock, a Swing framework for MDI applications that is compatible with standard layout managers, and JResourceBrowser a generalization of JFileChooser. Most of the software is vailable for free evaluation and the sites, www.JAPISoft.com and www.SwingAll.com, are packed with screenshots. JSuite is a broad suite of Swing and AWT components, including charts (notably Gantt charts), tables and grids, calenders and schedules. Jeppers is a full featured web-based spreadsheet editor (free!) written in Java. It also provides an LGPL grid component that can be used in Swing applications. Check out this applet demo that launches the spreadsheet in its own window. ICESoft has been building pure Java web rendering software for quite a long time. They now have three big products:
Javaio - WebWindow, Component Suite, JModeler Loads of very high level parts here, beginning with the WebWindow browser component. WebWindow is a small pure Java HTML viewer that's intended to take up the slack from Swing's JEditorPane. JModeler is a generic diagram editor for building modeling applications and the Javaio Component Suite includes components like JOutlookBar (this sort of thing is sometimes called an "accordion" control), JHelpView for JavaHelp, and JSlidePanel for animating panels in and out of view. A big collection of Swing components (free!) including a font chooser, a directory chooser, and many other useful parts and dialogs. Try them out with this web started demo. Eltima - Visual Java/Swing Components Library A large suite of Swing components, from borders and buttons to date and color choosers. There's a nice looking general purpose image viewer in the mix and some high octane combobox variations as well. A general purprose collection of components. Some examples: JDataGrid (spreadsheet style table) JCalendar (date picker), JFontChooser, JLinkButton (button that can launch a URL), and a suite of custom layout managers. This component library from JGraph.com includes two Swing components: JAutoCombo and JTreeComboBox. JAutoCombo is a combo box with auto-completion, JTreeComboBox is a combo box where the user selects a node from a tree rather than an item from a list (good for large hierarchical sets of alternatives). Check out the web started demos for JAutoCombo and JTreeCombo components on the JGraph site. SyGem Software's collection of Swing Components are designed to provide Java Application programmers with a complete range of GUI widgets to complement and enhance any existing Swing interface. UltraSwing provides easy-to-use components that allow you to create polished, modern GUI applications. With this library, you can easily add advanced features, such as docking/floating, Outlook type side menu, to your GUI projects. A small (only 64K) library of Swing components, including a nice selection of color choosers. zBlueSoftware - Java Components Presently zBlueSoftware provides two components: ZCalendar - a data chooser for all locales, and zFontChooser - font chooser dialog or toolbar element. This company also makes a Java IDE called zBlueStudio. MultiSoftGroup - Blazze GUI Framework Blazze provides you with a set of prepared and common used elements of a user interface. These elements are joined together by intuitive presentation logic. As a result we have got universal, generally used GUI framework which can be used for most of business applications like CRM, ERP and others. Free! Check out the web started demo. An open source docking framework; now with alpha composited drag previewing! Components! is a robust collection of sophisticated components for JFC/Swing, including general purpose calendar and calculator components. An open source project with many things to offer, including a nice suite of custom Swing components. UICompiler makes it possible to take designs created with TrollTech's Qt Designer and compile them into Swing applications with a custom look and feel. Try the to see the end product in action.EII Commons - an open source library of commonly useful components. Among them are: a sortable JTable/model, a wizard framework, a a set of masked edit controls, a print preview widget that works with any Pageable, various tree, table, and list utilities, and more. Zeus is an open source Java Swing component library. It provides useful swing components for easier GUI development: JSplash and JConsole. The Kiwi Toolkit is a foundation class library containing many useful classes that complement the Java Foundation Classes (JFC). The toolkit includes many classes and components that were not provided with the JFC, such as a TreeTable component, a command line parser, a DateChooser dialog, and many others. Database Simprit Swing is an addon package for Java Swing. Simprit Swing includes useful data entry components to improve your users' data input experience, and some new components such as tool bar pane, calendar and date picker to impress your users The DBDataControl provides a fast and efficient way to retrieve and edit data from any relational database. SwingSet is an open source toolkit for making Swing database-aware. SwingSet utilizes SSTextDocument, an extension of the standard PlainDocument class to link the JTextField or JTextArea to a database column within a RowSet. In addition, custom classes are provided to replace the standard JComboBox and JCheckBox. An app framework for Swing with support for data binding, validation, and so on. JForm is a framework for building form-based user interfaces with Swing. It provides a library of custom Swing components that supports automatic databinding and validation. Graphs and Charts JUNG, the Java Universal Network/Graph Framework, is a software library that provides a common and extendible language for the modeling, analysis, and visualization of data that can be represented as a graph or network. Check out the JUNG applet demos here. JGo is a graphics library that makes it easy to build custom interactive diagrams, network or workflow editors, connected graphs, scheduling or organizational charts, smart maps, flowcharts, and software design tools. It has built-in support for many shapes, text, images, containers, connectors, orthogonal links, arrowheads, scrolling, zooming, selection, drag-and-drop, resizing, in-place text editing, tooltips, layers, and multi-page printing. It is powerful and efficient, yet small and easy to learn and use. It includes built-in support for Swing's Undo framework. Dirk's J Toolkit (DJT) - Gantt Chart Class Library DJT is used for developing sophisticated Gantt chart solutions for a big variety of applications that range from operation-based scheduling applications to enterprise resource planning applications (ERP). HyperGraph is an open source project which provides java code to work with hyperbolic geometry and especially with hyperbolic trees. It provides a very extensible api to visualize hyperbolic geometry, to handle graphs and to layout hyperbolic trees. Inxight's application and component SDK products are focused on "information discovery" and visualization. Among them are StarTree for visualizing large collections of objects and TableLens for visualizing large tables. The SDKs are for the 1.1 JRE however they're extremely capable. Check out the demos and see for yourself. The Thinkmap SDK enables organizations to incorporate data-driven visualization technology into their enterprise Web applications. Thinkmap applications allow users to make sense of complex information in ways that traditional interfaces are incapable of. Thinkmap's display is a JDK1.1 applet, see The Visual Thesaurus for a great example. Special Purpose JSky - Java Components for Astronomy JSky is being developed as part of the Gemini Observing Tool, which astronomers will use to plan their observations at the Gemini Telescope in Chile. Don't miss this press release about a recent Gemini telescope observation: "Like Nothing Seen Before - New Object Defies Classification". FreeHEP (High Energy Physics) Java Library The goal of the open source FreeHEP library is to encourage the sharing and reuse of Java code in High Energy Physics. Although some of the code is fairly specific to HEP, other code is more generic and could be used by anyone. To maximize reuse we strive to keep the dependencies between various packages in the FreeHEP library to a minimum, so you can use which ever parts interest you without being forced to use the entire library. J/GeoToolkit delivers high performance graphics capabilities for Oil and Gas E&P applications. ESRI - MapObjects Java Edition A rich collection of client and server components for Java developers. MapObjects-Java Edition can be used to build custom applications that incorporate GIS and mapping capabilities or to extend the capabilities of existing applications. TWaver is a complete suite of Java graphic components for telecommunication operations support system (OSSs). LAB Asprise! - Java Image Acquisition UI Components Presently there are two components in this free library:
Financial and mathematical J2SE Components. Use our JavaBeans compliant Java Class Libraries within any Java solution: JDBC enabled, detailed PDF technical documentation, JavaDocs, bundled GUI JavaBeans and client examples. The open source Gandalf wizard framework consists of a set of classes and interfaces that give developers an infrastructure to build wizards. Two types of wizards are currently supported: web-based wizards and Swing-based. JBalloonToolTip is the first true java implementation of balloon-like tooltips. It allows you to use the whole power of old-fashioned java tooltips (font, color, size, html formating, etc.) and modern ballon-like layout. G is a generic graphics library built on top of Java 2D in order to make scene graph oriented 2D graphics available to client applications in a high level, easy to use way. Starlink is a set of Java data reduction and analysis tools and Java data access classes. The new Java tools and classes are needed to produce applications in the Virtual Observatory (VO) era, and to complement the AstroGrid and other VO capabilities. Among the many examples of this software in action is SPLAT, a spectral analysis tool. Calendars A configurable date chooser panel. A customizable combo box style date chooser. This site offers some other generally useful components: JCalendarCombo, JCasc | |||