tutorial

How to Monitor Apache Wink REST Framework Health with Vigilmon

Apache Wink resource registry initialization failures, provider resolution loops, and JAX-RS context injection errors are invisible until your REST endpoints stop responding or start returning 500s. Learn how to monitor Wink REST application availability, resource provider health, and scheduled service endpoint liveness with Vigilmon HTTP probes and heartbeat monitors.

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 WinkApplication or javax.ws.rs.core.Application subclass throws during getSingletons() or getClasses(), 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 MessageBodyReader or MessageBodyWriter provider is registered with an ambiguous @Consumes/@Produces annotation, 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 throws NullPointerException mid-execution
  • Client connection pool exhaustion: a RestClient instance 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 with IOException
  • Exception mapper override conflict: when multiple ExceptionMapper implementations 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: RequestHandler and ResponseHandler filters 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

  1. Log in to vigilmon.online and go to Monitors → New Monitor
  2. Choose HTTP / HTTPS
  3. Set the URL to your Wink health endpoint: https://app.your-org.example.com/api/health/wink
  4. Set the check interval to 1 minute
  5. Under Expected response, configure:
    • Status code: 200
    • Response body contains: "status":"ok"
    • Response time threshold: 3000ms
  6. Under Alert channels, assign your Slack or PagerDuty channel
  7. 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 RestClient to 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:

  1. In Vigilmon, go to Monitors → New Monitor → Heartbeat
  2. Set the name: wink-service-health-poll
  3. Set the expected interval: 5 minutes
  4. Set the grace period: 10 minutes
  5. Save — copy the heartbeat URL

For service endpoint refresh job liveness:

  1. Create another Heartbeat monitor named wink-endpoint-refresh
  2. Set the expected interval: 30 minutes
  3. Set the grace period: 60 minutes
  4. 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 1000ms for the Wink health endpoint (slow responses indicate filter chain overhead or provider resolution delays)
  • Alert at 2000ms for representative REST endpoints (slow responses indicate connection pool pressure or heavy provider chain execution)
  • Alert at 10 minutes gap in service health poll heartbeat (downstream service state cache is growing stale)
  • Alert at 60 minutes gap 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.

Monitor your app with Vigilmon

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

Start free →