f:double上的convertNumber:ClassCastException

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

在JSF 2.3中,我有一个h:inputText来编辑Double值,它还有Bean-Validation约束。 h:inputText有一个f:convertNumber。提交表单时,这会导致ClassCastException(见下文)。

因此,似乎f:convertNumber产生了Long然后,无法转换为Double来验证@DecimalMin约束,对吧?

在JSF 2.2中,这按预期工作,升级到JSF 2.3后出现了问题。

有没有人有任何想法可能是什么问题?

我可以在WildFly 15.0.1中使用以下最小示例重现这一点,只有一个facelet和一个lombok作为依赖:

@Data
public class Building {

    @DecimalMin("0.0")
    private Double area;
}

@Named
@ViewScoped
public class BuildingEditBean implements Serializable {

    @Getter
    @Setter
    private Building building;

    @PostConstruct
    public void init() {
        this.building = new Building();
    }

    public void submit() {
        System.out.println("Building: " + this.building.getArea());
    }
}

的facelet:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <div class="form-group">
                <label>Area</label>
                <h:inputText id="area" value="#{buildingEditBean.building.area}">
                    <f:convertNumber />
                </h:inputText>
                <h:messages id="areaMessage" for="area" />
            </div>
            <h:commandButton value="Save" action="#{buildingEditBean.submit()}" />
        </h:form>
    </h:body>
</html>

表单提交后的Stacktrace具有有效值:

10:57:07,649 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (default task-1) HV000028: Unexpected exception during isValid call.: javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:177)
    at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:68)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:127)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:120)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:533)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:496)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:465)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:430)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateValueInContext(ValidatorImpl.java:781)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateValue(ValidatorImpl.java:210)
    at javax.faces.validator.BeanValidator.validate(BeanValidator.java:349)
    at javax.faces.component.UIInput.validateValue(UIInput.java:1248)
    at javax.faces.component.UIInput.validate(UIInput.java:1037)
    at javax.faces.component.UIInput.executeValidate(UIInput.java:1334)
    at javax.faces.component.UIInput.processValidators(UIInput.java:757)
    at javax.faces.component.UIForm.processValidators(UIForm.java:269)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1298)
    at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1298)
    at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1332)
    at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:77)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:201)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:670)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:55)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:360)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
    at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Double
    at org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.DecimalMinValidatorForDouble.compare(DecimalMinValidatorForDouble.java:17)
    at org.hibernate.validator.internal.constraintvalidators.bv.number.bound.decimal.AbstractDecimalMinValidator.isValid(AbstractDecimalMinValidator.java:51)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:171)
    ... 69 more

更新:错误取决于用户输入。如果输入一个带有小数位的数字,则everthing按预期工作,但是很快,当输入没有小数位的数字时,会发生所描述的错误

jsf bean-validation hibernate-validator mojarra jsf-2.3
1个回答
1
投票

我查看了JSF 2.2和2.3的NumberConverter的源代码,并注意到没有严重的差异(只是评论,版权等)。我隔离了执行实际转换的代码,并将其与一些非常小的普通java代码隔离开来。

NumberFormat nf = NumberFormat.getNumberInstance(Locale.getDefault());

System.out.println(nf.getClass());
System.out.println(Double.class.isAssignableFrom(BigDecimal.class));
System.out.println(nf.parse("10,0")+ " : " + nf.parse("10,0").getClass());
System.out.println(nf.parse("10,1")+ " : " + nf.parse("10,1").getClass());
System.out.println(nf.parse("10.0")+ " : " + nf.parse("10.0").getClass());
System.out.println(nf.parse("10.1")+ " : " + nf.parse("10.1").getClass());

这导致以下输出。

class java.text.DecimalFormat
false
100 : class java.lang.Long
101 : class java.lang.Long
10 : class java.lang.Long
10.1 : class java.lang.Double

我最初是在JDK 8上运行的,但是后来尝试了7次,结果相同。

所以我倾向于得出这样的结论:导致问题的不是JSF(虽然我说因为转换器使用the expected type to do some checking,返回预期的类型而不是NumberFormat返回的类型并不奇怪。

但是当将解析BigDecimal设置为true时

((DecimalFormat)nf).setParseBigDecimal(true);

并运行相同的'测试',精确qazxsw poi没有损失,输出是

like mentioned in the converter

但这也不能转为双倍。所以我个人开始研究BeanValidator代码。

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