Quarkus extension providing a web-based control dashboard and REST API for managing and monitoring jobs in JobRunr Pro.
- Web UI - Bootstrap 5 + htmx-based interface for managing scheduled jobs
- REST API - External trigger endpoints for job execution
- Job Discovery - Automatic discovery of
@ConfigurableJobimplementations at build time - Job Parameters - Type-safe parameter handling with validation
- Execution History - Monitor job executions and batch progress
- Security - Role-based access control with separate UI and REST API roles
- Batch Job Support - Create and monitor batch jobs with real-time progress tracking
- Job Chain Status - Accurate status evaluation for jobs with continuation chains
- Java 21+
- Quarkus 3.31.2
- JobRunr Pro 8.4.2 (license required)
Add the extension to your Quarkus project:
<dependency>
<groupId>ch.css.quarkus</groupId>
<artifactId>quarkus-jobrunr-control</artifactId>
<version>1.3.1</version>
</dependency>The extension automatically brings in:
- JobRunr Pro 8.4.2
- Qute templates
- htmx 2.0.8
- Bootstrap 5.3.8
- Bootstrap Icons
# Enable/disable UI (default: true)
quarkus.jobrunr-control.ui.enabled=true
# Enable/disable REST API (default: true)
quarkus.jobrunr-control.api.enabled=trueUse standard Quarkus HTTP configuration to customize paths:
# Set custom root path for all endpoints
quarkus.http.root-path=/scheduler
# Or set REST-specific path
quarkus.rest.path=/api# Enable JobRunr components
quarkus.jobrunr.background-job-server.enabled=true
quarkus.jobrunr.dashboard.enabled=true
quarkus.jobrunr.job-scheduler.enabled=true
# Database
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/yourdbDefine your parameters as a Java record:
public record MyJobRequest(
@JobParameterDefinition(name = "Message", defaultValue = "Hello")
String message,
@JobParameterDefinition(defaultValue = "1")
Integer count
) implements JobRequest {
@Override
public Class<MyJobHandler> getJobRequestHandler() {
return MyJobHandler.class;
}
}@ApplicationScoped
public class MyJobHandler implements JobRequestHandler<MyJobRequest> {
@ConfigurableJob(name = "My Custom Job")
@Override
public void run(MyJobRequest request) throws Exception {
jobContext().logger().info("Processing: " + request.message());
}
}Navigate to http://localhost:9090/q/jobrunr-control to:
- View all discovered jobs
- Schedule jobs with parameters
- Monitor execution history
- Track batch job progress
Start a job or template externally:
# Start a job (or template) immediately
curl -X POST http://localhost:9090/q/jobrunr-control/api/jobs/{jobId}/start \
-H "Content-Type: application/json" \
-d '{
"postfix": "20240127",
"parameters": {
"message": "Hello",
"count": 5
}
}'
# Check job status
curl http://localhost:9090/q/jobrunr-control/api/jobs/{jobId}Automated Script: For batch processing and polling, use the provided script:
./scripts/start-and-poll-job.sh <job-id> <postfix> [key1=value1] [key2=value2]See scripts/README.md for detailed documentation and examples.
Base path: /q/jobrunr-control
| Path | Description |
|---|---|
/ |
Redirects to jobs list |
/jobs |
List all scheduled jobs |
/templates |
Manage template jobs |
/executions |
View execution history |
Base path: /q/jobrunr-control/api
| Method | Path | Roles Required | Description |
|---|---|---|---|
| POST | /jobs/{jobId}/start |
api-executor, admin |
Start a job immediately. If the job is a template, it is cloned and executed. |
| GET | /jobs/{jobId} |
api-reader, api-executor, admin |
Get job status and progress |
Note: All REST API endpoints require authentication and appropriate role assignment.
The extension provides role-based access control (RBAC) with two distinct role sets for UI and REST API:
Access to the Web UI at /q/jobrunr-control is controlled by:
- viewer: Read-only access (view scheduled jobs and execution history)
- configurator: All viewer permissions plus create, edit, and delete jobs
- admin: All configurator permissions plus immediate job execution
Access to the REST API at /q/jobrunr-control/api uses separate roles:
- api-reader: Read-only access to GET endpoints (job status checks)
- api-executor: All api-reader permissions plus POST endpoints (start jobs)
- admin: Full access to all operations (shared with UI)
Why separate role sets?
This design allows organizations to:
- Grant operators UI access without exposing REST API credentials
- Issue API keys to external systems (CI/CD pipelines) with limited permissions
- Use different authentication methods for UI vs. API (form login vs. bearer tokens)
Development Mode (all roles granted automatically):
%dev.quarkus.security.users.embedded.enabled=true
%dev.quarkus.security.users.embedded.plain-text=true
%dev.quarkus.security.users.embedded.users.admin=admin
%dev.quarkus.security.users.embedded.roles.admin=admin,viewer,configurator,api-reader,api-executorProduction with OIDC/OAuth2:
quarkus.oidc.auth-server-url=https://your-keycloak.com/realms/your-realm
quarkus.oidc.client-id=jobrunr-control
quarkus.oidc.credentials.secret=${OIDC_CLIENT_SECRET}
quarkus.oidc.roles.source=accesstokenConfigure your OIDC provider to issue the appropriate roles:
- For UI users:
viewer,configurator,admin - For API clients:
api-reader,api-executor,admin
Production with Basic Auth:
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.users.operator=${OPERATOR_PASSWORD}
quarkus.security.users.embedded.roles.operator=viewer,configurator
quarkus.security.users.embedded.users.api-client=${API_CLIENT_PASSWORD}
quarkus.security.users.embedded.roles.api-client=api-reader,api-executorFor detailed security setup, see the Programmer's Guide.
For local development and testing, you can disable OIDC authentication:
quarkus.oidc.enabled=falseWhen OIDC is disabled:
- No login required
- No logout button in UI
- All requests are treated as "anonymous-user" with full permissions
- Audit logs show "anonymous-user" as the actor
Production deployments MUST use OIDC authentication (enabled by default).
For development and testing, grant all roles automatically:
dev.test.roles=admin,viewer,configurator,api-reader,api-executor| Type | Format | Example |
|---|---|---|
| String | Plain text | "Hello World" |
| Multiline | Multi-line text | "Line 1\nLine 2\nLine 3" |
| Integer | Whole number | 42 |
| Double | Decimal number | 3.14159 |
| Boolean | true/false | true |
| Date | ISO date | 2024-01-15 |
| DateTime | ISO datetime | 2024-01-15T10:30:00 |
| Enum | Enum constant name | OPTION_A |
| Multi-Enum | Comma-separated enum list | OPTION_A,OPTION_B,OPTION_C |
- Inline Storage (default): Parameters stored directly in JobRunr's job table
- External Storage: Parameters stored in a separate database table for large parameter sets
Use @JobParameterSet on a JobRequest to enable external storage. Requires Hibernate ORM.
For jobs with continuation chains (continueWith() or onFailure()), the extension evaluates the overall chain status:
- A chain is complete when all relevant leaf jobs have finished (SUCCEEDED, FAILED, or DELETED)
- A chain is in progress when any leaf job is still running (ENQUEUED, PROCESSING, PROCESSED)
- A chain succeeded when all executed leaf jobs succeeded
- A chain failed when any executed leaf job failed
# Storage strategy: INLINE (default) or EXTERNAL
quarkus.jobrunr-control.parameter-storage.strategy=INLINE
# Persistence unit for external parameter storage (build-time config)
# Default: <default> (Hibernate ORM default persistence unit)
quarkus.jobrunr-control.parameter-storage.persistence-unit-name=<default>
# Cleanup configuration for external parameter storage
quarkus.jobrunr-control.parameter-storage.cleanup.enabled=true
quarkus.jobrunr-control.parameter-storage.cleanup.retention-days=30jobrunr.batch-progress.timeout=PT5Squarkus.cache.caffeine.job-definitions.expire-after-write=15Mjobrunr.dashboard.url=http://localhost:8000The example application uses H2 in-memory by default for development, requiring no external setup. For Oracle or PostgreSQL, use the provided shell scripts to start and configure the database.
./mvnw -f jobrunr-control-example/pom.xml quarkus:devH2 runs in-memory in standard mode and automatically creates the jobrunr_control_parameter_sets table on startup.
Data is reset on every restart.
Important: H2 cannot run in Oracle compatibility mode because JobRunr generates H2-specific SQL syntax (e.g.,
LIMIT clause).
For Oracle compatibility testing, use an actual Oracle database with ./start-oracle.sh and the oracle profile.
Note: H2 uses CLOB instead of JSON type for the parameters_json column to avoid double-serialization issues
with JDBC drivers.
Prerequisites: Docker or Podman installed.
-
Start PostgreSQL and create the required table:
./start-postgres.sh
The script always creates a fresh
postgres:15container on port 5432 and creates thejobrunr_control_parameter_setstable automatically. Any existing container is removed first. -
Start the application with the
postgresprofile:./mvnw -f jobrunr-control-example/pom.xml quarkus:dev -Dquarkus.profile=dev,postgres
Connection details (configured in application.properties under %postgres):
| Property | Value |
|---|---|
| JDBC URL | jdbc:postgresql://localhost:5432/postgres |
| Username | postgres |
| Password | your_strong_password |
Prerequisites: Docker or Podman installed. You must be logged in to the Oracle Container Registry:
docker login container-registry.oracle.com-
Start Oracle and create the required table:
./start-oracle.sh
The script always creates a fresh Oracle Database Free container on port 1521, creates a
JOBRUNR_DATAtablespace, and creates thejobrunr_control_parameter_setstable automatically. Any existing container is removed first. First startup may take 2–5 minutes. -
Start the application with the
oracleprofile:./mvnw -f jobrunr-control-example/pom.xml quarkus:dev -Dquarkus.profile=dev,oracle
Connection details (configured in application.properties under %oracle):
| Property | Value |
|---|---|
| JDBC URL | jdbc:oracle:thin:@localhost:1521/FREEPDB1 |
| Username | system |
| Password | YourStrongPassword123 |
If you manage the database yourself, create the jobrunr_control_parameter_sets table using the
SQL scripts in docs/sql/:
| Database | Script |
|---|---|
| PostgreSQL | docs/sql/postgresql.sql |
| Oracle | docs/sql/oracle.sql |
| H2 | docs/sql/h2.sql |
| MySQL/MariaDB | docs/sql/mysql.sql |
This table is only required if any job uses @JobParameterSet for external parameter storage.
The extension follows Clean Architecture principles:
runtime/
├── domain/ # Core models and ports
├── application/ # Use cases
├── infrastructure/ # JobRunr integration
├── adapter/
│ ├── rest/ # REST API
│ └── ui/ # UI controllers
└── dev/ # Dev mode features
See Architecture Documentation for full details.
| Document | Description |
|---|---|
| Architecture Documentation | Technical architecture following arc42 template |
| User Guide | End-user guide for operating the dashboard |
| Programmer's Guide | Developer guide for implementing jobs |
./mvnw clean compile./mvnw clean verifycd jobrunr-control-example
../mvnw quarkus:devBy default, dev mode starts with Keycloak DevServices using Testcontainers:
- Automatic Setup: Keycloak container starts automatically with a pre-configured realm
- Pre-configured Users:
admin/admin- Full access (all roles)configurator/configurator- Can create/modify templatesviewer/viewer- Read-only access
See DEV-MODE-KEYCLOAK.md for detailed setup and customization.
Symptom: Application hangs during startup in dev mode, port 9090 doesn't open, no error displayed. Tests work fine.
Root Cause: The OIDC Dev UI attempts to discover metadata from Keycloak before the DevServices container is fully ready. With Podman, this causes an indefinite hang.
Solutions:
-
Use the no-docker profile (bypasses OIDC completely):
./mvnw -f jobrunr-control-example/pom.xml quarkus:dev -Dquarkus.profile=dev,no-docker
-
Disable OIDC Dev UI in
application.properties(already configured):%dev.quarkus.oidc.dev-ui.enabled=false -
Use external Keycloak (start separately):
./start-keycloak.sh ./mvnw -f jobrunr-control-example/pom.xml quarkus:dev -Dquarkus.profile=dev,start-keycloak
Diagnostic Commands:
# Check if process is hanging
ps aux | grep quarkus
# Get thread dump (replace PID)
jstack <PID> | grep -A 10 "Quarkus Main Thread"
# If you see "OidcDevUiRecorder.discoverMetadata", it's the OIDC Dev UI issueNote: This is a known limitation with Quarkus OIDC DevServices when using Podman. The fix (
%dev.quarkus.oidc.dev-ui.enabled=false) is already applied in the configuration.
- JobRunr Control:
http://localhost:9090/q/jobrunr-control - JobRunr Dashboard:
http://localhost:9090/q/jobrunr - Swagger UI:
http://localhost:9090/q/swagger-ui/ - Keycloak Admin: Check console output for dynamic URL
Internal CSS Project. Requires a valid JobRunr Pro license.
For issues and questions, contact the JobRunr Control Team.