The Source for Java Technology Collaboration
User: Password:



John Reynolds's Blog

August 2004 Archives


Creating custom components for Echo-Hangman

Posted by johnreynolds on August 26, 2004 at 08:06 AM | Permalink | Comments (0)

In an earlier blog entry I discussed "porting" the hangman game example described in Chapter 2 of Howard M. Lewis Ship's Tapestry in Action from Tapestry to Echo.

hangman image

My initial attempt was fairly straight-forward and relied only on a few of the stock Echo components.

In this installment, I have re-factored my application to utilize two custom Echo components of my own: ScaffoldComponent and LetterChooserComponent.

In the "real world" there would be little reason to develop custom components for a Hangman application. The components that I have implemented are very specific to the game and they lack general applicability to other applications. My motivation is curiosity: There are few better ways to understand a component framework then to develop new components for the framework.

The source code and a deployable war file are available for download. If you have Tomcat 5.0 installed, you should be able to run the application with no problems. You can find links to download my previous efforts from my earlier blog (links to examples using other web component frameworks are also available).

The Hangman Scaffold:

In my initial incarnation of the Hangman game, the "scaffold" area was implemented using a stock Label. Each time the player made a wrong guess, the game would change the image to reflect how close the player was to losing. In the new incarnation of Hangman, the game simply passes the number of wrong guesses to my ScaffoldComponent.

scaffold image

The Hangman Letter Chooser:

The "letter chooser" area of the original game was implemented as twenty six individual Button components arranged in a Grid. When a new game began, each Button was assigned an Image that represented a letter of the alphabet. When a Button was selected, the Image would be changed to an under-bar and the Button would be disabled to insure that a letter could not be selected twice. Additional logic was provided to hide all of the letters at the end of each game. The LetterChooserComponent encapsulates all of this logic in a single custom component. The new version of Echo-Hangman simply instantiates the component and processes letter events as they are returned.

letter chooser image

Writing components for Echo-Hangman:

ScaffoldComponent is "display only", LetterChooserComponent also generates ActionEvents. While not exhaustive, these two examples touch on most of the dependencies between the Echo framework and its components.

All Echo components have two parts, a Java class that extends Component, and a second class that extends ComponentPeer. The two classes are bound together in a file called PeerBindings.properties.

The ComponentPeer class is responsible for generating the HTML and Javascript that is output to the client's browser. If you have ever written a Servlet to output HTML, the ComponentPeer classes will look very familiar.

The Component class is responsible for interacting with the server-side application logic. The application logic is completely unaware that the ComponentPeer exists.

Display Only Components:

For a simple "display only" component such as the ScaffoldComponent, very little code is required. The only real necessity is to invoke the firePropertyChange method whenever any of the component's properties are changed by the application logic. For the ScaffoldComponent, there is only one property that changes, the count of wrong guesses.

When a component's firePropertyChange method is invoked, all of the Listeners that have registered with the component will be notified about the change. Echo relies on the standard java.beans.PropertyChangeSupport class for this functionality. By design, each Component will have a corresponding ComponentPeer that is listening for changes. When a change is made to a Component object, the bound ComponentPeer will detect the change and take the appropriate action. For ScaffoldComponent, the action to take is very simple:


public void propertyChange(PropertyChangeEvent e) 
{
    if (ScaffoldComponent.IMAGE_CHANGED_PROPERTY.equals(e.getPropertyName())) 
    {
        ScaffoldComponent scaffoldComponent = ((ScaffoldComponent) getComponent();
        imageManager.setImage(SCAFFOLD_IMAGE, scaffoldComponent.getCurrentImage());
    }
}

The previous code simply changes the current image. A reference to this image is written to the HTML output when ScaffoldComponent is rendered:


public void render(RenderingContext rc, Element parent) 
{
    Element imageElement = new Element("img", false);
    imageElement.addAttribute("src", 
    imageManager.getImageUri(rc.getConnection(), SCAFFOLD_IMAGE));
    parent.addElement(imageElement);
}

Pretty simple stuff.

Action Producing Components:

Most interesting UI components accept user input in addition to providing visual feedback. Echo provides an event-driven framework to support this. As I have demonstrated in my earlier blog, an ActionListener can be added to any Component:


public GamePane() 
{
    super();
    letter_chooser = new LetterChooserComponent();
    letter_chooser.addActionListener(this);
    add(letter_chooser);
}

This code insures that the GamePane.actionPerformed() method will be invoked whenever the LetterChooserComponent generates an Action. Both LetterChooserComponent and GamePane are instantiated as session scoped objects within the Servlet container. The mechanics of one Java object alerting another through a list of Listeners is well understood by most programmers, but how does an action generated by a user's browser get mapped back to a server-side object? The answer involves Javascript. Recall that ComponentPeer objects are responsible for generating all HTML output. When each new page is rendered, the ComponentPeers add HTML to the page to both display the component and to specify any actions that the component should perform. More specifically, the ComponentPeer will add onclick and onkeypress attributes to the HTML elements that display the component. Here is a snippet from my LetterChooserComponentUI:


public void render(RenderingContext rc, Element parent) 
{
    // Much deleted for brevity
    
    imageElement = new Element("img", false);
    imageElement.addAttribute("src", imageUri );

    // Add onclick event to element to invoke desired script action.
    imageElement.addAttribute("onclick",
       ContentPaneUI.getScriptSetAction("\'" + getId() + "\'", "\'" + letterKey +"\'");

    // Much deleted for brevity
}

Here's the javadoc for ContentPaneUI.getScriptSetAction:


   /**
     * Returns the JavaScript method call necessary to send an action command.
     * This method is called by components to generate script that will cause
     * server interaction.  For instance, a button that generates a link might
     * make its element's "href" attribute:
     * "javascript:E_setAction('4f', 'press')",
     * such that the server would be notified that component '4f' triggered an
     * action with the command 'press'.  On the server, the ComponentPeer with 
     * Id '4f' would have its clientActionPerformed() method called with the 
     * parameter 'press'.
     *
     * @param id The Id of the component setting the action.
     * @param command The action command that is to be sent.
     */

Echo includes the E_setAction function on each page that it renders. This Javascript method posts Http responses to the Echo Servlet, including in each response the unique Id of the component that was invoked. On the server side, the unique Id is used to locate the associated Component instance, and the rest of the process is relatively easy to infer.

Echo actually includes several Javascript functions on each page that it renders. Over a dozen Javascript files are included with the source distribution, and examination of these files reveals an involved client-side Javascript infrastructure. Fortunately, an Echo Component author does not have to deal with the intricacies of this Javascript framework, but it would certainly be a major concern to anyone porting an Echo Component to another framework.

Back on the Servlet side, the major difference between my display only ScaffoldComponent and my action producing LetterChooserComponent is the need to pass on any ActionEvents to the component's listeners. I chose to rely on an existing helper class, DefaultButtonModel, rather then implementing this myself. The guts of DefaultButtonModel.doAction() are as follows:


/**
* Processes a user action.
*/
public void doAction() 
{
    fireActionPerformed(new ActionEvent(this, getActionCommand()));
}

/**
* Notifies all listeners that have registered for this event type.
*
* @param e The ActionEvent to send.
*/
public void fireActionPerformed(ActionEvent e) 
{
    EventListener[] listeners = listenerList.getListeners(ActionListener.class);
    for (int index = 0; index < listeners.length; ++index) 
    {
        ((ActionListener) listeners[index]).actionPerformed(e);
    }
}

In a nutshell, those are the most challenging aspects of creating a custom Echo component. The available infrastructure makes it relatively simple to add new components if you have a solid understanding of the HTML and Javascript that your component must generate.

Continue Reading...



Echo-ing the Tapestry Hangman example

Posted by johnreynolds on August 18, 2004 at 11:29 AM | Permalink | Comments (4)

Hangman: the "Hello World" of web component frameworks?

To get the most out of this article, please download the source and war files. If you have Tomcat 5.0 installed, you should be able to deploy the war and run the application.


Hans Bergsten recently wrote about using Java Server Faces to re-implement the hangman game example described in Chapter 2 of Howard M. Lewis Ship's Tapestry in Action (Manning). Hans used the hangman game to demonstrate how JSF could be improved by implementing a custom ViewHandler instead of relying on JSP pages. It's a good article, and I recommend reading it if you haven't already.


In my experience, one of the best ways to grok the differences between application frameworks is to implement the same example using each framework. Inspired by Hans Bergsten, I set out to convert Ship's example hangman game from Tapestry to Echo. I had a lot of fun doing this, and you can now download the sources to compare hangman implementations in Tapestry, JSF, and Echo.


Echo, Tapestry and JSF are component-based web frameworks. All three are presentation layer frameworks, concerned with generating and handling user interfaces for web applications. Echo and Tapestry are open-source projects, and JSF is a specification with at least one open-source implementation (MyFaces). Of the three, Echo is unique in that it does not require the programmer to know anything about HTML. Writing an Echo application is a lot more like writing a desktop application then writing a web application.


I really like Tapestry, and I have great hopes for JSF, but I've got to admit that Echo makes me smile. I've been writing GUI applications since I helped write Tandy's Deskmate2 in the mid-80's, and Echo is a whole lot simpler for me to grok then HTML and XML infused toolkits. If you are used to writing applications using Swing, Awt, or Tcl, you'll understand Echo.

Echo applications are Java Servlets. I used Tomcat 5.0 to host my application, but any Java Servlet container should work. To create an Echo application, you must create three classes:

  1. A class that extends nextapp.echo.EchoServer (I named mine EchoHangmanServlet)
  2. A class that extends nextapp.echo.EchoInstance (I named mine HangmanInstance)
  3. A class that extends nextapp.echo.ContentPane implements nextapp.echo.ActionListener () (I named mine GamePane)


EchoHangmanServlet is the Java Servlet, HangmanInstance handles a specific session of the application, and GamePane defines all of the Components that make up the application (and handles events generated by the Components).
Echo masks all HTML and Javascript issues from the developer. As far as you are concerned, the Components render themselves and generate events. There is a lot of client-side Javascript under the covers, but you can remain blissfully unaware unless you choose to write your own components. Programs are made up of Content Panes full of Components rather then stateless HTML pages and Javascript. This may be more abstraction then many are comfortable with, but it certainly simplifies the programming model.

Echo “ships” with a number of built-in Components, but I only needed three of them to implement the tapestry application: Grid, Label, and Button. A companion project, EchoPoint, provides dozens of high quality open-source components. If you've ever seen a Javascript component on a web-page, you'll probably find that the EchoPoint project team has ported it to Echo.


The Grid components are used to control Component layout. The Label components provide instructions and feedback, and the Button components are used to enable user feedback. Button and Label both support images, so it was fairly easy for me to reuse the images from Howard Lewis Ship's Tapestry example. I am very grateful for this as it saved me a great deal of time. I also reused Howard's Game and WordSource classes. The “business logic” is identical, only the presentation code has changed.

Echo Component Layout:

Here is the meat of the layout logic in my GamePane:
        
public GamePane(HangmanInstance hangmanInstance, boolean firstGame, String wordToGuess) 
{
    super();
    this.hangmanInstance = hangmanInstance;
    this.wordToGuess = wordToGuess;
    game.start(wordToGuess);

    // Set the background color of the window
    setBackground(new Color(69,103,69));
                
    // Create a grid to position the elements
    layoutGrid = new Grid();
    layoutGrid.setCellMargin(10);
                
    // The title
    titleLabel.setIcon(new HttpImageReference("images/echo-hangman.png"));
    layoutGrid.add(0,0,titleLabel);

    // The number of guesses left
    guessesLeftCountLabel.setIcon(imageMapper.get('5'));
    Grid.Cell cell = new Grid.Cell();
    cell.setVerticalAlignment(EchoConstants.CENTER); 
    cell.setHorizontalAlignment(EchoConstants.RIGHT);
    cell.add(guessesLeftCountLabel);
    layoutGrid.add(1,0,cell);

    cell = new Grid.Cell();
    cell.setVerticalAlignment(EchoConstants.CENTER);
    cell.add(guessesLeftLabel);
    layoutGrid.add(2,0,cell);
                
    // The scaffold (big component, needs to span multiple columns)
    cell = new Grid.Cell();
    cell.setColumnSpan(3);
    cell.add(scaffoldLabel);
    layoutGrid.add(1,1,cell);
                
    // The correct guesses (big component, needs to span multiple columns)
    cell = new Grid.Cell();
    cell.setColumnSpan(3);
    cell.add(createGuessGrid());
    layoutGrid.add(0,2,cell);

    // The letter chooser (big component, needs to span multiple columns)
    cell = new Grid.Cell();
    cell.setColumnSpan(3);
    cell.add(createChooserGrid());
}

/**
 * Create the pushbuttons that allow the user to choose letters
 */
private Component createLetterButtonsGrid() {
  letterGrid = new Grid();
  String alphabet = new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  for (int letterIndex=0; letterIndex<26; letterIndex++)
  {
    int x = letterIndex%9;
    int y = letterIndex/9;
    char letter = alphabet.charAt(letterIndex);
    // Create a reference to the letter image
    //HttpImageReference letterImageRef = new HttpImageReference(letterImages[letterIndex]);
    HttpImageReference letterImageRef = (HttpImageReference)imageMapper.get(letter);
    // Create a button using that image
    Button letterButton = new Button(letterImageRef);
    // Set the action command to the letter the button represents
    letterButton.setActionCommand(""+letter);
    letterButton.setIdentifier("letterButton" + letter);
    // Set this GamePane as the listened for the button
    letterButton.addActionListener(this);
        // Add the button to this GamePane
        letterGrid.add(x,y,letterButton);
  }
  return(letterGrid);
}

This code is pretty simple, I am initializing components, and adding them to my ContentPane. As you can see, I am using Grid components to position the components to match the original tapestry example. The buttons that represent the alphabet are generated by the program and embedded in their own Grid.

Tapestry Component Layout:

For comparison, in Tapestry implementations all component positioning is handled by an HTML page. From Howard's Tapestry example:
<html>
<head> 
<title>Tapestry Hangman</title> 
<link rel="stylesheet" type="text/css" href="css/hangman.css"/>
</head> 
<body jwcid="$content$"> 
<span jwcid="@Border">
<table> 
  <tr> 
    <td><img alt="Tapestry Hangman" src="images/tapestry-hangman.png" 
           width="197" height="50" border="0"/> 
    </td> 
    <td width="70" align="right">
      <img jwcid="@Digit" digit="ognl:visit.game.incorrectGuessesLeft"
         src="images/Chalkboard_3x8.png"/> 
    </td>
    <td> <img alt="Guesses Left"
            src="images/guesses-left.png" width="164" height="11" border="0"/> 
    </td>
  </tr> 
  <tr> 
    <td></td><td></td>
    <td><img jwcid="@Scaffold" digit="ognl:visit.game.incorrectGuessesLeft"
          src="images/scaffold.png" border="0"/> 
    </td>
    </tr> 
</table> 
<br> 
<table> 
  <tr valign="center">
    <td width="160"> 
      <p align="right">
        <img alt="Current Guess" src="images/guess.png"
           align="MIDDLE" width="127" height="20" border="0"/></p> 
    </td> 
    <td><span jwcid="@Spell">
    <!--- Additional letters from the mockup --->
      <img height="36" alt="A"
        src="images/Chalkboard_1x1.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_5x3.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_1x5.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_5x3.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_5x3.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_5x3.png" width="36"
        border="0"/><img height="36" alt="_"
        src="images/Chalkboard_5x1.png" width="36"
        border="0"/></span> 
    </td> 
  </tr>
  <tr> 
    <td valign="top"> 
      <p align="right">
        <img alt="Choose" src="images/choose.png" 
             height="20" width="151" border="0"/>
      </p> 
    </td> 
    <td width="330"><span jwcid="selectLoop">
      <a href="#" jwcid="select"
         class="select-letter"><img jwcid="@Letter"
         letter="ognl:letterForGuessIndex"
         disabled="ognl:letterGuessed" border="0"
         src="images/Chalkboard_5x3.png"/></a></span><span
         jwcid="$remove$">
      <a class="select-letter" href="#"><img height="36" alt="B"
         src="images/Chalkboard_1x2.png" width="36" border="0"/></a> 
      <a class="select-letter" href="#"><img height="36" alt="C"
         src="images/Chalkboard_1x3.png" width="36" border="0"/></a> 
      <a class="select-letter" href="#"><img height="36" alt="D"
         src="images/Chalkboard_1x4.png" width="36" border="0"/></a>
      <img height="36" alt="-" src="images/letter-spacer.png" width="36"
          border="0"/>
<--! MUCH DELETED FOR SAKE OF BREVITY -->
   </td>
  </tr> 
</table></span> 
</body>
</html> 

To be fair, the HTML layout for the Tapestry components can be created by any one of a number of excellent HTML editors. One of the greatest strengths of Tapestry is the complete separation between layout in HTML and functionality in Java. Echo, in contrast, trades the separation of concerns for clear, easy to read, easy to maintain Java code. Programming skills are required to develop layout, but in small projects where a single developer is handling both tasks, this isn't much of a concern.

Echo Event Handling:

Event handling in Echo is straightforward. When a component is created, you can specify the component which should handle any events generated by the component. The event handling logic for my GamePane follows:

        
public void actionPerformed(ActionEvent e) 
{
  if (e.getActionCommand().equals("start game")) 
  {
     startButton.setVisible(false);
     startButton.setEnabled(false);
     chooserGrid.setVisible(true);
     chooserGrid.setEnabled(true);
  } 
  else if (e.getActionCommand().equals("new game")) 
  {
     hangmanInstance.startAnotherGame();
  } 
  else 
  {
     hideLetterButton("letterButton" + e.getActionCommand());
     // Pass the letter to the game
     boolean result = game.makeGuess(e.getActionCommand().charAt(0));
     int incorrectGuessesLeft = game.getIncorrectGuessesLeft();
     int numMistakes = 5-incorrectGuessesLeft;
     if (result==false)
     {
        playAgainButton.setVisible(true);
        playAgainButton.setEnabled(true);
        chooserGrid.setVisible(false);
        chooserGrid.setEnabled(false);
        disableAllLetters();
        if( game.isWin())
        {
          winLoseLabel.setIcon(
            new HttpImageReference("images/you-win.png"));
        }
        else
        {
          winLoseLabel.setIcon(
            new HttpImageReference("images/you-lose.png"));
          numMistakes=6;
        }
      }
      updateScaffold(numMistakes);
      updateWordSoFarLabels();
      guessesLeftCountLabel.setIcon(imageMapper.get(new String( ""
                                     + incorrectGuessesLeft)));
   }
}

This is not the prettiest event handler in the world, but it suffices. Events are returned as Java Strings, and you must perform string comparisons to determine the proper action. For a more complex example, the event handling would be spread across multiple components rather then consolidated in a single component.

Which is better?

I am sure that you know the answer to the question: "Which is better?". As always, the answer is a definite:"It depends".

Tapestry and Echo approach the same problem from different perspectives. If you are experienced in developing user interfaces in code, then you will probably prefer Echo. If you prefer to leave layout to an HTML page designer, then Tapestry will probably be more to your liking. There's no wrong answer here.

What about JSF?

Well, since JSF is the standard blessed by the JCP, then it's going to be an important player. I hope very much that the JSF expert group studies both Tapestry and Echo. The standard should not preclude developers from writing applications in either style.

Javascript Rules!

The not so dirty-little-secret of both Tapestry and Echo is that much of the nifty UI is due to client side Javascript. It would be really great if the Tapestry and EchoPoint commiters would collaborate. I have not delved into details, but surely Tapestry and Echo components could share a lot of Javascript (and hopefully JSF will get in the game too).

In closing.... Echo is just plain fun. I had never heard of Echo before last week, and I downloaded it on Monday. It's Wednesday evening now, and I've been able to successfully hack together a pretty reasonable knock-off of the Hangman application. I've just barely scratched the surface... I could refactor the example as custom Echo components, investigate the EchoPoint HTML layout components, or delve into better understanding the architecture. It's always a delight to come across projects like this.

Obviously I am not in a position to comment on the scalability or resource utilization of Echo, but I can say that it promotes a very clean programming methodology that should be attractive to a large audience of developers.

Check out Echo for yourself!

Update:Check out my blog on creating custom Echo components.

Continue Reading...





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