ICEfaces revisited
Since my previous blog-post (Dynamic DataTables in ICEfaces), I have encountered several more situations where calling setId() seems to be required: for example, PanelCollapsible content was not rendered, and the Highlight effect was never fired, unless their generation was accompanied with a setId() call. The strange thing is that in these cases the problem only occurred on some platforms, not on others. Same application source, same ICEfaces version, same GlassFish version, but slightly different Java revisions and completely different operating systems. I gave up trying to find the underlying reason for this, and added the following code instead:
private int ensureIds(UIComponent x, int i) {
if (x.getId() == null) {
i++;
x.setId("g" + Integer.toString(i));
}
for (Iterator j = x.getFacetsAndChildren(); j.hasNext(); ) {
i = ensureIds(j.next(), i);
}
return i;
}
Then immediately after all components have been generated, ensureIds(binding,0) is called, where binding is the root UIComponent of the generated tree.
Some more non-obvious quirks:
- The ICEfaces Ajax push-server does not work reliably with the GlassFish https listener. It has problems even with ordinary http behind a https-frontend reverse proxy (I am currently using Apache httpd with mod_proxy for this). This is a shame, because no one uses unencrypted transport these days for non-public data of any kind. Apparently ICEsoft provides a non-free Enterprise Push Server that is designed to handle https, and it also handles clustering and failover. Clustering and failover is clearly enterprise stuff, but surely something as basic as https should be among the fundamental feature set. I consider the push-server to be dangerously close to crippleware... Fortunately it is possible to get along without the push-server, as long as you are content to live with some limitations.
- Some ICEfaces components (e.g.
RowSelector) need method expressions with named beans. This is awkward for two reasons: the first is that a named JSF bean, which is a declared container-managed entity, must be referenced in the expression. But a simple listener object (or "lambda closure" in other languages) would have been enough. The second reason is that ICEfaces uses a deprecated API for this method expression, producing a warning at every compilation. - The presence of a started but uncommitted
UserTransactionseems to inhibit ICEfaces from performing a rendering after aOnDemandRenderer.requestRender(). Actually, I am not 100% sure about this one, but switching the order of myrequestRender()andcommit()calls got asynchronous rendering working for me.
I just had get this off my chest. All in all, I still like ICEfaces very much, and I wouldn't trade it for any alternative Ajax toolkit.
Dynamic DataTables in ICEfaces
However, ICEfaces is not free from quirks. For example, when creating a
DataTable dynamically from Java code, it is essential that UIColumn.setId() is called for each dynamically created UIColumn, otherwise incremental updates to the DataTable model will be rendered incorrectly. This is not at all obvious since everything works without any "id" attributes when the UIColumns are created via static XML code.
Mysterious injection problem...
build.xml to reflect the changed name both in the javac classpath and in the copy task that assembles WEB-INF/lib for the webapp. But I made a typo in the second case, so a <file> element in a <fileset> in a copy-task pointed to a non-existent file. Since the correct file was in the javac classpath, compilation of my own source code worked without problems. And "ant" happily proceeded to create the WAR file anyway, despite the missing file in the copy-task. This is not part of the mystery however — "ant" is extremely unintuitive so this behaviour is no different from business as usual.Deployment failed of course, but in a rather peculiar way. The broken WAR file contained a servlet that was as a subclass of one of the classes in the missing library JAR. It also contained a
ServletContextListener that invoked a method on an injected EJB. But this injection never happened, so I got a NullPointerException and initialization of the webapp context failed. It took me quite a long time plus some intense head-scratching to figure out why: the missing servlet superclass somehow stopped injection from happening in the context listener. The mysterious part is why the ServletContextListener.contextInitialized() method was called despite this failure. It wasn't until I removed the EJB invocation that the servlet initialization produced a ClassNotFoundException that revealed the true cause of the problem.Moral: never assume that a failure in one part of the system won't affect other parts in non-obvious and unpredictable ways.
