Apache Wink is an open-source Java framework that implements the JAX-RS specification for building RESTful web services, providing resource registration, content negotiation, message body reader/writer chains, and a client-side HTTP abstraction. When a Wink application registry fails to register all resource classes after a classloading error during deployment, some REST endpoints return 404 while others return 200 — the application process is healthy but the resource tree is partially built; when a custom MessageBodyWriter encounters an unhandled media type during content negotiation, Wink throws WebApplicationException with status 406 but the error propagates up incorrectly and clients receive 500 responses; when a Wink client instance is shared across request threads without proper connection pool configuration, concurrent requests exhaust the underlying HttpClient connection pool and new requests block until timeout — all while the JVM reports normal heap usage and the process health check returns green. These are silent correctness and availability failures that external monitoring catches before user-facing REST errors accumulate.
Vigilmon gives you external visibility into Wink-backed REST application health through HTTP probe monitoring and heartbeat monitors. This tutorial covers both.
Why Wink Applications Need External Monitoring
Wink's failure modes span the resource registration, provider resolution, and client execution layers:
- Resource registry corruption: if a
WinkApplicationorjavax.ws.rs.core.Applicationsubclass throws duringgetSingletons()orgetClasses(), Wink silently skips the affected resources — endpoints that should exist return 404 while the application continues serving other routes - Provider chain resolution failure: when a
MessageBodyReaderorMessageBodyWriterprovider is registered with an ambiguous@Consumes/@Producesannotation, Wink's provider selection algorithm may select the wrong provider — clients receive incorrectly serialized responses with no error indication - Context injection failure: Wink injects
@Context UriInfo,@Context HttpHeaders, and other JAX-RS context objects at runtime; if the injection container fails to resolve a context at request time, the affected endpoint throwsNullPointerExceptionmid-execution - Client connection pool exhaustion: a
RestClientinstance shared across threads without explicit connection manager configuration creates one connection per request — under concurrent load, file descriptor limits are reached and new requests fail withIOException - Exception mapper override conflict: when multiple
ExceptionMapperimplementations handle the same exception type, Wink's selection is non-deterministic; clients receive inconsistent error formats that break automated error handling in consumers - Filter chain order failure:
RequestHandlerandResponseHandlerfilters registered in the wrong order can corrupt request state — authentication filters running after business logic filters, or response compression applied before content-type headers are set
External monitoring with Vigilmon adds:
- Proactive alerting when Wink REST endpoints stop responding successfully
- Resource registry health visibility through a dedicated registry probe endpoint
- Heartbeat monitoring so you know when scheduled service endpoint liveness checks or background REST jobs stop completing
- Multi-region probe consensus that separates transient GC pauses from genuine registry initialization failures
Step 1: Build a Wink Health Endpoint
Wink does not ship a built-in health endpoint. Add one to your REST application that exercises the resource registry and returns a structured response.
Wink REST Resource (Health Endpoint)
// resources/HealthResource.java
package com.yourorg.resources;
import org.apache.wink.common.annotations.Workspace;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.LinkedHashMap;
import java.util.Map;
@Path("/health/wink")
@Workspace(workspaceTitle = "Health", collectionTitle = "Wink Health")
public class HealthResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response check(@Context UriInfo uriInfo, @Context HttpHeaders headers) {
Map<String, Object> result = new LinkedHashMap<>();
try {
// Verify context injection is working
if (uriInfo == null || headers == null) {
result.put("status", "degraded");
result.put("detail", "JAX-RS context injection failed");
return Response.status(503).entity(result).build();
}
// Verify the resource is reachable and serialization works
result.put("status", "ok");
result.put("path", uriInfo.getPath());
result.put("accept", headers.getRequestHeader("Accept") != null
? headers.getRequestHeader("Accept").toString()
: "not set");
return Response.ok(result).build();
} catch (Exception e) {
result.put("status", "down");
result.put("error", e.getMessage());
return Response.status(503).entity(result).build();
}
}
}
Register the health resource in your application:
// WinkApplication.java
package com.yourorg;
import com.yourorg.resources.HealthResource;
import org.apache.wink.server.utils.RegistrationUtils;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
public class WinkApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(HealthResource.class);
// ... other resources
return classes;
}
}
In web.xml, configure the Wink servlet:
<servlet>
<servlet-name>RestServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
<init-param>
<param-name>applicationConfigLocation</param-name>
<param-value>/WEB-INF/application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RestServlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
The /api/health/wink response:
{
"status": "ok",
"path": "health/wink",
"accept": "not set"
}
Spring-integrated Wink Application
// health/WinkHealthController.java
package com.yourorg.health;
import org.apache.wink.spring.Registrar;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class WinkHealthIndicator implements HealthIndicator {
private final Registrar winkRegistrar;
public WinkHealthIndicator(Registrar winkRegistrar) {
this.winkRegistrar = winkRegistrar;
}
@Override
public Health health() {
try {
// Verify the Wink registrar is initialized and has registered resources
int resourceCount = winkRegistrar.getInstances() != null
? winkRegistrar.getInstances().size() : 0;
if (resourceCount == 0) {
return Health.down()
.withDetail("wink", "no resources registered")
.build();
}
return Health.up()
.withDetail("wink", "registry operational")
.withDetail("resources", resourceCount)
.build();
} catch (Exception e) {
return Health.down(e)
.withDetail("wink", "registry unavailable")
.build();
}
}
}
Node.js Sidecar
// health/wink.js
const express = require('express');
const app = express();
const WINK_APP_BASE = process.env.WINK_APP_BASE || 'http://localhost:8080';
app.get('/health/wink', async (req, res) => {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 4000);
const response = await fetch(`${WINK_APP_BASE}/api/health/wink`, {
signal: controller.signal,
});
clearTimeout(timeout);
const body = await response.json();
const ok = response.ok && body?.status === 'ok';
return res.status(ok ? 200 : 503).json({ status: ok ? 'ok' : 'degraded', upstream: body });
} catch (err) {
return res.status(503).json({ status: 'down', error: err.message });
}
});
app.listen(3021);
Python Sidecar
# health_wink.py
import os
import urllib.request
import json
from flask import Flask, jsonify
app = Flask(__name__)
WINK_APP_BASE = os.environ.get('WINK_APP_BASE', 'http://localhost:8080')
@app.route('/health/wink')
def health():
try:
url = f'{WINK_APP_BASE}/api/health/wink'
with urllib.request.urlopen(url, timeout=4) as resp:
body = json.loads(resp.read())
ok = body.get('status') == 'ok'
return jsonify({'status': 'ok' if ok else 'degraded', 'upstream': body}), 200 if ok else 503
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3021)
Step 2: Configure Vigilmon HTTP Monitor for Wink
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your Wink health endpoint:
https://app.your-org.example.com/api/health/wink - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"ok" - Response time threshold:
3000ms
- Status code:
- Under Alert channels, assign your Slack or PagerDuty channel
- Save the monitor
Add a second monitor for a representative REST endpoint to confirm end-to-end resource registration:
- URL:
https://app.your-org.example.com/api/v1/ping(a lightweight endpoint that verifies Wink routing) - Expected:
200 - Interval: 2 minutes
- Alert channel: PagerDuty P1 — failure here means the resource registry is partially initialized or the provider chain is broken
For applications with multiple Wink servlet contexts, add one monitor per context health path:
| Endpoint | What it catches |
|---|---|
| /api/health/wink | Resource registration, context injection, serialization |
| /api/v1/ping | End-to-end Wink routing and provider resolution |
| /actuator/health/wink (Spring) | Spring-integrated Wink registry status |
Vigilmon's multi-region probing filters single-region network blips from genuine Wink registry failures.
Step 3: Heartbeat Monitoring for Service Discovery and Background REST Jobs
HTTP health checks verify the REST endpoints are reachable but not the health of background jobs that interact with remote services via the Wink client or maintain service registry state. Wink applications often run:
- Downstream service health polling: background jobs that use a
RestClientto probe dependent REST services and update local cache - Service endpoint refresh jobs: scheduled tasks that refresh registered service endpoint URLs from a configuration source
- REST-based report generation: batch jobs that call upstream REST APIs to gather data for nightly reports
Vigilmon heartbeat monitors detect when these jobs stop executing.
Set Up Heartbeat Monitors
For downstream service polling job liveness:
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
wink-service-health-poll - Set the expected interval: 5 minutes
- Set the grace period: 10 minutes
- Save — copy the heartbeat URL
For service endpoint refresh job liveness:
- Create another Heartbeat monitor named
wink-endpoint-refresh - Set the expected interval: 30 minutes
- Set the grace period: 60 minutes
- Save — copy the heartbeat URL
Wire Heartbeats Into Scheduled Jobs
// scheduler/ServiceHealthPollJob.java
package com.yourorg.scheduler;
import org.apache.wink.client.ClientConfig;
import org.apache.wink.client.Resource;
import org.apache.wink.client.RestClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.*;
@Service
public class ServiceHealthPollJob {
private final RestClient winkClient;
private final HttpClient http = HttpClient.newHttpClient();
private final String heartbeatUrl = System.getenv("VIGILMON_SERVICE_POLL_HEARTBEAT_URL");
public ServiceHealthPollJob() {
ClientConfig config = new ClientConfig();
config.connectTimeout(3000);
config.readTimeout(5000);
this.winkClient = new RestClient(config);
}
@Scheduled(fixedDelay = 300000) // every 5 minutes
public void pollDownstreamServices() {
try {
String[] serviceUrls = System.getenv("DOWNSTREAM_SERVICE_URLS").split(",");
for (String url : serviceUrls) {
Resource resource = winkClient.resource(url.trim() + "/health");
javax.ws.rs.core.Response resp = resource.get();
// Log status; update local health cache
resp.close();
}
pingHeartbeat();
} catch (Exception e) {
// log — do NOT ping heartbeat on failure
throw new RuntimeException("Service health poll failed", e);
}
}
private void pingHeartbeat() {
if (heartbeatUrl == null || heartbeatUrl.isBlank()) return;
try {
http.sendAsync(
HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
For shell-based REST polling scripts:
#!/bin/bash
# wink-service-poll.sh
set -e
java -jar /opt/app/wink-service-probe.jar --services="$DOWNSTREAM_SERVICE_URLS"
if [ -n "$VIGILMON_SERVICE_POLL_HEARTBEAT_URL" ]; then
curl -s "$VIGILMON_SERVICE_POLL_HEARTBEAT_URL" > /dev/null 2>&1 || true
fi
Step 4: Alert Routing for Wink Failures
Wink failures range from hard registry initialization crashes that take down all endpoints to silent provider chain misconfiguration that corrupts response serialization. Route alerts by impact:
| Monitor | Alert Channel | Priority |
|---|---|---|
| HTTP: /api/health/wink | Slack + PagerDuty | P1 |
| HTTP: representative REST endpoint | Slack + PagerDuty | P1 |
| HTTP: actuator health (Spring) | Slack + PagerDuty | P1 |
| Heartbeat: downstream service poll | Slack | P2 |
| Heartbeat: endpoint refresh job | Slack + email | P2 |
Set response time thresholds as early warning signals:
- Alert at
1000msfor the Wink health endpoint (slow responses indicate filter chain overhead or provider resolution delays) - Alert at
2000msfor representative REST endpoints (slow responses indicate connection pool pressure or heavy provider chain execution) - Alert at
10 minutesgap in service health poll heartbeat (downstream service state cache is growing stale) - Alert at
60 minutesgap in endpoint refresh heartbeat during the expected refresh window
For production Wink deployments backing microservice APIs, set up a status page in Vigilmon showing REST layer availability — this gives operations teams a single pane of glass to correlate Wink resource registry alerts with downstream service incidents before clients report unexpected error codes.
Summary
Wink failures typically present as partial outages — a subset of resource classes missing from the registry returns 404 while others serve correctly, or a provider chain misconfiguration corrupts serialization for specific media types — rather than total application crashes. External monitoring catches these earlier than your users do:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /api/health/wink | Resource registry, context injection, serialization |
| HTTP monitor on REST endpoint | End-to-end Wink routing and provider resolution |
| HTTP monitor on actuator health | Spring-integrated Wink registry and filter chain |
| Heartbeat monitor (service poll) | Downstream service health polling job liveness |
| Heartbeat monitor (endpoint refresh) | Service endpoint refresh job liveness |
Get started free at vigilmon.online — your first Wink REST monitor is running in under two minutes.