Introduction

Java EE 7 includes the new JSR 344 (JSF 2.2) standard and provides developers with new features like Resource Library Contracts, HTML5 Friendly Markup, and Faces Flows. The Liferay Faces team is hard at work at providing 1st class support for JSF 2.2 in Liferay Faces 4.x including the following new portlet demos:

JSF2 HTML5 Portlet (Source code at GitHub)

(Source code at GitHub) JSF2 Faces Flows Portlet (Source code at GitHub)

Webinar: Modern JSF Development

I recently had the privledge of showing these demos during a Liferay LIVE webinar titled Modern JSF Development. The webinar will be archived in a few weeks.

Personally I have to say that Faces Flows is my favorite feature in JSF 2.2. In fact, during the webinar one of the attendees wrote:

Wow.. Flows is a natural fit in Liferay... Excellent!

What Makes Faces Flows So Great?

Faces Flows is a standards-based Java EE feature that builds upon the lessons learned from projects like ADF Task Flows, Spring WebFlow, and Apache MyFaces CODI. It provides a way of connecting JSF views together and scoping data accordingly via the new @FlowScoped annotation and CDI.

@FlowScoped vs @ConversationScoped

The new JSF @FlowScoped annotation depends on CDI. At first glance it might seem similar to the CDI @ConversationScoped annotation, but it is a much better "fit" for JSF webapps/portlets because the developer doesn't need to make awkward programmatic calls to Conversation.begin() and Conversation.end() in a PhaseListener to make things work. Instead, beans annotated with @FlowScoped are created and destroyed automatically when the user navigates in and out of flows. In addition, developers can easily orchestrate sub-flows spawned from a parent flow and pass data between them using outbound and inbound parameters.

Let's take a closer look by examining the JSF2 Faces Flows Portlet demo...

Defining Flows

The portlet contains a main flow named "booking" and a sub-flow named "survey". The user (customer) books travel with Facelet views that are collected together in the "/booking" directory, and optionally completes a survey with Facelet views that are in the "/survey" directory.

As stated earlier, flows can pass data to each other using outbound and inbound parameters. If the customer takes the optional survey, then the customer's info is passed as an outbound parameter as defined in /booking/booking-flow.xml and received as an inbound parameter as defined in /survey/survey-flow.xml

Activating The Booking Flow

The initial view displayed by the portlet is /views/portletViewMode.xhtml

Simply clicking on the "Enter Booking Flow" button activates the "booking" flow:

<h:commandButton action=" booking " value="#{i18n['enter-booking-flow']}"> <f:ajax execute="@form" /> </h:commandButton>

Since the value of the action attribute is "booking", JSF will use convention-over-configuration to determine the target view for navigation. If it can't find a file named booking.xhtml in the same directory, it will attempt to find a directory named "/booking" and navigate to the view named /booking/booking.xhtml

Automatic Bean Scope Management by CDI

The portlet contains two model beans annotated with @FlowScoped:

When EL expressions like #{bookingFlowModelBean} or #{surveyFlowModelBean} are encountered by JSF in a Facelet view, the EL resolver chain will ask CDI to create an instance of BookingFlowModelBean.java or SurveyFlowModelBean.java respectively. Additionally, CDI will call any methods annotated with @PostConstruct such as BookingFlowModelBean.postConstruct() and SurveyFlowModelBean.postConstruct().

When the user navigates out of the "booking" flow or "survey" flow, then CDI will call any methods annotated with @PreDestroy such as BookingFlowModelBean.preDestroy() and SurveyFlowModelBean.preDestroy().

Final Thoughts

JSF portlet developers finally have a standards-based feature for creating wizard-like portlets. Gone are the integration headaches of making 3rd party flow add-ons work inside a portlet environment. The @FlowScoped annotation provides an elegant programming model and makes @ConversationScoped obsolete in many JSF use-cases. In addition, navigation between views can be fully ajaxified via f:ajax without writing any JavaScript.

All I can say is THREE CHEERS for Faces Flows!

+1 +1 +1 (pronounced Hip Hip Hooray in the olden days)