问题
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
回答1:
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 aList
, not aList<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 submittedString
value to theList
.
In order to get the submitted value to end up as Integer
in the model, you have 2 options:
Explicitly specify a converter for
String
toInteger
. 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'sconverter
attribute.<p:selectManyCheckbox ... converter="javax.faces.Integer">
Use
Integer[]
instead ofList<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 aCollection
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.
来源:https://stackoverflow.com/questions/21446068/listinteger-received-liststring