Workshop 5: Java Server Faces (JSF)

Download as pdf or txt
Download as pdf or txt
You are on page 1of 15

Distributed applications

Adam Waldenberg Joachim von Hacht

Workshop 5: Java Server Faces (JSF)


Objectives

Gaining some basic knowledge about Java Server Faces and Facelets. Learning how to use AJAX with JSF. Basic introduction to higher level component libraries (PrimeFaces & ICEFaces). Understanding scopes and how to take full advantage of them when dening managed beans. Learn how to use basic validators in JSF. Understanding server push and how it can be used in an application.

What you will do (and how)


In this workshop you will take what you did in the JPA Workshop and add a working JSF front-end to a table of the database model. You will warm up by investigating the JSF life cycle. Next step will be to start adding the pages needed for a basic (and partially complete) web shop. The pages will utilize the templating functionality from Facelets and explore how to connect logic and function in the form of managed beans, AJAX, page navigation and bean property validation. The components framework we will use is PrimeFaces. When you are done with that, we will also explore server push and investigate a practical example of how we can take advantage of it by implementing a web notice board. What you will learn in this workshop is quite extensive and very useful for the project.

Final date

Monday 3/10

Project relevance
It is preferred that you utilize JSF over ordinary servlets in your project application. Use it together with some kind of AJAX functionality or a component library that supports AJAX. Using server push in some clever way in your application is also very benecial.

General Tips

Confusion can occur. There are multiple annotations for ManagedBean, SessionScope etc. Always look out for the correct package (for now: javax.faces). If you notice that some annotations aren't working as expected, check the version of your web deployment descriptor (web.xml ). It should be v2.5+.

Distributed applications

Adam Waldenberg Joachim von Hacht

If you can't get Facelets or navigation to work properly, make sure the version of your faces conguration is really set to v2.0. Go to the course home page and look at the demonstration before you start.

1 The JSF Request cycle


The aim is to use a PhaseListener and phase events to get some understanding of the JSF request cycle. 1. Download jsf_request_cycle_samples.nbp.zip and open it in NetBeans. 2. Inspect the code. Run the application and inspect the GlassFish output window. 3. Try to get a grasp of the execution. Do some minor modications and explore what happens.

NOTE

web.xml.

The URL pattern to trigger the FacesServlet is .../faces/... See WelcomeFile in

2 Preparing the project


You need to do a few simple steps before any actual coding: 1. Create a new Maven web application with the following properties: a) Project Name: b)

jsfproducts.nbp Package Name: edu.chl.cid.jsfproducts

2. Next, we need to congure how to deploy and what server to use. Right click on the project and select Properties > Run . a) Select GlassFish as the server for the project. b) Context Path:

/jsfproducts
Right

3. We also need to add the JavaServer Faces framework to the application. click on the project again and select Properties > Frameworks . a) Add JavaServer Faces to the list of used frameworks. b) JSF Servlet URL Pattern:

*.jsf

4. As we want a working database layer for our web shop, you will need to import some of the work you did in the previous JPA workshop: a) Import the Product entity class. This will be used as the main table in the application we are going to make and is the only entity we will use.

Distributed applications

Adam Waldenberg Joachim von Hacht

b) You will also need to import the Product controller class for persisting and querying that you created in {5.1} in the JPA workshop. c) Do not forget to add the needed dependencies for persistence into your (Derby and EclipseLink). d) Make sure you have a working persistence unit dened. 5. Add a

pom.xml

faces-cong.xml to the project.

You can create such a le using New >

Other > JavaServer Faces > JSF Faces Conguration.


6. Delete any

index.jsp le (under Web Pages) that was automatically created with pom.xml, taking care of where you paste

the project. 7. Add the following two fragments to your them:


<r e p o s i t o r i e s > <r e p o s i t o r y > <i d >p r i m e

r e p o </ i d >
Maven R e p o s i t o r y </name>

<name>P r i m e F a c e s

<u r l >h t t p : / / r e p o s i t o r y . p r i m e f a c e s . o r g </ u r l > <l a y o u t >d e f a u l t </ l a y o u t > </ r e p o s i t o r y > </ r e p o s i t o r i e s >

<d e p e n d e n c y > <g r o u p I d >o r g . p r i m e f a c e s </ g r o u p I d > < a r t i f a c t I d >p r i m e f a c e s </ a r t i f a c t I d > < v e r s i o n >2.1 </ v e r s i o n > </d e p e n d e n c y >

8. If you run and deploy the project you should see the welcome page. Also inspect the GlassFish console for any errors.

Distributed applications

Adam Waldenberg Joachim von Hacht

3 Making a face with Facelets


In the workshop about basic web applications you dened (included) a header and footer using a simple JSP command. While this method works, it isn't very exible. With the release of JSF2, the Facelets technology became standard. This is a very versatile technology which gives us the possibility to create templates, include page fragments and dene custom tag libraries. We will focus on exploring how to use templating with Facelets.

NOTE:

Don't forget about the browser cache when working with the following as-

signment. It can really play tricks on you! Always reload when something that you think really should be working, isn't working. 1. Edit the deployment descriptor (

web.xml) in your project.


j s f </w e l c o m e

Modify the welcome

page to the following (overwrite any old conguration):


<w e l c o m e

f i l e l i s t > f i l e >v i e w P r o d u c t s . </w e l c o m e f i l e l i s t >


<w e l c o m e

f i l e >

2. Create three public pages;

dProduct.xhtml.

viewCart.xhtml, viewProducts.xhtml

and

ad-

These pages should be standard JSF pages and utilize facelets.

a) You can create these at New > Other > JavaServer Faces > JSF Page. Make sure the Facelets option is selected. b) To access facelets you need to add the following namespace:

xmlns:ui="http://java.sun.com/jsf/facelets".
3. Use Facelets templating and give the three pages a header and a footer of your choice. The header should be some kind of graphical logo (nd something appropriate using Google). The footer should be text-based.

TIP: A Facelets template

should not be accessible directly from the browser. This means you should put it somewhere under WEB-INF/, like WEB-INF/facelets/template.xhtml. a) A facelets template is also just a JSF page and can be created in the same way as the three public pages created previoulsy. b) The template should be a complete JSF page with a paste in their content. c) The template should dene the header and footer. d) Connect the three pages to the template and make sure you get correct output. 4. We want to give each page an appropriate

ui:insert

tag dened.

This tag species where in the template the pages that use the template can

title

and header (using a

h1

tag).

These tags should be dened inside the template. a parameter that will be sent into the template. parameter and display it's content inside the tags.

Using Facelets, we can dene We can then use that dened

Distributed applications

Adam Waldenberg Joachim von Hacht

a) You can set a parameter using the

ui:param command. h1 and title tags of the

b) The parameter can be accessed from within the template using #{parameter_name}. c) You should output your dened parameter inside the template. 5. You should end up with something somewhat similar to the image below (showing the

viewProducts.xhtml page).

The shop image used in the screen shot can be

found at http://commons.wikimedia.org/wiki/File:Shop.svg.

4 A discussion about managed beans


Before we start writing any beans, we need to have a small discussion about beans in JSF: 1. Can we somehow categorize the dierent managed beans that are usually used in a typical JSF application? How is it appropriate to categorize our bean and under which scope should they be dened? 2. When designing a bigger application in JSF we have to carefully consider what kind of beans we need: a)

Backing Beans - Connect to a page and back up

the properties of views

(pages). Thus, the name backing bean. Typically (but not necessarily), each

view has exactly one backing bean connected to it. Backing beans can be stored
in almost any scope, with the exception of application scope. What scope to use depends on what you want the bean to achieve in your application and what framework you are using. Backing beans often connect their properties to a database model. Each client typically gets a privately instantiated backing bean when viewing a page.

Distributed applications

Adam Waldenberg Joachim von Hacht

b)

Controller Beans - These beans either handle navigation or implement listeners used in views. In the latter case, controller beans often modify properties of the model or the backing bean they are associated with, when a certain event happens. Usually stored in the same scope as the backing bean(s) they are connected to.

c)

Model Beans - Pure model beans that hold properties that are common for
the application or client. These beans hold model properties that we do not (or should not) put inside a database. Usually dened in session or application scope.

d)

Support Beans

- These beans are similar to model beans, but are used

specically to support components shown in a view. They are often tightly coupled to the view, returning data that is specialized to the view itself. They can also be used as a container when developing custom components. These beans usually don't have any writable properties and are almost always dened in application scope to save memory in the application server. instances can be shared between clients. e) This way

Utility Beans

- Contain helper methods that can be used by other beans.

Usually dened in application scope.

View
Backing Bean

Model
Model Bean

Controller
Controller Bean

3. So why should we divide our beans in the above (or some other similar) manner? a) We want to clearly divide our application into logical parts. b) We want to minimize resource use (such as memory). c) We want to take the MVC pattern into consideration. 4. A common mistake is that programmers often put all their backing beans into session scope (even if this is not needed) and inject them into each other. Why is this mostly a bad idea?

Distributed applications

Adam Waldenberg Joachim von Hacht

5.

If your @ApplicationScoped bean is not immutable, you have to consider thread-safety.


inject one bean into another you can use @ManagedProperty together with a setter and getter.

6. To dene managed beans, use the @ManagedBean annotation. When you want to

5 Using managed beans


Now it's time to use the information discussed in {4} and actually implement something using JSF. 1. What we want to do rst, is to modify the template we did in {3} and output the current date and time inside the footer. For this we need to use a bean. a) Add a JSF bean DateTime<beanType>Bean.java, where <beanType> is the type of bean to use. Consider carefully what type of bean is most appropriate. Remember that we want to minimize resource use. Dene the following method inside:

getCurrentDateTime() - Returns a string with the current date and time (including seconds).

b) Use annotations to annotate the bean and dene what scope it should use. Carefully consider what type of scope you need. c) Modify the template so that it fetches the current time from the bean and puts it into the footer. Redene the footer as a d)

<h:outputText> tag. Style the outputText tag with some CSS that changes the colour to sienna

and makes the font smaller. 2. The public pages

viewCart.xhtml, viewProducts.xhtml and addProduct.xhtml


Your task is to connect Name your classes ViewIn

should all have a managed bean connected to them. the right type of managed bean to each page (view).

Cart<beanType>Bean.java, ViewProducts<beanType>Bean.java and so on.

a later assignment, you will use the data submitted in those beans to accomplish navigation and other actions when a form is submitted. After that, the data from the beans is no longer needed. What is the most appropriate scope for the three beans? 3. Create a shopping cart. The class should hold a list of products that have been added to the cart. The shopping cart class should be a managed bean of some kind. The question is, what kind? Both the view products page and the view cart

page will be able to empty the cart. Also consider what scope is most appropriate
for this bean. You will connect the shopping cart bean to the application at a later stage.

Distributed applications

Adam Waldenberg Joachim von Hacht

4. Next, we will add some standard JSF components to all three pages. Use standard JSF components for the forms (h:componentName). Make sure you connect the values of the components to a property in a managed bean. a)

viewProducts.xhtml - This page will later be extended with functionality


from PrimeFaces. For now, just make sure the page has a form with the following component:


b)

A button for navigating to

addProduct.xhtml. A button for navigating to viewCart.xhmtl.


A button for emptying the shopping cart. An output text describing the number of items currently in the shopping cart.

viewCart.xhtml

- Will also use PrimeFaces later.

Add a form with the

following component to the page: A button for navigating to

viewProducts.xhtml. view-

A button for emptying a shopping cart and then navigating to

Products.xhtml.
cart.

An output text describing the number of items currently in the shopping

c)

addProduct.xhtml - Create a simple form that can be used to add a product.


Add the following components:

A text eld for entering a product name. A text eld for entering a product category. A text eld for entering a price. A submit (add product) button.

5. Connect the components to properties in the managed beans (where it's needed). 6. You should now have three pages that show some content but don't really do anything yet. To add some life to them, we need to add some navigation rules and controllers...

Distributed applications

Adam Waldenberg Joachim von Hacht

6 Navigation and controller beans


Now that you have dened your rst working beans, it's time to continue and add navigation rules to your pages. Look at the following diagram:

viewCart.xhtml
onEmptpyCart onViewCart onViewProducts

viewProducts.xhtml

onViewAddProduct onAddProduct

addProduct.xhtml

1. Your task is to add navigation rules inside faces-cong.xml that work in the above manner. You can either write the rules by hand or use the page ow editor in NetBeans, which can be found when you double click on the faces-cong.xml le. When someone adds a product, the view products page should be shown. Add a rule that can be used to send the browser from the view products page to the add

product page when the button in the former page is pressed.


described by the diagram.

There should also

be similar rules that connect the view cart page with the view products page, as

2. Once the navigation rules for all pages are set up, you need to actually implement each controller bean properly.

You should realize that you do not have to connect all the navigation components to an actual controller method. You should only do this when you really need it. This way, if you need to make simple changes, you won't have to recompile. a) AddProductControllerBean - When the submit button is pressed, add
outcomes of the navigation rules dened in faces-cong.xml. the product to the database and navigate to the view products page. You will have to access the managed bean that holds properties for the page that this controller is connected to. b)

Navigation should be accomplished by using the

ViewCartControllerBean - If the user presses the button for emptying the cart, invalidate the session and then navigate to the view products page. TIP:
FacesContext.getCurrentInstance().getExternalContext()- .getSession(...)...

c)

ViewProductsControllerBean - You should invalidate the session and reload


the page when the button for emptying the cart is pressed.

Distributed applications

Adam Waldenberg Joachim von Hacht

3. You should now have a working application where you can add products to the database and navigate the pages (even if you can't view the cart or the products yet).

7 What about AJAX in JSF?


The template footer outputting the current time is very static and only updates if the page is reloaded. Let's modify it so that the footer updates once every ve seconds or whenever a button is clicked. We can accomplish this by using an very simple JavaScript. 1. To start with, add a button that, when clicked, updates the footer. a) First, add a b)

f:ajax tag and some

h:commandButton with the text Update footer . Connect a f:ajax event to the button. When the button is clicked, it should update the h:outputText tag that outputs the date and time. NOTE: Update only the h:outputText and no other part of the page.

2. (OPTIONAL) Your next task is to refresh the footer once every ve seconds. You have to add the JavaScript that handles this in an unobtrusive manner. The triggering of the update can be solved by executing a request for an update every ve second using

jsf.ajax.request().

This function can be found inside a JavaScript Whenever the faces servlet detects

library included in JSF 2; jsf.js.

f:ajax

tags

upon loading, this JavaScript le should be included in the header automatically, meaning we don't have to include it manually in the template. Inspect the documentation for

jsf.ajax.request() and re an update of the h:outputText inside

the footer once every ve seconds. 3. (OPTIONAL) If you happen to reload the page at the exact moment a request is made using

jsf.ajax.request(), that request will be interrupted and an annoying

10

Distributed applications

Adam Waldenberg Joachim von Hacht

httpError alert will pop up. This is because there is some debugging code present
inside jsf.js for cases where this happens on an actual live page (not when reloading in the browser). This alert can be disabled in a nal application by changing the project stage to production status by modifying web.xml. only be done in a nal release:
<c o n t e x t

NOTE:

This should

param> name>j a v a x . f a c e s . PROJECT_STAGE </paramname> <paramv a l u e >P r o d u c t i o n </paramv a l u e > </ c o n t e x t param>
<param

8 Taking it one step further with PrimeFaces


Adding AJAX functionality completely on your own using

f:ajax,

as you did in the

previous assignment, works. However, this means that you have to make your own custom components if you decide to build your application completely in this manner. This of course means extra work. When doing some of the assignments in the RIA Workshop, you got to use one of the best JavaScript libraries available; JQuery. PrimeFaces is a component suite for JSF, the component are implemented using JQuery. Many updates in PrimeFaces components are automatic (or use an update tag) and do not require the use of

f:ajax to get full AJAX functionality.

1. Modify the database:

viewProducts.xhtml page to use PrimeFaces components and add a

PrimeFaces table. In this table, view all the products that have been added to the

a) You should have columns in the table for all the elds in a product (such as the product name and category). b) You do not have to support pagination. c) When pressing the headers of the table, the items should be sorted according to the pressed column. d) Each row in the table should have two buttons:

One button for removing the product on that row from the database. When a product is removed, it's references in the shopping cart should also be removed.

Another button for adding a product to the shopping cart.

There is a bug in PrimeFaces 2.1 that results in a table not updating if the button with the actionListener is inside a cell in the table. To work around this, you can use update="@form" when dening the actionListener. inside something like a update attribute. This will force an update to the whole form. Another solution is to wrap the table

p:outputPanel

and reference that panel from the

11

Distributed applications

Adam Waldenberg Joachim von Hacht

2. Add a similar PrimeFaces table to the

viewCart.xhtml

page.

Similar requireIn this table

ments are present here as in the table mentioned earlier. The dierence is of course that this table should instead display items in the shopping cart. shopping cart). you will only require one button on each row (for removing the product from the

9 Validating bean properties


Next, we will add bean property validation to one of the pages. While it's possible to dene custom validators you should use the annotations that are available in JSF. They cover the most common validations needed. 1. Modify

addProduct.xhtml

to use PrimeFaces components.

The advantage of

using PrimeFaces components is that we can validate in real-time using AJAX whenever that functionality is needed. 2. Add annotations to the properties of the add product page. The following should hold: a) The price should be a valid number (with or without decimals). b) The product name has to be at least 3 characters long and can't be empty. c) The product category can be empty. 3. When you try to submit an incorrect form in

addProduct.xhtml, there should p:commandButton

be an error message shown next to the entry where an error was found. 4. For this to update with AJAX, you need to actually use PrimeFaces components for everything and specify the area to update whenever the attached inside a is pressed (see the update attribute). Put all the components that have messages

p:panel and give the panel an id.

You can then reference that

panel from the command button. 5. As you might have noticed, the validation doesn't run until you actually press the command button. This can be improved by using JavaScript and adding a form submission call to the onblur attribute of each inputText. You can also use eect.

p:ajax

with onblur to validate the form asynchronously with AJAX and achieve a similar

10 Server push with ICEFaces (OPTIONAL)


Server push simply means that the server can continuously push data to the client. The client can then fetch the data and update it's view accordingly. implemented dierently in dierent component libraries. tions, while others use polling on the client side. This functionality is Some use persistent connec-

While server push is implemented

in PrimeFaces, it isn't as robust as the implementation in ICEFaces and requires more

12

Distributed applications

Adam Waldenberg Joachim von Hacht

work from the programmer than it's counterpart in ICEFaces.

Therefore, we will use

ICEFaces to demonstrate a practical example of how server push can be used. You will implement a very simple web based notice board using ICEFaces and it's SessionRenderer class. Dierent browsers will be able to share the same notice board and see changes in real-time. 1. Create a new Maven web application called

jsfnoticeboard.nbp.

2. The central Maven repository seems to be missing the ICEFaces push server. Therefore, we have to add the ICEFaces repository in order to get access to it. Add the following dependencies to your
<r e p o s i t o r i e s > <r e p o s i t o r y > <i d >I C E F a c e s Maven Repo</ i d >

pom.xml:

<u r l >h t t p : / / a n o n s v n . i c e f a c e s . o r g / r e p o / maven2 / r e l e a s e s /</ u r l > </ r e p o s i t o r y > </ r e p o s i t o r i e s >

<d e p e n d e n c y > <g r o u p I d >o r g . i c e f a c e s </ g r o u p I d > < a r t i f a c t I d > i c e f a c e s </ a r t i f a c t I d > <v e r s i o n >2.0.2 </ v e r s i o n > <e x c l u s i o n s > <e x c l u s i o n > <g r o u p I d >j a v a x . f a c e s </ g r o u p I d > < a r t i f a c t I d >j s f </ e x c l u s i o n > <e x c l u s i o n > <g r o u p I d >j a v a x . f a c e s </ g r o u p I d > < a r t i f a c t I d >j s f </ e x c l u s i o n > </ e x c l u s i o n s > </d e p e n d e n c y > <d e p e n d e n c y > <g r o u p I d >o r g . i c e f a c e s </ g r o u p I d > < a r t i f a c t I d >i c e f a c e s

i m p l </ a r t i f a c t I d >

a p i </ a r t i f a c t I d >

compat </ a r t i f a c t I d >

<v e r s i o n >2.0.2 </ v e r s i o n > </d e p e n d e n c y > <d e p e n d e n c y > <g r o u p I d >o r g . i c e f a c e s </ g r o u p I d > < a r t i f a c t I d >i c e f a c e s

a c e </ a r t i f a c t I d >

<v e r s i o n >2.0.2 </ v e r s i o n > </d e p e n d e n c y > <d e p e n d e n c y > <g r o u p I d >o r g . i c e p u s h </ g r o u p I d > < a r t i f a c t I d >i c e p u s h </ a r t i f a c t I d > <v e r s i o n >2.0.2 </ v e r s i o n > </d e p e n d e n c y >

3. Remove

index.jsp and replace it with an index.xhtml page. Make sure there is a valid web.xml. Also make sure that the index page is the default welcome page and also add the following to web.xml:

13

Distributed applications

Adam Waldenberg Joachim von Hacht

<s e r v l e t > <s e r v l e t <s e r v l e t

name>R e s o u r c e c l a s s >

S e r v l e t </ s e r v l e t

name>

com . i c e s o f t . f a c e s . webapp . C o m p a t R e s o u r c e S e r v l e t </ s e r v l e t <l o a d

c l a s s > ons t a r t u p >1</ l o a d ons t a r t u p >

</ s e r v l e t > <s e r v l e t

mapping> name>R e s o u r c e S e r v l e t </ s e r v l e t name> <u r l p a t t e r n >/ x m l h t t p / </ u r l p a t t e r n > </ s e r v l e t mapping>
<s e r v l e t

4. Add a version 2

faces-cong.xml, like you did in the previous project.

5. In the index page, add the following: a) A header describing the page. b) An ICEFaces series panel that can hold an arbitrary number of notices (elements). You can nd more information about how the series panel in ICEFaces works at the ICEFaces Components Showcase site; http://component-

showcase.icefaces.org/. Inside each notice in the series panel you should put
a removal button of some kind (for removing the notice). c) A text entry (where a new notice can be entered) and a button to submit that notice to the notice board. 6. Create the model bean

NoticeBoardModelBean.java, that holds a certain amount

of notices posted to the notice board. What is the appropriate scope for this bean? Each notice in the model should hold the following information: a) The notice (message) itself. b) An id to uniquely identify a notice. c) The IP address of the client that added the notice. d) The server time that the notice was added. Show this information on the page in each individual element of the series panel. 7. Create a backing bean that connects to the properties of the components on the page, call it

NoticeBoardBackingBean.java.

14

Distributed applications

Adam Waldenberg Joachim von Hacht

8. Make the notice board functional and style all your pages with CSS. You can review the screen shots above in order to get some ideas for the nal layout. Notice how the button in the image to the right is ghosted when the notice board is full. a) You should dene the le the following: i. Remove individual messages from the notice board. ii. Add new messages to the notice board. iii. If the board is full, the button for adding a new notice should be ghosted. 9. Once you are done, try to visit the application with two browsers. client update whenever another client posts or removes a notice. 10. Read the documentation for the ICEFaces SessionRenderer class. We will use this class in order to take advantage of the push functionality in ICEFaces. Make sure you understand how it works before you continue. 11. When the You should

NoticeBoardControllerBean.java

and imple-

ment any listeners in that le. From the page, the user should be able to do

notice that it doesn't work as one would want. Using server push, we will force a

NoticeBoardBackingBean initializes, it should join the NOTICE_BOARD

group using:

SessionRenderer.addCurrentSession(NOTICE_BOARD)

When the bean is later destroyed, it should remove itself from the NOTICE_BOARD group. When the controller bean makes a change to the model, you should call:

SessionRenderer.render(NOTICE_BOARD)

Whenever a client then calls render(), ICEFaces will force an update to all other clients part of the same group. 12. Visit the application with two browsers again. expected. Hopefully, it's now working as

15

You might also like