Tapestry Core

The tapestry-core module provides the interfaces and annotations that form the Tapestry API.

tapestry-core is built upon the Tapestry IoC Container.

New And Of Note

  • The @Cached annotation has been added to allowing the caching of method results.
  • Tapestry can now generate accessor methods for fields automatically via the @Property annotation.
  • It is now possible to override the built-in display and edit blocks for data types.
  • Tapestry now supports "Index" pages at the root or in sub-folders.
  • Tapestry pages may now be secured for access only via HTTPS.
  • Added the FormFragment component to allow for forms that are mutable on the client-side. In addition, the Form component may now update a Zone.
  • Form components now trigger a "validateForm" event, not a "validate" event (so as to avoid conflict with the "validate" event triggered by TextFields).
  • Tapestry now understands JDK 1.5 Generics, allowing for simple parameterized types for properties of data objects and components.
  • Tapestry now differentiates between development mode and production mode, primarily in how it reports runtime exceptions. It defaults to production mode.
  • Components can now supply an event handler for type "exception" which will be invoked when an exception occurs in a component event handler method. This is useful for handling errors with the context for the "activate" event.
  • Page instances are now cached more efficiently, with Tapestry attempting to control how many instances are created, and culling unused instances periodically.
  • Component templates are stripped of unnecessary whitespace.
  • New Unless component (like an If component, but inverted).
  • Support for "password" and "longtext" data types (for use with the BeanEditor), and a new TextOutput component.
  • The new "var:" binding prefix allows for temporary, untyped storage of render-time values without having to define a new component property.

Changes from Tapestry 4 to Tapestry 5

Tapestry 5 represents a significant advance over Tapestry 4. The goal is to make Tapestry 5 significantly easier to use than Tapestry 4 (or any other Java web framework). We're keeping the essence of Tapestry 4, but starting with a brand new code base designed to provide a stable, powerful, extensible platform for many years to come.

Here's a few of the planned and implemented features:

  • Easy to get started using Maven.
  • Simplified, minimal API based on annotations.
  • No base class requirement; components are true, pure Pojos (Plain Old Java Objects).
  • Abstract classes ... gone! Classes are normal, concrete classes.
  • No XML descriptors for pages and components ... just the annotations.
  • Less configuration all around.
  • Automatic reloading of templates and even Java classes.
  • Super-duper Ajax integration built in.
  • Easy & fast unit testing of individual pages or components.

Adaptive API

A key feature of Tapestry 5 is adaptive API.

In traditional Java frameworks, including Tapestry 4, user code is expected to conform to the framework. You create classes that extend from framework-provided base classes, or implement framework-provided interfaces.

This works well until you upgrade to the next release of the framework: with the new features of the upgrade, you will more often than not experience breaks in backwards compatibility. Interfaces or base classes will have changed and your existing code will need to be changed to match.

In Tapestry 5, the framework adapts to your code. You have control over the names of the methods, the parameters they take, and the value that is returned. This is driven by annotations, which tell Tapestry under what circumstances your methods are to be invoked.

For example, you may have a login form and have a method that gets invoked when the form is submitted:

public class Login
{
  @Persist
  private String _userId;
  
  private String _password;
  
  @Component
  private Form _form;
  
  @Inject
  private LoginAuthenticator _authenticator;
  
  private Object onSubmit()
  {
    if (_authenticator.isValidLogin(_userId, _password))
      return Start.class;
      
    // Stay on this page:
  
   _form.recordError("Invalid user name or password."); 
    
    return null;
  }
  
  public String getUserId() { return _userId; }
  
  public String getPassword() { return _password; }
  
  public void setUserId(String userId) { _userId = userId; }
  
  public void setPassword(String password) { _password = password; }
}

This short snippet demonstrates a bit about how Tapestry operates. Pages and services within the application are injected with the @Inject annotation. The method name, onSubmit(), informs Tapestry about when the method is to be invoked (when a form component contained by the page emits a "submit" event). The method's return value directs Tapestry on what to do next: jump to another page within the application (here identified as the class for the page, but other options exist), or stay on the same page to display the error message.

This also represents a distinct change from Tapestry 4. In earlier versions of Tapestry, the Form component's listener parameter would be bound to the method to invoke, by name. Further, the listener method had to be public. This new approach not only support multiple listeners, but provides an improved separation of view concerns (inside the page's HTML template) and logic concerns, inside the Java class.

In many cases, additional information about the event is available, and can be passed into the method by adding parameters to the method. Again, Tapestry will adapt to your parameters, in whatever order you supply them.

Finally, Tapestry 5 explicitly separates actions (requests that change things) and rendering (requests that render pages) into two separate requests. Performing an action, such as clicking a link or submitting a form, results in a client side redirect to the new page. This is often called "redirect after post". This helps ensure that URLs in the browser are book-markable ... but also requires that a bit more information be stored in the session between requests (using the @Persist annotation).