OmniFaces goes CDI
Finally, OmniFaces 1.6 has been released! With this release, OmniFaces features for the first time CDI-related features:
- Fully transparent support for
@Inject
and@EJB
in@FacesConverter
and@FacesValidator
. No additional configuration or annotations are required. Exactly like as how it would have been in JSF 2.2 until they removed it at the last moment and postponed for JSF 2.3. - CDI compatible
@ViewScoped
specifically for JSF 2.0 and 2.1. Exactly like it works in new JSF 2.2, including proper handling of@PreDestroy
(JSF's own@ViewScoped
does in 2.0/2.1 not invoke the@PreDestroy
when the session is expired). - CDI alternative to
<f:viewParam>
which also supports JSF converters and validators, via an@Param
annotation. One big advantage as compared to<f:viewParam>
is that you can finally do the initialization thing based on those request parameters in a@PostConstruct
instead of apreRenderView
listener workaround.
Our main project zeef.com has run in production without any problems for almost a month using those new features from 1.6 snapshots.
CDI is as of now becoming more and more important in Java EE world. JSF is slowly moving to CDI as to bean management. As of JSF 2.2, @FlowScoped
is CDI-only and a new CDI compatible @ViewScoped
has been introduced. EJB is slowly moving to CDI as to injection. As of Java EE 6, you can already @Inject
an EJB instead of using @EJB
. Only self-injection (in order to properly invoke an @Asynchronous
method from within another method in same EJB) is still not possible with CDI. Java EE is slowly unifying all management annotations towards CDI and will slowly deprecate the old bean management annotations including the original JSF ones. Java EE 8 will even be more CDI.
Last but not least, a big thanks goes out to OmniFaces user Radu Creanga for providing kickoff code snippets for CDI @FacesConverter
, @FacesValidator
and @ViewScoped
. As we (Arjan and me) never really worked closely with CDI before, we wouldn't immediately have a clue where to start.
Delayed release
First of all, sorry for a somewhat delayed release. It has now already been over 3 months since 1.5 is released. Even though 1.5 was itself also 3 months after 1.4, we initially intented to release about every 2 months ("6 to 8 weeks"). As to the new features, the 1.6 was functionally ready for release almost one month ago. We were very exicted on that. However, intensive testing on a broad range of application servers taught that the new CDI features initially didn't work flawlessly on some of them. The new CDI features were thoroughly fixed, polished and enhanced to the max in order to work on as much application servers as possible. Also, possible workarounds were investigated in order to get the new CDI features to work anyway. After all, the delay was just because we wanted to get it all right instead of releasing a library which didn't work on half of servers.
Compatibility of OmniFaces 1.6 CDI features with application servers
So far, the following containers have been tested on new CDI features:
- Tomcat 7.0.42: importantly, it deploys the webapplication successfully (on the initial 1.6 snapshot versions it didn't due to unresolved CDI dependencies and that was fixed by abstracting away the CDI providers). Only the CDI features are obviously not usable as plain Tomcat doesn't support CDI out the box.
- Tomcat 7.0.42 + Weld 1.1.14: when manually installing Weld (and Hibernate Validator! there seems here to be an undocumented dependency on JSR303) on Tomcat, all CDI features start to work flawlessly.
- JBoss EAP 6.0.1: it has always worked flawlessly from the beginning on.
- JBoss EAP 6.1: it has always worked flawlessly from the beginning on.
- TomEE 1.5.2: it has always worked flawlessly from the beginning on.
- TomEE 1.6.0 SNAPSHOT: snapshot versions from before 11 september 2013 had problems with injecting the
@Param
. This was reported as OpenWebBeans issue 893. The OpenWebBeans guys were very supportive in this. It turned out that the CDI 1.1 spec was broken on this area (injecting a generic parameterized value holder). After all, OpenWebBeans guys decided to rollback the changes back to the way how CDI 1.0 work. The snapshot versions from after 11 september 2013 work flawlessly. - GlassFish 3.1.2.2 - 4.0.0: a web application with OmniFaces 1.6 bundled but without
/WEB-INF/beans.xml
will throw the following exception during deployment and end up with an unsuccessful deploy:Caused by: java.lang.NullPointerException at java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:333) at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:988) at org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:1076) at org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:148) at org.glassfish.weld.services.JCDIServiceImpl._createJCDIInjectionContext(JCDIServiceImpl.java:169) at org.glassfish.weld.services.JCDIServiceImpl.createJCDIInjectionContext(JCDIServiceImpl.java:146) at com.sun.ejb.containers.BaseContainer.createEjbInstanceAndContext(BaseContainer.java:1639) at com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:475) ...
This is related to GlassFish issue 20566 and already fixed in GlassFish 4.0.1. There are 2 workarounds for ones who can't upgrade:- Enable CDI anyway in your web application by creating/generating the
/WEB-INF/beans.xml
file. - Disable implicit CDI artifact checking by executing the following asadmin command:
$GFHOME/bin/asadmin set configs.config.server-config.cdi-service.enable-implicit-cdi=false
- Enable CDI anyway in your web application by creating/generating the
- Liberty 8.5.5: only the
@FacesConverter
and@FacesValidator
failed because of an issue in state saving management of the MyFaces 2.0.5 implementation which is being used under the covers. This is related to MyFaces issue 3257. This is fixed in MyFaces 2.0.8 and 2.1.2. As there's no obvious way to upgrade the Liberty-bundled MyFaces, the workaround is to let those@FacesConverter
and@FacesValidator
annotated classes implementSerializable
whenever you need@EJB
and@Inject
support in them. Note that they thus don't need to beSerializable
when you don't need the CDI features on them. It's by the way somewhat astonishing to see a relatively new application server to be shipped with a de dato more than 2 year old JSF implementation. Registered Liberty users can request IBM here for a newer MyFaces version. - WebLogic 12.1.2: a web application with OmniFaces 1.6 bundled will throw the following exceptions during deployment and end up with an unsuccessful deploy:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [ConverterExtension] with qualifiers [@Default] at injection point [[field] @Inject private org.omnifaces.cdi.converter.ConverterManager.extension] at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311) at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280) at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143) at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163) at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382) at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367) at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:379) ... org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [ValidatorExtension] with qualifiers [@Default] at injection point [[field] @Inject private org.omnifaces.cdi.validator.ValidatorManager.extension] at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311) at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280) at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143) at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163) at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382) at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367) at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:379) ...
The WebLogic guys were however very supportive and mentioned that this problem is already identified and fixed in upcoming 12.1.3. Commercial WebLogic 12.1.2 users can currently patch their server with patch ID 16785005 (which they can download by going to https://support.oracle.com and then go to the patches section). After that, all CDI features work flawlessly. - Geronimo 3.0.0: injection in validators and converters doesn't work. We didn't investigate this further as this version is deprecated and users can upgrade easily to 3.0.1.
- Geronimo 3.0.0.4 (packaged by IBM as WASCE): it has always worked flawlessly from the beginning on.
- Geronimo 3.0.1: it has always worked flawlessly from the beginning on.
- Resin 4.0.36: its CDI implementation CanDI didn't support the generic parameterized injection point of
@Param
nor the generic parameterized@Observes
of the extensions for@FacesConverter
and@FacesValidator
annotations. The web application deploys fine, but once the@Param
is used, the following exception is thrown:{resin-port-8080-43} classpath:META-INF/caucho/app-default.xml:55: javax.enterprise.inject.InjectionException: 'public org.omnifaces.cdi.param.ParamValue org.omnifaces.cdi.param.RequestParameterProducer.produce(javax.enterprise.inject.spi.InjectionPoint)' is an invalid @Produces method because it returns a generic type class org.omnifaces.cdi.param.ParamValue
Resin issue 5522 and this mailing list message was posted about this problem. As of now it's still unresolved. There was no feedback for a rather long time. We decided to not wait any longer and proceed with OmniFaces 1.6 release anyway. Injection in validators/converters simply doesn't work. We have found a workaround for this, but this has performance implications, so we decided to not implement it for now. In our opinion, CanDI is simply broken here. Resin users should report/vote the issue to CanDI. Noted should be that the@ViewScoped
works flawlessly on Resin.
FacesLocal
OmniFaces 1.6 is not only CDI. The Faces
utility class got a new brother, the FacesLocal
. You probably already know that the Faces
utility class is very handy for oneliner usages. However, once you need to invoke it multiple times in the same method scope, or when you happen to have the FacesContext
already available as method argument (e.g., inside a custom component or renderer), then the repeated FacesContext#getCurrentInstance()
calls which are been done under the covers by Faces
utility class may become a bit too expensive. Whilst the access to a ThreadLocal
variable is blazing fast on modern hardware, it is under the covers still blocking all other running threads for some nanoseconds or what.
Therefore, all methods of the Faces
utility class which are internally using FacesContext#getCurrentInstance()
have been split to a new FacesLocal
utility class where those methods now take FacesContext
as an argument.
So, instead of for example the following piece of code which blocks all other running threads for no less than 4 times,
User user = Faces.getSessionAttribute("user");
Item item = Faces.evaluateExpressionGet("#{item}");
String cookieValue = Faces.getRequestCookie("cookieName");
List<Locale> supportedLocales = Faces.getSupportedLocales();
you can do the following which does that only once:
FacesContext context = Faces.getContext();
User user = FacesLocal.getSessionAttribute(context, "user");
Item item = FacesLocal.evaluateExpressionGet(context, "#{item}");
String cookieValue = FacesLocal.getRequestCookie(context, "cookieName");
List<Locale> supportedLocales = FacesLocal.getSupportedLocales(context);
Noted should be that all methods of Faces
utility class still remain their functionality. It's only under the covers delegating further to FacesLocal
. So, the new OmniFaces 1.6 Faces
class is still fully backwards compatible and shouldn't break any existing code.
Mojarra 2.2
We briefly tested the OmniFaces 1.6 showcase application on Mojarra 2.2 as well. It works flawlessly on Tomcat 7.0.42 + Mojarra 2.2.3. CDI features also work flawlessly when Weld 1.1.14 was added on top of that. When we removed all references to #{now}
and #{startup}
from the showcase application, it also works flawlessly on GlassFish 4.0.0 + Mojarra 2.2.2. Those java.util.Date
beans failed to initialize due to this GlassFish bug which we hope to be fixed by them soon enough (please vote for the issue if you can).
Noted should be that the OmniFaces CDI @ViewScoped
also works flawlessly on Mojarra 2.2, but it's recommended to just use JSF 2.2's own CDI javax.faces.view.ViewScoped
annotation for this. Not because OmniFaces one is so bad, on the contrary, but just because standard solutions should be preferred over alternative solutions if they achieve exactly the same.
An overview of all additions/changes/bugfixes in OmniFaces 1.6
Taken over from the What's new? page on showcase:
Added in OmniFaces 1.6
- Injecting, converting and validating HTTP request parameters via CDI
@Param
(say, the CDI alternative to<f:viewParam>
) - Transparent support for dependency injection (CDI and EJB) inside
@FacesConverter
and@FacesValidator
ValueChangeConverter
, a base class which converts only when the submitted value is really changed as compared to the model valueComponents#getCurrentCommand()
which returns the currently invoked command component (useful for logging)Components#createValueExpression()
,#createMethodExpression()
,#createVoidMethodExpression()
,#createActionListenerMethodExpression()
and#createAjaxBehavior()
- New
JNDI
utility class Faces#getBookmarkableURL()
,#getRequestHostname()
and#setResponseStatus()
message
attribute for<o:messages>
to display a global message in case any of the infor
specified components has a faces message- Added
logException
method toFullAjaxExceptionHandler
so that it can be overridden for more fine grained control of logging - New
FacesLocal
utility class which extendsFaces
utility class with methods which takeFacesContext
as argument - CDI compatible
@ViewScoped
annotation specifically for JSF 2.0/2.1 useRequestURI
attribute for<o:form>
to submit to exactly the same URL as in browser's address bar, with query stringof:splitArray()
andof:splitList()
to split an array or list in subarrays or sublistsof:formatPercent()
to format a number as a percentage
Changed in OmniFaces 1.6
showMessageFor
attribute of multi field validators now supports a space separated collection of client IDs where the message should be shown- CDN resource handler now supports: 1) always being enabled, also during development stage, 2) wildcard configuration, and 3) EL resolving in CDN URL
Faces#redirect()
will not useString#format()
anymore when no params are supplied, this enables developers to pass already-encoded URLs toFaces#redirect()
Fixed in OmniFaces 1.6
Json#encode()
incorrectly escaped singlequotes (was causing validation error injQuery.parseJSON()
)GzipResponseFilter
returned gzipped content as plain text in WebSphere 8.5 / Liberty<o:onloadScript>
rendered its body as plain text during an@all
render and didn't work anymore during a postback since Mojarra 2.1.24<o:highlight>
threw duplicate component ID error in TomEE 1.6.0
Maven download stats
Here are the Maven download stats which are not counted on the Google Code downloads page:
- June 2013: 2739
- July 2013: 2432
- August 2013: 2266
Here's a screenshot of the graph at Maven central (click to enlarge):