Developing Web Applications Using JavaServer Faces

Transcription

Developing Web Applicationsusing JavaServer FacesIn the previous two chapters we covered how to develop web applications in Javausing Servlets and JSPs. Although a lot of applications have been written using theseAPIs, most modern Java applications are written using some kind of web applicationframework. As of Java EE 5, the standard framework for building web applicationsis Java Server Faces (JSF). In this chapter we will see how using JSF can simplify webapplication development.The following topics will be covered in this chapter: Creating a JSF project with NetBeans Generating a form to capture user data by draging a JSF form from theNetBeans palette into our page Laying out JSF tags by taking advantage of the JSF h:panelGrid tag Using static and dynamic navigation to define navigation between pages Using the NetBeans New JSF Managed Bean wizard to create a JSF managedbean and automatically add it to the application's faces-config.xml configuration file Using the NetBeans Page Flow editor to establish page navigation bygraphically connecting pages Implementing custom JSF validators Displaying tabular data in our pages by dragging-and-dropping the JSF DataTable item from the NetBeans palette into our page

Developing Web Applications using JavaServer FacesIntroduction to JavaServer FacesBefore JSF was developed, Java web applications were typically developed usingnon-standard web application frameworks such as Apache Struts, Tapestry, SpringWeb MVC, or many others. These frameworks are built on top of the Servlet and JSPstandards, and automate a lot of functionality that needs to be manually coded whenusing these APIs directly.Having a wide variety of web application frameworks available (at the time ofwriting, Wikipedia lists 35 Java web application frameworks, and this list is far fromextensive!), often resulted in "analysis paralysis", that is, developers often spend aninordinate amount of time evaluating frameworks for their applications.The introduction of JSF to the Java EE 5 specification resulted in having a standardweb application framework available in any Java EE 5 compliant application server.We don't mean to imply that other web application frameworksare obsolete or that they shouldn't be used at all, however, a lot oforganizations consider JSF the "safe" choice since it is part of the standardand should be well supported for the foreseeable future. Additionally,NetBeans offers excellent JSF support, making JSF a very attractive choice.Strictly speaking, JSF is not a web application framework as such, but a componentframework. In theory, JSF can be used to write applications that are not web-based,however, in practice JSF is almost always used for this purpose.In addition to being the standard Java EE 5 component framework, one benefit ofJSF is that it was designed with graphical tools in mind, making it easy for toolsand IDEs such as NetBeans to take advantage of the JSF component model withdrag-and-drop support for components. NetBeans provides a Visual Web JSFDesigner that allow us to visually create JSF applications. This tool is discussed indetail in Chapter 6.Developing Our first JSF ApplicationFrom an application developer's point of view, a JSF application consists of a seriesof JSP pages containing custom JSF tags, one or more JSF managed beans, and aconfiguration file named faces-config.xml. The faces-config.xml file declaresthe managed beans in the application, as well as the navigation rules to follow whennavigating from one JSF page to another.[ 136 ]

Chapter 4Creating a New JSF ProjectTo create a new JSF project, we need to go to File New Project, select the Java Webproject category, and Web Application as the project type.After clicking Next , we need to enter a Project Name, and optionally change otherinformation for our project, although NetBeans provides sensible defaults.[ 137 ]

Developing Web Applications using JavaServer FacesOn the next page in the wizard, we can select the Server, Java EE Version, andContext Path of our application. In our example we will simply pick thedefault values.On the next page of the new project wizard, we can select what frameworks our webapplication will use.[ 138 ]

Chapter 4Unsurprisingly, for JSF applications we need to select the JavaServerFaces framework.The Visual Web JavaServer Faces framework allows us to quickly buildweb pages by dragging-and-dropping components from the NetBeanspalette into our pages. Although it certainly allows us to developapplications a lot quicker than manually coding, it hides a lot of the "ins"and "outs" of JSF. Having a background in standard JSF development willhelp us understand what the NetBeans Visual Web functionality doesbehind the scenes. Visual Web JSF is covered in Chapter 6.When clicking Finish, the wizard generates a skeleton JSF project for us, consistingof a single JSP file called welcomeJSF.jsp, and a few configuration files: web.xml,faces-config.xml and, if we are using the default bundled GlassFish server, theGlassFish specific sun-web.xml file is generated as well.web.xml is the standard configuration file needed for all Java web applications.faces-config.xml is a JSF-specific configuration file used to declare JSF-managedbeans and navigation rules. sun-web.xml is a GlassFish-specific configuration filethat allows us to override the application's default context root, add security rolemappings, and perform several other configuration tasks.[ 139 ]

Developing Web Applications using JavaServer FacesThe generated JSP looks like this: %@page contentType "text/html"% %@page pageEncoding "UTF-8"% %@taglib prefix "f" uri "http://java.sun.com/jsf/core"% %@taglib prefix "h" uri "http://java.sun.com/jsf/html"% !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 .dtd" %-This file is an entry point for JavaServer Faces application.--% html head meta http-equiv "Content-Type" content "text/html;charset UTF-8" title JSP Page /title /head body f:view h1 h:outputText value "JavaServer Faces"/ /h1 /f:view /body /html As we can see, a JSF enabled JSP file is a standard JSP file using a couple ofJSF-specific tag libraries. The first tag library, declared in our JSP by thefollowing line: %@taglib prefix "f" uri "http://java.sun.com/jsf/core"% is the core JSF tag library, this library includes a number of tags that are independentof the rendering mechanism of the JSF application (recall that JSF can be used forapplications other than web applications). By convention, the prefix f (for faces) isused for this tag library.The second tag library in the generated JSP, declared by the following line: %@taglib prefix "h" uri "http://java.sun.com/jsf/html"% [ 140 ]

Chapter 4is the JSF HTML tag library. This tag library includes a number of tags that are usedto implement HTML specific functionality, such as creating HTML forms and inputfields. By convention, the prefix h (for HTML) is used for this tag library.The first JSF tag we see in the generated JSP file is the f:view tag. When writinga Java web application using JSF, all JSF custom tags must be enclosed inside an f:view tag. In addition to JSF-specific tags, this tag can contain standard HTMLtags, as well as tags from other tag libraries, such as the JSTL tags discussed in theprevious chapter.The next JSF-specific tag we see in the above JSP is h:outputText . This tag simplydisplays the value of its value attribute in the rendered page.The application generated by the new project wizard is a simple, but complete, JSFweb application. We can see it in action by right-clicking on our project in the projectwindow and selecting Run. At this point the application server is started (if it wasn'talready running), the application is deployed and the default system browser opens,displaying our application's welcome page.Modifying Our JSP to Capture User DataThe generated application, of course, is nothing but a starting point for us to create anew application. We will now modify the generated welcomeJSF.jsp file to collectsome data from the user.[ 141 ]

Developing Web Applications using JavaServer FacesThe first thing we need to do is to add an h:form tag inside the f:view tag. The h:form tag is equivalent to the form tag in standard HTML pages. We can eithertype the h:form tag directly in the page or drag the JSF Form item from the paletteinto the appropriate place in the page markup.If we choose the second approach, the following window will pop-up:Selecting Empty Form will generate an empty h:form tag which we can use to addour own input fields.The Form Generated from Entity Class selection is a very nice NetBeansfeature that allows us to generate a form that will include input fieldsmapping to all properties in a Java Persistence API (JPA) entity. JPA iscovered in detail in Chapter 5.After adding the h:form tag and a number of additional JSF tags, our page nowlooks like this: %@page contentType "text/html"% %@page pageEncoding "UTF-8"% %@taglib prefix "f" uri "http://java.sun.com/jsf/core"% %@taglib prefix "h" uri "http://java.sun.com/jsf/html"% [ 142 ]

Chapter 4 !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 .dtd" html head meta http-equiv "Content-Type"content "text/html; charset UTF-8" link rel "stylesheet" type "text/css"href "./css/style.css" title JSP Page /title /head body f:view h1 h:outputText value "JavaServer Faces" / /h1 h:form h:panelGrid columns "3"columnClasses "rightalign,leftalign,leftalign" !-- First row begins here -- h:outputLabel value "Salutation: "for "salutation"/ h:selectOneMenu id "salutation" label "Salutation"value "#{RegistrationBean.salutation}" f:selectItem itemLabel "" itemValue ""/ f:selectItem itemLabel "Mr." itemValue "MR"/ f:selectItem itemLabel "Mrs." itemValue "MRS"/ f:selectItem itemLabel "Miss" itemValue "MISS"/ f:selectItem itemLabel "Ms" itemValue "MS"/ f:selectItem itemLabel "Dr." itemValue "DR"/ /h:selectOneMenu h:message for "salutation"/ !-- Second row begins here -- h:outputLabel value "First Name:"for "firstName"/ h:inputText id "firstName" label "First Name"required "true"value "#{RegistrationBean.firstName}" / h:message for "firstName" / !-- Third row begins here -- h:outputLabel value "Last Name:" for "lastName"/ h:inputText id "lastName" label "Last Name"required "true"value "#{RegistrationBean.lastName}" / h:message for "lastName" / !-- Fourth row begins here -- [ 143 ]

Developing Web Applications using JavaServer Faces h:outputLabel for "age" value "Age:"/ h:inputText id "age" label "Age" size "2"value "#{RegistrationBean.age}"/ h:message for "age"/ !-- Fifth row begins here -- h:outputLabel value "Email Address:" for "email"/ h:inputText id "email" label "Email Address"required "true"value "#{RegistrationBean.email}" / h:message for "email" / !-- Sixth row begins here -- h:panelGroup/ h:commandButton id "register" value "Register"action "submit" / /h:panelGrid /h:form /f:view /body /html The following screenshot illustrates how our page will be rendered at runtime:All JSF input fields must be inside a h:form tag. The h:panelGrid helps usto easily align JSF tags in our page. It can be thought of as a grid where other JSFtags will be placed. The columns attribute of the h:panelGrid tag indicates howmany columns the grid will have, each JSF component inside the h:panelGrid component will be placed in an individual cell of the grid, when the number ofcomponents matching the value of the columns attribute (three in our example) hasbeen placed inside h:panelGrid , a new row is automatically started.[ 144 ]

Chapter 4The following table illustrates how tags will be laid out inside a h:panelGrid tag.First TagSecond TagThird TagFourth TagFifth TagSixth TagSeventh TagEighth TagNinth TagEach row in our h:panelGrid consists of an h:outputLabel tag, an input field,and an h:message tag.The columnClasses attribute of h:panelGrid allow us to assign CSS styles to eachcolumn inside the panel grid. Its value attribute must consist of a comma separatedlist of CSS styles (defined in a CSS stylesheet). The first style will be applied to thefirst column, the second style will be applied to the second column, the third stylewill be applied to the third column, so on and so forth. If our panel grid had morethan three columns, then the fourth column would have been styled using the firststyle in the columnClasses attribute, the fifth column would have been styled usingthe second style in the columnClasses attribute, so on and so forth.The CSS stylesheet for our example is very simple, therefore it is notshown. However, it is part of the code download for this chapter.If we wish to style rows in an h:panelGrid , we can do so with its rowClassesattribute, which works the same way that the columnClasses works for columns. h:outputLabel , generates a label for an input field in the form. The value of itsfor attribute must match the value of the id attribute of the correspondinginput field. h:message generates an error message for an input field. The value of its for fieldmust match the value of the id attribute for the corresponding input field.The first row in our grid contains an h:selectOneMenu . This tag generates anHTML select tag on the rendered page.Every JSF tag has an id attribute. The value for this attribute must be a stringcontaining a unique identifier for the tag. If we don't specify a value for thisattribute, one will be generated automatically. It is a good idea to explicitly statethe ID of every component, since this ID is used in runtime error messages (affectedcomponents are a lot easier to identify if we explicitly set their IDs).[ 145 ]

Developing Web Applications using JavaServer FacesWhen using h:label tags to generate labels for input fields, or when using h:message tags to generate validation errors, we need to explicitly set the valueof the id tag, since we need to specify it as the value of the for attribute of thecorresponding h:label and h:message tags.Every JSF input tag has a label attribute. This attribute is used to generatevalidation error messages on the rendered page. If we don't specify a value for thelabel attribute, then the field will be identified in the error message by it's ID.Each JSF input field has a value attribute. In the case of h:selectOneMenu , thisattribute indicates which of the options in the rendered select tag will be selected.The value of this attribute must match the value of the itemValue attribute of one ofthe nested f:selectItem tags. The value of this attribute is usually a value bindingexpression, which means that the value is read at runtime from a JSF-managed bean.In our example, the value binding expression #{RegistrationBean.salutation}is used. What will happen is, at runtime JSF will look for a managed bean namedRegistrationBean, and look for an attribute named salutation on this bean, thegetter method for this attribute will be invoked, and its return value will be used todetermine the selected value of the rendered HTML select tag.Nested inside the h:selectOneMenu there are a number of f:selectItem tags.These tags generate HTML option tags inside the HTML select tag generatedby h:selectOneMenu . The value of the itemLabel attribute is the value that theuser will see, while the value of the itemValue attribute will be the value that will besent to the server when the form is submitted.All other rows in our grid contain h:inputText tags. This tag generates an HTMLinput field of type text, which accepts a single line of typed text as input. Weexplicitly set the id attribute of all of our h:inputText fields; this allows us torefer to them from the corresponding h:outputLabel and h:message fields.We also set the label attribute for all of our h:inputText tags; this results in userfriendlier error messages.Some of our h:inputText fields require a value. These fields have their requiredattribute set to true, and each JSF input field has a required attribute. If we requirethe user to enter a value for this attribute, then we need to set this attribute to true.This attribute is optional, and if we don't explicitly set a value for it, then it defaultsto false.[ 146 ]

Chapter 4In the last row of our grid, we added an empty h:panelGroup tag. The purposeof this tag is to allow adding several tags into a single cell of an h:panelGrid .Any tags placed inside this tag are placed inside the same cell of the grid where h:panelGrid is placed. In this particular case, all we want to do is to have an"empty" cell in the grid so that the next tag, h:commandButton , is aligned with theinput fields in the rendered page. h:commandButton is used to submit a form to the server. The value of it's valueattribute is used to generate the text of the rendered button. The value of it's actionattribute is used to determine what page to display after the button is pressed. Thisis specified in the navigation rules of the application's faces-config.xml file, whichwill be covered later in the chapter.In our example, we are using static navigation. When using JSF static navigation,the value of the action attribute of a command button is hard coded in the JSPmarkup. An alternate to static navigation is dynamic navigation. When usingdynamic navigation, the value of the action attribute of the command button is avalue binding expression resolving to a method returning a String in a managedbean. The method may then return different values based on certain conditions.Navigation would proceed to a different page, depending on the value ofthe method.As long as it returns a String, the managed bean method executedwhen using dynamic navigation can contain any logic inside it, and isfrequently used to save data in a managed bean into a database.Both when using static or dynamic navigation, the page to navigate to is defined inthe application's faces-config.xml configuration file. Later in this chapter, we willexplain how we can graphically configure navigation rules using the NetBeans PageFlow editor.Creating Our Managed BeanJSF-managed beans are standard JavaBeans that are used to hold user-entered datain JSF applications. JSF-managed beans need to be declared in the application'sfaces-config.xml file. NetBeans can help expedite things by automatically addingour managed beans to faces-config.xml.[ 147 ]

Developing Web Applications using JavaServer FacesIn order to create a new managed bean, we need to go to File New, selectJavaServer Faces from the category list, and JSF Managed Bean from the filetype list.In the next screen in the wizard, we need to enter a name for our managed bean, aswell as a package.[ 148 ]

Chapter 4Most default values are sensible and in most cases can be accepted. The only one weshould change if necessary is the Scope field.Managed beans can have different scopes. A scope of request means that the beanis only available in a single HTTP request. Managed beans can also have sessionscope, in which case they are available in a single user's HTTP session. A scope ofapplication means that the bean is accessible to all users in the application, acrossuser sessions. Managed beans can also have a scope of none, which means that themanaged bean is not stored at any scope, but is created on demand as needed. Weshould select the appropriate scope for our managed bean. In our particular example,the default request scope will meet our needs.After finishing the wizard, two things happen: a boilerplate version of our managedbean is created in the specified package, and our managed bean is added to theapplication's faces-config.xml.The generated managed bean source simply consists of the class and a public noargument constructor.package com.ensode.jsf.managedbeans;public class RegistrationBean {/** Creates a new instance of RegistrationBean */public RegistrationBean() {}}The application's faces-config.xml contains our managed bean declaration. ?xml version '1.0' encoding 'UTF-8'? faces-config version "1.2"xmlns "http://java.sun.com/xml/ns/javaee"xmlns:xsi emaLocation com/xml/ns/javaee/web-facesconfig 1 2.xsd" managed-bean managed-bean-name RegistrationBean /managed-bean-name managed-bean-class com.ensode.jsf.managedbeans.RegistrationBean /managed-bean-class managed-bean-scope request /managed-bean-scope /managed-bean /faces-config [ 149 ]

Developing Web Applications using JavaServer FacesThe value of the managed-bean-name element matches the value we entered in theClass Name field in the wizard. Notice that this value is what we used in the valuebinding expressions in our page to access the managed bean properties. Althoughthe value we gave the managed bean matches it's class name, this is not mandatory.The value we entered in the wizard's Class Name field is also used as the nameof the class that was generated by the wizard, as can be seen by the value of the managed-bean-class element, which is the fully qualified name of our managedbean class. Unsurprisingly, the package structure matches the value we entered inthe Package field in the wizard. Finally, we see the scope we selected in the wizardas the value of the managed-bean-scope element.At this point, we need to modify our managed bean by adding properties that willhold the user-entered values.Automatic Generation of Getter and Setter MethodsNetbeans can automatically generate getter and setter methods for ourproperties. We simply need to click the keyboard shortcut for "insertcode", which defaults to Alt Insert in Windows and Linux, then selectGetters and Setters.package com.ensode.jsf.managedbeans;public class RegistrationBean {/** Creates a new instance of RegistrationBean */public RegistrationBean() {}privateprivateprivateprivateprivateString salutation;String firstName;String lastName;Integer age;String email;public String getEmail() {return email;}public void setEmail(String email) {this.email email;}[ 150 ]

Chapter 4public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName lastName;}public String getSalutation() {return salutation;}public void setSalutation(String salutation) {this.salutation salutation;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age age;}}Notice that the names of all of the bean's properties (instance variables) match thenames we used in the JSP's value binding expressions. These names must match sothat JSF knows how to map the bean's properties to the value binding expressions.[ 151 ]

Developing Web Applications using JavaServer FacesImplementing NavigationThe last thing we need to do before we can test our application is to implementapplication navigation. For our application we need to create a confirmation page,then add navigation rules to our JSP page so that the application navigates from theinput page to the confirmation page when the user submits the form.NetBeans allows us to save some time by allowing us to graphically add navigationrules via the NetBeans Page Flow Editor. To do so, we need to open faces-config.xml and click on the PageFlow button in the toolbar above the file. In our particularcase we haven't yet created the confirmation page we wish to navigate to. This is nota problem, since it can be created "on demand" by NetBeans by right-clicking on thePageFlow editor and selecting New File from the resulting pop-up menu.At this point the standard New JSP File wizard appears. We enter confirmation.jsp as the name of the new JSP. The new page is automatically created and added tothe page flow editor.Refer to Chapter 2 Developing Web Applications with Servlets and JSPs forinstructions on the New JSP File wizard.[ 152 ]

Chapter 4We can graphically connect the two pages by clicking on the connector to the right ofwelcomeJSF.jsp and dragging it to confirmation.jsp.[ 153 ]

Developing Web Applications using JavaServer FacesDoing so generates a navigation case from welcomeJSF.jsp to confirmation.jsp. Aswe can see, the navigation case is given a default outcome name of case1. We needto modify this to be the value of the action attribute of the h:commandButton inwelcomeJSF.jsp.To do this, we simply double-click on the text representing the navigation caseoutcome name, then replace it with the appropriate value.At this point, the navigation case name is updated with the value we entered.[ 154 ]

Chapter 4If we had been using dynamic navigation (and, of course, if there were more than twoJSP pages in the application), we would simply drag the connector from welcomeJSF.jsp to another page to create a different navigation case based on the value of themanaged bean method executed when clicking the page's command button.The NetBeans PageFlow editor updates our application's faces-config.xml behindthe scenes. It adds a navigation-rule element to it. navigation-rule from-view-id /welcomeJSF.jsp /from-view-id navigation-case from-outcome submit /from-outcome to-view-id /confirmation.jsp /to-view-id /navigation-case /navigation-rule The from-view-id element is the name of the JSP originating the navigation. It isthe JSP we drag from in the PageFlow editor to create the navigation case. The valueof the to-view-id element is the destination page. It is generated from the JSP wedrag the navigation case to in the PageFlow editor. The value of the from-outcome element is the name of the navigation case outcome in the PageFlow editor.If we had been using dynamic navigation, we would have separate navigationcase elements for each possible return value of the managed bean method boundto the page's command button, the body of the from-outcome element of eachnavigation case would be one possible return value, and the body of the to-viewid would be the page we would navigate to for that particular navigation case.Notice that the value of the from-view-id element starts with aforward slash (/). A common mistake when setting up JSF navigationis to forget this initial tag. When this happens, JSF will fail to find thedestination JSP and will simply redisplay the page that initiated thenavigation. Using NetBean's PageFlow editor prevents us from makingthat mistake.After setting up our navigation case, we now need to modify the generatedconfirmation.jsp so that it displays the values in our managed bean. %@page contentType "text/html" pageEncoding "UTF-8"% !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 .dtd" %@ taglib prefix "f" uri "http://java.sun.com/jsf/core" % %@ taglib prefix "h" uri "http://java.sun.com/jsf/html" % html [ 155 ]

Developing Web Applications using JavaServer Faces head meta http-equiv "Content-Type" content "text/html;charset UTF-8" link rel "stylesheet" type "text/css"href "./css/style.css" title Confirmation Page /title /head body h2 Confirmation Page /h2 f:view h:panelGrid columns "2"columnClasses "rightalign-bold,normal" !-- First row begins here -- h:outputText value "Salutation: "/ h:outputTextvalue "#{RegistrationBean.salutation}" / !-- Second row begins here -- h:outputText value "First Name:"/ h:outputText value "#{RegistrationBean.firstName}" / !-- Third row begins here -- h:outputText value "Last Name:"/ h:outputText value "#{RegistrationBean.lastName}" / !-- Fourth row begins here -- h:outputText value "Age:"/ h:outputText value "#{RegistrationBean.age}"/ !-- Fifth row begins here -- h:outputText value "Email Address:"/ h:outputText value "#{RegistrationBean.email}" / /h:panelGrid /f:view /body /html As we can see, our confirmation page is very simple. It consists of a series of h:outputText tags containing labels and value binding expressions bound to ourmanaged bean's properties.Executing Our ApplicationWe are now ready to execute our JSF application. The easiest way to do so is toright-click on welcomeJSF.jsp and click on Run File in the resulting pop-up menu,or, if our application is set as the main project, we can click directly to the "Run" iconin the tool bar at the top of the IDE.[ 156 ]

Chapter 4At this point GlassFish (or whatever application server we are using for our project)will start automatically, if it hadn't been started already, the default browser willopen and it will automatically be directed to our page's URL.After entering some data on the page, it should look something like thefollowing screenshot.When we click on the Register button, our RegistrationBean managed bean ispopulated with the values we entered into the page. Each property in the field willbe populated according to the value binding expression in each input field.At this point JSF navigation "kicks-in", and we are taken to the Confirmation Page.[ 157 ]

Developing Web Applications using JavaServer FacesThe values displayed in the confirmation page are taken from our managed bean,confirming that the bean's properties were populated correctly.JSF ValidationEarlier in this chapter we discussed how the required attribute for JSF input fieldsallows us to easily make input fields mandatory.If a user attempts to submit a form with one or more required fields missing, an errormessage is automatically generated.The error message is generated by the h:message tag corresponding to the invalidfield. The string First Name in the error message corresponds to the value of thelabel attribute for the field. Had we omitted the label attribute, the value of thefields id attribute would have been shown instead. As we can see, the requiredattribute makes it very easy to implement mandatory field functionali

APIs, most modern Java applications are written using some kind of web application framework. As of Java EE 5, the standard framework for building web applications is Java Server Faces (JSF). In this chapter we will see how using JSF can simplify web application development. The following topics will be covered in this chapter: