Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin
Div
stylefloat:right
titleRelated Articles
classaui-label
Content by Label
showLabelsfalse
showSpacefalse
titleRelated Articles
cqllabel = "new-users" and space = currentSpace()

Principle 1 – Static Structure, Dynamic Behavior

The concept of "Dynamic Behavior" should be pretty obvious when you are building a web application; things should look different for different users/situations. But what does it mean that Tapestry has "Static Structure?" Static structure implies that when you build a page in Tapestry you are going to define all of the types of components that are used within that page. Under no circumstance during the rendering or event processing of the page will you be able to dynamically create a new type of component and place that into the component tree.

At first glance, this seems quite limiting ... other frameworks allow new elements to be created on the fly; it's also a common feature of desktop GUIs such as Swing. But static structure turns out to be not so limiting after all. You can create new elements (you're actually re-rendering existing components with different properties). And you have plenty of options for getting dynamic behavior out of your static structure; from the simple conditional and looping components to the more advanced implementations of Tapestry's BeanEditor or Grid components, Tapestry gives you control over what renders and when, and even where it appears on the page. And starting in Tapestry 5.3 you can even use the Dynamic component, which renders whatever is in an external template file.

Why did Tapestry choose static structure as a core principle? It's really a matter of meeting the requirements of agility and scalability.

Agility

Tapestry is designed to be an agile working environment; "code less, deliver more". To support you writing less code Tapestry does a lot of work on your POJO pages and components when first loading them. It also uses shared instances of page and component classes (shared across multiple threads and requests). Having dynamically modifiable structure would imply that each request has its own instance and, further, that the entire structure would need to be serialized between requests so that it can be restored to handle later requests.

Tapestry also makes you more agile by speeding up the development cycle with Live Class Reloading. Tapestry monitors the file system for changes to Java page classes, component classes, service implementation classes, HTML templates and component property files, and it hot-swaps the changes into the running application without requiring a restart or losing session data. This provides a very short code-save-view cycle that no other framework can touch.

Scalability

When building large scale systems it is important to consider how your resources are going to be used on each deployed server, and how that information is going to be shared between servers. Static structure means that page instances do not need to be stored inside the HttpSession and simple browsing users do not require extra system resources

What is Apache Tapestry?

Apache Tapestry is an open-source framework for creating dynamic, robust, highly scalable web applications in Java. Tapestry complements and builds upon the standard Java Servlet API, and so it works in any servlet container or application server.

Tapestry divides a web application into a set of pages, each constructed from components. This provides a consistent structure, allowing the Tapestry framework to assume responsibility for key concerns such as URL construction and dispatch, persistent state storage on the client or on the server, user input validation, localization/internationalization, and exception reporting. Developing Tapestry applications involves creating HTML templates using plain HTML, and combining the templates with small amounts of Java code. In Tapestry, you create your application in terms of objects, and the methods and properties of those objects – and specifically not in terms of URLs and query parameters. Tapestry brings true object oriented development to Java web applications.

Tapestry is specifically designed to make creating new components very easy, as this is a routine approach when building applications.

Tapestry is architected to scale from tiny, single-page applications all the way up to massive applications consisting of hundreds of individual pages, developed by large, diverse teams. Tapestry easily integrates with any kind of backend, including JEE, Spring and Hibernate.

It's more than what you can do with Tapestry ... it's also how you do it! Tapestry is a vastly productive environment. Java developers love it because they can make Java code changes and see them immediately ... no redeploy, no restart! And it's blazingly fast to boot (even when files change). Designers love it because Tapestry templates are so close to ordinary HTML, without all the cruft and confusion seen in JavaServer Pages. Managers love it because it makes it easy for large teams to work together, and because they know important features (including localization) are baked right in. Once you work in Tapestry there's no going back!

Tapestry is released under the Apache Software Licence 2.0.

Principle 1 – Static Structure, Dynamic Behavior

Tapestry is designed to be extremely scalable in several dimensions:

  • Tapestry applications may contain large numbers of pages and many custom components.
  • Tapestry applications may contain very complex functionality.
  • Tapestry applications may be created by large, diverse teams.
  • Tapestry applications can service large numbers of concurrent users.

One core architecture decision in Tapestry exists to service many of the above goals (and others that are harder to describe). Static Structure, Dynamic Behavior

In Tapestry, the structure of any particular page is static. This is necessary for several reasons, most importantly because Tapestry pages are pooled. Creating a Tapestry page is an involved process, because the page object is simply the root of a large tree of other objects including user provided components, many kinds of structural objects, template objects, and others. Creating a new page instance for each request is simply not scalable.

Instead, Tapestry pools pages. Once created, a page instance will be stored in a pool for that particular type of page, and reused in later requests. An incoming request, the result of a user clicking a link or submitting a form, will be processed by some server within a cluster, and will use some page instance within the page pool. Because page instances are static and uniform across instances and servers, Tapestry can use any available page instance, or create a new one as needed.

Tapestry does not need to store page instances inside the HttpSession. At most, it stores a smattering of persistent field values from the page, but not the entire page instance. This lean use of the HttpSession is key to Tapestry's very high scalability, especially in a clustered configuration.

In some Tapestry-like frameworks, such as Faces and Wicket, the page structure is more dynamic, at the cost of storing much, much more data in the HttpSession.

This static structure is not so limiting as you might think. With different kinds of conditional and looping components, and the ability to "jump out of the flow" and render components in an arbitrary order, you will not find Tapestry to be rigid ... anything but!Again, linking an instance of a page to a particular client would require vastly more server-side resources than having a single shared page instance.

Principle 2 – Adaptive API

A key feature of Tapestry 5 is its adaptive API.

In traditional Java frameworks , including (including Struts, JSF and even the now-ancient 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.

...

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

Code Block
JAVA
langJAVAjava

public class Login
{
  @Persist
  @Property
  private String userId;

  @Property
  private String password;

  @Component
  private Form form;

  @Inject
  private LoginAuthenticator authenticator;

  void onValidateFormonValidateFromForm()
  {
    if (! authenticator.isValidLogin(userId, password))
    {
      form.recordError("Invalid user name or password.");
    }
  }

  Object onSuccessonSuccessFromForm()
  {
    return PostLogin.class;
  }
}

This short snippet demonstrates a bit about how Tapestry operates. Pages and services within the application are injected with the @Inject @Inject annotation. The method names, onValidateForm onValidateFromForm() and onSuccess onSuccessFromForm(), inform Tapestry about when the each method is to be invoked. This naming convention identifies the event that is handled, ("validate" and "success") and the id of the component from which the event is triggered (the "form" component).

The "validate" event The two events validateForm and success occur when a form is submitted; "validateForm" is triggered to perform cross-field validations, and the "success" event is only triggered when there are no validation errors. The onSuccess onSuccessFromForm() 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 many other options exist). When there are exceptions, the page will be redisplayed to the user.

This also represents a distinct change from Tapestry 4. In earlier versions of Tapestry, By contrast, in Tapestry 4 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 The Tapestry 5 approach not only support supports multiple listeners, but also 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 simply by adding parameters to the method. Again, Tapestry will adapt to your parameters, in whatever order you supply them.

Tapestry also saves you needless effort: the @Property @Property annotation marks a field as readable and writable; Tapestry will provide the accessor methods automatically.

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 an action link or submitting a form, results in a client-side redirect to the new page. This is often called "redirect after post"the "Post/Redirect/Get" pattern (alternatively "Post-Then-Redirect", or "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 @Persist annotation).

Principle 3 – Differentiate Public vs. Internal APIs

An issue plaguing previous much ancient versions of Tapestry (4 ( and earlier) was the lack of a clear delineator delineation between private, internal APIs and public, external APIs. The fact that your code would extend from base objects but that many of the methods on those base objects were "off limits" further confused the issue. This has been identified as a key factor in the "steep learning curve of Tapestry" meme.

With the Designed from a clean slate of , Tapestry 5 , we are being is much more ruthless about what is internal vs. external.

First of all, anything inside the org.apache.tapestry5.internal package is internal. It is part of the implementation of Tapestry. It is the man behind the curtain. You should not ever need to directly use this code. It is a bad idea to do so, because internal code may change from one release to the next without concern for backwards compatibility.

Tip

If you ever find yourself forced to make use of internal APIs, please bring it up on the developer mailing list; this is how we know which services should be exposed as public, and fall under the backwards compatibility umbrella.

Principle 4 – Ensure Backwards Compatibility

Older versions of Tapestry has been were plagued by backwards compatibility problems with every major release. Tapestry 5 does did not even attempt to be backwards compatible to Tapestry 4. Instead, it lays laid the ground work for true backwards compatibility going forwards.

Tapestry 5's API is based almost entirely largely on naming conventions and annotations. You Your components are just ordinary Java classes; you will annotate fields to allow Tapestry to maintain their state or to allow Tapestry to inject resources, and you will name (or annotate) methods to tell Tapestry under what circumstances a method should be invoked.

...

Because of this, Tapestry will be able to 5 can change internally to a great degree without it affecting any of the application code you write. This should has finally crack cracked the backwards compatibility nut, allowing you to have great assurance that you can upgrade to future releases of Tapestry without breaking your existing applications.

This is already evident in Tapestry 5.1, where many 5.2 and 5.3 where major new features and improvements have occurred, but is still while remaining 100% backwards compatible to Tapestry 5.0 , as long as you've avoided the temptation to make use of internal APIs.

...