JBoss Development is brought to you in partnership with:

Max Katz heads Developer Relations for Appery.io, a cloud-based mobile app platform. He loves trying out new and cool REST APIs in mobile apps. Max is the author of two books “Practical RichFaces” (Apress 2008, 2011), and is a frequent speaker at developer conferences. You can find out what Max is up to on his blog: mkblog.exadel.com and Twitter: @maxkatz. Max is a DZone MVB and is not an employee of DZone and has posted 83 posts at DZone. You can read more from them at their website. View Full User Profile

JBoss RichFaces with Spring

02.16.2009
| 188614 views |
  • submit to reddit

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;

 

AttachmentSize
richfaces-spring-wizard.zip11.15 MB
Published at DZone with permission of Max Katz, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Aleksandar Vidakovic replied on Mon, 2009/02/16 - 6:04pm

Salut Max,

interesting article. I've been experimenting with Richfaces and Spring for a while and would be extremely interested in the SpringWebflow integration...

Cheers,

Aleks

Frederic Chopard replied on Tue, 2009/02/17 - 9:57am

Hello Max,

 

I'm trying to build my richbar application but when I click on the next button of the first wizard page, nothing happens. So I'd like to check your project but I can't find any link in your article. Where can I download your project files ?

Thanks !

 

Wis

Nitin Bharti replied on Tue, 2009/02/17 - 10:19am in response to: Frederic Chopard

Hi Wis,

 I believe  you can download the project template from here -- http://drop.io/usoalrd

I will add this to the main article as well. Sorry about that.

Thanks,

Nitin

Gene De Lisa replied on Wed, 2009/02/25 - 9:08am

"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."

maven projects are "ready-made" and they don't have the disadvantages of the approach you have taken.

Other than that this is nothing more a mediocre do this do that tutorial.

Omar Palomino S... replied on Sun, 2009/03/08 - 10:28pm in response to: Aleksandar Vidakovic

Hi Max and Aleksandar,

 I'm also interesting in Spring Webflow integration [navigation rules are cumbersome :s]. I have a couple of implementations with both RichFaces, Spring and Jpa, and it's fun and easy to develop this way. Webflow would be a nice addition to this project acthitecture.

Tao Dong replied on Fri, 2009/03/13 - 2:34pm

Excellent Article. In case someone want to try it through Maven. Hope the following dependecy setting will save you some time.

        <dependency>
            <groupId>org.richfaces.ui</groupId>
            <artifactId>richfaces-ui</artifactId>
            <version>3.3.0.GA</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>org.richfaces.framework</groupId>
            <artifactId>richfaces-api</artifactId>
            <version>3.3.0.GA</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>org.richfaces.framework</groupId>
            <artifactId>richfaces-impl</artifactId>
            <version>3.3.0.GA</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>org.richfaces.samples</groupId>
            <artifactId>laguna</artifactId>
            <version>3.3.0.GA</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.0</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>commons-digester</groupId>
            <artifactId>commons-digester</artifactId>
            <version>2.0</version>
            <scope>compile</scope>
        </dependency>
       
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>compile</scope>
        </dependency> 

        <!-- JBoss RichFaces Repository -->
         <repository>
            <releases>
                <enabled>true</enabled>
             </releases>
             <snapshots>
                <enabled>false</enabled>
                <updatePolicy>never</updatePolicy>
             </snapshots>
             <id>repository.jboss.com</id>
             <name>Jboss Repository for Maven</name>
             <url>
                http://repository.jboss.com/maven2/
             </url>
             <layout>default</layout>
        </repository>   

Max Katz replied on Mon, 2009/03/16 - 10:53am in response to: Tao Dong

Thanks taod!

ben salem replied on Tue, 2009/03/17 - 6:44pm

First, thanks for the great work :)

but

<cite> (Note - The Project Template for this article can be downloaded from http://drop.io/usoalrd) </cite>

 what is the pass for download ?

thanks in advance!

Anderson Santos replied on Wed, 2009/03/18 - 9:28am

Hi Buddy,

 

This article is just great, I have been looking for a steb by step like this one, for at least a month.

 

I just couldn't download the template for this project, could you please send it by e-mail to me, that link where you say to click for download is broken and the one in the first page os this article, I just couldn't use...

 

thanks,

Anderson

ander.dev@gmail.com

Jean Chastain replied on Wed, 2009/03/18 - 4:27pm

I would like to download the code, too.  I've done everything you said to do and can't get it to deploy to JBoss and was wondering if you had some configuration you didn't tell us about.

sam set replied on Sat, 2009/03/21 - 12:01pm in response to: Jean Chastain

hi, it's been quite some time i am trying to make richfaces 3.3 work with spring 2.0 webflow. Can somebody tell me how to download the source code. I visited http://drop.io/usoalrd, it's asking for password. What am i suppose to enter there ?

or, can you send me through email ?

Appreciate your help!

 

 

Aseem Monga replied on Tue, 2009/03/31 - 7:59am

A very good introduction. Can someone provide the password for downloading the project template?

Juan Gonzales replied on Wed, 2009/04/01 - 7:03pm

A very good ...could you please send it by e-mail to me  password

 

 thanks

 

juanjogoa24@gmail.com 

 

 

 

.  

 

 

Max Katz replied on Sat, 2009/04/11 - 11:47am

Link to download project template has been updated.

Davut Uysal replied on Fri, 2009/04/17 - 9:48am

Thanks Max. It was great article!

shantanu banerjee replied on Sat, 2009/04/25 - 1:36am

Hi Max, I am going through your article as we speak. You writing is always so to-the-point. I like it very much. However the zip file is not working. Can you check if you get the opportunity? Shantanu

Donovan Makund replied on Sun, 2009/04/26 - 3:11pm in response to: Max Katz

The zip file cannot be extracted once downloaded.

shantanu banerjee replied on Thu, 2009/04/30 - 1:57am

As Donovan has pointed out, the zip file can not be extracted once downloaded. Can anyone please send the proper template zip to shanroam@yahoo.com. I would be immensely greatful.

Max Katz replied on Tue, 2009/05/05 - 1:45pm

Let's try this file. Here is what you need to do:

Download the template

In Eclipse:

  1. Select File/Import/General/Existing Project into Workspase
  2. Click Next
  3. Check 'Select arhcive file' option and browse to downloaded file
  4. Follow the wizards steps

shantanu banerjee replied on Thu, 2009/05/07 - 8:08am

I somehow cannot download the file from the link that Max provided in the last post. Any help would be greatly appreciated.

Max Katz replied on Tue, 2009/05/19 - 6:05pm in response to: shantanu banerjee

I'm not sure why the link is not working. Try this one: http://www.filedropper.com/richfaces-spring

Donovan Makund replied on Sat, 2009/06/06 - 11:48am

Hello Max, I was able to implement a prototype using Richfaces 3.3.1, Spring 2.5.5, JSF 1.2 with Spring managing all the beans. I am now trying to add Facelets 1.1.14 into the mix for template features. Have you tried this combination before?

Max Katz replied on Fri, 2009/06/12 - 11:18am in response to: Donovan Makund

If you look in my template, I used Facelets. So yes, you can (and should) use Facelets.

Mohsin Khan replied on Tue, 2009/06/16 - 2:30pm

Hi i am not able to run the application.Getting following exception javax.faces.FacesException: UIInclude component j_id10 could't include page with path at org.ajax4jsf.taglib.html.facelets.IncludeHandler.applyNextHandler(IncludeHandler.java:83) at com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:169) at com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHandler.java:314) at com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:169) at com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:47) at com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHandler.java Please help me Mohsin

Martin Haas replied on Mon, 2009/06/22 - 5:50am

None of the links above is working. Another source where I can download the project? The linked project is corrupt and all other links in the comments are broken.

Max Katz replied on Mon, 2009/06/22 - 3:26pm in response to: Mohsin Khan

Is this the full stack trace?

Max Katz replied on Mon, 2009/06/22 - 3:29pm in response to: Martin Haas

Let's try one more time: http://drop.io/pnbhzls

stu bilton replied on Thu, 2009/07/16 - 10:26am

Hi Max,

Firstly thanks for this article it's good to have a straigthforward example of using these frameworks together.

 I do have a problem running this on a was 6.1 instance. (However I don't think this is the issue here). I've placed the code into an existing webb app project to prove the spring framework can run in an existing richfaces / facelets implementation

 My stack trace on requesting http://localhost:9080/MyProjectName/start.jsf is as follows

java.io.NotSerializableException: bar.service.OrderService
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1113)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1467)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1439)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1382)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1111)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:325)
at java.util.HashMap.writeObject(HashMap.java:1290)
at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:973)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1382)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1111)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1343)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1107)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1343)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1107)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1343)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1107)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1343)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1107)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1343)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1107)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:325)
at com.sun.faces.renderkit.ResponseStateManagerImpl.writeState(ResponseStateManagerImpl.java:269)
at javax.faces.render.ResponseStateManager.writeState(ResponseStateManager.java:166)
at org.ajax4jsf.application.AjaxStateManager.writeState(AjaxStateManager.java:294)
at org.ajax4jsf.application.AjaxStateManager.writeState(AjaxStateManager.java:256)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:606)
at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:100)
at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:176)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:110)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:266)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1103)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1038)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:145)
at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:390)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:517)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:190)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:130)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:87)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:834)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:682)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:566)
at com.ibm.ws.wswebcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:486)
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3440)
at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:267)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:815)
at com.ibm.ws.wswebcontainer.WebContainer.handleRequest(WebContainer.java:1461)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:118)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:458)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:387)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:267)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214)
at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113)
at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:136)
at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:195)
at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:743)
at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:873)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1473)

 

Do you have any idea why I'm getting a NotSerializeableException thrown when trying to instantiate this bean? (presumeably through Spring DI ?(I'm a newbie when it comes to jsf and spring))

Thanks 

Stuart

Max Katz replied on Thu, 2009/07/16 - 10:47am

Not sure why you get this exception, the server is trying to save/restore the class from somewhere. Try adding implements java.io.Serializable to the class.

stu bilton replied on Thu, 2009/07/16 - 11:00am in response to: Max Katz

Thanks Max.

 I thought that might (and did) do the trick.  As a newbie just wondered if the stack trace might shed any light on why the exception was throwing.

 

Keep the articles coming . Thanks again

 Stuart

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.