This is the fourth post in a series about AI-assisted Jakarta EE development. After upgrading 30+ projects to Java 25, hardening the AI toolchain with security hooks, and building a CDI test extension from scratch, this time we tackle a practical gap in the MicroProfile ecosystem: a proper multi-app OpenAPI UI with project-stage support.
The Problem
If you've ever deployed multiple REST APIs on the same application server, you know the pain: each app needs its own OpenAPI UI, the server-level /openapi endpoint only serves one deployment, and the existing community addons like microprofile-extensions/openapi-ext don't support switching between APIs from a single UI.
On top of that, you probably want to disable the UI in production — but the existing addons auto-register via web-fragment.xml, giving you no control over when they're active.
The Solution: openapi-gui-addon
The openapi-gui-addon is a self-contained OpenAPI UI addon for Jakarta EE / MicroProfile applications that solves these problems. It builds on the microprofile-extensions openapi-ext but adds several key features:
- Multi-API dropdown — configure multiple OpenAPI endpoints and switch between them from a single UI
- Availability checking — on every page refresh, each configured endpoint is checked via a HEAD request. Reachable APIs appear as selectable, unreachable ones are shown as disabled below a separator
- Project-stage gating — disable the UI in production via config, no code changes needed (Jakarta EE 11) or via
Application.getClasses()(Jakarta EE 8) - Fully self-contained — Swagger UI bundled, no external requests
- Configurable via MicroProfile Config — all settings in
microprofile-config.properties
Quick Start
Add the dependency:
<dependency>
<groupId>org.os890.mp-ext</groupId>
<artifactId>openapi-gui-addon</artifactId>
<version>1.11.0</version>
</dependency>
Configure the endpoint in META-INF/microprofile-config.properties:
openapi.ui.yamlUrl=/my-app/openapi
openapi.ui.title=My API
mp.openapi.extensions.path=/my-app/openapi
That's it — your OpenAPI UI is available at /my-app/openapi-ui/. No code needed beyond your REST resources.
Multi-API Dropdown
The real power shows when you deploy multiple APIs on the same server. Add the openapi.ui.urls property to configure the dropdown:
openapi.ui.urls=Order API=/order-api/openapi,Customer API=/customer-api/openapi,Inventory API=/inventory-api/openapi
The format is simple: comma-separated Name=URL pairs. The UI then shows a "Select a definition" dropdown in the top bar. On every page load, each URL is checked for availability:
- Reachable APIs appear as normal selectable entries
- Unreachable APIs (not deployed, wrong URL, server down) appear below a "── Unavailable ──" separator as disabled/greyed-out entries
This is useful when you have a shared config (e.g., via a config JAR) that lists all known APIs — the UI gracefully handles APIs that aren't currently deployed.
Project-Stage Gating
The addon includes a built-in @PreMatching JAX-RS filter that controls UI access based on two MicroProfile Config properties:
openapi.ui.enabled— explicit override (true/false). When set, this takes precedence.project.stage— if set toproductionandopenapi.ui.enabledis not explicitly set, the UI is disabled.
This means: no code changes needed for project-stage gating. Just configure via properties.
Runtime Config (JVM System Property)
Pass -Dproject.stage=development to the JVM to enable the UI. Without it, the default behavior is production (UI disabled).
Build-Time Maven Profile
The most secure approach: don't ship the addon JAR at all in production. Place the dependency in a Maven profile so it's only included for non-production builds:
<profiles>
<profile>
<id>development</id>
<dependencies>
<dependency>
<groupId>org.os890.mp-ext</groupId>
<artifactId>openapi-gui-addon</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
</profile>
</profiles>
Build with mvn clean package -Pdevelopment to include the UI. The default build produces a WAR with zero addon code — nothing to exploit.
Always Enabled
For apps where the UI should always be available regardless of project stage, simply set:
openapi.ui.enabled=true
Jakarta EE 8 Alternative: getClasses()
On Jakarta EE 8 (version 1.8.0 of the addon), the auto-discovery filter is not available. Instead, control the UI via Application.getClasses():
import org.os890.mp.openapi.gui.OpenApiUiService;
import org.os890.mp.openapi.gui.StaticResourcesService;
@ApplicationPath("/")
public class MyApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(MyResource.class);
String stage = ConfigProvider.getConfig()
.getOptionalValue("project.stage", String.class)
.orElse("production");
if (!"production".equals(stage)) {
classes.add(OpenApiUiService.class);
classes.add(StaticResourcesService.class);
}
return classes;
}
}
How It Works Under the Hood
The addon uses the Maven Shade Plugin to repackage microprofile-extensions/openapi-ext with package relocation from org.microprofileext.openapi.swaggerui to org.os890.mp.openapi.gui. It replaces only the files that were changed:
- Templates.java — adds
openapi.ui.urlsconfig, availability-checking JavaScript, and auto-visible explore form when the dropdown is active - template.html — removes the Google Fonts CDN link and adds the client-side availability check with the multi-API dropdown logic
- web-fragment.xml — removed entirely, enabling explicit registration control
- OpenApiUiAccessFilter (new, Jakarta EE 11 only) —
@PreMatchingJAX-RS filter for config-driven project-stage gating
Everything else (OpenApiUiService, StaticResourcesService, RequestInfo, WhiteLabel, style.css, logo.png) comes unchanged from the original addon via the shade. The result is a single self-contained JAR with no transitive runtime dependencies.
Versions
Two versions are available:
| Version | Jakarta EE | Namespace | Swagger UI | Gating | Tested with |
|---|---|---|---|---|---|
| 1.8.0 | 8 | javax.* | 4.15.5 | getClasses() | WildFly 25 |
| 1.11.0 | 11 | jakarta.* | 5.18.2 | Auto-filter + config | WildFly 39 |
Examples
The examples directory contains three demo applications showing the different project-stage approaches, deployable on WildFly 39 via Podman. An interactive build_and_start.sh script lets you choose the project stage for each demo before building and deploying.
AI-Assisted Development
Like the Dynamic CDI Test Bean Addon, this project was developed with an AI co-pilot — from the initial Jakarta EE 8 demo through the shade-based addon architecture to the Jakarta EE 11 auto-discovery filter. The entire process, including the backport of the upstream library, the multi-API dropdown with availability checking, and the project-stage gating mechanism, was built iteratively in a single session. The security hooks we set up earlier ensured that all package installs and web requests stayed within safe boundaries throughout.
Links
- Source code: github.com/os890/openapi-gui-addon
- Based on: github.com/microprofile-extensions/openapi-ext
- License: Apache License 2.0
Previous posts in this series: Upgrading 30+ Projects to Java 25 | Hardening Claude Code | Dynamic CDI Test Bean Addon
No comments:
Post a Comment