I'm trying to learn the use of #Produces methods in CDI. I have made really simple web-app to test it out. What I'm trying to do is basically is upon submission of the form pass the value of one bean (Controller) to the other (Cont).
The problem is though that the str value never gets "injected". Obviously there are other ways to do this (inject the entire controller) but I'm trying to learn this specific way.
.xhtml
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
</h:head>
<h:body>
<h:form>
<h:outputText value="Input: " />
<h:inputText value="#{str}" />
<br />
<br />
<h:outputText value="#{cont.str}" />
<br />
<br />
<h:commandButton value="Submit">
</h:commandButton>
</h:form>
</h:body>
Controller.java
#Named
#SessionScoped
public class Controller implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Produces
#Named("str")
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
Cont.java
#Named
#SessionScoped
public class Cont implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Inject
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
Change your h:inputText to use #{cont.str}
The use of a producer here is awkward. #Named is always evaluated, it's best to use a wrapper object to handle it.
Related
I have been trying to run AJAX example in JSF. But I am getting "class does not have the property login". But in all the examples in various websites, code is same the same.
My index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF AJAX Calls</title>
</h:head>
<h:body>
<h2>AJAX Example</h2>
<h:form>
<h:inputText id="inputName" value="#{userData.name}"></h:inputText>
<h:commandButton value="Login">
<f:ajax execute="inputName" render="outputMsg" />
</h:commandButton>
<br />
<hr />
<h2><h:outputText id="outputMsg" value="#{userData.login}" /></h2>
</h:form>
</h:body>
</html>
UserData.java
package com.cyb3rh4wk.test;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean(name = "userData", eager = true)
#SessionScoped
public class UserData implements Serializable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String login() {
if ("".equals(name) || name == null)
return "";
else
return "Logged in as " + name;
}
}
This is my error,
/index.xhtml value="#{userData.login}": The class 'com.cyb3rh4wk.test.UserData' does not have the property 'login'.
How do I resolve this error ?
Value and Method Expressions
The EL defines two kinds of expressions: value expressions and method expressions. Value expressions can either yield a value or set a value. Method expressions reference methods that can be invoked and can return a value.
Example of a value expression according to the above definition in your code is:
userData.name
In the following tag definition:
<h:outputText id="outputMsg" value="#{userData.login}" />
you are not using a value expression but rather a method expression because login is not a simple JavaBean getter returning the value of a bean property.
So you have to change the above line as:
<h:outputText id="outputMsg" value="Logged in as : #{userData.name}" />
And remove your login or use it for navigation purpose (that is why it is there).
Here is the reason (taken from JSF 2.0 specification) why you have to pass a value expression to an output component instead of a method expression:
4.1.10 UIOutput
UIOutput (extends UIComponentBase; implements ValueHolder) is a component that has a value, optionally retrieved from a model tier bean via a value expression (see Section 5.1 “Value Expressions”), that is displayed to the
user. The user cannot directly modify the rendered value; it is for display purposes only:
Here's my FacesComponent class:
#FacesComponent("ExibicaoChecklistComponent")
public class ExibicaoChecklistComponent extends UINamingContainer {
private ListaChecklistWrapper checklist;
private String altura;
public ListaChecklistWrapper getChecklist() {
return checklist;
}
public void setChecklist(ListaChecklistWrapper checklist) {
this.checklist = checklist;
}
public String getAltura() {
return altura;
}
public void setAltura(String altura) {
this.altura = altura;
}
}
and the xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:pretty="http://ocpsoft.com/prettyfaces"
xmlns:ui="http://java.sun.com/jsf/facelets">
<cc:interface componentType="ExibicaoChecklistComponent">
<cc:attribute name="altura" default="300px" type="java.lang.String" />
<cc:attribute name="checklist" required="true" />
</cc:interface>
<cc:implementation>
Altura: #{cc.altura}
Checklist: #{cc.checklist.nome}
</cc:implementation>
and finally the usage:
<checando:exibicaoChecklist altura="200px" checklist="#{CheckBean.checklists[0]}" />
The setAltura method is called with the 200px value, but the setChecklist(ListaChecklistWrapper checklist) is not called and the checklist attribute is always null inside the component.
If I do #{CheckBean.checklists[0].nome} outside the <checando:exibi... tag it works. So, the object is not null... it's only a missing call to the set method.
Is there anything I'm missing?
Mojarra 2.1.13 (20120907-1514) and java version "1.7.0_25".
Thanks.
I guess, giving the nome directly as a parameter into the component does work again? (Something like setChecklistNome(String nome).
If so, there might be a challenge with giving direct parameters different from java.lang.String. Have you tried to give the parameters as cc.attrs.checklist instead from writing it directly into the UINamingContainer?
I am having problems with a pure JSF 2 page. When loading the page two inputText and one select is shown. There's a validation field to the right of each. The two inputs are required fields, while the select always has a selection, so it doesn't validate at all (at least it shouldn't).
Here's a screenshot:
This is what is displayed when the Create button was clicked. When re-hitting the Create button the following popup appears:
OK, nothing without code, here's the XHTML:
<?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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="3" id="base-data-grid">
<h:outputLabel value="Name:*" for="name-input" />
<h:inputText value="#{testBean.name}"
requiredMessage="Name required!"
id="name-input">
<f:validateRequired />
</h:inputText>
<h:message for="name-input" style="color: red;" />
<h:outputLabel value="Code:*" for="code-input" />
<h:inputText value="#{testBean.code}"
requiredMessage="Code required!"
id="code-input">
<f:validateRequired />
</h:inputText>
<h:message for="code-input" style="color: red;" />
<h:outputLabel value="Location:" for="location-select" />
<h:selectOneMenu value="#{testBean.location}"
converter="#{testConverter}"
id="location-select">
<f:selectItems value="#{testBean.locations}"
var="l"
itemValue="#{l}"
itemLabel="#{l.name}" />
</h:selectOneMenu>
<h:message for="location-select" style="color: red;" />
</h:panelGrid>
<h:panelGrid columns="1">
<h:commandButton value="Create"
action="#{testBean.create}">
<f:ajax execute="base-data-grid" render="base-data-grid lalala" />
</h:commandButton>
</h:panelGrid>
<h:messages id="lalala" />
</h:form>
</h:body>
</html>
The location class for the select (just a name):
public class Location
{
private String name;
public Location(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
The converter (as CDI version):
import javax.enterprise.context.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.inject.Inject;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Named
#RequestScoped
public class TestConverter implements Converter
{
private static final Logger log = LoggerFactory.getLogger(TestConverter.class);
#Inject
private TestBean testBean;
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String name)
{
log.info(getClass().getSimpleName() + ".getAsObject: " + name);
return testBean.getLocationFor(name);
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj)
{
log.info(getClass().getSimpleName() + ".getAsString: " + obj);
return ((Location)obj).getName();
}
}
And the test bean:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Named
#SessionScoped
public class TestBean implements Serializable
{
private static final Logger log = LoggerFactory.getLogger(TestBean.class);
private String name;
private String code;
private Location location;
private List<Location> locations;
#PostConstruct
public void init()
{
locations = new ArrayList<Location>();
locations.add(new Location("Berlin"));
locations.add(new Location("London"));
locations.add(new Location("New York"));
locations.add(new Location("Moscow"));
locations.add(new Location("Bejing"));
location = locations.get(2);
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getCode()
{
return code;
}
public void setCode(String code)
{
this.code = code;
}
public Location getLocation()
{
return location;
}
public void setLocation(Location location)
{
this.location = location;
}
public List<Location> getLocations()
{
return locations;
}
public void setLocations(List<Location> locations)
{
this.locations = locations;
}
public void create()
{
log.info("Creating new whatever...");
}
// for converter to mimic DB query
public Location getLocationFor(String name)
{
for ( Location location : locations )
{
if ( location.getName().equals(name) )
{
return location;
}
}
return null;
}
}
As you can see, there's not much the page is supposed to do. Validate the two input fields, display the select and call testBean.create if all is validated fine.
However, as soon as the validation has run once, each subsequent Create button click will cause that serverError.
NOTE, that if you remove the f:selectItems or the h:selectOneMenu entirely, the page will work as expected. This is what makes the whole thing so strange actually...
I have no idea what is going on here. Does anybody know what's wrong?
I have attached a JBoss AS 7 test app here: https://community.jboss.org/thread/202501 (dupe post, sorry need a lot of help).
Please have a look at this extremely weird IllegalStateException. There's not even something in the JBAS server.log...
PS: the Mojarra version is 2.1.7 of course (the one that comes with JBoss AS 7.1.1.Final)
Your problem is that the Location needs to be serializable
(implement the java.io.Serializable interface)
the f:validateRequired tag causes the required-validation to be performed by ajax.
During the RestoreView-phase of this ajax-request, the runtime encounters an InstantiationException due to the Location not being Serializable.
I do honestly not know why the error including stacktrace is not logged.
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;
}
}
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.
I generated a new form using sean-gen (seam new-form) and added another field to it using an #In annotation:
#Stateful
#Name("dummy")
public class DummyBean implements Dummy
{
#Logger private Log log;
#In StatusMessages statusMessages;
#In private String bar;
private String foo;
public void doStuff()
{
String msg = "dummy.doStuff() action called with foo: #{dummy.foo} and bar: #{bar}. instance variable for bar:" + bar;
log.info(msg);
statusMessages.add(msg);
}
#Length(max = 10)
public String getFoo()
{
return foo;
}
public void setFoo(String value)
{
this.foo = value;
}
#Remove
public void destroy() {}
}
The interface is this one:
#Local
public interface Dummy
{
public void doStuff();
public String getFoo();
public void setFoo(String value);
public void destroy();
}
The problem is, when I try to access the properties I get:
javax.faces.FacesException: javax.el.ELException: /dummy.xhtml #22,52 value="#{dummy.foo}": Error reading 'foo' on type org.javassist.tmp.java.lang.Object_$$_javassist_seam_2
at javax.faces.component.UIOutput.getValue(UIOutput.java:187)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:201)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:284)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:154)
.
.
.
Caused by: javax.ejb.EJBTransactionRolledbackException: #In attribute requires non-null value: dummy.bar
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleInCallerTx(Ejb3TxPolicy.java:115)
at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:130)
My view is this:
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a="http://richfaces.org/a4j"
template="layout/template.xhtml">
<ui:define name="body">
<h:form id="dummyForm">
<rich:panel>
<f:facet name="header">dummy header</f:facet>
<s:decorate id="fooField" template="layout/edit.xhtml">
<ui:define name="label">Foo</ui:define>
<h:inputText id="foo" required="true"
value="#{dummy.foo}"/>
</s:decorate>
<s:decorate id="barField" template="layout/edit.xhtml">
<ui:define name="label">Bar</ui:define>
<h:inputText id="bar" required="true"
value="#{bar}"/>
</s:decorate>
<div style="clear:both"/>
</rich:panel>
<div class="actionButtons">
<h:commandButton id="doStuff" value="doStuff"
action="#{dummy.doStuff}"/>
</div>
</h:form>
</ui:define>
</ui:composition>
If I remove either 'fooField' or 'barField' the view renders and works correctly, but if I try to use both at the same time I get the above exception.
I also noticed that if I use only #In annotations (remove the accessor methods from the bean) the page works.
Is this something expected and I should be doing my homework before asking?
I'm using JBoss 5.1.0.GA, Seam 2.2.0.GA and Java 6.
Turns out this is expected behavior, quoting an answer from the seam forum:
Use #In(required=false) if the Seam component you're injecting does not have the #AutoCreate annotation at the class level.
So, changing:
#In private String bar;
to:
#In (required=false) private String bar;
fixes the issue.