Tapestry Annotations

This library does not contain components; instead it provides Tapestry specific annotations (annotations are a a new feature in JDK 1.5 ). These annotations allow you to perform some operations inside Java code that otherwise would be specified in the page or component specification. This is very useful when using inheritance, because base classes can provide annotations that are inherited by subclasses.

The annotations are all in the package org.apache.tapestry.annotations.

Remember that a single method should only have, at most, one of these annotations! Having multiple annotations, or conflicts between method annotations and directives in the specification, will result in runtime exceptions. In addition, annotations don't provide the kind of line precise location data that the XML specifications or the templates do (but most exceptions will clearly identify the class and method, which should be sufficient).

Asset

The Asset annotation is the equivalent of the <asset> element in a specification. The value attribute is the path to the asset (possibly prefixed to indicate the domain for the path):

  @Asset("/style/global.css")
  public abstract IAsset getGlobalStylesheet();

The asset will be available using the property name using the "asset:" binding prefix.

Bean

The Bean annotation is the equivalent of the <bean> element in a specification.

The property type will be used as the Java class to instantiate for the bean; alternately, the value attribute may be specified (this is useful when, for example, the property type is an interface).

The examples below both define a bean of type HashMap:

  @Bean
  public abstract HashMap getHashMapBean();

  @Bean(HashMap.class)
  public abstract Map getMapBean();

A bean defined this way will be stored into the component's beans property, exactly as if specified using XML; its name will be the property name.

An additional attribute, lifecycle, controls the bean's lifecycle. The default is Lifecycle .REQUEST, and additional values are NONE, PAGE, and RENDER:

  @Bean(lifecycle = Lifecycle.RENDER)
  public abstract Map getRenderLifecycleBean();

Lastly, for simple configuration of the bean, there's the initializer attribute. This allows lightweight initialization , where the string is a series of name=value properties, seperated by commas (for boolean properties, the value is optional).

  @Bean(initializer = "maxRetries=3")
  public abstract LoginController getController();  

Component

The Component annotation is attached to an accessor method and allows a component type to be defined in place, as with the <component> element.

By default, the component type will match the return type and the component id will match the property name:

    @Component
    public abstract TextField getEmail();  

When component type and return type differ, then the type attibute should be specified:

    @Component(type = "contrib:Table")
    public abstract Table getResultsTable();  

Additionally, when component id and property name differ, then the id attibute can be specified:

    @Component(id = "email")
    public abstract TextField getEmailField();  

Component bindings are specified with an array of strings. The individual strings identify the parameter name and the binding reference:

    @Component(type = "Conditional", bindings =
    { "condition=message", "element=div" })
    public abstract IComponent getIfMessage();  

Component copies can be created by specifying the copyOf attribute. It should contain the name of another defined component. The type and bindings of that component will be copied to this component. The following code creates a copy of the previous component:

    @Component(copyOf = "ifMessage")
    public abstract IComponent getIfMessageCopy();  

ComponentClass

The ComponentClass annotation is used to mark a Java class as a component, and allows several properties normally specified using the <component-specification> element.

@ComponentClass
public abstract class MyComponent extends BaseComponent
{
...

The defaults for allowBody and allowInformalParameters are true; components may override:

@ComponentClass(allowBody = false, allowInformalParameters = false)
{
...

Note that simply having a @ComponentClass annotation will override those two properties.

If a component has reserved parameters, they can be specified as well:

@ComponentClass(reservedParameters = "href,name")
public abstract class LinkWriter extends BaseComponent
{
...

Finally, the presence of the @Deprecated annotation will mark the component as deprecated (and cause a warning to be output whenever the component is referenced):

@ComponentClass @Deprecated
public abstact class YeOldeComponent extends AbstractComponent
{
...

EventListener

The EventListener annotation is attached to listener methods to dynamically bind and listen to client side browser events.

This annotation is capable of a variety of tasks, but most of them are centered around the dojo javascript toolkit event API for binding and listening to javascript events in an AOP fashion.

As an example, this page form listener method will be called when the component with id "projectChoose" has it's javascript functional equivalent selectOption function called:

@EventListener(events = "onValueChanged", targets = "projectChoose")
public void projectSelected(IRequestCycle cycle)
{
  cycle.getResponseBuilder().updateComponent("projectDescription");
  cycle.getResponseBuilder().updateComponent("feedbackBlock");
}
                

Parameters

Name Type Required Default Description
targets String[],String no Specifies the components/widgets to listen to events on, in the form of unique component ids. One of either elements OR targets must be supplied. This is supposed to be the id as returned by IComponent.getClientId().
elements String[],String no Specifies the unique html element dom node ID's to listen to events on. One of either elements OR targets must be supplied.
events String[],String yes Specifies which javascript "events" to listen for. Just as with the dojo api, these events can be simple things like onclick or onsubmit, but can also be bound to specific functions if they exist on the component/widget you are targeting.



For example, the Autocomplete component wraps a dojo ComboBox widget that has a function named onValueChanged that is called when an item in the list is selected.
submitForm String no If specified, the form matching the passed in form ID will be submitted before calling your listener method. Information:

In almost all instances you will not want to use this parameter as any component target implementing IFormComponent will automatically submit the form containing it for you. This only needs to be used in the rare instance where you want to submit a different form.

validateForm boolean no false When targeting a component implementing IFormComponent, optionally enables/disables both client side validation and server side validation when the form is submitted.
async boolean no true When components implementing IFormComponent are targeted and will cause a form submission this parameter can additionally be used to cause the submission to happen with or without asynchronous (ajax) IO calls.
autoSubmit boolean no true When components implementing IFormComponent are targeted they will normally automatically cause a form submission to happen so that the property they manage on your form is updated when your listener method is called - this parameter can be used to disable the automatic submission of the enclosing form. Warning:

Use at your own risk, this is no guarantee what kind of state your properties will be in when this method is invoked. If you aren't relying on form specific properties to do things everything should be fine - just be careful.

focus boolean no false When components implementing IFormComponent are targeted and will cause a form submission this parameter can additionally be used to turn on/off the normal client side form element focusing that normally happens in Tapestry forms to focus the most important / first field in error. This can be annoying on AJAX IO calls and so is turned off by default.

Listener Parameters

Many people have asked about the possibility of specifying a list of parameters to pass in to the listener method like you would do with a DirectLink component. We are aware of this and hope to get it implemented sometime in one of the upcoming Tapestry 4.X releases.

InitialValue

The InitialValue annotation allows a default value to be specified for a property. The property may also be persistent (via the @Persist annotation).

The value is a binding reference; the reference will be evaluated when the page is first loaded, and again when the page is detached and returned to the page pool. The default binding prefix is "ognl:".

The annotation is attached to an accessor method:

  @InitialValue("request.serverName")
  public abstract String getServerName();        
      

In many cases, where the initial value is a constant, a better approach is to set the initial value from the component's finishLoad() method.

  public abstract int getMaxAttempts();
  
  public abstract void setMaxAttempts(int maxAttempts);
  
  protected void finishLoad()
  {
    setMaxAttempts(5);
  }        
      

InjectAsset

The InjectAsset annotation allows assets defined in the page or component specification to be exposed as read-only properties. It is attached to an accessor method:

  @InjectAsset("stylesheet")
  public abstract IAsset getStylesheet();

This is equivalent to specifying the property attribute of the <asset> element.

InjectComponent

The InjectComponent annotation allows nested components to be injected as read-only properties. It is attached to an accessor method:

  @InjectComponent("inputUserName")
  public abstract TextField getUserInput();

This is functionally the same as providing the property attribute of the <component> element.

InjectMeta

The InjectMeta annotation allows meta data from the specification ( <meta> elements ) to be exposed as properties.

  @InjectMeta("page-title")
  public abstract String getPageTitle();

The new property does not have to be type String; an automatic type conversion takes place.

The InjectMeta annotation can also be used alone - the method name is converted into a property key using the rules described at the Message annotation. Here's an equivalent to the previous example.

  @InjectMeta
  public abstract String getPageTitle();

InjectObject

The InjectObject annotation allows HiveMind objects to be injected. It is attached to an accessor method:

  @InjectObject("infrastructure:request")
  public abstract WebRequest getRequest();

The end result is the same as using the <inject> element, with the default type of "object".

InjectPage

The InjectPage annotation allows a page to be injected into another page or component. It is attached to an accessor method:

  @InjectPage("Details")
  public abstract Details getDetailsPage();

Injecting other pages is most commonly used as part of a listener method.

InjectScript

The InjectScript annotation allows JavaScript templates to be exposed as properties. The annotation's value is the path to the script (relative to the page or component specification, if it exists, or relative to the template otherwise).

  @InjectScript("scripts/VerifyAccountId.script")
  public abstract IScript getVerifyAccountIdScript();

InjectState

The InjectState annotation allows an Application State Object to be injected and a read/write property. It is attached to an accessor method:

  @InjectState("visit")
  public abstract MyAppVisit getVisit();

The end result is equivalent to using the <inject> element, with a type of "state".

The InjectState annotation can also be used alone - the method name is converted into a key using the rules described at the Message annotation. Here's an equivalent to the previous example.

  @InjectState
  public abstract MyAppVisit getVisit();

InjectStateFlag

The InjectStateFlag annotation implements a read-only boolean property that returns true if the identified application state object exists. This is useful for avoiding the accidental creation of the ASO, which helps avoid the unneccessary creation of the HttpSession.

    @InjectStateFlag("visit")
    public abstract boolean getVisitExists();
  

The end result is equivalent to using the <inject> element, with a type of "state-flag".

The InjectStateFlag annotation can also be used alone - the method name is converted into a key using the rules described at the Message annotation ( with the addition that any trailing "exists" is stripped ). Here's an equivalent to the previous example. It also injects the flag for the visit ASO.

  @InjectStateFlag
  public abstract boolean getVisitExists();

Message

The Message annotation provides easy access to localized messages. It is attached to a method that returns a String, and takes any number of parameters. In its most basic form, it is used alone:

    @Message
    public abstract String getPageTitle();

As used here, the method name is converted into a message property key: page-title :

  • The prefix "get" is stripped off (if present)
  • The letter following "get" is converted to lower case
  • Other capitalized letters are converted to lower case and preceded with a dash ("-")
The end result is equivalent to:
  public String getPageTitle()
  {
    return getMessages().getMessage("page-title");
  }
When these method-name to property key conversion rules don't yield the correct key, it may be specified explicitly:
  @Message("get-a-life")
  public abstract String getALife();
The method may take parameters as well; these parameter will be converted into message arguments, which can be referenced inside the message as {0}, {1}, etc.
  @Message
  public abstract String getLineTotal(BigDecimal total);

This is equivalent to:

  public String getLineTotal(BigDecimal total)
  {
    return getMessages().format("line-total", new Object[] { total });
  }

Primitive types passed in as parameters are automatically converted to wrapper types.

Meta

The Meta annotation is used to define a meta data value on a page or component class, as with the <meta> element in an XML component or page specification.

Meta data from base classes is merged into subclasses; when there's a name conflict, the subclass overrides the base class. This allows a base class to set a default that can be naturally overriden in a subclass.

@Meta({ "requires-login=false", "show-ad-banner=true" })
public abstract class AppBasePage extends BasePage
{
...

Subclasses of AppBasePage could provide a @Meta annotatioun that defines new meta data, or overrides either of these values.

Often, you only want to define a single meta value; the compiler allows you to omit the curly braces for this case:

@Meta("org.apache.tapestry.jwcid-attribute-name=id")
public abstract class MyComponent extends BaseComponent
{
...

Parameter

The Parameter annotation defines a new property, as with <parameter> element in an XML component specification.

This most simple use of this annotation is to simply mark a property as an optional parameter:

  @Parameter
  public abstract void MyType getMyParameter();

The parameter name will generally match the property name (as determined from the method to which the annotation is attached). This can be overriden with the name attribute:

  @Parameter(name = "page")
  public abstract String getTargetPage();

Parameters may be marked as deprecated if the method is marked deprecated (using the java.lang.Deprecated annotation):

  @Deprecated @Parameter
  public abstract int getRows();

The annotation supports several additional attributes, consult its JavaDoc for the full details.

Persist

The Persist annotation allows a property to be marked as persistent. Remember that any otherwise unclaimed abstract property will become a transient property automatically (in Tapestry 4.0), so Persist is only needed to mark a property as persistent.

The value of the Persist annotation defaults to "session":

  @Persist
  public abstract int getSessionPersistentProperty();
  
  @Persist("client")
  public abstract double getClientPersistentProperty();

This annotation works exactly like a <property> element, except that the initial-value attribute can't be specified. Use the @InitialValue annotation to set the property's initial value.