With ploinFaces you can define flows for your JSF-Application.
A flow has several views (xhtml or JSP or something diffrent)
and attributes (ManagedBeans). If you leave a flow, the framework
removes the attributes from the session. It works with the
standard HTTP-session scope. You need no special conversation scope.
Furthermore ploinFaces offers you some useful JSF-Validators,
a BackNavigationHandler and a BaseBean with convenient methods.
There is need for a flow. Different from standard JSF navigation, a flow can have N pages. For example, a registration can have 4 pages: base-data (page 1), survey-data (page 2), contracts (page 3) and the liability (page 4). At the end of the flow, after the customer has accepted the contracts and the liability, the input-data should be written in the database. You have to carry the input-data from page 1 and 2 to the end of the flow.
The input-data is passed from one page to another in hidden
fields. The input-data from page 1 will be validated if you are going from page
1 to page 2 and on page 2 they are saved in input hidden fields.
For a hacker it is very easy to change the data in the input hidden
fields.
To protect your data in the hidden fields you have to validate it
every time you are going to the next page.
If an error occurs when going from one page to another, all
data in the hidden fields are lost and the customer has to
start at the page 1 again.
The input data is stored in the HTTP-Session on the server.
A hacker has no access to the data in the HTTP-Session.
At the end of the flow the input data is stored in the database
and is removed from the HTTP-Session.
If the customer leaves the flow before having reached the last page,
the input data stays in the HTTP-Session until the session is ended by a timeout.
Often this can be the reason for memory and performance problems.
If there are to many unused objects in the HTTP-Session and
there are to many parallel HTTP-Sessions you can get an
OutOfMemoryException and the whole application crashes.
Some frameworks offers additional scopes. Here is short list of frameworks which are offering conversation-scopes.
With ploinFaces you need no special scopes, as it works with the standard session scope. You just have to define a flow in a xml-file and the framework will clear the session for you at the right moment. It works with every Servlet-Container and with every IoC-Container. You have just a dependency to the ploinFaces.jar (which is very lightweight with less than 40kb).
Download the ploinFaces.jar from sourceForge and add it to your Classpath.
http://sourceforge.net/project/showfiles.php?group_id=228979
To set up the Framework you have to add the BackNavigationHandler and the system-event-listener-class from ploinFaces to your faces-config.xml like this:
<application>
<navigation-handler> org.ploin.web.faces.core.BackNavigationHandler </navigation-handler>
</application>
And you have to add the following Listener to your web.xml:
<listener>
<listener-class> org.ploin.web.faces.core.PloinFacesListener </listener-class>
</listener>
That's it. Your ready for using ploinfaces in your project.
To set up the Framework you have to add the BackNavigationHandler and the system-event-listener-class from ploinFaces to your faces-config.xml like this:
<application>
<system-event-listener>
<system-event-listener-class> org.ploin.web.faces.core.PhaseListenerInstallationListener </system-event-listener-class>
<system-event-class> javax.faces.event.PostConstructApplicationEvent </system-event-class>
</system-event-listener>
<navigation-handler> org.ploin.web.faces.core.BackNavigationHandler </navigation-handler>
</application>
That's it. Your ready for using ploinfaces in your project.
The managedBean you are using in a flow, should be in the session scope. Be aware that ploinFaces is not responsible for the creation of the beans. You can use the standard JSF-Bean-Container configured in the faces-config.xml for the creation and managing of your managedBeans.
<managed-bean>
<managed-bean-name>logInOutBean</managed-bean-name>
<managed-bean-class>org.company.project.java.gui.model.LogInOutBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
ploinFaces expects the flow definition in a file "ploinFlows.xml" located in your src-folder, where your "*.java" files, hibernate.properties and log4j.properties are placed, too. Here is an example for a flow with three pages. The Managed-Bean "logInOutBean" has session scope.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<flow id="loginFlow">
<views>
<view>/login.xhtml</view>
<view>/page/help/agbLogin.xhtml</view>
<view>/page/help/haftungLogin.xhtml</view>
</views>
<attributes>
<attribute>logInOutBean</attribute>
</attributes>
</flow>
</flows>
If you leave the flow, ploinFaces removes the logInOutBean from the session. Furthermore you can use java-regex for the views. Here is the next example with java-regex:
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
</flow>
</flows>
Every view starting with "/page/useradmin" is set to be part of the flow.
You can enter and leave a flow at any point.
If you leave a flow, ploinFaces removes all attributes that are set for the flow from the HTTP session.
With <viewsForAllFlows> you can define views that should be in all your flows. This can really come in handy if you have custom error pages (maybe defined in your web deployment descriptor (web.xml)). If an error occurs in your flow that will cause you to be forwarded to one of these error pages and if this pages are not defined in your flow, ploinFaces will remove your managedBean from the session because you will have left the flow. If you try to navigate back now using the browser back-button (to continue the flow), your bean container (JSF, Spring, Guice, Pico or whatever) creates a new managedBean for the flow because you enter the flow again. But this is not the behavior you expect and I am pretty sure you don't want to write separate error pages for all your flows, because it would break the DRY principle.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<viewsForAllFlows>
<view>/page/error/.*</view>
</viewsForAllFlows>
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
</flow>
</flows>
Equivalent to <viewsForAllFlows> you can use the tag <ignoreViews>. If you put a view in the ignoreViews-Set, ploinFaces will do nothing if you step into or leave the view.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<ignoreViews>
<view>/page/error/.*</view>
</ignoreViews>
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
</flow>
</flows>
If this tag is true, it is not possible to call manuelly a page in the application. You will always keep on the current view. With this tag you can force the customer to user the navigation buttons/links in your application.
This feature just work with JSF 1.2, NOT with JSF 2.0.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<disableUrlNavigation>true </disableUrlNavigation>
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
</flow>
</flows>
With the tag <afterLifecycleAction> you can execute a method in your managed bean, after the JSF-lifecycle (after the response is rendered). This is very useful if you want to clear/close some things in your managed bean.
Imagine you have a list (users) in your managed bean (useradministrationBean). You may need to keep some properties of the bean in the session the whole time, for example the selected User-Object. But maybe the list of users should be fetched from the database on every request. In this case it would be nonsense to hold the list of users in the session. Using the <afterLifecycleAction> tag you could clear the list, after the response has been rendered.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
<afterLifecycleAction> #{useradminBean.clear} </afterLifecycleAction>
</flow>
</flows>
public String getClear(){
users = null;
return null;
}
Equivalent to the tag <afterLifecycleAction> you can use <afterFlowAction>, which will be executed when you leave the flow.
With the tag <subFlow> you can include a flow into another flow. If you enter a child flow (profileViewFlow) from a parent flow (useradministrationFlow), both flows stay in the session. If you return from a child flow to the parent flow, the child flow will be removed from the session. If you step into a page, which is not defined in the current flow and not defined in a subflow, the parent flow and all his subflows will be removed from the session.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
<subFlows>
<flowId>profileViewFlow</flowId>
<flowId>messageFlow</flowId>
</subFlows>
</flow>
</flows>
You have the possibility to block a complete flow. Say you have a flow in your useradministration that you want to control access to - you can do this by defining that only an administrator or a GIS can enter this flow for example and all other users will be forwarded to an access-denied-page.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<authoritySource>#{sessionBean.loginUserRole}</authoritySource>
<accessDeniedPage>/page/accessDenied.xhtml</accessDeniedPage>
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
<includeAuthorities>
<authority>Administrator</authority>
<authority>GIS</authority>
</includeAuthorities>
</flow>
</flows>
With the tag <includeAuthorities> you can define multiple authorities (roles/credentials) that are allowed
enter this flow. With the tag <excludeAuthorities> you can define authorities that are explicitly
excluded from entering this flow. You should either use <includeAuthorities> or <excludeAuthorities>,
but not at the same time.
If you work with authorities you have to define an <authoritySource> at the top of your xml config file.
In the <authoritySource> you should write a valid EL-Expression, for example the name of a managedBean
and a property. The expression should return a String. If the returned String is equal to an <includeAuthorities>
the user can access the flow. If it is not equal to an <includeAuthorities> the user will be
forwarded to the <accessDeniedPage>. If no <accessDeniedPage> defined the user stay on the
current page.
Here is an equivalent example with <excludeAuthorities>.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<authoritySource>#{sessionBean.loginUserRole}</authoritySource>
<accessDeniedPage>/page/accessDenied.xhtml</accessDeniedPage>
<flow id="useradministrationFlow">
<views>
<view>/page/useradmin.*</view>
</views>
<attributes>
<attribute>useradminBean</attribute>
<attribute>secureBean</attribute>
</attributes>
<excludeAuthorities>
<authority>User</authority>
<authority>PremiumUser</authority>
</excludeAuthorities>
</flow>
</flows>
If you have a web-application with a login area we recommend you to put all your files for the login area in a subdirectory, for example "/page/inside". If you do this, you can protect your login area with a simple flow.
<flows xmlns="http://www.ploinfaces.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ploinfaces.org/schema http://www.ploinfaces.org/schema/ploinFlows_1.4.xml.xsd">
<authoritySource>#{sessionBean.isUserLoggedIn}</authoritySource>
<accessDeniedPage>/page/accessDenied.xhtml</accessDeniedPage>
<flow id="insideFlow">
<views>
<view>/page/inside/.*</view>
</views>
<includeAuthorities>
<authority>true</authority>
</includeAuthorities>
</flow>
</flows>
ploinFaces offers you some very useful JSF-Validators.
<validator>
<validator-id>htmlKicker</validator-id>
<validator-class>org.ploin.web.faces.validator.HtmlKicker</validator-class>
</validator>
In your xhtm or jsp sites you can use the validator with the id. Here is an example with iceFaces.
<ice:inputText id="firstName"
value="#{profileBean.usersfirstName}"
required="true"
validator="htmlKicker"/>
If anybody tries to include html code in the textfield, the validator throws a JSF-Message. You can customize the message in your standard ResourceBundle with the key "ploin.htmlKicker".
ploin.htmlKicker=Please dont't type in html code!
You can configure, use and customize the other validators in the same way.
The Browser-Back-Button does not work correctly in many JSF and
AJAX Applications. As a work-around you'll often find a special back-button included
in the page.
To install a back-button you can look in the faces-config.xml to see which outcome you have to
use to navigate back to a special site. This works as long as every page has at max one predecessor.
But just take the following situation where 5 diffrent pages lead to the same page.

In this situation you have to store the viewId, a user is coming from, in your application.
I have faced this scenario dozens of times and it is a pain!
To install a back-button with ploinFaces is a breeze. You just have to type in "back" in
the action and the Framework navigates back to the last site. Here is an example with iceFaces.
<ice:commandButton action="back"
id="backButton"
value="#{labels['back']}" />
It's easy, isn't it?
And that's not all. If you have to navigate two steps back, you can use the outcome "backback".
<ice:commandButton action="backback"
id="backButton"
value="#{labels['back']}" />
And with "backbackback" you can navigate three steps back.
<ice:commandButton action="backbackback"
id="backButton"
value="#{labels['back']}" />
In JSF you have to define your navigation-rules in the faces-config.xml. If your managedBean
returns an outcome, JSF looks for a navigation-rule in the faces-config.xml and navigates to the
site.
With the new NavigationHandler you don't need navigation-rules in your faces-config.xml. If
your outcome starts with "->". ploinFaces looks for a viewId named like your outcome without
"->". For example, your outcome is "->/page/welcome.xhtml" then ploinFaces will navigate to
/page/welcome.xhtml. You can navigate directly from your ManagedBean to your xhtml page,
without navigation rules.
public String finishSomething(){
return "->/page/welcome.xhtml";
}
log4j.logger.org.ploin.web DEBUG
2008-09-16 00:39:36,551 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener01 - beforePhase RESTORE_VIEW(1) 2008-09-16 00:39:36,551 [http-8080-3] DEBUG org.ploin.web.faces.phaselistener.JsfPhaseListener01 - fromViewId /page/inside/pag1.xhtml 2008-09-16 00:39:36,552 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener01 - afterPhase RESTORE_VIEW(1) 2008-09-16 00:39:36,553 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener02 - beforePhase APPLY_REQUEST_VALUES(2) 2008-09-16 00:39:36,554 [http-8080-3] DEBUG org.ploin.project.java.gui.bean.SessionBean - isStartButtonDisabled false 2008-09-16 00:39:36,557 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener02 - afterPhase APPLY_REQUEST_VALUES(2) 2008-09-16 00:39:36,558 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener03 - beforePhase PROCESS_VALIDATIONS(3) 2008-09-16 00:39:36,558 [http-8080-3] DEBUG org.ploin.project.java.gui.bean.SessionBean - isStartButtonDisabled false 2008-09-16 00:39:36,562 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener03 - afterPhase PROCESS_VALIDATIONS(3) 2008-09-16 00:39:36,563 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener04 - beforePhase UPDATE_MODEL_VALUES(4) 2008-09-16 00:39:36,563 [http-8080-3] DEBUG org.ploin.project.java.gui.bean.SessionBean - isStartButtonDisabled false 2008-09-16 00:39:36,565 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener04 - afterPhase UPDATE_MODEL_VALUES(4) 2008-09-16 00:39:36,566 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener05 - beforePhase INVOKE_APPLICATION(5) 2008-09-16 00:39:36,568 [http-8080-3] DEBUG org.ploin.web.faces.core.BackNavigationHandler - outcome ->/page/inside/page2.xhtml 2008-09-16 00:39:36,570 [http-8080-3] DEBUG org.ploin.web.faces.core.BackNavigationHandler - navigateToViewId to /page/inside/page2.xhtml 2008-09-16 00:39:36,571 [http-8080-3] DEBUG org.ploin.web.faces.core.BackNavigationHandler - after navigation 2008-09-16 00:39:36,571 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener05 - afterPhaseINVOKE_APPLICATION(5) 2008-09-16 00:39:36,573 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener06 - beforePhase RENDER_RESPONSE(6) 2008-09-16 00:39:36,709 [http-8080-3] DEBUG org.ploin.project.java.gui.bean.SessionBean - getSelectedLocale en_GB 2008-09-16 00:39:37,202 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener06 - afterPhase RENDER_RESPONSE(6) 2008-09-16 00:39:37,203 [http-8080-3] DEBUG org.ploin.web.faces.phaselistener.JsfPhaseListener06 - go to viewId /page/inside/page2.xhtml 2008-09-16 00:39:37,203 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener06 - . 2008-09-16 00:39:37,204 [http-8080-3] INFO org.ploin.web.faces.phaselistener.JsfPhaseListener06 - from beforePhase1 to afterPhase6: 153ms
log4j.logger.org.ploin.web ERROR