Tuesday, November 24, 2015

OmniFaces 2.2 released!

OmniFaces 2.2 has been released!

This version brings a bunch of new utility methods in Faces, Messages and Components classes, a new <o:viewAction> component, a new @ContextParam annotation, a whole FileServlet, and a second life for the @ViewScoped annotation.

As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 2.2. The top three additions are the second life for the @ViewScoped annotation, the new and refactored "BalusC FileServlet", and the new <o:viewAction>.

Installation

Non-Maven users: download OmniFaces 2.2 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.2</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.2</version>
</dependency>

For users who don't want to use CDI, there's the CDI-less 1.12.1 with all 2.2 enhancements and fixes (but no brand new 2.x additions!). UPDATE: FacesViews was broken in 1.12, so a 1.12.1 has been baked with the hotfix. FacesViews continues to work fine in 2.2, so there's no 2.2.1.

Second life for @ViewScoped

In standard JSF 2.0/2.1, the @PreDestroy annotated method on a standard JSF view scoped bean was never invoked when the session expires. This was solved since OmniFaces 1.6 with its new CDI @ViewScoped annotation. However, since JSF 2.2 this problem is solved on JSF's own @ViewScoped beans, hereby basically making the OmniFaces CDI @ViewScoped annotation superflous in JSF 2.2. You could as good just stick to JSF's own @ViewScoped annotation.

Instead being potentially deprecated, the OmniFaces @ViewScoped got a second life: immediately invoke @PreDestroy when the browser unloads the page instead of waiting until the session is expired. In other words, when the user navigates away by a GET link, or closes the browser tab/window, or refreshes the page (by GET), then the OmniFaces @ViewScoped bean will immediately be destroyed. None of the both JSF 2.2 @ViewScoped annotations support this.

The trick is done by a synchronous XHR request during beforeunload event (thus not on the main thread! ;) ) via a helper script omnifaces:unload.js which is automatically included when an OmniFaces @ViewScoped bean is created. The XHR request sends a special request parameter omnifaces.event=unload along with the OmniFaces specific view scope ID and the JSF javax.faces.ViewState ID:

var xhr = new XMLHttpRequest();
xhr.open("POST", window.location.href.split(/[?#;]/)[0], false);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("omnifaces.event=unload&id=" + id + "&" + VIEW_STATE_PARAM + "=" + viewState);

The existing OmniFaces RestorableViewHandler has been extended with below lines:

if (isUnloadRequest(context)) {
    UIViewRoot createdView = createView(context, viewId);
    createdView.restoreViewScopeState(context, getRenderKit(context).getResponseStateManager().getState(context, viewId));
    BeanManager.INSTANCE.getReference(ViewScopeManager.class).preDestroyView();
    responseComplete();
    return createdView;
}

In a nutshell: when the omnifaces.event=unload parameter is present, then only create the view and restore any view scoped beans (and thus don't build the view! which might be time consuming and unnecessarily create new beans) and finally destroy those beans, hereby immediately invoking the @PreDestroy and freeing unused memory space on demand. After that, JSF is instructed to immediately render the response and return the dummy (non-built!) view, preventing potential unnecessary ViewExpiredException.

There's however a small caveat: on slow network and/or poor server hardware, there may be a noticeable lag between the enduser action of unloading the page and the desired result, even though the server side action may take place in only a few milliseconds (in case you're doing expensive tasks inside @PreDestroy method, better delegate it to an @Asynchronous EJB method or so). If the slow network/hardware lag is in your case noticeable and thus undesireable, then better stick to JSF 2.2's own @ViewScoped annotation and accept the postponed destroy.

New FileServlet with advanced HTTP range and caching support

The well known "BalusC FileServlet" from the blog article FileServlet supporting resume and caching has been cleaned, refactored and reworked into an abstract template. Now, instead of copypasting and editing the code from the blog, you can simply extend from org.omnifaces.servlet.FileServlet and implement the getFile() method in its simplest form as below:

@WebServlet("/media/*")
public class MediaFileServlet extends FileServlet {

    @Override
    protected File getFile(HttpServletRequest request) throws IllegalArgumentException {
        return new File("/var/webapp/media", request.getPathInfo());
    }

}

When referenced in e.g. a HTML5 <video> tag like below:

<video src="#{request.contextPath}/media/video.mp4" controls="controls" />

Then the FileServlet will worry about properly streaming the media range requests. I.e. the servlet is able to return specific parts of the media file on a request with a HTTP Range header. For example, only the bytes at exactly the index 1000 until 2000 on a file of 10MB long. This is mandatory for many media players in order to be able to skip a certain range of the media stream quickly enough and/or to improve buffering speed by creating multiple connections which each requests different parts of the file. Also download accelerators will take benefit of this on large files. They will then simply open multiple HTTP connections which each downloads a specific range of the file and afterwards put the pieces together.

Fix unintuitive "if" attribute of <f:viewAction>

The if attribute of the standard <f:viewAction> not really intuitive. It is checked during APPLY_REQUEST_VALUES phase instead of INVOKE_APPLICATION phase. This would make several straightforward use cases to fail.

In below example, the FooConverter may convert a non-null parameter to null without causing a validation or conversion error, and the intent is to redirect the current page to otherpage.xhtml when the converted result is null.

<f:viewParam name="foo" value="#{bean.foo}" converter="fooConverter" />
<f:viewAction action="otherpage" if="#{bean.foo eq null}" />

However, this fails because the if attribute runs before the conversion has taken place, when the component wants to check whether it should queue the action event. This happens during APPLY_REQUEST_VALUES phase. The OmniFaces <o:viewAction> solves this by postponing the evaluation of the if attribute to the INVOKE_APPLICATION phase.

<f:viewParam name="foo" value="#{bean.foo}" converter="fooConverter" />
<o:viewAction action="otherpage" if="#{bean.foo eq null}" />

The implementation of the extended component is really simple. It's nothing more than below:

@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
    if (super.isRendered()) {
        super.broadcast(event);
    }
}

@Override
public boolean isRendered() {
    return !isImmediate() || super.isRendered();
}

It gives a bit of thinking as the if attribute is basically a renamed rendered attribute, and one would wonder why !isImmediate() is checked instead of e.g. the current phase ID or a hardcoded true. But this way the original and intented behavior of immediate attribute is maintained. Actually, the <o:viewAction immediate="true"> behaves exactly the same as a <f:viewAction immediate="true">.

Maven download stats

Here are the Maven download stats after previous release:

  • June 2015: 6949
  • July 2015: 6574
  • August 2015: 5906
  • September 2015: 6561
  • October 2015: 5904

Below is the version pie of October 2015:

Surprising how many 1.8.1 users are still there. To those who can't upgrade to 2.2 or 1.12, I'd like to point out that 1.8.3 contains an important memory leak fix in case you're using UnmappedResourceHandler while having composite components on the pages. So, if you can't upgrade to 2.2 or 1.12, then please at least upgrade to 1.8.3. And those few old RC1/RC2 and M1 users, upgrade to the final version without any RC/M suffix!

Friday, October 30, 2015

The empty String madness

Introduction

When we submit a HTML form with empty input fields which are bound to non-primitive bean properties, we'd rather like to keep them null instead of being polluted with empty strings or zeroes. This is very significant as to validation constraints such as @NotNull in Bean Validation and NOT NULL in relational databases. Across years and JSF/EL versions this turned out to be troublesome as not anyone agreed on each other. I sometimes even got momentarily confused myself when it would work and when not. I can imagine that a lot of other JSF developers have the same feeling. So let's do some digging in history and list all the facts and milestones in one place for best overview, along with an useful summary table with the correct solutions.

JSF 1.0/1.1 (2004-2006)

Due to the nature of HTTP, empty input fields arrive as empty strings instead of null. The underlying servlet request.getParameter(name) call returns an empty string on empty input fields. Nothing to do against, that's just how HTTP and Servlets work. A value of null represents the complete absence of the request parameter, which is also very significant (e.g. the servlet could this way check if a certain form button is pressed or not, irrespective of its value/label which could be i18n'ed). So we can't fix this in HTTP/Servlet side and have to do it in MVC framework's side. To avoid the model being polluted with empty strings, you would in JSF 1.0/1.1 need to create a custom Converter like below which you explicitly register on the inputs tied to java.lang.String typed model value.

public class EmptyToNullStringConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
        if (submittedValue == null || submittedValue.isEmpty()) {
            if (component instanceof EditableValueHolder) {
                ((EditableValueHolder) component).setSubmittedValue(null);
            }

            return null;
        }

        return submittedValue;
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent component, Object modelValue) {
        return (modelValue == null) ? "" : modelValue.toString();
    }

}

Which is registered in faces-config.xml as below:

<converter>
    <converter-id>emptyToNull</converter-id>
    <converter-class>com.example.EmptyToNullStringConverter</converter-class>
</converter>

And used as below:

<h:inputText value="#{bean.string1}" converter="emptyToNull" />
<h:inputText value="#{bean.string2}" converter="emptyToNull" />
<h:inputText value="#{bean.string3}" converter="emptyToNull" />

The converter-for-class was not supported on java.lang.String until JSF 1.2.

The non-primitive numbers wasn't a problem in JSF 1.x, but only in specific server/EL versions. See later.

JSF 1.2 (2006-2009)

Since JSF 1.2, the converter-for-class finally supports java.lang.String (see also spec issue 131). So you can simply register the above converter as below and it'll get automatically applied on all inputs tied to java.lang.String typed model value.

<converter>
    <converter-for-class>java.lang.String</converter-for-class>
    <converter-class>com.example.EmptyToNullStringConverter</converter-class>
</converter>
<h:inputText value="#{bean.string1}" />
<h:inputText value="#{bean.string2}" />
<h:inputText value="#{bean.string3}" />

Tomcat 6.0.16 - 7.0.x (2007-2009)

Someone reported Tomcat issue 42385 wherein EL failed to set an empty String value representing an integer into a primitive int bean property. This uncovered a long time RI bug which violated section 1.18.3 of EL 2.1 specification.

1.18.3 Coerce A to Number type N

  • If A is null or "", return 0.
  • ...

In other words, when the model type is a number, and the submitted value is an empty string or null, then EL should coerce all integer based numbers int, long, Integer, Long and BigInteger to 0 (zero) before setting the model value. The same applies to decimal based numbers float, double, Float, Double and BigDecimal, which will then be coerced to 0.0. This was not done rightly in Oracle (Sun) nor in Apache EL implementations at the date. They both just set null in the number/decimal typed model value and only Apache EL failed on primitives whereas Oracle EL properly set the default value of zero (and hence that Tomcat issue report).

Since Tomcat 6.0.16, Apache EL started to set all number/decimal typed model values with 0 and 0.0 respectively. That's okay for primitive types like int, long, float and double, but that's absolutely not okay for non-primitive types like Integer, Long, Float, Double, BigInteger and BigDecimal. They should stay null when the submitted value is empty or null. The same applies to Boolean fields which got a default value of false and Character fields which got a default value of \u0000.

So I created JSP spec issue 184 for that (EL was then still part of JSP). This coercion doesn't make sense for non-primitives. The issue got a lot of recognition and votes. After complaints from JSF users, since Tomcat 6.0.17 a new VM argument was added to disable this Apache EL behavior on non-primitive number/decimal types.

-Dorg.apache.el.parser.COERCE_TO_ZERO=false

It became the most famous Tomcat-specific setting among JSF developers. It even worked in JBoss and all other servers using Apache EL parser (WebSphere a.o). It could even be set programmatically with help of a ServletContextListener.

@WebListener
public class Config implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.setProperty("org.apache.el.parser.COERCE_TO_ZERO", "false");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // NOOP.
    }

}

JSF 2.x (2009-current)

To reduce the EmptyToNullStringConverter boilerplate, JSF 2.0 introduced a new context param with a rather long name which should achieve exactly the desired behavior of interpreting empty string submitted values as null.

<context-param>
    <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
    <param-value>true</param-value>
</context-param>

To avoid non-primitive number/decimal typed model values being set with zeroes, on Tomcat and clones you still need the VM argument for the Apache EL parser as explained in the previous section. See also a.o. the Communication in JSF 2.0 article here.

EL 3.0 (2013-current)

And then EL 3.0 was introduced as part of Java EE 7 (which also covers JSF 2.2). With this version, the aforementioned JSP spec issue 184 was finally fixed. EL specification does no longer require to coerce non-primitive number/decimal types to zero. Apache EL parser was fixed in this regard. The -Dorg.apache.el.parser.COERCE_TO_ZERO=false is now the default behavior and the VM argument became superflous.

However, the EL guys went a bit overboard with fixing issue 184. They also treated java.lang.String the same way as a primitive! See also section 1.23.1 and 1.23.2 of EL 3.0 specification (emphasis mine):

1.23.1 To Coerce a Value X to Type Y

  • If X is null and Y is not a primitive type and also not a String, return null.
  • ...

1.23.2 Coerce A to String

  • If A is null: return “”
  • ...

They didn't seem to realize that coercion can work in two ways: when performing a "get" and when performing a "set". Coercion from null string to empty string makes definitely sense during invoking the getter (you don't want to see "null" being printed over all place in HTML output, right?). Only, it really doesn't make sense during invoking the setter (as the model would be polluted with empty strings over all place).

And suddenly, the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL didn't have any effect anymore. Even when JSF changes the empty string submitted value to null as instructed, EL 3.0 will afterwards coerce the null string back to empty string again right before invoking the model value setter. This was first noticeable in Oracle EL (WildFly, GlassFish, etc) and only later in Apache EL (see next chapter). This was discussed in JSF spec issue 1203 and Mojarra issue 3075, and finally EL spec issue 18 was created to point out this mistake in EL 3.0.

Until they fix it, this could be workarounded with a custom ELResolver for common property type of java.lang.String like below which utilizes the new EL 3.0 introduced ELResolver#convertToType() method. The remainder of the methods is not relevant.

public class EmptyToNullStringELResolver extends ELResolver {

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        return String.class;
    }

    @Override
    public Object convertToType(ELContext context, Object value, Class<?> targetType) {
        if (value == null && targetType == String.class) {
            context.setPropertyResolved(true);
        }

        return value;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        return null;
    }

    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        return null;
    }

    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        return null;
    }

    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        return true;
    }

    @Override
    public void setValue(ELContext context, Object base, Object property, Object value) {
        // NOOP.
    }

}

Which is registered in faces-config.xml as below:

<application>
    <el-resolver>com.example.EmptyToNullStringELResolver</el-resolver>
</application>

This was finally fixed in Oracle EL 3.0.1-b05 (July 2014). It is shipped as part of a.o. GlassFish 4.1 and WildFly 8.2. So the above custom ELResolver is unnecessary on those servers. Do note that you still need to keep The Context Param With The Long Name in EL 3.0 reagardless of the fix and the custom ELResolver!

Tomcat 8.0.7 - 8.0.15 (2014)

Apache EL 3.0 worked flawlessly until someone reported Tomcat issue 56522 that it didn't comply the new EL 3.0 requirement of coercing null string to empty string, even though that new requirement didn't make sense. So since Tomcat 8.0.7, Apache EL also suffered from this EL 3.0 problem of unnecessarily coercing null string to empty string during setting the model value. However, the above EmptyToNullStringELResolver workaround in turn still failed in all Tomcat versions until 8.0.15, because it didn't take any custom ELResolver into account. See also Tomcat issue 57309. This was fixed in Tomcat 8.0.16.

If upgrading to at least Tomcat 8.0.16 in order to utilize the EmptyToNullStringELResolver is not an option, the only way to get it to work is to replace Apache EL by Oracle EL in Tomcat-targeted JSF web applications. This can be achieved by dropping the current latest release in webapp's /WEB-INF/lib (which is javax.el-3.0.1-b08.jar at the time of writing) and adding the below context parameter to web.xml to tell Mojarra to use that EL implementation instead:

<context-param>
    <param-name>com.sun.faces.expressionFactory</param-name>
    <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>

Or when you're using MyFaces:

<context-param>
    <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
    <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>

Of course, this is also a good alternative to the custom EmptyToNullStringELResolver in its entirety. Also here, you still need to keep The Context Param With The Long Name.

Summary

Here's a summary table which should help you in figuring out what to do in order to keep non-primitive bean properties null when the submitted value is empty or null (so, to avoid pollution of model with empty strings or zeroes over all place).

Note: Tomcat and JBoss use Apache EL, and GlassFish and WildFly use Oracle EL. Other servers (mainly the closed source ones such as WebSphere, WebLogic, etc) are not covered as I can't tell the exact versions being affected, but generally the same rules apply depending on the EL implementation being used.

JSFTomcatJBoss ASWildFlyGlassFish
5.5.x-6.0.156.0.166.0.17+7.0.x8.0.0~68.0.7~158.0.16+9.0.x10.0.x10.1.x4.x/5.05.1+6.x/7.x8.0/8.18.2-13.x14-2122-2627+3.x4.04.1+5.x6.x7.x
1.0-1.1MCUTMC,CZMC,CZMCMC,UEMC,ERMC,ERMCMC,CZMC,CZMC,ERMCMCMCMC,ERMCMC
1.2ACUTAC,CZAC,CZACAC,UEAC,ERAC,ERACAC,CZAC,CZAC,ERACACACAC,ERACAC
2.0-2.1JFUTJF,CZJF,CZJFJF,UEJF,ERJF,ERJFJF,CZJF,CZJF,ERJFJFJFJF,ERJFJF
2.2JF,CZJFJF,UEJF,ERJF,ERJF,CZJF,ERJFJFJFJF,ERJFJF
2.3JF,ERJFJF
3.0JK*JK*JKJKJKJK
4.0JK*JKJK
  • MC: manually register EmptyToNullStringConverter over all place in <h:inputXxx converter>.
  • AC: automatically register EmptyToNullStringConverter on java.lang.String class.
  • UT: upgrade Tomcat to at least 6.0.17 as version 6.0.16 introduced the broken behavior on non-primitive number/decimal types and the VM argument was only added in 6.0.17.
  • CZ: add -Dorg.apache.el.parser.COERCE_TO_ZERO=false VM argument.
  • JF: add javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL=true context param.
  • JK: add jakarta.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL=true context param.
  • JK*: add jakarta.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL=true context param, and when using Mojarra, use a minimum of 3.0.4 and 4.0.1; for older versions you need a EmptyToNullStringELResolver.
  • ER: register EmptyToNullStringELResolver, or alternatively, just do UE.
  • UE: migrate/upgrade to Oracle EL implementation version 3.0.1-b05 or newer.
  • : this JSF version is not supported on this server anyway.

Thursday, October 22, 2015

Custom layout with h:selectOneRadio in JSF 2.2

Introduction

In HTML, particularly since HTML5 wherein the world is made more aware of semantic HTML, tables are discouraged for other purposes than tabular data. In JSF, the only components which by default render unnecessarily a HTML <table> this way are the <h:selectManyCheckbox> and <h:selectOneRadio>. If you loathe HTML tables for non-tabular data, then you could instead of the <h:selectManyCheckbox> render multiple <h:selectBooleanCheckbox>es in a loop based on an altered model. However, that isn't that trivial for a <h:selectOneRadio>. There was no way to achieve a custom layout without customizing the renderer and/or introducing a new component.

Ones who attempted to create multiple <h:selectOneRadio> components with each a single value using an <ui:repeat> or even <c:forEach>, likely already noticed it: the radio buttons are not grouped. When you select one radio button, then the others won't be unselected. The technical problem is, they don't share the same name attribute in generated HTML output. You could throw in some JavaScript to get the unselection to work anyway, but then there's the problem of setting the submitted value in the model. This works only if the last radio button of the group is being selected as they are in JSF side basically all processed individually instead of in a group and thus each radio button of the iteration would override the model value of the previous one.

Maddening. Fortunately a lot of component libraries offer a solution to this such as Tomahawk and PrimeFaces, which support a new layout attribute value of "spread" and an additional component to represent a single button (the <t:radio> resp. <p:radioButton>). Currently, there's ongoing discussion to adopt their approach in the standard JSF component set for the upcoming JSF 2.3.

Passthrough elements and attributes to rescue

But, since JSF 2.2, which came with new support for passthrough elements and attribtues, it's technically possible to override the name attribute when you set it as a passthrough attribute on a passthrough <input type="radio"> element. This opens up new possibilities. I created a quick attempt like below:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:jsf="http://xmlns.jcp.org/jsf"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:a="http://xmlns.jcp.org/jsf/passthrough">

...

<h:form id="form">
    <ul>
        <ui:repeat id="items" value="#{bean.items}" var="item">
            <li>
                <input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
                    value="#{item}" a:checked="#{item eq hiddenItem.value ? 'checked' : null}" />
                <h:outputLabel for="item" value="#{item}" />
            </li>
        </ui:repeat>
    </ul>

    <h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItem}"
        rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />

    <h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>

@Named
@RequestScoped
public class Bean {

    private List<String> items; // +getter
    private String selectedItem; // +getter+setter

    @PostConstruct
    public void init() {
        items = Arrays.asList("one", "two", "three");
    }

    public void submit() {
        System.out.println("Selected item: " + selectedItem);
    }

    // ...
}

It worked wonderfully! For the interested, here's how the generated HTML output look like:

<form id="form" name="form" method="post" action="/playground/test.xhtml" enctype="application/x-www-form-urlencoded">
    <input type="hidden" name="form" value="form" />

    <ul>
        <li>
            <input id="form:items:0:item" name="form:selectedItem" type="radio" value="one" />
            <label for="form:items:0:item">one</label>
        </li>
        <li>
            <input id="form:items:1:item" name="form:selectedItem" type="radio" value="two" />
            <label for="form:items:1:item">two</label>
        </li>
        <li>
            <input id="form:items:2:item" name="form:selectedItem" type="radio" value="three" />
            <label for="form:items:2:item">three</label>
        </li>
    </ul>

    <input id="form:submit" type="submit" name="form:submit" value="submit" />
    <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-1234567890123456789:1234567890123456789" autocomplete="off" />
</form>

This is easily to be turned into a tagfile or composite.

How exactly does this work?

In this little snippet there are quite a lot of tricks:

  1. The plain HTML <input type="radio"> element is being used instead of <h:selectOneRadio> because the latter stubbornly renders a HTML table element.
  2. The jsf:id attribute turns the HTML element into a passthrough element which will be backed by a fullworthy JSF component in the JSF component tree, which is in this particular case of HTML <input type="radio"> element a <h:inputText> (see also table 8-4 of the Java EE 7 tutorial on the subject). Its renderer doesn't render a table element. The type="radio" will just be passed through so it ultimately appears as a radio button.
  3. The a:name passthrough attribute overrides the autogenerated HTML name attribute which would otherwise have the iteration index of <ui:repeat> prepended. It's referring the client ID of the <h:inputHidden> further down in the code. So ultimately all radio buttons will have the same name attribute in HTML and JSF will set the submitted value there in the hidden input component.
  4. The a:checked passthrough attribute is being used instead of HTML checked attribute, because the HTML attribute is otherwise still rendered even though there's a null value. As this is a boolean attribute, only its presence would already force the "checked" state in HTML, so it has to be absent altogether. The passthrough attribute does that; it won't render the attribute at all when the value is null. It's referring the value of the <h:inputHidden> further down in the code.
  5. The <h:inputHidden> is being used to capture and set the model value as its name attribute is being taken over by the radio buttons.
  6. The binding attribute of the hidden input offers the radio button the opportunity to take over its client ID as the name of the radio button group. Do note that you do absolutely not need to bind it to a bean property. It's just being bound to the current Facelet scope so it's available elsewhere in the same facelet. For a in depth explanation on binding attribute, see also How does the 'binding' attribute work in JSF? When and how should it be used? In case you'd like to reuse the whole snippet in a composite component like <my:selectOneRadio>, then use binding="#{cc.hiddenItem}" with a UIInput hiddenItem property in the backing component instead to avoid it being shared between multiple instances of the composite component in the same view.
  7. The rendered attribute of the hidden input ensures that it does not in any way generate its HTML output during the JSF render response phase, but is still available in all other phases so it can properly process the submitted value, convert/validate it and update the model value. It isn't possible to let the passthrough component behind the radio button do that job, because JSF uses the component's client ID to extract the submitted value from the request parameter map, while the request parameter name is represented by name attribute, which has been changed to the one of the hidden input in this trick.

This all also works with <c:forEach> instead of <ui:repeat>, you only need to move the <h:inputHidden> to the top, before the <c:forEach>. Otherwise, the hidden input's client ID isn't available at the moment a:name is evaluated. And, you need to append #{loop.index} to the input ID and label for as in jsf:id="item_#{loop.index}" and for="item_#{loop.index}". For a in depth explanation on properly using JSTL in JSF, see also JSTL in JSF2 Facelets… makes sense?

Oh, I'm using xmlns:a="http://xmlns.jcp.org/jsf/passthrough" instead of xmlns:p as shown in the JSF documentation and Java EE 7 tutorial, for the simple reason that I'd like to keep PrimeFaces happy ;) The a stands here just for "attribute".

How about complex objects as item values?

You can just attach a Converter to the <h:inputHidden> the usual way. Ones who are familiar with OmniFaces only need to keep in mind that you can't use a SelectItemsConverter as there's no means of a <f:selectItems> anymore. Fortunately, conversion against the list of entities is still possible with help of the OmniFaces ListConverter like below:

<h:form id="form">
    <ul>
        <ui:repeat id="items" value="#{bean.items}" var="item">
            <li>
                <input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
                    value="#{item}" a:checked="#{item eq hiddenItem.value ? 'checked' : null}" />
                <h:outputLabel for="item" value="#{item.someProperty}" />
            </li>
        </ui:repeat>
    </ul>

    <h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItem}"
        rendered="#{facesContext.currentPhaseId.ordinal ne 6}">
        <o:converter converterId="omnifaces.ListConverter" list="#{bean.items}" />
    </h:inputHidden>

    <h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>

The same applies to validation. You can just add required="true" to the <h:inputHidden> the usual way.

Happy coding!

Wednesday, October 14, 2015

Logging duration of createView, buildView and renderView

Sometimes you'd like to measure how long JSF is taking to create, build and render the view. You can achieve this with a custom ViewDeclarationLanguage wrapper like below:

package com.example;

import java.io.IOException;
import java.util.logging.Logger;

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewDeclarationLanguageWrapper;

public class VdlLogger extends ViewDeclarationLanguageWrapper {

    private static final Logger logger = Logger.getLogger(VdlLoggerFactory.class.getName());

    private ViewDeclarationLanguage wrapped;

    public VdlLogger(ViewDeclarationLanguage wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public UIViewRoot createView(FacesContext context, String viewId) {
        long start = System.nanoTime();
        UIViewRoot view = super.createView(context, viewId);
        long end = System.nanoTime();
        logger.info(String.format("create %s: %.6fms", viewId, (end - start) / 1e6));
        return view;
    }

    @Override
    public void buildView(FacesContext context, UIViewRoot view) throws IOException {
        long start = System.nanoTime();
        super.buildView(context, view);
        long end = System.nanoTime();
        logger.info(String.format("build %s: %.6fms", view.getViewId(), (end - start) / 1e6));
    }

    @Override
    public void renderView(FacesContext context, UIViewRoot view) throws IOException {
        long start = System.nanoTime();
        super.renderView(context, view);
        long end = System.nanoTime();
        logger.info(String.format("render %s: %.6fms", view.getViewId(), (end - start) / 1e6));
    }

    @Override
    public ViewDeclarationLanguage getWrapped() {
        return wrapped;
    }

}

In order to get it to run, create the below factory:

package com.example;

import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewDeclarationLanguageFactory;

public class VdlLoggerFactory extends ViewDeclarationLanguageFactory {

    private ViewDeclarationLanguageFactory wrapped;

    public VdlLoggerFactory(ViewDeclarationLanguageFactory wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewDeclarationLanguage getViewDeclarationLanguage(String viewId) {
        return new VdlLogger(wrapped.getViewDeclarationLanguage(viewId));
    }

    @Override
    public ViewDeclarationLanguageFactory getWrapped() {
        return wrapped;
    }

}

And register it as below in faces-config.xml:

<factory>
    <view-declaration-language-factory>com.example.VdlLoggerFactory</view-declaration-language-factory>
</factory>

The createView() is the step of creating the concrete UIViewRoot instance based on <f:view> and <f:metadata> tags present in the view. When using Facelets (XHTML) as view, during this step all associated XHTML files will be parsed ("compiled") by the SAX parser and cached for a time as defined in the context parameter javax.faces.FACELETS_REFRESH_PERIOD. So it may happen that this step is one time relatively slow and the other time blazing fast. Use a value of -1 to cache them infinitely. When using Mojarra 2.2.11 or newer and the context parameter javax.faces.PROJECT_STAGE is already set to its default value of Production, then the refresh period already defaults to -1.

The buildView() is the step of populating the JSF component tree (the getChildren() of UIViewRoot) based on the view composition. During this step, all taghandlers (JSTL and friends) are executed and all EL expressions in those taghandlers and component's id and binding attributes are evaluated (for detail, see also JSTL in JSF2 Facelets... makes sense?). So if backing beans are constructed for first time during view build time and run some expensive business logic during e.g. @PostConstruct, then it may happen that this step is time consuming.

The renderView() is the step of generating the HTML output based on JSF component tree and the model, starting with UIViewRoot#encodeAll(). So if backing beans are constructed for first time during view render time and run some expensive business logic during e.g. @PostConstruct, then it may happen that this step is time consuming.

If a JSF page is loading slow in browser even though the above measurements run in milliseconds, then chances are big that the generated HTML DOM tree is simply big/bloated, and/or that the webbrowser is incapable of dealing with big HTML DOM trees, and/or that some JavaScript is inefficient on big HTML DOM trees. You'd then best profile the performance in the client side instead. For example, particularly Internet Explorer is slow with tables on big HTML DOM trees, and jQuery is slow with commaseparated selectors on big HTML DOM trees. Solutions would then be introducing filtering/pagination, and splitting into multiple selectors and passing each through a (callback) function, respectively.

Monday, October 5, 2015

First book dedicated to OmniFaces now available!

I'm proud to share with you that the first book dedicated to OmniFaces is finally available as of today!

Mastering OmniFaces is since October 5, 2015 available at Amazon.com! This book was created in cooperation with me and Arjan Tijms. From the beginning on, we worked together with Anghel Leonard and Constantin Alin to get this book ready, and we have reviewed it from top to bottom.

A little over 500 pages, this book goes into depth identifying general JSF problems and describing how OmniFaces has solved it, hereby uncovering several patterns and tricks. Basically, the working of every OmniFaces component, taghandler, converter, validator, and several handlers and listeners is break down in the book in a problem-to-solution approach. Reading this book is a true learning exercise as to exploiting JSF API, creating custom components, renderers, tagfiles and what not provided by JSF API in order to solve common problems.

A must read if you also like Mastering JSF 2.2 from the same author!

Monday, September 28, 2015

What Mojarra context parameters are available and what do they do?

Preface

Previously, on the GlassFish wiki page you could find an overview of all Mojarra specific web.xml context parameters (those starting with com.sun.faces), specifically here: wikis.oracle.com/display/GlassFish/JavaServerFacesRI. However, since Oracle changed one and other on the Internet again (wikis and blogs were shut down without proper redirecting and/or replacement), all links to that page suddenly became dead.

Fortunately, there's the web archive where I could recover the most recent snapshot and copy into this blog, so Mojarra users can find it by the usual search engines. Moreover, I've added a bunch of missing ones based on com.sun.faces.config.WebConfiguration class. The below listed available context parameters are clearly categorized and default values are valid as per Mojarra 2.2.11, whereby the deprecated and unused ones are already removed.

For standard JSF context parameters (those starting with javax.faces), head over to JSF specification chapter 11.1.3 or this Stack Overflow answer.

State saving related context parameters

Name Description Default Since
com.sun.faces.clientStateTimeout Maximum time, in seconds, that client state will be considered valid by the default StateManager/ResponseStateManager implementations. If the time between requests exceeds the configured time, a javax.faces.application.ViewExpiredException. will be thrown. It is important to note that if this feature is enabled, and client requests are recieved with view state produced from a previous version, the ViewExpiredException will be thrown immediately. NONE 1.2_05
com.sun.faces.clientStateWriteBufferSize The size, in bytes, of the buffer that is used to write client state. It should be noted, that the buffer used is split - half is for raw bytes, the other half is for the Base64 encoded characters of said bytes. So, for example, if the default, 8192, is used, then 4096 of that is used for the bytes, and the other 4096 is used for the Base64 encoded characters. 8192 1.2_01
com.sun.faces.compressViewState When true, the view is compressed after it is serialized and before base64 encoded. Works with client state saving only. As of 1.2_09, this option also impacts server side state saving when com.sun.faces.serializeServerState is set to true (this has a large impact of the size of the state in the session when using this option, at the expense of more CPU of course) true 1.2
com.sun.faces.disableClientStateEncryption Since Mojarra 2.2, the client state is by default always encrypted. When true, the client state encryption is disabled. See also com.sun.faces.ClientStateSavingPassword - recommendations for actual password? false 2.2
com.sun.faces.enableClientStateDebugging When true, the client state will as test be unserialized before writing and any serialization exceptions will be logged along with a debug path to the cause of the serialization error false ???
com.sun.faces.generateUniqueServerStateIds If true, generate random server view state ids. If false, create server view state ids sequentially. This resembles closely the JSF 1.x behavior, but this is more sensitive to CSRF. true 2.0
com.sun.faces.numberOfLogicalViews Defines the maximum number of serialized views stored in the session. Works with server state saving only (note the implementation unintentionally has swapped the meaning, as we cannot change this without breaking what people have become used to we have updated this here). See com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews for detail. 15 1.2
com.sun.faces.numberOfViewsInSession Definies the maximum number of serialized logical views per view. Works with server state saving only (note the implementation unintentionally has swapped the meaning, as we cannot change this without breaking what people have become used to we have updated this here) See com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews for detail. 15 1.2
com.sun.faces.serializeServerState If enabled the component state (not the tree) will be serialized before being stored in the session. This may be desirable for applications that may have issues with view state being sensitive to model changes after state saving which are reflected back in view state. This has since JSF 2.2 been replaced by javax.faces.SERIALIZE_SERVER_STATE. false 1.2_05
com.sun.faces.writeStateAtFormEnd Per the renderkit doc specification, the state information for the view will be written out prior to closing the form tag. However, it may be desirable to have the state information written out after the opening form tag. If this is the case, specifiy this parameter in the web.xml with a value of false. true 1.2_04

View building/rendering related context parameters

Name Description Default Since
com.sun.faces.allowTextChildren If true, allow children of h:inputText and h:outputText to be rendered. In 1.2, they would always be rendered before the value of tag. As of 2.0, children of UIInput and UIOutput components will not be rendered by the default renderer implementations. Set this option to true if this behavior is required, but note that doing so may cause issues when using Ajax. See issue 1154 for details. false 2.0.0
com.sun.faces.autoCompleteOffOnViewState If false, don't use autocomplete="off" on view state hidden input field. This attribute is by default always rendered in order to fix a Firefox related bug, but it is invalid on a hidden input field as per w3 HTML validator. So, when false, then beware of the Firefox bug which may trigger "unexpected" ViewExpiredException. See issue 1129 for details. true 2.0.0
com.sun.faces.compressJavaScript If true, then the JavaScript rendered by h:commandLink will be compressed to reduce the amount of whitespace transmitted in the response. If false then the JavaScript will be rendered to the client in a well formatted manner. true 1.2_02
com.sun.faces.disableUnicodeEscaping By default any characters above a certain range will be escaped as either an HTML entity or a decimal reference. This behavior is not always desirable. To allow more flexibility how content is rendered to a client, this option was introduced. Valid configuration values are: false, true, and auto with false being the default. When the option value is false, Mojarra will continue to escaped no matter the response encoding type. If the configuration value is true, Then no escaping will occur assuming that the response encoding can properly handle all characters. If the configuration option is auto then the response encoding will be checked. If the encoding is of the UTF family of encodings no unicode or html entity encoding will occur, however, if the response stream encoding is ISO-8859-1 then the ISO characters above a certain range will be encoded as HTML entities and any characters above that range will be written as decimal references. false 1.2_09
com.sun.faces.disableIdUniquenessCheck If true, then component ID uniqueness won't be checked if ProjectStage is Production to enhance performance. See issue 2414 for details. false 2.1.9
com.sun.faces.enabledJSStyleHiding If true, inlined JavaScript rendered by the HTML ResponseWriter implementation will be rendered so that the script is hidden from older browser implementations which does not recognize <script> elements. false 1.2_03
com.sun.faces.enableScriptsInAttributeValues If false, attribute values with javascript: or script: will not be rendered within attribute values to prevent potential XSS attacks. true 1.2_08
com.sun.faces.enableViewStateIdRendering If true, the view state hidden field will be rendered with both the id and name attributes having the value of "javax.faces.ViewState". This is what the spec requires, however, if there are multiple forms within a view and the response content-type is XHTML, the result will be XHTML that will fail validation due to multiple ID attributes with the same value: javax.faces.ViewState. Setting this parameter to false will result in the ID attribute not being rendered. Keep in mind however, that doing this may break integration with AJAX frameworks that get the state field via ID. See issue 433 for details. true 1.2_08
com.sun.faces.preferXHTML For the case where a browser supports XHTML and HTML without a quality. When enabled and this case occurs, then XHTML will be set as the content type. This setting is not recommended and should be only used to fix broken applications designed with XHTML output in mind instead of HTML(5) output. false 1.2
com.sun.faces.responseBufferSize This parameter specifies the size, in bytes, of the buffer that is used to write all generated JSP content excluding state. Note that this is ignored when Facelets is used. For Facelets, use javax.faces.FACELETS_BUFFER_SIZE instead. 1024 1.2_01

Resource handling related context parameters

Name Description Default Since
com.sun.faces.cacheResourceModificationTimestamp If true, cache the modification time of the resource and use the cached time to tell if the resource needs to be refreshed false 2.0.0
com.sun.faces.compressableMimeTypes Specify mime types that should be gzip compressed. Mime types can be specified by their exact name (i.e. text/css) or a wildcard can be used after the slash (i.e. text/*). The resource will not be compressed on each request, instead when building the cache, the resource will be compressed to a temporary directory and those bytes will be served instead. NONE 2.0.0
com.sun.faces.defaultResourceMaxAge This affects the value of the Expires response header that will be sent for a resource. The logic is basically Date.getTime() + valueOf(defaultResourceManxAge). Increase this value to increase the amount of time that a Resource is valid. The value is in milliseconds (so the default value of 604800000 is 7 days). 604800000 2.0.0
com.sun.faces.enableFaceletsResourceResolverCompositeComponents See issue 3684 for details. false 2.2.10
com.sun.faces.enableMissingResourceLibraryDetection If enabled, the runtime will check for the existence of a resource library before checking for the resource itself. If not found, an appropriate error message will be included in the log and in the view if ProjectStage is Development. false 2.1
com.sun.faces.resourceUpdateCheckPeriod When javax.faces.PROJECT_STAGE is Production, UnitTest, or SystemTest resource paths will be cached to reduce the overhead of resource path compuation. By default, updates (i.e. new files, new directories, new versions, etc.) will be checked for every 5 minutes. If a change is detected, the cache will be cleared and rebuilt. If the value of this option is -1, the cache will never be cleared and new resources will not be picked up. The value is in minutes. 5 2.0.0

Clustering related context parameters

Name Description Default Since
com.sun.faces.enableAgressiveSessionDirtying If true, makes it so every session attribute is touched in for every request through the lifecycle. This makes it much harder to make mistakes that cause session replication to fail. false 2.0.0
com.sun.faces.enableDistributable If true, signal JSF that the application is deployed to a clustered environment, so that session dirtying will be explicitly performed, hereby forcing session replication. This is automatically true when <distributable /> entry is present in web.xml. false 2.???

Configuration related context parameters

Name Description Default Since
com.sun.faces.annotationScanPackages The value of this context init parameter is a whitespace separated list of values that control which class packages are scanned for javax.faces annotations. To restrict which jars/packages are scanned, use the following entry format: jar:<jar name>:<comma separated list of packages> So an example would be: jar:a.jar:com.acme.package1,com.acme.package2. NONE 2.0.0
com.sun.faces.displayConfiguration If true then all web configuration information (context initialization parameters and environment entries) will be written to the log. This is useful during development to confirm your application is configured as expected. false 1.2_01
com.sun.faces.enableCoreTagLibValidator When true, enable validation of standard Core TagLibs, at the expense of a slightly slower start time. false 2.0
com.sun.faces.enableHtmlTagLibValidator When true, enable validation of standard Html TagLibs, at the expense of a slightly slower start time. false 2.0
com.sun.faces.enableLazyBeanValidation When true, managed beans will be validated when first created. If false, managed beans will be validated when the application is started, at the expense of a slightly slower start time. true 1.2_05
com.sun.faces.enableThreading When enabled, the runtime initialization and default ResourceHandler implementation will use threads to perform their functions. Set this value to false if threads aren't desired (as in the case of running within the Google Application Engine). Note that when this option is disabled, the ResourceHandler will not pick up new versions of resources when ProjectStage is development. See issue 2385 for details. true 1.2_13
com.sun.faces.forceLoadConfiguration There is a chance for an NPE in com.sun.faces.application.WebappLifecycleListener with some configurations. Take for example, installing JSF in a container such that JSF will be available to all web applications. The NPE will occur for an application that doesn't have the FacesServlet defined within its web.xml. The workaround for this issue is, within the global web.xml for the container (JBoss and Tomcat both have one) add either a FacesServlet definition (no mapping) or add the context init parameter, com.sun.faces.forceLoadConfiguration, with a value of true. See issue 670 for details. false 1.2_08
com.sun.faces.validateXml When true, enable validation of faces-config.xml files, at the expense of a slightly slower start time. false 1.2
com.sun.faces.verifyObjects When true, JSF makes during startup sure all that registered managed beans components, validators, etc can be instantiated by the runtime, at the expense of a slightly slower start time. false 1.2

Miscellaneous context parameters

Name Description Default Since
com.sun.faces.enableTransitionTimeNoOpFlash If true, the act of calling getExternalContext().getFlash() on the FacesContext at startup or shutdown time will cause a no-op implementation of the Flash to be returned. This should prevent startup/shutdown exceptions caused by invalid/stale flash cookies. See "bugdb 17024459" for details. false 2.2.8
com.sun.faces.expressionFactory This parameter specifies a class that implements the ExpressionFactory. NONE ???
com.sun.faces.forceAlwaysWriteFlashCookie If true, then the flash cookie will always be written, regardless of whether or not the flash has data. This should prevent problems on multiple successive redirects. See issue 3735 for details. false 2.1.20
com.sun.faces.injectionProvider This parameter specifies a class that implements the InjectionProvider. NONE 1.2_01
com.sun.faces.namespaceParameters If true, then view state hidden field is namespaced according to NamingContainer rules. See issue 3031 for details. false 2.1.29
com.sun.faces.registerConverterPropertyEditors If true, allow EL Coercion to use JSF Custom converters. false 2.0
com.sun.faces.sendPoweredByHeader The servlet specification defines an optional header that can be sent by a container to communicate the version of the JSP/Servlet the response was generated by. If this is enabled, then X-Powered-By=JSF/2.2 header is included in all responses. false 1.2
com.sun.faces.serializationProvider This parameter specifies a class that implements the SerializationProvider SPI. This implementation represents a hook the JSF implementation will use in order to allow the use of alternate Serialization implementations. NONE 1.2_01

JSF 1.x backwards compatibility related context parameters

Never use them in JSF 2.x.

Name Description Default Since
com.sun.faces.enabledLoadBundle11Compatibility When this flag is true, The f:loadBundle will behave as it did in JSF 1.1. If you've migrated from 1.1 to 1.2, and haven't had any issues with f:loadBundle it is safe to ignore this option. See issue 577 for details. false 1.2_05
com.sun.faces.enableRestoreView11Compatibility When this flag is true, The restore view phase will behave as it did in JSF 1.1. If you've migrated from 1.1 to 1.2, and haven't had any issues with handling ViewExpiredException it is safe to ignore this option. false 1.2_???

Wednesday, June 3, 2015

OmniFaces 2.1 release!

OmniFaces 2.1 has been released!

This version brings relatively more enhancements and fixes than really new things. Among others, the code base has been thoroughly tested with serialized view state turned on (as in javax.faces.SERIALIZE_SERVER_STATE=true, which is new since JSF 2.2) and fixed where needed. And, the code base was inspected with Sonar and as much as possible warnings were fixed, hereby even further improving the quality of the code base. Also, the Javadocs have been reviewed and improved where possible. Further, all resource handlers (combined resource handler, unmapped resource handler, CDN resource handler and graphic image resource handler) have been reworked to be better compatible with other JSF component libraries (like PrimeFaces and PrimeFaces Extensions, which were using only one or two of the three available createResource() methods). Finally, the CDI detection has been altered to be better compatible with Tomcat + OpenWebBeans.

As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 2.1. The top three additions are the brand new @Cookie annotation, improved SVG support by <o:graphicImage> component and support for server side caching in CombinedResourceHandler.

Oh, we finally got a new logo too. The old logo, basically a buckminsterfullerene in pentagon projection, was not really scalable. Now just a cyclopentane is extracted from it. You can see it on the header of the showcase. For fans, the full logo is also available in SVG format here.

Installation

Non-Maven users: download OmniFaces 2.1 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.1</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.1</version>
</dependency>

For users who don't want to use CDI, there's the 1.11 with all 2.1 enhancements and fixes (but no brand new additions!).

Inject cookies via CDI

This is actually a really trivial addition. Just a new annotation and only 3 lines of hard work in the producer class.

Normally, with JSF managed beans as managed by @ManagedBean, you would be able to inject the value of a cookie with name "foo" via @ManagedProperty as below:

@ManagedProperty("#{cookie.foo.value}")
private String foo;

However, this possibility has disappeared when switching to CDI managed beans as managed by @Named. OmniFaces therefore injects a @Cookie in its CDI assortiment.

@Inject @Cookie
private String foo;

Here, the property name foo will by default be used as cookie name, like as how @Param already works.

Improved SVG support in <o:graphicImage>

The <o:graphicImage>, new since OmniFaces 2.0, has been enhanced to better support SVG images, including SVG view mode support via fragment attribute (i.e. being able to show only specific sections of a SVG image).

<o:graphicImage name="some.svg" fragment="svgView(viewBox(0,50,200,200))" />

Server side caching in CombinedResourceHandler

Thanks to inspiration and contributions of Stephan Rauh (from beyondjava.net, AngularFaces and BootsFaces), it's now possible to let CombinedResourceHandler cache the combined resources in server memory without the need to read/combine them from source again and again.

To turn on it, just set the org.omnifaces.COMBINED_RESOURCE_HANDLER_CACHE_TTL context param with a value bigger than 0, indicating the "cache time to live" in seconds (i.e. cache will be purged after X seconds). Here's an example assuming you'd like to cache them for 1 day (86400 seconds):

<context-param>
    <param-name>org.omnifaces.COMBINED_RESOURCE_HANDLER_CACHE_TTL</param-name>
    <param-value>86400</param-value>
</context-param>

See also Stephan's blog on the subject.

Maven download stats

Here are the Maven download stats:

  • November 2014: 4444
  • December 2014: 5081
  • January 2015: 5837
  • February 2015: 4985
  • March 2015: 5303
  • April 2015: 5749
  • May 2015: 5995

Below is the version pie of May 2015:

Surprising how many <=1.8.1 users are still there. To those who can't upgrade to 2.1 or 1.11, I'd like to point out that 1.8.3 contains an important memory leak fix in case you're using UnmappedResourceHandler while having composite components on the pages. So, if you can't upgrade to 2.1 or 1.11, then at least upgrade to 1.8.3.