Workshop 5: Java Server Faces (JSF)
Workshop 5: Java Server Faces (JSF)
Workshop 5: Java Server Faces (JSF)
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.
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
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.
NOTE
web.xml.
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
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
index.jsp le (under Web Pages) that was automatically created with pom.xml, taking care of where you paste
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
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 (
f i l e >
dProduct.xhtml.
viewCart.xhtml, viewProducts.xhtml
and
ad-
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.
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
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.
Distributed applications
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).
found at http://commons.wikimedia.org/wiki/File:Shop.svg.
(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
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
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
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
5.
6. To dene managed beans, use the @ManagedBean annotation. When you want to
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
should all have a managed bean connected to them. the right type of managed bean to each page (view).
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
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)
b)
viewCart.xhtml
viewProducts.xhtml. view-
Products.xhtml.
cart.
c)
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
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
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)
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)
Distributed applications
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).
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
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
the footer once every ve seconds. 3. (OPTIONAL) If you happen to reload the page at the exact moment a request is made using
10
Distributed applications
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
f:ajax,
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
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.
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
11
Distributed applications
viewCart.xhtml
page.
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
addProduct.xhtml
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
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
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
12
Distributed applications
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:
<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 >
<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
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>
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
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
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
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
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