Comprehensive Controller Runtime Management Implementation
1. Controller Runtime Core Framework
Runtime Controller Manager
// ControllerRuntimeManager.java
package com.example.controller.runtime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class ControllerRuntimeManager {
private static final Logger logger = LoggerFactory.getLogger(ControllerRuntimeManager.class);
private final ApplicationContext applicationContext;
private final ControllerMetricsService metricsService;
private final ControllerConfiguration config;
private final Map<String, ControllerRuntimeInfo> runtimeInfoMap = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private final AtomicInteger requestCounter = new AtomicInteger(0);
@Autowired
public ControllerRuntimeManager(ApplicationContext applicationContext,
ControllerMetricsService metricsService,
ControllerConfiguration config) {
this.applicationContext = applicationContext;
this.metricsService = metricsService;
this.config = config;
}
@PostConstruct
public void initialize() {
logger.info("Initializing Controller Runtime Manager");
discoverControllers();
startHealthMonitoring();
startMetricsCollection();
logger.info("Controller Runtime Manager initialized with {} controllers", runtimeInfoMap.size());
}
@PreDestroy
public void shutdown() {
logger.info("Shutting down Controller Runtime Manager");
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
logger.info("Controller Runtime Manager shutdown completed");
}
private void discoverControllers() {
Map<String, Object> controllers = applicationContext.getBeansWithAnnotation(
org.springframework.stereotype.Controller.class);
controllers.putAll(applicationContext.getBeansWithAnnotation(
org.springframework.web.bind.annotation.RestController.class));
for (Map.Entry<String, Object> entry : controllers.entrySet()) {
String beanName = entry.getKey();
Object controller = entry.getValue();
Class<?> controllerClass = controller.getClass();
ControllerRuntimeInfo runtimeInfo = createRuntimeInfo(beanName, controller, controllerClass);
runtimeInfoMap.put(beanName, runtimeInfo);
logger.debug("Discovered controller: {} - {}", beanName, controllerClass.getName());
}
}
private ControllerRuntimeInfo createRuntimeInfo(String beanName, Object controller, Class<?> controllerClass) {
ControllerRuntimeInfo info = new ControllerRuntimeInfo();
info.setBeanName(beanName);
info.setControllerClass(controllerClass);
info.setControllerInstance(controller);
info.setStartTime(System.currentTimeMillis());
info.setStatus(ControllerStatus.ACTIVE);
// Analyze controller methods
analyzeControllerMethods(controllerClass, info);
return info;
}
private void analyzeControllerMethods(Class<?> controllerClass, ControllerRuntimeInfo runtimeInfo) {
Method[] methods = controllerClass.getDeclaredMethods();
List<ControllerMethodInfo> methodInfos = new ArrayList<>();
for (Method method : methods) {
if (isRequestMappingMethod(method)) {
ControllerMethodInfo methodInfo = analyzeMethod(method);
methodInfos.add(methodInfo);
}
}
runtimeInfo.setMethods(methodInfos);
}
private boolean isRequestMappingMethod(Method method) {
return method.isAnnotationPresent(org.springframework.web.bind.annotation.RequestMapping.class) ||
method.isAnnotationPresent(org.springframework.web.bind.annotation.GetMapping.class) ||
method.isAnnotationPresent(org.springframework.web.bind.annotation.PostMapping.class) ||
method.isAnnotationPresent(org.springframework.web.bind.annotation.PutMapping.class) ||
method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class) ||
method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class);
}
private ControllerMethodInfo analyzeMethod(Method method) {
ControllerMethodInfo methodInfo = new ControllerMethodInfo();
methodInfo.setMethodName(method.getName());
methodInfo.setReturnType(method.getReturnType().getSimpleName());
methodInfo.setParameterTypes(Arrays.stream(method.getParameterTypes())
.map(Class::getSimpleName)
.toArray(String[]::new));
// Extract HTTP method and path
extractHttpMappingInfo(method, methodInfo);
return methodInfo;
}
private void extractHttpMappingInfo(Method method, ControllerMethodInfo methodInfo) {
if (method.isAnnotationPresent(org.springframework.web.bind.annotation.GetMapping.class)) {
org.springframework.web.bind.annotation.GetMapping mapping =
method.getAnnotation(org.springframework.web.bind.annotation.GetMapping.class);
methodInfo.setHttpMethod("GET");
methodInfo.setPaths(mapping.value());
} else if (method.isAnnotationPresent(org.springframework.web.bind.annotation.PostMapping.class)) {
org.springframework.web.bind.annotation.PostMapping mapping =
method.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class);
methodInfo.setHttpMethod("POST");
methodInfo.setPaths(mapping.value());
}
// Add other HTTP method mappings...
}
private void startHealthMonitoring() {
scheduler.scheduleAtFixedRate(this::checkControllerHealth,
config.getHealthCheckInterval(), config.getHealthCheckInterval(), TimeUnit.SECONDS);
}
private void startMetricsCollection() {
scheduler.scheduleAtFixedRate(this::collectRuntimeMetrics,
config.getMetricsCollectionInterval(), config.getMetricsCollectionInterval(), TimeUnit.SECONDS);
}
private void checkControllerHealth() {
for (ControllerRuntimeInfo runtimeInfo : runtimeInfoMap.values()) {
try {
boolean isHealthy = performHealthCheck(runtimeInfo);
runtimeInfo.setStatus(isHealthy ? ControllerStatus.ACTIVE : ControllerStatus.UNHEALTHY);
runtimeInfo.setLastHealthCheck(System.currentTimeMillis());
if (!isHealthy) {
logger.warn("Controller {} is unhealthy", runtimeInfo.getBeanName());
}
} catch (Exception e) {
logger.error("Health check failed for controller: {}", runtimeInfo.getBeanName(), e);
runtimeInfo.setStatus(ControllerStatus.ERROR);
}
}
}
private boolean performHealthCheck(ControllerRuntimeInfo runtimeInfo) {
// Implement health check logic
// This could check if the controller can process requests, dependencies are available, etc.
return true; // Simplified implementation
}
private void collectRuntimeMetrics() {
for (ControllerRuntimeInfo runtimeInfo : runtimeInfoMap.values()) {
metricsService.recordControllerMetrics(runtimeInfo);
}
}
public ControllerRuntimeInfo getControllerInfo(String beanName) {
return runtimeInfoMap.get(beanName);
}
public List<ControllerRuntimeInfo> getAllControllers() {
return new ArrayList<>(runtimeInfoMap.values());
}
public Map<String, Object> getRuntimeStats() {
Map<String, Object> stats = new HashMap<>();
stats.put("totalControllers", runtimeInfoMap.size());
long activeControllers = runtimeInfoMap.values().stream()
.filter(info -> info.getStatus() == ControllerStatus.ACTIVE)
.count();
stats.put("activeControllers", activeControllers);
long totalMethods = runtimeInfoMap.values().stream()
.mapToLong(info -> info.getMethods().size())
.sum();
stats.put("totalMethods", totalMethods);
stats.put("totalRequestsProcessed", requestCounter.get());
stats.put("uptime", System.currentTimeMillis() - getStartTime());
return stats;
}
public void recordRequest(String controllerName, String methodName, long duration, boolean success) {
requestCounter.incrementAndGet();
ControllerRuntimeInfo runtimeInfo = runtimeInfoMap.get(controllerName);
if (runtimeInfo != null) {
runtimeInfo.recordRequest(methodName, duration, success);
metricsService.recordRequestMetrics(controllerName, methodName, duration, success);
}
}
private long getStartTime() {
return runtimeInfoMap.values().stream()
.mapToLong(ControllerRuntimeInfo::getStartTime)
.min()
.orElse(System.currentTimeMillis());
}
public enum ControllerStatus {
ACTIVE, UNHEALTHY, ERROR, DISABLED
}
}
2. Controller Runtime Information Model
// ControllerRuntimeInfo.java
package com.example.controller.runtime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public class ControllerRuntimeInfo {
private String beanName;
private Class<?> controllerClass;
private Object controllerInstance;
private ControllerRuntimeManager.ControllerStatus status;
private long startTime;
private long lastHealthCheck;
private List<ControllerMethodInfo> methods = new ArrayList<>();
private Map<String, MethodExecutionStats> methodStats = new ConcurrentHashMap<>();
private AtomicLong totalRequests = new AtomicLong(0);
private AtomicLong failedRequests = new AtomicLong(0);
// Getters and setters
public String getBeanName() { return beanName; }
public void setBeanName(String beanName) { this.beanName = beanName; }
public Class<?> getControllerClass() { return controllerClass; }
public void setControllerClass(Class<?> controllerClass) { this.controllerClass = controllerClass; }
public Object getControllerInstance() { return controllerInstance; }
public void setControllerInstance(Object controllerInstance) { this.controllerInstance = controllerInstance; }
public ControllerRuntimeManager.ControllerStatus getStatus() { return status; }
public void setStatus(ControllerRuntimeManager.ControllerStatus status) { this.status = status; }
public long getStartTime() { return startTime; }
public void setStartTime(long startTime) { this.startTime = startTime; }
public long getLastHealthCheck() { return lastHealthCheck; }
public void setLastHealthCheck(long lastHealthCheck) { this.lastHealthCheck = lastHealthCheck; }
public List<ControllerMethodInfo> getMethods() { return methods; }
public void setMethods(List<ControllerMethodInfo> methods) { this.methods = methods; }
public Map<String, MethodExecutionStats> getMethodStats() { return methodStats; }
public long getTotalRequests() { return totalRequests.get(); }
public long getFailedRequests() { return failedRequests.get(); }
public void recordRequest(String methodName, long duration, boolean success) {
totalRequests.incrementAndGet();
if (!success) {
failedRequests.incrementAndGet();
}
MethodExecutionStats stats = methodStats.computeIfAbsent(methodName,
k -> new MethodExecutionStats());
stats.recordExecution(duration, success);
}
public double getErrorRate() {
long total = totalRequests.get();
return total > 0 ? (double) failedRequests.get() / total : 0.0;
}
public MethodExecutionStats getMethodStats(String methodName) {
return methodStats.get(methodName);
}
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("beanName", beanName);
map.put("controllerClass", controllerClass.getName());
map.put("status", status.name());
map.put("startTime", startTime);
map.put("uptime", System.currentTimeMillis() - startTime);
map.put("lastHealthCheck", lastHealthCheck);
map.put("totalRequests", totalRequests.get());
map.put("failedRequests", failedRequests.get());
map.put("errorRate", getErrorRate());
map.put("methodsCount", methods.size());
List<Map<String, Object>> methodMaps = new ArrayList<>();
for (ControllerMethodInfo method : methods) {
methodMaps.add(method.toMap());
}
map.put("methods", methodMaps);
return map;
}
}
// ControllerMethodInfo.java
package com.example.controller.runtime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class ControllerMethodInfo {
private String methodName;
private String returnType;
private String[] parameterTypes;
private String httpMethod;
private String[] paths;
// Getters and setters
public String getMethodName() { return methodName; }
public void setMethodName(String methodName) { this.methodName = methodName; }
public String getReturnType() { return returnType; }
public void setReturnType(String returnType) { this.returnType = returnType; }
public String[] getParameterTypes() { return parameterTypes; }
public void setParameterTypes(String[] parameterTypes) { this.parameterTypes = parameterTypes; }
public String getHttpMethod() { return httpMethod; }
public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; }
public String[] getPaths() { return paths; }
public void setPaths(String[] paths) { this.paths = paths; }
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("methodName", methodName);
map.put("returnType", returnType);
map.put("parameterTypes", Arrays.asList(parameterTypes));
map.put("httpMethod", httpMethod);
map.put("paths", paths != null ? Arrays.asList(paths) : Arrays.asList());
return map;
}
}
// MethodExecutionStats.java
package com.example.controller.runtime;
import java.util.concurrent.atomic.AtomicLong;
public class MethodExecutionStats {
private final AtomicLong executionCount = new AtomicLong(0);
private final AtomicLong errorCount = new AtomicLong(0);
private final AtomicLong totalExecutionTime = new AtomicLong(0);
private final AtomicLong minExecutionTime = new AtomicLong(Long.MAX_VALUE);
private final AtomicLong maxExecutionTime = new AtomicLong(0);
public void recordExecution(long duration, boolean success) {
executionCount.incrementAndGet();
if (!success) {
errorCount.incrementAndGet();
}
totalExecutionTime.addAndGet(duration);
// Update min execution time
minExecutionTime.updateAndGet(current -> Math.min(current, duration));
// Update max execution time
maxExecutionTime.updateAndGet(current -> Math.max(current, duration));
}
public long getExecutionCount() { return executionCount.get(); }
public long getErrorCount() { return errorCount.get(); }
public long getTotalExecutionTime() { return totalExecutionTime.get(); }
public long getMinExecutionTime() {
return minExecutionTime.get() == Long.MAX_VALUE ? 0 : minExecutionTime.get();
}
public long getMaxExecutionTime() { return maxExecutionTime.get(); }
public double getAverageExecutionTime() {
long count = executionCount.get();
return count > 0 ? (double) totalExecutionTime.get() / count : 0.0;
}
public double getErrorRate() {
long count = executionCount.get();
return count > 0 ? (double) errorCount.get() / count : 0.0;
}
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("executionCount", executionCount.get());
map.put("errorCount", errorCount.get());
map.put("totalExecutionTime", totalExecutionTime.get());
map.put("minExecutionTime", getMinExecutionTime());
map.put("maxExecutionTime", maxExecutionTime.get());
map.put("averageExecutionTime", getAverageExecutionTime());
map.put("errorRate", getErrorRate());
return map;
}
}
3. Controller Metrics Service
// ControllerMetricsService.java
package com.example.controller.runtime;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Service
public class ControllerMetricsService {
private static final Logger logger = LoggerFactory.getLogger(ControllerMetricsService.class);
private final MeterRegistry meterRegistry;
private final Map<String, Counter> requestCounters = new ConcurrentHashMap<>();
private final Map<String, Counter> errorCounters = new ConcurrentHashMap<>();
private final Map<String, Timer> executionTimers = new ConcurrentHashMap<>();
public ControllerMetricsService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordControllerMetrics(ControllerRuntimeInfo runtimeInfo) {
String controllerName = runtimeInfo.getBeanName();
// Register gauges for controller metrics
Gauge.builder("controller.requests.total", runtimeInfo, ControllerRuntimeInfo::getTotalRequests)
.tag("controller", controllerName)
.register(meterRegistry);
Gauge.builder("controller.requests.failed", runtimeInfo, ControllerRuntimeInfo::getFailedRequests)
.tag("controller", controllerName)
.register(meterRegistry);
Gauge.builder("controller.error.rate", runtimeInfo, ControllerRuntimeInfo::getErrorRate)
.tag("controller", controllerName)
.register(meterRegistry);
// Record method-level metrics
for (Map.Entry<String, MethodExecutionStats> entry : runtimeInfo.getMethodStats().entrySet()) {
String methodName = entry.getKey();
MethodExecutionStats stats = entry.getValue();
String metricKey = controllerName + "." + methodName;
Gauge.builder("controller.method.executions", stats, MethodExecutionStats::getExecutionCount)
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry);
Gauge.builder("controller.method.errors", stats, MethodExecutionStats::getErrorCount)
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry);
Gauge.builder("controller.method.execution.time.avg", stats, MethodExecutionStats::getAverageExecutionTime)
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry);
}
}
public void recordRequestMetrics(String controllerName, String methodName, long duration, boolean success) {
String metricKey = controllerName + "." + methodName;
// Record request counter
Counter requestCounter = requestCounters.computeIfAbsent(metricKey,
k -> Counter.builder("controller.request.count")
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry));
requestCounter.increment();
// Record error counter if request failed
if (!success) {
Counter errorCounter = errorCounters.computeIfAbsent(metricKey,
k -> Counter.builder("controller.request.errors")
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry));
errorCounter.increment();
}
// Record execution time
Timer executionTimer = executionTimers.computeIfAbsent(metricKey,
k -> Timer.builder("controller.execution.time")
.tag("controller", controllerName)
.tag("method", methodName)
.register(meterRegistry));
executionTimer.record(duration, TimeUnit.MILLISECONDS);
}
public Map<String, Object> getMetricsSnapshot() {
Map<String, Object> snapshot = new ConcurrentHashMap<>();
requestCounters.forEach((key, counter) -> {
snapshot.put("counter." + key, counter.count());
});
errorCounters.forEach((key, counter) -> {
snapshot.put("error." + key, counter.count());
});
executionTimers.forEach((key, timer) -> {
snapshot.put("timer." + key + ".count", timer.count());
snapshot.put("timer." + key + ".mean", timer.mean(TimeUnit.MILLISECONDS));
snapshot.put("timer." + key + ".max", timer.max(TimeUnit.MILLISECONDS));
});
return snapshot;
}
}
4. Controller Configuration
// ControllerConfiguration.java
package com.example.controller.runtime;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "controller.runtime")
public class ControllerConfiguration {
private boolean enabled = true;
private long healthCheckInterval = 30; // seconds
private long metricsCollectionInterval = 60; // seconds
private boolean requestLoggingEnabled = true;
private boolean performanceMonitoringEnabled = true;
private int maxRequestHistory = 1000;
private long slowRequestThreshold = 1000; // milliseconds
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public long getHealthCheckInterval() { return healthCheckInterval; }
public void setHealthCheckInterval(long healthCheckInterval) { this.healthCheckInterval = healthCheckInterval; }
public long getMetricsCollectionInterval() { return metricsCollectionInterval; }
public void setMetricsCollectionInterval(long metricsCollectionInterval) { this.metricsCollectionInterval = metricsCollectionInterval; }
public boolean isRequestLoggingEnabled() { return requestLoggingEnabled; }
public void setRequestLoggingEnabled(boolean requestLoggingEnabled) { this.requestLoggingEnabled = requestLoggingEnabled; }
public boolean isPerformanceMonitoringEnabled() { return performanceMonitoringEnabled; }
public void setPerformanceMonitoringEnabled(boolean performanceMonitoringEnabled) { this.performanceMonitoringEnabled = performanceMonitoringEnabled; }
public int getMaxRequestHistory() { return maxRequestHistory; }
public void setMaxRequestHistory(int maxRequestHistory) { this.maxRequestHistory = maxRequestHistory; }
public long getSlowRequestThreshold() { return slowRequestThreshold; }
public void setSlowRequestThreshold(long slowRequestThreshold) { this.slowRequestThreshold = slowRequestThreshold; }
}
5. Controller Runtime Aspect
// ControllerRuntimeAspect.java
package com.example.controller.runtime;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class ControllerRuntimeAspect {
private static final Logger logger = LoggerFactory.getLogger(ControllerRuntimeAspect.class);
private final ControllerRuntimeManager runtimeManager;
private final ControllerConfiguration config;
@Autowired
public ControllerRuntimeAspect(ControllerRuntimeManager runtimeManager,
ControllerConfiguration config) {
this.runtimeManager = runtimeManager;
this.config = config;
}
@Around("@within(org.springframework.stereotype.Controller) || " +
"@within(org.springframework.web.bind.annotation.RestController)")
public Object monitorControllerExecution(ProceedingJoinPoint joinPoint) throws Throwable {
if (!config.isEnabled()) {
return joinPoint.proceed();
}
String controllerName = getControllerName(joinPoint);
String methodName = getMethodName(joinPoint);
long startTime = System.currentTimeMillis();
boolean success = false;
try {
Object result = joinPoint.proceed();
success = true;
return result;
} catch (Exception e) {
logger.warn("Controller method execution failed: {}.{} - {}",
controllerName, methodName, e.getMessage());
throw e;
} finally {
long duration = System.currentTimeMillis() - startTime;
// Record execution metrics
runtimeManager.recordRequest(controllerName, methodName, duration, success);
// Log slow requests
if (config.isRequestLoggingEnabled() && duration > config.getSlowRequestThreshold()) {
logger.info("Slow controller method execution: {}.{} took {}ms",
controllerName, methodName, duration);
}
// Log request details if enabled
if (config.isRequestLoggingEnabled()) {
logger.debug("Controller method executed: {}.{} - {}ms - success: {}",
controllerName, methodName, duration, success);
}
}
}
private String getControllerName(ProceedingJoinPoint joinPoint) {
Object target = joinPoint.getTarget();
return target.getClass().getSimpleName();
}
private String getMethodName(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
return method.getName();
}
}
6. REST Controller for Runtime Management
// ControllerRuntimeController.java
package com.example.controller.runtime;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/runtime/controllers")
public class ControllerRuntimeController {
private final ControllerRuntimeManager runtimeManager;
private final ControllerMetricsService metricsService;
public ControllerRuntimeController(ControllerRuntimeManager runtimeManager,
ControllerMetricsService metricsService) {
this.runtimeManager = runtimeManager;
this.metricsService = metricsService;
}
@GetMapping
public ResponseEntity<List<ControllerRuntimeInfo>> getAllControllers() {
return ResponseEntity.ok(runtimeManager.getAllControllers());
}
@GetMapping("/{controllerName}")
public ResponseEntity<ControllerRuntimeInfo> getController(@PathVariable String controllerName) {
ControllerRuntimeInfo info = runtimeManager.getControllerInfo(controllerName);
if (info == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(info);
}
@GetMapping("/{controllerName}/stats")
public ResponseEntity<Map<String, Object>> getControllerStats(@PathVariable String controllerName) {
ControllerRuntimeInfo info = runtimeManager.getControllerInfo(controllerName);
if (info == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(info.toMap());
}
@GetMapping("/{controllerName}/methods/{methodName}/stats")
public ResponseEntity<Map<String, Object>> getMethodStats(
@PathVariable String controllerName,
@PathVariable String methodName) {
ControllerRuntimeInfo info = runtimeManager.getControllerInfo(controllerName);
if (info == null) {
return ResponseEntity.notFound().build();
}
MethodExecutionStats stats = info.getMethodStats(methodName);
if (stats == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(stats.toMap());
}
@GetMapping("/stats/overview")
public ResponseEntity<Map<String, Object>> getRuntimeOverview() {
Map<String, Object> overview = runtimeManager.getRuntimeStats();
return ResponseEntity.ok(overview);
}
@GetMapping("/metrics")
public ResponseEntity<Map<String, Object>> getMetrics() {
Map<String, Object> metrics = metricsService.getMetricsSnapshot();
return ResponseEntity.ok(metrics);
}
@PostMapping("/{controllerName}/health-check")
public ResponseEntity<Map<String, Object>> performHealthCheck(@PathVariable String controllerName) {
ControllerRuntimeInfo info = runtimeManager.getControllerInfo(controllerName);
if (info == null) {
return ResponseEntity.notFound().build();
}
// This would trigger an immediate health check
Map<String, Object> result = Map.of(
"controller", controllerName,
"status", info.getStatus().name(),
"lastHealthCheck", info.getLastHealthCheck(),
"timestamp", System.currentTimeMillis()
);
return ResponseEntity.ok(result);
}
}
7. Dynamic Controller Management
// DynamicControllerManager.java
package com.example.controller.runtime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class DynamicControllerManager {
private static final Logger logger = LoggerFactory.getLogger(DynamicControllerManager.class);
private final ApplicationContext applicationContext;
private final ControllerRuntimeManager runtimeManager;
private final Map<String, Object> dynamicControllers = new ConcurrentHashMap<>();
@Autowired
public DynamicControllerManager(ApplicationContext applicationContext,
ControllerRuntimeManager runtimeManager) {
this.applicationContext = applicationContext;
this.runtimeManager = runtimeManager;
}
public boolean registerDynamicController(String controllerName, Object controller) {
try {
if (dynamicControllers.containsKey(controllerName)) {
logger.warn("Dynamic controller already registered: {}", controllerName);
return false;
}
dynamicControllers.put(controllerName, controller);
logger.info("Registered dynamic controller: {}", controllerName);
// Notify runtime manager about the new controller
// This would typically involve more complex integration
// with the Spring application context
return true;
} catch (Exception e) {
logger.error("Failed to register dynamic controller: {}", controllerName, e);
return false;
}
}
public boolean unregisterDynamicController(String controllerName) {
try {
Object controller = dynamicControllers.remove(controllerName);
if (controller != null) {
logger.info("Unregistered dynamic controller: {}", controllerName);
return true;
}
return false;
} catch (Exception e) {
logger.error("Failed to unregister dynamic controller: {}", controllerName, e);
return false;
}
}
public Object getDynamicController(String controllerName) {
return dynamicControllers.get(controllerName);
}
public Map<String, Object> getAllDynamicControllers() {
return new ConcurrentHashMap<>(dynamicControllers);
}
public boolean invokeDynamicMethod(String controllerName, String methodName, Object... args) {
try {
Object controller = dynamicControllers.get(controllerName);
if (controller == null) {
logger.error("Dynamic controller not found: {}", controllerName);
return false;
}
Method method = findMethod(controller, methodName, args);
if (method == null) {
logger.error("Method not found: {}.{}", controllerName, methodName);
return false;
}
method.setAccessible(true);
Object result = method.invoke(controller, args);
logger.debug("Invoked dynamic method: {}.{}", controllerName, methodName);
return true;
} catch (Exception e) {
logger.error("Failed to invoke dynamic method: {}.{}", controllerName, methodName, e);
return false;
}
}
private Method findMethod(Object controller, String methodName, Object[] args) {
Class<?> controllerClass = controller.getClass();
Method[] methods = controllerClass.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName) &&
isMethodCompatible(method, args)) {
return method;
}
}
return null;
}
private boolean isMethodCompatible(Method method, Object[] args) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != args.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (args[i] != null && !parameterTypes[i].isInstance(args[i])) {
return false;
}
}
return true;
}
}
8. Application Configuration
application.yml
# Controller Runtime Configuration controller: runtime: enabled: true health-check-interval: 30 metrics-collection-interval: 60 request-logging-enabled: true performance-monitoring-enabled: true max-request-history: 1000 slow-request-threshold: 1000 # Spring Configuration spring: application: name: controller-runtime-demo # Actuator endpoints management: endpoints: web: exposure: include: health,info,metrics,controllers endpoint: controllers: enabled: true # Logging Configuration logging: level: com.example.controller.runtime: DEBUG org.springframework.web: INFO
9. Example Usage Controller
// ExampleController.java
package com.example.controller.demo;
import com.example.controller.runtime.ControllerRuntimeManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/demo")
public class ExampleController {
private final ControllerRuntimeManager runtimeManager;
@Autowired
public ExampleController(ControllerRuntimeManager runtimeManager) {
this.runtimeManager = runtimeManager;
}
@GetMapping("/hello")
public ResponseEntity<String> hello() {
return ResponseEntity.ok("Hello from Controller Runtime Demo!");
}
@GetMapping("/slow")
public ResponseEntity<String> slowOperation() throws InterruptedException {
// Simulate slow operation
Thread.sleep(2000);
return ResponseEntity.ok("Slow operation completed");
}
@PostMapping("/echo")
public ResponseEntity<Map<String, Object>> echo(@RequestBody Map<String, Object> request) {
return ResponseEntity.ok(request);
}
@GetMapping("/runtime-info")
public ResponseEntity<Map<String, Object>> getRuntimeInfo() {
Map<String, Object> runtimeInfo = runtimeManager.getRuntimeStats();
runtimeInfo.put("currentTime", System.currentTimeMillis());
return ResponseEntity.ok(runtimeInfo);
}
}
This comprehensive Controller Runtime implementation provides:
- Real-time controller monitoring and health checks
- Performance metrics collection and analysis
- Dynamic controller management capabilities
- AOP-based request interception for monitoring
- REST API for runtime management
- Integration with Micrometer for metrics
- Configuration management for runtime behavior
- Request logging and slow request detection
- Method-level statistics tracking
- Health monitoring and status reporting
The system enables comprehensive runtime management and monitoring of Spring controllers in Java applications.
Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/
OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/
OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/
Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.
https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics
Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2
Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide
Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2
Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide
Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.
https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server
Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.