Article

Step-by-Step Jakarta Tapestry

Page: 1 2 3 4 Next

Now let's edit our Home.page file and add to it the component specifications we've just discussed. But writing XML by hand is boring and error-prone, so we're going to rely on Spindle's help here.

Place your cursor in the line below the <description> tag and press Ctrl + Space. In the Code Assist window that opens, select component (see Fig. 11).

1508_figure11
Fig. 11. Writing component specification with the help of Code Assist

Press Enter and you'll get a stub of a component specification:

<component id="value"></component>

Notice that value is already selected for you, so you can enter something to replace it immediately. Enter loginForm. Press Tab, and the cursor will jump to the next place of entry. This feature may not work if you're constantly switching between your browser and your Eclipse window, so you might want to print this tutorial or arrange your workspace (or, if possible, memorize these few steps) so you can perform this procedure in one go. Press Ctrl + Space again, select type from the list and press Enter. This is how your new specification should look:

<component id="loginForm" type="value"></component>

The word value is selected again. Once again, press Ctrl + Space and type the letter 'f'. You'll see the names of the components that start with F. Choose Form and press Enter. This is what you'll see now:

<component id="loginForm" type="Form"></component>

Press Tab again to jump to the next entry point, right between the <component ...> and </component> tags, and -- you guessed it -- press Ctrl + Space. Choose binding (empty) from the list and press Enter. At this stage, your specification will look like this (I pressed Enter another couple of times to format the code nicely; you might want to do the same):

<component id="loginForm" type="Form">  
 <binding name="value"/>  
</component>

With the word value selected, press Ctrl + Space. Choose listener, press Enter, then Tab and Ctrl + Space again. The only choice in the list will be expression, so choose it by pressing Enter. Finally, enter listeners.onFormSubmit instead of value. That's it! We've completed the specification for our first component.

After you become accustomed to using Code Assist, you'll see how easy it is to write component specifications.

Now go on and have some practice. Create specifications for the other two components, and check back here when you're done. The final result of your numerous Ctrl + Space keystrokes should look like this:

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE page-specification PUBLIC  
 "-//Apache Software Foundation//Tapestry Specification 3.0//EN"  
 "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">  
<!-- generated by Spindle, http://spindle.sourceforge.net -->  
 
<page-specification class="org.apache.tapestry.html.BasePage">  
 
 <description>Login page</description>  
     
 <component id="loginForm" type="Form">  
   <binding name="listener"  
       expression="listeners.onFormSubmit"/>      
 </component>  
 <component id="uname" type="TextField">  
   <binding name="value" expression="uname"/>      
 </component>  
 <component id="password" type="TextField">  
   <binding name="value" expression="password"/>  
   <static-binding name="hidden" value="true"/>      
 </component>  
     
</page-specification>

We've completed the page specification for our Home page, and the Spindle syntax checker should be completely happy with what we have written (i.e. there should be no wavy red lines either in Home.html, nor in Home.page). However, the page won't work yet. Remember, we promised Tapestry that there would be an onFormSubmit listener and two properties named uname and password in the page class.

Right now, this page uses the default BasePage class, and it doesn't have any custom methods or properties. To implement our custom logic, we should create our own Java class that will inherit from BasePage.

Creating the Page Class

Right-click (or Ctrl-click on a Mac) in your Package Explorer and choose New > Other..., then, in the Select a wizard dialog, choose Class (see Fig. 12). Press Next.

1508_figure12
Figure 12. Selecting a Class wizard

In the Java Class dialog enter a package name, for example com.sitepoint.tapestry.login. Enter Home for the name of the new class, and org.apache.tapestry.html.BasePage for the Superclass. Make sure the public static void main(String[] args) checkbox is deselected -- we're not going to run this class as a standalone program. You might find it useful to add automatically generated comments, so select the Generate Comments checkbox. The final result should look like Fig. 13.

1508_figure13
Figure 13. A new class is ready to be created

Press Finish, and a brand new page class will be generated for you in a few seconds. It will be named Home.java and its code will look like this:

/**  
*    
*/  
package com.sitepoint.tapestry.login;  
 
import org.apache.tapestry.html.BasePage;  
 
/**  
* @author alex  
*  
*/  
public class Home extends BasePage {  
 
}

This is where we define our listener method. Specify it like this:

public class Home extends BasePage {  
 
 public void onFormSubmit(IRequestCycle cycle) {  
   // Some code will go here  
 }  
}

At this stage, we'll create only the skeleton of the method -- the rest of the code will be added later. The mysterious IRequestCycle parameter can be thought of as Tapestry's representative in our code. Whenever we need something from the framework, we'll ask this cycle to get it for us. We'll see an example of this soon.

However, at the moment Eclipse tells us that it doesn't know what an IRequestCycle is. To make it happy, we'll need to add the following import statement:

import org.apache.tapestry.IRequestCycle;

Now that we have a custom page class, we need to specify it in our page specification. Change the opening <page-specification> tag in Home.page to look like this:

<page-specification class="com.sitepoint.tapestry.login.Home">

Specifying Properties the Tapestry Way

Now is the time to deal with the two properties uname and password. There are two ways to specify a property in Tapestry: the standard Java way (a private class member, public getter and setter, and perhaps some other maintenance code), and the Tapestry way. The latter, although it looks slightly unusual, is used much more often, so we'll use the Tapestry approach here.

We don't want to write all that property-related code by hand, so we'll just ask Tapestry to generate it for us automatically at run time. All we need to do is to describe how the properties should be named, and what type they should be (any Java class available to Tapestry can suffice as the type).

You've probably already guessed that we'll describe the properties we want in our page specification. In Home.page class, just below the <description> tag, add the following two lines (of course, you don't have to write them all out by hand -- use your Code Assist Ctrl + Space skills):

<property-specification name="uname" type="java.lang.String"/>  
<property-specification name="password" type="java.lang.String"/>

That's it! Now you can just assume that at run time these properties will be ready for you, and they'll be managed by Tapestry properly without any efforts from your side and without any code written by you.

Previously, before we even specified these properties, we had already connected them to two of our components: a TextField component with the ID uname is bound to the property uname, and a TextField component with the ID password is bound to the password property. Let me describe all the activities that Tapestry will perform based on our page specification:

  1. When the Home page is requested, Tapestry will create the page class for it (an instance of com.sitepoint.tapestry.login.Home, created by us). It will then notice that we asked to add two properties to this page, so Tapestry will create an 'enhanced' version of our class (i.e. another class that inherits from Home), and will add to it the two requested properties along with all the code that might be needed to handle them.
  2. Tapestry then asks the enhanced page class instance to render itself to the user. To do that, the page class instance will use our Home.html template. When rendering the uname and password components, the class instance will check whether these components have any default values. We didn't specify any, so the components will just be shown as blank text fields.
  3. When the login form is submitted, Tapestry (using the same kind of page class instance, either created anew or taken from the pool) will take the values entered by the user into the two text fields, and put them into their corresponding properties, so that we can use them in our code.

It took three paragraphs for me to describe what Tapestry will do automatically for us as a result of just a few lines in the page specification. You don't really need to know all those procedures when you're taking your first steps with Tapestry. All you need to know is that you can specify the properties you need, connect them to the components you use on your page and, as a result, whatever is submitted by a user will be available in your code via these properties.

The only nuance is that to use or edit a value of this kind of property in your code, you should create a getter or a setter (or both, if necessary) in your page class. Admittedly, this is a tad unusual: you didn't create any properties in your Java code, and you certainly didn't write anything like this:

private String uname;  
private String password;

Yet we're asking Tapestry to provide them at run time, so we'll just believe that the properties will be there. All this is a little bit... abstract. And yes, we are going to create abstract getters for these shadowy properties. Here they are:

public abstract String getUname();  
public abstract String getPassword();

We'll add these getters to our page class and, since it will have abstract methods, this class will also become abstract! This is how it will look (with comments removed for the sake of brevity):

package com.sitepoint.tapestry.login;  
 
import org.apache.tapestry.html.BasePage;  
import org.apache.tapestry.IRequestCycle;  
 
public abstract class Home extends BasePage {  
 public void onFormSubmit(IRequestCycle cycle) {  
   // Some code using getUname() and getPassword will go here  
 }  
   
 public abstract String getUname();  
 public abstract String getPassword();  
}

Using abstract classes as a "final product" in your application looks somewhat strange, but as soon as you get used to it, you'll realize that this is actually very handy: you write exactly what you need, and Tapestry takes over and cleverly creates all the "plumbing" for you.

By the way, you can see this approach not only in Tapestry, but also in EJBs with container-managed persistence.

Right, we've written enough to test our first Tapestry page. It won't do anything visible at this point, but at least we can check whether Tapestry understands our code properly. If everything goes as planned, we'll see a web page that looks identical to our mockup for the Home page.

Informing Tomcat about our Web Application

Before running our Tapestry Web application for the first time, we need to inform Tomcat that we've created this application and where to find it. To do this, we'll create a very simple XML file, a kind of note for Tomcat. We'll call it Login.xml. You can use any text editor to create this file; it won't be a part of our Web application.

This is all we need to write:

<Context docBase="/Users/alex/Documents/workspace/Login/context"    
   path="/Login"/>

In the docBase attribute, you must specify the path to the context subdirectory of your Web application. It will depend on where you've chosen to place your workspace directory when installing Eclipse. In this example, you can see the location of this directory on my Mac. It will be something similar on a Linux machine.

For Windows, note that when specifying the path to your context, you should use forward slashes, not the backward ones normally used in Windows paths. In other words, you should write docBase="c:/workspace/Login/context", instead of docBase="c:\workspace\Login\context".

Put this Login.xml file into the conf/Catalina/localhost subdirectory in your Tomcat directory (examples: c:\apache-tomcat-5.5.12\conf\Catalina\localhost or /Users/alex/jakarta-tomcat-5.0.28/conf/Catalina/localhost).

We're now ready to run our Tapestry Web application.

Running the Application

Start your Tomcat server as explained in the "Installing Tomcat" section. If Tomcat was already running, shut it down and start up again -- this will give Tomcat the opportunity to read the new configuration file that we just created when it starts up.

Point your browser to http://localhost:8080/Login/app. If everything was done properly, you should see the login page as shown in Figure 5 -- exactly as expected. If instead you get an error, go back and double-check that every step has been followed, then restart your Tomcat server and try again.

You already know that a request for http://localhost:8080 will invoke Tomcat running on your computer. /Login tells Tomcat which Web application we want our request sent to. Remember, when configuring Tomcat in the previous section, we told it the path to our application using path="/Login".

The /app part of our request tells our application that this is a request for its Tapestry part. By default, it's configured in such a way that all requests addressed to Tapestry must end with /app. This is for the case that our application contains other, non-Tapestry resources -- static HTML pages, for example. In the next part of this tutorial, I'll show you how to change the configuration so that all requests to /Login are passed to Tapestry.

Congratulations! You've got your first Tapestry Web application running. Admittedly, it's not much more impressive than 'Hello World' right now, but behind the scenes this page is already using three Tapestry components, and it's just waiting for us to add more functionality.

Let's add the following ground-breaking behaviour: when a user presses the "Let me in!" button, the second page will be displayed. For now, we won't perform any validation of the username or password. We'll just react to the pressing of the button by displaying the second page.

Which second page? The one we are going to create right now!

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links