Web Application Development
Hypes and trends (such as Web 2.0) cause a change in the requirements for user interfaces every now and then. Although a lot of frameworks are capable of meeting these changing requirements, they often mean that you, as a developer, need in-depth knowledge of web standards, such as XHTML and JavaScript. Apache MyFaces hides all of the details of how the page is rendered at the client, and at the same time offers a rich set of tools and building blocks. This can save you a lot of time not only when you’re building a brand-new application, but also when you’re adapting an existing application to meet new user interface requirements.
also read:
This book will teach you everything that you need to know in order to build appealing web interfaces with Apache MyFaces, and to maintain your code in a pragmatic way. It describes all of the steps that are involved in building a user interface with Apache
MyFaces. This includes building templates and composition components with Facelets, and using all sorts of specialized components from the Tomahawk and Trinidad component sets. Adding validation with MyFaces Extensions Validator as well as using MyFaces Orchestra to manage transactions in a page fl ow, are also covered.
Unlike comparable books, this book not only introduces Facelets as an alternative to JSP, but actually uses Facelets in all the examples throughout this book. This makes the book a valuable resource for Facelets examples. The book also shows how various components of the MyFaces project can be used together, in order to deliver the functionality of the new JSF 2.0 standard, in current projects, without the need to upgrade your project to JSF 2.0.
This book uses a step-by-step approach, and contains a lot of tips based on experience of the MyFaces libraries in real-world projects. Throughout the book, an example scenario is used to work towards a fully-functional application by the end of this book.
This step-by-step guide will help you to build a fully-functional and powerful application.
What This Book Covers
Chapter 1, Introduction, introduces the Apache MyFaces project and all of its subprojects. Forward references to other chapters are given wherever applicable.
Chapter 2, Getting Started, discusses downloading and installing the MyFaces libraries. The set-up of two specific IDEs is discussed, as well as the set-up of an application server for testing. This chapter also covers the use of Maven and the Maven artifacts that are provided by the MyFaces project.
Chapter 3, Facelets, covers the installation of Facelets into our project. It discusses the benefits of Facelets over JavaServer Pages as a view technology for JavaServer Faces. This chapter also introduces the most important features of Facelets. By the end of the chapter, we have created a layout template that we can use throughout the book, when developing our application. We will also have learned the basic Facelets techniques that we will use in all examples throughout the book.
Chapter 4, Tomahawk, looks at the Tomahawk component set that is a part of MyFaces. Some of the most important components from the set are covered, and we will learn how we can use these in an optimal way, in combination with Facelets. This chapter gives us enough information to build fully-functional JSF pages by using Tomahawk components.
Chapter 5, Trinidad—the Basics, is the first of three chapters covering MyFaces Trinidad. This chapter introduces a lot of Trinidad components, including the data input and output components. Special attention is given to the many layout components that are available in the Trinidad library. As with Tomahawk, we will see how we can get the most out of the combination of Trinidad and Facelets.
Chapter 6, Advanced Trinidad, introduces some more advanced features of the Trinidad library. This includes the charting component that can be used to easily create nice looking charts. Also, Trinidad’s page fl ow scope feature, which enables us to create page flows more easily, is introduced. This chapter also discusses the AJAX or Partial Page Rendering capabilities of Trinidad, including client-side validation and conversion. The Trinidad dialog framework is also covered.
Chapter 7, Trinidad Skinning and Tuning, is an introduction to the advanced skinning framework that is a part of Trinidad. This chapter also discusses the most important tuning parameters of Trinidad.
Chapter 8, Integrating with the Backend, discusses how we can integrate the frontend that we created with some backend system, in a standard way. This chapter gives us some basic knowledge about the Model-View-Controller architecture, and about important standards such as Enterprise Java Beans (EJB) and the Java Persistence API (JPA). We will use the knowledge and examples from this chapter as a starting point for the more advanced integration topics discussed in the subsequent chapters.
Chapter 9, MyFaces Orchestra, introduces the MyFaces Orchestra library. This chapter starts with a very brief introduction to the Spring framework, as Orchestra is based on parts of that framework. We see how we can create a Spring application context and then how we should use such a context in combination with Orchestra. Some key concepts of Orchestra are introduced, such as the Orchestra ViewController concept and the concept of conversations. This chapter concludes with a quick view of Orchestra’s DynaForm component.
Chapter 10, Extensions Validator, is about one of the latest additions to the MyFaces project: the Extensions Validator, or ExtVal for short. This chapter starts by teaching us how to configure our project to use ExtVal. We see how JPA annotations can be used to automatically generate JSF validations. This chapter also shows us the extra annotations that ExtVal offers to complement the JPA annotations. This chapter also shows how we can use Bean Validation (JSR 303) annotations as an alternative to JPA and ExtVal annotations. As a whole, this chapter is a good introduction to this very fl exible and versatile member of the MyFaces family.
Chapter 11, Best Practices, is the last chapter of this book. It discusses some best practices with JSF in general and MyFaces in particular. This chapter describes a way to prevent direct access to page definitions, as well as a way to enable container-based security in our JSF application. This chapter also shows how to create a login page by using JSF components, and discusses how to use component bindings wisely. This Chapter concludes by discussing how to save the state of request-scoped components in an elegant way.
Appendix A, XHTML Entities, lists all of the numeric entities that can be used in XML documents. This list may be needed because Facelets files must be valid, plain XML files, and can’t contain named entities that can be used in normal XHTML files.
Appendix B, Trinidad Tags, gives a list of all of the tags from the Trinidad library. This can be referred to if you forget the exact name of one of the many tags. It can also be used to determine if a certain Trinidad tag is a naming container.
Appendix C, Trinidad Text Keys, lists the keys that Trinidad uses to lookup the default texts that are displayed on components. These keys can be used to customize or translate the default texts.
Appendix D, Default JSF Error Messages, gives an overview of the default error messages that are defined in the JSF standard. This list can be used if you want to customize or translate the default messages. This may also be useful in combination with the MyFaces Extensions Validator.
Appendix E, ExtVal Default Error Messages, contains a list of the default error messages that are defined by the Extensions Validator for use with its custom validation annotations. This can be useful if you want to override the default messages.
Extensions Validator
A common problem with the Model-View-Controller pattern (MVC) is that often the Don’t Repeat Yourself (DRY) principle is violated when it comes to validation of data. The “single source of truth” with regards to validation is often either the Model layer or the underlying database. But to be able to give the user usable, easy-to-understand error messages, and to give those in a timely manner, we often need to repeat a lot of validation in the View layer.
This often leads to inconsistencies in applications when the validation code in the Model and View layers gets out of sync. This can happen because of a changed business rule that is implemented in the Model, but the View is not updated accordingly. Or if the View is redesigned, unintentional changes in the validation can occur. Even when Model and View are created at the same time but by different engineers, crippled communication between those engineers can lead to validation code that is out of sync.
Repeating validation logic in the View layer also breaks the DRY principle at another level. Often, information from a certain bean can be edited in different pages in the user interface. This means that the validation has to be repeated in all of those UI pages. So we can end up repeating the same validation logic in different pages.
Wouldn’t it be better if we didn’t have to repeat our validation code in the View layer while keeping usable error messages, and having the validation still taking place on the client side? This is the main reason that the “Extensions Validator” project was added to MyFaces. The word “Extensions” refers to the fact that this project is not about JSF components, but rather has to be seen as an extension to the JSF Framework. The idea is that more projects can be added in the future, as further extensions. However, for now, Validator is the only project under the “MyFaces Extensions” umbrella that has released any software yet. As “Extensions Validator” is a long name, the project is most of the time referred to as “ExtVal”. We’ll use this short name throughout this chapter.
After reading this chapter, you will be able to:
- Set up a project to use ExtVal
- Use ExtVal to generate validation based on JPA annotations
- Use ExtVal’s added annotations for additional validation
- Implement cross validation using ExtVal’s annotations
- Use ExtVal with custom JSF validators
- Create custom error messages for ExtVal validations
- Override and extend ExtVal’s default behavior
- Use Bean Validation (JSR 303) annotations in combination with ExtVal
- Use metadata to set the severity level of constraints
Setting up ExtVal
As with all other libraries, we start by downloading ExtVal and installing it in our project. As with many other JSF libraries, the ExtVal project has different branches for JSF 1.1 and 1.2. The first two digits of ExtVal’s version number are the JSF version they are made for. So ExtVal 1.1.x is the xth version of ExtVal for JSF 1.1, whereas ExtVal 1.2.x is the xth version for JSF 1.2. Versions of ExtVal are not released very often. At the time of writing this book, only two official releases have been published for each branch. According to the lead developer of ExtVal, a third release (1.1.3 and 1.2.3) is in the works for both branches, as well as a first release from the new JSF 2.0 branch.
Apart from stable releases, ExtVal offers snapshot builds that are created on a regular basis. The snapshots are created manually, which gives some guarantees about the quality compared to automatically-created daily releases. No snapshots with major bugs will be created. According to the lead developer of ExtVal, the snapshot builds have “milestone quality”.
Because of some issues and limitations in ExtVal 1.2.2, a snapshot build of ExtVal 1.2.3 was used while writing this chapter. A stable release of ExtVal 1.2.3 is expected to be available soon after the publishing date of this book. Stable releases can be downloaded from the ExtVal download site at http://myfaces.apache.org/extensions/validator/download.html. The downloaded ZIP file will contain all of the ExtVal modules, as listed in the next table. Note that more modules may be added to ExtVal in future releases. It is also possible that additional support modules will be provided by others. For example, a JSF component project may create a support module to get the most out of its components with ExtVal.
Regarding component support modules, it is also worth mentioning the “Sandbox 890” project, which provides proof of concept implementations of support modules for some non-MyFaces component libraries. Currently, proofs of concept are available for IceFaces, PrimeFaces, RichFaces, and OpenFaces.
The source code for the proofs of concept can be found at http://code.google.com/p/sandbox890/source/browse/#svn/trunk/component-support.
Ready-to-use JARs can be downloaded from http://code.google.com/p/os890-m2-repository/source/browse/#svn/trunk/sandbox890/sandbox890/extensions/validator/
component-support-modules.
Snapshot builds of ExtVal can be downloaded from ExtVal’s Maven snapshot repository, which can be found at http://people.apache.org/maven-snapshotrepository/
org/apache/myfaces/extensions/validator/. In the case of snapshot builds, no single ZIP file is available, and each module has to be downloaded separately as a JAR file. Note that if Maven is used, there is no need to manually download the snapshots. In that case, we only have to change the version number in the pom.xml file to a snapshot version number, and Maven will automatically download the latest snapshot. The following table lists the URLs within the Maven repository from where the modules can be downloaded:
URLs in this table are relative to the URL of the Maven repository that we just saw. After each URL, 1.2.x-SNAPSHOT/ has to be appended, where 1.2.x has to be replaced by the appropriate version number.
Once we’ve finished downloading, we can start adding the JARs to our project. ExtVal differs in one thing from other libraries—it needs to access our Model and View project. So we have to add the ExtVal libraries to the lib directory of the EAR, instead of the WAR or the JAR with the entities. Some libraries that ExtVal uses have to be moved there as well. If we don’t do this, we’ll end up with all sorts of weird exceptions related to class-loading errors.?
Libraries that are added to the lib directory of an EAR are automatically available to all contained WAR and JAR files. However, depending on the IDE and build system that we are using, we may have to take some additional steps to be able to build the WAR and JAR with dependencies to the libraries in the EAR’s lib directory.
This image shows a simplified structure of the EAR with ExtVal’s libraries added to it. Note that the MyFaces ExtVal and dependencies node in the image actually represents multiple JAR files. It is important to verify that none of the libraries that are in the lib directory of the EAR are included in either the WAR or the
entities JAR. Otherwise, we could still encounter class-loading confl icts. The following table lists all of the libraries that have to be moved into the EAR to avoid these class-loading confl icts:
Note that the aforementioned changes in our project structure are necessary only because we chose to have our Model layer in a separate JAR file. In smaller projects, it is often the case that the whole project is deployed as a single WAR file without enclosing it in an EAR. If we had chosen that strategy, no changes to the structure would have been necessary and we could have added all of the libraries to the WAR file, as we would do with any other library.
Other than including the necessary libraries as discussed before, no configuration is needed to get started with ExtVal. ExtVal uses the convention over configuration pattern extensively. That means, a lot of sensible defaults are chosen, and as long as we’re satisfied with the defaults, no configuration is needed. The next section will get us started with some basic ExtVal usage.
Bug with Trinidad tables
There’s a bug in ExtVal that can cause some weird behavior in Trinidad’s
jira/browse/EXTVAL-77. There’s a workaround for the bug that we can use until it gets fixed. Be warned that this workaround may have other side effects. This workaround is shown in the following code snippet, in which we have created a class called DevStartupListener:
public class DevStartupListener extends AbstractStartupListener { @Override protected void init() { ExtValContext.getContext() .addGlobalProperty(ExtValRendererProxy.KEY, null, true); } } The required imports are in the org.apache.myfaces.extensions. validator.core package and subpackages. Register this class as a phase listener in the faces-config.xml file: <lifecycle> <phase-listener> inc.monsters.mias.workaround.DevStartupListener </phase-listener> </lifecycle> You’re all set, and the <tr:table> will now act as expected. Don’t forget to remove this workaround if the bug gets fixed in a future release of ExtVal.
Basic usage
After setting up ExtVal, the basic usage is very simple. Let’s explore a simple example in our MIAS application. In our Kid.java entity, we have some JPA annotations that map the properties of the Kid bean to a database table. Let’s take a closer look at the lastName property of our Kid bean:
@Column(name = "LAST_NAME", nullable = false, length = 30) private String lastName;
The @Column annotation maps the lastName property to the LAST_NAME column in the database. It also shows some information that is derived from the table definition in the database. nullable = false means the database won’t accept an empty value in this field, and length = 30 means that no more than 30 characters can be stored in
the corresponding database column. This information could be used for validation in our View layer. If we hadn’t used ExtVal, we would have added a required=”true” attribute to the input element in our EditKid.xhtml page. We also would have added a component to the input component, or we could have set the maximumLength attribute. But all of these things would have been a duplication of information and logic, and would thus break the DRY principle.
With ExtVal, we don’t have to duplicate this information anymore. Whenever ExtVal encounters a nullable = false setting, it will automatically add a required=”true” attribute to the corresponding input element. In the same way, it will translate the length = 30 from the @Column annotation into a maximumLength attribute on the input component. The next screenshot shows ExtVal in action. (Note that all validators, and the required and maximumLength attributes were removed from the JSF code before the screenshot was taken.) The really nice thing about this example is that the validations created by ExtVal make use of Trinidad’s client-side validation capabilities. In other words, the error message is created within the user’s web browser before any input is sent to the server.
Complementing JPA annotations
It’s nice that we can reuse our JPA annotations for validation. But the chances are that not all validation that we want can be expressed in JPA annotations. For that reason, ExtVal offers a set of extra annotations that we can add to our beans to complement the implicit validation constraints that ExtVal derives from JPA annotations. These annotations are a part of the myfaces-extval-propertyvalidation-
1.2.x.jar library. For example, if we want to add a minimum
length to the lastName field, we could use the @Length annotation as follows:
<B>@Length(minimum = 5)</B> @Column(name = "LAST_NAME", nullable = false, length = 30) private String lastName;
Note that if, for some reason, we couldn’t use the length = 30 setting on the @Column annotation, the @Length annotation also has a maximum property that can be set. The @Length annotation can be imported from the org.apache.myfaces. extensions.validator.baseval.annotation package, which is where the
other annotations that ExtVal offers are also located. The following image shows
the minimum length validation in action:
As the example in the screenshot shows, setting a minimum input length of five characters for a name might not be a good idea. However, that’s an entirely different discussion.
Using ExtVal annotations for standard JSF validators
For each of the validator components that is a part of the JSF standard, there is an annotation in ExtVal’s Property Validation library. These are covered briefl y in the following subsections.
Note that ExtVal will automatically use any overridden versions of the standard validators, if they are present in the project.
Defining length validation
For the length validation of input strings, the @Length annotation can be used, as shown in the previous example. This annotation relies on the javax.faces. validator.LengthValidator to implement the validation. The following table lists the available properties:
Defining double range validation
To validate if a double value is within a certain range, the @DoubleRange annotation can be used, which delegates the implementation of the validation to the javax. faces.validator.DoubleRangeValidator validator. See the following table for the available properties:
Defining long range validation
What @DoubleRange annotation does for doubles, the @LongRange annotation does for long values. It uses javax.faces.validator.LongRangeValidator for the implementation:
Defining required fields
The example given at the beginning of this section showed how ExtVal can create a required=”true” attribute based on an @Column annotation with the nullable =false setting. If it is not possible to use this setting, ExtVal also has an alternative @Required annotation . Just add this annotation to a field to make it required.
Using ExtVal’s additional annotations
Apart from the annotations that correspond to the standard JSF validators, some additional annotations exist in the Property Validation module that perform other validations. These are listed in the following subsections.
Whereas the validations based on the JSF standard validators use the error messages provided by the JSF validators, the additional validations cannot use standard messages from JSF. Therefore, standard messages are provided by ExtVal. Should you want to use your own message, all additional annotations have a
validationErrorMsgKey property that can be used to assign a message key for the error message. We’ll discuss custom error messages in more detail later in this chapter.
Defining pattern-based validation
Validation with regular expressions is very powerful, and is very usable for validating phone numbers, postal codes, tax numbers, and any other data that has to fit in a certain pattern. Regular expression-based validation can be added by using the @Pattern annotation. For example, to allow only letters and spaces in the
firstName field, we could write:
<B>@Pattern(value="[A-Za-z ]*")</B> @Column(name = "FIRST_NAME", nullable = false, length = 30) private String firstName;
For completeness, the following table lists the arguments of the
@Pattern annotation:
Using custom validators
ExtVal also allows us to use any custom JSF validator that implements the javax.faces.validator.Validator interface. For this purpose, we can use the @Validator annotation. Let’s have a look at another example. Assume that we would allow kids to have all sorts of first names, except “Bart”. We could write a fairly simple JSF validator for that like this:
package inc.monsters.mias.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; public class NoBartValidator implements Validator { public FirstNameValidator() { } public void validate(FacesContext ctx, UIComponent comp, Object value) throws ValidatorException { if("Bart".equals(value)) { FacesMessage fm = new FacesMessage( "First name Bart is not allowed."); throw new ValidatorException(fm); } } }
Normally, we would put the NoBartValidator.java file in our web project, eventually resulting in a NoBartValidator.class in our WAR file. As we’re going to use this class in an ExtVal annotation, which is in our Entities JAR, we have to put the validator class there as well.
Instead of putting our custom validator class in our Entities JAR as proposed, creating a separate validation module into which we can place our custom validator(s) is perhaps a better idea. This gives us the chance to easily reuse our custom validator(s) in other projects. In this case, we put our custom validator(s) in a separate JAR and add that JAR to our collection of libraries.
The custom validator does not have to be configured in the faces-config.xml file as is normally the case with JSF validators. ExtVal will add the custom validator at runtime, when needed.
Now that we have prepared our custom validator, we can use it with the @Validator annotation in our entities. For example, in the Kid entity:
package inc.monsters.mias.data; <B>import inc.monsters.mias.validators.NoBartValidator;</B> ... public class Kid implements Serializable { ... @Column(name = "FIRST_NAME") <B>@Validator(NoBartValidator.class)</B> private String firstName; ... }
Note that we have to import the NoBartValidator class, because it is referenced in the @Validator annotation in a typesafe way.
Reusing validation
If multiple fields share the same validation rules, then according to the DRY principle, we should not repeat these validations for all of the fields. For this reason, the @JoinValidation annotation exists. As a simple example, we could reuse the validation rules of the lastName field for the firstName field, as shown here:
@Column(name = "FIRST_NAME") <B>@JoinValidation(value="lastName")</B> private String firstName; @Length(minimum = 5) @Column(name = "LAST_NAME", nullable = false, length = 30) private String lastName;
In this example, all validation of the lastName field will be copied to the firstName field by ExtVal.
Although it is nice to have a way to prevent the repetition of the same validation, care should be taken when using the @JoinValidation annotation. The example that we just saw makes sense, as we can say that first names and last names are both names, and for that reason can share their validation. But sharing of validation between semantically unrelated fields can lead to messy code. As a rule of thumb, validation should only be shared if it makes sense semantically.
The @JoinValidation annotation can also be used to reuse validations in other beans. To do so, there are multiple ways to refer to the property in another bean, as follows:
- Property chain reference: If the other bean has a relationship to the current one, a “property chain” can be constructed to reach the property that we want to refer to. For example, if our Kid bean had a parent property (and a getParent()method) that referred to a Parent bean, which also has a lastName property, that property could be reached by the property chain parent.lastName. Therefore, we could write @JoinValidation(value=”parent.lastName”).
There are a lot of reasons why a property chain might not be possible. The bean to reference might not have any relation to the bean where the validation is to be reused, or the relationship might be “one to many”—for example, a Manager bean that has a List of Employees. For that reason, two alternatives to the property chain exist, as listed next. - Expression language reference: Assume that there is a managed bean (or Spring bean) in the View project, with the name employee. In this case, we could write—@JoinValidation(value=”#{employee.lastName}”).
We should realize that Expression Language reference breaks the separation of concerns of the Model-View-Controller (MVC) pattern. By using a JSF EL expression in the Model layer, the Model layer now has a detailed knowledge of the (implementation of) the View layer. In this example, this means that if the name of the employee bean changes in the future, the reference to it in the Model layer will have to be updated too. And if the same Model is to be used
with different View layers, you’ll have to make sure that all of the View layers have an employee bean. For that reason, the use of Expression Language reference should be avoided. - Static reference: To overcome the problems of Expression Language reference, static reference can be used as an alternative. With static reference, a property is referenced by the fully-qualified name of its parent class, followed by the property name, separated by a colon. So to reference the lastName property of Employee bean, the complete annotation could look as follows: @JoinValidation(value=”inc.monsters.mias.data.Employee:lastName”). Note that static reference is not available in the second release of ExtVal. So as long as there is no stable third release, we have to use a snapshot build of ExtVal 1.2.3 to be able to use static reference.
To summarize, the easiest way to reference another validation for reuse is, of course, local reference; but that’s only possible for properties within the same bean. To refer to a property outside the current bean, property chain reference is a clean and easy
solution in some cases, but it can’t be used in all cases. For the cases where local or property chain reference can’t be used, static reference is the best choice. Expression Language reference could be used as an alternative, but it comes at the price of breaking the MVC separation of concerns—a reason to avoid it if possible.
Applying cross validation
Cross validation is the validation of one value against another value that is entered by the user, or a value that is already somewhere in the Model. An example in our MIAS application could be that the “last scared” date for a kid can never be before the birth date of that kid. This type of validation is often hard to implement,
especially because JSF doesn’t offer validators for this. ExtVal offers a set of annotations that lets us define cross validations easily, and in an elegant way. For example, to implement the constraint on the “last scared” date just described, we simply add a single annotation to the lastScared property:
@Temporal(TemporalType.DATE) @Column(name = "LAST_SCARED") <B>@DateIs(type = DateIsType.after, valueOf = "birthDate")</B> private Date lastScared;
Now it won’t be possible to add a scare date that is before the birth date. The following subsections cover all of the cross validations that are available “out of the box” in ExtVal.
As with the @JoinValidation annotation, cross validation needs a mechanism to refer to other properties that take part in the cross validation. ExtVal cross validation annotations use the same referencing mechanisms that @JoinValidation uses. However, static references do not make sense for cross validation, so the static reference option is not available for the cross validation annotations.
Using cross validation for date values
Cross validation for dates is possible with the @DateIs annotation. The most important arguments for @DateIs are listed in the following table:
Using cross validation based on equality
Two annotations exist to check if two values are equal or not—@Equals and @NotEquals. The attributes for both annotations are the same, as listed in the following table:
As an example, the @Equals annotation could be used to verify a “retype password” field:
<B>@Equals(value = "password", validationErrorMsgKey = "retype_password")</B> private String retypePassword;
Making a value required conditionally
Another form of cross validation is making a field required only if one or more other fields are not empty. This can easily be accomplished with the @RequiredIf annotation. The arguments of the @RequiredIf annotation are shown in the following table:
This section discussed the annotations that are a part of ExtVal’s Property Validation module. From version *.*.3 onwards, ExtVal also offers a Bean Validation module that allows us to use annotations from the Bean Validation (JSR 303) standard, instead of, or in combination with, ExtVal’s own annotations. See the Using Bean Validation section for more information. In the following section, we will see how we can customize all of the error messages generated by ExtVal.
Creating custom error messages
With ExtVal, the error messages shown if validation fails come from different sources, as follows:
- For validation that is derived from JPA annotations, ExtVal relies on the standard JSF validators. Hence, the error messages shown are the standard JSF error messages. The way in which standard JSF messages can be overridden is defined in the JSF standard. This is covered in the next section,
Overriding standard JSF error messages. - The ExtVal annotations @Length, @DoubleRange, @LongRange, and @Required also rely on standard JSF mechanisms for implementing the validation. So these will lead to standard JSF error messages as well.
- All other ExtVal annotations have their own default error messages. How to override these ExtVal messages is covered in the Overriding ExtVal default error messages section.
Overriding standard JSF error messages
Although overriding standard JSF messages is not a feature of ExtVal, we cover it briefl y here for convenience. Standard JSF error messages can be overridden by configuring a message bundle in the faces-config.xml file, and adding certain key/value pairs to that message bundle. In our MIAS application, we’ve configured a message bundle as follows:
<message-bundle>inc.monsters.mias.Messages</message-bundle>
This means that the JSF framework expects a file called Messages.properties to be present in the inc/monsters/mias directory. In that file, we can configure our custom messages. For example, to override the default message for required fields that are left empty, we could add the following to the file:
javax.faces.component.UIInput.REQUIRED = Hey dude, this field is required!
The important thing here is the key—javax.faces.component.UIInput.REQUIRED. A list of all JSF error messages with their keys can be found in Appendix D. This appendix also shows the placeholders that can be used in the message texts. The placeholders will be replaced by the label of the input element that the message is
related to, and examples of good values or maximum and minimum values where applicable.
Overriding ExtVal default error messages
ExtVal always looks in a fixed location for a message bundle; it doesn’t care about the JSF message bundle configuration. To change a message, we can either put a message bundle in that default location, or we can tell ExtVal to look for the message bundle in another location. The default message bundle that ExtVal looks for is validation_messages in the org.apache.myfaces.extensions.validator. custom package. Of course, we could create that package within our application and put a validation_messages.properties file there. But wouldn’t it be great if we could just use our application-wide message bundle? That’s possible by telling ExtVal to look somewhere else for a message bundle. This is done by setting a context parameter in the web.xml file as follows:
<context-param> <param-name> org.apache.myfaces.extensions.validator.CUSTOM_MESSAGE_BUNDLE </param-name> <param-value>inc.monsters.mias.Messages</param-value> </context-param>
Now we can put custom messages in our own Messages.properties file. To override the default error message for the @Pattern annotation, we could add:
no_match = Pattern not matched
But in this case, “pattern not matched” might be a bit too generic as an error message for end users. ExtVal lets us override the error message on a per-field basis, allowing us to define more specific error messages. For example, we could have a firstName field with a pattern that allows only letters. Now we would like to have a message saying that only letters are allowed in names. In that case, we could write:
@Column(name = "FIRST_NAME") @Pattern(value="[A-Za-z]*" <B>validationErrorMsgKey="name_characters")</B> private String firstName;
Now if we add a name_characters key to our message bundle, we can set our customized, field-specific message:
name_characters = A name may only contain letters
In case we want to override the default ExtVal messages, a list of the default messages and their keys can be found in Appendix E.
Alternative options for customized messages
The options that we just saw are not the only options that ExtVal offers for creating custom error messages. However, the presented options are very useful in most cases and allow us to use a single message bundle for
the whole application. However, there may be reasons why we need other ways to customize the messages. For example, if we were creating a standalone validation module for reuse in more than one project, setting a context parameter in the web.xml file is not an option. In that case, implementing our own MessageResolver is probably a good idea. This is fairly simple to do by just subclassing the DefaultValidationErrorMessageResolver
class from the org.apache.myfaces.extensions.validator. core.validation.message.resolver package. Only the
getCustomBaseName() method needs to be overridden, and this should return the fully-classified name of the message bundle. ExtVal offers even more alternatives. Should you want to look for other ways to provide custom messages, the ExtVal wikiis a good place to start: http://wiki.apache.org/ myfaces/Extensions/Validator/ Getting_Started/Custom_Messages.
The next section discusses how we can extend ExtVal by creating our own custom
validation strategy.
Creating our own validation strategy
The ExtVal framework is very extensible, and extending it is fairly simple. The framework uses the convention over configuration paradigm. This means that if we’re happy with the conventions of the framework, we don’t have to configure anything. As an example of the extensibility of ExtVal, in this section we’re going to change the default behavior of ExtVal’s @Pattern annotation.
The @Pattern annotation accepts an array of Strings for the value argument. This means that more than one regular expression can be used to validate the input. By default, all regular expressions have to be matched in order for an input string to be valid. For example, if the patterns [A-Z].\\S* and [A-Za-z]* are combined, this
effectively means that only words starting with a capital letter and containing only the characters a through to z, which may or may not be in capitals, are allowed. Note that this can be achieved with one single expression too—[A-Z].[A-Za-z]*.
Although combining two regular expressions with an “and” relation might be useful sometimes, having multiple expressions where only one of them has to be matched can be quite powerful too. We can think of a list of patterns for various (international) phone number formats. The input would be valid if one of the patterns is matched. The same can be done for postal codes, social security codes, and so on. So let’s see how we can change the behavior of ExtVal to achieve this.
Implementing a custom validation strategy
ExtVal uses the concept of Validation Strategy for every type of validation. So, if an @Pattern annotation is used, ExtVal will use a PatternStrategy to execute the validation. We can implement our own ValidationStrategy to override the functionality of ExtVal’s standard PatternStrategy. The easiest way to do this is to create a subclass of AbstractAnnotationValidationStrategy:
package inc.monsters.mias.extval; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.ValidatorException; import org.apache.myfaces.extensions.validator.baseval .annotation.Pattern; import org.apache.myfaces.extensions.validator.core .metadata.MetaDataEntry; import org.apache.myfaces.extensions.validator.core .validation.strategy .AbstractAnnotationValidationStrategy; public class PatternOrValidationStrategy extends AbstractAnnotationValidationStrategy<Pattern> { @Override protected String getValidationErrorMsgKey(Pattern annotation) { return annotation.validationErrorMsgKey(); } @Override protected void processValidation( FacesContext facesContext, UIComponent uiComponent, MetaDataEntry metaDataEntry, Object convertedObject) throws ValidatorException { Pattern annotation = metaDataEntry.getValue(Pattern.class); boolean matched = false; String expressions = null; for (String expression : annotation.value()) { if (convertedObject != null && java.util.regex.Pattern.compile(expression) .matcher(convertedObject.toString()).matches()) { matched = true; break; } else { if (expressions == null) { expressions = expression; } else { expressions += ", " + expression; } } } if(!matched) { FacesMessage fm = new FacesMessage( FacesMessage.SEVERITY_ERROR, getErrorMessageSummary(annotation), getErrorMessageDetail(annotation) .replace("{0}",expressions)) throw new ValidatorException(fm); } } }
The most important part of this class is, of course, the processValidation() method. This uses the MetaDataEntry object to access the annotation that defines the validation. By calling annotation.value(), the array of Strings that was set in the @Pattern annotation’s value attribute is obtained. By iterating over that array, the user input (convertedObject.toString()) is matched against each of the patterns. If one of the patterns matches the input, the boolean variable matched is set to true and the iteration is stopped. A ValidatorException is thrown if none of the patterns matches the input. The else branch of the outer if statement is used to create a list of patterns that didn’t match. That list is appended to the error message
if none of the patterns matches.
Now that we’ve created our own custom validation strategy, we will have to tell ExtVal to use that instead of the default strategy for the @Pattern annotation. The next section shows how to do that.
Configuring ExtVal to use a custom validation strategy
The most straightforward way to configure a custom Validation Strategy in ExtVal is to write a custom Startup Listener that will add our Validation Strategy to the ExtVal configuration. A Startup Listener is just a JSF PhaseListener with some specific ExtVal functionality—it deregisters itself after being executed, thus guaranteeing that it will be executed only once. We can simply subclass ExtVal’s
AbstractStartupListener. That way, we don’t have to implement much ourselves:
package inc.monsters.mias.extval; import org.apache.myfaces.extensions.validator .baseval.annotation.Pattern; import org.apache.myfaces.extensions.validator .core.ExtValContext; import org.apache.myfaces.extensions.validator .core.initializer .configuration.StaticConfigurationNames; import org.apache.myfaces.extensions.validator .core.initializer .configuration.StaticInMemoryConfiguration; import org.apache.myfaces.extensions.validator .core.startup.AbstractStartupListener; public class PatternOrStartupListener extends AbstractStartupListener { @Override protected void init() { // 1. StaticInMemoryConfiguration config = new StaticInMemoryConfiguration(); // 2. config.addMapping( Pattern.class.getName(), PatternOrValidationStrategy.class.getName()); // 3. ExtValContext.getContext().addStaticConfiguration( StaticConfigurationNames .META_DATA_TO_VALIDATION_STRATEGY_CONFIG, config); } }
There are three important steps here, which tie up with the numbers in the previous code:
- Create a new StaticInMemoryConfiguration object.
- Add a mapping to the StaticInMemoryConfiguration object. This maps the @Pattern annotation to our own PatternOrValidationStrategy validation strategy implementation. The addMapping() method expects two Strings,
each containing a fully qualified class name. The safest way to get these class names is to use class.getName() because that will work even if the class is moved to another package. This step actually maps the @Pattern annotation to the PatternOrValidationStrategy. - The created StaticInMemoryConfiguration object has to be added to the ExtValContext to become effective.
Note that we don’t implement the usual PhaseListener methods here. They are already implemented by the AbstractStartupListener. The last thing that we have to do is to add this Startup Listener to our faces-config.xml file as an ordinary PhaseListener, as follows:
<phase-listener> inc.monsters.mias.extval.PatternOrStartupListener </phase-listener>
Mapping an annotation to a validation strategy is one of the many name mappings that are performed inside the ExtVal framework. ExtVal implements its own NameMapper mechanism for all mappings that are performed inside the framework. As with nearly every part of ExtVal, this NameMapper mechanism can be overridden if desired. See also the Extending ExtVal in many other ways section.
Using alternative configuration add-ons
Although implementing a custom Startup Listener is fairly simple, it might not be the most ideal way to configure ExtVal—especially if a lot of configuration changes have to be made. The author of ExtVal has created two add-ons for ExtVal that provide alternative ways to configure ExtVal. Those add-ons are not apart of the
ExtVal project. The author of ExtVal provides them as examples, but no support is available for them. If they fit your needs, you can use them. If not, you can use them as a starting point to implement your own configuration add-on.
- One alternative is the annotation-based configuration. In this case, custom implementations can be annotated with special annotations, and should be put in a special base package. At application startup, the base package will be scanned for annotations, and the found annotations will be used to create the necessary configuration. See the Extending ExtVal with add-ons section for the download location, and installation instructions for this add-on. Some basic usage documentation is provided at http://os890.blogspot.
com/2008/10/myfaces-extval-config-extension.html. - The other alternative way to configure ExtVal is to use Java in a way that is inspired by the way Google Guice does this sort of things. In this case, a custom startup listener has to be created in which the Google Guice style code can be executed. Basic usage information can be found at http://os890.blogspot.com/2009/09/myfaces-extval-java-configextension.html. See the Extending ExtVal with add-ons section for the download location and installation instructions.
Testing the custom validation strategy
Now that we’ve implemented our custom Validation Strategy, let’s do a simple test. For example, we could add the @Pattern annotation to the firstName property of the Kid class, as follows:
@Column(name = "FIRST_NAME") <B>@Pattern(value={"[A-Za-z]*", "[0-9]*"})</B> private String firstName;
In this case, “Shirley” would be valid input, as would be “4623”. But “Shirley7” wouldn’t be valid, as none of the regular expressions allow both letters and digits. If we had used the default PatternStrategy, no valid input for the firstName field would be possible, as the regular expressions in this example exclude each other.
Of course this test case is not very useful. As mentioned before, having different patterns where only one of them has to be matched can be very useful for different (international) phone number formats, postal codes, social security codes, and so on. The example here is kept simple in order to make it easy to understand what input will match and what input won’t match.
Extending ExtVal in many other ways
Implementing a custom Validation Strategy is just one example of the many concepts in ExtVal that can be overridden by implementing a custom subclass, albeit one of the most useful ones. Here’s a list of other concepts in ExtVal that can be overridden:
- StartupListener can be used to perform various actions at startup, such as registering any overridden ExtVal class. See the example in the Configuring ExtVal to use a custom Validation Strategy section.
- ValidationStrategy can be used to customize the validation behavior, as discussed in the previous section. The easiest way to implement this interface is to subclass one of the abstract classes provided by ExtVal.
- MessageResolver can be used to customize the error messages.
- ComponentInitializer allows the initialization of components before they are rendered. This can be used, for example, to add special client-side validation behavior to components.
- MetaDataTransformer transforms constraints to an independent format so a component initializer doesn’t have any knowledge about the annotation used. A detailed explanation of this mechanism can be found in the Empower the Client section of an article about ExtVal on JSF Central, at http://jsfcentral.com/articles/myfaces_extval_3.html.
- MetaDataExtractionInterceptor allows on-the-fl y manipulation of metadata.
- InformationProviderBean makes it possible to customize name conventions.
- ProcessedInformationRecorder can be used to capture values after they are converted by JSF. For example, the ExtVals implementation of cross validation is based on this mechanism.
- RendererInterceptor is one of the base mechanisms of ExtVal that is used to intercept renderers. All methods of javax.faces.render.Renderer can be intercepted.
- NameMapper is used extensively throughout the ExtVal framework, in order to map sources to targets. In most cases, names are mapped; for example, annotation names are mapped to validation strategy names.
- ExtVal makes extensive use of the Factory design pattern, and comes with a lot of factories that can be used when extending ExtVal. It is also possible to override the default factories as a way of changing ExtVal’s behavior. An overview of all of the factories can be found in the org.apache.myfaces.extensions.validator.core.factory.FactoryNames class in the ExtVal sources.
This list can be used as a starting point for exploring the extension opportunities in ExtVal. Some more information can be found on the ExtVal wikiat http://wiki.apache.org/myfaces/Extensions/Validator/DevDoc and http://wiki.apache.org/myfaces/Extensions/Validator/ConceptOverview.
Extending ExtVal with add-ons
ExtVal is a very fl exible framework that was built with the possibility to extend it in mind. As we saw in the previous section, the ExtVal framework is full of hooks that can be a starting point for extending it. Of course, because it is an open source framework, anyone has the opportunity to extend and modify the framework to fit his needs. This can be a challenging job, even for advanced programmers. Everyone who has the time and knowledge should be encouraged to do so, as they can help in improving and expanding the ExtVal framework, or any other part of MyFaces.
However, this section will focus on an easier way to expand the possibilities of the ExtVal framework: by using add-ons.
Getting add-ons for ExtVal
As ExtVal is a relatively new project and is not yet widely used, there are currently no “third party” open source add-ons for ExtVal. However, the lead developer of the ExtVal project has created some very useful add-ons. The following tables give an overview of the ExtVal add-ons that are available at the time of writing of this chapter. Keep an eye on the weblog of ExtVal’s lead developer for the latest news about add-ons—http://os890.blogspot.com/.
Note that all of the add-ons mentioned here are hosted on the “os890” Maven repository. Two different root paths are used for the add-ons. The path starting with http://os890-m2-repository.googlecode.com/svn/trunk/os890 is the location for add-ons that work only with snapshot releases of ExtVal. Once a new stable version of ExtVal is released, we can expect these plugins to move to the other path in the Maven repository where all plugins that work with the stable versions of ExtVal are located, which is the URL starting with http://os890-m2-repository. googlecode.com/svn/tags/os890. If we were using Maven, we could have used these two URLs as Maven repositories and let Maven download the add-ons and install them.
More cutting-edge add-ons for adventurous people
As stated above, the add-ons listed here are all available via the “os890” Maven repository. Should you want to look for even more add-ons, and if you are prepared to compile them from the source code yourself, the source code repository of the “os890” project is an interesting place to visit. You can find it at http://code.google.com/p/os890/ source/browse/#svn/trunk/java/web/jsf/extval.
Installing ExtVal add-ons
Installing an ExtVal add-on is simple. We only have to add the downloaded JAR to the shared lib directory in which all other ExtVal JARs are located, in our EAR file. If we have an application that is deployed as a single WAR file, we could simply add the JARs to the deployed libraries of that WAR file. This is all there is to do to install an ExtVal add-on. Note that each add-on has its specific usage instructions. Refer to the documentation that is linked to in the tables in the previous section.
Using Bean Validation
Using annotations in JavaBeans is an elegant way of defining validation rules in a declarative way. Apart from MyFaces ExtVal there are other projects that introduced declarative validation, such as the Hibernate Validator and the Bean Validation Framework for Spring. Some framework developers realized that it would be a good idea to standardize this type of validation. This led to the Bean Validation specification that was developed as JSR 303 in the Java Community Process. Accordingly, Bean Validation will be a standard part of Java EE 6, but it can be used in Java EE 5 by manually including a Bean Validation implementation.
One of the benefits of having an official standard for validation is that various user interface frameworks can implement support for this type of validation. For example, JavaServer Faces 2.0 will have support for Bean Validation embedded in it, and other UI frameworks will probably follow.
But at the moment, we’re still building Java EE 5 and JSF 1.2 applications. And although we can use Bean Validation in Java EE 5, JSF 1.2 doesn’t have Bean Validation support. And that’s where ExtVal comes in. We can use ExtVal to integrate JSR 303 Bean Validation into JSF 1.2 (and even JSF 1.1) projects. This section will discuss some Bean Validation basics and show how to use Bean Validation with ExtVal.
Note that we can only cover some basics of Bean Validation here. As Bean Validation is a new standard, there is not much reference documentation available yet. However, some decent documentation comes bundled with Hibernate Validator—the reference implementation of JSR 303. That documentation is also available online at http://docs.jboss.org/hibernate/stable/validator/reference/. As an alternative, the official specification of JSR 303 can be used as documentation. The official specification can be found at http://jcp.org/en/jsr/
summary?id=303.
Setting up Bean Validation and ExtVal
To use Bean Validation, we need a JSR 303 implementation, unless we’re using a Java EE 6 compliant application server. Currently, the only available JSR 303 implementation is the reference implementation, which is Hibernate Validator 4.0. Hibernate Validator can be downloaded from https://www.hibernate.org/30.
html. We should make sure we download a 4.0 version, as versions before 4.0 do not implement the JSR 303 standard. At the time of writing this chapter, the latest release is 4.0.2 GA.
After downloading Hibernate Validator, we have to add the Bean Validation libraries to our project. As described in the Setting up ExtVal section at the beginning of this chapter, all libraries have to be in the shared lib directory of our EAR. We also have to add the libraries that Hibernate Validator depends on. The following table shows a list of libraries that have to be added to our project in order to be able to use the Hibernate Validator. If we had used Maven, these libraries would have been downloaded and added to our project automatically by Maven.
Once we have added the Bean Validation libraries to our project, we have to make sure that we have also added ExtVal’s Bean Validation module to our project. The Bean Validation module is only available from ExtVal version 1.2.3 onwards. See the Setting up ExtVal section for more details.
Using Bean Validation annotations
The basic usage of Bean Validation is very similar to the use of ExtVal’s Property Validation annotations. There are some differences in the annotations, though. The following table lists all of the annotations that are defined in the Bean Validation specification:
All annotations are defined in the javax.validation.constraints package. Apart from the attributes mentioned in the previous table, all annotations (except the @Valid annotation) have the following common attributes:
- message: This attribute can be used to set a custom error message that will be displayed if the constraint defined by the annotation is not met. If we want to set a message bundle key instead of a literal message, we should surround it with braces. So we can set message to either “This value is not valid” or “inc.monsters.mias.not_valid}”.
- groups: This attribute can be used to associate a constraint with one or more validation processing groups. Validation processing groups can be used to influence the order in which constraints get validated, or to validate a bean only partially. (See http://docs.jboss.org/hibernate/stable/validator/reference/en/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups for more onvalidation groups.)
- payload: This attribute can be used to attach extra meta information to a constraint. The Bean Validation standard does not define any standard metadata that can be used, but specific libraries can define their own
metadata. This mechanism can be used with ExtVal to add severity information to constraints, enabling the JSF pages to show certain constraint violations as warnings instead of errors. See the Using payloads to set severity levels section for an example of this.
OK, now we know which annotations can be used. Let’s see how we can use Bean Validation annotations on our Employee class:
// Package declaration and imports omitted for brevity public class Employee implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; @Temporal(TemporalType.DATE) @Column(name="BIRTH_DATE") <B>@Past</B> private Date birthDate; @Column(name="FIRST_NAME") private String firstName; @Temporal(TemporalType.DATE) @Column(name="HIRE_DATE") <B>@Past</B> private Date hireDate; @Column(name="JOB_TITLE") @NotNull <B>@Size(min=1)</B> private String jobTitle; @Column(name="LAST_NAME") private String lastName; <B>@Min(value=100)</B> private int salary; @Column(name="KIDS_SCARED") private int kidsScared; @OneToMany(mappedBy="employee") private List<Kid> kids; // Getters and setters and other code omitted. }
The Bean Validation annotations are highlighted in the code example. Note that the annotations are applied to the member variables here. Alternatively, we could have applied them to the getter methods. The JPA annotations that we added in Chapter 8 are still present. In this example, the birthDate and hireDate are annotated with
@Past so that only dates in the past can be set. The jobTitle is set to have a minimum length of one character by the @Size annotation. The salary must have a minimum value of 100, as set by the @Min annotation.
Reusing validation
Bean Validation does not have a solution like the @JoinValidation annotation of ExtVal’s Property Validation module. However, Bean Validation offers other ways to avoid repetitive code and help us reusing validation. This section describes some of the possibilities.
Inheriting validation
Constraints defined on (the properties of) super classes are inherited. This means that if we have a super class called Person, like the following example, our Employee class can inherit the properties—including the annotated constraints—as follows:
public class Person { @Size(min=1) private String firstName; @Size(min=1) private String lastName; @Past private Date birthDate; // Getters and setters omitted. }
No special actions have to be taken to inherit annotated validation constraints.
Using recursive validation
We can use the @Valid annotation to use recursive validation (or graph validation as it is called in the JSR 303 specification). The @Valid annotation can be used on single member objects as well as on Collections. If applied to a Collection, all objects in the collection are validated, but null values in the Collection are ignored. For example, we could use this to validate the List of scared Kids that is part of our Employee class, as follows:
public class Employee implements Serializable { // Other member variables are left out here. @OneToMany(mappedBy="employee") <B>@Valid</B> private List<Kid> kids; // Getters and setters are omitted. }
Now the List of Kids that is referenced by the kids variable can only contain valid Kid objects. This means that all Bean Validation constraints that are defined on the Kid class will be checked on all Kid objects in the List.
Composing custom constraints
A third way to prevent the repetition of code is to define constraint compositions.
Compared to ExtVal’s @JoinValidation, defining a custom constraint can be used in at least as many situations as @JoinValidation. Defining custom constraints with Bean Validation gives a more structural solution, compared to using @JoinValidation. However, defining a custom constraint might involve a bit more
work than using @JoinValidation.
Another difference between constraint compositions and @JoinValidation is that constraint compositions can only be used to compose constraints out of other JSR 303 constraints, whereas @JoinValidation can also be used to combine constraints from different types, such as combining JPA constraints with ExtVal Property Valdiation constraints. So all-in-all, @JoinValidation and constraint composition have some overlap, but both also have their own unique features.
Defining a custom constraint involves creating a new annotation. This may look a bit complicated at first, but it is less complicated than it seems. Let’s see how we can create an @Name annotation that we can use on all names in our project:
package inc.monsters.mias.data.validation; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.OverridesAttribute; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; <B>@NotNull @Size(min = 2)</B> @Constraint(validatedBy = {}) @Retention(RUNTIME) @Target({METHOD, FIELD, ANNOTATION_TYPE}) public @interface Name { <B>String message() default "{inc.monsters.mias.data.validation.Name.invalid_name}";</B> Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; <B>@OverridesAttribute(constraint = Size.class, name = "max") int maxLength() default 20;</B> }
In this example, the following interesting things can be observed:
- public @interface Name: This defines a new annotation—@Name.
- @Target: This defines on what elements the new @Name annotation can be used. In this case, it can be used on methods, fields, and other annotations. Most of the time, this is fine for new constraints. It is also possible to create constraints that validate a class; in that case, TYPE should be used as the target. (See http://docs.jboss.org/hibernate/stable/validator/reference/en/html/validator-usingvalidator.html#d0e328 for more
information on class-level constraints.) - @Retention: This defines that this annotation should be executed at runtime. For validation constraints, this should always be set to RUNTIME.
- @Constraint: This identifies this annotation as being a validation constraint. It should always be used for custom constraints.
- @NotNull and @Size(min=2) (right above the @Constraint annotation, highlighted): These are the constraints that the @Name constraint is based on. In other words, any element annotated with @Name must not be null and
must have a size of at least 2. - int maxLength() default 20: This defines an attribute maxLength for the @Name annotation, with a default value of 20. So if no maxLength is specified, the maxLength will be 20.
- @OverridesAttribute(constraint = Size.class, name = “max”): This causes the maxLength attribute to override the max attribute of the Size annotation.
- String message() default “{…}”: This sets the default value of the message attribute to a message bundle key.
Other code that is not mentioned in the bulleted list is needed for every constraint definition. We now have an @Name annotation that can be used on any name field in our project. The annotated field cannot be empty, and should have a size of at least 2, and at most 20. The maximum size can be overridden by the maxLength attribute.
We can use it, for example, in our Employee class, as follows:
public class Employee implements Serializable { // Other member variable are omitted. @Column(name="FIRST_NAME") @Name private String firstName; @Column(name="LAST_NAME") @Name(maxLength = 40) private String lastName; // Getters and setters are omitted. }
Now, the firstName can’t be null. It must have at least 2 characters and at most 20 characters. The lastName has the same constraints, but can be up to 40 characters long. Note how we have reached the same level of reuse as we did when we used @JoinValidation in our Kid class earlier in this chapter. Creating our own custom constraint may be a little more work, but it gives us a more structural way of reuse. And we don’t get referencing problems, as we did with @JoinValidation. As a bonus, we can reuse custom constraints over different projects. We can even create a library of custom constraints to be used in several projects.
As an example of the fl exibility and extendability of ExtVal, the next section will show us how we can set severity levels on certain constraints that give the users the possibility to ignore certain warnings.
Using payloads to set severity levels
As mentioned, we can use the payload attribute on every Bean Validation annotation to pass on meta information about the constraint. With ExtVal, we can use this to create warning messages for certain constraints. These warning messages will appear the first time the user submits a value that violates the constraint. The user can either change the value or ignore the warning by submitting the value for the second time. This section describes how we can implement this for the salary field of our Employee class.
Setting up the Continue with warnings add-on
Let’s start by downloading and installing the Continue with warnings add-on, as described in the Extending ExtVal with add-ons section. Once we’ve downloaded the JAR file and added it to our project, we can start preparing our project to allow the users to ignore warnings. The first thing we’ll have to do is to add a hidden input component to all pages where we expect warnings to be shown that the user should be able to ignore. In our example, we only have to add this hidden component to our EditEmployee.xhtml page, as we will only be adding a warning-level constraint to our Employee entity. The following code snippet shows the hidden component added to the EditEmployee.xhtml page:
<ui:composition template="templates/template.xhtml"> <ui:define name="title">Edit employee</ui:define> <ui:define name="content"> <B><h:inputHidden id="extValWarnState" value="#{extValWarnState.continueWithWarnings}"/></B> <tr:panelFormLayout> <!-- Form contents left out to save space. --> </tr:panelFormLayout> </ui:define> </ui:composition>
The component provides the Continue with warnings add-on with a way to remember if the warning has already been shown to the user, and if the user has already clicked on the submit button. It is important to not change the id and value of the component, as the add-on expects them to have the values that we just saw.
In our example, we add the hidden input component to only one page. In a real-world application, the chances are that we need this component on (nearly) every page. It may seem a good idea to add the hidden component to our page template. However, this will cause the “warning state” to be shared by all pages, which will cause undesired behavior. So, unfortunately, we’ll have to add the hidden component to every page. On a side note, the requirement to use a hidden input is a design decision that was made when the add-on was created. Of course, the open source nature of the project allows us to alter this behavior of the add-on if we like.
Setting the severity level of a constraint
Once we have added the hidden input component to the pages where warnings can be shown, we can set the severity level of one of our constraints to “warning”. Let’s see how we can do this for the salary variable in the Employee class:
package inc.monsters.mias.data; <B>import org.apache.myfaces.extensions.validator .beanval.payload.ViolationSeverity;</B> // Other imports are hidden for brevity. public class Employee implements Serializable { <B>@Min(value=100, payload=ViolationSeverity.Warn.class)</B> private int salary; // All other variables and methods are omitted. }
The only thing we added here is the payload=ViolationSeverity.Warn.class attribute of the @Min annotation. And, of course, we had to add an import for the ViolotionSeverity class.
Now, if we run our application and put a value below 100 in the salary input field on the “Edit employee” page, a warning will be shown on submitting the form. However, if we submit the form with the same value for the second time, the warning will disappear and the data will be saved regardless of the violation of the @Min constraint. This allows the manager that uses the “Edit Employee” page to override the “no salaries below 100” policy.
Setting the severity level is not limited to Bean Validation constraints
Although setting a severity level is introduced in the Using Bean Validation section, setting a severity level is also possible with ExtVal’s own Property Validation annotations, and even with other constraints such as constraints based on JPA annotations. This will be discussed in the following two sections.
Setting the severity level on ExtVal Property Validation constraints
To set a severity level to an ExtVal Property Validation annotation, we should use the parameters attribute of the annotation instead of payload. We should also import another ValidationSeverity class. If we had used ExtVal annotations instead of JSR 303 annotations in our Employee class, the salary variable declaration would now look like this:
package inc.monsters.mias.data; <B>import org.apache.myfaces.extensions.validator .core.validation.parameter.ViolationSeverity;</B> // Other imports are hidden for brevity. public class Employee implements Serializable { <B>@LongRange(minimum=100, parameters=ViolationSeverity.Warn.class)</B> private int salary; // All other variables and methods are omitted. }
Notice the changed import (the first highlighted part of the snippet) and the ExtVal @LongRange annotation that replaces the @Min annotation from the JSR 303 example.
The use of the parameters attribute in this example is just a convention. Unlike the payload attribute in the Bean Validation example, the attributes of ExtVal annotations are not fixed. ExtVal scans all of the attributes of its annotations by type. This means that any parameter with the ViolationSeverity.Warn type—or any other type implementing the ValidationParameter interface—will be interpreted as metadata by ExtVal.
Setting the severity level on any constraint
Although the Bean Validation annotations, as well as the ExtVal annotations, have special attributes that can be used to add metadata such as the severity level, other annotations that are used to define constraints may not have such facilities. For these cases, we can use the Virtual Meta Data add-on , which is a part of the Advanced Meta Data add-on bundle. See the Extending ExtVal with add-ons section for download and installation instructions for this add-on.
The only other constraint type that is currently supported out of the box by ExtVal is the JPA constraint. As JPA constraints are derived from database constraints, it
doesn’t make sense to bypass the violation of such constraints. So, by default, ExtVal will not allow this even if we set the severity level to warning. However, we can set the severity level to “fatal” to be able to mark error messages from JPA constraints as more important than other error messages that are marked with level “error” by default. This does make more sense as an example in this case.
The next example shows the lastName variable, annotated with the JPA @Column annotation with its nullable attribute set to false. This effectively makes the lastName variable a required field. To mark the violation of this constraint as more important than other violations, we want to set the severity level to “fatal”. As the @Column annotation does not have an attribute to set extra metadata, we use the @VirtualMetaData annotation from the add-on to set the metadata as follows:
package inc.monsters.mias.data; <B>import org.apache.myfaces.extensions.validator .core.validation.parameter.ViolationSeverity;</B> // Other imports are hidden for brevity. public class Employee implements Serializable { @Column(nullable=false) <B>@VirtualMetaData(target=Column.class, parameters=ViolationSeverity.Fatal.class)</B> private String lastName; // All other variables and methods are omitted. }
Note that the attributes of the @VirtualMetaData annotation accept exactly the same types as the attributes of all ExtVal Property Validation annotations. That’s why we can use the same ValdationSeverity class in this case. The target attribute is needed to link the @VirtualMetaData annotation to the @Column annotation.
Summary
This chapter introduced MyFaces Extensions Validator, or ExtVal for short. After the installation of ExtVal, we saw that no configuration is needed to get started, based on standard JPA annotations. After that, we had a look at the extra annotations that ExtVal adds to facilitate more validation options and to enable cross validation. We saw how we can combine ExtVal with custom JSF validators. We also looked into creating custom error messages. We saw how we can customize and extend ExtVal in various ways. And finally, this chapter showed us how we can integrate ExtVal with JSR 303 Bean Validation.
This chapter has covered only the basics of what is possible with ExtVal. As ExtVal provides a very extensible and fl exible infrastructure, the possibilities are virtually endless. More information can be found on the weblog of Gerhard Petracek, the lead developer of ExtVal, at http://os890.blogspot.com/. Another resource of additional information is a series of articles about ExtVal on JSF Central. The first article of the series can be found at http://jsfcentral.com/articles/myfaces_extval_1.html. The easiest way to find the other articles in the series is to just replace the 1 in the URL by a higher number. The series currently consists of three articles, but more may be added in the future.
The next and last chapter of this book will introduce some best practices for using the various MyFaces libraries.