Over 10 years experiences across various industries, with focus on the finance industry. I have led the development team delivering multi-million dollar projects for online trading solution, relaunching of the retail banking website, portfolio management website, trading platform. He recently started his own blog at http://www.kole-consulting.com.au Ed has posted 3 posts at DZone. View Full User Profile

Event-Driven MVC with JSF and JBoss Seam - Common Pitfalls

03.17.2010
| 17499 views |
  • submit to reddit

Most Java web frameworks were designed with MVC in mind; however, JSF is probably one of the only few that achieved the goal. Combining JSF with JBoss Seam and Facelets, a developer can implement the MVC design pattern by using:

  • JBoss Seam POJO beans, representing the data model
  • JBoss Seam action beans, representing the controller, where the business logic is implemented, and where the data access layer is implemented
  • XHTML Facelets, representing the view
This represents a true MVC implemention because the JBoss Seam POJO bean acts like a DTO to allow the reading/writing of data. The JBoss Seam action bean is used for manipulating the data object, and the XHTML facelets only render the data that is "pushed" into the Seam beans.

Nevertheless, while the theories behind MVC and the JSF/Seam/Facelets model might seem apparent, I've seen developers oftentimes misuse them. Here are a few common pitfalls I have come across:

Scenario 1 - Placing controller initiation code in the facelets:

<s:div rendered="#{bean.init()}">
<h:outputText value="#{bean.text}"/>
</s:div>

 

Scenario 2 - Placing data retrieval methods in the facelets:

<s:div>
<h:panelGroup rendered="#{bean.getAccountbyAccountNumber(accountNumber)}">
<h:outputText value="#{accountBean.firstname}"/>
<h:outputText value="#{accountBean.lastname}"/>
</h:panelGroup>
</s:div>

 

Scenario 3 - Mis-understanding of JSF life cycles and JBoss Seam scope:

<s:div id="renderSection" rendered="#{bean.isVisible()}">
<h:outputText value="#{bean.text}"/>
<a4j:commandLink action="#{actionBean.retrieveBean()}" reRender="renderSection" />
</s:div>

...where bean was initialised through some other events and the bean has "Event Scope".

NOTE: the above link can never be "clicked" because as part of "restore view" stage, the "renderSection" is never rendered.

 

The 6 JSF life cycles

Understanding the life cycles of JSF are essential for developers to properly implement event driven JSF and partial rendering behaviour. JSF has 6 different phases:

JSF Life cycles

(source: JEE tutorial)

  1. Restore view
  2. Apply request values; process events
  3. Process validations; process events
  4. Update model values; process events
  5. Invoke application; process events
  6. Render response

In my opinion, restore view is the most diffcult phase to understand. In a restore view phase, it can be a new view or a postback being created:

  • A new view is created and saved in the FaceContext, when you visit a page the first time
  • Postback is whenever the user submit a form on an existing rendered page.

In Scenario 3, when the view handler restores the view from the FaceContext, it would be missing the Event Scope bean value and therefore stopped the execution.

 

Driving everything from events

Depending on how complex the logic is when rendering a page, you're able to decide whether you should adopt the pattern I described below. Lets look at an example mockup below JSF example mockup

It is a simple mailbox webpage and notice that whenever you select a tab on the left: inbox, outbox, contacts ... etc, or select a different account from the account dropdown, the folder tree, the list of mails, and the content of the mail views need to be updated. Obviously we could implement it by having

<h:selectOneMenu id="account" value="#{accountBean.selectedAccount}">
<f:selectItem itemValue="Select an account...."/>
<f:selectItems value="#{accountBean.validAccounts}"/>
<a4j:support event="onchange" actionListener="#{accountSelectAction.selectOneAccount}"
reRender="mailPanel" ajaxSingle="true"/>
</h:selectOneMenu>

 

...and within the selectOneAccount method implements the following logic:

  1. Retrieve a list of folders
  2. Set default folders
  3. Retrieve a list of mails in the folder
  4. Set the default displaying mail
  5. Retrieve the contents of the mail

A much more elegant solution is to implement:
Event.raiseEvent("ReloadFoldersEvent");Event.raiseEvent("ReloadFoldersEvent");

 

....and have a listener on RetrieveFolders method listening for the ReloadFoldersEvent. Once the folder retrieval is done, we want to raise another event within the method to trigger the retrieval of mails:

Event.raiseEvent("ReloadMailsEvent");

 

Repeat this pattern for RetrieveMails method (ReloadMailContentsEvent) and RetrieveMailContents method.

By raising the ReloadFoldersEvent, ReloadMailsEvent, ReloadMailContentsEvent and listen for the events in the Seam controllers, it is possible to eliminate creating very similiar logic over and over in RetrieveFolders, RetrieveMails and RetrieveMailContents controller methods.

Moreover, proper MVC is achieved by allowing RetrieveFolders, RetrieveMails and RetrieveMailContents (Controller) methods to populate the value beans (Model) on events, and the facelets (View) are ONLY required to interact with the value beans to populate the UI.

From KoLe Enterprise Consulting blog

#{bean.init()}
Published at DZone with permission of its author, Ed Lee.

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

Comments

Syed Mahdi replied on Tue, 2011/03/08 - 2:44am

Hello, nice post, I am new to seam: The three scenarios you gave what are the alternatives that a developer can do instead of doing these three things.

Syed Mahdi replied on Tue, 2011/03/08 - 2:53am

hello again: If I did this instead of calling bean.init() is it correct for the first scenario: create a backingBean pojo with a stringvariable create an Action class backingbeanaction and put @Name("backingbeanaction") and create a @Factory ("backingb") public BackingBeaninit(){ return new BaackingBean(); } and then in the xhtml use the #{backingb.stringvariable}

Comment viewing options

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