tutorial

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

Apache MetaModel abstracts data queries across JDBC, CSV, JSON, and more — but nobody's watching your data access layer at runtime. Set up external uptime monitoring and heartbeat checks for your MetaModel jobs in under 30 minutes, free.

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

Apache MetaModel gives you a unified API to query data from JDBC databases, CSV files, JSON documents, spreadsheets, and dozens of other sources. But when a data pipeline silently fails — a broken JDBC connection, a missing CSV file, or an empty result set — nobody gets alerted unless you set up external monitoring.

By the end of this guide you'll have external uptime monitoring, heartbeat checks for your MetaModel batch jobs, and instant alerts — all running on the free tier.


Why MetaModel applications go dark silently

MetaModel apps have two failure modes that slip through:

Data source connectivity failures — your JDBC DataSource loses connectivity, a CSV path changes, or a remote Elasticsearch cluster becomes unreachable. MetaModel will throw at query time, but unless something is watching, users get errors or stale data with no alert fired.

Silent batch job failures — a scheduled MetaModel ETL job throws a MetaModelException or a QueryParserException. The thread pool catches it and the next run never triggers. Your reporting pipeline is broken, and the dashboard just shows yesterday's numbers forever.

Both are solvable with one external monitoring tool checking from outside your infrastructure.


Step 1: Expose a health endpoint

MetaModel doesn't include a built-in health endpoint, so add a lightweight servlet or controller that validates your primary data sources at startup and on demand.

With Spring Boot:

import org.apache.metamodel.DataContext;
import org.apache.metamodel.DataContextFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;
import java.util.Map;

@RestController
public class HealthController {

    private final DataSource dataSource;

    public HealthController(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @GetMapping("/health")
    public Map<String, String> health() {
        try {
            DataContext dc = DataContextFactory.createJdbcDataContext(dataSource);
            // A lightweight schema fetch proves connectivity
            dc.getDefaultSchema();
            return Map.of("status", "UP", "metamodel", "connected");
        } catch (Exception e) {
            throw new RuntimeException("MetaModel data source unavailable: " + e.getMessage());
        }
    }
}

For a standalone (non-Spring) application, wire a simple HTTP server:

import com.sun.net.httpserver.HttpServer;
import org.apache.metamodel.DataContextFactory;
import java.net.InetSocketAddress;

public class HealthServer {

    public static void start(DataSource ds) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8081), 0);
        server.createContext("/health", exchange -> {
            try {
                DataContextFactory.createJdbcDataContext(ds).getDefaultSchema();
                byte[] body = "{\"status\":\"UP\"}".getBytes();
                exchange.sendResponseHeaders(200, body.length);
                exchange.getResponseBody().write(body);
            } catch (Exception e) {
                byte[] body = "{\"status\":\"DOWN\"}".getBytes();
                exchange.sendResponseHeaders(503, body.length);
                exchange.getResponseBody().write(body);
            } finally {
                exchange.close();
            }
        });
        server.start();
    }
}

Verify locally:

curl http://localhost:8081/health
# {"status":"UP","metamodel":"connected"}

Step 2: Test multiple data source types

If your app queries several backends, surface each one in the health response:

@GetMapping("/health")
public ResponseEntity<Map<String, Object>> health(
        DataSource jdbcDs,
        Path csvPath) {

    Map<String, Object> details = new LinkedHashMap<>();
    boolean allUp = true;

    // Check JDBC
    try {
        DataContextFactory.createJdbcDataContext(jdbcDs).getDefaultSchema();
        details.put("jdbc", "UP");
    } catch (Exception e) {
        details.put("jdbc", "DOWN: " + e.getMessage());
        allUp = false;
    }

    // Check CSV file availability
    try {
        DataContextFactory.createCsvDataContext(csvPath.toFile(), ',', '"');
        details.put("csv", "UP");
    } catch (Exception e) {
        details.put("csv", "DOWN: " + e.getMessage());
        allUp = false;
    }

    details.put("status", allUp ? "UP" : "DOWN");
    return ResponseEntity
            .status(allUp ? 200 : 503)
            .body(details);
}

When any source is down, the endpoint returns HTTP 503, which Vigilmon treats as an incident.


Step 3: Set up external monitoring with Vigilmon

With /health 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
  4. Set check interval (5 minutes on free tier)
  5. Save

Vigilmon probes from multiple regions simultaneously. If it receives a non-2xx response or a timeout, it opens an incident and alerts you before your users notice.

Recommended monitors for a MetaModel app:

| Endpoint | What it catches | |---|---| | /health | JDBC, CSV, or remote source down | | /api/reports/latest | End-to-end query pipeline broken | | / | Frontend or API gateway down |


Step 4: Heartbeat monitoring for scheduled MetaModel jobs

HTTP checks don't catch silent ETL failures. Heartbeat monitoring does.

The pattern: your batch job pings a unique URL at the end of each successful run. If Vigilmon stops receiving the ping within the expected window, it fires an alert.

import org.apache.metamodel.DataContext;
import org.apache.metamodel.DataContextFactory;
import org.apache.metamodel.query.Query;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class NightlyEtlJob {

    private static final String HEARTBEAT_URL =
            System.getenv("HEARTBEAT_ETL_URL"); // set in env

    private final DataSource source;
    private final DataSource target;

    public NightlyEtlJob(DataSource source, DataSource target) {
        this.source = source;
        this.target = target;
    }

    public void run() throws Exception {
        DataContext sourceCtx = DataContextFactory.createJdbcDataContext(source);
        DataContext targetCtx = DataContextFactory.createJdbcDataContext(target);

        // Execute MetaModel query
        Query q = sourceCtx.query()
                .from(sourceCtx.getDefaultSchema().getTableByName("orders"))
                .select("id", "total", "created_at")
                .where("processed").eq(false)
                .toQuery();

        var rows = sourceCtx.executeQuery(q);

        // ... transform and insert into target ...

        // Only ping heartbeat on clean success
        pingHeartbeat();
    }

    private void pingHeartbeat() {
        if (HEARTBEAT_URL == null || HEARTBEAT_URL.isBlank()) return;
        try {
            HttpClient.newHttpClient().send(
                    HttpRequest.newBuilder(URI.create(HEARTBEAT_URL)).GET().build(),
                    HttpResponse.BodyHandlers.discarding()
            );
        } catch (Exception ignored) {}
    }
}

In Vigilmon:

  1. Click New Monitor → Heartbeat
  2. Set the expected interval (e.g. 25 hours for a daily job)
  3. Copy the unique ping URL
  4. Set HEARTBEAT_ETL_URL in your environment

If the ETL throws at any step before pingHeartbeat(), 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=metamodel-tutorial)

What you've built

| What | How | |---|---| | External health checks | /health endpoint + Vigilmon HTTP monitor | | Multi-source connectivity | JDBC, CSV checks in one response | | ETL job monitoring | Heartbeat ping at end of each run | | Instant alerts | Slack/Discord notifications | | README status badge | Vigilmon badge embed |

The entire setup runs on the free tier in under 30 minutes. Your next silent MetaModel failure will fire an alert before your users see stale data.


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 →