mojarra
mojarra copied to clipboard
Injection in Validators does not work
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]
Reported by chris21k
chris21k said: The same with latest Tomcat 8.5.13 . So all libraries are up to date (release).
Chris
This issue was imported from java.net JIRA JAVASERVERFACES-4241
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
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
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.
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.