tutorial

How to Monitor Apache Griffon Applications with Vigilmon

Apache Griffon provides a structured MVC framework for JVM desktop apps — but desktop apps can crash silently, background services can stall, and embedded servers can stop responding without anyone noticing. Here's how to monitor Griffon applications end-to-end with Vigilmon.

How to Monitor Apache Griffon Applications with Vigilmon

Apache Griffon is a JVM desktop application framework inspired by Grails, bringing convention-over-configuration, MVC structure, and plugin architecture to Swing, JavaFX, and Pivot applications written in Groovy or Java. But desktop apps have a monitoring blind spot: they run on end-user machines or internal servers without the same visibility infrastructure you'd apply to a web service. Background service threads can deadlock, embedded HTTP servers can stop responding, and the application can crash with no alert reaching operations.

This tutorial sets up layered monitoring for a Griffon application:

  • An embedded HTTP health endpoint inside the Griffon app
  • HTTP uptime monitoring with Vigilmon
  • Heartbeat emission from the application's main service loop
  • Alert channels for crash and degradation detection
  • Status page for internal teams

Why Monitor Griffon Applications?

| Signal | What it catches | |---|---| | Embedded health endpoint | Application process down, startup failures | | Background service heartbeat | Worker thread deadlock, infinite loops | | Response time | UI thread starvation, slow data access | | Service manager availability | Griffon service lifecycle failures | | External API connectivity | Downstream dependency outages affecting the app |

A plain process-running check misses deadlocked threads and stalled services. You need to exercise the application's actual service layer.


Step 1: Add an Embedded HTTP Health Server

Griffon applications are long-running JVM processes. Add a lightweight Jetty or com.sun.net.httpserver health endpoint that Vigilmon can poll.

Add the dependency to build.gradle:

// build.gradle
dependencies {
    implementation 'org.eclipse.jetty:jetty-server:11.0.20'
    // ... existing dependencies
}

Create a Griffon service that starts the embedded health server:

// griffon-app/services/com/example/HealthService.groovy
package com.example

import griffon.core.artifact.GriffonService
import griffon.metadata.ArtifactProviderFor
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.handler.AbstractHandler
import groovy.json.JsonOutput

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@ArtifactProviderFor(GriffonService)
class HealthService {

    private Server healthServer
    private volatile boolean appHealthy = true
    private volatile long lastHeartbeat = System.currentTimeMillis()

    void startHealthServer(int port = 9100) {
        healthServer = new Server(port)
        healthServer.handler = new AbstractHandler() {
            void handle(String target, Request base, HttpServletRequest req, HttpServletResponse res) {
                res.contentType = 'application/json'
                long staleSince = System.currentTimeMillis() - lastHeartbeat

                if (appHealthy && staleSince < 120_000) {
                    res.status = 200
                    res.writer.println(JsonOutput.toJson([
                        status: 'ok',
                        uptime: ManagementFactory.runtimeMXBean.uptime,
                        lastHeartbeat: staleSince
                    ]))
                } else {
                    res.status = 503
                    res.writer.println(JsonOutput.toJson([
                        status: 'degraded',
                        healthy: appHealthy,
                        lastHeartbeat: staleSince
                    ]))
                }
                base.handled = true
            }
        }
        healthServer.start()
    }

    void recordHeartbeat() {
        lastHeartbeat = System.currentTimeMillis()
    }

    void setHealthy(boolean healthy) {
        appHealthy = healthy
    }

    void stopHealthServer() {
        healthServer?.stop()
    }
}

Start the server from the application lifecycle:

// griffon-app/lifecycle/Initialize.groovy
import griffon.core.GriffonApplication

class Initialize {
    void init(GriffonApplication app) {
        def healthService = app.serviceManager.findService('health')
        healthService.startHealthServer(9100)
    }
}

Test it locally:

curl http://localhost:9100/
# => {"status":"ok","uptime":12345,"lastHeartbeat":500}

Step 2: Emit Heartbeats from Background Services

Griffon services run business logic in background threads. Make each service emit regular heartbeats so Vigilmon detects stalls:

// griffon-app/services/com/example/DataSyncService.groovy
package com.example

import griffon.core.artifact.GriffonService
import griffon.metadata.ArtifactProviderFor
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

@ArtifactProviderFor(GriffonService)
class DataSyncService {

    HealthService healthService
    private final executor = Executors.newScheduledThreadPool(1)

    void startSync() {
        executor.scheduleAtFixedRate({
            try {
                performSync()
                healthService.recordHeartbeat()
                pingVigilmon()
            } catch (Exception e) {
                log.error('Sync failed', e)
                healthService.setHealthy(false)
            }
        }, 0, 60, TimeUnit.SECONDS)
    }

    private void performSync() {
        // your business logic here
    }

    private void pingVigilmon() {
        def url = System.getenv('VIGILMON_HEARTBEAT_URL')
        if (url) {
            new URL(url).openConnection().with {
                requestMethod = 'GET'
                connect()
                responseCode // read response to complete the request
            }
        }
    }
}

Set the environment variable before launching the app:

export VIGILMON_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"

Step 3: Add Vigilmon HTTP Uptime Monitoring

  1. Log in to vigilmon.online and click Add Monitor.
  2. Set Type to HTTP / HTTPS.
  3. Enter your health endpoint URL: http://your-griffon-host:9100/.
  4. Set Check interval to 1 minute.
  5. Set Expected HTTP status to 200.
  6. Under Advanced, add a Response body contains check: "status":"ok".
  7. Click Save.

If the Griffon app crashes or the health server stops responding, Vigilmon alerts you within 2 minutes.


Step 4: Add a Heartbeat Monitor

The HTTP health endpoint tells you if the process is alive. The heartbeat monitor tells you if the application's background work is actually progressing.

  1. Click Add MonitorCron / Heartbeat.
  2. Set Expected ping interval to 1 minute.
  3. Set Grace period to 2 minutes (allows for GC pauses and sync jitter).
  4. Copy the generated heartbeat URL into the VIGILMON_HEARTBEAT_URL environment variable above.
  5. Click Save.

If your DataSyncService deadlocks or an exception breaks the schedule loop, pings stop and Vigilmon fires an alert.


Step 5: Monitor External Dependencies

Griffon desktop apps typically connect to APIs and databases. Monitor those dependencies independently so you know whether an incident originates in the app or a downstream service:

  1. Click Add MonitorHTTP / HTTPS.
  2. Add monitors for each external API your Griffon app depends on.
  3. Group them under a Monitor Group named Griffon App Dependencies.

When an alert fires on the app's health endpoint, check whether a dependency monitor also shows red — that narrows the root cause immediately.


Step 6: Set Up Alerts

In Vigilmon, configure alert channels for your Griffon monitors:

  1. Go to Alert ChannelsAdd Channel.
  2. Choose Slack, Email, or PagerDuty.
  3. Set alert thresholds: notify after 2 consecutive failures to avoid flapping on GC pauses.
  4. Assign the alert channel to both your HTTP monitor and your heartbeat monitor.

For internal tooling, create a Status Page so the support team can check app status without escalating to engineering:

  1. Go to Status PagesCreate Page.
  2. Add your Griffon health monitor and dependency monitors.
  3. Share the internal URL with the support desk.

Key Metrics to Watch

| Metric | Vigilmon feature | What to alert on | |---|---|---| | Process availability | HTTP monitor on :9100/ | Any 5xx or timeout | | Background service progress | Heartbeat monitor | Heartbeat missing > 3 min | | Response time | Response time chart | P95 > 5s | | External API health | HTTP monitor per dependency | Any 5xx or timeout | | SSL certs (if applicable) | Cert expiry monitor | Expires < 14 days |


Conclusion

Griffon desktop applications are easy to overlook in your monitoring stack because they don't sit behind a load balancer. An embedded health endpoint plus a heartbeat monitor gives you the same observability you'd apply to a microservice: you'll know within minutes if the app crashes, if a background service stalls, or if a downstream dependency is causing degradation. With these Vigilmon monitors in place, your operations team gets paged — not your users.

Get started free at vigilmon.online.

Monitor your app with Vigilmon

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

Start free →