Sie sind auf Seite 1von 32

JBoss RichFaces with Spring

This 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 XMLbased configuration approach and then switch to an annotations approach in Spring. (Note - The Project Template for this article can be downloaded here) If you want first to try RichFaces with standard JSF managed beans, no problem. Start with this article. 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, commonscollections.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: 01.<filter> 02. <display-name>Ajax4jsf Filter</display-name> 03. <filter-name>richfaces</filter-name> 04. <filter-class>org.ajax4jsf.Filter</filter-class> 05.</filter> 06.<filter-mapping> 07. <filter-name>richfaces</filter-name> 08. <servlet-name>Faces Servlet</servlet-name> 09. <dispatcher>REQUEST</dispatcher> 10. <dispatcher>FORWARD</dispatcher> 11. <dispatcher>INCLUDE</dispatcher> 12.</filter-mapping>
view source print?

Skinning

Optionally, to use one of the existing skins, add the following: 1.<context-param> 2. <param-name>org.richfaces.SKIN</param-name> 3. <param-value>laguna</param-value> 4.</context-param>
view source print?

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:
view source print?

1.xmlns:a4j="http://richfaces.org/a4j" 2.xmlns:rich="http://richfaces.org/rich"

Just in case you are still stuck with JSPs, you would then add this to your pages:
view source print?

1.<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%> 2.<%@ 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 a great article on it here ). 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: 01.<context-param> 02. <param-name>contextConfigLocation</param-name> 03. <param-value>/WEB-INF/spring-beans.xml</param-value> 04.</context-param> 05.<listener> 06. <listener class> org.springframework.web.context.ContextLoaderListener 07. </listener-class> 08.</listener> 09.<listener> 10. <listener-class> 11. org.springframework.web.context.request.RequestContextListener 12. </listener-class> 13.</listener>
view source print?

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:

view source print?

01.<?xml version="1.0" encoding="UTF-8"?> 02.<beans xmlns="http://www.springframework.org/schema/beans" 03.xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"xmlns:context="http://www.springframework.org/schema/context" 04.xsi:schemaLocation="http://www.springframework.org/schema/beans 05.http://www.springframework.org/schema/beans/spring-beans.xsd 06.http://www.springframework.org/schema/context 07.http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 08. 09.<context:annotation-config /> 10.</beans>

We will be adding more stuff as we go. For now we only have <context:annotationconfig>. 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: 1.<application> 2. <el-resolver> org.springframework.web.jsf.el.SpringBeanFacesELResolver </el-res olver> 3.</application>
view source print?

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): 01.package bar.model; 02. 03.public class Order { 04. 05.private String name; 06.private String email; 07.private String drink; 08.private String comments; 09. 10.public Order(String name, String email, String drink, String comments) { 11. super(); 12. this.name = name; 13. this.email = email; 14. this.drink = drink; 15. this.comments = comments; 16. 17. // getter and setters for each property 18.}
view source print?

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: 01.package bar.service; 02. 03.import java.util.ArrayList; 04.import java.util.List; 05.import javax.annotation.PostConstruct; 06.import bar.model.Order; 07. 08.public class OrderService { 09. 10. private List <Order>orders; 11. 12. public List<Order> getOrders() {
view source print?

13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25.}

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: 1.<bean id="service" class="bar.service.OrderService" scope="session" />
view source print?

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: 1.<context:annotation-config/>
view source print?

Alternatively , Spring also provides init-method attribute when registering the bean as shown here: 1.<bean id="orderService" class="bar.service.Service" scope="session" init-method="create"/>
view source print?

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: 01.package bar.view; 02. 03.import java.util.List; 04.import bar.service.Service; 05. 06.public class BarBean { 07. 08. private OrderService orderService; 09. 10. @SuppressWarnings("unchecked") 11. public List getOrders () { 12. return orderService.getOrders(); 13. } 14. public int getRowCount () { 15. return orderService.getOrders().size(); 16. } 17. public Service getOrderService() { 18. return orderService; 19. } 20. public void setOrderService(Service orderService) { 21. this.orderService = orderService; 22. } 23.}
view source print?

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: 1.<bean id="barBean" class="bar.view.BarBean" scope="request"/>
view source print?

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:

view source print?

1.<bean id="barBean" class="bar.view.BarBean" scope="request"> 2. <property name="orderService" > 3. <ref bean="service" /> 4. </property> 5.</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. 01.package bar.view; 02. 03.import java.util.List; 04.import javax.annotation.PostConstruct; 05.import javax.faces.context.FacesContext; 06.import javax.faces.event.ValueChangeEvent; 07.import javax.faces.model.SelectItem; 08.import org.hibernate.validator.Email; 09.import org.hibernate.validator.NotEmpty; 10.import org.hibernate.validator.Pattern; 11.import bar.service.Service; 12.import bar.utils.JSFUtils; 13. 14.public class WizardBean implements java.io.Serializable { 15. 16.private static final long serialVersionUID = 1L; 17. 18.@NotEmpty(message = "Name must not be empty") 19.@Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces") 20.private String name; 21. 22.@NotEmpty 23.@Email(message = "Invalid email format") 24.private String email; 25. 26.private String drink; 27.private String comments; 28.private String drinkCategorySelected; 29. 30.private Service orderService; 31. 32.private String startPage; 33. 34.private List<SelectItem> drinkCategory; 35.private List<SelectItem> drinkList;
view source print?

36. 37.// init all drinks 38.private static final String [] CATEGORY = {"Brandy", "Rum", "Tequila","Whiskey", "Wine", "Beer"}; 39.private static final String [] BRANDY = {"XO", "VSOP", "VS"}; 40.private static final String [] RUM = {"Medium", "Fullbodied","Aromatic"}; 41.private static final String [] TEQUILA = {"Reposado", "Aejo","Blanco"}; 42.private static final String [] WHISKEY = {"Malt", "Grain", "Single Malt", }; 43.private static final String [] WINE = {"Red", "White", "Pink"}; 44.private static final String [] BEER = {"Ales", "Lager", "Specialty Beer", }; 45. 46.public void changeDrink(ValueChangeEvent event) { 47.String newValue = (String) event.getNewValue(); 48. 49.if (newValue.equals("Brandy")) {drinkList = JSFUtils.createList(BRANDY); drink=BRANDY[0];} 50.else if (newValue.equals("Rum")) {drinkList = JSFUtils.createList(RUM); drink=RUM[0];} 51.else if (newValue.equals("Tequila")) {drinkList = JSFUtils.createList(TEQUILA);drink=TEQUILA[0];} 52.else if (newValue.equals("Whiskey")) {drinkList = JSFUtils.createList(WHISKEY);drink=WHISKEY[0];} 53.else if (newValue.equals("Wine")) {drinkList = JSFUtils.createList(WINE);drink=WINE[0];} 54.else if (newValue.equals("Beer")) {drinkList = JSFUtils.createList(BEER);drink=BEER[0];} 55.} 56. 57.@PostConstruct 58.public void create() { 59.drinkCategorySelected = CATEGORY[0]; 60.drinkCategory = JSFUtils.createList(CATEGORY); 61.drinkList = JSFUtils.createList(BRANDY); 62.drink = BRANDY[0]; 63.} 64.public void save() { 65.orderService.addOrder(name, email, drink, comments); 66.this.startPage = "/page1.xhtml"; 67. 68.FacesContext.getCurrentInstance().getExternalContext().getRequestM ap() 69..remove("wizardBean"); 70.} 71.

72.}

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.
view source print?

1.<bean id="wizardBean" class="bar.view.WizardBean" scope="request"> 2. <property name="startPage" value="/page1.xhtml"/> 3. <property name="orderService" > 4. <ref bean="service" /> 5. </property> 6.</bean>

startPage property is initialized to the first page in the wizard. orderService is injected with service bean.

One last class is the JSFUtils: 01.package bar.utils; 02. 03.import java.util.ArrayList; 04.import java.util.List; 05. 06.import javax.faces.model.SelectItem; 07. 08.public class JSFUtils { 09. 10. public static List <SelectItem> createList (String [] data){ 11. ArrayList <SelectItem>list = new ArrayList <SelectItem>(); 12. for (String item : data){ 13. list.add (new SelectItem (item, item)); 14. } 15. return list; 16. } 17.}
view source print?

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:
view source print?

01.<h:form> 02. <!-- launch wizard button --> 03. <rich:panel header="Welcome to RichBar"> 04. <a4j:commandButton value="Click to start your order" 05. oncomplete="#{rich:component('wizard')}.show()" /> 06. </rich:panel> 07. <rich:editor rendered="false" /> 08.</h:form> 09. 10.<!-- modal panel where the wizard is running --> 11.<rich:modalPanel id="wizard" width="550" height="300"> 12. <f:facet name="header">Drink Order</f:facet> 13. <f:facet name="controls"> 14. <a href="#" onclick="#{rich:component('wizard')}.hide()">X</a> 15. </f:facet> 16. 17. <h:panelGroup id="wizardInclude"> 18. <a4j:include viewId="#{wizardBean.startPage}" /> 19. </h:panelGroup> 20. 21.</rich:modalPanel> 22. 23.<!-- order table --> 24.<a4j:outputPanel id="orders"> 25. <rich:dataTable value="#{barBean.orders}" var="order" 26. rendered="#{barBean.rowCount>0}" rowKeyVar="row"> 27. <rich:column> 28. <f:facet name="header">Order #</f:facet> 29. <h:outputText value="#{row+1}" /> 30. </rich:column> 31. <rich:column> 32. <f:facet name="header">Name</f:facet> 33. <h:outputText value="#{order.name}" /> 34. </rich:column> 35. <rich:column> 36. <f:facet name="header">Email</f:facet> 37. <h:outputText value="#{order.email}" /> 38. </rich:column>

39. <rich:column> 40. <f:facet name="header">Drink</f:facet> 41. <h:outputText value="#{order.drink}" /> 42. </rich:column> 43. <rich:column> 44. <f:facet name="header">Comments</f:facet> 45. <h:outputText value="#{order.comments}" escape="false" /> 46. </rich:column> 47. </rich:dataTable> 48.</a4j:outputPanel> 49.</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

view source print?

01.<h:form xmlns="http://www.w3.org/1999/xhtml" 02.xmlns:ui="http://java.sun.com/jsf/facelets" 03.xmlns:h="http://java.sun.com/jsf/html" 04.xmlns:f="http://java.sun.com/jsf/core" 05.xmlns:rich="http://richfaces.org/rich" 06.xmlns:a4j="http://richfaces.org/a4j"> 07. 08.<a4j:keepAlive beanName="wizardBean" /> 09. <rich:panel> 10. <h:panelGrid> 11. <h:panelGroup> 12. <h:outputLabel value="Email:" for="email" /> 13. <h:outputText value=" (We will email you your receipt!)" 14.style="FONT-SIZE: small;" /> 15. </h:panelGroup> 16. <h:panelGroup> 17. <h:inputText id="email" value="#{wizardBean.email}"> 18. <rich:ajaxValidator event="onkeyup" /> 19. </h:inputText> 20. <rich:message for="email" /> 21. </h:panelGroup> 22. <a4j:commandButton value="Next" action="next" /> 23. </h:panelGrid> 24. </rich:panel> 25.</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:
view source print?

1.@NotEmpty 2.@Email(message = "Invalid email format") 3.private String email;

This eliminates having to register a validator with the component on the page. Another benefit is thatrich: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 atwizardBean 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 intorequest 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 placinga4j: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: 1.public void save() { 2. orderService.addOrder(name, email, drink, comments); 3. 4. FacesContext.getCurrentInstance().getExternalContext(). getRequestMap() 5.. remove("wizardBean"); 6.}
view source print?

page2.xhtml

view source print?

01.<h:form xmlns="http://www.w3.org/1999/xhtml" 02. xmlns:ui="http://java.sun.com/jsf/facelets" 03. xmlns:h="http://java.sun.com/jsf/html" 04 xmlns:f="http://java.sun.com/jsf/core" 05. xmlns:rich="http://richfaces.org/rich" 06 .xmlns:a4j="http://richfaces.org/a4j"> 07.<rich:panel> 08. <h:panelGrid> 09. <h:outputLabel value="Name:" for="name" /> 10. <h:panelGroup> 11. <h:inputText id="name" value="#{wizardBean.name}" > 12. <rich:ajaxValidator event="onkeyup"/> 13. </h:inputText> 14. <rich:message for="name"/> 15. </h:panelGroup> 16. <h:panelGroup> 17. <a4j:commandButton value="Previous" action="prev" /> 18. <a4j:commandButton value="Next" action="next" /> 19. </h:panelGroup>

20. </h:panelGrid> 21.</rich:panel> 22.</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:
view source print?

1.@NotEmpty(message = "Name must not be empty") 2.@Pattern(regex = ".*[^\\s].*", message = "This string contain only spaces") 3.private String name;

page3.xhtml

view source print?

01.<h:form xmlns="http://www.w3.org/1999/xhtml" 02.xmlns:ui="http://java.sun.com/jsf/facelets" 03.xmlns:h="http://java.sun.com/jsf/html" 04.xmlns:f="http://java.sun.com/jsf/core" 05.xmlns:rich="http://richfaces.org/rich" 06.xmlns:a4j="http://richfaces.org/a4j"> 07.<rich:panel> 08. <h:panelGrid> 09. <h:panelGrid columns="2"> 10 .<h:outputLabel value="Category:" for="want" /> 11. <rich:inplaceSelect id="want" value="#{wizardBean.drinkCategorySelected}" 12. valueChangeListener="#{wizardBean.changeDrink}"> 13. <f:selectItems value="#{wizardBean.drinkCategory}"/> 14. <a4j:support event="onviewactivated" ajaxSingle="true"reRender="drink"/> 15. </rich:inplaceSelect> 16. 17. <h:outputLabel value="Drink:" for="drink" /> 18. <rich:inplaceSelect id="drink" value="#{wizardBean.drink}" > 19. <f:selectItems value="#{wizardBean.drinkList}"/> 20. </rich:inplaceSelect> 21. 22. </h:panelGrid> 23. <h:panelGroup> 24. <a4j:commandButton value="Previous" action="prev" /> 25. <a4j:commandButton value="Next" action="next" /> 26. </h:panelGroup> 27. </h:panelGrid> 28.</rich:panel> 29.</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

view source print?

01.<h:form xmlns="http://www.w3.org/1999/xhtml" 02.xmlns:ui="http://java.sun.com/jsf/facelets" 03.xmlns:h="http://java.sun.com/jsf/html" 04.xmlns:f="http://java.sun.com/jsf/core" 05.xmlns:rich="http://richfaces.org/rich" 06.xmlns:a4j="http://richfaces.org/a4j"> 07.<rich:panel> 08. <h:panelGrid> 09. <h:outputLabel value="Anything else?" for="comments" /> 10. <rich:editor id="comments" autoResize="true" 11. value="#{wizardBean.comments}"> 12. </rich:editor> 13. 14. <h:panelGroup>

15. <a4j:commandButton value="Previous" action="prev" /> 16. <a4j:commandButton value="Next" action="next" /> 17. </h:panelGroup> 18. </h:panelGrid> 19.</rich:panel> 20.</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

view source print?

01.<h:form xmlns="http://www.w3.org/1999/xhtml" 02.xmlns:ui="http://java.sun.com/jsf/facelets" 03.xmlns:h="http://java.sun.com/jsf/html" 04.xmlns:f="http://java.sun.com/jsf/core" 05.xmlns:rich="http://richfaces.org/rich" 06.xmlns:a4j="http://richfaces.org/a4j"> 07. 08.<rich:panel> 09. <h:panelGrid columns="2"> 10. <f:facet name="header">Summary</f:facet> 11. 12. <h:outputText value="Name" /> 13. <h:outputText value="#{wizardBean.name}" />

14. 15. <h:outputText value="Email:" /> 16. <h:outputText value="#{wizardBean.email}" /> 17. 18. <h:outputText value="Drink ordered:" /> 19. <h:outputText value="#{wizardBean.drink}" /> 20. 21. 22. <h:outputText value="Comments:" /> 23. <h:outputText value="#{wizardBean.comments}" escape="false"/> 24. 25. </h:panelGrid> 26. <h:panelGrid columns="2"> 27. <a4j:commandButton value="Wait.. Go back" action="prev" /> 28. <a4j:commandButton value="Place Order" 29. action="#{wizardBean.save}" 30. oncomplete="#{rich:component('wizard')}.hide()" 31. reRender="orders, wizard"/> 32. </h:panelGrid> 33.</rich:panel> 34.</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:
view source print?

01.<navigation-rule> 02. <from-view-id>/page1.xhtml</from-view-id> 03. <navigation-case> 04. <from-outcome>next</from-outcome> 05. <to-view-id>/page2.xhtml</to-view-id> 06. </navigation-case> 07.</navigation-rule>

08.<navigation-rule> 09. <from-view-id>/page2.xhtml</from-view-id> 10. <navigation-case> 11. <from-outcome>next</from-outcome> 12. <to-view-id>/page3.xhtml</to-view-id> 13. </navigation-case> 14. <navigation-case> 15. <from-outcome>prev</from-outcome> 16. <to-view-id>/page1.xhtml</to-view-id> 17. </navigation-case> 18.</navigation-rule> 19.<navigation-rule> 20. <from-view-id>/page3.xhtml</from-view-id> 21. <navigation-case> 22. <from-outcome>next</from-outcome> 23. <to-view-id>/page4.xhtml</to-view-id> 24. </navigation-case> 25. <navigation-case> 26. <from-outcome>prev</from-outcome> 27. <to-view-id>/page2.xhtml</to-view-id> 28. </navigation-case> 29.</navigation-rule> 30.<navigation-rule> 31. <from-view-id>/summary.xhtml</from-view-id> 32. <navigation-case> 33. <from-outcome>prev</from-outcome> 34. <to-view-id>/page3.xhtml</to-view-id> 35. </navigation-case> 36.</navigation-rule> 37.<navigation-rule> 38. <from-view-id>/page4.xhtml</from-view-id> 39. <navigation-case> 40. <from-outcome>next</from-outcome> 41. <to-view-id>/summary.xhtml</to-view-id> 42. </navigation-case> 43. <navigation-case> 44. <from-outcome>prev</from-outcome> 45. <to-view-id>/page3.xhtml</to-view-id> 46. </navigation-case> 47.</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: 1.<context-param> 2. <param-name>org.richfaces.SKIN</param-name> 3. <param-value>laguna</param-value> 4.</context-param>
view source print?

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 lagunalargefont.skin.properties (all skins have to follow this naming convention): 1.baseSkin=laguna 2.generalSizeFont=20px 3.headerSizeFont=20px
view source print?

All we are doing is using the existing laguna skin and overwriting just two properties. Now just update skin in web.xml file:
view source print?

1.<context-param> 2. <param-name>org.richfaces.SKIN</param-name> 3. <param-value>laguna-largefont</param-value> 4.</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: 1.<context:component-scan base-package="bar" />
view source print?

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:
view source print?

01.<!-02.<bean id="barBean" class="bar.view.BarBean" scope="request"> 03. <property name="orderService" > 04. <ref bean="service" /> 05. </property> 06.</bean> 07.<bean id="wizardBean" class="bar.view.WizardBean" scope="request"> 08. <property name="startPage" value="/page1.xhtml"/> 09. <property name="orderService" > 10. <ref bean="service" /> 11. </property> 12.</bean> 13.<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: 1.@Service("orderService") 2.@Scope ("session") 3.public class OrderService { 4.... 5.}
view source print?

@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. 1.@Component("barBean") 2.@Scope("request") 3.public class BarBean { 4.... 5.} 6. 7.@Autowired 8.private OrderService orderService;
view source print?

@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 @Autowiredannotation. 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: 1.@Component("wizardBean") 2.@Scope("request" 3.public class WizardBean { 4.... 5.} 6. 7.@Autowired 8.private OrderService orderService;
view source print?

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 athttp://mkblog.exadel.com/

Das könnte Ihnen auch gefallen