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
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter your health endpoint URL:
http://your-griffon-host:9100/. - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Under Advanced, add a Response body contains check:
"status":"ok". - 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.
- Click Add Monitor → Cron / Heartbeat.
- Set Expected ping interval to
1 minute. - Set Grace period to
2 minutes(allows for GC pauses and sync jitter). - Copy the generated heartbeat URL into the
VIGILMON_HEARTBEAT_URLenvironment variable above. - 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:
- Click Add Monitor → HTTP / HTTPS.
- Add monitors for each external API your Griffon app depends on.
- 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:
- Go to Alert Channels → Add Channel.
- Choose Slack, Email, or PagerDuty.
- Set alert thresholds: notify after 2 consecutive failures to avoid flapping on GC pauses.
- 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:
- Go to Status Pages → Create Page.
- Add your Griffon health monitor and dependency monitors.
- 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.