Reference Guide

The Problem
The PLOIN Solution
Download
Configuration for JSF 1.2
Configuration for JSF 2.0
Flow Definition
viewsForAllFlows
ignoreViews
disableUrlNavigation
afterLifecycleAction
afterFlowAction
subFlows
Security
Validators
BackNavigation
Navigation
BaseBean
Logging


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.

The Problem

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 Request-Solution

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 Session-Solution

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.

More Scopes

Some frameworks offers additional scopes. Here is short list of frameworks which are offering conversation-scopes.

  • Seam
  • Orchestra
  • Spring WebFlow
The Apache Trinidad Framework offers a PageFlow-Scope. The lifetime of the PageFlowScope from Trinidad is bound to the lifetime of the window. If your application is running in just one window, the PageFlowScope is the same as the HTTP-session-scope.
Every non-standard-scope has a strong binding to a special container. You always need a container which is offering you the non-standard-scope. You can not use the conversation scope from SpringWebFlow or Orchestra without the Spring IoC Container for example, because just this container offers you this special scope. If you are working with Google Guice or with PicoContainer you can not use the conversation scope from the spring IoC-Container. This is the big disadvantage of special scopes.

The ploinFaces-Solution

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

Download the ploinFaces.jar from sourceForge and add it to your Classpath.

http://sourceforge.net/project/showfiles.php?group_id=228979

Configuration for JSF 1.2

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.

Configuration for JSF 2.0

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.

Flow Definition

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.

viewsForAllFlows

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>

ignoreViews

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>

disableUrlNavigation

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>

afterLifecycleAction

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;
}
	

afterFlowAction

Equivalent to the tag <afterLifecycleAction> you can use <afterFlowAction>, which will be executed when you leave the flow.

subFlows

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>

Security

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>

Validators

ploinFaces offers you some very useful JSF-Validators.

  • HtmlKicker (protects from html injection)
  • EmailValidator
  • NumberOnlyValidator
  • PlzValidator (zipcode, just 5 numbers, there is no plausibility checking)
In the next lines I will show how you can use the htmlKicker. You have to put the following lines to your faces-config.xml

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

BackNavigation

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.
navigation overview
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']}" />
	

Navigation

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"; 
}
	

BaseBean

Sometimes you may want to get a String from the resource bundle or add a html message to your page. To do such work you can write a method. But you likely need this method in all your managedBeans. As a best practice you can write a BaseBean that all your other managedBeans extend. If you have to write more than a single JSF-application you would have to write this BaseBean for each project or you have carried it from project to project by copy and paste.
ploinFaces offers you a very good BaseBean with convenience methods that your managedBeans (and maybe your own customized BaseBean) can extend.
  • public FacesContext getFacesContext();
  • public Application getApplication();
  • public HttpServletRequest getRequest();
  • public HttpSession getSession();
  • public Map getApplicationMap();
  • public Object getValueFromApplicationMap(Object key);
  • public Map<String, String> getRequestMap();
  • public ResourceBundle getResourceBundle();
  • public String getStringFromResourceBundle(final String key);
  • public String getStringFromResourceBundle(final String key, final String[] params);
  • public String getStringFromResourceBundleDetail(String key);
  • public String getStringFromResourceBundleDetail(String key, final String[] params);
  • public String getStringFromResourceBundleSummary(String key);
  • public void addHTMLMessageFromBundle(FacesMessage.Severity severity, String key);
  • public void addHTMLMessageFromBundle(String compId, FacesMessage.Severity severity, String key, String[] params)
  • public void addHTMLMessageFromBundle(String compId, FacesMessage.Severity severity, String key)
  • public void addHTMLMessageFromBundle(FacesMessage.Severity severity, final String destination, final Sring key); This methode add a html message from your resource bundle to a special component (destination=componentId)
  • public void addHTMLMessage(FacesMessage.Severity severity, String summary, String detail);
  • public void addHTMLMessageFromBundle(FacesMessage.Severity severity, String key, String[] params) With this method you can add a parameterized html message to your site. If you have something like "key=welcome {0}, you are in {1}" in your message bundle, you can call this method with a StringArray of two Strings. {0} will be replaced by params[0] and {1} will be replaced by params[1]
  • public Lifecycle getCurrentDefaultLifecycleInstance();
  • public Long getLifecycleId(); ploinFaces generates a unique ID for every JSF-lifecycle which you can access this method. A usage for the LifecycleId can be in detecting redirects.
  • public Integer getCurrentPhaseId(); With this method you can find out in which JSF-Phase you are. The method returns a value between 1 and 6.
  • public String getFromViewId(); Here you can ask which viewId the request is coming from.
  • public Locale getLocale(); Return the current Locale from the viewRoot.
  • public void setLocale(); Set a new Locale in the JSF viewRoot.
  • public UIViewRoot getViewRoot(); Return the JSF viewRoot.
  • public UIViewRoot getViewRootId(); Return the JSF viewRootId.
A complete documentation of all methods, with examples, can be found in the ploinFaces javaDoc.

Logging

ploinFaces uses Apache log4j with the commons-logging package from Apache for logging. By inserting the following line in your log4j.properties file, you can run ploinfaces in debug-mode.
log4j.logger.org.ploin.web DEBUG
		
If you are using ploinFaces in debug-mode, you can see what happens in which JSF-Phasea and you can the time it takes for the Lifecycle needs to run through. Here is an example output.
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
		
If you don't want see this output, place the following line in your log4j.properites file.
log4j.logger.org.ploin.web ERROR
		
This line will order log4j to log only real errors.