Thursday, September 15, 2016

OmniFaces 2.5.1 released with o:inputFile, @GraphicImageBean and MultiViews

Update 21 sep 2016: Oops, I made a mistake in <o:validateBean> class level validation. OmniFaces 2.5.1 has therefore been released!

OmniFaces 2.5 2.5.1 has been released!

The major visible changes are the <o:inputFile> component which extends and improves <h:inputFile>, the @GraphicImageBean annotation which can be used to mark a bean as a public and dedicated image service, and adoption of "MultiViews" in FacesViews.

The major invisible changes are removal of CDI 1.0 targeted workarounds (including the deprecation of org.omnifaces.config.BeanManager enum), intergration of Travis CI and addition of several integration tests based on JUnit/Arquillian/Selenium/Graphene. As of now, @ViewScoped, @Eager, FullAjaxExceptionHandler, FacesViews and CombinedResourceHandler have IT cases which are run against WildFly 10.0.0, TomEE 7.0.1 and Payara 4.1.1.163.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 2.5.1? list in showcase.

Installation

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

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

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

For CDI-less users, there will be no version 1.15. There are no major 2.x bugfixes anymore which should also end up in 1.1x. Therefore it isn't worth the effort to merge 2.x back to 1.1x again. And, due to major changes in 2.x project structure, merging 2.x branch back to 1.1x branch has become a tedious task which isn't worth the effort anymore. OmniFaces 1.1x is now in maintenance mode. The latest 1.1x version is still 1.14. Of course there may be bugfix releases in the future, but so far there are no major 2.4-2.5 bugs which also affect 1.14, so there's no 1.14.1 yet as of now.

If you need some arguments to move to CDI:

  • Upcoming JSF 2.3 will require a CDI dependency
  • JSF own @ManagedBean and @XxxScope annotations are marked deprecated in JSF 2.3 (already as of 2.3-m06)
  • Spring 3.x already supports javax.inject API from CDI, so Spring services (which are often used to substitute the lack of EJB support in Tomcat) can seamlessly be injected in CDI managed beans.

Multi file upload with builtin file type and size validation

The relatively new <h:inputFile> component, which was introduced only in JSF 2.2, has been extended and enhanced into new <o:inputFile> component with support for multiple file selection and folder selection via new HTML5 multiple and directory attributes.

<h:form enctype="multipart/form-data">
    <o:inputFile value="#{bean.files}" multiple="true" />
    <h:commandButton value="Upload" action="#{bean.upload}" />
</h:form>
private List<Part> files; // +getter+setter

public void upload() {
    if (files != null) {
        for (Part file : files) {
            String name = Servlets.getSubmittedFileName(file);
            String type = file.getContentType();
            long size = file.getSize();
            InputStream content = file.getInputStream();
            // ...
        }
    }
}

Also, media type filtering via new HTML5 accept attribute has been added, along with built-in server side validation based on file extension. The below example will in modern browsers only show image files in file browse dialog.

<o:inputFile value="#{bean.file}" accept="image/*" />

And, file size validation via a custom maxsize attribute has been added which runs in both client and server side. The below example sets the file size limit to 10MiB.

<o:inputFile value="#{bean.file}" maxsize="#{10 * 1024 * 1024}" />

In client side JavaScript, the new HTML5 File API will be used to check the file size and a special ajax request will be sent to trigger a JSF faces message. This all will take place without that the whole file needs to be sent to the server side. Instant feedback thus. As fallback for older non-HTML5 clients and as safeguard against spoofed requests, the server side will validate the file size once again after the file is arrived over there.

Independent image service

The <o:graphicImage> component, which was added in OmniFaces 2.0, had a builtin security restriction to prevent users from being able to invoke arbitrary bean methods by manipulating the GET request URL path. One of the consequences of this security restriction is that images served by <o:graphicImage> component aren't consistently hotlinkable. It works only if the page referencing the <o:graphicImage> has been invoked at least once in application's lifetime.

Therefore a new managed bean annotation has been introduced which should mark a bean as a dedicated and public graphic image service. When putting @GraphicImageBean annotation on the bean, then all of bean's public methods which return either InputStream or byte[] will become accessible by a direct GET request URL.

import org.omnifaces.cdi.GraphicImageBean;

@GraphicImageBean
public class Images {

    @Inject
    private ImageService service;

    public byte[] get(Long id) {
        return service.getContent(id);
    }

}

This also allowed the creation of a bunch of new EL functions such as #{of:graphicImageURL(...)} which merely print the graphic image URL without the need for a whole <o:graphicImage> component, which finally makes the below case possible:

<a href="#{of:graphicImageURL('images.full(product.imageId)')}">
    <o:graphicImage value="#{images.thumb(product.imageId)}" />
</a>
import org.omnifaces.cdi.GraphicImageBean;

@GraphicImageBean
public class Images {

    @Inject
    private ImageService service;

    public byte[] full(Long id) {
        return service.getFullContent(id);
    }

    public byte[] thumb(Long id) {
        return service.getThumbContent(id);
    }

}

MultiViews

Ones who are familiar with Apache HTTPD+PHP world are probably aware of the age-old but very useful MultiViews feature of Apache HTTPD which allowed usage of clean URLs such as http://example.com/foo/bar/baz which searches for respectively /foo/bar/baz.php, /foo/bar.php and /foo.php until the first one is found, and then passes the rest of the URL as PATH_INFO variable.

The OmniFaces-builtin extensionless URL feature known as FacesViews has been enhanced to support exactly this MultiViews feature too. It's a matter of adding /* suffix to the org.omnifaces.FACES_VIEWS_SCAN_PATHS context parameter value. The below context parameter makes all files in the current webapp available via extensionless URLs with MultiViews support.

<context-param>
    <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
    <param-value>/*.xhtml/*</param-value>
</context-param>

With above configuration, an URL such as http://example.com/contextpath/foo/bar/baz will search for /foo/bar/baz.xhtml, /foo/bar.xhtml and /foo.xhtml in this order until the first one is found and then invoke it. The existing @Param annotation has been enhanced to inject path parameters by their index. Assuming that you've a /foo.xhtml, then the path parameters bar and baz can be injected in the managed bean associated with /foo.xhtml as below:

@Inject @Param(pathIndex=0)
private String bar;

@Inject @Param(pathIndex=1)
private String baz;

Maven download stats

Here are the Maven download stats after previous release:

  • July 2016: 8560
  • August 2016: 8660
  • September 2016: 9341

Monday, July 4, 2016

Integrating Tomcat 8.5.x and TomEE 7.x in Eclipse

Are you also seeing below error when trying to integrate Tomcat 8.5.x or TomEE 7.x using Tomcat v8.0 Server plugin in Eclipse?

For searchbots, the error says The Apache Tomcat installation at this directory is version 8.5.3. A Tomcat 8.0 installation is expected. This error even occurs in the current Eclipse Neon release. Perhaps they'll fix it in Neon SR1 by adding a new Tomcat v8.5 Server plugin. But for now and for older versions a workaround is needed.

The Eclipse built-in Tomcat server plugin basically detects the server version based on server.info property of org/apache/catalina/util/ServerInfo.properties file of Tomcat's /lib/catalina.jar file which looks like below in case of Tomcat 8.5.3:

server.info=Apache Tomcat/8.5.3
server.number=8.5.3.0
server.built=Jun 9 2016 11:16:29 UTC

All we need to do is to edit the version in server.info property to start with 8.0. Any ZIP or JAR aware tool should do it to edit it on the fly. I'm myself using WinRAR for the job.

server.info=Apache Tomcat/8.0.8.5.3
server.number=8.5.3.0
server.built=Jun 9 2016 11:16:29 UTC

Finally, it works.

INFO: Starting Servlet Engine: Apache Tomcat/8.0.8.5.3

The same procedure applies to TomEE 7.x which is based on Tomcat 8.5.x.

The difference between Tomcat 8.0 and 8.5 is the integration of Jaspic which is the first step towards standardizing Java EE authentication as part of JSR375. You probably already know that the ways to configure Java EE container managed authentication are totally scattered across different servers, making third party libraries such as Shiro and Spring Security more attractive. Each server needed its own way of configuring "realms" or "identity stores" to manage the database of users, passwords and roles. This will with JSR375 be unified using annotations and CDI provided via standard javax.security.* API. See also a.o. the question Java EE authentication: how to capture login event?

Friday, July 1, 2016

OmniFaces 2.4 revives c:url, improves @Param and simplifies logging

OmniFaces 2.4 has been released!

This version has relatively few new additions, only one brand new tag <o:url> which revives good 'ol JSTL <c:url> for Facelets, and a few utility methods/functions. There are several enhancements and bugfixes in existing stuff, such as the @Param finally supporting injecting multi-valued parameters such as foo=bar1&foo=bar2 into a String[] or List<String> and injecting into primitive type fields. Those few utility methods and enhancements must further simplify logging of JSF (ajax) actions, exceptions and JavaScript errors.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 2.4? list in showcase.

Installation

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

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

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

For users who don't want to use CDI, there's the CDI-less 1.14 with all 2.4 enhancements and fixes (but no brand new 2.x additions nor the o:url!).

Good ol' JSTL <c:url> revived

Sometimes there's need to access and/or print the current request URL "plain vanilla" in a JSF page. Common use cases are to prepopulate a ?from= request parameter for the login form so it can figure out where to redirect back after a successful login, and filling the HTML5 link relations referring the current or another page on the same site. Previously, this could to some extent (no control over domains) be done with JSTL ><c:url> which was removed in Facelets.

This has now been revived in OmniFaces 2.4 as <o:url> and even further enhanced. It supports setting the (relative) domain via domain attribute and it supports the same includeViewParams, includeRequestParams and useRequestURI attributes as <o:form>. The <o:url> also supports the var attribute allowing to set the value as an EL variable which can in turn be used in an EL expression elsewhere in the page.

Here's an example:

<o:url var="currentURL" />
...
<h:head>
    ...
    <link rel="canonical" href="#{currentURL}" />
    ...
</h:head>
<h:body>
    ...
    <h:link value="Login" outcome="login">
        <f:param name="from" value="#{currentURL}" />
    </h:link>
    ...
</h:body>

@Param now supports multiple values and primitives

The CDI @Param annotation has in OmniFaces 2.4 been further improved to also support injecting multi-valued request parameters such as foo=value1&foo=value2&foo=value3 into an array or List.

@Inject @Param(name="foo")
private List<String> foos;

@Inject @Param(name="bar")
private String[] bars;

Conversion and validation is also supported on multi-valued parameters like as already supported on single-valued parameters. On multi-valued parameters, JSF native conversion and validation will run on each submitted value. Bean Validation, if any, will however be performed on the entire List or array property and not on each individual item.

Furthermore, the @Param now also supports injecting into a primitive field. Previously this wasn't possible because CDI would attempt to inject an empty value as null into the field, which would only throw an exception (no autoboxing magic there in reflection). The @Param will now automatically take care about injecting the right default value of the primitive field in case the value is absent or empty.

Logging made easier

OmniFaces 2.4 comes with several utilities and enhancements which should further simplify logging of (ajax) actions, exceptions and JavaScript errors.

First, the Components got two new utility methods, one to find out the source component of the (ajax) form submit and another to extract all actions and listeners from it as a list of EL expression strings such as #{bean.action}, #{bean.actionListener}, #{bean.ajaxListener}.

UIComponent actionSource = Components.getCurrentActionSource();
List<String> actionExpressions = Components.getActionExpressionsAndListeners(actionSource);
// ...

This also works for e.g. <h:inputText><f:ajax listener="#{bean.listener}">. You can find a concrete example as PhaseListener in my answer to How can I log method expressions of JSF ajax requests.

Second, the FullAjaxExceptionHandler got a new logException overloaded method with a LogReason enum argument which should help in determining when exactly the exception has been caught and whether it was successfully handled or not. For example, if handling has failed because the response was already committed, or the error page itself contained an error, then you could take additional action by sending an email to site admin.

@Override
protected void logException(FacesContext context, Throwable exception, String location, LogReason reason) {
    switch(reason) {
        case RENDER_EXCEPTION_UNHANDLED:
        case ERROR_PAGE_ERROR:
            yourMethodToSendAnEmailToAdmin(exception, reason);
            break;
        default:
            super.logException(context, exception, location, reason);
    }
}

Third, the CombinedResourceHandler and <o:deferredScript> have been altered to always render crossorigin="anonymous" attribute to the generated <script> element. This will basically enable JavaScript error logging via window.onerror on scripts possibly served via a CDN. Such a logger can look like below, with little help of jQuery and a servlet:

$(window).on("error", function(message, source, line, column, error) {
    try {
        $.post("/script-error", $.param({
            url: window.location.href,
            client: navigator.userAgent,
            message: message,
            source: source,
            line: line,
            column: column,
            error: error ? error.stack : null
        }));
    }
    catch(e) {
        // Ignore.
    }
});
@WebServlet("/script-error")
public class ScriptErrorServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String url = request.getParameter("url");
        String client = request.getParameter("client");
        String message = request.getParameter("message");
        String source = request.getParameter("source");
        String line = request.getParameter("line");
        String column = request.getParameter("column");
        String error = request.getParameter("error");
        // ...
    }

}

Noted should be that the detail of the information being sent depends on the client used. Chrome for example is very kind to include the full stack trace

More OmniFaces in upcoming JSF 2.3

Besides the new JSF 2.3 <f:websocket>, which is largely based on <o:socket> introduced in previous OmniFaces version, more OmniFaces solutions have been integrated into upcoming JSF 2.3 the last months. The FixViewState is now in standard jsf.js as per spec issue 790, the <o:commandScript> is integrated as new JSF 2.3 <h:commandScript> as per spec issue 613, and the <o:importConstants> is integrated as new JSF 2.3 <f:importConstants> as per spec issue 1424 (which should be declared inside <f:metadata>). Those OmniFaces artifacts will be deprecated as per upcoming OmniFaces 3.0 for JSF 2.3.

Maven download stats

Here are the Maven download stats after previous release:

  • April 2016: 8050
  • May 2016: 8590
  • June 2016: 8560

Friday, April 1, 2016

OmniFaces 2.3 brings JSF and WebSockets together!

OmniFaces 2.3 has been released!

This version brings a fairly important new tag to the world: <o:socket> for WebSocket based server-side push. Not only because it integrates JSR-356 WebSocket API seamlessly into JSF API with help of the CDI API, but also because it is the base for JSF 2.3's upcoming new <f:websocket> tag. Basically, OmniFaces offers you the opportunity to benefit of it long before JSF 2.3 itself is released (not before 2017). Afterwards, any migration should be a piece of cake.

Next to the <o:socket> and a lot of utility methods and functions, there's another new tag <o:skipValidators> and a further improved @ViewScoped which now also really physically destroys the associated JSF view state during unload (and thus not only the view scoped bean itself as it did before).

Note: whilst perhaps confusing, OmniFaces version 2.3 is still targeted at JSF 2.2. All OmniFaces 2.x versions are targeted at JSF 2.2. Only the future OmniFaces 3.x will be targeted at JSF 2.3.

Installation

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

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

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

For users who don't want to use CDI, there's the CDI-less 1.13 with all 2.3 enhancements and fixes (but no brand new 2.x additions nor the o:socket!).

Easy scoped and user-targeted push

Besides "easy to use", an important design decision of <o:socket> was being able to send push messages from the server side to a specific scope (application, session or view) and/or to a specific user.

Example 1

The below application scoped socket example will receive the same push message from the application in all pages having the same socket channel.

<o:socket channel="top10" onmessage="top10Listener" />
function top10Listener(top10) {
    console.log(top10); // Do your thing with HTML DOM.
}
@Startup @Singleton
@ConcurrencyManagement(BEAN)
public class SomeTop10Manager {

    private List<Some> top10;

    @PersistenceContext
    private EntityManager entityManager;

    @Inject
    private BeanManager beanManager;

    @PostConstruct
    @Schedule(hour="*", minute="*/1", second="0", persistent=false)
    public void load() {
        List<Some> top10 = entityManager.createNamedQuery("Some.top10", Some.class).getResultList();

        if (!top10.equals(this.top10)) {
            this.top10 = top10;
            beanManager.fireEvent(new SomeTop10Event(top10));
        }
    }

    public List<Some> list() {
        return top10;
    }

}
@ApplicationScoped
public class SomeTop10Observer {

    @Inject @Push
    private PushContext top10;

    public void onTop10Change(@Observes SomeTop10Event event) {
        top10.send(event.getTop10());
    }

}

Example 2

The below user-targeted (implicitly session scoped) socket example will receive the same push message in all pages having the same socket channel and user. This example assumes that the logged-in user is available via an EL variable as #{user} which has an id property representing its identifier.

<o:socket channel="chat" user="#{user.id}" onmessage="chatListener" />
function chatListener(message) {
    console.log(message); // Do your thing with HTML DOM.
}
<h:form>
    <p:inputText value="#{someChatBacking.message}" required="true" />
    <p:selectOneMenu value="#{someChatBacking.recipientUser}" required="true">
        <f:selectItems value="#{someSocketObserver.onlineUsers}" var="user" itemLabel="#{user.name}" />
    </p:selectOneMenu>
    <p:commandButton value="send" action="#{someChatBacking.send}" />
</h:form>
@Named
@RequestScoped
public class SomeChatBacking {

    @Inject @Push
    private PushContext chat;

    private String message;
    private User recipientUser;

    public void send() {
        Set<Future<Void>> sent = chat.send(message, recipientUser.getId());

        // TIPS:
        // sent.size() represents amount of open web sockets at send time.
        // future.get() will return null if message is successfully delivered.
        // future.get() will throw ExecutionException if message delivery failed.
        // You can observe @Opened/@Closed websockets with CDI. See o:socket showcase/javadoc.
    }

    // ...
}

Example 3

The below conditionally connected view scoped socket example will receive the push message as result of a user-initiated asynchronous task only in the current view.

<h:form>
    <p:commandButton widgetVar="asyncCommand" value="submit"
        action="#{someTaskBacking.start}" onclick="startAsync()" />
</h:form>
<o:socket channel="async" scope="view" onmessage="asyncListener" connected="false" />
function startAsync() {
    PF("asyncCommand").disable();
    OmniFaces.Push.open("async");
}
function asyncListener(result) {
    OmniFaces.Push.close("async");
    console.log(result); // Do your thing with HTML DOM.
    PF("asyncCommand").enable();
}
@Named
@RequestScoped
public class SomeTaskBacking {

    @Inject
    private SomeTaskService service;

    @Inject @Push
    private PushContext async;

    public void start() {
        service.someLongRunningAsyncTask(result -> async.send(result));
    }

}
@Stateless
public class SomeTaskService {

    @Asynchronous
    public void someLongRunningAsyncTask(Consumer<Object> callback) {
        // ...
        // ... (some long process)
        // ...

        callback.accept(result);
    }

}

Complex UI updates

The only disadvantage from the JSF perspective is that the target JSF view state isn't anywhere available at the moment the push message is being sent. So, no partial rendering based on JSF view could be performed. That's why it isn't possible to perform complex UI updates as easily as with JSF ajax. If you're not that fluent with e.g. jQuery, then you could combine <o:socket> with <o:commandScript> which in turn uses ajax to obtain the desired rendering based on the message.

<o:socket ... onmessage="commandScriptName" />
<o:commandScript name="commandScriptName" action="#{bean.pushed}" render="result" />

If you pass a Map<String, V> or a JavaBean as push message object, then all entries/properties will transparently be available as request parameters in the command script action method.

Session/view expired

Oh, if you ever wanted to immediately warn the user about an expired session, then you could now use onclose of a session scoped <o:socket> for that. If it returns code 1000 while you didn't manually close the socket, then it means that session has been expired on the server side. How useful! This also works on view scoped sockets (but indicates "View expired" instead and should actually be a very rare case if it isn't caused by an expired session).

<o:socket ... scope="session" onclose="sessionCloseListener" />
function sessionCloseListener(code) {
    if (code == 1000) {
        alert("Session has expired! Page will be reloaded.");
        window.location.reload(true); // Or just go to some login/home page.
    }
}

For an elaborate documentation and several live demos, checkout the <o:socket> showcase.

Skip validators

Contributed by Michele Mariotti, <o:skipValidators> allows developers to entirely skip JSF validation (required, <f:validateXxx>, custom validators, etc) when executing a specific UICommand or ClientBehaviorHolder action.

For example, when adding a new row to the data table, you'd like to not immediately validate all empty rows.

<h:form>
    <h:dataTable value="#{bean.items}" var="item">
        <h:column>
            <h:inputText value="#{item.value}" required="true" />
        </h:column>
    </h:dataTable>
    <h:commandButton value="add new row" action="#{bean.add}">
        <o:skipValidators />
    </h:commandButton>
    <h:commandButton value="save all data" action="#{bean.save}" />
    <h:messages />
</h:form>

Under the covers, it will basically during PreValidateEvent remove and remember all validators and re-attach them during the PostValidateEvent.

Only converters will still run. Conversion failures can still be suppressed with help of the existing <o:ignoreValidationFailed>.

Further improved @ViewScoped

The OmniFaces CDI @ViewScoped annotation has been further improved to also physically destroy the associated JSF view state in session during an unload event. I.e. during an unload it does not only destroy view scoped beans in order to eagerly free memory, but it will now also immediately push out unused JSF view states from the session. This is important in order to prevent "older" JSF view states from expiring too soon as they are basically held in a LRU map.

A good use case is described in OmniFaces issue 208:

  1. Set number of physical views to 3 (in Mojarra, use com.sun.faces.numberOfLogicalViews context param and in MyFaces use org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION context param).
  2. Create a page which immediately initializes an OmniFaces @ViewScoped bean during load, and has a form which submits to it. For clarity, add @PostConstruct and @PreDestroy methods which log a debug line.
  3. Open this page in a tab and keep it open all time.
  4. Open the same page in another tab and then immediately close this tab.
  5. Open the same page in another tab and then immediately close this tab.
  6. Open the same page in another tab and then immediately close this tab.
  7. Submit the form in the first tab.

Previously, this would have failed with ViewExpiredException, but since OmniFaces 2.3 not anymore. Currently, a fairly convoluted hack has to be used, as the way to explicitly destroy a physical view is implementation specific. Both MyFaces and Mojarra required a different way. Currently, it only works when partial state saving is kept turned on (as by default). Those who still use full state saving should better ask themselves if it's still fruitful to keep using full state saving in JSF 2.2. During JSF 2.0/2.1 there have been reasons to keep using full state saving, but all of them are workarounds for partial state saving bugs which have long been fixed in JSF 2.2.

While at it I noticed in MyFaces org.apache.myfaces.spi.ViewScopeProvider source code a TODO comment saying that something should be provided to cleanup the session when a view is discarded. This is exactly what we want! It would be even nicer if there was a standard API which explicitly destroys the view state. Perhaps a new removeState or destroyState method in ResponseStateManager. That would make things simpler, also for the JSF implementation itself.

Maven download stats

Here are the Maven download stats after previous release:

  • November 2015: 5906
  • December 2015: 6068
  • January 2016: 6135
  • February 2016: 6635
  • March 2016: 7265

Below is the version pie of March 2016:

Monday, February 8, 2016

Recursive tree of composite components

While trying to create kind of tree structure (menu, discussion, comments, etc) with Facelets and composite components, you'll probably ever have attempted to recursively nest a composite component in itself like the below <my:tree node="#{bean.tree}"> composite component example.

<cc:interface>
    <cc:attribute name="node" type="com.example.SomeTreeModel" />
</cc:interface>
<cc:implementation>
    <c:if test="#{not empty cc.attrs.node.children}">
        <ul>
            <c:forEach items="#{cc.attrs.node.children}" var="node">
                <li>
                    #{node.data}
                    <my:tree node="#{node}" />
                </li>
            </c:forEach>
        </ul>
    </c:if>
</cc:implementation>

And then you was surprised to see a stack overflow error (line numbers match Mojarra 2.2.12) — not surprisingly, this was also ever asked on Stack Overflow:

java.lang.StackOverflowError
    at java.lang.Exception.(Exception.java:84)
    at java.lang.RuntimeException.(RuntimeException.java:80)
    at javax.el.ELException.(ELException.java:66)
    at com.sun.faces.facelets.el.VariableMapperWrapper.resolveVariable(VariableMapperWrapper.java:107)
    at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$1.resolveVariable(CompositeComponentTagHandler.java:377)
    at com.sun.faces.facelets.el.VariableMapperWrapper.resolveVariable(VariableMapperWrapper.java:103)
    at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$1.resolveVariable(CompositeComponentTagHandler.java:377)
    at com.sun.faces.facelets.el.VariableMapperWrapper.resolveVariable(VariableMapperWrapper.java:103)
    at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler$1.resolveVariable(CompositeComponentTagHandler.java:377)
    at com.sun.faces.facelets.el.VariableMapperWrapper.resolveVariable(VariableMapperWrapper.java:103)
    [repeat]

What's happening here? Which Facelets EL variable exactly is being resolved on itself in an infinite loop? We have already used JSTL <c:forEach> instead of <ui:repeat> to avoid an infinite component tree during view build time (as the <ui:repeat> only runs during view render time not during view build time, see also JSTL in JSF2 Facelets… makes sense?), so that can't be the cause.

Upon inspection (just putting a debug breakpoint in VariableMapperWrapper) it appears to be the EL variable #{cc}.

What turns out, when you pass #{node} to the nested composite, then you're technically basically passing #{cc.attrs.node.children[index]} to it. The nested composite is in turn thus interpreting its #{cc.attrs.node} as #{cc.attrs[cc.attrs.node.children[index]]}. But ... in the context of the nested composite, the #{cc} is referring the nested composite itself! So, the #{cc.attrs.node} actually refers to the nested composite's own node. This results in a never ending loop.

We ultimately actually want it to be #{cc.attrs[cc.parent.attrs.node.children[index]]} where the #{cc.parent} just refers the parent component via UIComponent#getParent(). So, let's try it.

<cc:interface>
    <cc:attribute name="node" type="com.example.SomeTreeModel" />
</cc:interface>
<cc:implementation>
    <c:if test="#{not empty cc.attrs.node.children}">
        <ul>
            <c:forEach items="#{cc.attrs.node.children}" var="node" varStatus="loop">
                <li>
                    #{node.data}
                    <my:tree node="#{cc.parent.attrs.node.children[loop.index]}" />
                </li>
            </c:forEach>
        </ul>
    </c:if>
</cc:implementation>

It still yields a stack overflow error! It's only different from the above. Top lines of stack trace may vary depending on remaining stack size, but the repeating part is below (line numbers match Mojarra 2.2.12, Tomcat 8.0.30 and Weld 2.3.0):

    [repeat]
    at com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap.get(CompositeComponentAttributesELResolver.java:393)
    at javax.el.MapELResolver.getValue(MapELResolver.java:65)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
    at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
    at org.apache.el.parser.AstValue.getValue(AstValue.java:169)
    at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:184)
    at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
    at com.sun.faces.facelets.el.ContextualCompositeValueExpression.getValue(ContextualCompositeValueExpression.java:158)
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
    at javax.faces.component.UIComponentBase$AttributesMap.get(UIComponentBase.java:2427)
    at com.sun.faces.el.CompositeComponentAttributesELResolver$ExpressionEvalMap.get(CompositeComponentAttributesELResolver.java:393)
    [repeat]

What's happening here? Which composite component attribute exactly is being evaluated on itself in an infinite loop?

What turns out, the attributes are stored as deferred value expressions, not as immediately evaluated values. We all already kind of knew it (the #{} versus ${} in good old JSP), but it still easily slips our mind. When #{cc.parent.attrs.node} is about to be evaluated, the parent gives back the sole EL value expression #{cc.attrs.node} as a ValueExpression object to the child and then the child is evaluating it in its own context. However, as you have read above, the #{cc} refers to the current composite, not the intented parent. This results again in an infinite loop.

We ultimately want the composite to internally immediately evaluate the #{cc.attrs.node} and store it somewhere as a request based instance variable instead of storing only the ValueExpression object in the component state. A backing component is a good candidate. There you can hook on UIComponent#setValueExpression() and then caputure and immediately evaluate the node attribute. So let's try it.

@FacesComponent("treeComposite")
public class TreeComposite extends UINamingContainer {

    private SomeTreeModel node;

    @Override
    public void setValueExpression(String name, ValueExpression expression) {
        if ("node".equals(name)) {
            setNode((SomeTreeModel) expression.getValue(getFacesContext().getELContext()));
        }
        else {
            super.setValueExpression(name, expression);
        }
    }

    public SomeTreeModel getNode() {
        return node;
    }

    public void setNode(SomeTreeModel node) {
        this.node = node;
    }

}

Note: for SomeTreeModel you could use org.omnifaces.model.tree.TreeModel as also used by <o:tree>.

Declare the above backing component as componentType of the composite interface and then replace #{cc.attrs.node} over all place by #{cc.node}.

<cc:interface componentType="treeComposite">
    <cc:attribute name="node" type="com.example.SomeTreeModel" />
</cc:interface>
<cc:implementation>
    <c:if test="#{not empty cc.node.children}">
        <ul>
            <c:forEach items="#{cc.node.children}" var="node" varStatus="loop">
                <li>
                    #{node.data}
                    <my:tree node="#{cc.parent.node.children[loop.index]}" />
                </li>
            </c:forEach>
        </ul>
    </c:if>
</cc:implementation>

Now it finally works! :)

By the way, my hand is free again! Cast was removed February 1st. The rightmost fingers are still weak, and I'm still using only 2-3 fingers to type, and I have to pause my hand more often, but hey, there is progress as compared to last month.

Saturday, January 2, 2016

Programming with one hand

2016 started for me with this:

No, not a fireworks accident. Just a stupid stairway accident. Yesterday (1 January) I ran, yes ran, upwards stairs, stumbled near before end and my right hand hit the fence in an unfortunate way, between the middle and ring fingers, as in the Vulcan salute (Spock hand). The fourth metacarpal bone (the ring finger's one) of my right hand broke with a somewhat awkward fracture.

Now my entire right hand, except the thumb, is in a 90 degrees position in plaster cast, in a sock puppet position.

The expectation is that this accompanion will last 6-8 weeks. I can't use the keyboard anymore in a normal way. I can type reasonably with only my left hand, but all the special character and navigation keys which are most used when writing code are all on the right side of the keyboard. As my left hand isn't entirely used to that position, programming is a hell of a job. I'm at least 10 times slower this way.

Fortunately my left hand is the most experienced mouse hand, so I can still visit and browse Stack Overflow, and close some duplicates, but I'm afraid I'll be writing much less answers as they usually involve code snippets. As my thumb is free, I can still use the mobile, but I wholeheartedly hate writing code on a mobile keyboard. It's even more slow than doing it with only one hand on a desktop keyboard. Perhaps I will during the 6-8 weeks practice a bit with programming with only my left hand. We'll see, or else I may be on a necromancer spree when my right hand get freed up.

Happy new year! ;)


Update: today (11 January) I got new fiberglass cast, whereby my index finger finally get freed up!

It's much lighter and more convenient. I can finally type somewhat reasonable with my right hand, although it's still like as if I've never typed beforehand and are practicing my first type lessons with only the index finger stiffly dancing over the right side of the keyboard.

They killed my sock puppet tho :'(