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 JSF issue 3071, 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+4.x/5.05.1-26.x/7.x8.0-18.2/9.0+3.x4.04.1+
1.0-1.1MCUTMC,CZMC,CZMCMC,UEMC,ERMCMC,CZMC,CZMC,ERMCMCMC,ERMC
1.2ACUTAC,CZAC,CZACAC,UEAC,ERACAC,CZAC,CZAC,ERACACAC,ERAC
2.0-2.1JFUTJF,CZJF,CZJFJF,UEJF,ERJFJF,CZJF,CZJF,ERJFJFJF,ERJF
2.2JF,CZJFJF,UEJF,ERJF,CZJF,ERJFJFJF,ERJF
  • 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.
  • 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 bean.selectedItem ? '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.
  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 bean.selectedItem ? '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!