1. Overview

Nowadays, from social networking to banking, healthcare to government services, all activities are available online. Therefore, they rely heavily on web applications.

A web application enables users to consume/enjoy the online services provided by a company. At the same time, it acts as an interface to the backend software.

In this introductory tutorial, we’ll explore the Apache Tapestry web framework and create a simple web application using the basic features that it provides.

2. Apache Tapestry

Apache Tapestry is a component-based framework for building scalable web applications.

It follows the convention-over-configuration paradigm and uses annotations and naming conventions for configurations.

All the components are simple POJOs. At the same time, they are developed from scratch and have no dependencies on other libraries.

Along with Ajax support, Tapestry also has great exception reporting capabilities. It provides an extensive library of built-in common components as well.

Among other great features, a prominent one is the hot reloading of the code. Therefore, using this feature, we can see the changes instantly in the development environment.

3. Setup

Apache Tapestry requires a simple set of tools to create a web application:

  • Java 1.6 or later
  • Build Tool (Maven or Gradle)
  • IDE (Eclipse or IntelliJ)
  • Application Server (Tomcat or Jetty)

In this tutorial, we’ll use the combination of Java 8, Maven, Eclipse, and Jetty Server.

To set up the latest Apache Tapestry project, we’ll use Maven archetype and follow the instructions provided by the official documentation:

$ mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org

Or, if we have an existing project, we can simply add the tapestry-core Maven dependency to the pom.xml:

<dependency>
    <groupId>org.apache.tapestry</groupId>
    <artifactId>tapestry-core</artifactId>
    <version>5.4.5</version>
</dependency>

Once we’re ready with the setup, we can start the application apache-tapestry by the following Maven command:

$ mvn jetty:run

By default, the app will be accessible at localhost:8080/apache-tapestry:

homepage

4. Project Structure

Let’s explore the project layout created by Apache Tapestry:

tree structure

We can see a Maven-like project structure, along with a few packages based on conventions.

The Java classes are placed in src/main/java and categorized as components, pages, and services.

Likewise, src/main/resources hold our templates (similar to HTML files) — these have the .tml extension.

For every Java class placed under components and pages directories, a template file with the same name should be created.

The src/main/webapp directory contains resources like images, stylesheets, and JavaScript files. Similarly, testing files are placed in src/test.

Last, src/site will contain the documentation files.

For a better idea, let’s take a look at the project structure opened in Eclipse IDE:

project structure

5. Annotations

Let’s discuss a few handy annotations provided by Apache Tapestry for day-to-day use. Going forward, we’ll use these annotations in our implementations.

5.1. @Inject

The @Inject annotation is available in the org.apache.tapestry5.ioc.annotations package and provides an easy way to inject dependencies in Java classes.

This annotation is quite handy to inject an asset, block, resource, and service.

5.2. @InjectPage

Available in the org.apache.tapestry5.annotations package, the @InjectPage annotation allows us to inject a page into another component. Also, the injected page is always a read-only property.

5.3. @InjectComponent

Similarly, the @InjectComponent annotation allows us to inject a component defined in the template.

5.4. @Log

The @Log annotation is available in the org.apache.tapestry5.annotations package and is handy to enable the DEBUG level logging on any method. It logs method entry and exit, along with parameter values.

5.5. @Property

Available in the org.apache.tapestry5.annotations package, the @Property annotation marks a field as a property. At the same time, it automatically creates getters and setters for the property.

5.6. @Parameter

Similarly, the @Parameter annotation denotes that a field is a component parameter.

6. Page

So, we’re all set to explore the basic features of the framework. Let’s create a new Home page in our app.

First, we’ll define a Java class Home in the pages directory in src/main/java:

public class Home {
}

6.1. Template

Then, we’ll create a corresponding Home.tml template in the pages directory under src/main/resources.

A file with the extension .tml (Tapestry Markup Language) is similar to an HTML/XHTML file with XML markup provided by Apache Tapestry.

For instance, let’s have a look at the Home.tml template:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <head>
        <title>apache-tapestry Home</title>
    </head>
    <body>
        <h1>Home</h1>
    </body>   
</html>

Voila! Simply by restarting the Jetty server, we can access the Home page at localhost:8080/apache-tapestry/home:

home1

6.2. Property

Let’s explore how to render a property on the Home page.

For this, we’ll add a property and a getter method in the Home class:

@Property
private String appName = "apache-tapestry";

public Date getCurrentTime() {
    return new Date();
}

To render the appName property on the Home page, we can simply use ${appName}.

Similarly, we can write ${currentTime} to access the getCurrentTime method from the page.

6.3. Localization

Apache Tapestry provides integrated localization support. As per convention, a page name property file keeps the list of all the local messages to render on the page.

For instance, we’ll create a home.properties file in the pages directory for the Home page with a local message:

introMsg=Welcome to the Apache Tapestry Tutorial

The message properties are different from the Java properties.

For the same reason, the key name with the message prefix is used to render a message property — for instance, ${message:introMsg}.

6.4. Layout Component

Let’s define a basic layout component by creating the Layout.java class. We’ll keep the file in the components directory in src/main/java:

public class Layout {
    @Property
    @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
    private String title;
}

Here, the title property is marked required, and the default prefix for binding is set as literal String.

Then, we’ll write a corresponding template file Layout.tml in the components directory in src/main/resources:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <head>
        <title>${title}</title>
    </head>
    <body>
        <div class="container">
            <t:body />
            <hr/>
            <footer>
                <p>&copy; Your Company</p>
            </footer>
        </div>
    </body>
</html>

Now, let’s use the layout on the home page:

<html t:type="layout" title="apache-tapestry Home" 
    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <h1>Home! ${appName}</h1>
    <h2>${message:introMsg}</h2>
    <h3>${currentTime}</h3>
</html>

Note, the namespace is used to identify the elements (t:type and t:body) provided by Apache Tapestry. At the same time, the namespace also provides components and attributes.

Here, the t:type will set the layout on the home page. And, the t:body element will insert the content of the page.

Let’s take a look at the Home page with the layout:

homepage2-1

7. Form

Let’s create a Login page with a form, to allow users to sign-in.

As already explored, we’ll first create a Java class Login:

public class Login {
    // ...
    @InjectComponent
    private Form login;

    @Property
    private String email;

    @Property
    private String password;
}

Here, we’ve defined two properties — email and password. Also, we’ve injected a Form component for the login.

Then, let’s create a corresponding template login.tml:

<html t:type="layout" title="apache-tapestry com.example"
      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
      xmlns:p="tapestry:parameter">
    <t:form t:id="login">
        <h2>Please sign in</h2>
        <t:textfield t:id="email" placeholder="Email address"/>
        <t:passwordfield t:id="password" placeholder="Password"/>
        <t:submit class="btn btn-large btn-primary" value="Sign in"/>
    </t:form>
</html>

Now, we can access the login page at localhost:8080/apache-tapestry/login:

login-1

8. Validation

Apache Tapestry provides a few built-in methods for form validation. It also provides ways to handle the success or failure of the form submission.

The built-in method follows the convention of the event and the component name. For instance, the method onValidationFromLogin will validate the Login component.

Likewise, methods like onSuccessFromLogin and onFailureFromLogin are for success and failure events respectively.

So, let’s add these built-in methods to the Login class:

public class Login {
    // ...
    
    void onValidateFromLogin() {
        if (email == null)
            System.out.println("Email is null);

        if (password == null)
            System.out.println("Password is null);
    }

    Object onSuccessFromLogin() {
        System.out.println("Welcome! Login Successful");
        return Home.class;
    }

    void onFailureFromLogin() {
        System.out.println("Please try again with correct credentials");
    }
}

9. Alerts

Form validation is incomplete without proper alerts. Not to mention, the framework also has built-in support for alert messages.

For this, we’ll first inject the instance of the AlertManager in the Login class to manage the alerts*.* Then, replace the println statements in existing methods with the alert messages:

public class Login {
    // ...
    @Inject
    private AlertManager alertManager;

    void onValidateFromLogin() {
        if(email == null || password == null) {
            alertManager.error("Email/Password is null");
            login.recordError("Validation failed"); //submission failure on the form
        }
    }
 
    Object onSuccessFromLogin() {
        alertManager.success("Welcome! Login Successful");
        return Home.class;
    }

    void onFailureFromLogin() {
        alertManager.error("Please try again with correct credentials");
    }
}

Let’s see the alerts in action when the login fails:

loginfail-1

10. Ajax

So far, we’ve explored the creation of a simple home page with a form. At the same time, we’ve seen the validations and support for alert messages.

Next, let’s explore the Apache Tapestry’s built-in support for Ajax.

First, we’ll inject the instance of the AjaxResponseRenderer and Block component in the Home class. Then, we’ll create a method onCallAjax for processing the Ajax call:

public class Home {
    // ....

    @Inject
    private AjaxResponseRenderer ajaxResponseRenderer;
    
    @Inject
    private Block ajaxBlock;

    @Log
    void onCallAjax() {
        ajaxResponseRenderer.addRender("ajaxZone", ajaxBlock);
    }
}

Also, we need to make a few changes in our Home.tml.

First, we’ll add the eventLink to invoke the onCallAjax method. Then, we’ll add a zone element with id ajaxZone to render the Ajax response.

Last, we need to have a block component that will be injected in the Home class and rendered as Ajax response:

<p><t:eventlink event="callAjax" zone="ajaxZone" class="btn btn-default">Call Ajax</t:eventlink></p>
<t:zone t:id="ajaxZone"></t:zone>
<t:block t:id="ajaxBlock">
    <hr/>
    <h2>Rendered through Ajax</h2>
    <p>The current time is: <strong>${currentTime}</strong></p>
</t:block>

Let’s take a look at the updated home page:

home-1

Then, we can click the Call Ajax button and see the ajaxResponseRenderer in action:

homeAjax

11. Logging

To enable the built-in logging feature, the instance of the Logger is required to be injected. Then, we can use it to log at any level like TRACE, DEBUG, and INFO.

So, let’s make the required changes in the Home class:

public class Home {
    // ...

    @Inject
    private Logger logger;

    void onCallAjax() {
        logger.info("Ajax call");
        ajaxResponseRenderer.addRender("ajaxZone", ajaxBlock);
    }
}

Now, when we click the Call Ajax button, the logger will log at the INFO level:

[INFO] pages.Home Ajax call

12. Conclusion

In this article, we’ve explored the Apache Tapestry web framework.

To begin with, we’ve created a quickstart web application and added a Home page using basic features of Apache Tapestry, like components, pages, and templates.

Then, we’ve examined a few handy annotations provided by Apache Tapestry to configure a property and component/page injection.

Last, we’ve explored the built-in Ajax and logging support provided by the framework.

As usual, all the code implementations are available over on GitHub.