名单 收到清单

问题描述 投票:4回答:1

我认为我在使用JSF 2.0(使用Primefaces)的Java运行时发现了一个错误,在这个项目中我使用的是JSF 2.0 Primefaces和CDI。

恢复问题,我的业务类Role中有一个方法设置器,它接收一个List,但JSF正在设置一个ArrayList。 java抛出异常还是至少找不到匹配的方法?这是:

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);
    }
}

此方法的输出是:

Received: [1, 5]
class java.lang.String > 1
class java.lang.String > 5

当我尝试在这样的foreach中使用时:

for(Integer i : this.acl) {
    System.out.println(i.getClass() + " > " + i);
}

抛出

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

你能解释一下这里发生了什么吗?这是JSF还是Java的错误?或者是我被误解了?

首先我有一个带有p:selectManyCheckbox的UI JSF(.xhtml),这个manyCheckbox从managedbean接收一个枚举PontoSenha数组(使用enum的方法值())并将值定义为itemValue和itemLabel。按照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>

枚举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);
    }
}

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);
        }
    }
}

商务舱角色

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);
    }
}

我用来注册角色的页面JSF(template.xhtml只是HTML和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>

完整的打印堆栈跟踪

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
jsf-2 primefaces cdi
1个回答
13
投票

这是由几个技术限制和事实的组合引起的。

  • 在Java中,泛型是编译时的语法糖。最终,当编译Java类时,所有泛型类型信息都将丢失。因此,在运行时期间,传递的List实例中没有任何泛型类型信息。
  • 表达式语言(EL,#{})在运行时使用Java Reflection API运行,在您的特定情况下只能看到List,而不是List<Integer>
  • 生成的HTML输出和获得的HTTP请求参数在Java透视图中基本上是Strings。
  • 只要您没有在Converter之间明确指定JSF String到所需类型,JSF就会让EL(读取:Reflection API)将未转换的String值添加到List

为了使提交的值最终成为模型中的Integer,您有两个选择:

  1. 明确指定StringInteger的转换器。幸运的是,JSF有一个内置的,IntegerConverter,它有转换器ID javax.faces.Integer。所以你需要做的就是在输入组件的converter属性中指定它。 <p:selectManyCheckbox ... converter="javax.faces.Integer">
  2. 使用Integer[]而不是List<Integer>作为模型属性。 private Integer[] acl; 这样,EL可以看到所需的类型(读取:Reflection API),它将使用内置转换器执行自动转换。
  3. 升级到至少JSF 2.3。根据spec issue 1422,当使用UISelectMany时,Collection组件将使用与OmniFaces SelectItemsConverter相同的基本原理进行自动转换。

另见UISelectMany javadoc

使用以下算法获取Converter

  • 如果组件附有Converter,请使用它。
  • 如果没有,请查找ValueExpression的值(如果有的话)。 ValueExpression必须指向以下内容: 一组基元(如int[])。查找已注册的类Converter以获取此原始类型。 一组对象(例如Integer[]String[])。查找注册的类Converter以获取基础元素类型。 一个java.util.Collection。不要转换值。相反,将提供的可用选项集转换为字符串,与呈现响应期间完全相同,并且对于与提交的值的任何匹配,将可用选项作为对象添加到集合中。

如果由于任何原因无法找到Converter,则假定该类型为String数组。

© www.soinside.com 2019 - 2024. All rights reserved.