JSR296 Swing Application Framework

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.

14 Responses to “JSR296 Swing Application Framework”

  1. David Venz Says:

    Adrian,

    Just wanted to say thanks for the 296 overview - I hadn’t got around to reading the JSR yet. Some of it is rather MIDP-like.

    Good luck in the new role.

    -Dave.


  2. Adrian Sutton Says:

    Thanks Dave, glad it helped. I’ve played around with the framework a little bit now and it looks promising - hopefully I’ll find time to do something moer serious with it and see how it copes.


  3. Ricky Clarkson Says:

    I wonder why Application is intended to be subclassed. Frameworks these days don’t tend to encourage inheritance, and for good reasons.


  4. Ricky Clarkson Says:

    By the way, the Atom feed link on this page points to the feed for another post.


  5. Anonymous Says:

    Hi Ricky,
    Extending from Application makes logical sense - your application is the Application and it extends the functionality of the startup and quit immediately application that you get by default. It also means you have much simpler access to the methods exposed by Application instead of having to delegate to another class all the time. Basically, I can’t imagine a clean way to it that doesn’t use sublcassing.

    Also, thanks for pointing out the atom link was broken, it’s fixed now.


  6. Ricky Clarkson Says:

    A clean way would be for Application to be an interface, and to provide those methods via some other object. Another clean way would be for Application to be a final class, and to provide implementations as callbacks.

    Subclassing tends to lead to big complex classes, partially because of the convenience of accessing the superclass methods - you pay a price if you split your work into more than one class.

    Also, it seems like it might be hard to automatically test an Application subclass, as you’re depending on whatever behaviour is inherited from above.

    In the same way, you cannot test a JFrame subclass in headless mode, because instantiating a JFrame requires a GUI (even if you don’t display it).


  7. Adrian Sutton Says:

    Any solution other than subclassing would require the implementation to provide every single method in the life cycle regardless of whether or not it will be used. Since the entire point of the framework is to remove boiler plate code, callbacks or an interface would not be a clean solution.

    As for automatically testing, if you’re testing a Swing app in headless mode, you’re not testing your swing app. If you’re separating your business logic from your UI and testing the business model in headless mode, then you shouldn’t have any business logic for either classes that work with JFrames or classes that extend Application. The swing app framework and the Application class is all about the life cycle of your applications UI, business logic should not be mixed in there.


  8. Ricky Clarkson Says:

    Au contraire, for the subclass is not the only useful tool in the toolbox. There are ways of doing this without boilerplate - the callback is one such way, in that you only need to provide callbacks for those methods you’re interested in.

    I perform as much separation of ‘business logic’ from my UI as I like. Typically the more unwieldy I find the combination, the more I separate things. Are you saying that one should not wish to automatically test a JFrame subclass, nor an Application subclass?


  9. Adrian Sutton Says:

    The callback would have to have an interface to call back into which would either require you to implement all the methods or have a separate interface for every method - neither is a very clean solution. Java doesn’t use duck typing.

    As for testing, my point was that you can’t test a Swing app in headless mode so there’s no point in trying to design frameworks that let you use headless mode for your tests. If your tests run headless, you’re not testing your swing code properly.


  10. Ricky Clarkson Says:

    There’s nothing conceptually unclean about having a separate interface for each method (see lambda calculus) - and in fact usually the interfaces will all be the same, so they can be one. Runnable works for the basic cases.

    With closures in Java 7, this becomes more syntactically attractive.


  11. Adrian Sutton Says:

    Having to register for every single event you want is unclean in and of itself. The prototype their working on runs on Java 1.5 and will be useful prior to 1.7 - there’s not point in tying this to a JVM that won’t be out for at least another year and attempting to use a JSR (closures) that is changing a great deal or it just makes everything harder. Besides, you don’t want to use closures to hold the kind of code you’d write in the app framework callback methods and it’s annoying to have to register a closure as a listener and go and create a method as well. Much easier to just override a method and be done.


  12. Pure Danger Tech » Blog Archive » Java 7 Roundup (May 21st) Says:

    [...] Sutton posted notes from the JSR 296 Swing Application Framework session at JavaOne. Malcolm Davis posted some requests [...]


  13. Steven Says:

    You remark that ‘updates are covered by webstart’

    We use Webstart for a moderately complex application - and we’re dumping it as fast as we can for the next release.

    It is slow, unreliable, and remarkably intrusive!


  14. Thorn Green Says:

    I have an alternate framework that the JSR-296 people may want to look at. See:

    http://verdantium.blogspot.com/


Leave a Reply

Alternatively, subscribe to the Atom feed.