tutorial

How to Monitor Apache OpenWebBeans Applications (Free, Multi-Region)

Apache OpenWebBeans is the CDI container powering TomEE and other Jakarta EE runtimes — but a broken bean scope or a failed CDI extension won't alert anyone. Set up external uptime monitoring and heartbeat checks in under 30 minutes, free.

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:

  1. Sign up at vigilmon.online — free tier, no credit card
  2. Click New Monitor → HTTP
  3. Enter https://yourdomain.com/health/live
  4. Set check interval (5 minutes on free tier)
  5. 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:

  1. Click New Monitor → Heartbeat
  2. Set the expected interval (e.g. 10 minutes for a 5-minute job, giving one grace period)
  3. Copy the unique ping URL
  4. Set HEARTBEAT_ORDER_EVENTS_URL or HEARTBEAT_BATCH_URL in 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:

  1. In Vigilmon go to Notifications → New Channel
  2. Choose Slack or Discord, paste your webhook URL
  3. 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:

[![Uptime](https://vigilmon.online/badge/your-monitor-id.svg)](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.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →