I think that I found a error in runtime of Java with JSF 2.0 (Using Primefaces), in this project I'm using JSF 2.0 Primefaces and CDI.
Resuming the problem, I have a method setter in my business class Role that received a List, but the JSF is setting a ArrayList on that. Should the java throw a exception or at least should not find a method that matches? here is:
public void setAcl(List<Integer> acl) {
this.acl = acl;
System.out.println("Received: " + this.acl);
for(Object i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
}
The output of this method is:
Received: [1, 5] class java.lang.String > 1 class java.lang.String > 5
And when I try to use in foreach like this:
for(Integer i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
Throws
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Can you explain what is going on here? Is it a error on JSF or Java? Or is my misunderstood?
First I have a UI JSF (.xhtml) that has a p:selectManyCheckbox
, this manyCheckbox receive a array of enum PontoSenha (using the enum's method values() ) from managedbean and define the values to itemValue and itemLabel.
Follow the code of manyCheckbox:
<p:selectManyCheckbox id="acl" value="#{roleController.role.acl}" layout="grid" columns="4" required="true" requiredMessage="Selecione pelo menos um ponto de senha" >
<f:selectItems value="#{roleController.pontosSenha}" var="pontoSenha" itemValue="#{pontoSenha.pontoSenha}" itemLabel="#{pontoSenha.descricao}" />
</p:selectManyCheckbox>
Declaration of enum PontoSenha:
package br.com.bsetechnology.atacadao.business;
public enum PontoSenha {
CADASTRO_FORNECEDOR(1, "Cadastrar fornecedor"),
CADASTRO_LOJA(2, "Cadastrar loja"),
CADASTRO_PRODUTO(3, "Cadastrar produto"),
RELATORIO(4, "Gerar relatório"),
SEGURANCA_GRUPOS(5, "Gerenciar grupos de usuário"),
SEGURANCA_USUARIOS(6, "Gerenciar usuários");
private int pontoSenha;
private String descricao;
private PontoSenha(int pontoSenha, String descricao) {
this.pontoSenha = pontoSenha;
this.descricao = descricao;
}
public int getPontoSenha() {
return pontoSenha;
}
public String getDescricao() {
return descricao;
}
@Override
public String toString() {
return String.format("%02d - %s", pontoSenha, descricao);
}
}
Declaration of the ManagedBean
package br.com.bsetechnology.atacadao.controller;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.inject.Inject;
import javax.inject.Named;
import br.com.bsetechnology.atacadao.business.PontoSenha;
import br.com.bsetechnology.atacadao.business.Role;
import br.com.bsetechnology.atacadao.core.FacesUtil;
import br.com.bsetechnology.atacadao.dao.RoleDAO;
@Named("roleController")
@RequestScoped
public class RoleController {
@Inject
private RoleDAO roleDao;
private Role role;
public void setRoleDao(RoleDAO roleDao) {
this.roleDao = roleDao;
}
public Role getRole() {
if(role == null)
role = new Role();
return role;
}
public void setRole(Role role) {
this.role = role;
}
public PontoSenha[] getPontosSenha() {
return PontoSenha.values();
}
public List<Role> getAll() {
return roleDao.getAll();
}
public void salva() {
System.out.println("Salvando");
boolean resultAction = false;
if(role.getCodigo() > 0) {
resultAction = roleDao.update(role);
} else {
resultAction = roleDao.insert(role);
}
if(resultAction) {
role = new Role();
FacesUtil.addMessage(FacesMessage.SEVERITY_INFO, "Grupo salvo com sucesso.", null);
} else {
FacesUtil.addMessage(FacesMessage.SEVERITY_WARN, "Grupo não foi salvo.", null);
}
}
}
Business class Role
package br.com.bsetechnology.atacadao.business;
import java.util.ArrayList;
import java.util.List;
public class Role {
private int codigo;
private String descricao;
private List<Integer> acl;
public Role() {
acl = new ArrayList<Integer>();
}
public Role(int codigo, String descricao, List<Integer> acl) {
setCodigo(codigo);
setDescricao(descricao);
setAcl(acl);
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public List<Integer> getAcl() {
return acl;
}
public void setAcl(List<Integer> acl) {
this.acl = acl;
System.out.println("Received: " + this.acl);
for(Object i : this.acl) {
System.out.println(i.getClass() + " > " + i);
}
}
public void addPontoSenha(int pontoSenha) {
this.acl.add(pontoSenha);
}
public void remPontoSenha(int pontoSenha) {
this.acl.remove(pontoSenha);
}
}
The page JSF that I use to register roles (template.xhtml is only HTML and CSS):
<ui:composition template="/WEB-INF/template.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
<ui:define name="body-content">
<div class="row">
<h:form id="formGrupo" class="form-horizontal"><fieldset>
<p:panel header="Edição de grupo de usuário" >
<div class="control-group">
<h:outputLabel for="codigo" value="Código" readonly="" styleClass="control-label" />
<div class="controls">
<h:inputText id="codigo" class="form-control" value="#{roleController.role.codigo}"
style="width:100px;text-align:right;" />
</div>
</div>
<div class="control-group">
<h:outputLabel for="descricao" value="Descrição" styleClass="control-label" />
<div class="controls">
<h:inputText id="descricao" class="form-control" value="#{roleController.role.descricao}"
required="true" maxlength="20" requiredMessage="Descrição obrigatória" converterMessage="Descrição inválida" />
<h:message for="descricao" class="error" />
</div>
</div>
<div class="control-group">
<h:outputLabel value="Pontos de senha" styleClass="control-label" />
<div class="controls checkbox">
<p:selectManyCheckbox id="acl" value="#{roleController.role.acl}" layout="grid" columns="4"
required="true" requiredMessage="Selecione pelo menos um ponto de senha" >
<f:selectItems value="#{roleController.pontosSenha}" var="pontoSenha" itemValue="#{pontoSenha.pontoSenha}" itemLabel="#{pontoSenha.descricao}" />
</p:selectManyCheckbox>
<h:message for="acl" errorClass="error" />
</div>
</div>
<div class="form-actions">
<hr/>
<p:commandLink value="Salvar" styleClass="btn btn-primary" style="color:#fff;" action="#{roleController.salva}" update=":formGrupo,:formTable:tblRoles">
</p:commandLink>
</div>
<p:messages globalOnly="true" showDetail="false" closable="true" />
</p:panel>
</fieldset></h:form>
</div>
<br/>
<div class="row">
<h:form id="formTable" styleClass="form-horizontal"><fieldset>
<p:panel header="Grupos de usuários">
<p:dataTable id="tblRoles" var="role" value="#{roleController.all}" rowKey="#{role.codigo}" stickheader="true" >
<p:column headerText="Código" width="20">
<h:outputText value="#{role.codigo}" />
</p:column>
<p:column headerText="Descrição">
<h:outputText value="#{role.descricao}" />
</p:column>
<p:column width="200">
<p:commandLink value="Editar" class="btn btn-success btn-sm" style="color:#fff;margin-left:5px;" update=":formGrupo">
<f:setPropertyActionListener value="#{role}" target="#{roleController.role}" />
</p:commandLink>
<p:commandLink value="Excluir" styleClass="btn btn-danger btn-sm" style="color:#fff;margin-left:5px;" />
</p:column>
</p:dataTable>
</p:panel>
</fieldset></h:form>
</div>
</ui:define>
</ui:composition>
Full print stack trace
Jan 29, 2014 10:59:43 PM com.sun.faces.context.AjaxExceptionHandlerImpl handlePartialResponseError
SEVERE: javax.faces.component.UpdateModelException: javax.el.ELException: /seguranca/grupos.xhtml @27,87 value="#{roleController.role.acl}": Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at javax.faces.component.UIInput.updateModel(UIInput.java:867)
at javax.faces.component.UIInput.processUpdates(UIInput.java:749)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at org.primefaces.component.panel.Panel.processUpdates(Panel.java:288)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1254)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at br.com.bsetechnology.atacadao.core.AccessControlFilter.doFilter(AccessControlFilter.java:31)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.el.ELException: /seguranca/grupos.xhtml @27,87 value="#{roleController.role.acl}": Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:139)
at javax.faces.component.UIInput.updateModel(UIInput.java:832)
... 30 more
Caused by: javax.el.ELException: Error writing 'acl' on type br.com.bsetechnology.atacadao.business.Role
at javax.el.BeanELResolver.setValue(BeanELResolver.java:153)
at com.sun.faces.el.DemuxCompositeELResolver._setValue(DemuxCompositeELResolver.java:255)
at com.sun.faces.el.DemuxCompositeELResolver.setValue(DemuxCompositeELResolver.java:281)
at org.apache.el.parser.AstValue.setValue(AstValue.java:218)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:253)
at org.jboss.weld.el.WeldValueExpression.setValue(WeldValueExpression.java:64)
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:131)
... 31 more
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at br.com.bsetechnology.atacadao.business.Role.setAcl(Role.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at javax.el.BeanELResolver.setValue(BeanELResolver.java:142)
... 37 more
This basic method to convert a list of strings to a list of integers uses three steps: Create an empty list with ints = [] . Iterate over each string element using a for loop such as for element in list . Convert the string to an integer using int(element) and append it to the new integer list using the list.
To print a list of integers similar to how you printed the strings in the previous example: Use the map() function to transform them into strings. Call join() method to merge them into one string. Print the resulting string out using the print() function.
Pass the List<String> as a parameter to the constructor of a new ArrayList<Object> . List<Object> objectList = new ArrayList<Object>(stringList); Any Collection can be passed as an argument to the constructor as long as its type extends the type of the ArrayList , as String extends Object .
This is caused by a combination of several technical limitations and facts.
In Java, generics are compile time syntactic sugar. Ultimately, when a Java class is compiled, all generic type information is lost. So, during runtime there's no means of any generic type information in the List
instance being passed around.
The expression language (EL, #{}
) runs during runtime using Java Reflection API and sees in your particular case only a List
, not a List<Integer>
.
The generated HTML output and the obtained HTTP request parameters are in Java perspective basically String
s.
As long as you don't explicitly specify a JSF Converter
between String
to the desired type, JSF will let EL (read: Reflection API) add the unconverted submitted String
value to the List
.
In order to get the submitted value to end up as Integer
in the model, you have 3 options:
Explicitly specify a converter for String
to Integer
. Fortunately, JSF has a built-in one, the IntegerConverter
which has the converter ID javax.faces.Integer
. So all you need to do is specifying it in the input component's converter
attribute.
<p:selectManyCheckbox ... converter="javax.faces.Integer">
Use Integer[]
instead of List<Integer>
as model property.
private Integer[] acl;
This way the desired type is visible to EL (read: Reflection API) and it'll perform automatic conversion using the built-in converter.
Upgrade to at least JSF 2.3. As per spec issue 1422 the UISelectMany
components will have automatic conversion when a Collection
is used, using the same basic principle as OmniFaces SelectItemsConverter
.
See also UISelectMany
javadoc.
Obtain the
Converter
using the following algorithm:
If the component has an attached
Converter
, use it.If not, look for a
ValueExpression
for value (if any). TheValueExpression
must point to something that is:
An array of primitives (such as
int[]
). Look up the registered by-classConverter
for this primitive type.An array of objects (such as
Integer[]
orString[]
). Look up the registered by-classConverter
for the underlying element type.A
java.util.Collection
. Do not convert the values. Instead, convert the provided set of available options to string, exactly as done during render response, and for any match with the submitted values, add the available option as object to the collection.If for any reason a
Converter
cannot be found, assume the type to be a String array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With