|
|
||
Scott Violet's BlogJanuary 2006 ArchivesArchitecting Applications 2: the Application classPosted by zixle on January 30, 2006 at 03:03 PM | Permalink | Comments (36)This is the second blog in a series on architecting applications. In the first blog I discussed the application I'm going to develop, how it would be architected, and briefly went over the model. In this second article I'll motivate the need for an Application class that is suitable for typical Swing based Apps, as well as the functionality it should provide. Swing is an incredibly rich toolkit, but to date we haven't provided much guidance on how to structure your app. How should I start my app? Where should I place resource bundles in my project? How should I use preferences? Logging? ... The list goes on. While I'm not going to address all of these issues I'm going to focus on the first issue. How should I start my app? The first thing to comes to mind is something along the lines of:
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame(...);
frame.pack();
frame.show();
}
}
Unfortunately this code can be problematic. Swing is not thread safe
and unless otherwise mentioned in the javadoc all methods must be
invoked on the event dispatch thread. As it's name implies, the
main method is invoked on the main thread. The preceeding code
calls into Swing widgets from this thread - the main thread. As
such, it is entirely likely that wierd errors or exceptions can
occur. Why you ask? Invoking various Swing/AWT methods can result
in events getting posted to the event dispatch thread. If the
thread scheduler happens to schedule processing of those events
while the main thread is still invoking methods you'll have two
threads interacting with the same Swing component at the same
time - a definite no-no. Best case, it all works and you're fine,
worst case you get random deadlocks!
We currently recommend an approach along the following lines:
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
But this gets to be a bit painful to remember every time
through. Ideally there would be a class that isolates you from the
various Swing threading vagaries. Furthermore, it would be great
if this class were seamlessly integrated with builders.
Here's how I would like to write main:
public class Main {
public static void main(String[] args) {
new MyApplication().start();
}
}
And MyApplication would have methods intended to be overriden that
are called on the event dispatch thread. This way I don't have to
do the invokeLater everytime, it's handled for me.
Beyond handling proper initialization there are a number of other
features common across Swing apps. In no particular order they
are:
I suspect anyone that's developed a number of Swing apps has already developed a similar class. You can find the version used in this article here, or peruse the doc of Application and ApplicationListener. Application itself is an abstract class, a typical subclass, and the one for this blog series, looks like:
public class PasswordStoreApplication extends Application {
public String getName() {
return "PasswordStore";
}
protected void init() {
JFrame frame = new JFrame(getName());
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
exit();
}
});
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new PasswordStoreApplication().start();
}
}
Notice main, it's now short and sweet. While not obvious, init is
invoked on the event dispatch thread and I needn't worry about
using invokeLater, it's all done for me! Similarly the look and
feel is automatically set to the system look and feel for me from
the preInit method.
Those that look at the code will notice a couple of hard code strings. Bad Scott, bad Scott! I did this to make it easier for folks to download the source and use, but this most definitely needs to come from a resource bundle. You've been warned. I've tried to keep Application simple. It doesn't know about Frame or any other application specific components, that's up to subclasses. In fact it probably makes sense to have a subclass that knows about a single frame. Such a subclass would further reduce the code of PasswordStoreApplication as registering the window listener, packing and all that would be done for you. One of the biggest open questions with a class like Application, is should it do dependency injection? That is, should Application contain meta information about your application that can be used in configuring it. For example, database style apps need to know what database to connect to. Does it make sense to have Application configure your connection settings so that there is a standard place for this meta information? I'm not sure, but I'm certainly interested in feedback from the community. A related issue I mentioned is how this class would work with builders. Ideally one would be able to create a 'Swing Application' which in turn would configure things like the name of the application and perhaps a class to create on the EDT. In this way developers needn't subclass Application directly, the builders would do the wiring for you. More research is needed on this. Hans suggested rather than using listeners you have the ability to register Runnables given a name, with the Runnables processed at well known points in the app life cycle. This would make for a more loosely specified init sequence, but one that allows for a bit more than listeners. I'll explore this in more detail later. In the interest of keeping each blog short and sweet I'm going to wrap this one up here. I had hoped to have something running by this point, but I'll leave that to the next blog. In the next blog I'll create a controller class along with the UI that ties the model together and is started with the Application subclass. I promise that by the end of the next article you'll be able to click on a web start link to see the app running. Architecting Applications 1: the modelPosted by zixle on January 18, 2006 at 04:33 PM | Permalink | Comments (18)While in Russia (see Shannon's blog) I wrote a long blog on undo, actions and various other things. At the time I felt this a bit long for a blog and so it languishes in my unposted blog queue (with many others, but that's a different story). I really liked the idea of writing a series of blogs centered around a real app though, hence this blog and hopefully more to follow. As part of the series I'll cover architecting the app (this blog) including using beans persistence as a way to save the model, Actions, Undo, the need for an application class, data binding and various other things that come up. In writing a series of blogs around an app I thought it important that the app be compelling enough, but not too big such that you become bogged down in all the implementation details. So, I needed something small but not too small as to be trivial. I may have erred on the side of trivial, none-the-less the concepts and points are still worth while to explore. If you're like me you have a ton of passwords for various machines and web sites that you're expected to remember. As an added wrinkle each password should be unique and unreadable, ugh! Added together this makes it nearly impossible to remember all your passwords. For this series I'm going to stick with a simple app that manages passwords. A Java based app that stores passwords has the added bonus of being able to run from a USB memory stick on the three major platforms: Windows, OS X and Linux. I realize there are loads of commercial apps in this space that undoubtedly go way beyond what I'm going to do. None-the-less such an app is at least interesting and not too complicated (at least as far I'm going to take it). Here's roughly where I'll end up when done with the series:
A password store will need to be encrypted, I'm going to leave that out as I don't want to worry about the legal issues of such a blog. There are various approaches to designing apps. Some of the more popular patterns are model-view-presenter , presentation model and model-view-controller. I'm a fan of the latter, and especially for an app of this size. In creating any app you'll need to make the desicision as to which approach is applicable to your design space and requirements. Model-View-Controller dictates that you have a clear model for your application. The Controller coordinates keeping the view and model in sync. The model for Password Store is going to be trivial. It'll consist of a List of PasswordEntrys. Each PasswordEntry will have the following properties: host, user and password. PasswordEntry will be a proper bean in that it'll communicate changes to listeners by way of a PropertyChangeListener notification. That means any time you change a property all PropertyChangeListeners are notified of the change. Here's the code for setting the host property:
public void setHost(String host) {
String oldHost = this.host;
this.host = host;
if (propertySupport != null) {
propertySupport.firePropertyChange("host", oldHost, host);
}
}
The methods for setting the user and password follow the same pattern. This may seem like a lot of boilerplate code for something so trivial, and I would agree. I'll try and address that in a later blog.
The List of PasswordEntrys will be managed by a PasswordModel. PasswordModel will initially expose the List as modifiable. I suspect this is a bad long term decision, but I'm going to stick with it in hopes of keeping the API simple and not getting bogged down in details. Here's the code:
public class PasswordModel {
private List
The biggest downside with choosing List is that it doesn't notify listeners of changes to the contents. This is key, and something I'll touch on later.
An added bonus of making the PasswordEntrys proper beans is that I can save and restore the PasswordEntrys using beans persistence. Here's the code:
public void load(String file) throws IOException {
BufferedInputStream inputStream = new BufferedInputStream(
new FileInputStream(file));
XMLDecoder decoder = new XMLDecoder(inputStream);
List entries = (List)decoder.readObject();
decoder.close();
passwordEntries.clear();
passwordEntries.addAll(entries);
}
public void save(String file) throws IOException {
BufferedOutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(file));
XMLEncoder encoder = new XMLEncoder(outputStream);
encoder.writeObject(passwordEntries);
encoder.close();
}
At this point we have the model, which by itself isn't all that interesting. Next time around by the end of the blog we should have something that you can at least run.
-Scott
| ||
|
|