The Holy Java

Building the right thing, building it right, fast

Seam Tutorial 1.2: RichFaces and paged table (datascroller)

Posted by Jakub Holý on March 23, 2009

In this two-part tutorial you will
learn how to get started with the development of Seam applications
with RichFaces using Eclipse with JBoss Tools. In the 1st
part
we’ve set up our environment, created, and run an empty shell
Seam application. In this 2nd part we will create a simple
web page with a table presenting data on multiple pages using Ajax (a
RichFaces component) and its model stored as a POJO Component in the
Seam Conversation scope. I assume that you already have some basic
knowledge of Seam and JSF, for instance that you know what a
Component or the Conversation scope are. I’ll present my path to this
goal with all the mistakes so that you too can learn from them.

My aim in this tutorial series is to
create a Seam portlet displaying data in a paged (and ideally also
sortable and filterable, but lets be realistic) table running in the
Liferay portal.

Attempt 1: The straightforward version

First we will create a new Seam project in exactly the same way as
in part 1.1, the only difference being that we will call it
seamTutorial12. Or you can reuse the existing project. I will not
repeat the steps here, you can refer to the previous post.

TIP: Cleaning of the jboss deploy folder doesn’t work very well
(event with the Clean button in the JBoss Server View). You may need
to help it by undeploying all projects and manually deleting contents
of deploy/ in the JBoss folder, which is, for a server named
JBoss_4.2.3, <eclipse
workspace>/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_4.2.3/.
An alternative is to create a new server.

Note: When working offline you may get exceptions due to
www.w3.org being unreachable, which
will prevent the deployment of
jboss-portal.sar/portal-wsrp.sar/portal-wsrp.war/. This is nothing to
worry about.

Create a component and a page

Assuming that you have the project seamTutorial12 created and
running, we will add a new component and page to it for our
experiments.

File > New > Seam Action

  • Type the Seam component name richTable and accept the
    defaults (it would be better to apply a reasonable naming standard
    but let’s keep it simple).
  • Finish.
  • Note: We could also select Seam Form, which would create a
    page with a form and button.

This will create two files:

1. src/hot/org/domain/seamtutorial12/session/RichTable.java


package org.domain.seamtutorial12.session;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.log.Log;
import org.jboss.seam.faces.FacesMessages;

@Name("richTable")
public class RichTable {
@Logger private Log log;
@In FacesMessages facesMessages;

public void richTable()
{
   //implement your business logic here
   log.info("richTable.richTable() action called");
   facesMessages.add("richTable");
}

//add additional action methods
}

2. WebContent/richTable.xhtml .


<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich" xmlns:a="http://richfaces.org/a4j"
template="layout/template.xhtml">

<ui:define name="body">

<h:messages globalOnly="true" styleClass="message" />

<rich:panel>
<f:facet name="header">richTable</f:facet>
<h:form id="richTableForm">
<h:commandButton id="richTable" value="richTable!"
action="#{richTable.richTable}" />
</h:form>
</rich:panel>

</ui:define>

</ui:composition>

Opening the page in Eclipse will show an editor with a text and
visual views of the page (notice that the selected tag – h:form –
is emphasized in the visual view – the blue line):

Go to http://localhost:8080/seamTutorial12/richTable.seam
to see it:

Clicking the button RichTable! should add a message “RichTable”
to the page.

Define a table data model

To use a table we need the data to display in it. JSF requires
data to be provided as an instance of DataModel however we will use a
simple List and let Seam to wrap it into a DataModel. We will add the
following snippet to RichTable.java (plus the necessary imports) to
define the data model property/component rowList and to initialize in
the class’ constructor:

@DataModel
List<String> rowList = new LinkedList<String>();

public RichTable() {
final Date now = new Date();
rowList.add("row 1 created on " + now);
rowList.add("row 2 created on " + now);
rowList.add("row 3 created on " + now);
}

Create a RichFaces table

Finally we will create the rich table to display the data.

  1. Double click on richTable.xhtml in Eclipse to open it in the
    JBoss Tools HTML Editor.
  2. In the source view click inside the h:form and in the JBoss
    Tools Palette click on JBoss RichFaces – extendedDataTable. I’ve selected the extendedDataTable and not the
    basic rich:dataTable because the extended one has sorting and
    filtering built-in. (You can also add these features to the standard
    table using other components.)
    The table will be inserted at the
    cursor’s location.

  3. You could continue with adding columns etc. in this manner
    but I leave this up to your playful nature and just provide the
    final code that the ones in a hurry can copy&paste.

The final code:

<h:form>

<rich:extendedDataTable width=“483″ id=“richTable” rows=“2″ columnClasses=“col” value=“#{rowList}” var=“row”>

<f:facet name=“header”>

<rich:columnGroup>
<
h:column>
<
h:outputText styleClass=“headerText” value=“A Column” />

</h:column>
</rich:columnGroup>

</f:facet>

<h:column><h:outputText value=“#{row}” /></h:column>

</rich:extendedDataTable>

<rich:spacer height=“30″ />

<rich:datascroller id=“richTableScroller” align=“left” for=“richTable” maxPages=“20″ />

</h:form>

Notice that I’ve removed the command
button (h:commandButton),
which we don’t need, to make the code cleaner.

That’s it! Notes:

  • We use a datascroller to have
    paging of the table via Ajax. The table is set to display at most 2
    rows via its attribute rows=2 thus we should see the 3rd
    element of our data model on the next page of the table.

  • RichFaces (Ajax) components must
    be within a form because its function is based on submitting the
    closest enclosing form. (See the RichFaces Developer Guide for
    3.1.4.GA, part 5.4.3. Data Processing Options ).

Run the application

Now we can run it. In the JBoss Server
View right-click on seamTutorial12 > Full Publish (though
incremental publish or waiting for JBoss tools to publish the change
automatically should also work).

When you access
http://localhost:8080/seamTutorial12/richTable.seam
you will be surprised to see a Facelets Error page reading:

/richTable.xhtml @22,46 <rich:extendedDataTable> Tag Library supports namespace: http://richfaces.org/rich, but no tag was defined for name: extendedDataTable.

Catch #1: JBoss Tools Palette uses newer version of RichFaces than
Seam itself and let you add a component not present in the runtime
version.

The error message above indicates that
Facelets can’t find the tag extendedDataTable in the taglib rich.
Indeed if you look in Eclipse in the Package Explorer into
seamTutorial12 > Web App Libraries >
richfaces-ui.jar/META-INF/rich.tld you will not find it there either.
A look inside richfaces-ui.jar/META-INF/MANIFEST.MF reveals that this
version of RichFaces is 3.1.4 GA and not the latest (as of 2/2009)
3.3.0.

Intermezzo: Get RichFaces demo 3.1.4 GA running

The RichFaces demo is the best way to learn what its components
can do, how to do it, and to copy the code for that. The online demo
is of the latest version but for Seam 2.0.2 SP1 you would need
RichFaces demo 3.1.4 GA.

  1. Download richfaces-demo-3.1.4.GA-tomcat6.war
  2. Download Tomcat 6.0.18.
  3. Install it to the Tomcat. I’ve imported the .war into Eclipse
    (File > Import) as JSF Project From *.war, defined a Tomcat
    server (right click in JBoss Server View > New > Server >
    …) , and added the project to the server (JBoss Server View >
    [your Tomcat server] > Add and Remove Projects…).
  4. Perhaps change the ports Tomcat uses so that you can have it
    running in parallel with JBoss: double click on the newly created
    Tomcat server in the JBoss Server View, change Ports e.g. by
    prepending 2 (-> 28080 etc.).
  5. Start Tomcat.
  6. Go to
    http://localhost:28080/Richfaces-demo-3.1.4.GA-tomcat6/richfaces/dataTable.jsf?c=dataTable
    (or :8080 if you haven’t changed the port) and you shall see the
    demo .

I haven’t managed to get the demo running under JBoss but this
solution is fine.

Attempt 2: Page valid for the actual RichFaces
version

Create a RichFaces table, version 2 – a basic
rich:dataTable

Since RichFaces 3.1.4 doesn’t have the lovely extendedDataTable we
will give up on sorting and filtering and use the basic
rich:dataTable. The change to the code is trivial, just replace
extendedDataTable with dataTable.

<h:form>

<rich:dataTable width=“483″ id=“richTable” rows=“2″ columnClasses=“col” value=“#{rowList}” var=“row”>

</rich:dataTable>

</h:form>

Modify the .xhtml and save it, JBoss should pick the change up
automatically. (Or do force its publication.) Reload
http://localhost:8080/seamTutorial12/richTable.seam
in your browser. You may need to wait for some time.

Certainly you wonder why the table is empty instead of showing our
3 rows (or actually the first two ones) . The answer is that at the
time the table was being rendered, the data model component rowList
was undefined.

Issue: Undefined data source during startup
When deploying and undeploying projects it may (will) happen that
JBoss Tools get confused and forget to deploy the project’s data
source. Normally when you are adding a project, like seamTutorial12,
to a server, you should be able and should do select also its data
source, like /../seamTutorial12-ds.xml. If you haven’t the choice or
forget it you will see an error like the following one during the
Seam application startup following its deployment:

ERROR
[DatasourceConnectionProvider] Could not find datasource:
java:/seamTutorial12Datasource

javax.naming.NameNotFoundException:
seamTutorial12Datasource not bound

The solution is to deploy the data source manually:

  1. In the Package Explorer, right-click on
    /seamTutorial12/resources/seamTutorial12-ds.xml and select Make
    Deployable from the context menu, then select the proper target
    server from the window that pops up (JBoss_4.2.3)
  2. In the JBoss Server View. you should now see
    /seamTutorial12/resources/seamTutorial12-ds.xml below the server name
    and the server’s status changed to Started, Republish (if it was
    started).
  3. After republishing or restarting (which may be safer) the
    server the error should have been gone. In the log you should
    see:
    INFO
    [ConnectionFactoryBindingService] Bound ConnectionManager
    ‘jboss.jca:service=DataSourceBinding,name=seamTutorial12Datasource’
    to JNDI name ‘java:seamTutorial12Datasource’

Notice: On the screenshot there is “Make Undeployable” instead
of Deployable because it’s been taken after the step #3, i.e. when
already deployed.

Lesson learned 1: DataModel needs a Factory
method unless the parent component is also used on the page

You may have a component that outjects another component as a data
model via the annotation @DataModel on a property. However if the
owning component (richTable) isn’t used on a page where the data
model is used, it won’t be instantiated and therefore also there will
be nobody to create and outject the data model component (rowList)
itself. Seam is not clever or active enough to understand that to
have the component rowList it must first instantiate richTable.

The solution is to provide a method annotated with
@Factory(“rowList”)
to tell Seam that to instantiate the rowList component, it must call
this method (which will likely also require instantiation of its
parent component, richTable). See Seam Reference 2.0.2 SP1, chapter
4.8. Factory and manager components.

Attempt 3: Tell Seam how to get an instance of
rowList via a @Factory method

As explained above, we will provide a new method to tell Seam what
to do when #{rowList} is referenced on a page and it doesn’t exist
yet. We will move here the initiation code from the parent
component’s constructor.

@Factory(“rowList”)

public void initRowList() {

rowList = new LinkedList<String>();

final Date now = new Date();

rowList.add(“row1 created on “ + now);

rowList.add(“row2 created on “ + now);

rowList.add(“row3 created on “ + now);

}

Publishing the change to the server and accessing
http://localhost:8080/seamTutorial12/richTable.seam
again will finally show the paged table (nearly) as expected. On the
following screenshot you can see both the 1st and the 2nd
page of the table, the red arrowhead emphasizing the current one:

Have you noticed anything strange? Not? Look carefully at the
dates on both pages. You will see that on the first page there is
“row 1 created on Sat Mar 21 09:04:27 GMT+01:00 2009”
while on the second one “row 3 created on Sat Mar 21 09:06:22
GMT+01:00 2009”. The times differ even though all the rows were
created at once in the initRowList method!

The explanation is obvious: When we move to the 2nd
page of the table, it triggers a new request to the server and since
the data model only lives during a request, a new one was created
with a new creation time. Coming back to page 1 would show yet later
time. The solution is of course to keep the list in a longer-living
scope and since we’ve Seam, the best candidate is the Conversation
scope.

Attempt 4: Moving the data model into the
Conversation scope to survive across request

We want to create the rowList data model only once and then simply
access this instance when a user moves through the table’s pages. The
motivation is clear – in a normal, non-tutorial scenario we’d load
data from a database and wouldn’t want to repeat this operation over
and over again.

You’ve been nice readers and thus I’ll
tell you two important things right away without teasing you.

Lesson learned 2: POJO Components are in the
Event (Request) scope by default

As opposed to EJB components, plain old Java object components are
stored in the Event scope unless specified otherwise. Don’t trust you
friends, don’t be lazy, and check the defaults by yourself :-)

If you’re not sure what scope a component is in, you can check it:

  • In Eclipse in the Seam Component view (Window > Show View
    > Other > Seam > Seam Components)

  • In the server log during startup of a Seam web application,
    Seam prints all components it has found together with their scope
    and class.

To make the component conversation-scoped, we will modify
RichTable.java as follows:

@Name(“richTable”)
@Scope
(ScopeType.CONVERSATION)
public
class RichTable {

Lesson learned 3: Conversation scope doesn’t
exist and behaves as Event scope unless you start a conversation
explicitly.

It may surprise you (it did surprise me!) but marking a component
as a conversation-scoped is not enough. That’s because no
conversation scope actually exists until created explicitly. Normally
Seam creates a new conversation for each request and destroys it when
the request ends, in the very same way as with the Event scope. To
get a truly long-lived conversation you must start it explicitly.

You can start a conversation in several ways, usually you do it
when an action method is called. But we’ve no such method, we need a
conversation started as soon as somebody lands on the richTable.seam
page. This is something that can be set in the page configuration
file. You may have a global pages.xml but we will create a separate
configuration file /seamTutorial12/WebContent/richTable.page.xml next
to the page file itself (thanks to the matching names Seam will
understand it’s for richTable.xhtml):

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
      login-required="false"
      >

      <begin-conversation join="true"/>
</page>

The only important line is <begin-conversation join=”true”/>
that tells Seam to start a new or join an existing long-lived
conversation when the page is accessed.

Summary

  1. We marked the component as belonging to the Conversation
    scope applying the annotation @Scope(ScopeType.CONVERSATION)
    in the .java file
  2. We requested that a real conversation is started whenever a
    users enters the page richTable.xhtml (.seam) by specifying
    begin-conversation join=”true” in a new page configuration
    file richTable.page.xml.

Now you can once again go to
http://localhost:8080/seamTutorial12/richTable.seam
and you will see that the dates within rows do not change anymore no
matter how you switch the table pages.

The final version

RichTable.java:

package org.domain.seamtutorial12.session;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.log.Log;

@Name("richTable")
@Scope(ScopeType.CONVERSATION)
public class RichTable {

    @Logger private Log log;

    @In FacesMessages facesMessages;

    @DataModel
    List&lt;String&gt; rowList;

    /**
     * We must provide this factory method to init the rowList instead of
     * initiating it e.g. in a constructor because otherwise it would be
     * uninitialized until this component itself - tichTable - is also
     * used by the page using  rowList.
     * &lt;p&gt;
     * See Seam Reference 2.0.2 SP1, chapter 4.8. Factory and manager components.
     */
    @Factory("rowList")
    public void initRowList() {
    	rowList = new LinkedList&lt;String&gt;();
    	final Date now = new Date();
    	rowList.add("row 1 created on " + now);
    	rowList.add("row 2 created on " + now);
    	rowList.add("row 3 created on " + now);

    	log.info("initRowList called at " + now);
    }

}

richTable.xhtml:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich"
                xmlns:a="http://richfaces.org/a4j"
                template="layout/template.xhtml">

<ui:define name="body">

    <h:messages globalOnly="true" styleClass="message"/>

    <rich:panel>
        <f:facet name="header">richTable</f:facet>

        <h:form>
        	<rich:dataTable width="483" id="myRichTable" rows="2" columnClasses="col"
                value="#{rowList}" var="row">

                <f:facet name="header">
                    <rich:columnGroup>
                        <h:column>
                            <h:outputText styleClass="headerText" value="A Column" />
                        </h:column>
                    </rich:columnGroup>
                </f:facet>

                <h:column>
                    <h:outputText value="#{row}" />
                </h:column>

            </rich:dataTable>

            <rich:spacer height="30" />

            <rich:datascroller id="richTableScroller" align="left"  for="myRichTable" maxPages="20" />
         </h:form>

    </rich:panel>

</ui:define>

</ui:composition>

richTable.page.xml

<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
      login-required="false"
      >

      <begin-conversation join="true"/>
</page>

Troubleshooting tips

RichFaces monitoring and troubleshooting

There are two tools you can use for monitoring Ajax requests and
RichFaces behavior. The first one is the RichFaces tag a:log
that will display detailed log about what RichFaces is doing, once it
actually starts doing something:

<a:log level=”ALL” popup=”false”
width=”400″ height=”200″/>

I’ve found this tag useful but not sufficient. I also haven’t got
popup=true working (likely a mistake on my part).

With the addition of the Firefox extension FireBug,
a JavaScript/CSS/… debugger and monitor and one of the best free
web development tools, you’ll have all you need. Notice that you will
fist need to enable FireBug for the site (localhost) to monitor.
Below you can see FireBug in action observing Post parameters of an
Ajax request (tab “XHR”):

Tracking Seam conversations

How do you find out whether an action triggered a new conversation
or remained in an existing conversation? By checking whether the
request parameter cid has changed it’s number or not or wasn’t
a part of the request at all. This parameter carries a numerical ID
of the current conversation and is always present and unchanged if a
user action is within an existing conversation. (But notice above
that the Ajax request for switching a table page captured by FireBug
doesn’t have any such attribute – perhaps some Seam-RichFaces
magic.)

Summary

We have created a conversation scoped Seam component holding a
list of items that are displayed in a paged RichFaces table. We have
also learned what the correct version of RichFaces is and how to get
its demo running and about all the steps required to really get a
component into a persistent conversation scope. Additionally we’ve
found out what to do when the project’s data source isn’t deployed
correctly to the server and got to know some troubleshooting tools.

Update 2009-04-21: Hotdeploy Seam app with Maven

There is an interesting Maven plugin (under active development) that can perform a hot-deploy of a Seam application to JBoss: Seam Hotdeploy Maven Plugin.

Resources

You can download
the complete seamTutorial12 Eclipse projects
, namely the project
itself and its companion Seam-generated *-test project. Notice that
the login, home, etc. pages have been generated by Seam and aren’t
actually needed for the richTable stuff though their removal would
require some modifications.

PS: Please excuse the terrible formatting :-(

About these ads

4 Responses to “Seam Tutorial 1.2: RichFaces and paged table (datascroller)”

  1. Bibek said

    I am using RICH Face in a jsf application.I want to retrieve data from data base as the same format it was stored in database e:g bold color italic etc
    pls help.

  2. Tim said

    Where is the portal mentioned in part 1?

  3. Jakub Holy said

    Tim> Well, I run out of time and also came to the conclusion that getting RichFaces/Seam into a Liferay portal would be rather complicated and actually not suitable for my needs as the JSF Bridge 1.0 doesn’t support JSR286 as you can see an a previous post (“When will we see JSR 286 in JSF portlets?”, http://www.jroller.com/holy/entry/when_will_we_see_jsr)

    However I did some experiments regarding running a Seam application in JBoss Portal, see the post “How I managed to deploy a JSF/Seam portlet to JBoss after all” (http://www.jroller.com/holy/entry/how_i_managed_to_deploy)

  4. abdalla said

    this was great, it is a good one, thank you.
    i have two points:
    1-if you want to use the extendedDataTable component, you can simply get the latest richfaces jars 3.3, remove the old jar files from the file system (the project lib) and put the new files.
    2- if i tried to use extendedDataTable, no data is printed at the runtime, nothing appears on the table.

Sorry, the comment form is closed at this time.

 
%d bloggers like this: