Generates Angular 21 TypeScript projects from JWebMP annotation metadata and serves the built SPA via Vert.x 5 with STOMP/WebSocket bridging. Annotate your Java classes with @NgApp, @NgComponent, @NgRoutable, @NgDataService, etc. — the compiler produces .ts files, angular.json, package.json, tsconfig.json, routing modules, and environment config. At runtime, the plugin hosts the compiled dist directory with SPA-fallback routing, static-asset serving, and a full STOMP event-bus bridge.
TypeScript code generation — TypeScriptCompiler orchestrates TypeScriptCodeGenerator, AngularModuleProcessor, ComponentProcessor, AngularAppSetup, AssetManager, DependencyManager, and TypeScriptCodeValidator to produce a complete Angular project from Java annotationsSPA hosting via Vert.x — serves the dist/ output with StaticHandler, explicit /assets/* mount, regex-based static file routing, and an index.html SPA fallback for Angular Router client-side routesSTOMP/WebSocket bridge — StompServer at /eventbus with configurable heartbeats (10s server → client, 50s client tolerance), v10/v11/v12.stomp sub-protocols, inbound /toBus.* and outbound /toStomp.* address patternsReactive message processing — inbound STOMP messages on /toBus/incoming are deserialised to WebSocketMessageReceiver, dispatched to registered IWebSocketMessageReceiver listeners, and responses are published back via the event bus (data returns, session/local storage)Built-in WebSocket receivers — WebSocketAjaxCallReceiver (action: ajax), WebSocketDataRequestCallReceiver (action: data), WebSocketDataSendCallReceiver (action: dataSend), WSAddToGroupMessageReceiver (action: AddToWebSocketGroup), WSRemoveFromWebsocketGroupMessageReceiver (action: RemoveFromWebSocketGroup)Angular control-flow components — NgIf, NgIfElse, NgElse, NgFor, NgForEmpty, NgLet rendering Angular 17+ @if, @for, @let template syntax from JavaAngular modules — pre-built CommonsModule, FormsModule, RouterModule wrappersRouting — AngularRoutingModule scans @NgRoutable-annotated classes, builds a DefinedRoute tree with parent/child nesting, and generates RouterModule.forRoot(routes) with configurable RoutingModuleOptionsEnvironment module — EnvironmentModule + EnvironmentOptions generates a TypeScript const environment = { ... } from Java configurationRouterLink — server-side RouterLink component with [routerLink], [queryParams], and [state] bindingOnClick directive — OnClickListenerDirective + OnClickListener bridges JWebMP click events to Angular directivesWebSocket group management — WebSocketGroupsDirective + WebSocketGroupAdd SPI for declarative WebSocket group subscription from componentsSTOMP publisher helper — StompEventBusPublisher for simple vertx.eventBus().publish("/toStomp/<addr>", message) callsNPM resource locator — discovers and serves bundled node_modules resources from the classpathAngular project scaffolding — generates angular.json, package.json, tsconfig.json, tsconfig.app.json, .gitignore, and app.config.json templates from bundled resources@NgApp page disables JWebMP page routing — AngularPreStartup sets BIND_JW_PAGES=false so Vert.x routes are managed by this plugin instead<dependency >
<groupId >com.jwebmp.plugins</groupId >
<artifactId >angular</artifactId >
</dependency > Version is managed by the com.jwebmp:jwebmp-bom imported in the parent POM.
module com.jwebmp.core.angular {
requires transitive com.jwebmp.core.base.angular.client;
requires transitive com.jwebmp.vertx;
requires transitive io.vertx.eventbusbridge;
requires transitive io.vertx.stomp;
requires transitive org.apache.commons.text;
requires transitive org.apache.commons.lang3;
requires org.apache.commons.io;
provides IGuiceScanModuleInclusions with AngularScanModuleInclusion;
provides IGuiceConfigurator with SearchPathConfigurator;
provides IGuicePreStartup with AngularPreStartup;
provides IGuiceModule with AngularTSSiteBinder;
provides VertxRouterConfigurator with AngularTSSiteBinder;
provides VertxHttpServerOptionsConfigurator with AngularTSSiteBinder;
provides IGuicePostStartup with AngularTSPostStartup;
provides IOnComponentConfigured with ConfigureImportReferences;
provides IOnClickService with OnClickListener;
provides IWebSocketMessageReceiver with WebSocketAjaxCallReceiver,
WebSocketDataRequestCallReceiver,
WebSocketDataSendCallReceiver,
WSAddToGroupMessageReceiver,
WSRemoveFromWebsocketGroupMessageReceiver;
uses AngularScanPackages;
uses RenderedAssets;
uses NpmrcConfigurator;
uses WebSocketGroupAdd;
uses TypescriptIndexPageConfigurator;
uses IPageConfigurator;
uses IWebSocketAuthDataProvider;
}
@ NgApp (value = "my-app" , bootComponent = AppComponent .class )
public class MyApp extends NGApplication <MyApp > { }2. Create the boot component @ NgComponent ("app-root" )
public class AppComponent extends DivSimple <AppComponent >
implements INgComponent <AppComponent > { }@ NgRoutable (path = "dashboard" , parent = {AppComponent .class })
@ NgComponent ("app-dashboard" )
public class DashboardPage extends DivSimple <DashboardPage >
implements INgComponent <DashboardPage > { }4. Enable TypeScript generation # Environment variableexport JWEBMP_PROCESS_ANGULAR_TS=true
# Or system property
mvn clean verify -Djwebmp.process.angular.ts=trueIGuiceContext .instance ();
// → TypeScript is generated to ~/.jwebmp/<appName>/ // → Vert.x serves the dist at the configured routes // → STOMP/WebSocket bridge is live at /eventbus Note: The plugin generates the TypeScript project but does not run ng build. Build the Angular app separately (e.g. cd ~/.jwebmp/my-app && npm install && ng build) and the dist output is served automatically.
IGuiceContext.instance()
└─ AngularPreStartup → sets BIND_JW_PAGES=false
└─ SearchPathConfigurator → enables annotation/classpath/path scanning
└─ AngularTSSiteBinder → binds EnvironmentModule, discovers @NgApp classes,
│ creates output dirs, configures STOMP server + router
└─ AngularTSPostStartup → if TS processing enabled, runs TypeScriptCompiler.compileApp()
for each @NgApp (worker pool, reactive Uni pipeline)
TypeScript Compiler Pipeline TypeScriptCompiler
├── AngularAppSetup → scaffolds angular.json, package.json, tsconfig, .gitignore
├── DependencyManager → resolves @TsDependency / @TsDevDependency
├── ComponentProcessor → processes each INgComponent, INgDirective, INgDataService, etc.
├── AngularModuleProcessor → generates boot module, routing module, module imports
├── TypeScriptCodeGenerator → renders .ts files from annotation metadata
├── AssetManager → copies @NgAsset, @NgScript, @NgStyleSheet resources
└── TypeScriptCodeValidator → validates generated output
Route Method Handler Purpose /eventbus/*WebSocket STOMP server WebSocket → STOMP bridge /assets/*GET StaticHandler Angular compiled assets (1-year cache) /{file}.{ext} (excl. index.html)GET StaticHandler Root-level static files /** (SPA fallback)GET sendFile(index.html)Angular Router client-side routes Route-per-@NgRoutable GET StaticHandler Component-level route bindings
STOMP/WebSocket Message Flow Angular Client (STOMP)
└─ ws://host/eventbus
├─ SEND /toBus/incoming → event bus consumer
│ ├─ deserialise WebSocketMessageReceiver
│ ├─ dispatch to registered IWebSocketMessageReceiver (ajax / data / dataSend / group ops)
│ ├─ run in CallScope (CallScopeSource.WebSocket)
│ └─ reply or publish results back:
│ ├─ dataReturns → publish to per-key event bus addresses
│ ├─ sessionStorage → publish to "SessionStorage"
│ └─ localStorage → publish to "LocalStorage"
└─ SUBSCRIBE /toStomp/* ← server pushes via StompEventBusPublisher
WebSocket Message Receivers Receiver Action Purpose WebSocketAjaxCallReceiverajaxDeserialises AjaxCall, resolves IEvent, fires event reactively, returns AjaxResponse WebSocketDataRequestCallReceiverdataResolves INgDataService, calls getData(), adds to response data returns WebSocketDataSendCallReceiverdataSendResolves INgDataService, calls receiveData() WSAddToGroupMessageReceiverAddToWebSocketGroupAdds session to a WebSocket group WSRemoveFromWebsocketGroupMessageReceiverRemoveFromWebSocketGroupRemoves session from a WebSocket group
Angular Control-Flow Components Java Class Angular Output Purpose NgIf@if (condition) { ... }Conditional rendering NgIfElse@if (condition) { ... } @else { ... }Conditional with else NgElse@else { ... }Else block NgFor@for (item of list; track trackBy) { ... }Loop with track, index, count, odd/even/first/last NgForEmpty@empty { ... }Empty block for @for NgLet@let variable = expression;Local variable declaration
Class Role AngularTSSiteBinderCore Guice module + VertxRouterConfigurator + VertxHttpServerOptionsConfigurator — binds environment, configures STOMP, wires router, processes inbound messages AngularTSPostStartupPost-startup hook that runs TypeScriptCompiler.compileApp() for each @NgApp AngularPreStartupPre-startup hook that disables JWebMP page routing (BIND_JW_PAGES=false) TypeScriptCompilerOrchestrates all compiler stages (setup, dependencies, components, modules, assets, validation) NGApplicationBase class for @NgApp — extends Page, implements INgApp + INgComponent, sets up HTML head/base/meta AngularRoutingModuleScans @NgRoutable classes, builds route tree, generates RouterModule.forRoot(routes) EnvironmentModuleGenerates const environment = { ... } from EnvironmentOptions StompEventBusPublisherStatic helper for publishing to /toStomp/<address> ConfigureImportReferencesIOnComponentConfigured that processes the component tree and configures Angular import referencesRouterLinkServer-side anchor component with [routerLink], [queryParams], [state] bindings AngularWebSocketHelper for binding websocketjw, websocketgroup, websocketdata attributes to components
SPI Purpose AngularScanPackagesAdd additional packages to the Angular classpath scan RenderedAssetsProvide additional assets to include in the build NpmrcConfiguratorCustomise .npmrc file lines WebSocketGroupAddCustom logic when a component joins a WebSocket group TypescriptIndexPageConfiguratorCustomise the generated index.html IWebSocketAuthDataProviderProvide authentication data for WebSocket connections IPageConfiguratorStandard JWebMP page configuration hooks
Environment Variable / System Property Default Purpose JWEBMP_PROCESS_ANGULAR_TS / jwebmp.process.angular.tsfalseEnable/disable TypeScript generation on startup jwebmp.outputDirectory— Override the generated output directory jwebmp~ (user home)Base directory (output goes to <base>/.jwebmp/<appName>) ENVIRONMENTdevRuntime environment hint for EnvironmentOptions PORT8080Server port
Setting Value Notes WebSocket path /eventbusSTOMP over WebSocket endpoint Server heartbeat (x) 10000 msServer → client heartbeat interval Client heartbeat (y) 50000 msExpected client → server interval (lenient for background tabs) Max body/header/frame Integer.MAX_VALUENo size limits Sub-protocols v10.stomp, v11.stomp, v12.stompAdvertised on HTTP upgrade Idle timeout 0 (disabled)Relies on STOMP heartbeats instead TCP keep-alive trueFor intermediary NATs/proxies Compression trueBoth request and response
Direction Address Pattern Purpose Inbound /toBus.*Client → server messages Outbound /toStomp.*Server → client pushes
com.jwebmp.core.angular
├── com.jwebmp.core.base.angular.client (TypeScript Client — annotations, interfaces, EventBusService)
├── com.jwebmp.vertx (JWebMP Vert.x bridge — HTTP routes, call scopes)
├── com.jwebmp.core (JWebMP Core — pages, events, AJAX)
├── com.guicedee.guicedinjection (Guice DI + classpath scanning)
├── io.vertx.stomp (Vert.x STOMP server)
├── io.vertx.eventbusbridge (Vert.x event bus bridge)
├── org.apache.commons.text (Template substitution)
├── org.apache.commons.lang3 (ExceptionUtils, StringUtils)
├── org.apache.commons.io (FileUtils, IOUtils)
└── org.mapstruct (DTO mapping — annotation processor)
Java : 25 LTSMaven : inherits com.jwebmp:parent:2.0.0-SNAPSHOTJPMS : module descriptor at src/main/java/module-info.javaAnnotation processors : Lombok + MapStruct (configured in pom.xml)Run tests:
GitHub Actions workflow at .github/workflows/maven-package.yml (GuicedEE shared workflow).
Required secrets: USERNAME, USER_TOKEN, SONA_USERNAME, SONA_PASSWORD.
Example environment file: .env.example.
Document Path Architecture overview docs/architecture/overview.mdArchitecture diagrams index docs/architecture/README.mdC4 Context docs/architecture/c4-context.mdC4 Container docs/architecture/c4-container.mdC4 Component docs/architecture/c4-component-angular.mdTS generation sequence docs/architecture/sequence-typescript-generation.mdWebSocket/STOMP sequence docs/architecture/sequence-websocket-stomp.mdDependency map docs/architecture/dependency-map.mdERD docs/architecture/erd-angular-plugin.mdInteraction flows docs/architecture/interaction-flows.md
Issues and pull requests are welcome. Use Log4j2 for logging and keep generated artifacts out of PRs.
Apache 2.0