Showing posts with label Performance. Show all posts
Showing posts with label Performance. Show all posts

Tuesday, July 14, 2026

Making Mojarra fast: a per-phase performance review

Mojarra 4.1.9 was measurably slower than MyFaces 4.1.3. That was the whole of issue #5753: perform a performance review of the Jakarta Faces reference implementation and close or even surpass the gap where possible. This post walks through how the review was done and what came out of it, broken down per lifecycle phase. The story throughout is 4.1.10 against the 4.1.9 release; MyFaces is the yardstick it chases. Each phase where MyFaces ran faster marked a lever to find, and a few times the fix was simply to adopt what MyFaces already did, and for the rest AI figured out it by itself. The short version: Mojarra 4.1.10 is 64% (almost 3 times) faster than the 4.1.9 release, and along the way overtook the current MyFaces 4.1.4-SNAPSHOT development build (a72f081e2) by 18%, faster on every one of six tested servers.

None of this is one clever trick. It is a few dozen small, individually-boring levers, each measured on its own, spread across the six phases of the request lifecycle. So let's first look at how they were found.

The benchmark: a WAR that touches every phase

You cannot optimize what you cannot measure, so an early deliverable of the review was a benchmark WAR under test/perf. The first cut was incomplete: it did not exercise the whole pipeline, and PROCESS_VALIDATIONS in particular was barely touched, so the real conversion and validation cost stayed invisible. So it was reworked round after round until every phase and every component was covered under a realistic load. Components like h:selectOneRadio, h:outputScript and h:message were only added in later rounds, which exposed yet more performance levers.

It is a component-family matrix. Four iterating families, h:dataTable, ui:repeat, composite components and build-time unrolled c:forEach, each spanning up to six variants: a read-only GET, a full postback with per-row inputs, a two-level nested variant, a state-restore-only "build" postback, plus ajax twins. On top of that sit flat multi-section forms (text, textarea, select, checkbox, radio over managed converters, validators and Jakarta Validation), a happy and an unhappy validation path, two dynamic-component scenarios and a couple of trivial baselines. 32 scenarios in total, each firing exactly the phases you would expect: GET-only scenarios fire Restore View and Render Response, postbacks fire all six.

A Faces PhaseListener registered in the WAR times every phase of every request and accumulates the result per scenario into a shared PerfStats. A companion servlet exposes those accumulators at /perf-stats as a fixed-width text table parseable by AI. A single integration test drives the whole thing: it resets the stats, loops thousands of GETs and postbacks against a managed app server, then reads /perf-stats at the end to dump the per-scenario, per-phase count/total/avg/min/max table. The same WAR runs against GlassFish, WildFly, TomEE, Payara, OpenLiberty and Tomcat. Every server has a -myfaces twin that swaps in MyFaces instead of Mojarra, so the exact same request stream is measured on both implementations. It lives in the test/perf module of the Mojarra source tree on the 4.1 branch. Run after cloning https://github.com/eclipse-ee4j/mojarra in the repo folder:

git switch 4.1
mvn clean install -pl impl,test/base -DskipTests
cd test/perf

Then run the perf bench using one of the following commands:

mvn clean verify -Dperf=true -Pglassfish           # Mojarra on GlassFish (default)
mvn clean verify -Dperf=true -Pwildfly             # Mojarra on WildFly
mvn clean verify -Dperf=true -Ptomee               # Mojarra on TomEE
mvn clean verify -Dperf=true -Ppayara              # Mojarra on Payara
mvn clean verify -Dperf=true -Pliberty             # Mojarra on OpenLiberty
mvn clean verify -Dperf=true -Ptomcat              # Mojarra on Tomcat
mvn clean verify -Dperf=true -Pglassfish-myfaces   # MyFaces on GlassFish
mvn clean verify -Dperf=true -Pwildfly-myfaces     # MyFaces on WildFly
mvn clean verify -Dperf=true -Ptomee-myfaces       # MyFaces on TomEE
mvn clean verify -Dperf=true -Ppayara-myfaces      # MyFaces on Payara
mvn clean verify -Dperf=true -Pliberty-myfaces     # MyFaces on OpenLiberty
mvn clean verify -Dperf=true -Ptomcat-myfaces      # MyFaces on Tomcat

Each run will take a little over a minute before it prints a fixed-width text table with the numbers. Note that an explicit -Dperf=true is mandatory to activate the PerfBenchIT, otherwise a regular mvn clean install in Mojarra repo root will unintentionally launch it as well.

The versions are not fixed either. -Dmojarra.version picks the Mojarra jar on any profile and -Dmyfaces.version picks the MyFaces jar on the -myfaces twins, so a released version can be benched against the current build without a rebuild. For example, this explicitly runs the 4.1.9 release on Tomcat instead of the current build:

mvn clean verify -Dperf=true -Ptomcat -Dmojarra.version=4.1.9

The server versions themselves are knobs too. Each profile takes a matching -Dglassfish.version, -Dwildfly.version, -Dtomee.version, -Dpayara.version, -Dliberty.version or -Dtomcat.version, and the Tomcat profile additionally takes -Dweld.version and -DhibernateValidator.version for the Weld and Hibernate Validator jars it bundles into the WAR. For example, this pins the GlassFish container version explicitly instead of using the default version hardcoded in test/pom.xml:

mvn clean verify -Dperf=true -Pglassfish -Dglassfish.version=8.0.2

The state-saving knobs, the heap size and the iteration counts are all filtered into the WAR at package time, so some predefined state-saving configurations can be applied without editing anything:

mvn clean verify -Dperf=true -Pwildfly -Dwebapp.stateSavingMethod=client

Or even custom context parameters, which you'll have to spell out fully:

mvn clean verify -Dperf=true -Ptomcat -Dwebapp.additionalContextParams='<context-param><param-name>com.sun.faces.disableIdUniquenessCheck</param-name><param-value>true</param-value></context-param>'

Every server launches with the same -Xmx1g so the cross-server comparison stays apples-to-apples.

The workflow: measure, profile, prototype, discard

The bench tells you which phase is slow. It does not tell you why. For that the same WAR doubles as a JFR driver: append the flight-recorder flags to the forked server VM, run at a tighter iteration count, and you get a per-method CPU profile and per-call-site allocation profile of the entire request stack, Mojarra, EL, CDI, Servlet, the server and the JDK all together. The jfr tool ranks the hot methods and the top allocators; a small script attributes each execution sample to a lifecycle phase by scanning its stack for the phase class.

This is where AI-assisted development earned its keep. The loop, run the A/B bench back-to-back on a quiesced machine (performance governor, no background load, no temperature throttles), profile the slow phase, read the JFR hot-method and allocation rankings, form a hypothesis, prototype a lever, rebuild both arms, remeasure, was run hundreds of times. Claude Code drove the harness, ran the mvn commands, injected the JFR flight-recorder flags into the forked server VM, generated the scripts, aggregated the raw per-phase dumps into readable delta tables, cross-checked JFR self-time against actual wall time, and kept the bookkeeping honest across dozens of candidate changes. That last part matters more than it sounds: a handful of candidate levers do not survive measurement. For example, caching the composite ValueExpression in TagAttributeImpl during Restore View looked obvious, because MyFaces caches it too, and was prototyped and reverted three separate times, each time about 2% slower, and VE creation is only ~1.7% of the phase to begin with. An attributesThatAreSet bitmask regressed buildView by ~20%. A whole class of "obvious" micro-optimizations measured at exactly zero. Only the levers that held up under a clean back-to-back A/B shipped.

A recurring trap worth calling out: JFR self-time percentage is not wall-clock cost. A phase that is thin in wall time starves for samples, and a subtree that shows 9% self-time can move the needle 0% when you actually fix it. The bench wall time, not the profiler, was always the final word.

The machine itself is the other thing that lies to you. A performance run is only as honest as the box it runs on, and a box that is busy, or hot, or on a scaling governor hands you swings the size of a real lever. The early rounds showed it plainly. WildFly came out surprisingly fast and OpenLiberty surprisingly slow, and neither had anything to do with the code: the six-server run was sequential, and a long sequential run heats the CPU until it throttles, so whichever server ran late looked slow. The fix is boring. Pin the governor to performance, drop the background load, let the machine cool between servers, and run each arm back-to-back so the two builds you compare meet the same conditions. A change that only shows up on a loaded or throttling box is not a lever; it is weather. Every number in this post was taken on a quiesced machine for that reason. In the end, all servers were within a few % from each other.

The container can lie to you too, and profiling the whole stack is what caught it. Because the JFR run captures CDI and the app server alongside Mojarra, an early GlassFish profile showed almost half of the HTTP-listener CPU going into GlassFish's own InjectionServicesImpl, not into Faces at all. Every CDI injection was re-walking the superclass and annotation hierarchy, which a view full of initially unoptimized @FacesConverter and @FacesValidator injections hits hard. That was a GlassFish bug, not a Mojarra one, and it made the initial GlassFish bench run about twice as slow. It was reported and fixed in GlassFish 8.0.3 (glassfish#26046); if you reproduce the bench on 8.0.2 you will see that inflation, so use 8.0.3 or later. It hit both implementations equally, so it never tilted the Mojarra-versus-MyFaces comparison, but it is exactly the kind of container cost a Faces-only view would have hidden.

Now the levers, phase by phase.

Restore View

Restore View rebuilds the component tree on every postback and restores its partial state. Mojarra was allocating heavily here. The biggest single lever was #5761: the descendant-id lookup used a per-tree HashMap cache that was rebuilt eagerly; it was replaced with a refresh-gated direct scan, and UIComponentBase.findComponent stopped allocating a fresh collection at every node it walked. #5819 cut further allocation on the unrolled c:forEach restore path, and #5828 memoizes the c:forEach items expression so ForEachHandler evaluates it once per phase instead of on every access. #5822 skips the duplicate-id uniqueness check on postbacks that reuse the already-restored tree, the way MyFaces does, plus two smaller lifecycle levers.

A subtle one came in via #5853: an h:selectOneRadio's @ListenerFor listener was being persisted as a needless per-component partial-state delta on every request. A single non-empty component delta is enough to force the full O(N) clientId-keyed restore walk instead of the cheap view-root-only fast path. Persisting the system-event listeners in the delta only when they actually changed brought the flat-form Restore View back to parity in isolation.

Net Restore View dropped about 65% versus 4.1.9, landing within a few percent of MyFaces; what's left is the shared buildView Facelet re-apply that MyFaces pays too. Notably the unrolled views, which carry the most components and the most per-component state of anything in the suite, are already faster than MyFaces here.

Several smaller levers round out the phase: #5764 (a dynamic-add gate, field-backed Facelets markers and ancestor memoization on the restore walk), #5778 (partial-state-saving and tree-walk trims), #5800 (cheaper per-component createComponent in buildView), #5805 (field-backed component value expressions), #5836 (an indexed Facelets tag-id lookup on refresh) and #5813 (dropping a per-component reflective composite-probe from the restore walk).

Apply Request Values

Apply Request Values decodes the submitted values onto the components. This is per-component overhead territory, and the fix is boring: #5777 reduced the fixed cost paid at every component in the pipeline. There is no single hotspot here; it is a broad, flat cost that only comes down by shaving every node a little. The phase came down about 54% versus 4.1.9, and now runs 18% faster than MyFaces across the suite; the win is dead consistent across all six servers, which is the tell that it is structural and not host-specific.

#5798 reads disabled and readonly through typed getters on decode instead of the reflective attribute map. Two broader request-pipeline levers land hardest in this phase, decisively faster on all six servers, though they touch every phase: #5757 (a FacetsMap empty short-circuit, a descriptor-map computeIfAbsent and hoisted UIData/UIRepeat restore state) and #5759 (per-component state access, a NamingContainer-ancestor cache, per-row state, indexed traversal, event publishing and render-output coalescing).

Process Validations

Of every phase this one fell the furthest against 4.1.9, by nearly 79%. It was also the closer: after the other phases had landed, Process Validations was the one still trailing MyFaces. Once the bench exposed the real conversion and validation load, it measured 44% slower than MyFaces; it is now 32% faster, and Mojarra leads in every component family. It came mainly from three PRs.

#5838 moved the java.time formatters used by f:convertDateTime to application scope instead of rebuilding them per conversion. #5840 shared a single application-scoped BeanValidator, cached the NumberConverter parser, and made UIInput's per-request validation flags (valid, localValueSet, submittedValue) cheaper: they live in transient state, and a default value now costs no map lookup or allocation, so an input whose flags are all default holds no transient map at all, saving a per-input map hash across a table's rows. An intermediate experiment had field-backed those flags as plain fields instead; a MyFaces maintainer rightly flagged that as fishy in the review thread, since per-request lifecycle state does not belong in persisted fields, so it was reverted to transient state and the transient get/put path was optimized instead.

The big one was #5847. Mojarra re-resolved the EL root once per input per validation, and each re-resolution allocated a fresh CDI CreationalContext. On a form with dozens of inputs that is dozens of pointless CDI allocations per request. After the fix, UIInput.validate also skips reading the previous value when nothing observes a value change, and the bean property type is taken from the already-resolved ValueReference instead of a second BeanValidator.getType walk. The form-* family, which carries all the new conversion work the bench added, dropped from behind MyFaces to parity, and the composite family, which had been the single biggest deficit anywhere in the suite at +18%, inverted to 28% faster once the composite cc.attrs EL depth was addressed with #5843 and #5847 (composite component stacks backed by ArrayList, non-contributing ELResolvers skipped, the postback check gated behind the composite check).

Four more levers finish the phase: #5767 (eliminating nested UIData/UIRepeat per-row state cost), #5782 (a guard plus per-attribute memoization in place of the composite-expression cache), #5831 (converter formatter caching and reuse of the built-in by-type converters) and #5834 (a cached parse formatter in DateTimeConverter).

Update Model Values

Update Model Values pushes the validated values into the backing beans, so it walks the same per-component and EL resolution paths as Apply Request Values and Process Validations. It did not get its own dedicated PR; it rode along on the per-component overhead work behind Apply Request Values and the validation EL work behind Process Validations, coming down about 63% versus 4.1.9 and landing ~9% faster than MyFaces. Worth naming as its own phase because that shared-cost effect is the point: a per-component or EL-resolution lever fixed once pays off in every phase that walks the tree.

Invoke Application

Invoke Application is sub-100µs per request in every scenario except the dynamic-component ones which programmatically modify the component tree during an action, so in absolute terms it is too small to matter; the aggregate percentage is a rough indicator only. But the dynamic add/remove machinery the dynamic-component scenarios drive was genuinely broken and got a cluster of fixes. #5783 made dynamic-component restore O(N) instead of O(N²). #5785 fixed a regression where a full-tree clientId index was being built on every postback even when there were zero dynamic actions, and trimmed CDI and event-dispatch overhead on the same path. #5853 landed two more: skipping the redundant dynamic-child reorder in buildView when the children are already in order, and trimming the add/remove path further while removing the dead TREE_HAS_DYNAMIC_COMPONENTS view-root flag that had been write-only since #5761 (for Restore View) dropped its only reader. Finally #5791 trims further per-component overhead on that same add/remove path.

One dynamic case is deliberately left alone, and it comes down to how each impl carries dynamic structure across a postback. Rather than persist an added subtree as full component state the way MyFaces does, Mojarra records each add or remove as a compact action (a ComponentStruct) and replays that action list when the view is rebuilt. It defers that replay to Render Response, because an Invoke Application action may still navigate away; deferring means Mojarra only ever re-applies the actions to the view it actually renders, never one it discards. The subtree itself is fully in the tree from Restore View onward, so nothing is withheld from decode, validate or update; only the render-time re-apply is deferred. That is why the dynamic-toggle-ajax scenario, which toggles on the same view, renders slower than MyFaces while its Restore View is faster; net it is a wash. A sound state-model trade, not a defect, and "fixing" it would break the navigation case.

Render Response

Render Response is the heaviest phase in absolute terms, so even modest percentages here move the suite total more than anything else. It came down about 58% versus 4.1.9 and is now 20% faster than MyFaces on all six servers. The largest lever was #5812: for static views Mojarra was re-applying the Facelet at render time even though nothing had changed since the build, a redundant tree walk that was skipped. #5796 attacked a subtler, per-component cost: the renderers were reading standard attributes like styleClass through getAttributes().get(...), which routes through the reflective AttributesMap, on every component they encoded. Casting to the concrete component subclass and calling its typed getter (getStyleClass() and friends) skips the reflection entirely, and the read is skipped altogether when the attribute is unset. #5822 does the same for the pass-through attribute sweep, which is skipped when a component declares none. Finally #5785 also lives partly here: a @FacesConverter(managed=true) was being re-resolved through CDI for every single table cell, which a small Bean cache eliminated, and the per-event listener dispatch got a fast path that took it off the top of the allocation profile.

The remaining Render Response levers are a spread of per-component and output trims: #5752 (caching CDI bean resolution to remove per-render BeanManager lookups), #5755 (response-writer range-emit and buffer elimination), #5770 (a lazy state map, UIOutput converter, rendererType and AttributesMap getter), #5793 (per-component encode and view-build overhead), #5825 (skipping the render re-apply of static c:forEach and deduping ViewScope FacesContext lookups) and #5839 (emitting an event-handler attribute only once when set via expression).

The numbers

Six servers, default settings (partial state saving, server state saving, Production stage), 1000-run suite, JDK 21, all three implementations built back-to-back on a quiesced machine. Values below are aggregated across servers; negative means Mojarra 4.1.10 is faster.

phasevs Mojarra 4.1.9vs MyFaces
Restore View−65%−4%
Apply Request Values−54%−18%
Process Validations−79%−32%
Update Model Values−63%−9%
Invoke Application−56%+22% †
Render Response−58%−20%
whole suite−64%−18%

† Invoke Application is sub-100µs per request outside the dynamic-toggle-ajax scenario, so its aggregate percentage is a rough indicator, not a real cost.

The vs-4.1.9 column is the cumulative gain since the last release: 64% faster, dead consistent across every clean server at −64 to −65%. The vs-MyFaces column is measured against the current MyFaces 4.1.4-SNAPSHOT development build (a72f081e2), itself 22% faster than its own 4.1.3 release, so this is Mojarra ahead of MyFaces at its best, not against a stale yardstick. The wins are structural: the tight cross-server spread on Apply Request Values and Render Response is what tells you these are real code changes and not one lucky host.

The same levers ship across all three active release lines: authored on the 4.1 branch or backported from master, they are all present in 4.1.10, backported to 4.0.19, and forward-ported to 5.0.0-M3.

What made the iteration fast

A performance review is only as fast as your validation loop, because every lever has to pass the full Jakarta Faces TCK before it can ship. A lever that shaves 3% is worthless if it quietly breaks a spec-mandated edge case, and the only way to know is to run the TCK, all of it.

Not long ago that meant hours. The Faces TCK ran the old JavaTest harness through repeated GlassFish cold starts and took over three hours end to end, which is fatal to an iterate-measure-validate loop; you get one or two validated levers per day and you lose your train of thought between them. The GlassFish pool work described in From hours to minutes: GlassFish pool for Jakarta EE TCKs brought that same TCK down to under four minutes with a reusable, leased server pool and parallel execution.

Four minutes changes the character of the work. A candidate lever could be prototyped, benched, and TCK-validated within an hour, so it was cheap to try a change, measure it, and throw it away when it did not pan out; and, as noted, a handful did not. The speed of the TCK is what let the review be exhaustive rather than conservative. 64% faster than the previous release is the combined result of being able to afford that many attempts in a relatively short time.

The fast TCK was only half of the loop; the other half was that same Claude Code workflow on the authoring side. Drafting a candidate lever, then forward- and backporting it across the release lines once it held up, is mechanical work that scales badly by hand. The judgement stayed human: which lever is worth shipping, whether a change is spec-legal, and reading the TCK output when a change was not. What came off your hands was the bulk, and for a review this wide that bulk is most of the hours.

The traffic went both ways. Digging into why Restore View was slow surfaced a corner case worth guarding: a UICommand bound into an h:dataTable via binding must fire its action exactly once per click instead of multiple times, and an earlier Restore View fix (#4128) was the thing keeping it that way. Analyzing that fix, because it badly impacted performance, turned up that its regression test, which had guarded exactly this behaviour, was nowhere in the TCK: it had been dropped from Mojarra's own test tree in the 3.0-to-4.0 migration and never migrated across. So it was ported back into the TCK as jakartaee/faces#2179, so that no Restore View optimization could quietly reintroduce the double-fire.

That one test exposed a bigger gap: Mojarra's old suite of 577 integration tests had been dropped wholesale in that migration, and only a part was ever migrated into the TCK, because porting each one by hand was too much work to justify. With AI assistance it no longer was. jakartaee/faces#2181 resurrected 138 of the spec-relevant ones with help of Claude Code, filtering out the ones not yet covered by the existing tests, each ported from the old HtmlUnit harness to Selenium, modernised to Jakarta namespaces and CDI, and named after the issue it covers. All within hours instead of weeks. Those resurrected tests promptly caught three real Mojarra regressions, unrelated to the performance work, fixed in #5774. And the pool swallowed all 138 new tests without complaint: the full 6026-test 5.0 TCK still finishes in less than four minutes at my machine with -T8.

The bottom line

"Mojarra is slow" was a fair thing to say against 4.0.18, 4.1.9 and 5.0.0-M2 or earlier. Now it does not hold anymore. Since 4.0.19, 4.1.10 and 5.0.0-M3, Mojarra is not just almost 3 times faster than its own previous release, it runs ahead of MyFaces at its best, on every one of six tested servers. If you last benchmarked Mojarra a release or two ago and wrote it off, the number you remember is stale; the reputation outlived the reality, and the numbers have caught up.

UPDATE: a few corner-case regressions have been reported and fixed, with no performance loss. Upgrade further to 4.0.21, 4.1.12 or 5.0.0-M5 to get them.

Appendix: all levers at a glance

Every lever from the sections above, collected in one place and grouped by the phase it moves most. All are present in 4.0.19, 4.1.10 and 5.0.0-M3: those authored on the 4.1 branch landed directly; those authored on master reached the release lines as a backport. A handful of small regression-guard fixes that accompanied the perf work are omitted.

phasePRwhat it does
Restore View#5761Descendant-id HashMap cache replaced with a refresh-gated direct scan; findComponent stops allocating per node
#5764Dynamic-add gate, field-backed Facelets markers, ancestor memoization on the restore walk
#5778Partial-state-saving and component-tree-walk improvements
#5800Cut per-component createComponent overhead in buildView
#5805Field-back component value expressions to cut restore cost
#5813Drop the per-component composite-probe reflective lookup from the restore walk
#5818Reduce restore allocation on JSTL c:forEach views
#5821Skip the id-uniqueness check on tree-reusing postbacks, plus two more view-lifecycle levers
#5828Memoize the c:forEach items expression per phase
#5835Index the Facelets tag-id lookup on refresh
#5853Persist system-event listeners in the partial-state delta only when changed
Apply Request Values#5757FacetsMap empty short-circuit, descriptor-map computeIfAbsent, UIData/UIRepeat restore-state hoist, indexed child traversal
#5759Per-component state/property access, NamingContainer-ancestor cache, per-row state, indexed traversal, event publishing, render-output coalescing
#5777Reduce per-component request-pipeline overhead
#5798Read disabled/readonly via typed getters on decode; less StateHelper and reflective overhead
Process Validations#5767Eliminate nested UIData/UIRepeat per-row state cost
#5782Replace the composite-expression cache with a guard plus per-attribute memoization (cc.attrs)
#5829Reuse the built-in by-type converters per target class
#5831Cache converter formatters/parsers
#5838Move the java.time f:convertDateTime formatters to application scope
#5840Shared BeanValidator, cached NumberConverter parser, lazy transient UIInput state
#5843EL resolution, composite component (ArrayList-backed stacks, non-contributing ELResolver skip) and component-id
#5847No per-input EL-root re-resolution; skip the previous-value read when unobserved; bean type from the ValueReference
Invoke Application#5783Dynamic component add/remove restore made O(N) instead of O(N²)
#5785Dynamic-action zero-actions regression fix, plus CDI and event-dispatch overhead
#5791Trim per-component overhead on the dynamic add/remove path
#5848Skip the redundant dynamic-child reorder; trim the add/remove path
#5851Remove the dead TREE_HAS_DYNAMIC_COMPONENTS flag, collapse the DYNAMIC_CHILD_COUNT counter
Render Response#5752Cache CDI bean resolution to eliminate per-render BeanManager lookups
#5755Response-writer range-emit and buffer elimination
#5770Lazy state map, UIOutput converter, rendererType, AttributesMap getter
#5793Reduce per-component overhead in encode and view build
#5796Read renderer attributes (e.g. styleClass) via typed getters instead of the reflective AttributesMap; skip when unset
#5811Skip the render-time Facelet re-apply for static views
#5824Skip the render re-apply of static c:forEach; dedup ViewScope FacesContext lookups
#5839Render an event-handler attribute only once when set via expression

All of it was validated on GlassFish, WildFly, TomEE, Payara, OpenLiberty and Tomcat. The full breakdown, round by round, is on issue #5753.

Thursday, June 4, 2026

From hours to minutes: GlassFish pool for Jakarta EE TCKs

Jakarta EE TCKs are notoriously slow. The bulk of the wall clock is not test execution but GlassFish cold-start: every test module unpacks a dist, boots a domain, deploys, undeploys, and stops the domain. With one hundred-ish test modules and several seconds of start/stop per module, a full TCK run easily takes hours. The arquillian-glassfish-server-pool module and the glassfish-pool-maven-plugin, both released as part of OmniFish arquillian-container-glassfish 2.2.0, eliminate that overhead by sharing a pool of pre-started GlassFish instances across the entire reactor.

The proof of concept: Faces TCK

The Jakarta Faces TCK historically consisted of two parts. The "old TCK" was the original Oracle suite: an Ant-driven JavaTest harness inherited from the JSF 1.x days, with around 5000 tests. The "new TCK" was the body of contributed and later-added tests built on JUnit + Arquillian, optionally with HtmlUnit or Selenium for browser interaction. Running both ends to end easily took over 3 hours on Jenkins CI, dominated by the old TCK.

Folding the old TCK into the new-TCK style was always the goal, but per-test manual conversion was prohibitively cumbersome; AI-assisted development is what finally made it feasible. Pull requests #2145, #2146, #2147, #2149, and #2150 mechanically migrated the entire old TCK, with Claude Code doing the bulk of the rewriting and consolidating the remaining HtmlUnit assertions onto Selenium along the way. WAR consolidation (one WAR per feature group instead of one per test) brought wall clock down to ~1h.

The second step was an in-house gf-pool prototype (#2156) that pre-started a pool of GlassFish instances and leased one slot per failsafe-forked JVM. With mvn clean verify -T8 (8 threads), the full Faces TCK reactor now finishes in under 4 minutes. The prototype proved the model works; the natural next step was extracting it into a reusable Maven plugin so other TCKs do not have to copy-paste the wiring.

PhaseLinuxMacBookJenkins
Pre-migration02:57 h02:53 h03:19 h
Post-migration01:05 h40:52 m01:18 h
With gf-pool3:46 m (-T8)4:47 m (-T5)13:06 m (-T2)

Linux: Intel Core i9-10900X with 32GB
MacBook: M1 Pro 10 Core with 16GB
Jenkins: Eclipse Jiro with "2 CPU" and "8 GB"

What gf-pool is

The pool ships in two artifacts:

  • arquillian-glassfish-server-pool: a runtime Arquillian DeployableContainer that leases a slot for the duration of a test JVM and deploys against the leased slot's DAS through the standard CommonGlassFishManager.
  • glassfish-pool-maven-plugin: lifecycle goals (up, down, provision, status, nuke) that provision and start slots before integration-test and stop them after.

Provisioning clones a single source GlassFish install into slot-1/, slot-2/, …, rewrites each slot's domain.xml so its ports land in a non-overlapping window (adminBase + (slot - 1) * portStride), and starts every slot in parallel. Each test JVM acquires an exclusive FileChannel.tryLock() on slot-N/lock, reads slot-N/ports.properties, and holds the lock for the JVM's lifetime. The lease protocol is pure Java; there's no -javaagent, no surefire argLine plumbing, and no shell scripts.

The pool grows on demand. A sequential build uses one slot; mvn clean verify -T4 grows to four; -T8 grows to eight. A JVM shutdown hook installed on Maven's own JVM stops every slot at session end (or on Ctrl+C), so no orphaned processes survive a hard build failure.

The optimal -TN for your machine is bounded by available RAM, not by core count. Each slot is a full GlassFish JVM plus a failsafe-forked test JVM, so the dominant cost is heap and resident memory, not CPU. Moreover, if your TCK drives a browser (as Faces does), each slot also spawns its own Chrome plus chromedriver, which pushes the total to ~1.5GB per slot. A 16-core box with 16GB will usually thrash at -T8 while a 8-core box with 32GB happily handles -T8; pick N by watching resident memory and swap, not number of processors.

Maven setup

Two plugin blocks: run the pool plugin (which resolves and unpacks GlassFish itself), and point failsafe at the same <poolDir> and <poolSource>.

<build>
    <plugins>
        <plugin>
            <groupId>ee.omnifish.arquillian</groupId>
            <artifactId>glassfish-pool-maven-plugin</artifactId>
            <version>2.2.0</version>
            <configuration>
                <poolDir>${project.build.directory}/pool</poolDir>
                <poolSource>${project.build.directory}/dist/glassfish9</poolSource>
                <distribution>
                    <groupId>org.glassfish.main.distributions</groupId>
                    <artifactId>glassfish</artifactId>
                    <version>9.0.0-M2</version>
                    <type>zip</type>
                </distribution>
            </configuration>
            <executions>
                <execution><id>pool-up</id><goals><goal>up</goal></goals></execution>
                <execution><id>pool-down</id><goals><goal>down</goal></goals></execution>
            </executions>
        </plugin>

        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <configuration>
                <systemPropertyVariables>
                    <gf.pool.dir>${project.build.directory}/pool</gf.pool.dir>
                    <gf.pool.source>${project.build.directory}/dist/glassfish9</gf.pool.source>
                </systemPropertyVariables>
            </configuration>
        </plugin>
    </plugins>
</build>

The <distribution> block tells the plugin to resolve the named artifact through your usual Maven repositories and unpack it under ${project.build.directory}/dist before provisioning runs. Staging is idempotent: re-runs fast-exit when the marker file written after a successful unpack is still present.

Add the runtime as a test-scope dependency:

<dependency>
    <groupId>ee.omnifish.arquillian</groupId>
    <artifactId>arquillian-glassfish-server-pool</artifactId>
    <version>2.2.0</version>
    <scope>test</scope>
</dependency>

No arquillian.xml is needed. The failsafe <systemPropertyVariables> above forward gf.pool.dir and gf.pool.source to the test JVM, and the container adapter reads them at start() when it leases a slot. Drop in an arquillian.xml with <container qualifier="glassfish-pool"> only if you need to override inherited fields like adminPassword, or if you're running against a hand-staged pool without the plugin.

That's it. mvn clean verify works sequentially; mvn clean verify -T8 fans out across eight slots.

Need to peek at the pool? mvn glassfish-pool:status in a separate terminal redraws a top-style table once per second:

mvn glassfish-pool:status command output

Overlays

For TCK-style builds that test a SNAPSHOT impl jar against a released distribution (or vice versa), the plugin can copy overlay jars into glassfish/modules/ after unpack and before slot cloning. Declare zero or more <overlay> blocks:

<configuration>
    <overlays>
        <overlay>
            <groupId>org.glassfish</groupId>
            <artifactId>jakarta.faces</artifactId>
            <version>5.0.0-SNAPSHOT</version>
            <destFileName>mojarra.jar</destFileName>
        </overlay>
    </overlays>
</configuration>

Each overlay accepts a <skip> child that wires to your existing per-profile property switches, so you can hold back individual jars per release line without forking the pom.

Bring your own unpack

If you'd rather use maven-dependency-plugin for the unpack (e.g. because your build already has one for unrelated reasons), drop the <distribution> block from the pool plugin and add a regular unpack execution that lands in the same directory <poolSource> points at:

<plugin>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>unpack-glassfish</id>
            <phase>process-test-classes</phase>
            <goals><goal>unpack</goal></goals>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>org.glassfish.main.distributions</groupId>
                        <artifactId>glassfish</artifactId>
                        <version>9.0.0-M2</version>
                        <type>zip</type>
                        <outputDirectory>${project.build.directory}/dist</outputDirectory>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

The pool plugin will then skip staging and clone slots directly from ${project.build.directory}/dist/glassfish9.

JVM system properties at slot boot

Some properties have to be on the GF JVM at startup. The canonical example is javax.net.ssl.trustStorePassword: a PKCS12 truststore needs the password before SSL is used for the first time, because Java caches the default SSLContext after first use and never reloads from disk. The plugin's <systemProperties> hook bakes each key=value into every <java-config> of each slot's domain.xml at provisioning time:

<configuration>
    <systemProperties>
        javax.net.ssl.trustStorePassword=changeit
        java.awt.headless=true
    </systemProperties>
</configuration>

Adoption: Security TCK

The Jakarta Security TCK followed the same old-TCK / new-TCK split as Faces: a JavaTest "old TCK" from Oracle plus a JUnit + Arquillian "new TCK" of later contributions. The combined suite ran in just under 13 minutes. Unlike Faces, the old-TCK side wasn't the bottleneck: it deployed its apps onto a single long-running GlassFish domain and replayed all 83 of its JavaTest clients against them, so it was already efficient on its own. Migrating it was therefore not about runtime; it was about consolidating on a single test harness. Pull request #365 did exactly that, mechanically rewriting the old TCK into the new-TCK style with Claude Code doing the bulk of the assertion work. The unavoidable trade-off is that each migrated test now spins up its own Arquillian-managed GlassFish instead of sharing one domain, so single-threaded runtime nearly doubled to ~24 minutes. That is the cost of trading a shared harness for per-test isolation. Pull request #368 recovers that cost (and then some) by swapping the JVM-scoped arquillian-glassfish-server-managed container for arquillian-glassfish-server-pool. With mvn clean verify -T8, the Security TCK now finishes in under 2 minutes.

Per-test isolation is a much bigger deal for Faces, whose old TCK has ~5000 tests and takes 2+ hours on a single shared GlassFish; there the post-migration single-threaded runtime would be prohibitive without the pool. Security is the small-scale case where you can see the trade clearly; Faces is where parallelism stops being optional.

PhaseLinuxMacBookJenkins
Pre-migration12:50 m9:38 m19:05 m
Post-migration23:42 m16:34 m
With gf-pool1:49 m (-T8)1:30 m (-T5)

Linux: Intel Core i9-10900X with 32GB
MacBook: M1 Pro 10 Core with 16GB
Jenkins: Eclipse Jiro with "2 CPU" and "8 GB"

Parallelism: what you'll discover

Moving to a shared pool surfaces parallelism issues that a sequential build hides. The Security TCK migration is a good cross-section. None of these are pool bugs; they're latent contracts that finally see daylight when two slots run side by side.

Hardcoded ports. Tests that embed an LDAP server, a Tomcat instance, or any other side-process on a fixed port collide as soon as two slots co-run. Pick distinct ports per module, or derive them from the slot index (gf.pool.slot is published as a system property by the leaser). The Security TCK's embedded LDAP modules split 33389 onto 33390 and 33391 for its two extra app-ldap variants.

Hardcoded URLs. Tests that publish http://localhost:8080/... URLs to an external party (OAuth callback URIs, OIDC issuer metadata) break the moment the slot's HTTP port is anything but 8080. Replace literals with UriInfo-derived or request-derived URLs at runtime, so the URL tracks the slot's actual HTTP port.

Pre-registered redirect URIs for every slot. External identity providers that need redirect URIs pre-registered (Mitre OIDC in the Security TCK case) have to be told about every slot the pool may grow to. Maven exposes ${session.request.degreeOfConcurrency} as the -TN value; bsh-property can promote it to a regular property if your plugin only consumes typed properties.

Cross-app singletons. A java:global/ DataSource shared across apps, or any other JNDI/CDI/resource a previous app deploy leaves behind, can leak into the next app's lookup on the same slot. The Security TCK adoption uncovered a related upstream GlassFish bug where ComponentEnvManagerImpl.getResourceId returned an empty string for ScopeType.GLOBAL, fixed in eclipse-ee4j/glassfish#26029. Worth re-running your TCK against this fix if you exercise cross-app global resources.

Persisted state across re-runs. Anything written to work/, sessions/, or other on-disk caches survives a slot's lease release and can resurrect into the next consumer. If your test relies on a known starting state or an existing HTTP session variable, wipe the relevant directories on container start.

Aggregator goals on the reactor root. Goals like failsafe-report-only, cyclonedx:makeAggregateBom, or install-file bound to a per-module phase stall the -T reactor because Maven serialises them across modules. Move these to inherited=false on the reactor root only.

For Jakarta EE TCK maintainers

If your Jakarta EE TCK still drags a JavaTest "old TCK" alongside its JUnit + Arquillian "new TCK", or runs sequentially against a freshly-unpacked GlassFish per module, the migration path is the one Faces and Security walked. First, fold any remaining old TCK into the new-TCK style (JUnit + Arquillian, plus Selenium if your tests drive a browser, as Faces does); AI-assisted development handles the mechanical rewriting well enough that the bulk of the work is reviewing diffs, not writing them. Then wire arquillian-glassfish-server-pool and glassfish-pool-maven-plugin in. The result is a TCK that finishes in minutes instead of hours, and a build that still runs sequentially under mvn clean verify for vendors who prefer that.

The README at glassfish-pool-maven-plugin/README.md documents the full configuration surface; the working example at integration-tests/src/it/pool is ~150 lines of pom and runs as a smoke test in the project's own CI. Both Faces TCK (faces#2165) and Security TCK (security#368) are open and worth studying as real-world consumers.

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, June 2, 2014

OmniFaces 1.8.3 released!

OmniFaces 1.8.3 has finally been released!

Also this release had some unscheduled delay for various reasons. Great programmers are also just humans with a "life" next to all the development work. I personally had after all a lot more time needed to acclimatize myself to the Netherlands after having lived in Curaçao for almost 6 years (still don't really feel home here outside the working hours, still want to go back once the kids grow out the house). Arjan had among others also some unforeseen issues with his new home.

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 1.8. The three most useful additions are the <o:deferredScript>, <o:massAttribute> and @Eager.

Installation

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

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

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

Defer loading and parsing of JavaScript files

If you've ever analyzed the performance of your website using a tool like Google PageSpeed, then you'll probably recognize the recommendation to defer loading and parsing of JavaScript files. Basically, the recommendation is to load JavaScript files only when the browser is finished with rendering of the page. This is to be achieved by dynamically creating a <script> element via document.createElement() during window.onload. Please note that this is not the same as just moving the scripts to the bottom of the page using <h:outputScript target="body">! That would speed up downloading of other resources, but still block the rendering of the HTML in most browsers (read: everything expect IE).

OmniFaces now comes with a <o:deferredScript> component for this very purpose which works just like <h:outputScript> with a library and name attribute.

<h:head>
    ...
    <o:deferredScript library="libraryname" name="resourcename.js" />
</h:head>

You can also use it on for example PrimeFaces scripts, but some additional work needs to be done. For detail, refer this Stack Overflow Question and Answer: Defer loading and parsing of PrimeFaces JavaScript files. At a production site, this approach has proven to decrease the average time until "DOM content loaded" from ~3s to ~1s on a modern client machine.

Set a common attribute on multiple components

The new <o:massAttribute> taghandler allows you to set a common attribute on multiple components. So, instead of for example:

<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />

You can just do:

<o:massAttribute name="disabled" value="#{someBean.disabled}">
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
    <h:inputText ... />
</o:massAttribute>

The advantage speaks for itself.

Eagerly instantiate a CDI managed bean

When using the standard JSF managed bean facility via @ManagedBean which is since JSF 2.2 semi-official deprecated (not documented as such, but the JSF team is clearly pushing toward it given the total absence of new features around JSF managed bean facility, instead they are all in CDI managed bean facility), it was possible to declare an application scoped JSF managed bean to be eagerly instantiated during application's startup like so:

import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean(eager=true)
@ApplicationScoped
public class Bean {
    // ...
}

However, this isn't possible with standard CDI, not even with the one as available in Java EE 7. So OmniFaces has added the @Eager and @Startup annotations for the very purpose. The @Startup is just a stereotype for @Eager @ApplicationScoped.

So, both beans below are equivalent:

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
import javax.inject.Named;
import org.omnifaces.cdi.Startup;

@Named
@Startup
public class Bean {
    // ...
}

An additional bonus of OmniFaces @Eager is that it not only works on application scoped CDI managed beans, but also on request and session scoped CDI managed beans and on beans annotated with OmniFaces @ViewScoped (thus not the JSF 2.2 one yet, that will come in the upcoming OmniFaces 2.0 which will target JSF 2.2, OmniFaces 1.x is namely JSF 2.0 targeted).

Having @Eager on a request scoped bean may look somewhat strange, but this makes an interesting use case possible: eagerly and asynchronously fetch some data from a DB in the very beginning of the request, long before the FacesServlet is invoked, it runs even before the servlet filters are hit (it's initiated via a ServletRequestListener). Depending on the server hardware used, the available server resources, all code running between the invocation of the first servlet filter and entering the JSF render response, this may give you a time space of 10ms ~ 500ms (or perhaps more if you've some inefficient code in the pipeline ;) ) to fetch some data from DB in a different thread parallel with the HTTP request and thus a speed improvement equivalent to the time the DB needs to fetch the data. Below is an example of how such an approach can look like:

The asynchronous service (this silly example fetches the entire table; just do whatever DB or any other relatively long-lasting service task you want to do as long as the method is annotated with @Asynchronous and you return an AsyncResult as Future; the container will all by itself worry about managing the threads):

package com.example;

import java.util.List;
import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class MyEntityService {

    @PersistenceContext
    private EntityManager em;

    @Asynchronous
    public Future<List<MyEntity>> asyncList() {
        List<MyEntity> entities = em
            .createQuery("SELECT e FROM MyEntity e", MyEntity.class)
            .getResultList();
        return new AsyncResult<>(entities);
    }

}

The @Eager request scoped bean (note the requestURI attribute, this must exactly match the context-relative request URI without any path fragments and query strings, this example assumes a /test.xhtml page (with a FacesServlet mapping of *.xhtml); wildcards like * are not supported yet, this may come in the future if there's demand)

package com.example;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;

@Named
@Eager(requestURI="/test.xhtml")
@RequestScoped
public class MyEagerRequestBean {

    private Future<List<MyEntity>> entities;

    @Inject
    private MyEntityService service;

    @PostConstruct
    public void init() {
        entities = service.asyncList();
    }

    public List<MyEntity> getEntities() {
        try {
            return entities.get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new FacesException(e);
        } catch (ExecutionException e) {
            throw new FacesException(e);
        }
    }

}

This way, when you request /test.xhtml with something like this:

<h:dataTable value="#{myEagerRequestBean.entities}" var="entity">
    <h:column>#{entity.property}</h:column>
</h:dataTable>

... then the above bean will be constructed and initialized far before the FacesServlet is invoked. Note that this thus also means that the FacesContext is not available inside the @PostConstruct! From that point on, both JSF and JPA will do their jobs simultaneously in separate threads until JSF calls the getter for the first time. The JSF thread (the HTTP request thread) will then block until JPA has returned the result, or perhaps it's already returned at that moment and then JSF can just advance immediately without waiting for JPA.

An overview of all additions/changes/bugfixes in OmniFaces 1.8

Taken over from the What's new? page on showcase:

Added in OmniFaces 1.8

  • WebXml#getFormErrorPage() to get web.xml configured location of the FORM authentication error page
  • <o:deferredScript> which is capable of deferring JavaScript resources to window.onload
  • Faces#addResponseCookie() got 2 new overloaded methods whereby domain and path defaults to current request domain and current path
  • Components#isRendered() which also checks the rendered attribute of all parents of the given component
  • <o:massAttribute> which sets the given attribute on all nested components
  • FacesMessageExceptionHandler which sets any caught exception as a global FATAL faces message
  • <o:cache> has new disabled attribute to temporarily disable the cache and pass-through children directly
  • @Eager annotation to eagerly instantiate request-, view-, session- and application scoped beans
  • <o:viewParam> skips converter for null model values so that query string doesn't get polluted with an empty string
  • Small amount of utility methods and classes, e.g. method to check CDI annotations recursively in stereotypes, shortcut method to obtain VDL, etc

Changed in OmniFaces 1.8

  • CombinedResourceHandler now also recognizes and combines <o:deferredScript>
  • UnmappedResourceHandler now also recognizes PrimeFaces dynamic resources using StreamedContent

Fixed in OmniFaces 1.8

  • Assume RuntimeException in BeanManager#init() as CDI not available (fixes deployment error on WAS 8.5 without CDI enabled)
  • Use "-" (hyphen) instead of null as default option value to workaround noSelectionOption fail with GenericEnumConverter
  • <o:param> shouldn't silently convert the value to String (fixes e.g. java.util.Date formatting fail in <o:outputFormat>)
  • Fixed javax.enterprise.inject.AmbiguousResolutionException in subclassed @FacesConverter and @FacesValidator
  • <o:messages> failed to find the for component when it's not in the same parent
  • <o:conditionalComment> shouldn't XML-escape the if value, enabling usage of & character
  • UnmappedResourceHandler broke state saving when partial state saving is turned off
  • CombinedResourceHandler didn't properly deal with MyFaces-managed resources

Maven download stats

Here are the Maven download stats:

  • January 2014: 3537
  • February 2014: 3580
  • March 2014: 3892
  • April 2014: 3572
  • May 2014: 3971

Below is the version pie of May 2014:

Last but not least

For the case you missed it: OmniFaces repo, wiki and issue tracking (basically: everything) has moved from Google Code to GitHub, along with a "brand new" homepage in GitHub style at omnifaces.org. The downloads will from now just point directly to Maven via links at the homepage.

Wednesday, July 31, 2013

Serving multiple images from database as a CSS sprite

Introduction

In the first public beta version of ZEEF which was somewhat thrown together (first get the minimum working using standard techniques, then review, refactor and improve it), all favicons were served individually. Although they were set to be agressively cached (1 year, whereby a reload is when necessary forced by the timestamp-in-query-string trick with the last-modified timestamp of the link), this resulted in case of an empty cache in a ridiculous amount of HTTP requests on a subject page with relatively a lot of links, such as Curaçao by Bauke Scholtz:

Yes, 209 image requests of which 10 are not for favicons, which nets as 199 favicon requests. Yes, that much links are currently on the Curaçao subject. The average modern webbrowser has only 6~8 simultaneous connections available on a specific domain. That's thus a huge queue. You can see it in the screenshot, it took on an empty cache nearly 5 seconds to get them all (on a primed cache, it's less than 1 second).

If you look closer, you'll see that there's another problem with this approach: links which doesn't have a favicon re-requests the very same default favicon again and again with a different last-modified timestamp of the link itself, ending up in copies of exactly same image in the browser cache. Also, links from the same domain which share the same favicon, have their favicons duplicated this way. In spite of the agressive cache, this was simply too inefficient.

Converting images to common format and size

The most straightforward solution would be to serve all those favicons as a single CSS sprite and make use of CSS background-position to reference the right favicon in the sprite. This however requires that all favicons are first parsed and converted to a common format and size which allows easy manipulation by standard Java 2D API (ImageIO and friends) and easy generation of the CSS sprite image. PNG was chosen as format as that's the most efficient and lossless format. 16x16 was chosen as default size.

As first step, a favicon parser was created which verifies and parses the scraped favicon file and saves every found image as PNG (the ICO format can store multiple images, usually each with a different dimension, e.g. 16x16, 32x32, 64x64, etc). For this, Image4J (a mavenized fork with bugfix) has been of a great help. The original Image4J had only a minor bug, it ran in an infinite loop on favicons with broken metadata, such as this one. This was fixed by vijedi/image4j. However, when an ICO file contained multiple images, this fix discarded all images, instead of only the broken one. So, another bugfix was done on top of that (which by the way just leniently returned the "broken" image — in fact, only the metadata was broken, not the image content itself). Every single favicon will now be parsed by ICODecoder and BMPDecoder of Image4J and then ImageIO#read() of standard Java SE API in this sequence. Whoever returned the first non-null BufferedImage(s) without exceptions, this will be used. This step also made us able to completely bypass the content-type check which we initially had, because we discovered that a lot of websites were doing a bad job in this, some favicons were even served as text/html which caused false negatives.

As second step, if the parsing of a favicon resulted in at least one BufferedImage, but no one was in 16x16 dimension, then it will be created based on the firstnext dimension which is resized back to 16x16 with help of thebuzzmedia/imgscalr which yielded high quality resizings.

Finally all formats are converted to PNG and saved in the DB (and cached in the local disk file system).

Serving images as CSS sprite

For this a simple servlet was been used which does basically ultimately the following in doGet() (error/cache checking omitted for simplicity):


Long pageId = Long.valueOf(request.getPathInfo().substring(1));
Page page = pageService.getById(pageId);
long lastModified = page.getLastModified();
byte[] content = faviconService.getSpriteById(pageId, lastModified);

if (content != null) { // Found same version in disk file system cache.
    response.getOutputStream().write(content);
    return;
}

Set<Long> faviconIds = new TreeSet<>();
faviconIds.add(0L); // Default favicon, appears as 1st image of sprite.
faviconIds.addAll(page.getFaviconIds());

int width = Favicon.DEFAULT_SIZE; // 16px.
int height = width * faviconIds.size();

BufferedImage sprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = sprite.createGraphics();
graphics.setBackground(new Color(0xff, 0xff, 0xff, 0)); // Transparent.
graphics.fillRect(0, 0, width, height);

int i = 0;

for (Long faviconId : faviconIds) {
    Favicon favicon = faviconService.getById(faviconId); // Loads from disk file system cache.
    byte[] content = favicon.getContent();
    BufferedImage image = ImageIO.read(new ByteArrayInputStream(content));
    graphics.drawImage(image, 0, width * i++, null);
}

ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(sprite, "png", output);
content = output.toByteArray();
faviconService.saveSprite(pageId, lastModified, content); // Store in disk file system cache.
response.getOutputStream().write(content);

To see it in action, you can get all favicons of the page Curaçao by Bauke Scholtz (which has page ID 18) as CSS sprite on the following URL: https://zeef.com/favicons/page/18.

Serving the CSS file containing sprite-image-specific selectors

In order to present the CSS sprite images at the right places, we should also have a simple servlet which generates the desired CSS stylesheet file containing sprite-image-specific selectors with the right background-position. The servlet should basically ultimately do the following in doGet() (error/cache checking omitted to keep it simple):


Long pageId = Long.valueOf(request.getPathInfo().substring(1));
Page page = pageService.getById(pageId);

Set<Long> faviconIds = new TreeSet<>();
faviconIds.add(0L); // Default favicon, appears as 1st image of sprite.
faviconIds.addAll(page.getFaviconIds());

long lastModified = page.getLastModified().getTime();
int height = Favicon.DEFAULT_SIZE; // 16px.

PrintWriter writer = response.getWriter();
writer.printf("[class^='favicon-']{background-image:url('../page/%d?%d')!important}", 
    pageId, lastModified);
int i = 0;

for (Long faviconId : faviconIds) {
    writer.printf(".favicon-%s{background-position:0 -%spx}", faviconId, height * i++);
}

To see it in action, you can get the CSS file of the page Curaçao by Bauke Scholtz (which has page ID 18) on the following URL: https://zeef.com/favicons/css/18.

Note that the background-image URL has the page's last modified timestamp in the query string which should force a browser reload of the sprite whenever a link has been added/removed in the page. The CSS file itself has also such a query string as you can see in HTML source code of the ZEEF page, which is basically generated as follows:


<link id="favicons" rel="stylesheet" 
    href="//zeef.com/favicons/css/#{zeef.page.id}?#{zeef.page.lastModified.time}" />

Also note that the !important is there to overrule the default favicon for the case the serving of the CSS sprite failed somehow. The default favicon is specified in general layout CSS file layout.css as follows:


#blocks .link.block li .favicon,
#blocks .link.block li [class^='favicon-'] {
    position: absolute;
    left: -7px;
    top: 4px;
    width: 16px;
    height: 16px;
}

#blocks .link.block li [class^='favicon-'] {
    background-image: url("#{resource['zeef:images/default_favicon.png']}");
}

Referencing images in HTML

It's rather simple, the links were just generated in a loop whereby the favicon image is represented via a plain HTML <span> element basically as follows:


<a id="link_#{linkPosition.id}" href="#{link.targetURL}" title="#{link.defaultTitle}">
    <span class="favicon-#{link.faviconId}" />
    <span class="text">#{linkPosition.displayTitle}</span>
</a>

The HTTP requests on image files have been reduced from 209 to 12 (note that 10 non-favicon requests have increased to 11 non-favicon requests due to changes in social buttons, but that's not further related to the matter):

It took on an empty cache on average only half a second to download the CSS file and another half a second to download the CSS sprite. Per saldo, that's thus 5 times faster with 197 connections less! On a primed cache it's even not requested at all. Noted should be that I'm here behind a relatively slow network and that the current ZEEF production server on a 3rd party host isn't using "state of the art" hardware yet. The hardware will be handpicked later on once we grow.

Reloading CSS sprite by JavaScript whenever necessary

When you're logged in as page owner, you can edit the page by adding/removing/drag'n'drop links and blocks. This all takes place by ajax without a full page reload. Whenever necessary, the CSS sprite can during ajax oncomplete be forced to be reloaded by the following script which references the <link id="favicons">:


function reloadFavicons() {
    var $favicons = $("#favicons");
    $favicons.attr("href", $favicons.attr("href").replace(/\?.*/, "?" + new Date().getTime()));
}

Basically, it just updates the timestamp in the query string of the <link href> which in turn forces the webbrowser to request it straight from the server instead of from the cache.

Note that in case of newly added links which do not exist in the system yet, favicons are resolved asynchronously in the background and pushed back via Server-Sent Events. In this case, the new favicon is still downloaded individually and explicitly set as CSS background image. You can find it in the global-push.js file:


function updateLink(data) {
    var $link = $("#link_" + data.id);
    $link.attr("title", data.title);
    $link.find(".text").text(data.text);
    $link.find("[class^='favicon-']").attr("class", "favicon")
        .css("background-image", "url(/favicons/link/" + data.icon + "?" + new Date().getTime() + ")");
    highlight($link);
}

But once the HTML DOM representation of the link or block is later ajax-updated after an edit or drag'n'drop, then it will re-reference the CSS sprite again.

The individual favicon request is also done in "Edit link" dialog. The servlet code for that is not exciting, but for the case you're interested, the URL is like https://zeef.com/favicons/link/354 and all the servlet basically does is (error/cache checking omitted for brevity):


Long linkId = Long.valueOf(request.getPathInfo().substring(1));
Link link = linkService.getById(linkId);
Favicon favicon = faviconService.getById(link.getFaviconId());
byte[] content = favicon.getContent();
response.getWriter().write(content);

Note that individual favicons are not downloaded by their own ID, but instead by the link ID, because a link doesn't necessarily have any favicon. This way the default favicon can easily be returned.