Skip to main content

Creating custom components for Echo-Hangman

Posted by johnreynolds on August 26, 2004 at 8:06 AM PDT

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


style="font-weight:normal;color:#008000">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",

style="font-weight:normal;color:#008000">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


style="font-weight:normal;color:#008000">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:

[prettifystyle="font-weight:normal;color:#008000"] public void propertyChange(PropertyChangeEvent e) {     if (ScaffoldComponent.IMAGE_CHANGED_PROPERTY.equals(e.getPropertyName()))     {         ScaffoldComponent scaffoldComponent = ((ScaffoldComponent) getComponent();         imageManager.setImage(SCAFFOLD_IMAGE, scaffoldComponent.getCurrentImage());     } } [/prettify]

The previous code simply changes the current image. A reference to this image is written to the HTML

output when ScaffoldComponent is rendered:

[prettifystyle="font-weight:normal;color:#008000"] 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); } [/prettify]

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:

[prettifystyle="font-weight:normal;color:#008000"] public GamePane() {     super();     letter_chooser = new <code style="font-weight:normal;color:#008000">LetterChooserComponent(); letter_chooser.addActionListener(this); add(letter_chooser); } [/prettify]

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


style="font-weight:normal;color:#008000">LetterChooserComponent
UI:

[prettifystyle="font-weight:normal;color:#008000"] 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 } [/prettify]

Here's the javadoc for ContentPaneUI.getScriptSetAction:

[prettifystyle="font-weight:normal;color:#008000"]    /**      * 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 &quot;href&quot; attribute:      * &quot;javascript:E_setAction('4f', 'press')&quot;,      * such that the server would be notified that component '4f' triggered an      * action with the command 'press'.  On the server, the <code style="font-weight:normal;color:#008000">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. */ [/prettify]

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


style="font-weight:normal;color:#008000">ScaffoldComponent
and my action producing

style="font-weight:normal;color:#008000">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:

[prettifystyle="font-weight:normal;color:#008000"] /** * 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 <code style="font-weight:normal;color:#008000">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); } } [/prettify]

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.

Update:

Echo2 has added support for AJaX enabled components.

Related Topics >>