Archive for November, 2014

Providing alternatives for JSF 2.3’s injected artifacts

6 November 2014

At the JSF 2.3 EG we’re currently busy with introducing the ability to inject several of JSF’s own artifacts in your own beans.

On the implementation side this is done via a dynamic CDI producer. There’s for instance a producer for the FacesContext, which is then registered via a CDI extension.

This can be tested via a simple test application. See these instructions for how to obtain a JSF 2.3 snapshot build and update GlassFish with it.

The test application will consist of the following code:

WEB-INF/faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config 
	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"
	version="2.3"
>
</faces-config>

This file is needed to activate injection of JSF artefacts. For backwards compatibility reasons this feature is only activated when running with a JSF 2.3 deployment descriptor. The second purpose of a (near) empty faces-config.xml is to signal JSF to automatically map the FacesServlet, so we don’t have to create a more verbose web.xml with an explicit mapping. (however the default mappings are not the best ones as the most obvious one, *.xhtml is missing. This is something we hope to rectify in JSF 2.3 as well)

WEB-INF/beans.xml
(empty)

An empty beans.xml is still needed in GlassFish 4.1 to actually enable CDI in a web archive.

index.xhtml

<!DOCTYPE html>
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:jsf="http://xmlns.jcp.org/jsf"
>
    <head jsf:id="head">
        <title>FacesContext inject test app</title>
    </head>
 
    <body jsf:id="body">
	   #{testBean.test}
    </body>
</html>

[java src]/test/TestBean.java

package test;
 
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
 
@Named
@RequestScoped
public class TestBean {
 
    @Inject
    private FacesContext context;
 
    public String getTest() {
        return context.toString();
    }
}

Deploying this to our updated GlassFish and requesting http://localhost:8080/itest/index.jsf will result in something like the following:

com.sun.faces.context.FacesContextImpl@7c46fc07 

So injection works! Now what if we want to “override” the default producer provided by JSF, e.g. what if we want to provide our own alternative implementation?

The answer is to provide your own producer, but mark it as @Dependent, @Alternative and @Priority. E.g. add the following class to the files shown above:

[java src]/test/ContextProducer.java

package test;
 
import static javax.interceptor.Interceptor.Priority.APPLICATION;
 
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextWrapper;
 
@Dependent
@Alternative
@Priority(APPLICATION)
public class ContextProducer {
 
    @Produces
    public FacesContext producer() {
        return new FacesContextWrapper() {
 
            @Override
            public String toString() {
                return "Still ours";
            }
 
            @Override
            public FacesContext getWrapped() {
                return FacesContext.getCurrentInstance();
            }
        };
    }
}

Then deploying this again and request http://localhost:8080/itest/index.jsf once more, will now result in the following:

Still ours 

As we see, the JSF provided producer can be overridden by standard CDI means.

The feature is not finalized yet so things may still change, but hopefully this gives some idea of what direction JSF 2.3 is moving in.

Arjan Tijms

css.php best counter