JBoss RichFaces with Spring
This article is going to show you how to build a RichFaces application with Spring.
Join the DZone community and get the full member experience.
Join For FreeThis article is going to show you how to build a RichFaces application with Spring. We are going to replace the standard JSF managed beans with Spring beans. We will first use an XML-based configuration approach and then switch to an annotations approach in Spring.
(Note - The Project Template for this article can be )
If you want first to try RichFaces with standard JSF managed beans, no problem. .
To make it possible to create the application in just under an hour, we are not going to persist any data into a database. To keep things simple, we will keep the data in memory. Of course it's not very difficult to add a persistence layer to this application. Spring provides many tools to do that.
Finally an article on how to use RichFaces with Spring Web Flow, is something that I'm saving for a future article.
What Are We Going to Build?
A pretty common request that I hear is how do you build a wizard in RichFaces. So, that's exactly what we are going to build. You come into a bar and on each table there is a screen via which you place an order. You click to start an order, a wizard is launched where you enter all the required information and place the order. We will also have the ability to view all placed orders. Additionally, we will be able to change the look and feel of the ordering screen using RichFaces’ skinnability feature.
Note: All screen shots show a custom skin which you are going to create later.
JBoss RichFaces
JBoss RichFaces is a framework that consists of three main parts: AJAX (rich) enabled JSF components, skinnability, and CDK (Component Development Kit). RichFaces UI components are divided into two tag libraries a4j: and rich:. Both tag libraries offer out-of-the-box AJAX enabled JSF components. Skinnability enables you to skin a JSF application with default or custom skins (themes). Finally, the CDK is a facility for creating, generating and testing rich JSF components.
RichFaces is not a JSF implementation. RichFaces UI components are just extra AJAX JSF components that work with any JSF implementation (1.1, 1.2, 2.0), any view technology (Facelets, JSP), and are integrated with 3rd party components (MyFaces, Tomahawk, Trinidad, etc).
Installing RichFaces
Installing RichFaces is very easy.
Download
Download the latest RichFaces version from http://www.jboss.org/jbossrichfaces/ .
Add three RichFaces JAR files to your application WEB-INF/lib directory:
- richfaces-api-X.X.X.jar,
- richfaces-impl-X.X.X.jar,
- richfaces-ui-X.X.X.jar
RichFaces also depends on the following libraries: commons-beanutils.jar, commons-collections.jar, commons-digester.jar, commons-logging.jar
As of the writing of this article, the latest RichFaces version is 3.3.0 .
Note: to use Hibernate Validation, additional JAR files are needed. The JAR files are included in the project.
RichFaces Filter
Register the RichFaces filter in a web.xml file:
<filter> <display-name>Ajax4jsf Filter</display-name> <filter-name>richfaces</filter-name> <filter-class>org.ajax4jsf.Filter</filter-class> </filter> <filter-mapping> <filter-name>richfaces</filter-name> <servlet-name>Faces Servlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping>
Skinning
Optionally, to use one of the existing skins, add the following:
<context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>laguna</param-value></context-param>
Note: new skins such as laguna, glassX and darkX are available in their own separate JAR files.
Page Setup
If you are using Facelets, then add this to your page:
xmlns:a4j="http://richfaces.org/a4j"xmlns:rich="http://richfaces.org/rich"
Just in case you are still stuck with JSPs, you would then add this to your pages:
<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%><%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
A little bit further in this article, I have created a ready-to-use Eclipse project with RichFaces and Spring already configured. We are going to import the project into Eclipse and then start development. In the “real” world, you would probably use Maven 2, but, to keep things simple and concentrate on RichFaces and Spring, we are going to use a ready-made project.
Spring
Not to repeat what has been said about Spring in numerous great articles and other resources, I will not list every Spring feature. You can find numerous resources on the Internet. Simply stated, Spring is a framework that greatly simplifies enterprise Java development.
The part that's of interest to us here is Spring beans and injection of control (there's ). Spring is often a great candidate for a middle tier in a Web application. While JSF offers us managed beans and basic dependency injection, Spring's dependency injection is much more powerful. Why use two containers (JSF and Spring) to manage beans when you can use just one?
As I mentioned at the beginning, I will show you two ways to configure Spring beans. One is XML-based and the other is annotation-based. Even the XML-based configuration significantly reduces the amount of XML you have to write compared to the XML configuration of JSF managed beans.
Installing Spring
For the purpose of this example, Spring is installed and configured as follows.
The following JAR files are added to the application:
- spring-beans.jar
- spring-context.jar
- spring-core.jar
- spring-web.jar
- slf4-api-1.5.5.jar
- slf4-simple-1.5.5.jar
Registering Spring in web.xml
Add the following to web.xml file:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-beans.xml</param-value></context-param><listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class></listener><listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class></listener>
The contextConfigLocation parameter specifies a Spring beans configuration file where Spring beans are going to be defined and managed. It looks (when almost empty) like this:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config /></beans>
We will be adding more stuff as we go. For now we only have <context:annotation-config>. This tag enables support for Java EE 5 annotations such as @PostConstruct, @PreDestory, and @Resource.
JSF Configuration File
As we are going to use Spring beans, we want Spring to resolve the beans and thus need to register Spring's EL resolver in a JSF configuration file:
<application> <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> </application>
That's all the configuration you need to use RichFaces and Spring.
Getting the Project
I have used JBoss Tools to build the project. You can of course use any tool of your choice. Below I show steps for how to import the project as a JSF project into JBoss Tools. You can also import the project as an existing Eclipse project. It's up to you. If you need to install JBoss Tools, here is a [link]
1. Download this project
2. Import the project into Eclipse
1. File/Import/Other
2. Select JSF Project
3. Point to web.xml file and follow the wizard steps.
Before we continue, let's make sure that the project was deployed correctly and that we can run it. Start the server by right-clicking Tomcat in the Servers view and selecting Run.
Right-click the project and select Run As\Run On Server. You should get this welcome page.
Assembling the Application
Creating the Model
Let's start by creating the model for an order. In a real application, this object would usually be an entity – an object that is saved and retrieved from a database.
The class is called Order and looks like this (note the package):
package bar.model;public class Order { private String name; private String email; private String drink; private String comments; public Order(String name, String email, String drink, String comments) { super(); this.name = name; this.email = email; this.drink = drink; this.comments = comments; // getter and setters for each property}
Don't forget to generate getters and setters for each property. There is nothing interesting about this object.
Creating the Order Service
Next we are going to create a service class that knows how to add a new order. You can place other methods into this service, such as finding and deleting orders.
The OrderService class looks like this:
package bar.service;import java.util.ArrayList;import java.util.List;import javax.annotation.PostConstruct;import bar.model.Order;public class OrderService { private List <Order>orders; public List<Order> getOrders() { return orders; } public void addOrder (String name, String email, String drink, String comments){ Order order = new Order(name, email, drink, comments); orders.add(order); } @PostConstruct public void create (){ orders = new ArrayList <Order> (); }}
A few things to note:
- This service class will keep the list of all orders.
- The addOrder method adds a new order to the list.
- The @PostConstruct annotation marks the create() method to be invoked right after the OrderService class has been created. To be more technically correct, this method is called after all injections have been satisfied. While the class doesn't have dependencies, this method is still invoked and is a good place to initialize any properties. In our case, we initialize the list object.
Registering as a Spring Bean
We also need to register the Service bean with Spring. We want Spring to manage this bean. Remember that we will first use XML to configure that. Registration looks like this:
<bean id="service" class="bar.service.OrderService" scope="session" />
id is just the id or the name of this bean.
We are placing this bean in session scope because we are keeping the list of all orders in it.
We used the @PostConstruct annotation to initialized the list property. This is a standard Java 5 annotation which is supported by Spring.
Note: for @PostConstruct, @PreDestory and @Resource annotations to work, the following has to be added to Spring configuration file:
<context:annotation-config/>
Alternatively , Spring also provides init-method attribute when registering the bean as shown here:
<bean id="orderService" class="bar.service.Service" scope="session" init-method="create"/>
This attribute points to a custom initialization method to invoke after setting bean properties.
A third option is to implement InitializingBean and DisposableBean interfaces: [http://static.springframework.org/spring/docs/2.5.x/reference/beans.html].
Next we are going to create two view beans, one for the wizard and one for displaying current orders.
View Beans
Let's start with BarBean class:
package bar.view;import java.util.List;import bar.service.Service;public class BarBean { private OrderService orderService; @SuppressWarnings("unchecked") public List getOrders () { return orderService.getOrders(); } public int getRowCount () { return orderService.getOrders().size(); } public Service getOrderService() { return orderService; } public void setOrderService(Service orderService) { this.orderService = orderService; }}
getOrders method calls the service class and returns all the orders.
getRowCount will return the number of rows. It's used not to render rich:dataTable component if the order is empty.
The other question is how do we get an instance of orderService into this bean. First, let's register this bean in Spring configuration file:
<bean id="barBean" class="bar.view.BarBean" scope="request"/>
We still have to deal with userService and how it gets initialized inside the bean. This is were Spring DI comes into play. We want Spring to inject userService into the barBean, when barBean is being created. To accomplish that, we modify the configuration to look like this:
<bean id="barBean" class="bar.view.BarBean" scope="request"> <property name="orderService" > <ref bean="service" /> </property></bean>
service is the name under which we registered the Service class.
We are ready to look at WizardBean class. This class is the back end for the wizard.
package bar.view;import java.util.List;import javax.annotation.PostConstruct;import javax.faces.context.FacesContext;import javax.faces.event.ValueChangeEvent;import javax.faces.model.SelectItem;import org.hibernate.validator.Email;import org.hibernate.validator.NotEmpty;import org.hibernate.validator.Pattern;import bar.service.Service;import bar.utils.JSFUtils;public class WizardBean implements java.io.Serializable { private static final long serialVersionUID = 1L; @NotEmpty(message = "Name must not be empty") @Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces") private String name; @NotEmpty @Email(message = "Invalid email format") private String email; private String drink; private String comments; private String drinkCategorySelected; private Service orderService; private String startPage; private List<SelectItem> drinkCategory; private List<SelectItem> drinkList; // init all drinks private static final String [] CATEGORY = {"Brandy", "Rum", "Tequila", "Whiskey", "Wine", "Beer"}; private static final String [] BRANDY = {"XO", "VSOP", "VS"}; private static final String [] RUM = {"Medium", "Full-bodied", "Aromatic"}; private static final String [] TEQUILA = {"Reposado", "Añejo", "Blanco"}; private static final String [] WHISKEY = {"Malt", "Grain", "Single Malt", }; private static final String [] WINE = {"Red", "White", "Pink"}; private static final String [] BEER = {"Ales", "Lager", "Specialty Beer", }; public void changeDrink(ValueChangeEvent event) { String newValue = (String) event.getNewValue(); if (newValue.equals("Brandy")) {drinkList = JSFUtils.createList(BRANDY); drink=BRANDY[0];} else if (newValue.equals("Rum")) {drinkList = JSFUtils.createList(RUM); drink=RUM[0];} else if (newValue.equals("Tequila")) {drinkList = JSFUtils.createList(TEQUILA);drink=TEQUILA[0];} else if (newValue.equals("Whiskey")) {drinkList = JSFUtils.createList(WHISKEY);drink=WHISKEY[0];} else if (newValue.equals("Wine")) {drinkList = JSFUtils.createList(WINE);drink=WINE[0];} else if (newValue.equals("Beer")) {drinkList = JSFUtils.createList(BEER);drink=BEER[0];} } @PostConstruct public void create() { drinkCategorySelected = CATEGORY[0]; drinkCategory = JSFUtils.createList(CATEGORY); drinkList = JSFUtils.createList(BRANDY); drink = BRANDY[0]; } public void save() { orderService.addOrder(name, email, drink, comments); this.startPage = "/page1.xhtml"; FacesContext.getCurrentInstance().getExternalContext().getRequestMap() .remove("wizardBean"); }}
Don't forget to generate setters and getters for each property. I have omitted them in order to save space.
It looks like a lot more happening in this class, but in reality not that much. First, there are propeties such as email, name, drink and comments which correspond to different wizard screens. If you notice, they also correspond to Order model class.
email and name properties are annotated with Hibernate Validations annotations. We will come back to them when we build the pages.
The large section preceded by // init all drinks comment is how drinks list is created. It's not the most pretty way, but it will do for this example.
There is also JSF changeDrink value change listener. It will be invoked each time a new drink category is selected and will load the drinks associated with that category.
The save method saves the new order. The last line in save method removes the bean from request scope. We will come back to it shortly.
In this class as in BarBean, we have a reference to orderService. No where in the class we instantiate the property. You probably guessed, but we are going to use Spring's DI to inject the service into this bean. Let's register this bean in Spring configuratin file.
<bean id="wizardBean" class="bar.view.WizardBean" scope="request"> <property name="startPage" value="/page1.xhtml"/> <property name="orderService" > <ref bean="service" /> </property></bean>
startPage property is initialized to the first page in the wizard.
orderService is injected with service bean.
One last class is the JSFUtils:
package bar.utils;import java.util.ArrayList;import java.util.List;import javax.faces.model.SelectItem;public class JSFUtils { public static List <SelectItem> createList (String [] data){ ArrayList <SelectItem>list = new ArrayList <SelectItem>(); for (String item : data){ list.add (new SelectItem (item, item)); } return list; }}
This only method in this class creates a list of SelectItem's. We are ready to create the views.
Creating Views
Let's start with the main view which has a button to launch the wizard as well as display all current orders.
start.xhtml:
<h:form> <!-- launch wizard button --> <rich:panel header="Welcome to RichBar"> <a4j:commandButton value="Click to start your order" oncomplete="#{rich:component('wizard')}.show()" /> </rich:panel> <rich:editor rendered="false" /></h:form><!-- modal panel where the wizard is running --><rich:modalPanel id="wizard" width="550" height="300"> <f:facet name="header">Drink Order</f:facet> <f:facet name="controls"> <a href="#" onclick="#{rich:component('wizard')}.hide()">X</a> </f:facet> <h:panelGroup id="wizardInclude"> <a4j:include viewId="#{wizardBean.startPage}" /> </h:panelGroup></rich:modalPanel><!-- order table --><a4j:outputPanel id="orders"> <rich:dataTable value="#{barBean.orders}" var="order" rendered="#{barBean.rowCount>0}" rowKeyVar="row"> <rich:column> <f:facet name="header">Order #</f:facet> <h:outputText value="#{row+1}" /> </rich:column> <rich:column> <f:facet name="header">Name</f:facet> <h:outputText value="#{order.name}" /> </rich:column> <rich:column> <f:facet name="header">Email</f:facet> <h:outputText value="#{order.email}" /> </rich:column> <rich:column> <f:facet name="header">Drink</f:facet> <h:outputText value="#{order.drink}" /> </rich:column> <rich:column> <f:facet name="header">Comments</f:facet> <h:outputText value="#{order.comments}" escape="false" /> </rich:column> </rich:dataTable></a4j:outputPanel></body>
There are three main things here.
At the very top of the page, we place a button to launch the wizard. a4j:commandButton opens the modal panel by using a built-in RichFaces JavaScript function rich:component('id') and invoking the show() method on the modal panel.
Next we have the actual modal panel. Two facets define modal panel header and a control to close the modal panel. To close the modal panel we use the hide() JavaScript function on the modal panel. Next is a4j:include tag. The tag works similar to Facelets ui:include but also allows to navigate within the included content. That's what's going to give us wizard functionality. Remember that #{wizardBean.startPage} is initialized to /page1.xhtml in Spring configuration file.
The last portion is a rich:dataTable control that displays all current orders. The table is not rendered if the list is empty. We have also defined rowKeyVar attribute which holds the current row number. We use it as order number in the display.
The next four pages are part of the wizard.
page1.xhtml
<h:form xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j"> <a4j:keepAlive beanName="wizardBean" /> <rich:panel> <h:panelGrid> <h:panelGroup> <h:outputLabel value="Email:" for="email" /> <h:outputText value=" (We will email you your receipt!)" style="FONT-SIZE: small;" /> </h:panelGroup> <h:panelGroup> <h:inputText id="email" value="#{wizardBean.email}"> <rich:ajaxValidator event="onkeyup" /> </h:inputText> <rich:message for="email" /> </h:panelGroup> <a4j:commandButton value="Next" action="next" /> </h:panelGrid> </rich:panel></h:form>
In the first page of the wizard, we are only asking for an email so we can send the customer the receipt via email (just like in Apple stores). Notice that the page uses rich:ajaxValidator to validate the email field. rich:ajaxValidator works against standard Hibernate Validation set in the bean:
@NotEmpty@Email(message = "Invalid email format")private String email;
This eliminates having to register a validator with the component on the page. Another benefit is that rich:ajaxValidator skips the Process Validations and Update Model phases. As we are only validating the field, we can slightly increase performance by not executing those phases. Skipping those two phases is identical to setting bypassUpdates="true" (see RichFaces developers guide for further information).
One last thing we need to cover is a4:keepAlive tag placed at the top of the page. If you look at wizardBean registration in Spring configuration file, you will notice that the scope of this bean is request. As you know, this bean is what backing up the wizard screens. Now, as we move from wizard screen to wizard screen, the values entered on the previous screen are somehow being remembered. They are being remembered because on the last screen (summary.xhtml), we are shown all the values entered. But the bean is in request scope which means that for every request a new bean is created and if that's the case then the old value will be lost. So, how are the values saved?
That's exactly what a4j:keepAlive tag does. It stores the bean (wizardBean) with the UI component tree. On postback, the UI component tree is restored and also restores the bean placing it back into request scope. This is basically a page scope, as long as we are staying on this page, the bean and its properties will be available to us on postback.
The way a4j:keepAlive works is that it adds the bean to current UI view root. The following is important, even though we placed a4j:keepAlive in the first wizard screen (page1.xhtml), technically, the bean will be added to main.xhtml UI view root. That's also the reason we only had to place a4j:keepAlive on the first page without having it on other wizard screens. Again, by placing a4j:keepAlive in the first included page (page1.xhtml), we really added the tag to main.xhtml view.
Now, because we are always staying on the same page, the wizardBean will always be there. This is the correct behavior as we are basically getting page scope. However, we want to clear the wizardBean in order to launch it again and not have old values populated. Thus, we have to programmatically end the page scope and restart it on next wizard launch. That's exactly what the last line in save method does:
public void save() { orderService.addOrder(name, email, drink, comments); FacesContext.getCurrentInstance().getExternalContext().getRequestMap() .remove("wizardBean");}
page2.xhtml
<h:form xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j"> <rich:panel> <h:panelGrid> <h:outputLabel value="Name:" for="name" /> <h:panelGroup> <h:inputText id="name" value="#{wizardBean.name}" > <rich:ajaxValidator event="onkeyup"/> </h:inputText> <rich:message for="name"/> </h:panelGroup> <h:panelGroup> <a4j:commandButton value="Previous" action="prev" /> <a4j:commandButton value="Next" action="next" /> </h:panelGroup> </h:panelGrid> </rich:panel></h:form>
The second page in the wizard prompts to enter the name. We again use rich:ajaxValidator to validate the name field with Hibernate Validation. Validation is defined as follows:
@NotEmpty(message = "Name must not be empty")@Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces")private String name;
page3.xhtml
<h:form xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j"> <rich:panel> <h:panelGrid> <h:panelGrid columns="2"> <h:outputLabel value="Category:" for="want" /> <rich:inplaceSelect id="want" value="#{wizardBean.drinkCategorySelected}" valueChangeListener="#{wizardBean.changeDrink}"> <f:selectItems value="#{wizardBean.drinkCategory}"/> <a4j:support event="onviewactivated" ajaxSingle="true" reRender="drink"/> </rich:inplaceSelect> <h:outputLabel value="Drink:" for="drink" /> <rich:inplaceSelect id="drink" value="#{wizardBean.drink}" > <f:selectItems value="#{wizardBean.drinkList}"/> </rich:inplaceSelect> </h:panelGrid> <h:panelGroup> <a4j:commandButton value="Previous" action="prev" /> <a4j:commandButton value="Next" action="next" /> </h:panelGroup> </h:panelGrid> </rich:panel></h:form>
The third screen in the wizard uses two rich:inplaceSelect components. The first one allows you to select a drink category. Once a category is selected, drinks that belong to that category are loaded in the second select via AJAX. Value change listener wizardBean.changeDrink is called and loads the drinks associated with selected category. Using a4j:support tag we attached an onchange event to rich:inplaceSelect UI component to be fired when value changes. When the event is fired, an AJAX request is sent. ajaxSingle="true" means we only want to process that UI component on the server. Finally, reRender points to the second list we are rendering back now with a list of drinks.
page4.xhtml
<h:form xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j"> <rich:panel> <h:panelGrid> <h:outputLabel value="Anything else?" for="comments" /> <rich:editor id="comments" autoResize="true" value="#{wizardBean.comments}"> </rich:editor> <h:panelGroup> <a4j:commandButton value="Previous" action="prev" /> <a4j:commandButton value="Next" action="next" /> </h:panelGroup> </h:panelGrid> </rich:panel></h:form>
This page uses one of the new UI components introduced in RichFaces 3.3.0 – a rich editor. rich:editor is based on tinyMCE widget. Rich:editor can be configured in many different ways, here we are using a very simple configuration. You can check out another example here.
summary.xhtml
<h:form xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j"> <rich:panel> <h:panelGrid columns="2"> <f:facet name="header">Summary</f:facet> <h:outputText value="Name" /> <h:outputText value="#{wizardBean.name}" /> <h:outputText value="Email:" /> <h:outputText value="#{wizardBean.email}" /> <h:outputText value="Drink ordered:" /> <h:outputText value="#{wizardBean.drink}" /> <h:outputText value="Comments:" /> <h:outputText value="#{wizardBean.comments}" escape="false"/> </h:panelGrid> <h:panelGrid columns="2"> <a4j:commandButton value="Wait.. Go back" action="prev" /> <a4j:commandButton value="Place Order" action="#{wizardBean.save}" oncomplete="#{rich:component('wizard')}.hide()" reRender="orders, wizard"/> </h:panelGrid> </rich:panel></h:form>
The last page is just a summary page. If any corrections are needed, it's possible to click the back button. The Place Order button is bound to a save method and once the action is completed we close the modal panel and re render the table – to update the orders.
Creating Navigation
We won't be able to navigation inside the wizard without defining navigation rules. Navigation rules are defined in JSF configuration file and look like this:
<navigation-rule> <from-view-id>/page1.xhtml</from-view-id> <navigation-case> <from-outcome>next</from-outcome> <to-view-id>/page2.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/page2.xhtml</from-view-id> <navigation-case> <from-outcome>next</from-outcome> <to-view-id>/page3.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>prev</from-outcome> <to-view-id>/page1.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/page3.xhtml</from-view-id> <navigation-case> <from-outcome>next</from-outcome> <to-view-id>/page4.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>prev</from-outcome> <to-view-id>/page2.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/summary.xhtml</from-view-id> <navigation-case> <from-outcome>prev</from-outcome> <to-view-id>/page3.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/page4.xhtml</from-view-id> <navigation-case> <from-outcome>next</from-outcome> <to-view-id>/summary.xhtml</to-view-id> </navigation-case> <navigation-case> <from-outcome>prev</from-outcome> <to-view-id>/page3.xhtml</to-view-id> </navigation-case> </navigation-rule>
Creating Custom Skins
If you open web.xml file, you will see the following entry that defines the current skin used in the application:
<context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>laguna</param-value></context-param>
It's very easy to create a custom skin by changing just a few skin properties. Skinnability is just an extension to CSS that allows to change the look and feel of entire application by modifying a simple property file.
Create a new property file in JavaSource root, call it laguna-largefont.skin.properties (all skins have to follow this naming convention):
baseSkin=lagunageneralSizeFont=20pxheaderSizeFont=20px
All we are doing is using the existing laguna skin and overwriting just two properties. Now just update skin in web.xml file:
<context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>laguna-largefont</param-value></context-param>
Save, restart and run the application.
At this point save everything and run the application.
Learn how to change skins in runtime.
Switching to Spring Annotations
Using annotations instead off XML-base configurations is the fashionable thing in Java enterprise development today. On a more serious note, both approaches are needed. I will leave it up to you to decide which to use and when. There are lots of resources on the Internet the deal with that question.
Using annotations does make it a little easier to develop the application you don't need to worry about XML files which can grow rather large. In this last section, we are going to use annotations instead of XML-based configuration.
First, open WEB-INF/spring-beans.xml and add the following tag to support annotations-based configuration:
<context:component-scan base-package="bar" />
base-package is the package from which Spring container will start looking for annotated classes.
Next we should comment (or delete) the XML-based bean configuration:
<!--<bean id="barBean" class="bar.view.BarBean" scope="request"> <property name="orderService" > <ref bean="service" /> </property></bean><bean id="wizardBean" class="bar.view.WizardBean" scope="request"> <property name="startPage" value="/page1.xhtml"/> <property name="orderService" > <ref bean="service" /> </property></bean><bean id="service" class="bar.service.Service" scope="session" />-->
Let's start with bar.service.Service bean:
We are going to annotate it as a Spring component:
@Service("orderService")@Scope ("session")public class OrderService {...}
@Service is basically a specialization of @Component annotation and is better suited to annotate components that perform service-layer functionality. @Scope annotation specified the scope into which this object will be placed. If we didn't specify a scope, the default scope would be Singleton which means there is just a once instance of this object in application which would also work in our case.
Next we need to annotate barBean and wizardBean classes. Both have identical annotations. Let's first start with BarBean.
@Component("barBean")@Scope("request")public class BarBean { ...}@Autowiredprivate OrderService orderService;
@Component registers the bean as a component with name specified in parenthesis. One last thing is that we have to inject the service component into this bean. This is done via @Autowired annotation. The annotation will inject a dependency based on type (@Qualified annotation can be used to to gain more control in case Spring context contains more than one object of the expected type).
Finally, WizardBean looks similar:
@Component("wizardBean")@Scope("request"public class WizardBean {...}@Autowiredprivate OrderService orderService;
Well, we are done. Save and restart the server.
Summary
I'm hoping this example gave you a good idea of how to use RichFaces with Spring. While it can still be considered “introductory”, we covered some core concepts and features. If you are just starting with RichFaces, I suggest first to try this example which uses standard JSF managed beans and then move to this one. For more RichFaces examples and tips, check out my blog.
Opinions expressed by DZone contributors are their own.
Comments