Thursday, September 19, 2013

OmniFaces goes CDI with release of 1.6!

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 a preRenderView 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.

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:

$GFHOME/bin/asadmin set configs.config.server-config.cdi-service.enable-implicit-cdi=false

After that, all CDI features work flawlessly.

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 implement Serializable whenever you need @EJB and @Inject support in them. Note that they thus don't need to be Serializable 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 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,

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.