Versione italiana: README.it.md
AliFaces is a marketplace proof of concept built with Jakarta Faces 4.1, PrimeFaces 16, OmniFaces 5 and PrimeUIX design tokens. It covers catalogue, search, product page, cart, checkout, orders, login, a JSON API and two visual profiles inspired by the patterns of large marketplaces.
The project shows that a rich, modern interface does not necessarily call for a SPA. It is an independent technical case study: it is not a clone of AliExpress or Amazon, it reproduces no proprietary asset or code, it is not affiliated with those brands, and it should not be put online as a real shop without the adapters and controls listed in SECURITY.md.
Requirements: JDK 17 or later and Maven 3.9 or later.
# WAR for Tomcat 11 (default profile)
mvn clean package
# Open Liberty
mvn -Pliberty clean package liberty:runOn Liberty the application answers at http://localhost:8080/alifaces/. The demo credentials
are shown on the login page; they are deliberately demo-only.
A production configuration combines the runtime with the additive production profile:
mvn -Ptomcat,production clean package
mvn -Pliberty,production clean packageThe profile puts Faces in Production and turns the accidental detection of a session from an
immediate error into a report (NoSessionGuard). It does not by itself make the application
ready for the internet. The /status.xhtml page stays reachable in Production too, since
showing what it runs on is the point of the proof of concept, with sensitive values redacted;
close it with the alifaces.DIAGNOSTICS=false context parameter.
- Views are transient and no
HttpSessionis used. - The catalogue pages (
index,product) are an anonymous shell: no identity in the markup, noSet-Cookie,Cache-Control: public, s-maxage=60. The cart badge, the user name, the history and the stateless strip arrive immediately afterwards from ap:remoteCommand, which is the "cacheable page plus edge-side include" pattern expressed in Faces components. A CDN must includealifacesLangandamaFacesThemein the cache key and exclude the identity cookies. - Fragments that are the same for everyone (footer, trust row) go through an application-scoped
<o:cache>, keyed by language and theme: rendered markup in a cache, not state. - The "recently viewed" history is written by an asynchronous CDI observer, never on the request path.
- Checkout carries an idempotency key: a double click, a browser retry or a POST replayed by a load balancer produce one order.
/health/readyanswers 200 or 503 according to the reachability of theSharedStore, so a load balancer can take a node out instead of watching requests fail one at a time.- Not even the Faces flash: Mojarra implements it as an in-memory map of a single node, so a message after a redirect would have required node affinity. Messages that must survive a redirect travel in a 60 second cookie as a bundle key plus arguments, and are translated again in the language of the page that displays them.
- Authenticated identity is the
subof a signed JWT in anHttpOnlycookie, or inAuthorization: Bearerfor a native client. - A guest gets an opaque random 256 bit identifier: never prices, never cart contents in the cookie.
- Cart and history go through the
SharedStoreport. The proof of concept ships onlyInMemorySharedStore, local to the JVM and without persistence. - The in-memory adapter expires idle shoppers after 15 minutes (
-Dalifaces.store.ttlMillis) and keeps at most 10000 of them (-Dalifaces.store.maxShoppers): a guest cart left alone for longer is gone, even though the cookie lasts a year. - In production that port belongs on a shared cache or data grid with persistence, TTL, eviction
and atomic operations: Redis, Hazelcast, GridGain or an equivalent service.
OrderStorelikewise belongs on durable storage. - The two themes compile the same PrimeUIX and Aura primitives into different visual profiles; components, accessibility and behaviour remain those of PrimeFaces.
- The interface is in English and Italian: the language comes from
?lang=or from a cookie and never touches a session, exactly like the theme. The English text lives in the base bundlemessages.properties;messages_en.propertiesis empty on purpose, see the comment inside it.
The WAR also carries a supporting PWA (manifest.webmanifest, service-worker.js): the
application is installable, static assets served from /jakarta.faces.resource/ are reused from
the cache, and with no network a dedicated offline page appears instead of the browser error. No
application page is ever cached: every Faces response contains shopper-bound elements (cart badge,
recently viewed, authenticated header) and is served with Cache-Control: no-store. A service
worker does not see the Cookie header, so "this page is anonymous enough to keep" is not a
decision it can make.
In a real environment HTTPS and a shared secret of at least 32 bytes are mandatory:
export ALIFACES_JWT_SECRET="$(openssl rand -base64 48)"
export ALIFACES_PUBLIC_ORIGIN="https://shop.example.com"ALIFACES_PUBLIC_ORIGIN lets the CSRF filter compare Origin and Referer against the exact
public origin when TLS terminates on a reverse proxy. The proxy or container must also report
correctly that the external request is HTTPS, otherwise the Secure flag on cookies cannot be
issued.
Native clients use a Bearer token. For API calls without a Bearer, login and the guest flow
included, they send X-AliFaces-Client: 1; the server enables no CORS.
mvn clean package # WAR for Tomcat 11
mvn -Pliberty clean package # WAR plus Open Liberty server
scripts/stateless-load-test.sh # no JSESSIONID under concurrent load
scripts/render-benchmark.sh mine # throughput and latency of a single instance
# In Production the secret is mandatory: export it before starting the server,
# because entries in server.env do NOT reach the environment of the JVM process.
export ALIFACES_JWT_SECRET="$(openssl rand -base64 48)"
# Node independence: two Liberty servers from the same build, requests alternating between them
mvn -Pliberty package liberty:create liberty:install-feature liberty:deploy
scripts/two-node-test.shThe two-node test checks that guest and authenticated identity, theme and language preferences,
views and post-redirect messages all cross a change of node. The cart does not, and that is
expected: it is the one adapter still held in memory. That line converges the day SharedStore
talks to a shared cache, and in the meantime it is also the proof that the two nodes are separate
processes.
There are no automated tests yet: verification is the build of both targets plus the load script,
which fails if a single response emits a JSESSIONID.
The long-form article is in docs/article-it.md, in Italian. Limits, threat model and an exposure checklist are in SECURITY.md.
Code under the Apache License 2.0. Attributions for the theme, the font and the demo catalogue are in NOTICE. Third-party names and trademarks belong to their respective owners.