Download

Get Loom

Property annotations

Property annotations can be bound to the attribute, its getter or setter. It is not required to comply to the java properties standard (meaning that you can annotate a private field with no public getters or setters), but you may need to provide them anyway to use EL expressions like ${action.customer.name}.

Validation annotations

Validation annotations are used to configure a Validator instance and bind it to some events. Since java does not allow attaching the same annotation more than once to the same java property, some of them include a "plural" form (StringValidations, NumberValidations).

  • StringValidation: Validates string properties (minimum and maximum length, format mask, etc)
  • RequiredValidation: Fails if the field is null or an empty String. It also works with empty arrays, Collections and Maps.
  • NumberValidation: Validates number instances (double, BigDecimal, long, integer) for precision, scope, minimum and maximum values, etc. The default minimum value is 0, and float is specifically not supported because of its lack of precision.
  • DateValidation: Validates a java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp and Joda date types. Minimum and maximum values can be specified as absolute dates or relative to the current day ("today + 2d", "today - 5m").

Validators contribute their metadata to JSP tags bound to the same property. For example, a validator that knows the maximum length of a field will contribute the maxlength attribute, RequiredValidator will add a "required" CSS class, etc.

Validators are triggered on some events indicated by the 'on' and 'except' attributes. Both accept wildcards:


   // validate the maximum length only on events that start with "save"
   @StringValidation(maxlength=100, on="save*")
   private String name;

   // validate the minimum value except on the list event
   @NumberValidation(minValue="120", except="list")
   private int value;

   // validate that this date has a minimum value of one year in the future (server time), on any event
   @DateValidation(minValue="today+1y")
   private Date start;

For cases where the validated property cannot be annotated (legacy classes, coding policy, etc) most of the Loom annotations include a propertyPath attribute that specify the nested attribute to validate:


   // validate customer.name, customer.email and customer.id
   @StringValidations({
      @StringValidation(maxlength=100, on="save", propertyPath="name"),
      @StringValidation(mask=StringValidation.EMAIL_MASK, on="save", propertyPath="email")
   })
   @RequiredValidation(propertyPath="id")
   private Customer customer;

Keep in mind that the recommended practice is to annotate the model classes instead.

The configured validator has a default error message specified in the messages.properties file, but it can also be overriden on a per field basis by using the message attribute:


   @StringValidation(mask=StringValidation.EMAIL_MASK, message="loom.validation.customEmailFailed")
   private String name;

It is recommended that you prefix your validation messages with "loom." to have it automatically included in the javascript validations.

NestedAnnotations

Annotations are usually set in the model attributes, in which case the action class must include @NestedAnnotations to inspect the annotated attribute for validation annotations:


   // Inspect the Customer class for annotations to apply for the "save" event,
   // except customer.lastName
   @NestedAnnotations(on="save" exceptProperties="lastName")
   private Customer customer;

Important note: Loom will not look inside any attribute class unless @NestedAnnotations is used.

JPA annotations

JPA annotations are also translated to the corresponding Validator instances:


public class CustomersAction extends AbstractAction {

   // use nested annotations for the "save" event
   @NestedAnnotations(on="save")
   private Customer customer;

}

@Entity
public class Customer {

	@Basic(optional = false)
	@Column(length=40)
	private String firstName;
	
	@Column(length=80)
	private String lastName;

}

Both @Basic and @Column are supported.

Hibernate-annotations

Hibernate validation annotations are also supported:


public class CustomersAction extends AbstractAction {

   @NestedAnnotations(on="save")
   private Customer customer;

}

public class Customer {

	@Required
	@Length(min=15)
	private String firstName;
	
	@Email
	private String email;

}

The following hibernate annotations are supported: Pattern, Email, NotEmpty, Length, NotNull, Min, Max, Range, Future and Past.

RetrieveEntity

The @RetrieveEntity annotation marks a persistent attribute. If its id is included in the current request, the instance will be read from the database. Other attributes will be populated after retrieving the instance. If there is more than one @RetrieveEntity annotations they will be processed in the declaration order.

In the following example, three database retrievals will be performed in the specified order:

@RetrieveEntities({
	@RetrieveEntity
	@RetrieveEntity(propertyPath="folder"),
	@RetrieveEntity(propertyPath="answerTo")
})
private Message message;

If the primary key value is not included in the request, there will be no retrieval.

This annotation requires the corresponding Interceptor configured in the Dependency Injection framework:


    <!-- 
    
    @RetrieveEntity  
    
    -->
    
    <bean class="org.loom.interceptor.RetrieveEntityInterceptor" 
        p:transactionalService-ref="transactionalService"
        scope="prototype"
    />

Injected

Marks a property that will be injected from a bean with the same name on the Dependency Injection framework.

public class MyAction extends AbstractAction {

   @Injected
   private MyService myService;

}