Java EE · 3D Web · Full-Stack
Product Configurator
A configurable-product platform — a graph-driven engine resolves which parts fit where, a Three.js WebGL viewer renders the assembled product in 3D, and the whole thing ships as a Java EE EAR on JBoss with MySQL + MongoDB.
Pick a part, see it snap into place in 3D. Product Configurator is a graph-driven configuration engine: an assembly is a directed graph of nodes and edges, each node holds parts, each part references a 3D model asset, and connectors on parts encode how pieces fit together. A Backbone + Three.js single-page app drives the UX, a Java EE backend walks the graph to resolve valid choices, and the result is rendered live in WebGL — all packaged as one EAR on JBoss.
The idea
Let a customer configure a product visually — a table, a Lego figure, a piece of furniture — and see it assemble in 3D as they choose.
A configurable product is modelled as a directed graph: nodes are configurable decision points, edges define which nodes can follow which, and each node carries one or more parts (the actual selectable items). A part references a 3D asset (a Three.js geometry stored in MongoDB GridFS) and exposes connectors — 3D positions and rotations that say where it joins neighbouring parts. When a user picks a part, the backend walks the graph, adds the adjacent nodes’ parts, aligns them in 3D via the connectors, and returns both the assembled product and the next set of available choices.
The work splits into three layers:
- Model — an assembly graph editor (d3 force layout) and a part editor (Three.js) let an author build the configuration graph, place 3D models and define connectors.
- Resolve — a JAX-RS endpoint runs the graph engine: load the in-memory graph, apply the user’s choices, walk adjacent nodes, align parts in 3D and return the assembled product plus the next available choices.
- View — a Three.js WebGL viewer renders the assembled product, and an
embeddable widget lets the configurator be dropped onto any third-party page
with a single
<script>tag.
System architecture
The diagram shows the full stack as it ships: the JBoss application server
runs a single Configurator.ear made of an EJB module (domain, DAOs, JAX-RS
resources, the graph/product engine) and two WAR frontends — the Backbone
editor at /editor and the embeddable viewer at /embed. MySQL holds the
configuration graph (users, assemblies, nodes, parts, edges, connectors,
assets, settings); MongoDB GridFS holds the 3D model files and textures. The
flagship endpoint POST /rest/configurator/product is where a client sends
its choices and gets back the assembled 3D product.
Backbone SPA · /editor"] EMBED["Consumer widget
Backbone + Three.js · /embed
drop-in <script> tag"] end subgraph jboss["JBoss AS 7.1.1 · Configurator.ear"] direction TB subgraph fe["WAR frontends"] WEDITOR["configurator-web-editor
RequireJS / r.js · Bower
Three.js + d3"] WEMBED["configurator-web-embed
Browserify / Gulp
Three.js"] end subgraph be["configurator-ejb · EJB 3.1"] direction TB REST["JAX-RS / RESTEasy
/rest/* · Basic-auth + CORS"] SEC["SecurityInterceptor
HTTP Basic · PBKDF2
roles: user / admin"] DAO["DAO layer · @Stateless
Assembly · Node · Part · Edge
Connector · Asset · User · Setting"] GRAPH["GraphLoader
@Startup @Singleton
in-memory graph cache"] ENGINE["ProductBuilder + PartAligner
walk graph · align parts in 3D"] MONGO["MongoClientProvider
GridFS 3D models + textures"] end end MYSQL[("MySQL 5.6
users · assemblies · nodes
parts · edges · connectors
assets · settings")] MONGODB[("MongoDB · GridFS
Three.js model JSON
texture images")] EDITOR --> WEDITOR EMBED --> WEMBED WEDITOR -->|"static SPA"| EDITOR WEMBED -->|"static bundle"| EMBED EDITOR -->|"/rest/* · Basic auth"| SEC EMBED -->|"https://nordlytics.se/editor/rest/*"| SEC SEC --> REST REST --> DAO REST -->|"POST /configurator/product"| ENGINE GRAPH --> ENGINE DAO <--> MYSQL DAO --> MONGO MONGO --> MONGO ENGINE -->|"ProductResponse
added parts + choices"| REST REST -->|"JSON"| EDITOR REST -->|"JSON"| EMBED subgraph ops["Build & deploy"] MVN["Maven multi-module
ejb · web-editor · web-embed · ear"] DOCKER["Docker · MySQL + MongoDB"] end MVN -->|"deploy Configurator.ear"| jboss DOCKER --> MYSQL DOCKER --> MONGODB
The configuration graph
The whole product model is a directed graph. The diagram below shows how the
entities relate: a user owns assemblies; an assembly has nodes and directed
edges (node → node); each node holds parts; each part references a 3D asset and
exposes connectors that sit on an edge; connectors carry the 3D position and
rotation that the PartAligner uses to snap parts together.
PBKDF2 · roles"] ASM["AssemblyEntity
uuid"] NODE["NodeEntity"] EDGE["EdgeEntity
composite PK: source → target"] PART["PartEntity"] CONN["ConnectorEntity
Position + Rotation"] ASSET["AssetEntity
scale · Position · Rotation"] GRIDFS[("MongoDB GridFS
asset_store_id (ObjectId)")] SETTING["SettingEntity
key / value"] USER -->|"1..* user_id"| ASM ASM -->|"1..* assembly_id"| NODE NODE -->|"source"| EDGE NODE -->|"target"| EDGE NODE -->|"1..* node_id"| PART PART -->|"1..* part_id"| CONN EDGE -->|"edge_source/target"| CONN ASSET -->|"1..* asset_id"| PART ASSET -->|"asset_store_id"| GRIDFS USER -.->|"1..* user_id"| ASSET SETTING -.->|"mongo_db · mongo_collection"| GRIDFS
The full entity-relationship diagram of the relational schema is below.
Architecture by component
1. Backend (Java EE 6)
A classic Java EE stack — EJB 3.1 + CDI + JPA 2.0 (Hibernate 3.6) + JAX-RS 1.1 (RESTEasy) + Hibernate Search — packaged as a single EAR and deployed to JBoss AS 7.1.1. The Maven reactor builds four modules:
configurator-ejb(packagingejb) — all domain logic: JPA entities,@StatelessDAOs, JAX-RS resources, the graph/product engine and the MongoDB integration.configurator-web-editor(packagingwar, context root/editor) — the Backbone authoring SPA, built with Bower + RequireJS (r.jsoptimizer).configurator-web-embed(packagingwar, context root/embed) — the embeddable viewer widget, built with npm + Gulp (Browserify).configurator-ear(packagingear,Configurator.ear) — aggregates the EJB jar and both WARs viaapplication.xml.
Entity layer. Nine JPA entities in com.jiekebo.model.persistence map the
configuration graph to MySQL: UserEntity, AssemblyEntity, NodeEntity,
PartEntity, EdgeEntity (composite key source/target via @IdClass),
ConnectorEntity, AssetEntity, SettingEntity and HibernateSequenceEntity.
Two @Embeddable value objects — Position (x/y/z) and Rotation (i/j/k
Euler angles with full rotation-matrix math) — carry the 3D transforms. An
AssetStoreEntity POJO (not @Entity) represents a parsed Three.js mesh and
lives in MongoDB, not MySQL.
DAO layer. @Stateless session beans (AssemblyDao, NodeDao,
PartDao, EdgeDao, ConnectorDao, AssetDao, UserDao, SettingDao,
AssetStoreDao) wrap a @PersistenceContext EntityManager. AssetStoreDao
is the exception — it talks to MongoDB GridFS for 3D model files and the
texture collection for base64-encoded texture images.
Business layer. GraphLoader (@Startup @Singleton) builds an in-memory
graph cache (ConcurrentHashMap) of all assemblies at startup; ProductBuilder
is the configurator engine — it loads the graph, applies the user’s part
choices, walks adjacent nodes and runs PartAligner to compute 3D positions,
returning a ProductResponse with the added parts and the next available
choices. UserController handles PBKDF2 password hashing and authentication.
REST layer. JaxRsActivator (@ApplicationPath("rest")) activates JAX-RS
under /rest/. Resources expose full CRUD for assemblies, nodes, parts, edges,
connectors and assets, plus GraphService (graph cache rebuild) and
UserService (auth). The flagship endpoint is
POST /rest/configurator/product (@PermitAll) — it takes a ProductRequest
and returns the assembled product. SecurityInterceptor (a RESTEasy
PreProcessInterceptor) enforces HTTP Basic auth with @RolesAllowed/@PermitAll
and pushes the authenticated UserEntity into the request context;
CorsInterceptor adds the CORS headers the embed widget needs to call the API
cross-origin.
2. Frontend — editor SPA (Backbone + Three.js + d3)
A Backbone.js single-page app built with RequireJS/Bower, deployed as a WAR at
/editor. Templating is Handlebars, utility is lodash, DOM is jQuery — the
classic Backbone stack. Routing (Backbone.Router) drives four pages:
- Configurator (
#configurator/:id) — the end-user product configuration page. AProductcollection POSTs choices to/rest/configurator/product, the 3DEditorViewre-renders the assembled product, and anAvailableChoicespanel shows the dropdowns for the next configurable parts. AnEmbedStringViewgenerates a copy-to-clipboard embed snippet (<script>tag +data-uuiddiv) via ZeroClipboard. - Graph Editor (
#graphEditor/:id) — a node/edge graph editor for an assembly, drawn with d3 v3 force layout (d3.layout.force, SVG). Add, rename and delete nodes; drag to create directed edges. - Part Editor (
#partEditor/:id) — a Three.js editor for parts, assets and connectors within a node. Camera/select/translate/rotate/scale modes viaTransformControls; connectors are pickable magenta cubes selected byTHREE.Raycaster. - Dashboard (
#home) — the landing page.
Shared 3D components: EditorView (a THREE.WebGLRenderer viewport with a
PerspectiveCamera, OrbitControls, TransformControls and a per-frame
render loop over three scenes — modelScene, connectorScene, editorScene)
and SceneManager (loads Three.js JSON geometries from the backend asset
store via THREE.JSONLoader, places/rotates/scales meshes, manages connector
tokens).
State is classic Backbone — models and collections are the stores, a shared
Backbone.Events bus decouples views, and act.js (a custom command/undo
library) wraps every mutation as an Act.Action with persist/destroy
callbacks that drive model.save()/model.destroy() against the REST API.
3. Frontend — embeddable widget (Backbone + Three.js)
A Browserify/Gulp-built read-only subset of the editor, deployed as a WAR at
/embed and consumable by any third-party page with a single script tag and a
container:
<div id="configurator" data-uuid="e2aa656e-cc5b-4871-b752-4e33eaf2ffc9"></div>
<script src="/embed/configurator-0.0.1-SNAPSHOT.js"></script>
The widget reads the data-uuid, loads the assembly, drives the
POST /rest/configurator/product flow and renders the assembled product in
Three.js — the same Configurator + Controls + AvailableChoices +
EditorView + SceneManager views as the editor, minus the authoring tools.
Example pages (table.html, ikea.html, lego.html, aiaiai.html) under
src/htdocs/example/ show it skinned for different products.
4. Databases
Two stores, split by data shape:
- MySQL 5.6 (InnoDB) — the configuration graph. Datasource
java:jboss/datasources/configurator, Hibernate dialectMySQL5InnoDBDialect,hbm2ddl.auto=updatekeeps the schema in sync with the entities. Nine tables:user,assembly,node,part,edge,connector,asset,setting,hibernate_sequence. Schema and seed data live indocker/mysql/dump.sql. - MongoDB (GridFS) — the 3D model assets.
AssetEntity.asset_store_id(an ObjectId) links the MySQLassetrow to a GridFS file containing a Three.js JSON geometry; a separatetexturecollection stores base64-encoded texture images keyed byasset_id.MongoClientProvider(@Singleton) manages the connection; the db and collection names are overridable via thesettingtable.
Both databases are bootstrapped with Docker (docker/1-mysql-run.sh …
4-mongodb-dbinit.sh), and docker/start-databases.sh runs them together.
The UI
The screenshot below shows the configurator in action — the Three.js WebGL viewer rendering an assembled product with the available-choices panel driving the configuration.
Technologies used
Backend
- Java EE 6 · EJB 3.1 · CDI
- JPA 2.0 / Hibernate 3.6
- JAX-RS 1.1 / RESTEasy
- Hibernate Search (Lucene/Solr)
- JBoss AS 7.1.1
- Maven multi-module (EAR)
Frontend — editor
- Backbone.js + lodash + jQuery
- Handlebars templates
- RequireJS / r.js (Bower)
- Three.js r93 (WebGL)
- d3 v3 force layout (graph editor)
- act.js (custom undo/redo)
Frontend — embed
- Backbone.js + Three.js (read-only)
- Browserify / Gulp build
- Drop-in <script> widget
- data-uuid assembly loading
- Cross-origin REST (CORS)
Data & infra
- MySQL 5.6 (InnoDB)
- MongoDB + GridFS (3D assets)
- Docker (MySQL + MongoDB)
- HTTP Basic / PBKDF2 auth
- JAX-RS interceptors (security + CORS)
- SonarQube (code quality)
Status: a complete, deployed Java EE product configurator — a graph-driven engine that resolves configurable products in 3D, a Backbone + Three.js authoring SPA, an embeddable consumer widget, and the MySQL + MongoDB data layer backing it all. The architecture above describes the EAR as it ships to JBoss.