f: convertNumber для Double: ClassCastException - PullRequest
       62

f: convertNumber для Double: ClassCastException

0 голосов
/ 06 февраля 2019

В 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 на следующем минимальном примере с одной лицевой стороной и просто ломбоком в качестве зависимости:

@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

ОБНОВЛЕНИЕ: ошибка зависит от ввода пользователя.В случае ввода числа с цифрами дроби все работает как положено, но как только вводится число без цифр дроби, возникает описанная ошибка

1 Ответ

0 голосов
/ 13 февраля 2019

Я посмотрел исходный код NumberConverter для JSF 2.2 и 2.3 и не заметил серьезных отличий (только комментарии, авторские права и т. Д.).Я выделил код, который выполняет фактическое преобразование, и выделил его в некотором очень маленьком простом 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, что вызывает проблемы (хотя я бы сказал, что, поскольку конвертер использует ожидаемый тип для некоторой проверки , он будетне странно возвращать ожидаемый тип, а не тип, возвращаемый NumberFormat.

Но при установке parseBigDecimal в значение true

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

И при запуске тех же «тестов», нетпотеря в прецизионности , как упомянуто в конвертере , а выход

100 : class java.math.BigDecimal
101 : class java.math.BigDecimal
10.0 : class java.math.BigDecimal
10.1 : class java.math.BigDecimal

Но это также не может быть приведено к Double. Поэтому я лично начну изучать код BeanValidator.

...