Woozle Wuzzle
GEF Command creation and debugging tip

My particular GEF application has very complex and coordinated EditPolicy's. For example, on a LayoutEditPolicy's getAddCommand(), I have to dispatch other Requests and accumulate their resulting Commands. To be more concrete: when a child figure (and its associated component (model)) is moved from one figure to another there may be a set of other Requests that get fired off to perform functions such as changing the look of the child based on the new parent and adding and removing other children which provide context.

I following the advice of the GEF guru's and modelled my application after the logic example. Unfortunately, the paradigm presented therein is not suitable for my situation for one primary reason: when "creating" Commands (I put that in quotes only to emphasize the fact that I'm talking about both construction and the calling of the appropriate setters) some of the elements may change at execution time due to other Commands that have been called.

For example:

LogicFlowEditPolicy.createAddCommand(...) {
    AddCommand command = new AddCommand();
    command.setChild((LogicSubpart)child.getModel());
    command.setParent((LogicFlowContainer)getHost().getModel());
    int index = getHost().getChildren().indexOf(after);
    command.setIndex(index);
    return command;
}

In my particular application the parent cannot be set when "creating" the Command since the component may be reparented (by other Commands) between the time that the Command is "created" and added to the CommandStack and the time that it is executed. Only on Command.execute() can the parent be retrieved from the child and be stored. (Note that the parent must be stored on Command.execute() for Command.undo() to function correctly. If Command.undo() was to retrieve the parent when it was called then that would obviously be the incorrect parent.)

Tracking down this situation was non-trival since there are two different factors that must be considered: when / where the Command was "created" and when / where the Command was executed. In my case, the "created" part was the most important. But as most of you know, the various EditPolicy methods are called quite frequently and it's very very very hard to determine which Command "create" is the one that is actually added to the CommandStack and executed. (If you don't already know, the various EditPolicy methods are also called to make sure that you can do something such as when components are selected ComponentEditPolicy.createDeleteCommand(..) is called to ensure that the component is deletable (i.e. Command.canExecute() is called). In this case the Command retrieved from ComponentEditPolicy.createDeleteCommand(..) is never actually exectued. (The Command from ComponentEditPolicy.createDeleteCommand(..) is only exectued when the DeleteAction occurs.) This can be very confusing the first time you stumble across it.)

To get around this problem of knowing which Command is actually executed and where it was "created" from, I used the following trick:

In the constructor of my Command I did the following:

public SomeConcreteCommand() {
    ...
    this.constructionException = new Exception();
}

(where constructionException is declared as private Exception constructionException). Then in execute() I did the following:

public void execute() {
    ...
    constructionException.printStackTrace(...);
    ...
}

What this does is allow me to see the stacktrace of where the Command was "created" only when it is executed.

(I should mention of completeness that this technique for storing an Exception() is very heavy weight and will affect performance. This is only suitable for development / debugging and should not be kept for production code.)

In summary: There may be cases in which some elements of a Command cannot be set when "created". These elements can be retrieved and stored when Command.execute() is called. To facilitate debugging Commands it may be useful to store an Exception created on construction of the Command and display the stacktrace of the Exception when Command.execute() is called.

Integrating Agile Development in the Real World

I had the opportunity to attend Peter Schuh's presentation Integrating Agile Development in the Real World sponsored by Chicago Agile Developers. This was the first prenetation on agile development that I attended where the focus was not on XP and its techniques but (just as the title states) integrating agile development in the real world. Given my years of project management experience I can honestly say that the presentation was spot on. The moment I returned home I ordered his similarly titled book Integrating Agile Development in the Real World. I am anxiously awaiting its arrival.

Big Bang

A few weeks ago I had the privilege to attend a talk by Simon Singh at the Adler Planetarium. It was a engaging and entertaining talk. If you have the opportunity to see one of his presentations, I recommend it.

At the close of the presentation I found out that Dr. Singh was signing copies of his new book Big Bang: The Origin Of The Universe. I figured what the heck and I picked up a copy. If you have the slightest interest in the history of astronomy and cosmology then I high recommend this book. Dr. Singh has a great writing style and the book is a quick read.

Lack of entries

If you notice a lack of entries in my blog, head on over to the Java Information Group's Yahoo! Group. Once some of those discussions level out I will attempt to extract the tasty nuggets and present them here.

A classic story

I was recently reminded of a classic story that I want to make sure that I get out in print so that I don't forget it.

The story takes places in the mid 90's when the internet was just getting off the ground. I had developed an application that used a cron job to periodically check a file for entries and if they were found it would run another application to remove the entries and process them. Periodically the process checking the entries would hang and someone would have to kill them.

The story continues as follows:

   Me:  It's getting too tedious to manually kill all of these hung
        processes.  We're going to need to spend some time getting 
        to the root of problem.
   Manager:  Why burn the time?  Why don't you just create a cron job
             that periodically checks for hung processes and kills 
             them?

   [After much bickering the end result was that another cron job was
   created since it was the fastest path to a "solution".  In time 
   it too would hang while killing the hung processes.]

   Me:  We *really* need to investigate why the file checking process
        keeps haning!
   Manager:  Didn't you set up that cron job to kill off the
             processes?
   Me:  Yes, but it keeps hanging!
   Manager:  [Exhasperated and wondering why he has to explain my 
             job to me]  Just create a cron job to kill off that
             process too!

I wish that I could say that I was kidding but that would be a lie. I also wish that I could say that intelligence in the software community has increased in the past ten years so that these situations no longer occur, but that too would be a lie *grin*.

Intent based computing

A friend of mine told me about some humor that happened in his office this past week. It seems that the SAN admin (aka the SAN man) needed to reformat a disk in the array. The documentation mentioned that formatting a disk is much faster when the array is put in the offline mode. So that's exactly what he did:

  Manager: "What happened to the SAN?!?"
  SAN Admin: "I dunno.  I just brought it offline to format a disk.
              That shouldn't do anything."
  Manager:  "You did WHAT?!?!"
  SAN Admin:  [blank stare]

Did the SAN admin think that bringing it offline with the intent to format a disk was different from bringing it offline with the intent to bring it offline?

I mention this story because it brings up an interesting problem that I run into from time to time. It seems that there are people that do not realize that an application, hardware, etc does not know what their intent is and therefore it cannot adapt to it. For example, if they click on button A (which copies a file) with the intention of deleting something then they wonder why it didn't delete the file. This occurs with them knowing that the button has created copies of files in the past.

This problem is exacerbated by context sensitive functionality. I have found out that there are people that aren't aware of the "context" of an application. Function A leads to state B which causes button C to perform function D. But if function A is not performed first then button C performs function E. (My father is one of these people *grin*.) These people find some applications to be magical and quite confusing.

This is something to keep in mind if you are designing software to be used by the masses.

No more trackbacks

Because I was pounded with trackback spam I have disabled them. If anyone has any insight into curbing trackback spam with MovableType, please let me know.

Creative Commons License Unless otherwise expressly stated, all original material of whatever nature created by Rob Grzywinski and included in this weblog and any related pages, including the weblog's archives, is licensed under a Creative Commons License.