My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

sâmbătă, 23 aprilie 2016

Caching with PrimeFaces and Hazelcast


PrimeFaces supports two different providers of cache implementation: EHCache and Hazelcast. In this post, we will take a look on the Hazelcast provider based on the same scenario described in the Caching via PrimeFaces and EHCache (register MBeans in JConsole via OmniFaces @Eager).

So, instead of re-rendering this static table, we better cache it. In order to accomplish that via PrimeFaces and Hazelcast, we need to follow these steps:

1.Configure the cache provider in web.xml via the primefaces.CACHE_PROVIDER context parameter:

<context-param>
  <param-name>primefaces.CACHE_PROVIDER</param-name>
  <param-value>org.primefaces.cache.HazelcastCacheProvider</param-value>
</context-param>

2. Use the <p:cache/> tag to point out the content that should be cached. As you can see in the official documentation this tag support a bunch of optional attributes. We are especially interested in the region and key attributes. The region attribute allows us to point to the cache region that we want to use, which is myCache in our case. The key attribute represents an unique id of the cache entry in region and defaults to client id of component. We set it as myTable. Of course, this means that we can use <p:cache/> with different regions and keys:

<p:cache region="myCache" key="myTable">
 <p:dataTable var="t" value="#{playersBean.data}">
  <p:column headerText="Player">
   <h:panelGroup id="playerId">#{t.player}</h:panelGroup>
  </p:column>

  <p:column headerText="Age">
   <h:panelGroup id="ageId">#{t.age}</h:panelGroup>
  </p:column>

  <p:column headerText="Birthplace">
   <h:panelGroup id="birthplaceId">#{t.birthplace}</h:panelGroup>
  </p:column>

  <p:column headerText="Residence">
   <h:panelGroup id="residenceId">#{t.residence}</h:panelGroup>
  </p:column>

  <p:column headerText="Height">
   <h:panelGroup id="heightId">#{t.height} cm</h:panelGroup>
  </p:column>

  <p:column headerText="Weight">
   <h:panelGroup id="weightId">#{t.weight} kg</h:panelGroup>
  </p:column>
 </p:dataTable>
</p:cache>

Done! If you run the application at this point then everything should work as expected. You will notice that initial request take some time to load, while postbacks will work very fast. This is a sign that, at postbacks, the table markup comes from cache. Moreover, you can see that Hazelcast is at work:
But, let's add some code to check the cache content. First, let's display the cache content in page:

Cache content:
<hr/>       
 <p>
  <code>
   #{playersBean.showCache()}
  </code>
 </p>
<hr/>

public Object showCache() {
 HazelcastCacheProvider cacheProvider = (HazelcastCacheProvider)
  RequestContext.getCurrentInstance().getApplicationContext().getCacheProvider();
 return cacheProvider.get("myCache", "myTable");
}

We also may want to programmatically remove our entry from cache. For this we can simply use an action method as below:

public void clearCache() {
 HazelcastCacheProvider cacheProvider = (HazelcastCacheProvider)
  RequestContext.getCurrentInstance().getApplicationContext().getCacheProvider();
 cacheProvider.remove("myCache", "myTable");
}

At initial request there is nothing in the cache:
At postback (click the Refresh button) the cache content shows the HTML markup corresponding to our data table:
Further, if you click the Clear Cache button, the cache entry will be removed.

The complete application is available here.

vineri, 22 aprilie 2016

Caching via PrimeFaces and EHCache (register MBeans in JConsole via OmniFaces @Eager)

PrimeFaces supports cache at rendering time. Basically, at initial request, PrimeFaces will cache the HTML markup that corresponds to the content delimited by <p:cache/> tag. This means that the initial request doesn't take advantage of caching, and even more it will take longer than usually since at this moment PrimeFaces caches the corresponding markup. But, at postbacks the cache will serve the cached HTML instead of rendering it again via specific renderers. This will reduce the time for loading page at postbacks.

PrimeFaces supports two different providers of cache implementation; EHCache and Hazelcast. In this post, we will take a look on the EHCache provider.

Let's suppose that we have a static table that list the tennis players from ATP. Something like in figure below (this table is produced via a simple usage of the <p:dataTable/> tag):

Rendering a table (<p:dataTable/>) is a time-consuming task, especially if the table contains many rows. So, instead of re-rendering this static table, we better cache it. In order to accomplish that via PrimeFaces and EHCache, we need to follow these steps:

1. Configure the pom.xml to contain the needed dependencies as below:

<dependencies>
 <dependency> 
  <groupId>org.primefaces</groupId> 
  <artifactId>primefaces</artifactId> 
  <version>5.3</version> 
 </dependency>
 <dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>3.0.0</version>
 </dependency>  
 <dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>2.10.1</version>
 </dependency>
 ...
</dependencies>

2.Configure the cache provider in web.xml via the primefaces.CACHE_PROVIDER context parameter:

<context-param>
  <param-name>primefaces.CACHE_PROVIDER</param-name>
  <param-value>org.primefaces.cache.EHCacheProvider</param-value>
</context-param>

3. Configure EHCache. There are multiple ways to accomplish this, and one of these consist in providing the ehcache.xml file in /src/main/resources folder of your application. The content of this file calibrates cache as you want (more details are available here):

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
  monitoring="autodetect" dynamicConfig="true">

  <!-- By default, Ehcache stored the cached files in temp folder. -->
  <!-- <diskStore path="java.io.tmpdir" /> -->
      
  <!-- Ask Ehcache to store cache in this path -->
  <diskStore path="D:\\cache" />
       
  <!-- Sample cache named myCache
       This cache contains a maximum in memory of 10000 elements, and will expire
       an element if it is idle for more than 5 minutes and lives for more than
       10 minutes. If there are more than 10000 elements it will overflow to the
       disk cache -->   
  <cache name="myCache"
         statistics="true"
         maxEntriesLocalHeap="10000"
         maxEntriesLocalDisk="1000"
         eternal="false"
         diskSpoolBufferSizeMB="20"
         timeToIdleSeconds="300" timeToLiveSeconds="600"
         memoryStoreEvictionPolicy="LFU"
         transactionalMode="off">
          <persistence strategy="localTempSwap" />
 </cache>
</ehcache>

The path D:\\cache must be manually created, or simply modify this entry to reflect a convenient path for you.

4. Use the <p:cache/> tag to point out the content that should be cached. As you can see in the official documentation this tag support a bunch of optional attributes. We are especially interested in the region attribute which allows us to point to the cache region that we want to use, which is myCache in our case. Of course, this means that we can use <p:cache/> with different regions. Since, by default, the region defaults to view id (if you want to use it like this simply add a <defaultCache/> region), we need to explicitly set it as below:

<p:cache region="myCache">
 <p:dataTable var="t" value="#{playersBean.data}">
  <p:column headerText="Player">
   <h:panelGroup id="playerId">#{t.player}</h:panelGroup>
  </p:column>

  <p:column headerText="Age">
   <h:panelGroup id="ageId">#{t.age}</h:panelGroup>
  </p:column>

  <p:column headerText="Birthplace">
   <h:panelGroup id="birthplaceId">#{t.birthplace}</h:panelGroup>
  </p:column>

  <p:column headerText="Residence">
   <h:panelGroup id="residenceId">#{t.residence}</h:panelGroup>
  </p:column>

  <p:column headerText="Height">
   <h:panelGroup id="heightId">#{t.height} cm</h:panelGroup>
  </p:column>

  <p:column headerText="Weight">
   <h:panelGroup id="weightId">#{t.weight} kg</h:panelGroup>
  </p:column>
 </p:dataTable>
</p:cache>

Done! If you run the application at this point then everything should work as expected. You will notice that initial request take some time to load, while postbacks will work very fast. This is a sign that, at postbacks, the table markup comes from cache.

But, how can we be sure that this is working? Well, EHCache provides management and monitoring using JMX. A simple approach consist in registering the cache statistics in the JDK platform MBeanServer, which works with the JConsole management agent. The needed code is listed below:

CacheManager manager = CacheManager.create();
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);

We can easily slip this code in an application scoped bean that is eagerly loaded via OmniFaces @Eager.

@Eager
@ApplicationScoped
public class CacheStatisticsBean {

 private static final Logger LOG = Logger.getLogger(CacheStatisticsBean.class.getName());
 private static final String CACHE_MANAGER = "net.sf.ehcache:type=CacheManager,name=__DEFAULT__";
 private static final String CACHE = "net.sf.ehcache:type=Cache,CacheManager=__DEFAULT__,name=myCache";
 private static final String CACHE_STATISTICS = "net.sf.ehcache:type=CacheStatistics,CacheManager=__DEFAULT__,name=myCache";
 private static final String CACHE_CONFIGURATION = "net.sf.ehcache:type=CacheConfiguration,CacheManager=__DEFAULT__,name=myCache";
 private static final ArrayList<ObjectName> objectNames = new ArrayList<ObjectName>() {
  {
  try {
      add(new ObjectName(CACHE_MANAGER));
      add(new ObjectName(CACHE));
      add(new ObjectName(CACHE_STATISTICS));
      add(new ObjectName(CACHE_CONFIGURATION));
  } catch (MalformedObjectNameException ex) {
                Logger.getLogger(CacheStatisticsBean.class.getName()).log(Level.SEVERE, null, ex);
  }
 }
 };

 @PostConstruct
 public void init() {
  try {
      LOG.info("------------ Configure JConsole MBeans ------------");
      MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
      LOG.info("----------------- Unregister MBeans ---------------");
      for (ObjectName name : objectNames) {
           if (mBeanServer.isRegistered(name)) {
               mBeanServer.unregisterMBean(name);
           }
      }

      LOG.info("------------------ Register MBeans ----------------");
      CacheManager manager = CacheManager.create();
      ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);

      LOG.info("------------ ------------------------  ------------");
  } catch (NullPointerException | InstanceNotFoundException | MBeanRegistrationException ex) {
    Logger.getLogger(CacheStatisticsBean.class.getName()).log(Level.SEVERE, null, ex);
  }
 }
}

Now we can perform a quick test to see if cache is working. For this, we have run the application on Payara 4. The steps of the test are:

1. Delete the content of D:\\cache folder and ensure that Payara is not running.
2. Start the application server. For Payara 4 simply start it via asadmin start-domain form /bin folder.
3. Start JConsole. Simply navigate to your Java home and double-click on jconsole.exe in the /bin folder.
4. Connect to Payara domain as in figure below:
5. After the connection is successfully accomplished navigate to the MBeans tab. Notice there the entry named net.sf.ehcache. This entry was added via the CacheStatisticsBean from above and it is what we are looking for.

At this moment, there is nothing in the cache. For checking this, simply expose some attributes under CacheStatistics as DiskStoreObjectCount, MemoryStoreObjectCount, CacheHits, etc:
 6. Now, deploy and run the application. After the application starts, let's point out that at this moment we have 1 object stored in memory and on disk (check also the D:\\cache content) and nothing was read from cache yet:
7. Now, refresh the browser few times or open the application in multiple tabs or even other browsers. After that refresh the indicators from figure above and you should notice something like below (pay attention to a few more also):
Well, now it is obvious that our cache is working fine.

Programmatically, you can clean cache content like this:
RequestContext.getCurrentInstance().getApplicationContext().getCacheProvider().clear();

The complete application is named PFAndECache.

miercuri, 20 aprilie 2016

JSF Navigation Tutorial - Programmatic Navigation

JSF provides the NavigationHandler and ConfigurableNavigationHandler APIs that can be used for tasks such as accessing navigation cases, customizing navigation handlers, conditional navigations, and so on. It is good to know that, programmatically speaking, we can do the following:

1. Obtain access to navigation handler (NavigationHandler) using the following code:

FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
NavigationHandler nh = application.getNavigationHandler();

2. Invoke navigation case using NavigationHandler as follows:

nh.handleNavigation(context, fromAction, outcome);
nh.handleNavigation(context, fromAction, outcome, toFlowDocumentId);

3. Access the ConfigurableNavigationHandler API using the following code:

ConfigurableNavigationHandler cnh = (ConfigurableNavigationHandler) FacesContext.
getCurrentInstance().getApplication().getNavigationHandler();

4. Invoke navigation case using ConfigurableNavigationHandler as follows:

cnh.handleNavigation(context, fromAction, outcome);
cnh.handleNavigation(context, fromAction, outcome, toFlowDocumentId);

5. Retrieve one NavigationCase object by the action expression signature and outcome as shown in the following code:

NavigationCase case = cnh.getNavigationCase(context, fromAction, outcome);
NavigationCase case = cnh.getNavigationCase(context, fromAction, outcome, toFlowDocumentId);

6. Access all navigation rules into Map<String, Set<NavigationCase>>, where the keys are the <from-view-id/> values as follows:

Map<String, Set<NavigationCase>> cases = cnh.getNavigationCases();

Starting with JSF 2.2, we have wrappers for many classes that provide basic implementations and help developers to extend those classes and override only the necessary methods. Among them, we have a wrapper class for NavigationHandler, named NavigationHandlerWrapper, one for ConfigurableNavigationHandler, named ConfigurableNavigationHandlerWrapper, and one for NavigationCase, named NavigationCaseWrapper.

marți, 19 aprilie 2016

JSF Navigation Tutorial - Preemptive Navigation

Check also:
The three golden rules of use
JSF Navigation Tutorial - Implicit Navigation
JSF Navigation Tutorial - Declarative Navigation
JSF Navigation Tutorial - Conditional Navigation
JSF Navigation Tutorial - Programmatic Navigation
JSF VS Series: Implicit Navigation VS Declarative (explicit) Navigation

Preemptive navigation is available starting with JSF 2.0. The navigation rules are more permissive and they are evaluated during the Render Response phase instead of the Invoke Application phase.

This is known as predetermined navigation or preemptive navigation. The current view ID and specified outcome are used to determine the target view ID. Afterwards, the target view ID is translated into a bookmarkable URL and used as the hyperlink's target. Practically, the URL is prepared without user interaction.

The main usage of preemptive navigation appears in bookmarkable component tags, <h:link/> and <h:button/>. For example, the following are two classical examples of preemptive navigation:

<h:link value="Success" outcome="success"/>
<h:button value="Success" outcome="success"/>

When the application starts, you can check the source code of the page to see how the corresponding URLs were mapped in the HTML tag <a/> in case of <h:link/>, and the HTML tag <input type="button"/> in case of <h:button>. Even if you never use those URLs, they are ready to serve.  Well, before JSF 2.0, navigation rules were explicitly the domain of POST requests (NavigationHandler.handleNavigation() was doing the dirty job behind the scene), but the new support for GET-based navigation and bookmarkability takes navigation to another level of flexibility and transparency (for example, the ConfigurableNavigationHandler API).

The interesting part here is how the query string of a URL is assembled. The simplest case consists of the implicit query string parameter as shown in the following code:

<h:link value="Done" outcome="done?id=done"/>.

But, you can also use <f:param/> and/or <f:viewParam/>.

Another way consists in using the <view-param/> tag nested in a <redirect/> tag in a navigation case. For example, we can add query string parameters to a redirect URL in the navigation rules. Let's create the following button:

<h:commandButton value="Success" action="#{playerBean.playerDone()}"/>

Also, a silly method named playerDone is as follows:

private String player;

public String getPlayer() {
 return player;
}

public void setPlayer(String player) {
 this.player = player;
}

public String playerDone() {
 player = "Rafael Nadal";
 return "done";
}

Now, we can add the player property value (of course, you can add any other value) as a parameter in the query string of the redirection navigation URL:

<navigation-rule>
 <from-view-id>/index.xhtml</from-view-id>
 <navigation-case>
  <from-action>#{playerBean.playerDone()}</from-action>
  <from-outcome>done</from-outcome>
  <to-view-id>/success.xhtml</to-view-id>
  <redirect>
   <view-param>
    <name>playerparam</name>
    <value>#{playerBean.player}</value>
   </view-param>
  </redirect>
 </navigation-case>
</navigation-rule>

A URL like this will be of the format (notice how the request parameter was attached based on the navigation rule) http://host:port/app-name/faces/success.xhtml?playerparam=Rafael+Nadal.

The playerparam value will be available through the param implicit object:

#{param['playerparam']}

The complete application is available here.

[OmniFaces utilities 2.4] URI-encode the given string using UTF-8


[OmniFaces utilities] The encodeURI() method encodes the given string using UTF-8. URIs (paths and filenames) have different encoding rules as compared to URLs. URLEncoder is actually only for www (HTML) form based query string parameter values. URI encoding has a lot in common with URL encoding, but the space has to be %20 and some chars doesn't necessarily need to be encoded.

Method
Usage

import org.omnifaces.util.Utils;
...
// http%3A%2F%2Fwww.names.org%3Fname%3DAnghel%20Leonard
String encode_uri = Utils.encodeURI("http://www.names.org?name=Anghel Leonard");

// check #encodeURL() also
// http%3A%2F%2Fwww.names.org%3Fname%3DAnghel+Leonard
String encode_url = Utils.encodeURL("http://www.names.org?name=Anghel Leonard");

Notice how the empty space was encoded by #encodeURI() as %20, while #encodeURL() as +.

JSF Navigation Tutorial - Conditional Navigation

Check also:
The three golden rules of use
JSF Navigation Tutorial - Implicit Navigation
JSF Navigation Tutorial - Declarative Navigation
JSF Navigation Tutorial - Preemptive Navigation
JSF Navigation Tutorial - Programmatic Navigation
JSF VS Series: Implicit Navigation VS Declarative (explicit) Navigation

Conditional navigation allows us to specify preconditions for choosing the desired navigation case; a precondition must be met in order for the navigation case to be accepted. For this, we use the <if/> tag as a child of the <navigation-case/> tag and use an EL expression that can be evaluated to a boolean value; here the true value matches the navigation case. If you don't prefer the declarative approach, then you can simply add the conditions in action methods.

Let's see some examples of using implicit navigation. The managed bean used in the next examples is listed first and the application is named ConditionalNavigation:

@Named
@RequestScoped
public class TheBean {

 private static final Logger LOG = Logger.getLogger(TheBean.class.getName());

 private int rnd = new Random().nextInt(100);

 public int getRnd() {
  return rnd;
 }

 public String theActionWithDoneOutcome() {
  LOG.info("TheBean#theActionWithDoneOutcome() called ...");
  return rnd > 50 ? "success" : "failure";
 }
   
 public String theActionWithDoneOutcomeAndRedirect() {
  LOG.info("TheBean#theActionWithDoneOutcome() called ...");
  return rnd > 50 ? "success?faces-redirect=true" : "failure?faces-redirect=true";
 }
}

FIRE A JSF GET REQUEST AND NAVIGATE TO THE VIEW ID COMPUTED FROM THE SPECIFIED OUTCOME
JSF will interpret the outcome value of <h:link/>/<h:button/> as the targeted page name (done becomes success.xhtml/failure.xhtml via conditional navigation)

<h:link value="Click me! (h:link)" outcome="done"/>
<h:button value="Click me! (h:button)" outcome="done"/>

JSF will render the right view by evaluating the following declarative condition:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <if>#{theBean.rnd gt 50}</if>
  <to-view-id>/success.xhtml</to-view-id>
 </navigation-case>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <if>#{theBean.rnd le 50}</if>
  <to-view-id>/failure.xhtml</to-view-id>
 </navigation-case>
</navigation-rule>

FIRE A JSF GET REQUEST. PROVIDE THE NAVIGATION OUTCOME VIA A SERVER-SIDE METHOD CALLED DURING COMPUTING THE VIEW ID (AT RENDERING TIME)
JSF will interpret the outcome value of <h:link/>/<h:button/> as the targeted page name (done returned by theActionWithDoneOutcome() becomes success.xhtml/failure.xhtml via conditional navigation)

<h:link value="Click me! (h:link)" outcome="#{theBean.theActionWithDoneOutcome()}"/>
<h:button value="Click me! (h:button)" outcome="#{theBean.theActionWithDoneOutcome()}"/>

JSF will render the right view by evaluating the following programmatic condition:

public String theActionWithDoneOutcome() {
 return rnd > 50 ? "success" : "failure";
}

FIRE (SUBMIT) A POST REQUEST VIA FORWARD MECHANISM AND NAVIGATE TO THE VIEW ID COMPUTED FROM THE SPECIFIED OUTCOME
JSF will interpret the action value of <h:commandLink/Button/> as the targeted page name (done becomes success.xhtml/failure.xhtml via conditional navigation)

<h:commandLink value="Click Me! (h:commandLink)" action="done"/>
<h:commandButton value="Click Me! (h:commandButton)" action="done"/>

JSF will render the right view by evaluating the same declarative condition from above:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <if>#{theBean.rnd gt 50}</if>
  <to-view-id>/success.xhtml</to-view-id>
 </navigation-case>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <if>#{theBean.rnd le 50}</if>
  <to-view-id>/failure.xhtml</to-view-id>
 </navigation-case>
</navigation-rule>

FIRE (SUBMIT) A POST REQUEST VIA REDIRECT MECHANISM AND NAVIGATE TO THE VIEW ID COMPUTED FROM THE SPECIFIED OUTCOME
The presence of <redirect/> in navigation case will instruct JSF to rely on POST-redirect-GET (PRG) navigation pattern

<h:commandLink value="Click Me! (h:commandLink)" action="doneredirect"/>
<h:commandButton value="Click Me! (h:commandButton)" action="doneredirect"/>

JSF will render the right view by evaluating the following declarative condition:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>doneredirect</from-outcome>
  <if>#{theBean.rnd gt 50}</if>
  <to-view-id>/success.xhtml</to-view-id>
  <redirect/>
 </navigation-case>
 <navigation-case>
  <from-outcome>doneredirect</from-outcome>
  <if>#{theBean.rnd le 50}</if>
  <to-view-id>/failure.xhtml</to-view-id>
  <redirect/>
 </navigation-case>
</navigation-rule>

FIRE (SUBMIT) A POST REQUEST VIA FORWARD MECHANISM. INVOKE AN ACTION METHOD AND NAVIGATE TO THE VIEW ID COMPUTED BASED ON THE OUTCOME RETURNED BY THIS METHOD
The action can point to an action method that returns a String. This string is considered the outcome and it will be interpreted as the targeted page name.

<h:commandLink value="Click Me! (h:commandLink)" action="#{theBean.theActionWithDoneOutcome()}"/>
<h:commandButton value="Click Me! (h:commandButton)" action="#{theBean.theActionWithDoneOutcome()}"/>           

JSF will render the right view by evaluating the following programmatic condition:

public String theActionWithDoneOutcome() {
 return rnd > 50 ? "success" : "failure";
}

FIRE (SUBMIT) A POST REQUEST VIA REDIRECT MECHANISM. INVOKE AN ACTION METHOD AND NAVIGATE TO THE VIEW ID COMPUTED BASED ON THE OUTCOME RETURNED BY THIS METHOD
The action can point to an action method that returns a String. This string is considered the outcome and it will be interpreted as the targeted page name. The presence of ?faces-redirect=true will instruct JSF to rely on POST-redirect-GET (PRG) navigation pattern.

<h:commandLink value="Click Me! (h:commandLink)" action="#{theBean.theActionWithDoneOutcomeAndRedirect()}"/>
<h:commandButton value="Click Me! (h:commandButton)" action="#{theBean.theActionWithDoneOutcomeAndRedirect()}"/>          

JSF will render the right view by evaluating the following programmatic condition:

public String theActionWithDoneOutcomeAndRedirect() {
 return rnd > 50 ? "success?faces-redirect=true" : "failure?faces-redirect=true";
}

Note
In conditional navigation, the navigation cases are evaluated even when the outcome is null or void. Notice that there is no <else/> tag or multiple conditional checking; therefore, in such cases, you have to emulate a switch statement. If you want to simply match the null outcome in any case, then you can use a condition of type: <if>#{true}</if>. Moreover, the sequence of the navigation rule affects the navigation flow; therefore, it is a good practice to prioritize conditions.

The complete application is available here.

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors