How to Monitor Your Apache XMLBeans XML Binding Service (Free, Multi-Region)
Apache XMLBeans is the Java library that lets you access XML by binding it directly to strongly-typed Java classes generated from XML Schema (XSD). It powers enterprise document exchange in financial services, healthcare (HL7/FHIR document layers), insurance, and EDI processing — any domain where XML Schema compliance is a contractual requirement. When an XSD contract changes but the generated Java types are stale, or when the XMLBeans type system cache becomes corrupt after a deployment, parse() calls start returning null or throwing XmlException without a meaningful message.
By the end of this guide you'll have external uptime monitoring, multi-region health probes, heartbeat monitoring for batch XML processing jobs, and instant alerts — all on the free tier.
Why XMLBeans failures are hard to catch
XMLBeans combines XML parsing, schema validation, and Java type binding in a pipeline where each stage can fail silently:
Stale generated types — XMLBeans generates Java source from XSD. When the XSD contract changes in a partner system but scomp (the schema compiler) isn't re-run, XmlObject.parse() succeeds but the returned object silently ignores new required elements, causing validation to pass while data is lost.
Type system cache corruption — XMLBeans caches compiled schema metadata in JAR entries at startup. A partial deployment that overwrites some but not all JARs can corrupt this cache, causing XmlBeans.getContextTypeLoader() to return an inconsistent type loader that parses some documents but fails others.
StAX implementation conflict — XMLBeans uses StAX (JSR 173) internally. Enterprise application servers that bundle their own StAX implementation (WebLogic, JBoss) can override the JDK default and cause XmlObject.parse() to throw org.apache.xmlbeans.XmlException: error: Unexpected StAX event.
Batch document validation hangs — nightly regulatory filings and partner data exchanges run as scheduled jobs. If the XML Schema validation thread blocks on a malformed document, the entire batch job hangs indefinitely with no visible error.
Step 1: Expose a health endpoint that exercises XMLBeans
A health endpoint that only pings a database proves nothing about your XMLBeans pipeline. Validate an actual parse/validate round-trip:
With Spring Boot:
import org.apache.xmlbeans.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class HealthController {
// Minimal well-formed XML document for round-trip validation
private static final String PROBE_XML =
"<probe xmlns='http://vigilmon.online/health'>" +
" <status>UP</status>" +
" <timestamp>2026-01-01T00:00:00Z</timestamp>" +
"</probe>";
@GetMapping("/health")
public Map<String, Object> health() throws XmlException {
// Exercise XmlObject.parse() — tests the type system loader
XmlObject doc = XmlObject.Factory.parse(PROBE_XML);
if (doc == null) {
throw new IllegalStateException("XmlObject.Factory.parse returned null");
}
// Exercise XmlCursor — tests the cursor navigation layer
XmlCursor cursor = doc.newCursor();
cursor.toFirstChild();
String localName = cursor.getName().getLocalPart();
cursor.dispose();
if (!"probe".equals(localName)) {
throw new IllegalStateException("Cursor navigation returned: " + localName);
}
// Verify the type system is coherent
SchemaTypeSystem sts = XmlBeans.getBuiltinTypeSystem();
if (sts == null) {
throw new IllegalStateException("XmlBeans.getBuiltinTypeSystem() returned null");
}
return Map.of(
"status", "UP",
"xmlbeans", "ok",
"typeSystem", "ok",
"cursorNav", "ok"
);
}
}
With a plain servlet:
import org.apache.xmlbeans.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
@WebServlet("/health")
public class HealthServlet extends HttpServlet {
private static final String PROBE_XML =
"<probe><status>UP</status></probe>";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
res.setContentType("application/json");
try {
XmlObject doc = XmlObject.Factory.parse(PROBE_XML);
if (doc == null) throw new IllegalStateException("parse returned null");
XmlCursor cursor = doc.newCursor();
cursor.toFirstChild();
cursor.dispose();
res.getWriter().write("{\"status\":\"UP\",\"xmlbeans\":\"ok\"}");
} catch (Exception e) {
res.setStatus(500);
res.getWriter().write("{\"status\":\"DOWN\",\"error\":\"" + e.getMessage() + "\"}");
}
}
}
For services that use generated schema-bound types, add a round-trip parse of your most critical document type to the health check:
// If you have a generated type (e.g. from an invoice XSD):
// InvoiceDocument invoice = InvoiceDocument.Factory.parse(MINIMAL_INVOICE_XML);
// XmlOptions opts = new XmlOptions();
// List<XmlError> errors = new ArrayList<>();
// opts.setErrorListener(errors);
// if (!invoice.validate(opts) || !errors.isEmpty()) {
// throw new IllegalStateException("Schema validation failed: " + errors);
// }
Step 2: Set up external monitoring with Vigilmon
With /health live, point Vigilmon at it:
- Sign up at vigilmon.online — free tier, no credit card
- Click New Monitor → HTTP
- Enter
https://yourdomain.com/health - Set check interval (5 minutes on free tier)
- Save
Vigilmon checks from multiple geographic regions. A non-2xx response or timeout opens an incident and sends you an alert before a regulatory submission deadline is missed.
Add monitors for each critical endpoint:
| Endpoint | What it catches |
|---|---|
| /health | XMLBeans type system, parse pipeline, cursor layer |
| /api/submit/invoice | Invoice XML submission endpoint |
| /api/validate/document | Schema validation endpoint |
Step 3: Heartbeat monitoring for batch XML processing
Regulatory submission jobs, nightly partner data exchanges, and EDI batch processors run as scheduled jobs without HTTP endpoints. Heartbeat monitoring is essential:
import org.apache.xmlbeans.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.util.List;
public class NightlyRegulatorySubmissionJob {
private final String heartbeatUrl =
System.getenv("HEARTBEAT_REGULATORY_JOB_URL");
private final Path inboxPath = Path.of(System.getenv("REGULATORY_INBOX_DIR"));
public void run() throws Exception {
List<Path> xmlFiles = Files.list(inboxPath)
.filter(p -> p.toString().endsWith(".xml"))
.toList();
if (xmlFiles.isEmpty()) {
throw new IllegalStateException(
"No XML files found in regulatory inbox — upstream may have stalled");
}
int submitted = 0;
for (Path xmlFile : xmlFiles) {
String content = Files.readString(xmlFile);
XmlObject doc = XmlObject.Factory.parse(content);
// Validate against schema
XmlOptions opts = new XmlOptions();
List<XmlError> errors = new java.util.ArrayList<>();
opts.setErrorListener(errors);
if (!doc.validate(opts)) {
throw new XmlException("Schema validation failed for " +
xmlFile.getFileName() + ": " + errors);
}
submitToRegulator(doc);
Files.move(xmlFile, inboxPath.resolve("processed").resolve(xmlFile.getFileName()));
submitted++;
}
if (submitted != xmlFiles.size()) {
throw new IllegalStateException(
"Submitted " + submitted + "/" + xmlFiles.size() + " documents");
}
// Only ping after all documents submitted and moved
if (heartbeatUrl != null && !heartbeatUrl.isBlank()) {
pingHeartbeat(heartbeatUrl);
}
}
private void pingHeartbeat(String url) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5_000);
conn.getResponseCode();
conn.disconnect();
}
private void submitToRegulator(XmlObject doc) throws Exception {
// ... submit to regulatory endpoint
}
}
In Vigilmon:
- Click New Monitor → Heartbeat
- Set the expected interval (e.g. 25 hours for a daily job)
- Copy the unique ping URL
- Set the environment variable:
HEARTBEAT_REGULATORY_JOB_URL=https://vigilmon.online/api/heartbeat/your-unique-token
If schema validation fails, the type system cache is corrupt, or a StAX conflict causes XmlException, the heartbeat is never pinged and you get an alert after one missed interval.
Step 4: Validate generated schema types at startup
Generated XMLBeans types go stale when partner XSDs change. Catch this at startup rather than at midnight during a batch run:
import org.apache.xmlbeans.*;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class XmlBeansStartupValidator {
@EventListener(ApplicationReadyEvent.class)
public void validate() throws XmlException {
// Verify the parse pipeline is operational
String probeXml = "<probe><field>startup-check</field></probe>";
XmlObject doc = XmlObject.Factory.parse(probeXml);
if (doc == null) {
throw new IllegalStateException("XmlObject.Factory.parse returned null at startup");
}
// Verify the built-in type system is coherent
SchemaTypeSystem builtIn = XmlBeans.getBuiltinTypeSystem();
if (builtIn == null) {
throw new IllegalStateException(
"XmlBeans built-in type system is null — type system cache may be corrupt. " +
"Check for partial deployment or JAR version conflicts.");
}
// Verify XmlCursor is available (detects StAX implementation conflicts)
XmlCursor cursor = doc.newCursor();
try {
boolean moved = cursor.toFirstChild();
if (!moved) {
throw new IllegalStateException("XmlCursor.toFirstChild failed on valid document");
}
} finally {
cursor.dispose();
}
// If you have generated schema types, validate a representative minimal document:
// InvoiceDocument invoice = InvoiceDocument.Factory.parse(MINIMAL_VALID_INVOICE_XML);
// XmlOptions opts = new XmlOptions().setErrorListener(new ArrayList<>());
// if (!invoice.validate(opts)) {
// throw new IllegalStateException(
// "Generated InvoiceDocument schema validation failed — XSD may have changed. " +
// "Re-run scomp against the current partner XSD.");
// }
}
}
A corrupt type system cache, a StAX conflict introduced by a new application server version, or stale generated types now cause a startup crash rather than silent data loss at the first batch run.
Step 5: Alerts and badge embed
Slack/Discord alerts:
- In Vigilmon go to Notifications → New Channel
- Choose Slack or Discord, paste your webhook URL
- Enable it on your monitors
You get an instant alert when a monitor trips and a recovery notification when it's back.
Add an uptime badge to your README:
[](https://vigilmon.online?utm_source=devto&utm_medium=article&utm_campaign=xmlbeans-tutorial)
What you've built
| What | How |
|---|---|
| External health checks | /health with parse round-trip, cursor nav, type system check |
| Batch job monitoring | Heartbeat ping after each successful regulatory submission run |
| Type system validation | Startup check detects cache corruption and StAX conflicts |
| Schema staleness detection | Startup validate of generated types against current XSD |
| Instant alerts | Slack/Discord notifications |
| README status badge | Vigilmon badge embed |
The whole setup runs on the free tier in under 30 minutes. You'll catch XMLBeans type system cache corruption, StAX implementation conflicts, stale generated schema types, and silently stalled regulatory submission jobs before a filing deadline is missed.
Get started free at vigilmon.online — monitors running in under a minute, no credit card required.