Apache CXF is the most widely used Java framework for building SOAP and REST web services. It handles WSDL-to-Java code generation, WS-Security, WS-Addressing, MTOM attachments, and dozens of other JAX-WS and JAX-RS standards automatically. But CXF failure modes are subtle: a SOAPFaultException returns HTTP 200 with a fault body, a misconfigured security interceptor can reject all requests silently, and a CXF endpoint bound to the wrong WSDL version can appear healthy while returning malformed responses to every caller.
Vigilmon gives you external visibility into CXF web service health through HTTP probe monitoring and heartbeat monitors for the services behind those endpoints. This tutorial covers both.
Why CXF Needs External Monitoring
CXF's layered interceptor chain and protocol abstraction create failure modes that standard uptime checks miss:
- SOAP faults over HTTP 200: a SOAP service returns
<faultcode>in the response body with HTTP 200 — no port check or generic HTTP monitor catches this - Interceptor chain failures: a WS-Security
UsernameTokenvalidation failure, an expiredwsse:Timestamp, or a schema validation error returns HTTP 500 with a SOAP fault — not the usual 4xx/5xx an HTTP monitor expects - WSDL unavailability: the
?wsdlendpoint goes down independently of the service endpoint, breaking all clients that fetch WSDL dynamically - Thread pool exhaustion: CXF uses a Jetty or Tomcat thread pool; under load, requests queue indefinitely without returning errors until the timeout fires
- Bus lifecycle issues: a CXF
Busthat fails to initialize (missing feature, bad extension) leaves all endpoints on that bus unreachable
External monitoring with Vigilmon adds:
- Proactive alerting when CXF WSDL or REST health endpoints return non-200 or unexpected bodies
- SOAP-aware probe monitoring that checks for fault bodies in 200 responses
- Heartbeat monitoring for the business operations running behind your CXF endpoints
- Multi-region availability checking from outside your network perimeter
Step 1: Build a CXF Health Endpoint
CXF doesn't expose an HTTP health interface by default — you need to add one. The simplest approach is a dedicated JAX-RS health resource on the same CXF server.
Java JAX-RS Health Resource (CXF + Spring Boot)
// src/main/java/com/example/health/CxfHealthResource.java
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.endpoint.ServerRegistry;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.*;
@Path("/health")
public class CxfHealthResource {
private final Bus bus;
public CxfHealthResource(Bus bus) {
this.bus = bus;
}
@GET
@Path("/cxf")
@Produces(MediaType.APPLICATION_JSON)
public Response health() {
if (bus == null || bus.getState() == Bus.BusState.SHUTDOWN) {
return Response.status(503).entity(Map.of(
"status", "down",
"reason", "cxf_bus_shutdown"
)).build();
}
ServerRegistry registry = bus.getExtension(ServerRegistry.class);
if (registry == null) {
return Response.status(503).entity(Map.of(
"status", "degraded",
"reason", "server_registry_unavailable"
)).build();
}
List<Server> servers = registry.getServers();
List<Map<String, Object>> endpointDetails = new ArrayList<>();
List<String> unavailable = new ArrayList<>();
for (Server server : servers) {
String address = server.getEndpoint().getEndpointInfo().getAddress();
boolean started = server.isStarted();
endpointDetails.add(Map.of("address", address, "started", started));
if (!started) {
unavailable.add(address);
}
}
if (!unavailable.isEmpty()) {
return Response.status(503).entity(Map.of(
"status", "degraded",
"reason", "endpoints_not_started",
"unavailable", unavailable,
"endpoints", endpointDetails
)).build();
}
return Response.ok(Map.of(
"status", "ok",
"bus_state", bus.getState().name(),
"endpoint_count", servers.size(),
"endpoints", endpointDetails
)).build();
}
}
Register the health resource in your Spring Boot CXF configuration:
// CxfConfig.java
@Configuration
public class CxfConfig {
@Bean
public Server jaxRsServer(Bus bus) {
JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
factory.setBus(bus);
factory.setAddress("/api");
factory.setServiceBeans(List.of(
new CxfHealthResource(bus),
// ... your other resources
));
return factory.create();
}
}
WSDL Availability Check (Node.js Sidecar)
// health/cxf.js
const express = require('express');
const https = require('https');
const http = require('http');
const app = express();
const WSDL_URLS = (process.env.CXF_WSDL_URLS || '').split(',').filter(Boolean);
const SERVICE_URL = process.env.CXF_SERVICE_URL || 'http://localhost:8080/services/MyService';
function fetchUrl(url) {
return new Promise((resolve, reject) => {
const client = url.startsWith('https') ? https : http;
const req = client.get(url, { timeout: 5000 }, res => {
let body = '';
res.on('data', chunk => { body += chunk; });
res.on('end', () => resolve({ status: res.statusCode, body }));
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
});
}
app.get('/health/cxf', async (req, res) => {
const results = [];
let allOk = true;
// Check each WSDL endpoint
for (const wsdlUrl of WSDL_URLS) {
try {
const { status, body } = await fetchUrl(wsdlUrl);
const ok = status === 200 && body.includes('definitions');
results.push({ url: wsdlUrl, ok, status });
if (!ok) allOk = false;
} catch (err) {
results.push({ url: wsdlUrl, ok: false, error: err.message });
allOk = false;
}
}
// Check the main service URL for basic HTTP liveness
try {
const { status } = await fetchUrl(SERVICE_URL);
results.push({ url: SERVICE_URL, ok: status < 500, status });
if (status >= 500) allOk = false;
} catch (err) {
results.push({ url: SERVICE_URL, ok: false, error: err.message });
allOk = false;
}
const httpStatus = allOk ? 200 : 503;
return res.status(httpStatus).json({
status: allOk ? 'ok' : 'degraded',
endpoints: results,
});
});
app.listen(3009);
Python Health Sidecar
# health_cxf.py
import os, requests
from flask import Flask, jsonify
app = Flask(__name__)
WSDL_URLS = [u for u in os.environ.get('CXF_WSDL_URLS', '').split(',') if u]
SERVICE_URL = os.environ.get('CXF_SERVICE_URL', 'http://localhost:8080/services/MyService')
@app.route('/health/cxf')
def health():
results = []
all_ok = True
for url in WSDL_URLS:
try:
r = requests.get(url, timeout=5)
ok = r.status_code == 200 and 'definitions' in r.text
results.append({'url': url, 'ok': ok, 'status': r.status_code})
if not ok:
all_ok = False
except Exception as e:
results.append({'url': url, 'ok': False, 'error': str(e)})
all_ok = False
try:
r = requests.get(SERVICE_URL, timeout=5)
ok = r.status_code < 500
results.append({'url': SERVICE_URL, 'ok': ok, 'status': r.status_code})
if not ok:
all_ok = False
except Exception as e:
results.append({'url': SERVICE_URL, 'ok': False, 'error': str(e)})
all_ok = False
code = 200 if all_ok else 503
return jsonify({'status': 'ok' if all_ok else 'degraded', 'endpoints': results}), code
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3009)
Step 2: Configure Vigilmon HTTP Monitor for CXF
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your CXF health endpoint:
https://your-app.example.com/api/health/cxf - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"ok" - Response time threshold:
5000ms
- Status code:
- Under Alert channels, assign your Slack or PagerDuty channel
- Save the monitor
Add a dedicated WSDL monitor for each SOAP service:
- URL:
https://your-app.example.com/services/PaymentService?wsdl - Expected:
200, body contains:definitions - Interval: 2 minutes
- Alert channel: P2 Slack (WSDL unavailability breaks new integrations but not existing ones immediately)
Add a monitor for the REST API docs endpoint:
- URL:
https://your-app.example.com/services/api-docs(or OpenAPI endpoint) - Expected:
200 - Interval: 5 minutes
Vigilmon's multi-region probe consensus filters transient network issues from genuine endpoint failures.
Step 3: Heartbeat Monitoring for CXF Business Operations
Endpoint liveness monitoring catches down services, but not functionally broken ones. A SOAP service can return HTTP 200 with a <faultcode>Server</faultcode> for every request — the endpoint is "up" but producing no successful results.
Vigilmon heartbeat monitors catch these: your service logic pings Vigilmon after each successful business operation.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
payment-service-cxf-processing - Set the expected interval: 10 minutes
- Set the grace period: 15 minutes
- Save — copy the heartbeat URL, e.g.
https://vigilmon.online/heartbeat/abc123xyz
Wire It Into a JAX-WS Service Implementation
// PaymentServiceImpl.java
import javax.jws.WebService;
import java.net.http.*;
import java.net.URI;
@WebService(endpointInterface = "com.example.PaymentService")
public class PaymentServiceImpl implements PaymentService {
private final HttpClient http = HttpClient.newHttpClient();
private final String heartbeatUrl = System.getenv("VIGILMON_HEARTBEAT_URL");
@Override
public PaymentResponse processPayment(PaymentRequest request) {
// Validate and process
PaymentResponse response = paymentGateway.charge(request);
if (response.isSuccess()) {
// Signal Vigilmon that a payment was successfully processed
pingHeartbeat();
}
return response;
}
private void pingHeartbeat() {
if (heartbeatUrl == null) return;
try {
http.sendAsync(
HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
Wire It Into a CXF Interceptor for Cross-Cutting Heartbeat Pings
// VigilmonHeartbeatInterceptor.java
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import java.net.http.*;
import java.net.URI;
public class VigilmonHeartbeatInterceptor extends AbstractPhaseInterceptor<Message> {
private final HttpClient http = HttpClient.newHttpClient();
private final String heartbeatUrl;
public VigilmonHeartbeatInterceptor(String heartbeatUrl) {
super(Phase.SEND);
this.heartbeatUrl = heartbeatUrl;
}
@Override
public void handleMessage(Message message) throws Fault {
// This fires after a successful outbound response is assembled
Boolean fault = message.getContent(Boolean.class);
if (Boolean.TRUE.equals(fault)) return; // Don't ping on faults
try {
http.sendAsync(
HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
Register the interceptor in Spring configuration:
@Bean
public Endpoint paymentEndpoint(Bus bus, PaymentServiceImpl impl) {
EndpointImpl endpoint = new EndpointImpl(bus, impl);
endpoint.publish("/PaymentService");
endpoint.getOutInterceptors().add(new VigilmonHeartbeatInterceptor(
System.getenv("VIGILMON_HEARTBEAT_URL")
));
return endpoint;
}
Step 4: Alert Routing for CXF Failures
CXF failures range from a stalled interceptor (degraded but recoverable) to a Bus shutdown (all services unreachable). Route alerts by severity:
| Monitor | Alert Channel | Priority |
|---|---|---|
| HTTP: /api/health/cxf bus + endpoint state | Slack + PagerDuty | P1 |
| HTTP: WSDL availability ?wsdl | Slack + PagerDuty | P1 |
| Heartbeat: business operation (payment, order, etc.) | Slack + PagerDuty | P1 |
| HTTP: REST API docs / OpenAPI endpoint | Slack | P2 |
Set response time thresholds as early warning signals:
- Alert at
2000msfor the health endpoint (elevated latency may indicate thread pool pressure) - Alert at
3000msfor WSDL endpoints (slow WSDL responses block client startup) - Alert at
5000msfor the main service endpoint under load
For production SOAP services, set up a status page in Vigilmon showing CXF endpoint health alongside WSDL availability — consumers integrating with your service can self-diagnose integration failures against this page.
Summary
CXF failures are protocol-layer failures — the JVM is up, the port is open, but SOAP faults are flowing or the WS-Security interceptor is rejecting every request. External monitoring catches these before they become incidents:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /api/health/cxf | CXF Bus state, registered endpoint count, endpoint started state |
| HTTP monitor on ?wsdl endpoints | WSDL availability and schema validity |
| HTTP monitor on REST API docs | OpenAPI / Swagger endpoint availability |
| Heartbeat monitor on business operation | End-to-end successful service invocation liveness |
Get started free at vigilmon.online — your first CXF service monitor is running in under two minutes.