How to Monitor Apache OpenWebBeans Applications (Free, Multi-Region)
Apache OpenWebBeans is the CDI (Contexts and Dependency Injection) container for TomEE, and it's widely used in standalone CDI deployments. It manages bean lifecycle, interceptors, events, and decorators — but when the container fails to start, a scoped bean is unavailable, or a CDI event observer silently throws, nothing alerts you unless you set up external monitoring.
By the end of this guide you'll have external uptime monitoring, heartbeat checks for your CDI event-driven processes, and instant alerts — all running on the free tier.
Why OpenWebBeans applications fail silently
OWB-backed applications have two common silent failure modes:
Container startup and scope failures — OpenWebBeans may fail to initialize a @ApplicationScoped bean if a dependency is misconfigured or a producer method throws. Depending on how the failure is handled, the app may boot but serve broken responses for every request that touches the broken bean graph.
CDI observer method failures — a @Observes method that processes domain events throws a runtime exception. OpenWebBeans swallows it and moves on. Your event-driven workflows stop executing silently, and the application surface looks healthy from the outside.
Both are solvable with one external monitoring tool checking from outside your infrastructure.
Step 1: Add a health endpoint
For a TomEE or Jakarta EE application, use MicroProfile Health:
<!-- pom.xml -->
<dependency>
<groupId>org.eclipse.microprofile.health</groupId>
<artifactId>microprofile-health-api</artifactId>
<scope>provided</scope>
</dependency>
Write a liveness check that exercises the CDI container:
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
@Liveness
@ApplicationScoped
public class OwbLivenessCheck implements HealthCheck {
@Inject
private BeanRegistry beanRegistry; // your own application-scoped bean
@Override
public HealthCheckResponse call() {
try {
// Calling any method on an injected @ApplicationScoped bean
// proves the CDI container is live and the bean is resolved.
String status = beanRegistry.getStatus();
return HealthCheckResponse.named("owb-container")
.withData("registry-status", status)
.up()
.build();
} catch (Exception e) {
return HealthCheckResponse.named("owb-container")
.withData("error", e.getMessage())
.down()
.build();
}
}
}
For a Spring Boot application that uses OpenWebBeans as its CDI provider, add Spring Actuator and a matching HealthIndicator:
import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.inject.Inject;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class OwbHealthIndicator implements HealthIndicator {
@Inject
private BeanManager beanManager;
@Override
public Health health() {
try {
// Check the BeanManager is alive
beanManager.getBeans(Object.class);
return Health.up().withDetail("owb-bean-manager", "ok").build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
Verify locally:
curl http://localhost:8080/health/live
# {"status":"UP","checks":[{"name":"owb-container","status":"UP"}]}
Step 2: Add a readiness check for critical beans
Add a readiness check that validates your most critical application-scoped services are injectable and responsive:
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
@Readiness
@ApplicationScoped
public class CriticalServicesReadinessCheck implements HealthCheck {
@Inject
private OrderService orderService;
@Inject
private InventoryService inventoryService;
@Override
public HealthCheckResponse call() {
try {
boolean ordersOk = orderService.isReady();
boolean inventoryOk = inventoryService.isReady();
if (ordersOk && inventoryOk) {
return HealthCheckResponse.up("critical-services");
}
return HealthCheckResponse.named("critical-services")
.withData("orders", ordersOk)
.withData("inventory", inventoryOk)
.down()
.build();
} catch (Exception e) {
return HealthCheckResponse.named("critical-services")
.withData("error", e.getMessage())
.down()
.build();
}
}
}
When any critical service is unhealthy, /health/ready returns HTTP 503.
Step 3: Set up external monitoring with Vigilmon
With your health endpoint live, point Vigilmon at it:
- Sign up at vigilmon.online — free tier, no credit card
- Click New Monitor → HTTP
- Enter
https://yourdomain.com/health/live - Set check interval (5 minutes on free tier)
- Save
Vigilmon probes from multiple regions simultaneously. A non-2xx response or timeout opens an incident and alerts you before your users notice.
Recommended monitors for an OpenWebBeans app:
| Endpoint | What it catches |
|---|---|
| /health/live | CDI container dead, BeanManager unavailable |
| /health/ready | Critical beans not injectable |
| /api/orders | Core business endpoint broken |
Step 4: Heartbeat monitoring for CDI event-driven workflows
HTTP checks don't catch silent CDI observer failures. Heartbeat monitoring does.
The pattern: your CDI event observer pings a unique URL after successfully processing each batch of events. If Vigilmon stops receiving the ping within the expected window, it fires an alert.
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@ApplicationScoped
public class OrderEventProcessor {
private static final String HEARTBEAT_URL =
System.getenv("HEARTBEAT_ORDER_EVENTS_URL");
private final HttpClient httpClient = HttpClient.newHttpClient();
public void onOrderCompleted(@Observes OrderCompletedEvent event) {
try {
// Process the domain event
processOrder(event.getOrderId());
// Only ping heartbeat on clean success
pingHeartbeat();
} catch (Exception e) {
// Log — but do NOT ping heartbeat
System.err.println("Order event processing failed: " + e.getMessage());
// Re-throw so the caller is aware the event was not fully handled
throw new RuntimeException(e);
}
}
private void processOrder(String orderId) {
// your business logic
}
private void pingHeartbeat() {
if (HEARTBEAT_URL == null || HEARTBEAT_URL.isBlank()) return;
try {
httpClient.send(
HttpRequest.newBuilder(URI.create(HEARTBEAT_URL)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
For periodic batch processing rather than per-event pings, wrap it in a scheduled method:
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
@Singleton
public class BatchHeartbeatJob {
private static final String HEARTBEAT_URL =
System.getenv("HEARTBEAT_BATCH_URL");
@Schedule(hour = "*", minute = "*/5", second = "0", persistent = false)
public void runBatch() {
try {
// do work
pingHeartbeat();
} catch (Exception e) {
System.err.println("Batch failed: " + e.getMessage());
}
}
private void pingHeartbeat() {
if (HEARTBEAT_URL == null || HEARTBEAT_URL.isBlank()) return;
try {
java.net.http.HttpClient.newHttpClient().send(
java.net.http.HttpRequest.newBuilder(URI.create(HEARTBEAT_URL)).GET().build(),
java.net.http.HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
In Vigilmon:
- Click New Monitor → Heartbeat
- Set the expected interval (e.g. 10 minutes for a 5-minute job, giving one grace period)
- Copy the unique ping URL
- Set
HEARTBEAT_ORDER_EVENTS_URLorHEARTBEAT_BATCH_URLin your environment
If the observer or batch job throws before pingHeartbeat() is called, the ping is never sent and Vigilmon alerts you within one missed interval.
Step 5: Webhook alerts and badge embed
Slack/Discord alerts:
- In Vigilmon go to Notifications → New Channel
- Choose Slack or Discord, paste your webhook URL
- Enable it on your monitors
You'll get instant alerts when a monitor goes down and a recovery notification when it comes back up.
Add an uptime badge to your README:
[](https://vigilmon.online?utm_source=devto&utm_medium=article&utm_campaign=openwebbeans-tutorial)
What you've built
| What | How |
|---|---|
| External health checks | /health/live + Vigilmon HTTP monitor |
| CDI container validation | Liveness check via BeanManager |
| Critical bean readiness | Readiness check on core services |
| CDI event pipeline monitoring | Heartbeat ping after each successful batch |
| Instant alerts | Slack/Discord notifications |
The entire setup runs on the free tier in under 30 minutes. The next time an OpenWebBeans CDI observer silently stops processing, you'll know before your domain events start piling up unprocessed.
Get started free at vigilmon.online — monitors running in under a minute, no credit card required.