ADR-072: Qute Web Documentation Server
Status
Accepted (Implemented)
Date
2025-12-27
Context
The iDempiere Hub needs to serve documentation to users via HTTP. Requirements:
- Dynamic content - Render docs from AD metadata in real-time
- Search - Allow filtering tables, windows, processes
- Navigation - Browse ADRs, guides, reference docs
- Multiple formats - HTML for web, JSON for API, Markdown for export
We evaluated options:
- Static site generator (Hugo, Jekyll) - Requires pre-build, not dynamic
- Custom REST API - More work, reinvents rendering
- Qute Web - Quarkus native, template-based, dynamic
This ADR documents the Qute Web approach which is now working.
Related ADRs:
Decision
1. Quarkus Qute Web Extension
Use quarkus-qute-web for template-based HTTP responses:
<dependency>
<groupId>io.quarkiverse.qute.web</groupId>
<artifactId>quarkus-qute-web</artifactId>
<version>3.3.1</version>
</dependency>
2. Documentation Endpoints
| Endpoint | Template | Data Source |
|---|---|---|
GET /docs/ |
pub/docs/index.html |
Static navigation |
GET /docs/tables/ |
tablesList.html |
AD_Table list |
GET /docs/tables/{name} |
tableView.html |
TableDocGenerator |
GET /docs/windows/ |
windowsList.html |
WindowRegistryLogic |
GET /docs/processes/ |
processesList.html |
ProcessRegistryLogic |
GET /docs/adr/ |
adrList.html |
AdrService |
GET /docs/adr/{id} |
adrView.html |
AdrService |
GET /docs/guides/ |
guidesList.html |
Static (future) |
3. DocsResource Implementation
@Path("/docs")
public class DocsResource {
@Inject TableDocGenerator tableDocGenerator;
@Inject WindowRegistryLogic windowRegistry;
@Inject ProcessRegistryLogic processRegistry;
@Inject AdrService adrService;
@Inject Template tableView;
@Inject Template windowsList;
@Inject Template processesList;
@GET
@Path("/tables/{tableName}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance getTableDoc(
@PathParam("tableName") String tableName,
@QueryParam("lang") String lang) {
Optional<TableDoc> doc = tableDocGenerator.generate(tableName, lang);
if (doc.isEmpty()) {
return tableView.data("error", "Table not found: " + tableName);
}
return tableView
.data("doc", doc.get())
.data("tableName", tableName);
}
@GET
@Path("/windows/")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance listWindows(@QueryParam("pattern") String pattern) {
String effectivePattern = (pattern != null) ? pattern : "%";
ToolResult result = windowRegistry.listWindows(effectivePattern, 100);
List<Map<String, Object>> windows =
(List<Map<String, Object>>) result.getData().get("windows");
return windowsList
.data("windows", windows)
.data("pattern", effectivePattern)
.data("count", windows.size());
}
}
4. Template Structure
src/main/resources/templates/
├── base.html # Base layout with header, footer
├── tableView.html # Single table documentation
├── tablesList.html # Table search/list
├── windowsList.html # Windows list with search
├── processesList.html # Processes list with badges
├── adrList.html # ADR list from docs/adr/
├── adrView.html # Single ADR with markdown
├── guidesList.html # Guides navigation
└── pub/
└── docs/
└── index.html # Static entry point
5. Profile Configuration
The docs server runs on a separate profile:
# application-docs.properties
quarkus.http.port=8080
quarkus.qute.content-types.md=text/html
Run command:
java -Dquarkus.profile=docs -jar idempiere-hub-runner.jar server docs
6. Qute Features Used
| Feature | Usage |
|---|---|
{#include base} |
Template inheritance |
{#for item in list} |
Iteration |
{#if cond}...{/if} |
Conditionals |
{value ?: 'default'} |
Elvis operator |
{#md}...{/md} |
Markdown rendering |
| Type-safe templates | @Inject Template name |
Implementation Status
| Component | File | Status |
|---|---|---|
| DocsResource | DocsResource.java |
Working |
| TableDocGenerator | TableDocGenerator.java |
Working |
| Table View | tableView.html |
Working |
| Windows List | windowsList.html |
Working |
| Processes List | processesList.html |
Working |
| ADR List | adrList.html |
Working |
| ADR View | adrView.html |
Working |
| Guides List | guidesList.html |
Placeholder |
Test Results
$ curl http://localhost:8080/docs/windows/?pattern=%Order%
Found 15 window(s) matching "%Order%"
- Sales Order
- Purchase Order
- Manufacturing Order
...
$ curl http://localhost:8080/docs/processes/?pattern=%Invoice%
Found 12 process(es) matching "%Invoice%"
- Generate Invoice
- Invoice Print
...
$ curl http://localhost:8080/docs/tables/C_Order
[Full table documentation with columns, references, etc.]
Consequences
Benefits
- Dynamic - Content from live AD metadata
- Searchable - SQL LIKE pattern filtering
- Extensible - Easy to add new endpoints
- Quarkus native - No external dependencies
Drawbacks
- No caching - Each request queries DB (can add)
- No authentication - Public access (see ADR-073)
- Template maintenance - HTML/Qute syntax
Next Steps
- Add caching for table metadata
- Implement guides content from Git
- Add authentication via OIDC
- Add JSON export for API consumers
References
- Implementation:
src/main/java/org/idempiere/cli/rag/docs/DocsResource.java - Templates:
src/main/resources/templates/ - Qute Web: https://docs.quarkiverse.io/quarkus-qute-web/dev/index.html
- Parent: ADR-068