Spring Richclient ~ Session One ~ Making Petclinic work
Right, here goes.
Initially I wasn't sure where to begin. I searched for "Spring Rich Client" on google, looking for a tutorial and found zero. I found the Rich Client homepage on the spring site, which was a start, and read the rather text-heavy introduction carefully.
I was rather suprised at this point that there wasn't an alpha release to download, but never mind, I suppose the framework is still in early development; and hey, I'm supposed to be a coder, and coders can handle checking out CVS, right? :)
Armed with an appropriate sense of coder machismo, I headed off to the sourceforge pages and checked out the code. Hit a hurdle trying to find the correct module name (spring-richclient, by the way - it's not mentioned on the webpage), but got there in the end by browsing the Sourceforge repository.
This got me the code, which looked to be a standard java project buildable with ant. Time to fire up emacs (I'm a JDE freak, away you Eclipse acolytes with your fancy iconry and easy-to-use plugins):
So far, so good. Lots of text whirred past and I've built a version of the latest framework and packages it into three nice jars in the 'dist' directory. I've still no idea what anything does, but I'm feeling good at this point. I'm even brave enough to try running the unit tests...
More whirring text. There are lots of tests passing, which is encouraging to a TDD fanboy. Most seem to pass without a hitch, I'm rather encouraged, then I see one failure:
I haven't a clue what a DefaultSecurityControllerManager is, and decide to ignore it completely at this stage. I figure that if I encounter a problem later, I'll do a cvs update and see if that tests was fixed, and try again, then post to the Spring forum if it still doesn't work.
So, onto the sample application. I find a samples directory which contains 'petclinic' - most encouraging. Another build.xml - excellent:
Hmm. Clean, build and javadoc I understand, but what's with the standalone and warfile variants? This is initially rather confusing, as I figured that there wouldn't be any need to run a web application to use Spring Rich Client. I peer at the build.xml file for a bit longer, until I see the Java Web Start (JWS) references and figure the web application must just be to demonstrate a Rich Client application running through JWS.
(Note: it's only later that I saw that you need a web application because petclinic can be a client-server application - this wasn't at all clear on first read. Perhaps two samples would be clearer, rather
than combining both in the same place? Or perhaps a simple "Hello, World?" application would bridge the gap?)
I decided to take one step at a time and forget about JWS for now:
More whirring text, with the petclinic jar placed in the 'dist' directory. Excellent - it seems all is well so far and I didn't need the web application stuff. Ok, let's give it a go. Thinking that it's ten to one I've missed something, I try:
Ooo. A splash screen. A frog, even better. And then, to my great delight:
Wow. JGoodies support right out of the box. I couldn't have hoped for anything better.
After clicking next a few times, and bypassing the login screen with a "Cancel", I get:
Wonderful. I've now got the source code compiling and I've a sample application I can tweak and see what happens - the only way I can really learn anything. I'm set.
A couple of thoughts before I close out this session. Firstly it's clear the bad old days of ugly java apps are over. I'm blown away by how good the application looks, for a java application. That's partly due to the wonders of JGoodies, but it looks like Spring Rich Client is going to help me produce a lovely looking application for the client. Excellent.
Secondly, I'm pleased that there really weren't that many steps for the average java developer who's used to ant to go through before getting this working. The only real hurdles were finding the right CVS module, and deciphering the ant build targets which at first glance are rather impeneterable. I'm also pleased it compiled straight from CVS and (almost) all the tests passed; a failing build, and I would have mostly likely given up there and then.
Coming soon: "Session Two: Hello, world!"
Share this article
Read more
Spring Richclient ~ That's a wrap
My project is shrink-wrapped in virtual plastic and the client is happy, so that just about wraps up my look at Spring Rich Client.
After writing a full app from conception to completion, I thought that it was a useful and powerful framework deserving of attention. There are plenty of features you get thrown in for free, such as decent localisation support and excellent form binding capabilities. A couple of closing thoughts follow...
The framework needs more documentation. Once you master the basics you're ok, but I get the nagging feeling I missed whole sets of features simply because I didn't know they were there.
A maintained stable release would be a significant step forward. Once the project was easier to download and try out I think many more people would at least evaluate it. The more people using it, the more developers that might come forward, which has to be a good thing for the project.
I hope you enjoyed our little jaunt. Best of luck on your own Spring Rich Client journey.
Update: It looks like Spring: Rich Client have released 0.1.0 on sourceforge; the first stable revision! Should have checked before I wrote this post :) Congratulations to the Rich Client team - hopefully the project will move forward in leaps and bounds now.
Read moreSpring Richclient ~ A Journey
This series of articles documented my journey through the depths of Spring Rich Client in an attempt to learn how it all works.
- Introduction
- Session One: Making Petclinic work
- Session Two: Hello, World!
- Session Three: How did that work then?
- Session Four: Beans, shmeans and services
- Session Five: Applications, windows, views
- Session Six: The View
- Session Seven: Beating the Command Framework into Submission
- Session Eight: Such a Bind
- That's a wrap: Closing Thoughts
Spring Richclient ~ Session Eight ~ Such a bind
Let's look at the binding framework today. I've got an options dialog to write, and it looks like the binding framework could be just the ticket...
For the uninitiated, when we talk about "binding" we mean linking values in a text field on a form to values in our Java class. Essentially we want to turn:
into this:
And when the user hits "Save", we want the values in our java object to change.
This is a standard well-defined problem for which there exists a number of solutions. So what does Spring Rich Client give us to aid us in our quest? Let's have a look.
How do we make a form?
The first example that comes to mind of a view with fields on it is the "owner properties view" in the petclinic sample. This is shown when we right click an owner:
Let's have a look at the code which created this page, from OwnerManagerView.java:
Ok, to create a form, we do the following things:
- Grab the currently selected Owner.
- Create a form model for this Owner. So what's one of those? Turns out that the class has got some documentation - from FormModel.java: Ok, so it's an abstract representation of a form. Fine.
- Create an OwnerGeneralForm, passing a "child page" version of the form model. This makes sense - we've got two tabs, one for "General Info" one for "Address". Rich Client seems to links the "child page" FormModel it creates with the main wizard's FormModel.
- Do a similar thing for the OwnerAddressForm.
- Add both of these forms to a TabbedDialogPage, which I guess does the fancy tab stuff.
- Create a new TitledPageApplicationDialog which takes the TabbedDialogPage we created earlier. We override this dialog to provide stuff to do when it closes.
- Show the dialog!
When we're done with a form, we do the following:
- "Commit" the form; I guess this allows it to reconstruct our data from the form fields.
- Store the updated owner in the clinic.
- Signal to the main view that we might have changed something.
Ok, so we've seen how to create a new form, and how to extract the data. Let's try one of our own, and see how far we get.
Having a go at an options dialog
Ok, first things first - create an options command in commands-context.xml and add it to the edit menu. I was going for the tools menu, but I don't need one for this app, so I'll just call the menu option 'Preferences' and be all 'mac-like' :)
I need a data access object (DAO) wired into the "OptionsCommand", as the command has got to load and save the options from somewhere...
Ok, so we create a new command class in OptionsCommand.java and come to writing doExecuteCommand() (see session seven for details on writing commands). First we need the existing options, and then we need to create a FormModel based on these options, as explained above:
As we're not splitting up the options onto different pages (there are only three for this application!) we don't need any of that compound form model stuff. A simple call to FormModelHelper.createFormModel() is all we need.
We'll come to writing OptionsForm in a minute.
After that, we need a new dialog:
This seems straightforward. We create a new TitledPageApplicationDialog, with a FormBackedDialogPage (and therefore our optionsForm) controlling the contents. We override the onFinish() method to 'commit' the form and save the options in the DAO.
Then we show it:
...and that's it for OptionsCommand! Easy, huh?
Hang on, you're probably thinking: what about supplying the text to go into this dialog? This is all controlled by the FormModel we supplied, our own OptionsForm. Let's take a look at how that is implemented:
I've shown the whole class, simply to show how short it is. The createFormControl starts up a new builder, adds a "url" field, a "user" field and a "password" field and then spits out a JComponent (the basic Swing GUI object, for the uninitiated). There's not much to creating these FormModels, is there?
Note that a lot of what I've done here is based on convention. The names of the rows given to the form builder are expected to exist as JavaBean properties in the object you pass to the model. For example, when I do this in OptionsCommand:
...the Options object must contain getUrl()/setUrl(), getUser()/setUser(), and getPassword()/setPassword() methods for this to work. I'm a big fan of convention over configuration, it saves a lot of hassle.
How to we get those aforementioned messages to work? In the constructor show above, you pass a string detailing what you want the form to be called (mine is called "optionsForm", although it could easily be called "allyourbasebelongtous" if desired). Then, you simply add message keys to messages.properties based on that key:
Note that you can use basic HTML for formatting. Lovely.
The actual field names are controlled by further message keys in messages.properties, the names of which are simply the names of the JavaBean properties of our Options class:
(Password already seems to have an internal Spring Rich Client label set up, so I didn't bother adding it)
And... we're done. We're done? Surely not!
No, we really are done. That wasn't at all hard, was it? When I first implemented this, after I got it compiling, it actually worked first time, too, which shows how little code I needed to write to make it work; if there was any more than a tiny amount of code to write, I would have made a mistake :)
Regular Swing types are probably thinking: "That's it? There must be some catch!" No, that really is all the coding you have to do to get a bound form up and running in Spring Rich Client; and there's not a GridBagLayout in sight. This framework is rather good, isn't it? :)
Next time, we'll probably look the validation framework to ensure the user actually types in valid values...
Read moreSpring Richclient ~ Session Seven ~ Beating the Command Framework into Submission
There's been a bit of a gap between the last session and this one; mostly because I've been actually doing coding rather than just writing about coding. It's always worth actually doing a bit of coding once in a while; it's quite fun really, and as I also happen to like eating most days (as does my family), some real work is necessary from time to time...
Anyhow, I apologise for the delay. On with our journey.
We left off last time looking at the ownerManagerView, and seeing how that was put together. I promised at the end or that session to look at the command framework next, and try and work it out. So here goes.
"Scalpal, please"
Let's dissect the OwnerManagerView command code into its component pieces and see what it's made of. It looks like the OwnerManagerView creates a bunch of "command executors", which define what actually happens when a command is executed by the user. There's a lot of different executors in the OwnerManagerView class. Rather than trying to understand them all at once and fry my brain, I think that the best thing to do would be to head through a custom command called "new owner", and try to understand how it's created and what it does.
How does "new owner" work?
Right, time to find the new owner command. It turns out that it's defined in commands-context.xml. This useful little file maps various different command locations (menus, toolbars, that sort of thing) to actual commands that do stuff. Let's look for the new owner command - it's in there:
It's referenced by the "new menu", which is turn is referenced from the toolbar and the file menu. Neat.
So what does the command actually do? It runs the executor "newOwnerWizard". This bean is defined rather simply in the richclient-application-context.xml file:
Ok, so when you click on "new owner" you get a wizard up (this manifests as a little boxes with some blank fields to fill in). You enter a bunch of information, and when this is finished, this bangs an event over to the main owner view using spring's built-in ApplicationEvent stuff:
(code taken from NewOwnerWizard.java)
The owner view handles this message, tweaks the tree and redraws based on the new owner:
(code from OwnerManagerView.java)
Ok, I followed that (I think). Let's try and do a similar thing for the application I'm writing.
The File List View
I have a main pane which shows a list of files in a directory, called FileListView. I want to have a button which when clicked opens a JFileChooser and returns a directory. This then refreshes my view. Easy enough, you might be thinking. It turned out to be rather tricky unfortunately...
Approach One - fire an event to the application context
Ok, so I thought I'd set up my command like this:
This is a tiny bit of java classage that handles the file dialog (OpenFolderCommand.java):
FolderSelectedApplicationEvent is my own application event to tell us that a new folder is selected.
Ok, so in FileListView, I need this:
Done... right?
Wrong, unfortunately. The problem is that event fires in the application context defined by commands-context.xml, not the main application context defined by richclient-application-context.xml, so I can't talk to the window. What do I do? I do what all experienced and knowledgable techs do: panic and turn to google for help...
Approach Two - Move command to main application context
Luckily someone aeons ago (well, 2004) had the same problem and was smart enough to post to the Spring forums about it. The law of WAEFBUG is proved right again!
Ok, so based on that thread, I now know that A) this stuff should be clearer, and B) I can try moving my command to the main application context. This I do, but now it doesn't show the icons on the toolbar button correctly :( Rather than try and work out why that's the case, let's try another approach...
Approach Three - TargetableActionCommand
Let's do what petclinic appears to do and use a TargetableActionCommand. This blog post helped me out setting one of these up.
So here's the latest definition of the command:
This unfortunately didn't work. I can't wire into the fileListView as it's wrapped up inside a view descriptor object:
I don't want to write some sort of proxy object to handle this in the main application context either - that seems overkill.
Approach Four - Cheat (sort of)
Aha - what about this?
Let's just use the method described in "Approach One", except we'll fire the event at the parent context of the current application context, which just happens to be the main context. This might be cheating, but it works, and I've got a deadline looming.
Summary
Whew! Got there in the end. Here's a summary of the way I did this:
- Define a new command bean "openFolderCommand" in the command context.
- Code OpenFolderCommand, extending ActionCommand, and implementing the ApplicationContextAware interface to get the application context. Code it so that it opens the JFileChooser and fires a FolderSelectedApplicationEvent event at the parent context of its own context.
- Code the FolderSelectedApplicationEvent, which takes a File so we know which directory we picked.
- Handle the event in FileListView.
I'm sure there's a better and more Richclient-ish way to do much of this. If you know of one, please let me know!
Next session, I'll probably be looking at either the Master/Detail stuff, or perhaps getting my app onto Java Web Start successfully.
Read moreSpring Richclient ~ Session Six ~ The View
In this session we will look at the ownerManagerView and its associated descriptor. This is the bit which displays the tree view on the "Owner Manager" screen of the Petclinic sample application. Looks like the author got the bean name right :) (What's the hardest problem in computer science? It's naming a class well, so that the next coder can understand what the class does. Believe it).
Ok, let's have a look at the view descriptor and see how one of those is defined.
This is fairly straightforward - a descriptor has a class name to instantiate, and what looks like a list of properties to apply to that class on creation.
The view is created by the descriptor, using the createView() method. This, amongst other things, adds the view to the 'ApplicationEventMulticaster' (defined in richclient-application-context.xml) if the view of type ApplicationListener. I guess this is so the view can get messages from the outside world if necessary. So far, so good.
So what does this view look like: The main window looks like this, with the ownerManagerView defined in the centre:
Let's see how we define each segment of this view.
View Caption
AbstractView contains a bunch of accessor methods (that's a fancy word for a 'get' method, in case you weren't aware) to define things like caption, description, display name etc. These are mostly got from the descriptor class. In this case, our descriptor doesn't appear to define them - perhaps it uses defaults. Let's skip a few steps and cheat: We'll search for the caption text and see what we come up with.
messages.properties: ownerManagerView.label=&Owner Manager
Aha. Our old friend messages.properties. Let's see what happens if we change this.
messages.properties: ownerManagerView.label=&Chris' Owner Manager
Sure enough:
Looks like to define a caption name, it looks like you define the "<viewName>.label" message. And even better, you can define the name in one place and it's changed across the whole application, including in the menus. Neat.
I'm guessing the icon and other text options are defined in the same way.
Laying out the view
The OwnerManagerView derives from AbstractView. Poking through AbstractView, we find the following:
Ok, so that looks like the method that's called when the view needs to be created. How does OwnerManagerView define this?
Ok, we're in standard Java Swing territory here. First we create a shiny new panel, with a simple layout. We create the owner manager tree, a new scroll pane to hold it, and then add the scroll pane to the panel.
The Tree
The createOwnerManagerTree() method is fairly straightforward Java Swing component construction also, except that there are a number of utility classes and interfaces built into Spring Rich Client which help you out. The FocusableTreeCellRenderer class, for example, makes the tree cells work a little more like windows trees (therefore, how people expect them to work, for better or worse). Choosing what to display in each tree cell is standard Swing API legwork, except that Spring Rich Client makes it easy to pick an icon - see this snippet from the custom treeCellRenderer:
That refers to our plumbed in icon source bean, which we looked at in Session Four. Let's be mischievous and change this icon to the spring logo, and see what happens:
owner.bullet=spring-logo.gif
This is what we get:
Hmm - that doesn't look like the spring logo... But hang on, what's that on standard output?
WARNING: Unable to load image resource at 'class path resource [images/spring-logo.gif]';
returning the broken image indicator.
Oops. Looks like I typed the name incorrectly. Let's try:
owner.bullet=spring-logo.png
That's better. It's looks awful, but that's what you get for sticking so large an image on a tree cell. I'm surprised it worked at all, actually :)
Tree Events
How do we handle events generated by this tree? Check out this cool Spring Rich Client utility class, TreeStatusBarUpdater:
When we select a new node, this inline class allows us to easily supply a String to update the status bar with. That's really neat.
Similar utilities exist for the mouse listeners:
All you do is define whether or not to show the popup, and which menu to show if you do. Creating Java's Swing components in Spring Rich Client seems to be a breeze...
Summary
This session, we learned how to create and lay out views in Spring Rich Client. We looked at the view descriptor and how it uses default labels to display captions and icons. We saw how easy it is to create tree components that feel 'right', and we oohed at the really useful utility classes Spring Rich Client provides to lay out controls and handle events.
Next time, let's have a look at all those commands and executors we've seen inside the OwnerManagerView.java, and try to work all that out.
Read more