JSR296 Swing Application Framework
By Adrian Sutton
These are my notes from the talk on the Swing Application Framework (JSR296). This is probably my favorite technology I’ve seen at JavaOne so far. It’s not trying to be overly fancy but it solves a clear need in a very simple way.
Malcolm Davis also commented on it but wasn’t impressed. Personally I think he missed the point. Firstly Malcolm, yes Eclipse and NetBeans platforms have had basically all these features and far, far more for a while and that’s the problem. Using Eclipse or NetBeans as a base for a small or medium sized swing app is total overkill and the frameworks are huge and take ages to learn and configure for your needs. JSR296 is all about defining a really simple framework that gives you just the basics. If you need more you can either add on to it or go the whole hog with Eclipse or NetBeans. Malcolm suggested a few areas he thought it should also cover:
- Help
- Preference Screen
- Update
The thing is, all these three things are really simple to plug in as separate modules. There’s no need to bundle them into every single application because not all apps provide or need help, preferences or automatic updates. Also, Help is already provided (poorly) by JavaHelp and updates are already handled by Java WebStart. So all we really need is a Swing based preferences library (that hopefully looks nothing like the Eclipse preferences which are pretty awful).
Anyway, the notes I took are below.
From the very first release of swing, people have been asking for a framework to make writing Swing applications easier. It’s finally happening.
JSR295 beans binding is also a part of the solution in that it simplifies the wiring together of the application logic and the GUI.
The swing application framework is intended to be a very lightweight framework for small to medium applications. It has no module system, GUI markup, docking etc. It should be as simple as possible and no simpler. The NetBeans and Eclipse frameworks provide these things and may be more suitable for large applications but are too big for small applications.
Key aspects of the swing application framework:
- Life Cycle
- Resource Management
- Actions
- Tasks (for asynchronous work in background threads)
- Session State
The framework is centered around two singletons, Application and ApplicationContext. They function in a similar manner to Applet and AppletContext. To use the framework, you create a subclass of Application and implement the startup method. Then, in the main method, call Application.launch. You use the ApplicationContext class to register tasks, retrieve resources etc. For applications that only use a single frame, the SingleFrameApplication provides some extra functionality like exiting when the frame is closed.
Life Cycle
The Application class defines the lifecycle of the application. You can control aspects of the life cycle by overriding on of the methods in Application: launch, init, startup, ready, exit, shutdown. You can also use exitListeners to veto the shutdown if needed (eg: prompting the user to save). It looks like most of the methods are called on the event dispatch thread but check the JavaDoc to be sure.
Resource Management
Resources are designed with the existing ResourceBundle system. There are some utilities to make the more useful, for example the ability to easily store and retrieve Colors and other resources. Locale specific bundles can be used as normal, but also platform specific bundles.
ResourceMaps are the interface to the resources and are just like a normal Map but include parent chaining. They also support string to type conversion (eg: Color) and the conversions supported are extensible.
The resources go in a resources subpackage. Eg: com.myapp.MyApplication has resources in com.myapp.resources
The framework can also handle resource injection by applying resources to components with a specified name. You can also use the @Resource annotation to have resources injected into a component stored in a class field. The name looked up in the resources is then the same as the name of the variable. So if there’s a button called “myButton” it’s resources would look like:
myButton.text=Foo
Advantages of resource injection:
- Localization happens by default
- No need to explicitly set resources – saves a lot of code.
- All GUI resources are stored in one place instead of scattered throughout the java code.
It’s not meant to be a styles mechanism or general GUI markup just a simple way to specify properties to set.
Note that injecting private fields with the @Resource annotation requires privileges (which it will have once this becomes part of the JRE).
Actions
The actions used are the standard Swing Actions. However they suffer from a few problems:
- Not localized
- Creating them is a pain.
- Asynchronous work is hard.
To solve this, there is a new @Action annotation. You mark a method with it and an action is automatically created that calls the method on actionPerformed. You can retrieve the action with the getAction method on ApplicationContext. For example:
@Action public void doStuff(ActionEvent e) {}
The ActionEvent argument is now optional and there are ways to specify what arguments are passed through to the method. The resource management system then injects the localized resources into the action based on it’s name.
You can also bind the action’s enabled state to a variable:
@Action(enabledProperty = "changesPending") public void doStuff() {}
the action will then enable and disable whenever the changesPending variable is changed (through beans binding).
Actions have problems when used in toolbar buttons and menu items – the menu items need text, the toolbars shouldn’t have it. You can achieve this by overriding the text for the action in the toolbar button by specifying toolbarButtonName.text=${null}
Tasks
Tasks are a simple mechanism to avoid blocking the event dispatch thread by spawning background threads. When using these background threads though, you need some way to manage them – display progress, start, stop and send messages.
SwingWorker already does most of this but Tasks takes it a step further.
Tasks use the Java concurrency library so it’s Java 1.5 only (probably possible to backport to 1.4)
The first enhancement to SwingWorker is the addition of multiple “done” type methods that handle different ways that the task completed – cancelled, error or successful completion. This is error prone with SwingWorker.
Tasks also provide a means to block the GUI without freezing it by displaying a modal progress dialog. Use the block and unblock methods to use it. There are also a series of properties to control the scope of blocking.
For managing the tasks, there is a TaskService which can be configured to execute tasks serially, in parallel with thread pools etc. It’s possible to create multiple task service instances for different types of operations (network, database etc). You can specify this via the Action annotation:
@Action(taskService="database")
The ApplicationContext provides access to the instances of TaskServices.
Finally, there’s a TaskMonitor class that provides a summary of the running tasks which is good for status bar implementations.
Session State
Very simple. It stores which windows are where on screen and JTable column sizings so they can be restored to those locations when the application is restarted. Most apps don’t manage this themselves because they either don’t know where to store the state, are unsigned and can’t store it or they don’t have a way to safely restore the state. The storage is available via the ApplicationContext.getSessionStorage method.