4

I am solving how to pass values from one page to another without making use of session scope managed bean. For most managed beans I would like to have only Request scope.

I created a very, very simple calculator example which passes Result object resulting from actions on request bean (CalculatorRequestBean) from 5th phase as initializing value for new instance of request bean initialized in next phase lifecycle.

In fact - in production environment we need to pass much more complicated data object which is not as primitive as Result defined below.

What is your opinion on this solution which considers both possibilities - we stay on the same view or we navigate to the new one. But in both cases I can get to previous value stored passed using view scoped managed bean.

Calculator page:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html">
<h:head>
    <title>Calculator</title>
</h:head>
<h:body>
    <h:form>
        <h:panelGrid columns="2">
            <h:outputText value="Value to use:"/>
            <h:inputText value="#{calculatorBeanRequest.valueToAdd}"/>

            <h:outputText value="Navigate to new view:"/>
            <h:selectBooleanCheckbox value="#{calculatorBeanRequest.navigateToNewView}"/>

            <h:commandButton value="Add" action="#{calculatorBeanRequest.add}"/>
            <h:commandButton value="Subtract" action="#{calculatorBeanRequest.subtract}"/>

            <h:outputText value="Result:"/>
            <h:outputText value="#{calculatorBeanRequest.result.value}"/>

            <h:commandButton value="Calculator2" action="calculator2"/>

            <h:outputText value="DUMMY" rendered="#{resultBeanView.dummy}"/>
        </h:panelGrid>
    </h:form>
</h:body>

Calculator2 page with operations multiply and divide:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Calculator 2</title>
    </h:head>
    <h:body>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputText value="Value to use:"/>
                <h:inputText value="#{calculatorBeanRequest2.valueToAdd}"/>

                <h:outputText value="Navigate to new view:"/>
                <h:selectBooleanCheckbox value="#{calculatorBeanRequest2.navigateToNewView}"/>

                <h:commandButton value="Multiply" action="#{calculatorBeanRequest2.multiply}"/>
                <h:commandButton value="Divide" action="#{calculatorBeanRequest2.divide}"/>

                <h:outputText value="Result:"/>
                <h:outputText value="#{calculatorBeanRequest2.result.value}"/>

                <h:commandButton value="Calculator" action="calculator"/>

                <h:outputText value="DUMMY" rendered="#{resultBeanView.dummy}"/>
            </h:panelGrid>
        </h:form>
    </h:body>
</html>

Object to be passed through lifecycle:

package cz.test.calculator;

import java.io.Serializable;

/**
 * Data object passed among pages.
 * Lets imagine it holds something much more complicated than primitive int
 */
 public class Result implements Serializable {

    private int value;

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }   
}

Request scoped managed bean used on view "calculator.xhtml" with actions add and subtract

package cz.test.calculator;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;


@ManagedBean
@RequestScoped
public class CalculatorBeanRequest {

    @ManagedProperty(value="#{resultBeanView}")
    ResultBeanView resultBeanView;

    private Result result;

    private int valueToAdd;

    /**
     *  Should perform navigation to 
     */
    private boolean navigateToNewView;

    /** Creates a new instance of CalculatorBeanRequest */
    public CalculatorBeanRequest() {        
    }


    @PostConstruct
    public void init() {       
        // Remember already saved result from view scoped bean
        result = resultBeanView.getResult();
    }

    // Dependency injections
    public void setResultBeanView(ResultBeanView resultBeanView) {
        this.resultBeanView = resultBeanView;
    }

    public ResultBeanView getResultBeanView() {
        return resultBeanView;
    }

    // Getters, setter
    public void setValueToAdd(int valueToAdd) {
        this.valueToAdd = valueToAdd;
    }

    public int getValueToAdd() {
        return valueToAdd;
    }

    public boolean isNavigateToNewView() {
        return navigateToNewView;
    }

    public void setNavigateToNewView(boolean navigateToNewView) {
        this.navigateToNewView = navigateToNewView;
    }

    public Result getResult() {
        return result;
    }

    // Actions
    public String add() {        
        result.setValue(result.getValue() + valueToAdd);
        return isNavigateToNewView() ? "calculator" : null;
    }    

    public String subtract() {        
        result.setValue(result.getValue() - valueToAdd);
        return isNavigateToNewView() ? "calculator" : null;
    }
}

Request scoped managed bean used on view "calculator2.xhtml" with actions divide and multiply:

package cz.test.calculator;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;


@ManagedBean
@RequestScoped
public class CalculatorBeanRequest2 {

    @ManagedProperty(value="#{resultBeanView}")
    ResultBeanView resultBeanView;

    private Result result;

    private int valueToUse;

    /**
     *  Should perform navigation to
     */
    private boolean navigateToNewView;

    /** Creates a new instance of CalculatorBeanRequest2 */
    public CalculatorBeanRequest2() {
    }


    @PostConstruct
    public void init() {
        result = resultBeanView.getResult();
    }

    // Dependency injections
    public void setResultBeanView(ResultBeanView resultBeanView) {
        this.resultBeanView = resultBeanView;
    }

    public ResultBeanView getResultBeanView() {
        return resultBeanView;
    }

    // Getters, setter
    public void setValueToAdd(int valueToAdd) {
        this.valueToUse = valueToAdd;
    }

    public int getValueToAdd() {
        return valueToUse;
    }

    public boolean isNavigateToNewView() {
        return navigateToNewView;
    }

    public void setNavigateToNewView(boolean navigateToNewView) {
        this.navigateToNewView = navigateToNewView;
    }

    public Result getResult() {
        return result;
    }

    // Actions
    public String multiply() {
        result.setValue(result.getValue() * valueToUse);
        return isNavigateToNewView() ? "calculator2" : null;
    }

    public String divide() {
        result.setValue(result.getValue() / valueToUse);
        return isNavigateToNewView() ? "calculator2" : null;
    }    
}

and finally view scoped managed bean to pass Result variable to new page:

package cz.test.calculator;

import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;


@ManagedBean
@ViewScoped
public class ResultBeanView implements Serializable {    

    private Result result = new Result();

    /** Creates a new instance of ResultBeanView */
    public ResultBeanView() {        
    }

    @PostConstruct
    public void init() {
        // Try to find request bean ManagedBeanRequest and reset result value
        CalculatorBeanRequest calculatorBeanRequest =  (CalculatorBeanRequest)FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("calculatorBeanRequest");
        if(calculatorBeanRequest != null) {
            setResult(calculatorBeanRequest.getResult());
        }
        CalculatorBeanRequest2 calculatorBeanRequest2 =  (CalculatorBeanRequest2)FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("calculatorBeanRequest2");
        if(calculatorBeanRequest2 != null) {
            setResult(calculatorBeanRequest2.getResult());
        }
    }
    /** No need to have public modifier as not used on view
     *  but only in managed bean within the same package
     */
    void setResult(Result result) {
        this.result = result;
    }

    /** No need to have public modifier as not used on view
     *  but only in managed bean within the same package
     */
    Result getResult() {
      return result;
    }

    /**
     * To be called on page to instantiate ResultBeanView in Render view phase
     */
    public boolean isDummy() {
        return false;
    }

}
Fekete Kamosh
  • 361
  • 1
  • 5
  • 18

1 Answers1

6

Your question asks about how to pass values from one page to another without making use of session scope managed bean, but in your example I don't actually see different pages.

You stay on the same page (view) all the time. JSF components automatically retain their values in the so-called view state, so there is no need for you to pass anything along manually here.

If you really wanted to pass information between completely different pages (e.g. from a calculator.xthml to a result_overview.xhtml) then one possible solution would be using the conversation scope from Java EE 6. If you are only using the JSF 2.0 libs on e.g. Tomcat, you can't use this scope, but if you added a CDI implementation or deployed to a full Java EE AS like Glassfish V3 or Jboss AS 6 then you could use this.

The conversation scope really works between pages, but there is a small catch in that you have to change your managed beans from @ManagedBean to @Named and have to realize you would be using CDI beans and not JSF managed beans. They mostly interoperate, but there are a number of caveats.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • Yes you are right that I am not navigating to new page in this example. Sorry I misused term "page". Anyway I navigate to new "view" which garbage view scoped bean (and this is the reason why I let it reinstantiate using . Also look at ResultBeanView.init(). I may add new page calculator2.xhtml with actions divide and multiply which might make use Result resulting from previous operations. Should I make up my example this way? Concerning Conversation scope - I will look at it but however now we have only Tomcat and Mojarra. – Fekete Kamosh Jan 02 '11 at 15:12
  • I altered my example to consist of two pages sharing the same data of Result object. – Fekete Kamosh Jan 02 '11 at 15:37
  • It seems like it should work ;) But notice you'll suffer from the *one-page-behind-syndrome* (address bar of browser still shows the URL for 'Calculator' while in fact it is displaying 'Calculator2'). You have created a little crude workaround what can also be done in JSF via ``The Flash`` scope or in Java EE in general via the already mentioned conversation scope. – Arjan Tijms Jan 02 '11 at 15:49
  • In fact I tried Flash scope but it could be used only once. Repetitive usage was impossible as Flash scope "stuff" survived only one round of lifecycle:-( If you have any clue how to use it in my example, please please show it to me. – Fekete Kamosh Jan 02 '11 at 16:21
  • I went through article http://www.ibm.com/developerworks/java/library/j-jsf2fu-0710/?ca=drs- Yes, this is something I want, but how to achieve that without having application server? – Fekete Kamosh Jan 02 '11 at 17:16
  • CDI is supported outside Java EE as well. It's just a little more trouble to setup and configure. You need to download Weld for this. See among others this question: http://stackoverflow.com/questions/2930889/are-managedbeans-obsolete-in-javaee6-because-of-named-in-cdi-weld – Arjan Tijms Jan 02 '11 at 17:21
  • I am still suprised how difficult is to pass objects through lifecycles, but maybe it is web's destiny:-) OK I will try Conversation scope from CDI. But still - if someone knows how to solve example using standard Mojarra implementation lets show it please. – Fekete Kamosh Jan 02 '11 at 17:55
  • It's actually passing objects between requests ;) The life-cycle is typically something the describe what happens during a single request. But yes, this has been a pain like forever. It's of course due to the stateless nature of HTTP and the inability to store cookies per tab/window. This is difficult in any web platform, be it Java in general, PHP, RoR etc. The conversation scope is one of the first things to make this a little easier. – Arjan Tijms Jan 02 '11 at 19:04