- cdi
- components
- contexts
- converters
- el
- eventlisteners
- exceptionhandlers
- facesviews
- filters
- functions
- managedbeans
- push
- resourcehandlers
- search
- servlets
- taghandlers
- utils
- validators
- viewhandlers
- - ContextParam
- Cookie
- Eager
- FacesConverter
- FacesValidator
- GraphicImageBean
- Param
- ViewScoped
- cdi
- components
- contexts
- converters
- el
- eventlisteners
- exceptionhandlers
- facesviews
- filters
- functions
- managedbeans
- push
- resourcehandlers
- search
- servlets
- taghandlers
- utils
- validators
- viewhandlers
- ContextParam
- Cookie
- Eager
- FacesConverter
- FacesValidator
- GraphicImageBean
- Param
- ViewScoped
The @FacesConverter
is by default not eligible for dependency injection by @Inject
nor @EJB
. There is a workaround for EJB, but this is nasty and doesn't work out for CDI. Another way would be to make it a JSF or CDI managed bean, however this doesn't register the converter instance into the JSF application context, and hence you won't be able to make use of Application.createConverter(String)
on it. Further it also breaks the power of forClass
attribute, i.e. you can't register a JSF converter for a specific type anymore and you'd need to explicitly declare it everytime.
Initially, this should be solved in JSF 2.2 which comes with new support for dependency injection in among others all javax.faces.*.*Factory
, NavigationHandler
, ResourceHandler
, ActionListener
, PhaseListener
and SystemEventListener
instances. The Converter
and Validator
were initially also among them, but they broke a TCK test and were at the last moment removed from dependency injection support.
The support is expected to come back in JSF 2.3, but we just can't wait any longer. MyFaces CODI has support for it, but it requires an additional @Advanced
annotation. OmniFaces solves this by implicitly making all FacesConverter
instances eligible for dependency injection without any further modification.
The ConverterManager
provides access to all FacesConverter
annotated Converter
instances which are made eligible for CDI.
bean-discovery-mode
In Java EE 7's CDI 1.1, when having a CDI 1.1 compatible beans.xml
, by default only classes with an explicit CDI managed bean scope annotation will be registered for dependency injection support. In order to cover FacesConverter
annotated classes as well, you need to explicitly set bean-discovery-mode="all"
attribute in beans.xml
. This was not necessary in Mojarra versions older than 2.2.9 due to an oversight. If you want to keep the default of bean-discovery-mode="annotated"
, then you need to add Dependent
annotation to the converter class.
AmbiguousResolutionException
In case you have a FacesConverter
annotated class extending another FacesConverter
annotated class which in turn extends a standard converter, then you may with bean-discovery-mode="all"
face an AmbiguousResolutionException
. This can be solved by placing Specializes
annotation on the subclass.
Converters with special Class constructor
By default, CDI only instantiates beans via the default constructor. In case a converter for a class is created, and the returned converter does not have a default constructor, or has a single argument constructor that takes a Class
instance, then this converter will not be made eligible for CDI. This change was added in OmniFaces 2.6 as per issue 25.
JSF 2.3 compatibility
OmniFaces 3.0 continued to work fine with regard to managed converters which are initially developed for JSF 2.2. However, JSF 2.3 introduced two new features for converters: parameterized converters and managed converters. When the converter is parameterized as in implements Converter<T>
, then you need to use at least OmniFaces 3.1 wherein the incompatibility was fixed. When the converter is managed with the new JSF 2.3 managed=true
attribute set on the FacesConverter
annotation, then the converter won't be managed by OmniFaces and will continue to work fine for JSF. But the <o:converter> tag won't be able to set attributes on it.
Submit the form
Converter will print itself and both the injected EJB and CDI bean in a faces message. Note: EJB is stateless and CDI bean is request scoped.
<h3>Submit the form</h3>
<p>
Converter will print itself and both the injected EJB and CDI bean in a faces message.
Note: EJB is stateless and CDI bean is request scoped.
</p>
<h:form>
<h:inputText converter="someConverter" />
<h:commandButton value="Submit">
<f:ajax execute="@form" render="@form" />
</h:commandButton>
<h:messages />
</h:form>
package org.omnifaces.showcase.cdi;
import javax.ejb.EJB;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;
import org.omnifaces.util.Messages;
@FacesConverter("someConverter")
public class SomeConverter implements Converter {
@EJB
private SomeEJB ejb;
@Inject
private SomeCDI cdi;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Messages.addInfo(component.getClientId(context), "Converter currently used: {0}", this);
Messages.addInfo(component.getClientId(context), "EJB injected in converter: {0}", ejb);
Messages.addInfo(component.getClientId(context), "CDI injected in converter: {0}", cdi);
return value;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value != null ? value.toString() : "";
}
}
package org.omnifaces.showcase.cdi;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class SomeCDI {
//
}
package org.omnifaces.showcase.cdi;
import javax.ejb.Stateless;
@Stateless
public class SomeEJB {
//
}