Apache MyFaces is a production-grade JavaServer Faces (JSF) implementation that powers enterprise Java EE and Jakarta EE web applications through a stateful component lifecycle, managed bean dependency injection, and server-side view state management. MyFaces sits in the critical path of every page render and form postback — when the session store is near capacity, ViewExpiredException is thrown on every postback; when a phase listener throws during the INVOKE_APPLICATION phase, JSF catches the exception and renders a styled error page that returns HTTP 200; when the EL resolver fails to find a managed bean property, the form field renders blank with no visible error. These are production incidents disguised as UI rendering problems.
Vigilmon gives you external visibility into MyFaces's JSF layer through HTTP probe monitoring and heartbeat monitors. This tutorial covers both.
Why MyFaces Needs External Monitoring
MyFaces's failure modes are subtle and high-impact:
- Phase listener exceptions returning 200: JSF's
ExceptionHandlerFactorycatches unhandled exceptions inRESTORE_VIEW,UPDATE_MODEL_VALUES, andINVOKE_APPLICATIONand renders a styled error page that returns HTTP 200 — load balancers and simple ping monitors see a healthy response while users see broken pages - ViewState and session failures: MyFaces stores view state server-side in the HTTP session; when the session store is full, replicated incorrectly, or corrupted, every form postback throws
ViewExpiredException— in default configuration this returns 200 and is invisible to standard uptime monitors - CDI and managed bean scope failures: CDI
@SessionScopedor@ConversationScopedbeans that fail to deserialize after a session store failover throwContextNotActiveExceptionon the first request that touches them, affecting all users whose sessions crossed the failover boundary - EL expression resolution failures: when the EL resolver cannot find a managed bean property (due to a refactoring that missed a
.xhtmltemplate), the affected form field renders blank with no exception thrown — the page appears to work while data is silently lost - Partial state saving failures: MyFaces's partial state saving (
javax.faces.PARTIAL_STATE_SAVING) can produce inconsistent component trees after a cluster node failover, causing subsequent requests to throwClassCastExceptionorIllegalStateExceptionduring view restoration - EJB
@Scheduletimer silent failures: EJB timer methods that fail during execution are rescheduled by the container without any HTTP-visible signal — data pipelines stop running while the application appears healthy
External monitoring with Vigilmon adds:
- Proactive alerting when JSF health endpoints detect phase listener or session store failures
- ViewState and session availability monitoring independent of the application server
- Heartbeat monitoring so you know when
@ScheduleEJB timers stop completing successfully - Multi-region probe consensus that filters transient blips from genuine JSF application failures
Step 1: Build a MyFaces Health Endpoint
MyFaces does not ship with a built-in health endpoint. Add one as a JAX-RS resource that bypasses the JSF lifecycle entirely, or via Spring Boot Actuator in hybrid setups.
JAX-RS Health Resource (bypasses JSF lifecycle)
// health/HealthResource.java
package com.yourapp.health;
import com.yourapp.service.DatabaseService;
import com.yourapp.service.CacheService;
import org.apache.myfaces.shared.config.MyfacesConfig;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.*;
@Path("/health")
public class HealthResource {
@Inject
private DatabaseService databaseService;
@Inject
private CacheService cacheService;
@Context
private ServletContext servletContext;
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response check() {
Map<String, String> checks = new LinkedHashMap<>();
boolean anyFailed = false;
checks.put("database", databaseService.isHealthy() ? "ok" : "down");
checks.put("cache", cacheService.isHealthy() ? "ok" : "down");
checks.put("myfaces", isMyfacesInitialized() ? "ok" : "down");
for (String v : checks.values()) {
if ("down".equals(v)) { anyFailed = true; break; }
}
Map<String, Object> response = new LinkedHashMap<>();
response.put("status", anyFailed ? "degraded" : "ok");
response.put("checks", checks);
return Response.status(anyFailed ? 503 : 200).entity(response).build();
}
private boolean isMyfacesInitialized() {
try {
MyfacesConfig config = MyfacesConfig.getCurrentInstance(servletContext);
return config != null;
} catch (Exception e) {
return false;
}
}
}
Register the JAX-RS application in web.xml or via annotation:
@ApplicationPath("/api")
public class RestApplication extends Application {}
Your health endpoint is now at /api/health.
Spring Boot Actuator Health Indicator (MyFaces + Spring Boot)
// MyfacesHealthIndicator.java
import org.apache.myfaces.shared.config.MyfacesConfig;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContext;
@Component
public class MyfacesHealthIndicator implements HealthIndicator {
private final ServletContext servletContext;
public MyfacesHealthIndicator(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public Health health() {
try {
MyfacesConfig config = MyfacesConfig.getCurrentInstance(servletContext);
if (config == null) {
return Health.down()
.withDetail("reason", "myfaces_config_null")
.build();
}
return Health.up()
.withDetail("partial_state_saving", config.isPartialStateSaving())
.withDetail("client_side_state", config.isClientSideStateCache())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("reason", "myfaces_not_initialized")
.withDetail("error", e.getMessage())
.build();
}
}
}
This exposes /actuator/health/myfaces automatically via Spring Boot Actuator.
Node.js Sidecar
// health/myfaces.js
const express = require('express');
const http = require('http');
const app = express();
const APP_HOST = process.env.APP_HOST || 'localhost';
const APP_PORT = parseInt(process.env.APP_PORT || '8080');
const HEALTH_PATH = process.env.MYFACES_HEALTH_PATH || '/api/health';
function probeMyFaces() {
return new Promise((resolve, reject) => {
const req = http.get(
{ host: APP_HOST, port: APP_PORT, path: HEALTH_PATH, 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/myfaces', async (req, res) => {
try {
const result = await probeMyFaces();
const up = result.status === 200 &&
(result.body.includes('"status":"ok"') || result.body.includes('"status":"UP"'));
if (!up) {
return res.status(503).json({
status: 'down',
upstream_status: result.status,
body: result.body.substring(0, 500),
});
}
return res.status(200).json({ status: 'ok' });
} catch (err) {
return res.status(503).json({ status: 'down', error: err.message });
}
});
app.listen(3015);
Python Health Sidecar
# health_myfaces.py
import os, requests
from flask import Flask, jsonify
app = Flask(__name__)
APP_BASE = f"http://{os.environ.get('APP_HOST', 'localhost')}:{os.environ.get('APP_PORT', '8080')}"
HEALTH_PATH = os.environ.get('MYFACES_HEALTH_PATH', '/api/health')
@app.route('/health/myfaces')
def health():
try:
resp = requests.get(f'{APP_BASE}{HEALTH_PATH}', timeout=5)
if resp.status_code != 200:
return jsonify({'status': 'down', 'upstream_status': resp.status_code}), 503
data = resp.json()
if data.get('status') not in ('ok', 'UP'):
return jsonify({'status': 'degraded', 'detail': data}), 503
return jsonify({'status': 'ok', 'checks': data.get('checks', {})})
except Exception as e:
return jsonify({'status': 'down', 'error': str(e)}), 503
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3015)
Step 2: Configure Vigilmon HTTP Monitor for MyFaces
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- Set the URL to your MyFaces health endpoint:
https://your-app.example.com/api/health - Set the check interval to 1 minute
- Under Expected response, configure:
- Status code:
200 - Response body contains:
"status":"ok"or"status":"UP" - Response time threshold:
3000ms
- Status code:
- Under Alert channels, assign your Slack or PagerDuty channel
- Save the monitor
Add a second monitor for the MyFaces initialization check:
- URL:
https://your-app.example.com/actuator/health/myfaces - Expected:
200, body contains:"status":"UP" - Interval: 2 minutes
- Alert channel: P1 pager — MyFaces initialization failure means all JSF pages are broken
Vigilmon's multi-region probe consensus filters transient network blips from genuine MyFaces application failures.
Step 3: Heartbeat Monitoring for EJB Timer Completion
Health endpoint monitoring catches JSF initialization and session store failures, but not background timer execution problems. A MyFaces application can report healthy while an EJB @Schedule timer has been silently failing every run due to a CDI scope boundary issue or a database transaction rollback.
Vigilmon heartbeat monitors catch these: your EJB timer pings Vigilmon after each successful execution.
Set Up the Heartbeat Monitor
- In Vigilmon, go to Monitors → New Monitor → Heartbeat
- Set the name:
myfaces-ejb-timer - Set the expected interval: 25 hours (for a daily job)
- Set the grace period: 2 hours
- Save — copy the heartbeat URL, e.g.
https://vigilmon.online/heartbeat/abc123xyz
Wire It Into an EJB @Schedule Timer
// timer/DailyReportTimer.java
package com.yourapp.timer;
import javax.ejb.*;
import java.net.http.*;
import java.net.URI;
import java.util.logging.Logger;
@Singleton
@Startup
public class DailyReportTimer {
private static final Logger LOG =
Logger.getLogger(DailyReportTimer.class.getName());
private final HttpClient http = HttpClient.newHttpClient();
private final String heartbeatUrl = System.getProperty("VIGILMON_HEARTBEAT_URL", "");
@Schedule(hour = "2", minute = "0", second = "0", persistent = false)
public void generateDailyReport() {
try {
runReport();
pingHeartbeat();
} catch (Exception e) {
LOG.severe("Daily report failed: " + e.getMessage());
// Heartbeat not pinged — Vigilmon alerts on the missed heartbeat
}
}
private void runReport() throws Exception {
// report generation logic
}
private void pingHeartbeat() {
if (heartbeatUrl.isEmpty()) return;
try {
http.sendAsync(
HttpRequest.newBuilder(URI.create(heartbeatUrl)).GET().build(),
HttpResponse.BodyHandlers.discarding()
);
} catch (Exception ignored) {}
}
}
Pass the heartbeat URL as a JVM system property to your application server:
-DVIGILMON_HEARTBEAT_URL=https://vigilmon.online/heartbeat/abc123xyz
Step 4: Alert Routing for MyFaces Failures
MyFaces failures range from a single managed bean scope failing to the entire JSF runtime being uninitialized after a deployment. Route alerts by severity:
| Monitor | Alert Channel | Priority |
|---|---|---|
| HTTP: /api/health JSF dependency check | Slack + PagerDuty | P1 |
| HTTP: /actuator/health/myfaces | Slack + PagerDuty | P1 |
| Heartbeat: EJB daily report timer | Slack + PagerDuty | P1 |
| Heartbeat: conversation cleanup job | Slack + email | P2 |
Set response time thresholds as early warning signals:
- Alert at
800msfor the MyFaces health endpoint (slow responses indicate session store or database latency) - Alert at
2000msfor the JSF initialization check (slow initialization precedes full page render failures) - Alert at
2 hourspast the expected daily timer heartbeat window
For production applications, set up a status page in Vigilmon showing JSF application health and EJB timer liveness — this lets on-call engineers identify MyFaces failures instantly rather than inferring them from user reports of broken forms or ViewExpiredException messages.
Summary
MyFaces failures present as UI rendering errors, not infrastructure failures — users see broken forms or blank fields while the real cause is a session store failure, a phase listener exception returning HTTP 200, or a silently failing EJB timer. External monitoring catches these before they become an incident:
| Monitor Type | What It Covers |
|---|---|
| HTTP monitor on /api/health | Database, cache, and MyFaces initialization |
| HTTP monitor on /actuator/health/myfaces | JSF config state, partial state saving |
| Heartbeat monitor (EJB timer) | Scheduled report and data pipeline execution |
| Heartbeat monitor (conversation cleanup) | Background CDI conversation management liveness |
Get started free at vigilmon.online — your first MyFaces application monitor is running in under two minutes.