Skip to content

Repository files navigation

AliFaces

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.

Getting started

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:run

On 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 package

The 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.

The architecture in brief

  • Views are transient and no HttpSession is used.
  • The catalogue pages (index, product) are an anonymous shell: no identity in the markup, no Set-Cookie, Cache-Control: public, s-maxage=60. The cart badge, the user name, the history and the stateless strip arrive immediately afterwards from a p:remoteCommand, which is the "cacheable page plus edge-side include" pattern expressed in Faces components. A CDN must include alifacesLang and amaFacesTheme in 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/ready answers 200 or 503 according to the reachability of the SharedStore, 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 sub of a signed JWT in an HttpOnly cookie, or in Authorization: Bearer for 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 SharedStore port. The proof of concept ships only InMemorySharedStore, 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. OrderStore likewise 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 bundle messages.properties; messages_en.properties is 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.

Security configuration

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.

Verification

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.sh

The 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.

Licence and demo data

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.

About

Marketplace proof of concept on Jakarta Faces 4.1, PrimeFaces 16 and OmniFaces 5: no HttpSession, transient views, one service behind both the pages and the JSON API. Runs on Tomcat, Open Liberty and as a unikernel.

Resources

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages