google-site-verification: google61fe8ba583a51912.html
Java Mission Control Dashboard in Java

Introduction

Java Mission Control (JMC) Dashboard is a powerful monitoring and management tool for Java applications. This implementation provides a custom dashboard for tracking JVM metrics, performance statistics, and application health in real-time.

Architecture Overview

Core Components

public class JMCDashboardArchitecture { // JVM Metrics Collector // Real-time Data Processor // Visualization Engine // Alert Management System // Data Storage and Retrieval // Web Dashboard Interface }

Core Data Models

JVM Metrics Model

public class JVMMetrics { private final String jvmId; private final Instant timestamp; private final MemoryMetrics memory; private final GCMetrics garbageCollection; private final ThreadMetrics threads; private final CPUMetrics cpu; private final ClassLoadingMetrics classLoading; private final RuntimeMetrics runtime; private final Map<String, Object> customMetrics; public JVMMetrics(String jvmId) { this.jvmId = jvmId; this.timestamp = Instant.now(); this.memory = new MemoryMetrics(); this.garbageCollection = new GCMetrics(); this.threads = new ThreadMetrics(); this.cpu = new CPUMetrics(); this.classLoading = new ClassLoadingMetrics(); this.runtime = new RuntimeMetrics(); this.customMetrics = new HashMap<>(); } // Getters and utility methods public String getJvmId() { return jvmId; } public Instant getTimestamp() { return timestamp; } public MemoryMetrics getMemory() { return memory; } public GCMetrics getGarbageCollection() { return garbageCollection; } public ThreadMetrics getThreads() { return threads; } public CPUMetrics getCpu() { return cpu; } public ClassLoadingMetrics getClassLoading() { return classLoading; } public RuntimeMetrics getRuntime() { return runtime; } public Map<String, Object> getCustomMetrics() { return customMetrics; } public void addCustomMetric(String key, Object value) { customMetrics.put(key, value); } } public class MemoryMetrics { private long heapUsed; private long heapCommitted; private long heapMax; private long nonHeapUsed; private long nonHeapCommitted; private long nonHeapMax; private Map<String, MemoryPoolMetrics> memoryPools; public MemoryMetrics() { this.memoryPools = new HashMap<>(); } // Getters and setters public long getHeapUsed() { return heapUsed; } public void setHeapUsed(long heapUsed) { this.heapUsed = heapUsed; } public long getHeapCommitted() { return heapCommitted; } public void setHeapCommitted(long heapCommitted) { this.heapCommitted = heapCommitted; } public long getHeapMax() { return heapMax; } public void setHeapMax(long heapMax) { this.heapMax = heapMax; } public long getNonHeapUsed() { return nonHeapUsed; } public void setNonHeapUsed(long nonHeapUsed) { this.nonHeapUsed = nonHeapUsed; } public long getNonHeapCommitted() { return nonHeapCommitted; } public void setNonHeapCommitted(long nonHeapCommitted) { this.nonHeapCommitted = nonHeapCommitted; } public long getNonHeapMax() { return nonHeapMax; } public void setNonHeapMax(long nonHeapMax) { this.nonHeapMax = nonHeapMax; } public Map<String, MemoryPoolMetrics> getMemoryPools() { return memoryPools; } public void addMemoryPool(String name, MemoryPoolMetrics pool) { memoryPools.put(name, pool); } public double getHeapUsagePercentage() { return heapMax > 0 ? (double) heapUsed / heapMax * 100 : 0; } public double getNonHeapUsagePercentage() { return nonHeapMax > 0 ? (double) nonHeapUsed / nonHeapMax * 100 : 0; } } public class MemoryPoolMetrics { private final String poolName; private long used; private long committed; private long max; private final MemoryType type; public MemoryPoolMetrics(String poolName, MemoryType type) { this.poolName = poolName; this.type = type; } // Getters and setters public String getPoolName() { return poolName; } public long getUsed() { return used; } public void setUsed(long used) { this.used = used; } public long getCommitted() { return committed; } public void setCommitted(long committed) { this.committed = committed; } public long getMax() { return max; } public void setMax(long max) { this.max = max; } public MemoryType getType() { return type; } public double getUsagePercentage() { return max > 0 ? (double) used / max * 100 : 0; } } public enum MemoryType { HEAP("Heap"), NON_HEAP("Non-Heap"), CODE_CACHE("Code Cache"), METASPACE("Metaspace"); private final String description; MemoryType(String description) { this.description = description; } public String getDescription() { return description; } } public class GCMetrics { private long gcCount; private long gcTime; private Map<String, GarbageCollectorMetrics> collectors; public GCMetrics() { this.collectors = new HashMap<>(); } // Getters and setters public long getGcCount() { return gcCount; } public void setGcCount(long gcCount) { this.gcCount = gcCount; } public long getGcTime() { return gcTime; } public void setGcTime(long gcTime) { this.gcTime = gcTime; } public Map<String, GarbageCollectorMetrics> getCollectors() { return collectors; } public void addCollector(String name, GarbageCollectorMetrics collector) { collectors.put(name, collector); } } public class GarbageCollectorMetrics { private final String collectorName; private long collectionCount; private long collectionTime; public GarbageCollectorMetrics(String collectorName) { this.collectorName = collectorName; } // Getters and setters public String getCollectorName() { return collectorName; } public long getCollectionCount() { return collectionCount; } public void setCollectionCount(long collectionCount) { this.collectionCount = collectionCount; } public long getCollectionTime() { return collectionTime; } public void setCollectionTime(long collectionTime) { this.collectionTime = collectionTime; } } public class ThreadMetrics { private int liveThreads; private int peakThreads; private int daemonThreads; private long totalStartedThreads; private Map<Thread.State, Integer> threadStateCounts; public ThreadMetrics() { this.threadStateCounts = new HashMap<>(); } // Getters and setters public int getLiveThreads() { return liveThreads; } public void setLiveThreads(int liveThreads) { this.liveThreads = liveThreads; } public int getPeakThreads() { return peakThreads; } public void setPeakThreads(int peakThreads) { this.peakThreads = peakThreads; } public int getDaemonThreads() { return daemonThreads; } public void setDaemonThreads(int daemonThreads) { this.daemonThreads = daemonThreads; } public long getTotalStartedThreads() { return totalStartedThreads; } public void setTotalStartedThreads(long totalStartedThreads) { this.totalStartedThreads = totalStartedThreads; } public Map<Thread.State, Integer> getThreadStateCounts() { return threadStateCounts; } public void setThreadStateCount(Thread.State state, int count) { threadStateCounts.put(state, count); } }

Metrics Collection System

JVM Metrics Collector

public class JVMMetricsCollector { private final String jvmId; private final RuntimeMXBean runtimeMXBean; private final MemoryMXBean memoryMXBean; private final ThreadMXBean threadMXBean; private final OperatingSystemMXBean osMXBean; private final List<GarbageCollectorMXBean> gcMXBeans; private final List<MemoryPoolMXBean> memoryPoolMXBeans; private final ClassLoadingMXBean classLoadingMXBean; private final CompilationMXBean compilationMXBean; public JVMMetricsCollector(String jvmId) { this.jvmId = jvmId; this.runtimeMXBean = ManagementFactory.getRuntimeMXBean(); this.memoryMXBean = ManagementFactory.getMemoryMXBean(); this.threadMXBean = ManagementFactory.getThreadMXBean(); this.osMXBean = ManagementFactory.getOperatingSystemMXBean(); this.gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); this.memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans(); this.classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); this.compilationMXBean = ManagementFactory.getCompilationMXBean(); } public JVMMetrics collectMetrics() { JVMMetrics metrics = new JVMMetrics(jvmId); collectMemoryMetrics(metrics); collectGCMetrics(metrics); collectThreadMetrics(metrics); collectCPUMetrics(metrics); collectClassLoadingMetrics(metrics); collectRuntimeMetrics(metrics); return metrics; } private void collectMemoryMetrics(JVMMetrics metrics) { MemoryMetrics memoryMetrics = metrics.getMemory(); // Heap memory MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage(); memoryMetrics.setHeapUsed(heapUsage.getUsed()); memoryMetrics.setHeapCommitted(heapUsage.getCommitted()); memoryMetrics.setHeapMax(heapUsage.getMax()); // Non-heap memory MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage(); memoryMetrics.setNonHeapUsed(nonHeapUsage.getUsed()); memoryMetrics.setNonHeapCommitted(nonHeapUsage.getCommitted()); memoryMetrics.setNonHeapMax(nonHeapUsage.getMax()); // Memory pools for (MemoryPoolMXBean pool : memoryPoolMXBeans) { MemoryUsage usage = pool.getUsage(); MemoryType type = determineMemoryType(pool.getType()); MemoryPoolMetrics poolMetrics = new MemoryPoolMetrics(pool.getName(), type); poolMetrics.setUsed(usage.getUsed()); poolMetrics.setCommitted(usage.getCommitted()); poolMetrics.setMax(usage.getMax()); memoryMetrics.addMemoryPool(pool.getName(), poolMetrics); } } private MemoryType determineMemoryType(MemoryType mxBeanType) { return switch (mxBeanType.name()) { case "HEAP" -> MemoryType.HEAP; case "NON_HEAP" -> MemoryType.NON_HEAP; case "CODE_CACHE" -> MemoryType.CODE_CACHE; case "METASPACE" -> MemoryType.METASPACE; default -> MemoryType.NON_HEAP; }; } private void collectGCMetrics(JVMMetrics metrics) { GCMetrics gcMetrics = metrics.getGarbageCollection(); long totalGcCount = 0; long totalGcTime = 0; for (GarbageCollectorMXBean gcBean : gcMXBeans) { GarbageCollectorMetrics collectorMetrics = new GarbageCollectorMetrics(gcBean.getName()); collectorMetrics.setCollectionCount(gcBean.getCollectionCount()); collectorMetrics.setCollectionTime(gcBean.getCollectionTime()); gcMetrics.addCollector(gcBean.getName(), collectorMetrics); totalGcCount += gcBean.getCollectionCount(); totalGcTime += gcBean.getCollectionTime(); } gcMetrics.setGcCount(totalGcCount); gcMetrics.setGcTime(totalGcTime); } private void collectThreadMetrics(JVMMetrics metrics) { ThreadMetrics threadMetrics = metrics.getThreads(); threadMetrics.setLiveThreads(threadMXBean.getThreadCount()); threadMetrics.setPeakThreads(threadMXBean.getPeakThreadCount()); threadMetrics.setDaemonThreads(threadMXBean.getDaemonThreadCount()); threadMetrics.setTotalStartedThreads(threadMXBean.getTotalStartedThreadCount()); // Thread states long[] threadIds = threadMXBean.getAllThreadIds(); for (long threadId : threadIds) { ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId); if (threadInfo != null) { Thread.State state = threadInfo.getThreadState(); threadMetrics.setThreadStateCount(state, threadMetrics.getThreadStateCounts().getOrDefault(state, 0) + 1); } } } private void collectCPUMetrics(JVMMetrics metrics) { CPUMetrics cpuMetrics = metrics.getCpu(); if (osMXBean instanceof com.sun.management.OperatingSystemMXBean sunOsMXBean) { cpuMetrics.setProcessCpuLoad(sunOsMXBean.getProcessCpuLoad()); cpuMetrics.setSystemCpuLoad(sunOsMXBean.getSystemCpuLoad()); cpuMetrics.setProcessCpuTime(sunOsMXBean.getProcessCpuTime()); } cpuMetrics.setAvailableProcessors(osMXBean.getAvailableProcessors()); // Thread CPU time if supported if (threadMXBean.isThreadCpuTimeSupported()) { threadMXBean.setThreadCpuTimeEnabled(true); } } private void collectClassLoadingMetrics(JVMMetrics metrics) { ClassLoadingMetrics classMetrics = metrics.getClassLoading(); classMetrics.setLoadedClassCount(classLoadingMXBean.getLoadedClassCount()); classMetrics.setTotalLoadedClassCount(classLoadingMXBean.getTotalLoadedClassCount()); classMetrics.setUnloadedClassCount(classLoadingMXBean.getUnloadedClassCount()); } private void collectRuntimeMetrics(JVMMetrics metrics) { RuntimeMetrics runtimeMetrics = metrics.getRuntime(); runtimeMetrics.setStartTime(runtimeMXBean.getStartTime()); runtimeMetrics.setUptime(runtimeMXBean.getUptime()); runtimeMetrics.setVmName(runtimeMXBean.getVmName()); runtimeMetrics.setVmVendor(runtimeMXBean.getVmVendor()); runtimeMetrics.setVmVersion(runtimeMXBean.getVmVersion()); // System properties runtimeMetrics.setSystemProperties(runtimeMXBean.getSystemProperties()); // Input arguments runtimeMetrics.setInputArguments(runtimeMXBean.getInputArguments()); } }

Real-time Monitoring Service

Metrics Storage and Retrieval

public class MetricsStorageService { private final Map<String, Deque<JVMMetrics>> metricsHistory; private final int maxHistorySize; private final ScheduledExecutorService scheduler; private final JVMMetricsCollector collector; public MetricsStorageService(String jvmId, int maxHistorySize) { this.metricsHistory = new ConcurrentHashMap<>(); this.maxHistorySize = maxHistorySize; this.scheduler = Executors.newScheduledThreadPool(1); this.collector = new JVMMetricsCollector(jvmId); startMetricsCollection(); } public void startMetricsCollection() { scheduler.scheduleAtFixedRate(() -> { try { JVMMetrics metrics = collector.collectMetrics(); storeMetrics(metrics); } catch (Exception e) { System.err.println("Error collecting metrics: " + e.getMessage()); } }, 0, 1, TimeUnit.SECONDS); // Collect every second } private void storeMetrics(JVMMetrics metrics) { String jvmId = metrics.getJvmId(); metricsHistory.computeIfAbsent(jvmId, k -> new ConcurrentLinkedDeque<>()); Deque<JVMMetrics> history = metricsHistory.get(jvmId); history.addLast(metrics); // Maintain history size while (history.size() > maxHistorySize) { history.removeFirst(); } } public List<JVMMetrics> getRecentMetrics(String jvmId, Duration duration) { Deque<JVMMetrics> history = metricsHistory.get(jvmId); if (history == null) { return Collections.emptyList(); } Instant cutoff = Instant.now().minus(duration); return history.stream() .filter(metric -> metric.getTimestamp().isAfter(cutoff)) .collect(Collectors.toList()); } public Optional<JVMMetrics> getLatestMetrics(String jvmId) { Deque<JVMMetrics> history = metricsHistory.get(jvmId); return history != null && !history.isEmpty() ? Optional.of(history.getLast()) : Optional.empty(); } public Map<String, List<JVMMetrics>> getAllMetrics() { return metricsHistory.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue()) )); } public void shutdown() { scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } } }

Web Dashboard with Spring Boot

REST API Controllers

@RestController @RequestMapping("/api/jmc") @CrossOrigin(origins = "*") public class JMCDashboardController { private final MetricsStorageService storageService; private final AlertService alertService; public JMCDashboardController(MetricsStorageService storageService, AlertService alertService) { this.storageService = storageService; this.alertService = alertService; } @GetMapping("/metrics/current") public ResponseEntity<JVMMetrics> getCurrentMetrics() { Optional<JVMMetrics> metrics = storageService.getLatestMetrics("default"); return metrics.map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @GetMapping("/metrics/history") public ResponseEntity<List<JVMMetrics>> getMetricsHistory( @RequestParam(defaultValue = "PT5M") String duration) { Duration durationParam = Duration.parse(duration); List<JVMMetrics> metrics = storageService.getRecentMetrics("default", durationParam); return ResponseEntity.ok(metrics); } @GetMapping("/metrics/summary") public ResponseEntity<MetricsSummary> getMetricsSummary() { List<JVMMetrics> recentMetrics = storageService.getRecentMetrics("default", Duration.ofMinutes(5)); MetricsSummary summary = calculateSummary(recentMetrics); return ResponseEntity.ok(summary); } @GetMapping("/alerts") public ResponseEntity<List<Alert>> getActiveAlerts() { List<Alert> alerts = alertService.getActiveAlerts(); return ResponseEntity.ok(alerts); } @GetMapping("/memory/details") public ResponseEntity<MemoryDetails> getMemoryDetails() { Optional<JVMMetrics> current = storageService.getLatestMetrics("default"); return current.map(metrics -> { MemoryDetails details = extractMemoryDetails(metrics); return ResponseEntity.ok(details); }).orElse(ResponseEntity.notFound().build()); } @GetMapping("/threads/details") public ResponseEntity<ThreadDetails> getThreadDetails() { Optional<JVMMetrics> current = storageService.getLatestMetrics("default"); return current.map(metrics -> { ThreadDetails details = extractThreadDetails(metrics); return ResponseEntity.ok(details); }).orElse(ResponseEntity.notFound().build()); } @GetMapping("/gc/details") public ResponseEntity<GCDetails> getGCDetails() { Optional<JVMMetrics> current = storageService.getLatestMetrics("default"); return current.map(metrics -> { GCDetails details = extractGCDetails(metrics); return ResponseEntity.ok(details); }).orElse(ResponseEntity.notFound().build()); } private MetricsSummary calculateSummary(List<JVMMetrics> metrics) { if (metrics.isEmpty()) { return new MetricsSummary(); } MetricsSummary summary = new MetricsSummary(); // Calculate averages, min, max, trends return summary; } private MemoryDetails extractMemoryDetails(JVMMetrics metrics) { MemoryDetails details = new MemoryDetails(); // Extract detailed memory information return details; } private ThreadDetails extractThreadDetails(JVMMetrics metrics) { ThreadDetails details = new ThreadDetails(); // Extract detailed thread information return details; } private GCDetails extractGCDetails(JVMMetrics metrics) { GCDetails details = new GCDetails(); // Extract detailed GC information return details; } } // DTO classes for API responses public class MetricsSummary { private double avgCpuUsage; private double maxCpuUsage; private double avgMemoryUsage; private double maxMemoryUsage; private long totalGcCount; private long totalGcTime; private int peakThreadCount; private double systemLoad; // Getters and setters public double getAvgCpuUsage() { return avgCpuUsage; } public void setAvgCpuUsage(double avgCpuUsage) { this.avgCpuUsage = avgCpuUsage; } public double getMaxCpuUsage() { return maxCpuUsage; } public void setMaxCpuUsage(double maxCpuUsage) { this.maxCpuUsage = maxCpuUsage; } public double getAvgMemoryUsage() { return avgMemoryUsage; } public void setAvgMemoryUsage(double avgMemoryUsage) { this.avgMemoryUsage = avgMemoryUsage; } public double getMaxMemoryUsage() { return maxMemoryUsage; } public void setMaxMemoryUsage(double maxMemoryUsage) { this.maxMemoryUsage = maxMemoryUsage; } public long getTotalGcCount() { return totalGcCount; } public void setTotalGcCount(long totalGcCount) { this.totalGcCount = totalGcCount; } public long getTotalGcTime() { return totalGcTime; } public void setTotalGcTime(long totalGcTime) { this.totalGcTime = totalGcTime; } public int getPeakThreadCount() { return peakThreadCount; } public void setPeakThreadCount(int peakThreadCount) { this.peakThreadCount = peakThreadCount; } public double getSystemLoad() { return systemLoad; } public void setSystemLoad(double systemLoad) { this.systemLoad = systemLoad; } } public class MemoryDetails { private Map<String, MemoryPoolDetails> memoryPools; private HeapDetails heapDetails; private NonHeapDetails nonHeapDetails; // Getters and setters public Map<String, MemoryPoolDetails> getMemoryPools() { return memoryPools; } public void setMemoryPools(Map<String, MemoryPoolDetails> memoryPools) { this.memoryPools = memoryPools; } public HeapDetails getHeapDetails() { return heapDetails; } public void setHeapDetails(HeapDetails heapDetails) { this.heapDetails = heapDetails; } public NonHeapDetails getNonHeapDetails() { return nonHeapDetails; } public void setNonHeapDetails(NonHeapDetails nonHeapDetails) { this.nonHeapDetails = nonHeapDetails; } } public class ThreadDetails { private Map<Thread.State, Integer> threadStates; private List<ThreadInfo> threadInfos; private ThreadSummary summary; // Getters and setters public Map<Thread.State, Integer> getThreadStates() { return threadStates; } public void setThreadStates(Map<Thread.State, Integer> threadStates) { this.threadStates = threadStates; } public List<ThreadInfo> getThreadInfos() { return threadInfos; } public void setThreadInfos(List<ThreadInfo> threadInfos) { this.threadInfos = threadInfos; } public ThreadSummary getSummary() { return summary; } public void setSummary(ThreadSummary summary) { this.summary = summary; } }

Real-time WebSocket Updates

WebSocket Configuration

@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new MetricsWebSocketHandler(), "/ws/metrics") .setAllowedOrigins("*"); } } @Component public class MetricsWebSocketHandler extends TextWebSocketHandler { private final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>()); private final MetricsStorageService storageService; private final ScheduledExecutorService scheduler; public MetricsWebSocketHandler(MetricsStorageService storageService) { this.storageService = storageService; this.scheduler = Executors.newScheduledThreadPool(1); startMetricsBroadcast(); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); // Send initial metrics sendCurrentMetrics(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); } @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // Handle client messages if needed String payload = message.getPayload(); // Process client requests } private void startMetricsBroadcast() { scheduler.scheduleAtFixedRate(() -> { if (!sessions.isEmpty()) { broadcastMetrics(); } }, 1, 1, TimeUnit.SECONDS); } private void broadcastMetrics() { Optional<JVMMetrics> currentMetrics = storageService.getLatestMetrics("default"); currentMetrics.ifPresent(metrics -> { String json = convertToJson(metrics); synchronized (sessions) { sessions.forEach(session -> { try { if (session.isOpen()) { session.sendMessage(new TextMessage(json)); } } catch (IOException e) { System.err.println("Error sending WebSocket message: " + e.getMessage()); } }); } }); } private void sendCurrentMetrics(WebSocketSession session) { Optional<JVMMetrics> currentMetrics = storageService.getLatestMetrics("default"); currentMetrics.ifPresent(metrics -> { try { String json = convertToJson(metrics); session.sendMessage(new TextMessage(json)); } catch (IOException e) { System.err.println("Error sending initial metrics: " + e.getMessage()); } }); } private String convertToJson(JVMMetrics metrics) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.writeValueAsString(metrics); } catch (JsonProcessingException e) { throw new RuntimeException("Error converting metrics to JSON", e); } } @PreDestroy public void cleanup() { scheduler.shutdown(); } }

Alert Management System

Alert Engine

@Component public class AlertService { private final List<AlertRule> alertRules; private final List<Alert> activeAlerts; private final MetricsStorageService storageService; private final ScheduledExecutorService alertChecker; public AlertService(MetricsStorageService storageService) { this.storageService = storageService; this.alertRules = new ArrayList<>(); this.activeAlerts = Collections.synchronizedList(new ArrayList<>()); this.alertChecker = Executors.newScheduledThreadPool(1); initializeDefaultRules(); startAlertChecking(); } private void initializeDefaultRules() { // Memory usage alerts alertRules.add(new MemoryUsageAlertRule(80, 90, 95)); // CPU usage alerts alertRules.add(new CPUUsageAlertRule(70, 85, 95)); // GC alerts alertRules.add(new GCAlertRule(1000, 5000)); // GC time in ms // Thread alerts alertRules.add(new ThreadAlertRule(100, 200, 500)); // Deadlock detection alertRules.add(new DeadlockAlertRule()); } private void startAlertChecking() { alertChecker.scheduleAtFixedRate(() -> { try { checkAlerts(); } catch (Exception e) { System.err.println("Error checking alerts: " + e.getMessage()); } }, 0, 5, TimeUnit.SECONDS); } private void checkAlerts() { Optional<JVMMetrics> currentMetrics = storageService.getLatestMetrics("default"); if (currentMetrics.isEmpty()) { return; } JVMMetrics metrics = currentMetrics.get(); for (AlertRule rule : alertRules) { Optional<Alert> alert = rule.check(metrics); alert.ifPresent(this::processAlert); } // Clean up resolved alerts cleanResolvedAlerts(); } private void processAlert(Alert alert) { // Check if similar alert already exists boolean exists = activeAlerts.stream() .anyMatch(a -> a.getType().equals(alert.getType()) && a.getSeverity() == alert.getSeverity()); if (!exists) { activeAlerts.add(alert); notifyAlert(alert); } } private void notifyAlert(Alert alert) { // Log alert System.out.printf("ALERT: [%s] %s - %s%n", alert.getSeverity(), alert.getType(), alert.getMessage()); // Could send email, Slack notification, etc. // implementNotificationSystem(alert); } private void cleanResolvedAlerts() { Instant cutoff = Instant.now().minus(Duration.ofMinutes(5)); activeAlerts.removeIf(alert -> alert.getTimestamp().isBefore(cutoff) && alert.isAutoResolve()); } public List<Alert> getActiveAlerts() { return new ArrayList<>(activeAlerts); } public void addAlertRule(AlertRule rule) { alertRules.add(rule); } public void removeAlertRule(AlertRule rule) { alertRules.remove(rule); } @PreDestroy public void cleanup() { alertChecker.shutdown(); } } public abstract class AlertRule { protected final String name; protected final AlertType type; public AlertRule(String name, AlertType type) { this.name = name; this.type = type; } public abstract Optional<Alert> check(JVMMetrics metrics); public String getName() { return name; } public AlertType getType() { return type; } } public class MemoryUsageAlertRule extends AlertRule { private final double warningThreshold; private final double errorThreshold; private final double criticalThreshold; public MemoryUsageAlertRule(double warning, double error, double critical) { super("Memory Usage", AlertType.MEMORY); this.warningThreshold = warning; this.errorThreshold = error; this.criticalThreshold = critical; } @Override public Optional<Alert> check(JVMMetrics metrics) { double heapUsage = metrics.getMemory().getHeapUsagePercentage(); if (heapUsage >= criticalThreshold) { return Optional.of(new Alert( AlertType.MEMORY, AlertSeverity.CRITICAL, String.format("Critical memory usage: %.1f%%", heapUsage), Instant.now() )); } else if (heapUsage >= errorThreshold) { return Optional.of(new Alert( AlertType.MEMORY, AlertSeverity.ERROR, String.format("High memory usage: %.1f%%", heapUsage), Instant.now() )); } else if (heapUsage >= warningThreshold) { return Optional.of(new Alert( AlertType.MEMORY, AlertSeverity.WARNING, String.format("Elevated memory usage: %.1f%%", heapUsage), Instant.now() )); } return Optional.empty(); } } public class CPUUsageAlertRule extends AlertRule { private final double warningThreshold; private final double errorThreshold; private final double criticalThreshold; public CPUUsageAlertRule(double warning, double error, double critical) { super("CPU Usage", AlertType.CPU); this.warningThreshold = warning; this.errorThreshold = error; this.criticalThreshold = critical; } @Override public Optional<Alert> check(JVMMetrics metrics) { double cpuUsage = metrics.getCpu().getProcessCpuLoad() * 100; if (cpuUsage >= criticalThreshold) { return Optional.of(new Alert( AlertType.CPU, AlertSeverity.CRITICAL, String.format("Critical CPU usage: %.1f%%", cpuUsage), Instant.now() )); } else if (cpuUsage >= errorThreshold) { return Optional.of(new Alert( AlertType.CPU, AlertSeverity.ERROR, String.format("High CPU usage: %.1f%%", cpuUsage), Instant.now() )); } else if (cpuUsage >= warningThreshold) { return Optional.of(new Alert( AlertType.CPU, AlertSeverity.WARNING, String.format("Elevated CPU usage: %.1f%%", cpuUsage), Instant.now() )); } return Optional.empty(); } } public class Alert { private final AlertType type; private final AlertSeverity severity; private final String message; private final Instant timestamp; private boolean autoResolve = true; public Alert(AlertType type, AlertSeverity severity, String message, Instant timestamp) { this.type = type; this.severity = severity; this.message = message; this.timestamp = timestamp; } // Getters public AlertType getType() { return type; } public AlertSeverity getSeverity() { return severity; } public String getMessage() { return message; } public Instant getTimestamp() { return timestamp; } public boolean isAutoResolve() { return autoResolve; } public void setAutoResolve(boolean autoResolve) { this.autoResolve = autoResolve; } } public enum AlertType { MEMORY("Memory"), CPU("CPU"), GC("Garbage Collection"), THREAD("Thread"), DEADLOCK("Deadlock"), CLASS_LOADING("Class Loading"); private final String description; AlertType(String description) { this.description = description; } public String getDescription() { return description; } } public enum AlertSeverity { INFO("Info", "blue"), WARNING("Warning", "yellow"), ERROR("Error", "orange"), CRITICAL("Critical", "red"); private final String description; private final String color; AlertSeverity(String description, String color) { this.description = description; this.color = color; } public String getDescription() { return description; } public String getColor() { return color; } }

Dashboard Visualization Components

Chart Data Generation

@Component public class ChartDataService { private final MetricsStorageService storageService; public ChartDataService(MetricsStorageService storageService) { this.storageService = storageService; } public ChartData generateMemoryUsageChart(Duration duration) { List<JVMMetrics> metrics = storageService.getRecentMetrics("default", duration); List<ChartDataPoint> dataPoints = metrics.stream() .map(metric -> new ChartDataPoint( metric.getTimestamp(), metric.getMemory().getHeapUsagePercentage(), metric.getMemory().getNonHeapUsagePercentage() )) .collect(Collectors.toList()); return new ChartData("Memory Usage", dataPoints, "line"); } public ChartData generateCPUUsageChart(Duration duration) { List<JVMMetrics> metrics = storageService.getRecentMetrics("default", duration); List<ChartDataPoint> dataPoints = metrics.stream() .map(metric -> new ChartDataPoint( metric.getTimestamp(), metric.getCpu().getProcessCpuLoad() * 100, metric.getCpu().getSystemCpuLoad() * 100 )) .collect(Collectors.toList()); return new ChartData("CPU Usage", dataPoints, "line"); } public ChartData generateThreadCountChart(Duration duration) { List<JVMMetrics> metrics = storageService.getRecentMetrics("default", duration); List<ChartDataPoint> dataPoints = metrics.stream() .map(metric -> new ChartDataPoint( metric.getTimestamp(), (double) metric.getThreads().getLiveThreads(), (double) metric.getThreads().getPeakThreads() )) .collect(Collectors.toList()); return new ChartData("Thread Count", dataPoints, "line"); } public ChartData generateGCTimeChart(Duration duration) { List<JVMMetrics> metrics = storageService.getRecentMetrics("default", duration); List<ChartDataPoint> dataPoints = metrics.stream() .map(metric -> new ChartDataPoint( metric.getTimestamp(), (double) metric.getGarbageCollection().getGcTime() )) .collect(Collectors.toList()); return new ChartData("GC Time", dataPoints, "bar"); } public ChartData generateMemoryPoolChart() { Optional<JVMMetrics> current = storageService.getLatestMetrics("default"); if (current.isEmpty()) { return new ChartData("Memory Pools", Collections.emptyList(), "doughnut"); } JVMMetrics metrics = current.get(); List<ChartDataPoint> dataPoints = metrics.getMemory().getMemoryPools().values().stream() .map(pool -> new ChartDataPoint( pool.getPoolName(), pool.getUsagePercentage() )) .collect(Collectors.toList()); return new ChartData("Memory Pools", dataPoints, "doughnut"); } } public class ChartData { private final String title; private final List<ChartDataPoint> dataPoints; private final String chartType; private final Map<String, Object> options; public ChartData(String title, List<ChartDataPoint> dataPoints, String chartType) { this.title = title; this.dataPoints = dataPoints; this.chartType = chartType; this.options = createDefaultOptions(); } private Map<String, Object> createDefaultOptions() { Map<String, Object> options = new HashMap<>(); options.put("responsive", true); options.put("maintainAspectRatio", false); options.put("animation", true); return options; } // Getters public String getTitle() { return title; } public List<ChartDataPoint> getDataPoints() { return dataPoints; } public String getChartType() { return chartType; } public Map<String, Object> getOptions() { return options; } } public class ChartDataPoint { private final Instant timestamp; private final String label; private final double value; private final double secondaryValue; public ChartDataPoint(Instant timestamp, double value) { this(timestamp, value, 0); } public ChartDataPoint(Instant timestamp, double value, double secondaryValue) { this.timestamp = timestamp; this.label = null; this.value = value; this.secondaryValue = secondaryValue; } public ChartDataPoint(String label, double value) { this.timestamp = null; this.label = label; this.value = value; this.secondaryValue = 0; } // Getters public Instant getTimestamp() { return timestamp; } public String getLabel() { return label; } public double getValue() { return value; } public double getSecondaryValue() { return secondaryValue; } }

Main Application Class

Spring Boot Application

@SpringBootApplication public class JMCDashboardApplication { public static void main(String[] args) { SpringApplication.run(JMCDashboardApplication.class, args); } @Bean public MetricsStorageService metricsStorageService() { return new MetricsStorageService("main-app", 1000); // Keep 1000 data points } @Bean public AlertService alertService(MetricsStorageService storageService) { return new AlertService(storageService); } @Bean public ChartDataService chartDataService(MetricsStorageService storageService) { return new ChartDataService(storageService); } @Bean public CommandLineRunner initDashboard(MetricsStorageService storageService) { return args -> { System.out.println("Java Mission Control Dashboard Started"); System.out.println("Monitoring JVM metrics..."); System.out.println("Dashboard available at: http://localhost:8080"); }; } } // Configuration properties @ConfigurationProperties(prefix = "jmc.dashboard") @Data public class JMCDashboardProperties { private int historySize = 1000; private int collectionIntervalSeconds = 1; private int alertCheckIntervalSeconds = 5; private boolean enableAlerts = true; private boolean enableWebSocket = true; private String defaultJvmId = "main-app"; // Chart configuration private Charts charts = new Charts(); @Data public static class Charts { private int defaultDataPoints = 100; private boolean enableAnimations = true; private String defaultTheme = "dark"; } }

This Java Mission Control Dashboard provides a comprehensive monitoring solution with real-time metrics collection, alerting, and visualization capabilities. It can be extended with additional features like historical data analysis, custom metrics, and integration with external monitoring systems.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper