The Source for Java Technology Collaboration
User: Password:



Scott Violet

Scott Violet's Blog

Architecting Applications 3: the Controller

Posted by zixle on March 06, 2006 at 04:24 PM | Comments (16)

This is the third 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 the second blog I motivated the need for an Application class that is suitable for typical Swing based Apps, as well as the functionality it should provide. In this third installment I'll go over the role of the controller as used in the MVC architecture. As promised, this blog has a runnable demo.

I was strongly tempted to redo the model a bit for this installment of the application architecture series, but I had to hold back lest there be no demo for this article. So, without further ado, here's the demo.


Password Store Demo Screenshot

The demo is far from complete. In particular most of the menus are not wired up and are disabled. The only menu items that work is creating a new account (control-n). I'll flesh out the implementation of the remaining menu items in future articles. Complete source code is available at the end of the article.

To keep the app from being signed, but be able to store information between sessions I'm using the persistence service provided by webstart. The persistence service allows an app to save state in a secure way, similar to cookies. If you run the app from the command line it'll save the file into your home directory.

Controller

As early explained this app is built using the model-view-controller pattern. As the name implies the pattern dictates you design three distinct areas: the model, for modelling your data; the view, for the graphical view the user will interact with; lastly, the controller which orchestrates the two, keeping them in sync. For the purposes of this demo the Controller will perform the following operations:
  1. Create and assemble the view.
  2. Wire the view to the model or controller as appropriate.
  3. Keep the view and model in sync.
A Controller need not create and assemble the view. Undoubtedly the view is intimately tied to the controller, and in may apps, such as this one, making the controller create the UI fits naturally. Ideally the view would be loaded from a GUI builder and the controller could extract the components it's interested in, but that's for a separate blog.

Creating the UI

Creating and assembling the view is pretty much route Swing programming. As earlier mentioned I'm not going to have any external dependencies, so I wrote this code by hand using GridBagLayout. I honestly don't recommend hand coding UI creation or layout, it's painful, error prone, and a maintenance burden. Use a builder. Of course I recommend Matisse and once done will will publish a followup blog that's written in terms of Matisse.

JList Wiring

The model consists of a List of PasswordEntrys. This will be rendered as a JList. JList itself requires a model which implements the ListModel interface. To display the List of PasswordEntrys a custom ListModel implementation will be used that talks to the List of PasswordEntrys from the model. Here's the code.
  private PasswordModel model;
  private class PasswordListModel extends AbstractListModel {
    public int getSize() {
      return model.getPasswordEntries().size();
    }
    public Object getElementAt(int index) {
      return model.getPasswordEntries().get(index);
    }
  }
Notice the ListModel returns a PasswordEntry for each of the elements. JList's default cell renderer will use toString on this, which isn't all that helpful here. As such we'll need a custom ListCellRenderer. I'm not going to go over the code for the CellRenderer, it's pretty simple, if you're curious look to the source.

Synchronization

Keeping the model and view in sync is one of the more tedious aspects of an application of this sort. To reinforce that, consider what happens when the user types in a text field. As the user types in a text field the model must be updated. As I wanted the model to be updated immediately I'm using a DocumentListener. DocumentListeners are notified immediately of any changes to a text component. Here's the code.
  // Install a DocumentListener on the host text field
  hostTF.addDocumentListener(new DocumentHandler());

  private class DocumentHandler implements DocumentListener {
    public void insertUpdate(DocumentEvent e) {
      edited(e);
    }

    public void removeUpdate(DocumentEvent e) {
      edited(e);
    }

    public void changedUpdate(DocumentEvent e) {
      // TextFields can ignore this one.
    }

    private void edited(DocumentEvent e) {
      // The host text field was edited, update the model
      hostChanged();
    }
  }

  // A valid edit has occured, update the model.
  private void hostChanged() {
    getSelectedEntry().setHost(hostTF.getText());
  }
The real source code is slightly different in so far as I've coalesced all the listeners into a single class, which leads to a switch like statement in the edited method. None-the-less the core idea is the same. As the document is edited the model is immediately updated.

The Controller must also track changes made to the model by other parts of the app. For example, an Action might directly update the model. To track changes to the model the Controller installs a PropertyChangeListener on each PasswordEntry. Here's the code.

  // Install a listener on each entry
  entry.addPropertyChangeListener(new PropertyChangeHandler());

  private class PropertyChangeHandler implements PropertyChangeListener {
    public void propertyChange(PropertyChangeEvent e) {
      entryChanged((PasswordEntry)e.getSource(), e.getPropertyName());
    }
  }

  private void entryChanged(PasswordEntry passwordEntry,
          String propertyChanged) {
    // Notify the list of the change
    listModel.fireContentsChanged(model.getPasswordEntries().
              indexOf(passwordEntry));

    // If the selected entry was changed, update the appropriate text field
    if (getSelectedEntry() == passwordEntry) {
      if (propertyChanged == "host") {
        hostTF.setText(passwordEntry.getHost());
      } else ...
    }
  }
A PropertyChangeListener is attached to every entry in the model. When an entry is changed, ListModel notification is dispatched (listModel.fireContentsChanged), which triggers the JList to update itself. Similarly if the contents of the selected entry have changed the appropriate text field is updated.

The following graphically depicts changes originating from the text field.

The following depicts changes originating from the model.

What's wrong with the previous code? Turns out there is a subtle bug. When the text field changes, we update the model, which sends notification, which triggers a reset of the text field. JTextField (actually javax.swing.text.Document) does not allow a listener to modify the contents, so that this code triggers Swing to thrown an exception. Here's a graphical depiction of this.

Notice the textfield on both sides.

This is happening because the Controller doesn't realize that it initiated the change to the model and doesn't need to update the display. There are a couple of schemes for dealing with this. You can disable and reenable listeners, check the contents of the text fields, or keep a property around. I've settled for keeping a property around that tracks if the Controller initiated a change to the model.

Here's the revised code for the edited method that is invoked when the text is edited.

  private void hostChanged() {
    if (!changingHost) {
      changingHost = true;
      getSelectedEntry().setHost(hostTF.getText());
      changingHost = false;
    }
  }
Now the hostChanged method knows who originated the code. If changingHost is true it indicates the change was initiated by the Controller and the Controller need not do anything.

The entryChanged method is similarly updated.

  private void entryChanged(PasswordEntry passwordEntry,
          String propertyChanged) {
    // Notify the list of the change
    listModel.entryChanged(model.getPasswordEntries().
              indexOf(passwordEntry));

    if (getSelectedEntry() == passwordEntry) {
      if (propertyChanged == "host" && changingHost) {
        changingHost = true;
        hostTF.setText(passwordEntry.getHost());
        changingHost = false;
      } else ...
    }
  }
This is most definitely one of the more tedious aspects of GUI programming. One forgotten !changingXXX or changingXXX=true, and you'll either get an exception, or you'll stop updating the model or UI. Ugh!

The last thing worth mentioning is that as List has no notification the Controller can not listen to it. Instead all mutations to the list itself are done in Controller which will update the ListModel appropriately. I will revist this later on.

Source

I've broken the source into two chunks: fabric.zip contains the generally useful stuff, and passwordStore.zip contains the source specific to PasswordStore.

Summary

So, where are we? I've gone through the model, I've touched on the Application class, and went through the bulk of the controller. At this point the app is usable, but by no means a complete app. Next time around I'll either go over undo, or cut/copy/paste. Which would you prefer?

    -Scott


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Both :) ....I guess if I had to pick, how about "Undo".

    Also, you suggest using a GUI builder and not hand coding things. I haven't really tried Matisse...but my problem with previous GUI builders is the code that they create...and then I have to label things, and put in my listeners, etc. Thoughts on this?

    Posted by: codecraig on March 07, 2006 at 05:11 AM


  • Hmm, trying to run the PasswordStoreApplication throws a MissingResourceException ... anything I overlooked?

    Thanks,
    Jeanette

    Posted by: kleopatra on March 07, 2006 at 06:26 AM

  • codecraig,

    but my problem with previous GUI builders is the code that they create...and then I have to label things, and put in my listeners, etc.

    Matisse allows you to change the name of the field for a component, as well as whether or not a field should be generated. It has a bit of listener generation code as well, but I haven't used that aspect much.

    This is an interesting problem. If instead of code we generated XML would you feel the need to change it too?

        -Scott

    Posted by: zixle on March 07, 2006 at 06:39 AM

  • kleopatra,

    Hmm, trying to run the PasswordStoreApplication throws a MissingResourceException ... anything I overlooked?

    D'OH! As you have found, when I zipped up the entries I didn't include the properties file. I recreated the zip file with the properties file. If you download it again it should all be good.

    Thanks!

       -Scott

    Posted by: zixle on March 07, 2006 at 06:40 AM

  • Scott,
    XML would possibly be better, guess I'd really have to see it to know. However, once other issue I find is when the GUI builders use layout the components, their layout code is always one big block of code that is hard to decipher when first viewing it. I tend to write code such that different areas are created using different methods, say, "buildInputPanel" ...which builds and returns a JPanel, "buildButtonsPanel", etc, etc. GUI builders tend to have one method, say "buildGUI"...which does everything in one place and mashed together.

    -Craig
    http://www.codecraig.com (FYI, working on converting old blog into new blog)

    Posted by: codecraig on March 07, 2006 at 12:14 PM

  • codecraig,

    However, once other issue I find is when the GUI builders use layout the components, their layout code is always one big block of code that is hard to decipher when first viewing it. I tend to write code such that different areas are created using different methods, say, "buildInputPanel" ...which builds and returns a JPanel, "buildButtonsPanel", etc, etc

    I have found that it doesn't really matter how you structure or format layout code, in the end it's only parsable by the person that created it and most likely only they can grok it for a while after it's been created. Layout is such an inherently visual process that to use anything but a builder is painful in the long run.

    We're developers and so we naturally want to look at the code and understand it. It's just who we are. That's one of the reasons for going to XML. If we were to go to XML I suspect people would be less inclined to want to understand or change the XML, and instead use a visual tool.

    Anyway, this is a charged topic and I'm sure we all have differing views on this.

        -Scott

    Posted by: zixle on March 07, 2006 at 03:24 PM

  • y u sun guys r so dumb in real-world applications ? i know i m being harsh , but i m really annoyed tht senior sun architect like you can only imagine 2 give this toy-like application in "Architecting Applications series". Please 4 god shake, give example of some big application.

    Posted by: hithacker on March 17, 2006 at 10:26 PM

  • hithacker,

    Please 4 god shake, give example of some big application.

    At some point I will cover a bigger app. I went with a small app for this series as it conveys the main points I wanted to make, without getting hung up in the specifics of a bigger app. A blog, by it's nature, is typically small. To do a big app justice begs for a lot longer article, something I don't have time for now:(

       -Scott

    Posted by: zixle on March 20, 2006 at 08:54 AM

  • scott, 1st of all i m feeling guilty for doing bad comment w/o knowing reason.( i forgot that u r busy in mustang ).

    What i want to say is that small application may show point what you want to prove but it don't show complexity which arise with using MVC in professional application. So i think you can show better with big application.

    One suggestion is that SUN can appoint one application developer so he can do this kind of work,so u don't need to do everything.

    Posted by: hithacker on March 21, 2006 at 08:20 AM

  • Scott,
    I agree with hithacker. I work for a large telco doing 3 dozen swing apps. I get irritated when i see swing articles about trivial "helloworld" style apps.

    We had tons of these articles in the past. The real need is for more advanced frameworks which wire all the pieces for a decent swing application together.

    You need to start developing a fullblown application with
    MenuBar, Tools, Status bar, Tasks along with a tabbed view like you see in Netbeans.

    Apps should have
    a) Thread management (to hande EDT issues with swing)
    b) Actions handling (load via XML, plugin to undo mgr)
    c) Object registration and lookup svc (like spring framework )
    d) UI state and navigation mgmt ( Busy windows, transition effects
    (kinda like what Romain has)
    e) Printing capabilities ( a decent print preview window with pdf /gif export for tables etc.)
    f) End user preference management ( UI to plugin to preference API)
    g) Loadable plugins ( to enhance application)
    h) A decent databinding api to wire tables/trees to jdbc result sets or EJB object collections.


    This is the industry requirement today.

    There is a geniune requirement to have a MVC application framework which wires together all these entities.

    Right now, swing is a widget framework with a million pieces.

    It take a lot of work to develop a UI which looks like a Netbeans application. You have thousands of engineers re-inventing the wheel.
    Hope you can consider some of my comments.

    Posted by: srema on March 30, 2006 at 12:20 PM

  • srema,

    Thanks for your comments. I completely agree on the need for all the pieces you're identified. I can only cover so much in this blog, and doing a full app, as you have outlined, is beyond the scope of what I have time for currently.

    We (Swing and NetBeans teams) are all to aware of the amount of work required to write a 'typical' application. Thankfully there are some upcoming projects that will address these issues. Luck for announcements soon!

        -Scott

    Posted by: zixle on March 30, 2006 at 01:13 PM

  • I agree with hithacker. I work for a large telco doing 3 dozen swing apps. I get irritated when i see swing articles about trivial "helloworld" style apps. We had tons of these articles in the past. The real need is for more advanced frameworks which wire all the pieces for a decent swing application together. You need to start developing a fullblown application with MenuBar, Tools, Status bar, Tasks along with a tabbed view like you see in Netbeans. Apps should have a) Thread management (to hande EDT issues with swing) b) Actions handling (load via XML, plugin to undo mgr) c) Object registration and lookup svc (like spring framework ) d) UI state and navigation mgmt ( Busy windows, transition effects (kinda like what Romain has) e) Printing capabilities ( a decent print preview window with pdf /gif export for tables etc.) f) End user preference management ( UI to plugin to preference API) g) Loadable plugins ( to enhance application) h) A decent databinding api to wire tables/trees to jdbc result sets or EJB object collections. This is the industry requirement today. There is a geniune requirement to have a MVC application framework which wires together all these entities. Right now, swing is a widget framework with a million pieces. It take a lot of work to develop a UI which looks like a Netbeans application. You have thousands of engineers re-inventing the wheel. Hope you can consider some of my comments.

    Posted by: sallee on October 11, 2006 at 02:15 AM

  • sallee,

    JSR 295, Swing Application Framework, will tackle many of the points you raise.

       -Scott

    Posted by: zixle on October 11, 2006 at 07:31 AM

  • Did you mean JSR 296 ?

    Posted by: louis_gabriel on December 21, 2006 at 01:44 PM

  • Indeed I meant JSR 296, sorry about that.

        -Scott

    Posted by: zixle on December 22, 2006 at 09:11 AM

  • The comment is too lately. But I still thanks for your article. It's give me more information. cnlogistic

    Posted by: winrelocation on August 09, 2007 at 01:34 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds