Apache Tapestry — Complete Component Framework Reference Guide
Learning Path
flowchart LR
A["Tapestry Overview"] --> B["Core Concepts"]
B --> C["Intermediate Topics"]
C --> D["Advanced Topics"]
D --> E["Practical Applications"]
A --> F["You Are Here"]
style F fill:#f90,color:#fff
Apache Tapestry is a component-oriented Java web framework emphasizing simplicity, developer productivity, and live class reloading — where every page and component is a POJO with an associated HTML template.
What You’ll Learn
By the end of this tutorial, you’ll create Tapestry pages with component templates, handle events and actions, use the IoC container for dependency injection, manage assets (CSS/JS), implement Ajax partial updates with Zones, and leverage live reloading during development.
Why Tapestry Matters
Tapestry’s most distinctive feature is its live class reloading — change Java code and see the result immediately without redeploying. This dramatically speeds up development compared to traditional Java EE frameworks that require server restarts. While less popular than JSF or Spring MVC, Tapestry has a dedicated following for projects where developer productivity is paramount. DodaTech uses Tapestry for internal tools where rapid iteration is more important than ecosystem size.
Security note: Understanding Reference helps build more secure applications — a core principle at DodaTech, where tools like Durga Antivirus Pro and Doda Browser rely on solid implementation practices.
Page Template
Tapestry pages consist of an HTML template and a Java class:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd">
<head><title>Product List</title></head>
<body>
<h1>${message}</h1>
<table t:type="grid" source="products" row="product">
<p:nameCell>
<a t:type="pagelink" page="detail" context="product.id">
${product.name}
</a>
</p:nameCell>
<p:priceCell>${product.price}</p:priceCell>
</table>
<t:if test="showForm">
<form t:type="form" t:id="productForm">
<t:label for="name">Name:</t:label>
<input t:type="textfield" t:id="name" value="product.name"/>
<input t:type="submit" value="Save"/>
</form>
</t:if>
</body>
</html>Line-by-line explanation:
xmlns:t="..."— Tapestry namespace. All Tapestry components use thet:prefix${message}— renders the value of thegetMessage()method from the Java page class<table t:type="grid">— Tapestry’s Grid component renders a sortable HTML table from a listsource="products"— binds the grid to thegetProducts()method<a t:type="pagelink" page="detail" context="product.id">— link to another page, passing the product ID<t:if test="showForm">— conditionally renders the form based onisShowForm()
Page Class
The Java class that backs the template:
public class ProductList {
@Inject
private ProductService productService;
@Property
private Product product;
@Property
private String message;
void onActivate() {
message = "Product Catalog";
}
public List<Product> getProducts() {
return productService.findAll();
}
}Line-by-line:
@Inject— Tapestry’s dependency injection. InjectsProductServicefrom the IoC container@Property— automatically generates getter/setter methods. Makesproductaccessible in the templatevoid onActivate()— lifecycle method called when the page is first accessed. Use it for initializationpublic List<Product> getProducts()— properties exposed to the template asproducts(Java Bean convention)
Event Handling
Tapestry uses an event-driven model — user actions trigger events on the server:
// Event handler for form submission
void onSuccessFromProductForm() {
productService.save(product);
}
// Event handler for link click (passing context)
void onActionFromDelete(int productId) {
productService.delete(productId);
}
// Event handler with return value
Object onSelectedFromCancel() {
return Index.class; // navigate to the Index page
}IoC Container
Tapestry has its own Inversion of Control container for dependency management:
// Service interface and implementation
public interface ProductService {
List<Product> findAll();
}
public class ProductServiceImpl implements ProductService {
public List<Product> findAll() {
// database lookup
}
}
// Contribute implementation in Module class
public class AppModule {
public static void bind(ServiceBinder binder) {
binder.bind(ProductService.class, ProductServiceImpl.class);
}
}Ajax Zones
Partial page updates without full page reload:
<div t:type="zone" t:id="resultZone">
Initial content
</div>
<a t:type="actionlink" t:id="updateLink"
zone="resultZone">Update</a>@Inject
private Zone resultZone;
Object onActionFromUpdateLink() {
return resultZone.getBody(); // only updates the zone
}Common Mistakes
1. Forgetting @Property on template-accessible fields
Without @Property, the field is private and not visible to the template. Use @Property to automatically generate getters/setters.
2. Not understanding the component tree
Tapestry creates a component tree on every request. Components like t:if and t:loop require special attention to how they affect the tree.
3. Using @Inject incorrectly
@Inject works for services from the IoC container, not for random POJOs. Use @InjectPage for injecting other page instances.
4. Ignoring Tapestry’s exception reporting
Tapestry shows detailed error pages with source code. This is a feature — use it to debug, but disable it in production.
5. Not leveraging live reload
Tapestry’s live reload is its superpower. Don’t stop your server during development — change code and let Tapestry reload it.
Practice Questions
1. How does Tapestry connect HTML templates to Java classes?
Answer: By naming convention — ProductList.tml (template) maps to ProductList.java (class). The template uses EL expressions like ${propertyName} to access Java properties.
2. What does @Property do in Tapestry?
Answer: @Property automatically generates getter and setter methods for the field, making it accessible in the template via EL.
3. How do you implement partial page updates in Tapestry?
Answer: Use Zones — wrap content in <t:type="zone">, then return the zone body from an event handler to update only that section.
4. What is the purpose of the IoC container?
Answer: Tapestry’s IoC container manages dependencies through @Inject and ServiceBinder, eliminating manual service instantiation and configuration.
Challenge
Build a Tapestry application with a product catalog page (list, sort, filter), a product detail page with Ajax Zones for reviews, and a form for adding new products with validation.
FAQ
Try It Yourself
# 1. Create a Tapestry project using Maven archetype
mvn archetype:generate \
-DarchetypeGroupId=org.apache.tapestry \
-DarchetypeArtifactId=quickstart \
-DarchetypeVersion=5.8.x
# 2. Navigate to the generated project
cd myapp
# 3. Start in development mode
mvn jetty:run
# 4. Open http://localhost:8080
# 5. Modify pages/Index.tml or pages/Index.java
# 6. Refresh the browser — changes appear immediatelyWhat’s Next
Explore more Java web frameworks:
| Topic | Description |
|---|---|
| https://tutorials.dodatech.com/java/jsf/reference/ | JavaServer Faces component framework |
| https://tutorials.dodatech.com/java/cxf/reference/ | Apache CXF web services |
Related topics to explore:
- Java Web Development
- Spring Framework
- Web Application Deployment
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro