Skip to content

Repository files navigation

JWebMP Core

BuildMaven CentralMaven SnapshotLicense

Java 25+Guice 7Vert.X 5Maven 4

Strongly-typed HTML/CSS/JS component model for Java — the heart of JWebMP.

The core defines the HTML language in Java — every HTML element, CSS property, event, and feature is a first-class Java object using CRTP generics. It generates the HTML, JavaScript, and CSS required for any page, and provides the SPI contracts that allow a mass ecosystem of plugins to be created that adhere to the component model.

Every component serves dual purposes:

  • toString(true) renders the component as HTML (with all queued CSS/JS) — use this for server-side rendering, template generation, or serving pages on the Vert.x router
  • toString() renders the component as JSON — use this for AJAX responses, API payloads, or any scenario where the component tree needs to be serialized

Pages can optionally be annotated with @PageConfiguration and served directly via the Vert.x HTTP server, or components can be used standalone to generate HTML/JSON without a server at all.

Provides Page, the full HTML element library (Div, Span, Table, Form, …), a typed CSS builder, 50+ server-driven event adapters, page configurators that inject scripts/styles before render, and a Jackson-serialized AJAX pipeline for live DOM updates. Extension is SPI-driven via ServiceLoader.

Built on JWebMP Client · GuicedEE · Vert.x 5 · JPMS module com.jwebmp.core · Java 25+

📦 Installation

<dependency>
<groupId>com.jwebmp.core</groupId>
<artifactId>core</artifactId>
</dependency>
Gradle (Kotlin DSL)
implementation("com.jwebmp.core:jwebmp-core:2.0.0-SNAPSHOT")

Version is managed by the JWebMP BOM.

✨ Features

  • HTML language in Java — the core defines the entire HTML5 element set as typed Java classes, along with CSS and JavaScript rendering rules, forming the foundation that all JWebMP plugins build on top of
  • Dual rendering modestoString(true) produces full HTML with all CSS/JS assets; toString() produces JSON for AJAX/API consumption — same component tree, two output formats
  • Complete HTML element library — every HTML5 element (Div, Span, Table, Form, Input, Select, Canvas, Video, Article, Section, …) is a typed Java class with CRTP fluent API; child constraints are enforced at compile time
  • Typed input elementsInputTextType, InputEmailType, InputNumberType, InputDateType, InputFileType, InputCheckBoxType, InputRadioType, and 15 more — each with the correct HTML attributes
  • Typed CSS builder — annotation-driven CSS properties (@CSS) across 14 sub-packages: backgrounds, borders, colours, fonts, margins, padding, displays, lists, tables, text, outline, measurement, height/width, image — rendered inline or composed via CSSPropertiesFactory
  • 50+ server-driven event adaptersOnClickAdapter, OnChangeAdapter, OnSubmitAdapter, OnDragAdapter, OnKeyDownAdapter, OnMouseEnterAdapter, and many more — each backed by a ServiceLoader SPI (IOnClickService, IOnChangeService, etc.). Events must be their own named classes — anonymous inner classes and lambdas are not supported because the framework relies on class identity for serialization and ServiceLoader discovery
  • Page configurators (optional)IPageConfigurator SPI is an optional hook that modifies pages before rendering — use it to inject CSS links, JavaScript references, dynamic scripts, or top-shelf scripts in priority order; pages work perfectly fine without any configurators
  • Render-ordering hooksRenderBeforeLinks, RenderAfterLinks, RenderBeforeScripts, RenderAfterScripts, RenderBeforeDynamicScripts, RenderAfterDynamicScripts — fine-grained control over asset insertion
  • AJAX pipelineAjaxCall/AjaxResponse (from jwebmp-client) carry event payloads and DOM update instructions; Event base class wires server handlers to browser events
  • Feature systemFeature<O, J> wraps a JavaScript library with typed options (JavaScriptPart), CSS/JS references, and automatic dependency ordering. Features must be their own named classes — the framework uses class identity for deduplication, dependency ordering, and Jackson serialization
  • CSSComponent — render-only CSS classes (no HTML tag) for reusable style-only components
  • DataAdapter — bridges server-side data into raw JSON for component consumption
  • Content Security PolicyContentSecurityPolicy builder for CSP headers
  • Jackson moduleJWebMPJacksonModule registers custom event deserializers at pre-startup
  • Component hierarchy — 11-layer deep CRTP chain: ComponentBaseComponentHierarchyBaseComponentHTMLBaseComponentHTMLAttributeBaseComponentHTMLOptionsBaseComponentStyleBaseComponentThemeBaseComponentDataBindingBaseComponentDependencyBaseComponentFeatureBaseComponentEventBaseComponent → your subclass
  • Utility beltGUIDGenerator, ColourUtils, TextUtilities, EscapeChars, DateUtils, regex patterns (email, date, text)

🚀 Quick Start

Build a page in pure Java

No server needed — just create components and render:

Page<?> page = newPage<>();
Div<?, ?, ?> container = newDiv<>();
container.add(newParagraph<>().setText("Welcome to JWebMP"));
page.getBody().add(container);
Stringhtml = page.toString(true); // full HTML + queued CSS/JSStringjson = page.toString(); // JSON representation of the component tree

Optionally serve it with GuicedEE + Vert.x

Annotate with @PageConfiguration to host the page on the Vert.x HTTP server — this is optional and only needed when you want server-side rendering via Vert.x:

@PageConfiguration(url = "/")
publicclassHomePageextendsPage<HomePage> {
publicHomePage() {
getBody().add(newH1<>().setText("Hello from JWebMP"));
getBody().add(newParagraph<>().setText("Server-rendered, type-safe HTML."));
}
}
modulemy.app {
requirescom.jwebmp.core;
providescom.jwebmp.core.services.IPagewithmy.app.HomePage;
}

Boot GuicedEE — the page is now live:

IGuiceContext.instance().inject();
// Vert.x HTTP server starts, HomePage served at "/"

Handle a click event

Events must be their own named classes — define a top-level or static inner class:

publicclassButtonClickEventextendsOnClickAdapter {
publicButtonClickEvent(Componentcomponent) {
super(component);
}
@OverridepublicvoidonClick(AjaxCall<?> call, AjaxResponse<?> response) {
response.addComponent(newParagraph<>().setText("Clicked!"));
}
}

Then attach it to any component:

Div<?, ?, ?> button = newDiv<>();
button.setText("Click me");
button.addEvent(newButtonClickEvent(button));
page.getBody().add(button);

⚠️Anonymous inner classes and lambdas are not supported for events or features. The framework relies on class identity for serialization, ServiceLoader discovery, and deduplication. Always create a named class.

Add a Feature with typed options

publicclassTooltipFeatureextendsFeature<TooltipOptions, TooltipFeature> {
publicTooltipFeature(IComponentHierarchyBase<?, ?> component) {
super("tooltip", component);
getOptions().setPlacement("top");
}
@OverrideprotectedvoidassignFunctionalityToComponent() {
addQuery(getComponent().asBase().getJQueryID() + "tooltip(" + getOptions() + ");");
}
}

Use the CSS builder

Div<?, ?, ?> styled = newDiv<>();
styled.getCss()
.getBackground().setBackgroundColor$(ColourNames.AliceBlue);
styled.getCss()
.getBorder().setBorderWidth(newMeasurementCSSImpl(1, MeasurementTypes.Pixels));
styled.getCss()
.getFont().setFontSize(newMeasurementCSSImpl(14, MeasurementTypes.Pixels));

📐 Page Render Flow

IGuiceContext.instance().inject()
└─ JWebMPPreStartup (IGuicePreStartup, sortOrder=15)
└─ Register JWebMPJacksonModule with ObjectMapper
HTTP request arrives at @PageConfiguration URL
└─ IPage instance created (via Guice binding → Page.class)
└─ IPageConfigurator chain (sorted by sortOrder)
├─ CSSLinksInsertPageConfigurator
│ ├─ RenderBeforeLinks SPIs
│ ├─ CSS <link> tags inserted into <head>
│ └─ RenderAfterLinks SPIs
├─ ScriptsInsertPageConfigurator
│ ├─ RenderBeforeScripts SPIs
│ ├─ <script> tags inserted (by RequirementsPriority)
│ └─ RenderAfterScripts SPIs
├─ TopShelfScriptsInsertPageConfigurator
│ └─ High-priority scripts in <head>
└─ ScriptsDynamicPageConfigurator
├─ RenderBeforeDynamicScripts SPIs
├─ Inline/dynamic <script> blocks
└─ RenderAfterDynamicScripts SPIs
└─ page.toString(true) → full HTML response

🔌 SPI Extension Points

All SPIs are discovered via ServiceLoader. Register implementations with JPMS provides...with or META-INF/services.

IPage

Provide a page implementation — typically by subclassing Page:

@PageConfiguration(url = "/dashboard")
publicclassDashboardextendsPage<Dashboard> {
publicDashboard() {
getBody().add(newH1<>().setText("Dashboard"));
}
@OverridepublicAjaxResponse<?> onConnect(AjaxCall<?> call, AjaxResponse<?> response) {
// handle initial connectionreturnresponse;
}
}

IPageConfigurator

Configure pages before rendering — inject CSS, JS, meta tags, or transform the component tree:

publicclassAnalyticsConfiguratorimplementsIPageConfigurator<AnalyticsConfigurator> {
@OverridepublicIPage<?> configure(IPage<?> page) {
page.addJavaScriptReference(newJavascriptReference("analytics", 1.0, "analytics.js"));
returnpage;
}
@OverridepublicIntegersortOrder() {
return500; // after core configurators
}
}

IEventConfigurator

Transform any Event after creation — add global behaviour to all events:

publicclassEventLoggerimplementsIEventConfigurator<EventLogger> {
@OverridepublicEvent<?, ?> configureEvent(Event<?, ?> event) {
// add logging, metrics, or security checksreturnevent;
}
}

Render-ordering SPIs

SPIPurpose
RenderBeforeLinksInsert content before CSS <link> tags
RenderAfterLinksInsert content after CSS <link> tags
RenderBeforeScriptsInsert content before <script> tags
RenderAfterScriptsInsert content after <script> tags
RenderBeforeDynamicScriptsInsert content before dynamic/inline scripts
RenderAfterDynamicScriptsInsert content after dynamic/inline scripts

Event SPIs

Each of the 50+ event types has a corresponding service interface discovered via ServiceLoader:

SPIEvent
IOnClickServiceClick
IOnChangeServiceChange
IOnSubmitServiceSubmit
IOnKeyDownServiceKey down
IOnMouseEnterServiceMouse enter
IOnDragService / IOnDropServiceDrag & drop
IOnFocusService / IOnBlurServiceFocus / blur
… and 40+ moreSee com.jwebmp.core.events.services

🧱 Component Hierarchy

The CRTP component chain provides progressive enhancement — each layer adds a capability:

ComponentBase → ID, name, properties, JSON serialization
└─ ComponentHierarchyBase → parent/child tree, add(), CSS/JS references
└─ ComponentHTMLBase → tag rendering, text, raw HTML, newlines
└─ ComponentHTMLAttributeBase → HTML attributes (typed enums)
└─ ComponentHTMLOptionsBase → JavaScript options (JavaScriptPart)
└─ ComponentStyleBase → inline CSS via the CSS builder
└─ ComponentThemeBase → theme support
└─ ComponentDataBindingBase → data-bind hooks
└─ ComponentDependencyBase → CSS/JS dependency refs
└─ ComponentFeatureBase → Feature attachment
└─ ComponentEventBase → Event attachment
└─ Component → final user-facing base

Concrete HTML elements (Div, Table, Form, etc.) extend Component and lock down child/attribute/event types via generics.

Note: The hierarchy depth is a design-time concern only. Modern JDK versions flatten class hierarchies at runtime, so the 11-layer chain has zero performance overhead — you get full type safety with no cost.

🎨 CSS Builder Packages

PackageProperties
css.backgroundsbackground-color, background-image, background-repeat, …
css.bordersborder-width, border-style, border-color, border-radius, …
css.coloursNamed colours, colour enumeration
css.fontsfont-family, font-size, font-weight, font-style, …
css.marginsmargin-top, margin-right, margin-bottom, margin-left
css.paddingpadding-top, padding-right, padding-bottom, padding-left
css.displaysdisplay, visibility, overflow, position, float, …
css.texttext-align, text-decoration, text-transform, line-height, …
css.tablesborder-collapse, border-spacing, table-layout, …
css.listslist-style-type, list-style-position, …
css.outlineoutline-width, outline-style, outline-color
css.heightwidthheight, width, min-height, max-width, …
css.measurementMeasurementCSSImpl — px, em, rem, %, vw, vh, …
css.animatableAnimatable property markers

⚙️ Configuration

Variable / PropertyDefaultPurpose
@PageConfiguration(url=)"/"URL path for page discovery
Environment-driven portsvia jwebmp-vertxHTTP/HTTPS listen ports

🗺️ Module Graph

com.jwebmp.core
├── com.jwebmp.client (SPI contracts, AjaxCall/Response, interceptors)
├── com.guicedee.client (DI, lifecycle, CallScope)
├── com.guicedee.vertx (Vert.x lifecycle, event bus)
├── com.guicedee.jsonrepresentation (Jackson ObjectMapper, IJsonRepresentation)
├── io.vertx.core (Vert.x core)
├── io.vertx.web (Vert.x Web — Router)
├── com.google.guice (Dependency injection)
├── com.fasterxml.jackson.databind (JSON serialization)
├── jakarta.validation (Bean Validation)
├── org.apache.commons.lang3 (String utilities)
├── org.apache.commons.text (Text utilities)
├── org.apache.commons.io (IO utilities)
├── net.sf.uadetector.core (User-agent parsing)
└── net.sf.uadetector.resources

🧩 JPMS

Module name: com.jwebmp.core

The module:

  • exportscom.jwebmp.core, com.jwebmp.core.base, com.jwebmp.core.base.html, com.jwebmp.core.base.html.inputs, com.jwebmp.core.base.page, com.jwebmp.core.components, com.jwebmp.core.events.* (50+ packages), com.jwebmp.core.htmlbuilder.css.* (14 packages), com.jwebmp.core.utilities, com.jwebmp.core.enumerations, com.jwebmp.core.implementations
  • providesIGuiceModule with JWebMPServicesBindings, IGuicePreStartup with JWebMPPreStartup, IGuiceScanModuleInclusions with JWebMPModuleInclusions, IPageConfigurator with CSSLinksInsertPageConfigurator, ScriptsInsertPageConfigurator, ScriptsDynamicPageConfigurator, TopShelfScriptsInsertPageConfigurator, IRegularExpressions with regex pattern providers
  • usesIPage, IPageConfigurator, IErrorPage, IEventConfigurator, IDynamicRenderingServlet, IOnComponentConfigured, IOnDataBind, IOnDataBindCloak, IOnComponentHtmlRender, IOnComponentAdded, IAfterRenderComplete, all RenderBefore*/RenderAfter* SPIs, all 50+ event service SPIs

🏗️ Key Classes

ClassRole
PageTop-level HTML page — subclass and annotate with @PageConfiguration
ComponentBase class for all HTML elements — CRTP with typed children, attributes, features, events
CSSComponentCSS-only component (renders as class, no HTML tag)
FeatureWraps a JS library — typed options, CSS/JS references, jQuery integration
EventServer-driven event handler — wires browser events to AjaxCall/AjaxResponse
DataAdapterBridges server data into raw JSON for component consumption
ContentSecurityPolicyBuilder for CSP headers
PageOptionsPage-level options (title, author, favicon, keywords, etc.)
CSSPropertiesFactoryRenders @CSS-annotated objects into inline CSS strings
JWebMPServicesBindingsIGuiceModule — binds IPage, page configurators, render-ordering SPIs
JWebMPPreStartupIGuicePreStartup — registers JWebMPJacksonModule at startup
JWebMPJacksonModuleJackson module — custom Event and IEventTypes deserializers
IEventConfiguratorSPI — transform any Event after creation
GUIDGeneratorUnique ID generation for components

HTML Elements (partial list)

ElementClassChildren type
<div>DivGlobalChildren
<span>SpanGlobalChildren
<p>ParagraphGlobalChildren
<h1><h6>H1H6GlobalChildren
<table>TableTableHeaderGroup, TableBodyGroup, TableFooterGroup
<form>FormFormChildren
<input>Input + 22 typed variantsinline
<select>SelectOption, OptionGroup
<ul> / <ol>ListListItem
<a>LinkGlobalChildren
<img>Imageinline
<button>ButtonGlobalChildren
<header> / <footer> / <nav> / <section> / <article> / <aside>Semantic elementsGlobalChildren
<canvas> / <video> / <audio>Media elementsSource, Track
<script> / <style> / <link>Asset elements

🧪 Build & Test

# Build
mvn -B -ntp -DskipTests package
# Test
mvn -B -ntp verify

Prerequisites: Java 25+, Maven 3.9+

🤝 Contributing

Issues and pull requests are welcome at github.com/JWebMP/Core/issues.

📄 License

Apache 2.0

About

JWebMP is a Java Web UI Framework for the rapid development of data bound web applications. It allows single language, bi-directional databinding and many more features

Topics

Resources

Stars

5 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages