In the Creating a web application recipe, we saw how to create a basic JSP web application and introduced the concepts behind creating, deploying, and running a web application.
In this recipe, we'll take things a little further and show how to create a Java Server Faces (JSF) web application and run it on the WildFly application server. This recipe isn't intended to be a thorough tutorial on JSF, although we will go through the basics.
We'll create a JSF application that asks for our name and then welcomes us to the application.
We'll see how NetBeans helps us make the development easier.
Getting ready
To complete this recipe, we need to have a running instance of the NetBeans Java EE bundle, together with a local installation of the WildFly 8 application server. We need to have the WildFly 8 plugin installed into NetBeans. See the Adding WildFly support to NetBeans recipe for further details.
How to do it…
Perform the following steps:
1. Click on the File menu, select New, and then New Project….
2. On the New Project dialog, select Java Web from the Categories list, and then Web Application from the Projects list.
3. Click on Next.
4. Enter the Project Name field as HelloJSF. 5. Click on Next.
6. On the Server and Settings page of the New Web Application wizard, select your WildFly instance as Server and ensure that Java EE Version is set to Java EE 7 Web.
7. Click on the Next button.
8. On the Frameworks selection page, check JavaServer Faces as a selected framework.
9. Click on Finish.
10. Double-click on the index.xhtml file within the Projects explorer to open it up within the HTML editing window.
11. Delete the default text, Hello from Facelets.
12. Right-click between the <h:body> and <h:/body> tags and select Insert Code…
from the context menu.
13. On the Generate pop-up window, click on JSF Form. The contents of the <h:body>
tag will now be updated to include a <f:view> tag containing a <h:form> tag as shown:
14. Insert the following markup between the <h:form> and </h:form> tags:
<h:panelGrid columns="3">
<h:outputText value="Hello. What is your name?"/>
<h:inputText id="name" value="#{helloBean.name}"/>
<h:commandButton action="#{helloBean.sayHello}"
value="Hello"/>
</h:panelGrid>
We've now created a simple input JSF page that displays some output text, asks for some input, and then has a button that can be clicked to submit the input.
Let's now create an output page that can echo the input that the user types, as follows:
1. Right-click on the Web Pages node in the Projects explorer, click on New, and then click on Other.
2. In the New File dialog, select JavaServer Faces from the Categories list, and JSF Page from the list of File Types.
3. Click on Next.
4. Enter the File Name value as hello.
It's tempting here to enter File Name as hello.xhtml instead of just hello. You must, however, omit adding the suffix to the filename as this is automatically added by NetBeans. Otherwise, NetBeans will create a file called hello.xhtml.xhtml.
5. Ensure that the option specifies Facelets instead of JSP File.
6. Click on Finish.
7. The new hello.xhtml file will now be opened for editing. Replace the default body of Hello from Facelets with:
Hi, <h:outputText value="#{helloBean.name}"/>
We've now created all of the views for our JSF application, all we need to do now is create a JSF backing bean that can take our input and forward it on to the output page. Perform the following steps:
1. Right-click on the Source Packages node of the HelloJSF application and click on New, and then click on Other.
2. In the New File dialog, select JavaServer Faces from the Categories list and JSF Managed Bean from the list of File Types.
3. Click on Next.
4. Enter the Class Name value as HelloBean and the Package value as com.davidsalter.hellojsf.
Notice that the bean is created with a default Name, the same as Class Name (capitalized as camelCase however), and the default Scope of the bean is set to request.
5. Click on Finish.
6. The HelloBean.java file will now be opened for editing. Replace the body of the class with the following code:
private String name;
public HelloBean() { }
public String sayHello() { return "hello.xhtml";
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
The HelloBean.java class has one property, name, with corresponding getters and setters.
There is one method, sayHello(), which is invoked when the button is clicked on our form.
This method tells JSF to render the hello.xhtml view when it is called.
Let's now deploy and run our application and see how it runs. Right-click on the Hello JSF project within the Projects explorer and then click on Run.
When the application is first run, WildFly is started up if it is not already running.
The application's .war file is then deployed to the running server. The initial page of the application is as shown in the following screenshot:
After entering your name and clicking on the Hello button, JSF says "Hi":
Now that the application is running, making any changes to the view files (the .xhtml files) or the JSF managed beans will automatically be hot deployed to WildFly when they are saved. The application does not need to be run again, all that is needed is a browser refresh to utilize the new content.
How it works…
When we created a JSF application, there were two aspects that we developed. We developed a couple of view pages (index.xhtml and hello.xhtml) and a managed JSF bean (HelloBean).
For the view pages, we used the Facelets technology. Facelets is the default view technology used with Java Server Faces 2 and has taken over from the use of JSP within Java Server Faces 1.x applications. JSP is now considered a legacy technology.
Facelets is a powerful templating system that allows developers to use any of the JSF components and create template-based web pages. Template-based web pages allow, for example, a layout page to be developed that has a header and a footer and a piece of content in between the two. With templating, we can use a layout page that defines all of the layout outside of the main content so that we can concentrate only on the main content. If we later decide that we wish to change the layout to add a sidebar, for example, we just need to change the template and not all of the pages we have developed.
Facelets allows the use of different component libraries. These libraries must be registered via a tag library in the <html> definition of a file. The two standard libraries for HTML and forms are provided with the JSF runtime. When we created a JSF page in this recipe, we told Facelets about these two libraries by defining the h and f tag libraries in the <HTML>
definition, as follows:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
By referencing these tag libraries, we were able to reference any of the components within the h and f tag libraries (such as <h:outputText /> and <f:view />).
For more information regarding Facelets, check out the Oracle documentation at:
http://docs.oracle.com/javaee/7/tutorial/doc/jsf-facelets.htm
To perform some processing, we created a JSF managed bean. This is simply a POJO with the
@ManagedBean annotation applied to it. We also applied the @RequestScoped annotation to the bean so that the lifecycle of the bean is tied to an HTTP request. The bean is therefore initialized every time an HTTP request is made. Within the managed bean, we created a single member to hold the name entered by the user and a single method (sayHello) that returned the address of the Facelets page to render when the button was pressed on the form.
To link the view and the managed bean, we used expression language. Here we link the input box to the name property of the managed bean as follows:
<h:inputText id="name" value="#{helloBean.name}"/>
We also link the button action to the sayHello method in the bean as follows:
<h:commandButton action="#{helloBean.sayHello}" value="Hello"/>
There's more
How did JSF know what page to display first, and how did it know to use .xhtml as the file extension for Facelets views?
When we created the project, we used the NetBeans defaults and allowed NetBeans to create some default configuration within the web.xml file for the project.
The Faces servlet was defined with a URL pattern set to /faces/*:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
All JSF requests go through the Faces servlet as this is what enables the lifecycle of Facelets components. Without this mapping, Facelets would just not work! This mapping states that any request to /faces/* will be executed via the Faces servlet. So for example, /faces/
index.xhtml would cause the index.xhtml file to be processed as a JSF Facelets file.
NetBeans also configured the default page of the application to be faces/index.xhtml:
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
This specifies the default page that will be opened when the root URL of the application is accessed.
What if I wanted to use a CDI bean or an EJB instead of the JSF managed bean. Could I do that?
Yes, it's entirely possible to use a different type of a managed bean as the backing bean for JSF pages. With Java EE 7, CDI beans are very common and can be used interchangeably with JSF managed beans. EJBs can also be used as backing beans if required, but they provide a greater overhead as they are considered more heavyweight (even though they are still essentially POJOs) than JSF backing beans or CDI beans.
A good rule of thumb it to use the lightest bean possible for a required situation and only move to the next higher level of bean when required.
So, for a simple application, a JSF Managed bean is sufficient. It allows us to receive data from the web page and then update the web page with some results. We don't really need anything more complex.
If we find that we need to start injecting beans into other resources, then a CDI managed bean makes sense. We can inject EJBs into CDI beans if we need to perform any processing within the bean.
Start with the simplest possible type of bean and only increase complexity when needed.
What if I want to perform validation on input fields? Does JSF allow that? It certainly does.
We can either use the Bean Validation API to perform validation within our model, or we can perform JSF validation within the view.
To output error messages to the view, we can use the <h:message /> tag to output a message for a single element, or the <h:messages /> tag to output a list of all the error messages within the form:
<h:inputText value="#{cc.attrs.editValue}" id="inputText" />
<h:message for="inputText" />
Using the Bean Validation API, we can add annotations onto fields that will be validated before being accepted by the JSF runtime. Bean Validation provides many annotations to perform validation, and even allows custom validators to be written. Some of the more common validations are as follows:
f @NotNull: The specified field value must not be null
f @Min: The specified field value must be at least that specified by the constraint
f @Max: The specified field value must be at most that specified by the constraint
f @Past: The specified field value date must be a date in the past
f @Pattern: The specified field value must match that specified by a regular expression
f @Size: The size of the field value must be between the specified limits For more information on Bean Validation, check out the website
http://beanvalidation.org.
With JSF validation, we can perform a similar task to Bean Validation, but instead using JSF tags. Validation is performed within the Validation phase of a JSF component's lifecycle. As with bean validation, JSF allows custom validators to be written. Some of the standard JSF validators are:
f <f:validateLength />: The size of the field value must be between the specified limits
f <f:validateLongRange />: The range of a long integer must be between the specified limits
f <f:validateDoubleRange />: The range of a double must be between the specified limits