tutorial

How to Monitor Apache Neethi WS-Policy Health with Vigilmon

Apache Neethi implements WS-Policy for CXF and Axis2 — but policy assertion not found, unsupported assertions, and policy merge conflicts fail silently as SOAP faults or silent security bypass. Learn how to monitor Neethi policy framework health with Vigilmon HTTP probes and heartbeat monitors.

Apache Neethi is the WS-Policy framework implementation used by both Apache CXF and Apache Axis2. It handles policy attachment, alternative selection, and assertion matching — determining which combination of security, reliability, and transport assertions both a service and client can satisfy. Neethi failure modes are among the most deceptive in the SOAP stack: a PolicyException thrown because no alternative can be satisfied returns a SOAP fault as HTTP 200, an unsupported assertion that Neethi cannot recognise causes it to silently select an alternative with weaker security (or none), and a policy merge conflict between an endpoint policy and an operation policy causes the effective policy to be undefined. The Axis2 or CXF container is running, the WSDL is accessible, and the service is silently operating outside its declared security policy.

Vigilmon gives you external visibility into Neethi policy framework health through HTTP probe monitoring and heartbeat monitors that detect when policy resolution and assertion matching stop working correctly. This tutorial covers both.


Why Neethi Needs External Monitoring

Neethi's policy assertion registry and alternative selection create failure modes that port checks and WSDL availability checks miss entirely:

  • Policy assertion not found: an assertion URI that no registered AssertionBuilder recognises causes Neethi to silently mark the policy as unsatisfiable or ignore the assertion depending on the wsp:Optional flag
  • No satisfiable alternative: when Neethi cannot find a policy alternative that both parties support, it throws PolicyException — returned to callers as HTTP 200 with a SOAP fault
  • Policy merge conflicts: endpoint-level and operation-level policies that share incompatible assertions produce an effective policy that is either overly permissive or unsatisfiable
  • Stale policy cache: Neethi caches resolved policies; a WSDL policy update that is not reflected in the cache causes requests to be validated against outdated assertions
  • Missing extension registrations: a custom assertion builder not registered with the Neethi AssertionBuilderFactory causes all policies containing that assertion to fail resolution

External monitoring with Vigilmon adds:

  • Proactive alerting when the Neethi health endpoint returns non-200 or unexpected bodies
  • Policy resolution and assertion registry monitoring via a custom health resource
  • Heartbeat monitoring for successful policy-compliant message exchange
  • Multi-region availability checking from outside your network perimeter

Step 1: Build a Neethi Health Endpoint

Neethi does not expose an HTTP health interface — you need to add one that validates the assertion builder registry, tests policy resolution against a reference policy document, and checks that the effective policy on critical service endpoints is non-empty.

Java JAX-RS Health Resource (Neethi + CXF + Spring Boot)

// src/main/java/com/example/health/NeethiHealthResource.java
import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.endpoint.ServerRegistry;
import org.apache.cxf.ws.policy.*;
import org.apache.neethi.*;
import org.apache.neethi.builders.AssertionBuilder;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.xml.namespace.QName;
import java.util.*;

@Path("/health")
public class NeethiHealthResource {

    private final Bus bus;

    public NeethiHealthResource(Bus bus) {
        this.bus = bus;
    }

    @GET
    @Path("/neethi")
    @Produces(MediaType.APPLICATION_JSON)
    public Response health() {
        List<String> issues = new ArrayList<>();
        Map<String, Object> info = new LinkedHashMap<>();

        // Check the CXF PolicyEngine is available on the Bus
        PolicyEngine policyEngine = bus.getExtension(PolicyEngine.class);
        if (policyEngine == null) {
            issues.add("policy_engine_not_available");
        } else {
            info.put("policy_engine_class", policyEngine.getClass().getSimpleName());
        }

        // Check the AssertionBuilderRegistry for required namespaces
        AssertionBuilderRegistry abr = bus.getExtension(AssertionBuilderRegistry.class);
        if (abr == null) {
            issues.add("assertion_builder_registry_not_available");
        } else {
            // Verify that at minimum the WS-Security assertion builder is registered
            String wsSecNs = "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702";
            QName usernameTokenQName = new QName(wsSecNs, "UsernameToken");
            AssertionBuilder<?> builder = abr.getAssertionBuilder(usernameTokenQName);
            if (builder == null) {
                issues.add("ws_security_assertion_builder_missing");
            } else {
                info.put("ws_security_assertions", "registered");
            }
        }

        // Check that registered service endpoints have non-empty effective policies
        ServerRegistry registry = bus.getExtension(ServerRegistry.class);
        if (registry != null && policyEngine != null) {
            List<String> unpoliciedEndpoints = new ArrayList<>();
            for (Server server : registry.getServers()) {
                try {
                    EndpointPolicy ep = policyEngine.getClientEndpointPolicy(
                        server.getEndpoint().getEndpointInfo(),
                        null, null);
                    if (ep == null || ep.getPolicy() == null || ep.getPolicy().isEmpty()) {
                        // Only flag as an issue if the service WSDL declares a policy
                        String address = server.getEndpoint().getEndpointInfo().getAddress();
                        unpoliciedEndpoints.add(address);
                    }
                } catch (Exception e) {
                    issues.add("policy_resolution_failed_for: " +
                        server.getEndpoint().getEndpointInfo().getAddress() +
                        " (" + e.getMessage() + ")");
                }
            }
            info.put("endpoint_count", registry.getServers().size());
        }

        if (!issues.isEmpty()) {
            return Response.status(503).entity(Map.of(
                "status", "degraded",
                "issues", issues,
                "info", info
            )).build();
        }

        return Response.ok(Map.of(
            "status", "ok",
            "info", info
        )).build();
    }
}

Register the health resource in your Spring Boot CXF configuration:

// NeethiHealthConfig.java
@Configuration
public class NeethiHealthConfig {

    @Bean
    public Server jaxRsServer(Bus bus) {
        JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
        factory.setBus(bus);
        factory.setAddress("/api");
        factory.setServiceBeans(List.of(new NeethiHealthResource(bus)));
        return factory.create();
    }
}

Node.js Health Sidecar

// health/neethi.js
const express = require('express');
const https = require('https');
const http = require('http');

const app = express();

const SERVICE_WSDL_URL = process.env.SERVICE_WSDL_URL || 'http://localhost:8080/services/PolicyService?wsdl';
const NEETHI_HEALTH_URL = process.env.NEETHI_HEALTH_URL || 'http://localhost:8080/api/health/neethi';

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/neethi', async (req, res) => {
  const results = [];
  let allOk = true;

  // Proxy the Java Neethi health resource
  try {
    const { status, body } = await fetchUrl(NEETHI_HEALTH_URL);
    const parsed = JSON.parse(body);
    const ok = status === 200 && parsed.status === 'ok';
    results.push({ check: 'neethi_policy_engine', ok, status, detail: parsed });
    if (!ok) allOk = false;
  } catch (err) {
    results.push({ check: 'neethi_policy_engine', ok: false, error: err.message });
    allOk = false;
  }

  // Verify that the service WSDL contains WS-Policy assertions
  try {
    const { status, body } = await fetchUrl(SERVICE_WSDL_URL);
    const hasPolicy = body.includes('wsp:Policy') || body.includes('Policy xmlns');
    const ok = status === 200 && hasPolicy;
    results.push({ check: 'wsdl_policy_assertions', ok, status });
    if (!ok) allOk = false;
  } catch (err) {
    results.push({ check: 'wsdl_policy_assertions', ok: false, error: err.message });
    allOk = false;
  }

  return res.status(allOk ? 200 : 503).json({
    status: allOk ? 'ok' : 'degraded',
    checks: results,
  });
});

app.listen(3013);

Python Health Sidecar

# health_neethi.py
import os, requests
from flask import Flask, jsonify

app = Flask(__name__)

SERVICE_WSDL_URL = os.environ.get('SERVICE_WSDL_URL', 'http://localhost:8080/services/PolicyService?wsdl')
NEETHI_HEALTH_URL = os.environ.get('NEETHI_HEALTH_URL', 'http://localhost:8080/api/health/neethi')

@app.route('/health/neethi')
def health():
    results = []
    all_ok = True

    try:
        r = requests.get(NEETHI_HEALTH_URL, timeout=5)
        parsed = r.json()
        ok = r.status_code == 200 and parsed.get('status') == 'ok'
        results.append({'check': 'neethi_policy_engine', 'ok': ok, 'status': r.status_code, 'detail': parsed})
        if not ok:
            all_ok = False
    except Exception as e:
        results.append({'check': 'neethi_policy_engine', 'ok': False, 'error': str(e)})
        all_ok = False

    try:
        r = requests.get(SERVICE_WSDL_URL, timeout=5)
        has_policy = 'wsp:Policy' in r.text or 'Policy xmlns' in r.text
        ok = r.status_code == 200 and has_policy
        results.append({'check': 'wsdl_policy_assertions', 'ok': ok, 'status': r.status_code})
        if not ok:
            all_ok = False
    except Exception as e:
        results.append({'check': 'wsdl_policy_assertions', 'ok': False, 'error': str(e)})
        all_ok = False

    code = 200 if all_ok else 503
    return jsonify({'status': 'ok' if all_ok else 'degraded', 'checks': results}), code

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3013)

Step 2: Configure Vigilmon HTTP Monitor for Neethi

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

Add a secondary monitor for the policy-enabled WSDL:

  • URL: https://your-app.example.com/services/PolicyService?wsdl
  • Expected: 200, body contains: wsp:Policy
  • Interval: 2 minutes
  • Alert channel: P1 (a WSDL without policy assertions means clients cannot establish compliant security channels)

Add a monitor for the CXF services list:

  • URL: https://your-app.example.com/services
  • Expected: 200, body contains: PolicyService
  • Interval: 5 minutes
  • Alert channel: P2 Slack

Vigilmon's multi-region probe consensus filters transient policy resolution timeouts from genuine assertion registry failures.


Step 3: Heartbeat Monitoring for Neethi Policy-Compliant Operations

Policy engine health confirms the assertion builder registry is intact, but not that policy negotiation succeeds for real client-service interactions. A Neethi PolicyEngine that resolves local assertions correctly may still fail when negotiating with an external client that sends an incompatible wsp:PolicyReference.

Vigilmon heartbeat monitors catch these gaps: your service logic pings Vigilmon after each successfully policy-negotiated and processed message exchange.

Set Up the Heartbeat Monitor

  1. In Vigilmon, go to Monitors → New Monitor → Heartbeat
  2. Set the name: policy-service-neethi-negotiation
  3. Set the expected interval: 10 minutes
  4. Set the grace period: 15 minutes
  5. Save — copy the heartbeat URL, e.g. https://vigilmon.online/heartbeat/abc123xyz

Wire It Into a CXF JAX-WS Service Implementation

// PolicyServiceImpl.java
import javax.jws.WebService;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.PhaseInterceptorChain;
import java.net.http.*;
import java.net.URI;

@WebService(endpointInterface = "com.example.PolicyService")
public class PolicyServiceImpl implements PolicyService {

    private final HttpClient http = HttpClient.newHttpClient();
    private final String heartbeatUrl = System.getenv("VIGILMON_HEARTBEAT_URL");

    @Override
    public TransactionResponse processTransaction(TransactionRequest request) {
        // By the time we reach here, Neethi has already resolved the effective policy
        // and CXF has enforced it against the incoming message
        TransactionResponse response = transactionProcessor.process(request);

        if (response.isSuccess()) {
            // Policy negotiation and business logic both succeeded
            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 That Confirms Policy Enforcement

// VigilmonPolicyHeartbeatInterceptor.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 org.apache.cxf.ws.policy.EffectivePolicy;
import java.net.http.*;
import java.net.URI;

public class VigilmonPolicyHeartbeatInterceptor extends AbstractPhaseInterceptor<Message> {

    private final HttpClient http = HttpClient.newHttpClient();
    private final String heartbeatUrl = System.getenv("VIGILMON_HEARTBEAT_URL");

    public VigilmonPolicyHeartbeatInterceptor() {
        super(Phase.POST_LOGICAL);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        // Verify an effective policy was applied (Neethi resolved a non-empty alternative)
        EffectivePolicy ep = message.get(EffectivePolicy.class);
        if (ep == null || ep.getPolicy() == null || ep.getPolicy().isEmpty()) {
            return; // No policy enforcement occurred — don't ping as success
        }
        if (heartbeatUrl == null) return;
        try {
            http.sendAsync(
                HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
                HttpResponse.BodyHandlers.discarding()
            );
        } catch (Exception ignored) {}
    }
}

Register the interceptor on your CXF endpoint:

@Bean
public Endpoint policyServiceEndpoint(Bus bus, PolicyServiceImpl impl) {
    EndpointImpl endpoint = new EndpointImpl(bus, impl);
    endpoint.publish("/PolicyService");
    endpoint.getInInterceptors().add(new VigilmonPolicyHeartbeatInterceptor());
    return endpoint;
}

Step 4: Alert Routing for Neethi Failures

Neethi failures range from a single unsupported assertion (one service endpoint unprotected) to a broken AssertionBuilderRegistry (all policy resolution failing). Route alerts by severity:

| Monitor | Alert Channel | Priority | |---|---|---| | HTTP: /api/health/neethi policy engine + registry | Slack + PagerDuty | P1 | | HTTP: Policy-enabled WSDL with wsp:Policy | Slack + PagerDuty | P1 | | Heartbeat: successful policy-negotiated message | Slack + PagerDuty | P1 | | HTTP: CXF services list with service name | Slack | P2 |

Set response time thresholds as early warning signals:

  • Alert at 2000ms for the Neethi health endpoint (slow assertion resolution may indicate policy graph complexity or registry contention)
  • Alert at 3000ms for the policy-enabled WSDL (slow WSDL delivery with embedded policy references delays client initialisation)
  • Alert at 10000ms for actual service invocations (policy negotiation and cryptographic enforcement under Neethi add measurable latency; elevated values indicate saturation or resolution failures)

For services operating under regulatory or contractual security requirements, set up a status page in Vigilmon showing Neethi policy engine health alongside the heartbeat monitor state — compliance teams can use this page to verify that WS-Policy enforcement is continuously active.


Summary

Neethi failures are policy framework failures — CXF or Axis2 is running, SOAP messages are being exchanged, but the WS-Policy assertion registry is broken or no satisfiable alternative can be resolved, leaving services operating outside their declared security envelope. External monitoring catches these before they become silent compliance violations:

| Monitor Type | What It Covers | |---|---| | HTTP monitor on /api/health/neethi | PolicyEngine availability, AssertionBuilderRegistry, WS-Security assertion registration | | HTTP monitor on policy WSDL | WS-Policy assertion presence in published WSDL | | Heartbeat monitor on policy-negotiated processing | End-to-end Neethi policy resolution and business logic liveness | | HTTP monitor on CXF services list | Service deployment confirmation |

Get started free at vigilmon.online — your first Neethi policy 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 →