Jsf 2.0 Populate h:selectOneMenu to access page - java

I am newbie in JSF 2.0, I worked in JSF 1.1 and 1.2 and I populate selectOneMenu in constructor of Managed bean's page. For when users to acces to page the List is populate. example below. I put the same in JSF 2.0 but is not work, the selectOneMenu appears empty.
<h:selectOneMenu id="cboStatus" value="#{PersonBean.idStatus}">
<f:selectItems value="#{PersonBean.status}"/>
</h:selectOneMenu>
In constructor's managed bean I put:
public class PersonBean {
private SelectItem[] status=new SelectItem[0];
public PersonBean () {
detallePersonas= new ArrayList();
status= new SelectItem[3];
status[0]=new SelectItem("S","Single");
status[1]=new SelectItem("M","Married");
status[2]=new SelectItem("D","Divorced");
}
}
Editor Netbeans 6.8 (JSF 2.0 default configuration wizard)
No Exception Error
Never run the constructor PersonBean (I put a breakpoint and never stops)
There are other ways to populate selects to load the page
This is the complete code:
index.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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Person</title>
</h:head>
<h:body>
<h:form id="frmPerson">
<h:outputLabel id="lblStatus" value="Status:"/>
<h:selectOneMenu id="cboStatus" value="#{PersonBean.idStatus}">
<f:selectItems value="#{PersonBean.status}"/>
</h:selectOneMenu>
</h:form>
</h:body>
</html>
PersonBean.java
package com.prueba.backingbean;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.model.SelectItem;
/**
*
* #author Administrador
*/
#ManagedBean(name = "Person")
#ViewScoped
public class PersonBean {
private String idStatus;
private SelectItem[] status = new SelectItem[0];
public PersonBean() {
status = new SelectItem[3];
status[0] = new SelectItem("S", "Single");
status[1] = new SelectItem("M", "Married");
status[2] = new SelectItem("D", "Divorced");
}
/**
* #return the idStatus
*/
public String getIdStatus() {
return idStatus;
}
/**
* #param idStatus the idStatus to set
*/
public void setIdStatus(String idStatus) {
this.idStatus = idStatus;
}
/**
* #return the status
*/
public SelectItem[] getStatus() {
return status;
}
/**
* #param status the status to set
*/
public void setStatus(SelectItem[] status) {
this.status = status;
}
}

private Map<String,String>status = new HashMap<String,String>();
.......
status.put("S", "Single");
status.put("M", "Married");
status.put("D", "Divorced");
in JSF PAGE:
<h:selectOneMenu value="#{personBean.idStatus}" >
<f:selectItems value="#{personBean.status}"/>
</h:selectOneMenu>

It should be "personBean" instead of "PersonBean" (first letter should be lowercase). You also need getter for status (getStatus()) and setter/getter for idStatus (setIdStatus()/getIdStatus()). Are they there?

Look here:
#ManagedBean(name = "Person")
#ViewScoped
public class PersonBean {
You've declared the managed bean name as Person. So it's in JSF EL available as #{Person}. Yet you're attempting to access it as #{PersonBean}. Because such a bean does not exist, the menu remains empty.
You've 3 options:
Rename #{PersonBean} by #{Person} in your JSF page.
Rename #ManagedBean(name = "Person") to #ManagedBean(name = "PersonBean") in your managed bean.
Get rid of bean name and use the default JSF naming conventions. I.e. just use #ManagedBean without a name and use #{personBean} in your JSF page (in essence, the bean's class name with first character lowercased).
Option 3 is preferred.

Have you tried using List instead of Array of SelectItems.
Below code might help you.
private List<SelectItem> status = new ArrayList<SelectItem>();
status.add(new SelectItem("S","Single"));
status.add(new SelectItem("M","Married"));
status.add(new SelectItem("D","Divorced"));
/*
SETTER / GETTER
*/

Related

JSF AJAX - Class doesn't have property

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:

JSF how to store, with Controller, in session without persist in DB

I'm trying to make a page where the user can insert one or more Providers, check all the Providers inserted, then confirm. ONLY when the user confirms, all the Providers inserted will be stored in my Database.
How can I do that? It seems that if I use my ManagedBean Controller, with List where I will store the Providers, I get a strange error
HTTP Status 500 -
root cause
java.lang.NullPointerException
it.myProject.model.Provider.equals(Provider.java:71)
org.apache.myfaces.shared.util.SelectItemsIterator.next(SelectItemsIterator.java:275)
org.apache.myfaces.shared.util.SelectItemsIterator.next(SelectItemsIterator.java:49)
org.apache.myfaces.shared.renderkit.RendererUtils.internalGetSelectItemList(RendererUtils.java:800)
org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(RendererUtils.java:764)
org.apache.myfaces.shared.renderkit.html.HtmlSelectableRendererBase.internalRenderSelect(HtmlSelectableRendererBase.java:74)
org.apache.myfaces.shared.renderkit.html.HtmlMenuRendererBase.renderMenu(HtmlMenuRendererBase.java:91)
org.apache.myfaces.shared.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:76)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:665)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:545)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:541)
javax.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:541)
org.apache.myfaces.shared.view.JspViewDeclarationLanguageBase.actuallyRenderView(JspViewDeclarationLanguageBase.java:364)
org.apache.myfaces.shared.view.JspViewDeclarationLanguageBase.renderView(JspViewDeclarationLanguageBase.java:201)
org.apache.myfaces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:285)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:59)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:59)
org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:116)
org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:199)
here's the code of my ProviderController
#ManagedBean
public class ProviderController{
private Long id;
private String vat;
private String address;
private List<Product> products;
private Provider provider;
private List<Provider> providers;
public ProviderController() {
this.products= new LinkedList<>();
this.providers= new LinkedList<>();
}
//adds the Provider in the c:forEach of the view newProvider.jsp
public String addProvider() {
this.provider= new Provider(vat, address, products);
this.providers.add(this.provider);
return "newProvider";
}
-I omitted the facade because I have nothing to store in this case, I just want to add the provider in the list and THEN, with another method, add all the providers of the list in my db
-Provider is a Model that uses the annotation #Entity, so my doubt is that I can't use Provider if I don't use the EntityManager
If I'm right, what can I do to store simply in session my providers? I have to create another class Provider? I have to create another ProviderController?
Thank you very much for the answers.
Keeping apart from your hypothetical persistence layer, you can store the information in the session. However, you have to differ between the information that belongs to the session and the one that does to the current view.
For your case, I think you want to have the stored providers as a session variable, while the information you send in the form when adding a new one should be in view scope. That way you divide the logic of the application in a proper way. Here you've got more information about the view scope, introduced in JSF 2.
As you're already in JSF 2, you could choose between JSP and Facelets for your view technology. If it's a new project, I encourage you to go with facelets, as it is the standard for JSF 2:
ProviderController.java
/*
* Manages providers in the session
*/
#ManagedBean
#SessionScoped
public class ProviderController{
private List<Provider> providers;
private List<Product> products;
public ProviderController() {
this.products= new LinkedList<>();
this.providers= new LinkedList<>();
}
public List<Provider> getProviders(){
return providers;
}
}
providers.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jsp/jstl/core">
<h:head />
<h:body>
<!-- Display all the providers from the session -->
<ul>
<ui:repeat value="#{providerController.providers}" var="provider">
<li>#{provider.vat}-#{provider.address}</li>
</ui:repeat>
</ul>
</h:body>
</html>
ProviderAdd.java
/*
* Manages the view to add a new provider
*/
#ManagedBean
#ViewScoped
public class ProviderAdd{
private Long id;
private String vat;
private String address;
//Injects the session scoped bean
#ManagedProperty(value="#{providerController}")
private ProviderController providerController;
//Getter and setters
//Adds a new provider to the session and returns to the provider list
public String addProvider() {
this.provider= new Provider(this.vat, this.address, this.products);
providerController.getProviders.add(this.provider);
return "providers";
}
}
addProvider.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jsp/jstl/core">
<h:head />
<h:body>
<h2>VAT:</h2><h:inputText value="#{providerAdd.vat}" />
<h2>Address:</h2><h:inputText value="#{providerAdd.address}" />
<h:commandButton action="#{providerAdd.addProvider}" value="Add provider" />
</h:body>
</html>

CommandButton lose action when I use the PickList

I´m with a strange problem. I´m using the picklist component but it seems like when I use the picklist my commandbutton stop working: Here´s the code:
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:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>PickList Test</title>
</h:head>
<h:body>
<h:form id="form">
<p:pickList value="#{pickListBean.employeeList}" var="employee" itemLabel="#{employee.employeeName}" itemValue="#{employee.employeeCode}" />
<p:commandButton value="Save" action="#{pickListBean.message}" style="margin-left: 12px;"/>
</h:form>
</h:body>
</html>
bean
#ManagedBean
#RequestScoped
public class PickListBean {
#EJB
private BussinessList bl = new BussinessList();
private DualListModel<Employee> employeeList;
private Employee employee;
/**
* Creates a new instance of PickListBean
*/
public PickListBean() {
List<Employee> source = new ArrayList<Employee>();
List<Employee> target = new ArrayList<Employee>();
source = bl.getEmployee();
employeeList = new DualListModel<Employee>(source, target);
}
public void message(){
System.out.println("CommandButton is working");
}
public DualListModel<Employee> getEmployeeList() {
return employeeList;
}
public void setEmployeeList(DualListModel<Employee> employeeList) {
this.employeeList = employeeList;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee= employee;
}
}
When I click in the commandbutton the message method is not called, but when I remove the picklist from my xhtml the commandbutton call the message method.
I´m using jsf 2.2, primefaces 4.0...
Lots of errors on this code
Before starting with the errors, I can see 2 seriously poor naming choices in the method named getEmployeePickList
It's not a PickList in the first place. its a DataList
the difference from EmployeeList is that it keeps instances of Employee instead of Functionario. The names you chose, give the impression that one of them is a PickList, while the other is an ArrayList, and they keep instances of the same Class
Now, your xhtml might missing something in the declarations. I tried your code, and even when i deleted the pickList, your commandbutton would not fire. Try starting with this (drop the xml declaration)
<!DOCTYPE html PUBLIC "-W3CDTD XHTML 1.0 TransitionalEN" "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"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
Also, the Employee Class is not a String, so your pickList will not work without a converter. See this for example
Finally, your PickList will search for a setEmployeePickList() method in your Class. I tried using your pickList working with String instead of Employee, to get past the converter issue, and naturally, I got this error:
javax.el.PropertyNotFoundException: test.xhtml #21,140 value="#{pickListBean.employeePickList}": Property 'employeePickList' not writable on type org.primefaces.model.DualListModel
The solution for this, would be , feeding the pickList with #{pickListBean.employeeList} instead of #{pickListBean.employeePickList}. You can populate your Employee List somewhere else, like in the PickListBean constructor, instead of re-creating in getEmployeePickList.

JSF2 - use view scope managed bean to pass value between navigation

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.

JBoss Seam - Can't #In annotations be used at the same time with accessor methods for different properties?

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.

Categories

Resources