mojarra icon indicating copy to clipboard operation
mojarra copied to clipboard

Injection in Validators does not work

Open javaserverfaces opened this issue 7 years ago • 7 comments

Consider the following examle:

**Index.java**import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class Index {
    private String text;

    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }

    public String submit() {
        System.out.println("submitted " + text);
        return null;
    }
}
**index.html**<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"> <h:body>
    <h:form>
        <h:message for="text" />
        <h:inputText id="text" value="#{index.text}" validator="textValidator" />
        <h:commandButton value="submit" type="submit" action="#{index.submit()}" />
    </h:form>
</h:body>
</html>
**Other.java**import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class Other implements Serializable {
    private static final long serialVersionUID = 1L;

    public String getHello() {
        return "hello";
    }
}
**TextValidator.java**import javax.enterprise.inject.spi.CDI;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.inject.Inject;

/**
 * managed = true necessary if only injecting into TextValidator and the
 * validator object itself is never injected? 
 */
@FacesValidator(managed = true)
public class TextValidator<T> implements Validator<T> {
    /**
     * Should work in Mojarra 2.3 according to Arjan Tijm's weblog
     * http://arjan-tijms.omnifaces.org/p/jsf-23.html#1316b referenced by
     * https://javaserverfaces.java.net/nonav/2.3/whatsnew.html .
     * 
     * However, stays null. -> bug? Tomcat specific?
     */
    @Inject
    private Other other;

    // /**
    //  * Works.
    //  */
    // private Other other = CDI.current().select(Other.class).get();;

    @Override
    public void validate(FacesContext fctx, UIComponent component, T value)
            throws ValidatorException {
        String text = (String) value;
        if (!text.equals(other.getHello())) {
            throw new ValidatorException(new FacesMessage("Validation failed.",
"Input must be 'hello'."));
        }
    }
}
**META-INF/context.xml**<?xml version='1.0' encoding='UTF-8'?>

<Context>
    <Resource name="BeanManager" auth="Container"
        type="javax.enterprise.inject.spi.BeanManager"
        factory="org.jboss.weld.resources.ManagerObjectFactory" />
</Context>
**beans.xml**<?xml version="1.0" encoding="UTF-8"?>
<beans version="1.2" bean-discovery-mode="annotated"
       xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"> </beans>
**faces-config.xml**<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.3"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd">
    <application></application>
</faces-config>
**web.xml**<?xml version="1.0" encoding="UTF-8"?>

<web-app id="JSFPrototype" version="3.1"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
            http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>javax.faces.ENABLE_CDI_RESOLVER_CHAIN</param-name> 
        <param-value>true</param-value> 
    </context-param>
    <context-param>
        <param-name>
            javax.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN
        </param-name>
        <param-value>true</param-value>
    </context-param>

    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>

    <resource-env-ref>
        <resource-env-ref-name>BeanManager</resource-env-ref-name>
        <resource-env-ref-type>
            javax.enterprise.inject.spi.BeanManager
        </resource-env-ref-type>
    </resource-env-ref>
</web-app>

Then in TestValidator other is not injected, i.e., stays null, and a NullPointerException is thrown in validate by the string comparison. According to the blog of Arjan Tijm (see code comment) this should work with Mojarra 2.3. Is Tomcat the culprit, or is a flaw in my code?

The commented out code below obtaining the reference programmatically from CDI works.

Thanks, Chris

Environment

Tomcat 8.5.5 with JSF 2.3.0 (release) and Weld 2.4.3 in WEB-INF/lib

Affected Versions

[2.3.0]

javaserverfaces avatar Apr 12 '17 13:04 javaserverfaces

Reported by chris21k

javaserverfaces avatar Apr 12 '17 13:04 javaserverfaces

chris21k said: The same with latest Tomcat 8.5.13 . So all libraries are up to date (release).

Chris

javaserverfaces avatar Apr 13 '17 10:04 javaserverfaces

This issue was imported from java.net JIRA JAVASERVERFACES-4241

javaserverfaces avatar May 03 '17 08:05 javaserverfaces

The same with Weld 3.0.0. However, I found out, that if using bean-discovery-mode="all" instead of bean-discovery-mode="annotated" the example above works, i.e., the other-object is injected within the validator. However, this should also work with "annotated" as the class Other is annotated with @Named.

Additionally, it seems that using @FacesValidator(managed = true) is not enough. Only @FacesValidator(value="textValidator", managed = true) works. Again, a (related?) bug, as the (default) value should be derived automatically from the class name TextValidator.

Chris

chris21k avatar Jul 30 '17 07:07 chris21k

Please see this important message regarding community contributions to Mojarra.

https://javaee.groups.io/g/jsf-spec/message/30

Also, please consider joining that group, as that group has taken the place of the old [email protected] mailing list.

Thanks,

Ed Burns

edburns avatar Oct 29 '17 03:10 edburns

I started using @FacesValidator in my current project with JSF Mojarra 2.3.3 and would like to confirm the behavior mentioned by chris21K, except I am using EAP 7.0.0 with CDI 1.2 and Java 8.

I only get it to work annotating the FacesValidator with @Dependent (or adding beans.xml with beand-discovery-mode="all" or calling the injectable bean with CDI.current()...) AND adding to the @FacesValidator annotation the attribute value with corresponding FacesValidator class name.

ghost avatar Jan 26 '18 14:01 ghost

I took the CDI.current() approach but only once, then the discovered bean is stored in a private static field to avoid such "expensive" calls as CDI look-ups are.

Quix0r avatar Mar 29 '18 22:03 Quix0r