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

luni, 28 martie 2016

JSF 2.3 Multiple File Upload with HTML 5, AJAX and upload progress bar via web sockets


The source code from the above article was modified to support a progress bar via the JSF 2.3 web sockets. The main modifications implies:

- add <f:websocket/> in page

Note: In production, this web socket should be adjusted to distinguish between users there are login  (in case that you have this feature) via the optional user attribute (e.g. you can use: user="#{request.remoteUser}") - this is needed for sending messages to a single login user, the one who initiated the upload. In the form below, JSF will push messages to  to the current view only. You can also set scope="session" and push messages to all views in the current user session only. But we don't take into account if the user is login or not!

<f:websocket channel="upload" onmessage="socketListener" scope="view" />

Notice that this web socket it is automatically open when you start the application and it is not explictly closed. If you want to accomplish such features please read further JSF 2.3 - Explicitly open/close a websocket channel.

- provide a div where the upload progress is displayed (we display the percent as: x%), but you can choose to implement a visual progress bar:

<div id="progressBarId">0 %</div>

- provide the JavaScript listener for processing the incoming messages:

function socketListener(message, channel, event) {               
 document.getElementById("progressBarId").innerHTML = message + "%";
}

- on server-side, in UploadBean, you have to inject the PushContext via @Push annotation on the given channel name (upload):

@Inject
@Push(channel = "upload")
private PushContext push;

- and, finally, push the messages during upload:

push.send((totalReadSize * 100) / totalSize);

Below, you can see a possible output:


The complete application is available here.

JSF 2.3 - Firing one-time push when the web socket channel has been opened

Read also:

WebSocket integration by Arjan Tijms


In this post, we want to write an application that uses a web socket to "say" Hello world! - pushes the Hello World! message to a channel named hello. This should take place automatically (when the channel has been opened) and one-time only.

Since a web socket channel is automatically opened, we don't need to put any effort in this step. We just define in page a simple web socket like below:

<f:websocket channel="hello" onmessage="socketListener" /> 

Note: In such cases, we should use this together with the optional user attribute of  <f:websocket/> to push the message to a specific user that it is login. Moreover, we can use the optional scope attribute set to session (push messages to all views in the current user session only) or view (push messages to  to the current view only). In such cases, the CDI managed bean should be annotated with the CDI @SessionScoped respectively @ViewScoped. Since we just want to show the technique of  firing one-time push when the web socket channel has been opened, we will not "pollute" the code and we will use the application scope.

Since this channel should "transport" a single message, we need to close the channel after receiving the first message, like below:

<div id="helloId"></div>

<script type="text/javascript">           
 function socketListener(message, channel, event) {               
  document.getElementById("helloId").innerHTML = message;           
  jsf.push.close("hello");
 }
</script>

Further, we have an application bean capable to push the message to hello channel:

@Named
@ApplicationScoped
public class PushBean implements Serializable {

 @Inject
 @Push(channel = "hello")
 private PushContext push;

 public void pushAction() {
  push.send("Hello world!");
 }
}

The only aspect that we didn't cover so far refers to pushing the message automatically when the channel has been opened. For this, we can use two server events:

CDI WebsocketEvent will be fired with @WebsocketEvent.Opened qualifier
This event is fired when a web socket channel has been opened.

CDI WebsocketEvent will be fired with @WebsocketEvent.Closed qualifier
This event is fired when a web socket channel has been closed.

Both events can be observed in an application scoped CDI bean as below. So, in our case we can take advantage of the event fired when the web socket channel has been opened and push the Hello world! message:

@ApplicationScoped
public class WebsocketObserver {
   
 private static final Logger LOG =  
                Logger.getLogger(WebsocketObserver.class.getName());       
   
 @Inject   
 private PushBean pushBean;

  public void onOpen(@Observes @Opened WebsocketEvent event) {        
  pushBean.pushAction();       
 }   
   
 public void onClose(@Observes @Closed WebsocketEvent event) {
  String channel = event.getChannel();
  LOG.log(Level.INFO, "Channel {0} was successfully closed!", channel);
 }
}

Notice that via the WebsocketEvent object we can obtain information such as:

Returns a string representing the <f:websocket/> channel name:
event.getChannel();         

Returns a string representing the <f:websocket/> user:
event.getUser();            


Returns a CloseCode instance representing the close reason code:
event.getCloseCode();      

Now, our application output will be like in figure below:


The complete application is available here.

duminică, 27 martie 2016

JSF VS Series: @ManagedBean VS @Named

JSF VS Series: Forward VS Redirect


JSF 2.3 - Conditionally open/close a websocket channel

Read also:

WebSocket integration by Arjan Tijms


In this post, you will see how to conditionally open/close a channel. In order to accomplish this, we place the condition on the connected attribute via a ValueExpression. This attribute (condition) will be evaluated at each AJAX request, and if the value of this EL expression becomes false during an AJAX request then the push connection will explicitly be closed during oncomplete of that AJAX request.

In the JSF 2.3 - The WebSocket Quickstart under Payara post, we are pushing a message that indicates the current server time in format, hh:mm:ss. Let's re-write that example for limiting the pushing service only between 8-9 AM (PM).

The relevant part of the JSF page will look like this (notice the connected attribute at work):

<h:form>               
 <h:commandButton value="Clock" action="#{pushBean.clockAction()}">
  <f:ajax render="@form"/>
 </h:commandButton>
 Service status:
 <h:outputText value="#{pushBean.info}"/>
</h:form>

<f:websocket channel="clock"
             connected="#{pushBean.connected}"
             onopen="websocketOpenListener"
             onclose="websocketCloseListener"
             onmessage="socketListener" />     

On server-side we implement the condition logic as follows:

@Named
@ApplicationScoped
public class PushBean implements Serializable {

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

 @Inject
 @Push(channel = "clock")
 private PushContext push;

 private boolean connected;
 private String info;
 private String time;

 private void pingClock() {
  Calendar now = Calendar.getInstance();

  int hour = now.get(Calendar.HOUR_OF_DAY);
  connected = (hour >= 8) && (hour < 9);

  if (connected) {
      time = now.get(Calendar.HOUR_OF_DAY) + ":"
              + now.get(Calendar.MINUTE) + ":" + now.get(Calendar.SECOND);
      info = "Service is available";
  } else {
      time = null;
      info = "Service is available only between 8-9 AM/PM";
  }
 }

 public void clockAction() {

  pingClock();

  if (time != null) {
      LOG.log(Level.INFO, "Time: {0}", time);
      push.send(time);
  }
 }

 public boolean isConnected() {
  return connected;
 }

 public void setConnected(boolean connected) {
  this.connected = connected;
 }

 public String getInfo() {
  return info;
 }
}

So, the connected attribute will be set initially to false (default it is true meaning auto-connect). Furthermore, the attribute is evaluated to true only between the 8:00:00 - 8:59:59 server-time. This means that the first AJAX request that take place after 8:00:00 and before 8:59:59 will open the connection, while the first AJAX request that take place after 8:59:59 will close the connection.

The figures below shows a flow of usage:

-request fired before 8:00:00


-request fired after 8:00:00 opens the connection


- between 8:00:00 and 8:59:59 the connection is open

-after 8:59:59, the first request close the connection


The complete application is available here.

vineri, 25 martie 2016

JSF 2.3 - Explicitly open/close a websocket channel

Before you read this post I recommend you to read:

WebSocket integration by Arjan Tijms


In this post, you will see how to explicitly open/close a channel. By default, a channel is automatically open when we start the application, but this is not always the desired behavior. As a consequence, first we need to instruct JSF to not open our channel automatically, and for this we set the optional channel attribute to false. This attribute can be a javax.el.ValueExpression that must be evaluated to boolean, and by default it is set to true.

<f:websocket channel="clock" connected="false" onmessage="socketListener" />

This attribute is re-evaluated on every AJAX request, but since we have set it to false, our channel will never be connected. This give us the freedom to open/close the channel explicitly via JavaScript functions:

jsf.push.open("channelName") - explicitly open a channel
jsf.push.close("channelName") - explicitly close a channel

Since these are JavaScript functions they can be invoked exactly as any other JavaScript function. For example, you can invoke them from a snippet of JavaScript, or from the onclick event of a button, as below:

<h:form>   
 <h:commandButton value="Open channel" onclick="jsf.push.open('clock')">
  <f:ajax />
 </h:commandButton>
 <h:commandButton value="Close channel" onclick="jsf.push.close('clock')">
  <f:ajax />
 </h:commandButton>
 <h:commandButton value="Clock" action="#{pushBean.clockAction()}">
  <f:ajax />
 </h:commandButton>
</h:form>

Moreover, we may want to be informed when the channel is open/close. For this, we can attach two special JavaScript listeners, as follows:

<f:websocket channel="clock" connected="false"
             onopen="websocketOpenListener"
             onclose="websocketCloseListener"
             onmessage="socketListener" />   

The optional onopen and onclose attributes are javax.el.ValueExpression that must be evaluated to String. These Strings represents the JavaScript listener functions that are invoked when the web socket is opened/closed. The JavaScript listener corresponding to onopen will receive a single argument represented by the channel name. The JavaScript listener corresponding to onclose will be invoked with three arguments: the close reason code, the channel name and the raw CloseEvent itself. Since this function is invoked on errors also, it is a good practice to have it around. Below, you can see two dummy implementation of our listeners:

function websocketOpenListener(channel) {
 alert("Channel " + channel + " was successfully open!");
}

function websocketCloseListener(code, channel, event) {
 if (code == -1) {
     // Web sockets not supported by client.
     alert("Web sockets not supported by client");
 } else if (code == 1000) {
     // Normal close (as result of expired session or view).
     alert("The channel " + channel + " was successfully closed");
 } else {
     // Abnormal close reason (as result of an error).
     alert("The channel " + channel + " was abnormally closed");
 }
}

Now, the interface will look like in figure below:

Explicitly open the channel
Use the opened channel
Explicitly close the channel
When you run the application, you can notice that the Clock button will not "work" until the channel is opened, and will not "work" after the channel is closed. This means that you are controlling the channel status explicitly via Open channel and Close channel buttons.

The complete application is available here.

JSF 2.3 - The WebSocket Quickstart under Payara

Read also:

WebSocket integration by Arjan Tijms

JSF 2.3 - Explicitly open/close a websocket channel
JSF 2.3 - Conditionally open/close a websocket channel
JSF 2.3 - Firing one-time push when the web socket channel has been opened
JSF 2.3 - Multiple File Upload with HTML 5, AJAX and upload progress bar via web sockets

Starting with JSF 2.3-m05 we can take advantage of a brand new feature - register a web socket push connection in client side. Thanks to the JSF team (especially to Bauke Scholtz (aka BalusC)) this feature is available in today milestone via <f:websocket/> tag.

In this post, let's see a minimal usage of <f:websocket/> tag.

In JSF page, we need to add the <f:websocket/> tag with its two required attributes:

·         channel - This is javax.el.ValueExpression that must be evaluated to String and it represents the name of the web socket channel. A channel name is restricted to alphanumeric characters, hyphens, underscores and periods. A channel can have multiple open web sockets, and each of these sockets will receive the same push notification from the server.
·         onmessage - This is javax.el.ValueExpression that must be evaluated to String and it represents the a JavaScript listener function that is automatically invoked when a push notification is received from the server.

The signature of the listener function for onmessage is of type:

function fooListener(message, channel, event) {                      
 // message - the message pushed by the server
 // channel - the channel name
 // event - the raw MessageEvent instance
}

So, a simple <f:websocket/> tag usage will look like this:

<f:websocket channel="clock" onmessage="socketListener" />

<div id="clockId"></div>

<script type="text/javascript">
 function socketListener(message, channel, event) {                      
  document.getElementById("clockId").innerHTML += message + "<br/>";
 }
</script>

By default, when we start the application, the web socket is automatically connected and open. As long as the document is open the web socket is open. When the document is unloaded the web socket is automatically closed. In the web socket is initially successfully connected but the connection is closed as a result of e.g. a network error or server restart, JSF will try to auto-reconnect it at increasing intervals.

Now, let's focus on the server side. Here we have to take into account the push messages mechanism. This mechanism is based on javax.faces.push.PushContext interface and javax.faces.push.Push API.

First, you need to know that by default the web socket is application scoped. This means that the managed bean that can push messages to this web socket must be in application scope (annotated with @ApplicationScope). In this case, the push message can be sent by all users and the application itself.

Furthermore, you have to inject PushContext via @Push annotation on the given channel name in any CDI/container managed artifact. For example:

@Inject
@Push(channel = "clock")
private PushContext push;

Finally, we need to write an action method capable to push messages to web socket via PushContext. For example:

public void clockAction(){
 Calendar now = Calendar.getInstance();
       
 String time = now.get(Calendar.HOUR_OF_DAY) + ":" +
               now.get(Calendar.MINUTE) + ":" +
               now.get(Calendar.SECOND);
 LOG.log(Level.INFO, "Time: {0}", time);
    
 push.send(time);
}

Let's glue everything together. First, the JSF page:

<h:body>        
 <h:form>           
  <h:commandButton value="Clock" action="#{pushBean.clockAction()}">
   <f:ajax />
  </h:commandButton>
 </h:form>

 <f:websocket channel="clock" onmessage="socketListener" />       

 <hr/>
 <div id="clockId"></div>

 <script type="text/javascript">
  function socketListener(message, channel, event) {                      
   document.getElementById("clockId").innerHTML += message + "<br/>";
  }
 </script>   
</h:body>

Next, our simple CDI bean:

@Named
@ApplicationScoped
public class PushBean implements Serializable {
   
 private static final Logger LOG = Logger.getLogger(PushBean.class.getName());
       
 @Inject
 @Push(channel = "clock")
 private PushContext push;
   
 public void clockAction(){
  Calendar now = Calendar.getInstance();
       
  String time = now.get(Calendar.HOUR_OF_DAY) + ":" +
                now.get(Calendar.MINUTE) + ":" + now.get(Calendar.SECOND);
  LOG.log(Level.INFO, "Time: {0}", time);
       
  push.send(time);
 }
}

In order to avoid an error as in figure below (from Payara), we need to add a fake endpoint:


As BalusC pointed out, this fake  endpoint should look like below:

import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;

public class FakeEndpoint extends Endpoint {
    
 @Override
 public void onOpen(Session session, EndpointConfig config) {
  // https://java.net/jira/browse/WEBSOCKET_SPEC-240
 }   
}

Update provided by Arjan Tijms
"Nevertheless, the GlassFish/Tyrus seems to be the only implementation that do not support adding an EndPoint dynamically, so JSF team have added an SPI jar for Tyrus in GlassFish, that negates the need for the fake end point. See https://github.com/jsf-spec/mojarra/tree/master/spi. As far as we know the fake endpoint is also only needed for Tyrus, not for the other WebSocket implementations (such as the one in Tomcat)."

Finally, the m05 requires the following settings in web.xml:

<context-param>
 <param-name>javax.faces.ENABLE_CDI_RESOLVER_CHAIN</param-name>
 <param-value>true</param-value>
</context-param>   
<context-param>
 <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
 <param-value>true</param-value>
</context-param>

Done! The complete application was tested under Payara server and it is available here.

luni, 7 martie 2016

JSF VS Series: Implicit Navigation VS Declarative (explicit) Navigation

JSF Navigation Tutorial - Declarative Navigation

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

Declarative navigation refers to defining the navigation cases in faces-config.xml via a bunch of dedicated tags. Before implicit navigation, this was the only way to navigate after an action. These days  the implicit navigation is available, so declarative navigation is considered obsolete (officially speaking they are not obsolete or deprecated!). But, since this is a navigation tutorial we cannot simply ignore declarative navigation, therefore let's have a quick intro and some examples.
In order to navigate, JSF needs the well-known coordinates: view ID, logical outcome and/or action expression signature. Only that this time they are described in the faces-config.xml descriptor using XML-based rules, as follow:

<navigation-rule/>
This tag wraps the navigation cases of a specific view ID.

<from-view-id/>
This is a child of <navigation-rule/> and it defines the view ID from which we navigate.

<navigation-case/>
This is a child of <navigation-rule/> and it wraps a single navigation case characterized by view ID, logical outcome and/or action expression signature. There can be multiple navigation cases.

<from-outcome/>
This tag is a child of <navigation-case/> and represents the logical outcome.

<from-action/>
This tag is a child of <navigation-case/> and represents an action expression signature (optional).

<to-view-id/>
This tag is a child of <navigation-case/> and represents the view ID that resolve the logical outcome and/or action expression signature.

<redirect/>
This tag is a child of <navigation-case/> and instructs JSF that the navigation should be accomplish via PRG. By default, JSF will navigate via POST request based in forward mechanism.

For example, check the below navigation items:

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

<h:commandButton value="Success" action="success"/>
<h:commandLink value="Success" action="success"/>

The declarative version of this is as follows—thanks to implicit navigation, this code
is not needed:

<navigation-rule>
 <from-view-id>*</from-view-id>
 <navigation-case>
  <from-outcome>success</from-outcome>
  <to-view-id>/success.xhtml</to-view-id>
 </navigation-case>
</navigation-rule>

Let's see some more examples. The managed bean used in the next examples is listed first and the application is named DeclarativeNavigation:

@Named
@RequestScoped
public class TheBean {

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

 public String theActionWithDoneOutcome() {
  LOG.info("TheBean#theActionWithDoneOutcome() called ...");
  return "done";
 }
   
 public String theActionWithOutcomeForSuccess() {
  LOG.info("TheBean#theActionWithOutcomeForSuccess() called ...");
  return "doneFromTheActionWithOutcome";
 }
   
 public String theActionWithOutcomeForFailure() {
  LOG.info("TheBean#theActionWithOutcomeForFailure() called ...");
  return "doneFromTheActionWithOutcome";
 }   
   
 public String theActionWithRedirectForSuccess() {
  LOG.info("TheBean#theActionWithRedirectForSuccess() called ...");
  return "doneFromTheActionWithRedirect";
 }
   
 public String theActionWithRedirectForFailure() {
  LOG.info("TheBean#theActionWithRedirectForFailure() called ...");
  return "doneFromTheActionWithRedirect";
 }
}

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 via declarative navigation)

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

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <to-view-id>/success.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 via declarative navigation)

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

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <to-view-id>/success.xhtml</to-view-id>
 </navigation-case>
</navigation-rule>

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 via declarative navigation)

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

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>done</from-outcome>
  <to-view-id>/success.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:form>
 <h:commandLink value="Click Me!" action="doneredirect"/>
 <h:commandButton value="Click Me!" action="doneredirect"/>
</h:form>

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>doneredirect</from-outcome>
  <to-view-id>/success.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 (doneFromtheActionWithOutcome becomes success/failure.xhtml via declarative navigation)

// theActionWithOutcomeForSuccess() → doneFromTheActionWithOutcome → success.xhtml
<h:form>
 <h:commandLink value="Click Me!" action="#‌{theBean.theActionWithOutcomeForSuccess()}"/>
 <h:commandButton value="Click Me!" action="#‌{theBean.theActionWithOutcomeForSuccess()}"/>
</h:form>

// theActionWithOutcomeForFailure() → doneFromTheActionWithOutcome → failure.xhtml
<h:form>
 <h:commandLink value="Click Me!" action="#‌{theBean.theActionWithOutcomeForFailure()}"/>
 <h:commandButton value="Click Me!" action="#‌{theBean.theActionWithOutcomeForFailure()}"/>
</h:form>

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>doneFromTheActionWithOutcome</from-outcome>
  <from-action>#{theBean.theActionWithOutcomeForSuccess()}</from-action>
  <to-view-id>/success.xhtml</to-view-id>
 </navigation-case>
 <navigation-case>
  <from-outcome>doneFromTheActionWithOutcome</from-outcome>
  <from-action>#{theBean.theActionWithOutcomeForFailure()}</from-action>
  <to-view-id>/failure.xhtml</to-view-id>
 </navigation-case>
</navigation-rule>

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 presence of <redirect/> in navigation case will instruct JSF to rely on POST-redirect-GET (PRG) navigation pattern

// theActionWithRedirectForSuccess() → doneFromTheActionWithRedirect → success.xhtml
<h:form>
 <h:commandLink value="Click Me!" action="#‌{theBean.theActionWithRedirectForSuccess()}"/>
 <h:commandButton value="Click Me!" action="#‌{theBean.theActionWithRedirectForSuccess()}"/>
</h:form>

// theActionWithRedirectForFailure() → doneFromTheActionWithRedirect → failure.xhtml
<h:form>
 <h:commandLink value="Click Me!" action="#‌{theBean.theActionWithRedirectForFailure()}"/>
 <h:commandButton value="Click Me!" action="#‌{theBean.theActionWithRedirectForFailure()}"/>
</h:form>

Declaratively, we have:

<navigation-rule>
 <from-view-id>index.xhtml</from-view-id>
 <navigation-case>
  <from-outcome>doneFromTheActionWithRedirect</from-outcome>
  <from-action>#{theBean.theActionWithRedirectForSuccess()}</from-action>
  <to-view-id>/success.xhtml</to-view-id>
  <redirect/>
 </navigation-case>
 <navigation-case>
  <from-outcome>doneFromTheActionWithRedirect</from-outcome>
  <from-action>#{theBean.theActionWithRedirectForFailure()}</from-action>
  <to-view-id>/failure.xhtml</to-view-id>
  <redirect/>
 </navigation-case>
</navigation-rule>

The complete application is available here.

JSF BOOKS COLLECTION

Postări populare

Visitors Starting 4 September 2015

Locations of Site Visitors