From 79bd80717e8dc6b691cd70d69aa99c235cda4d85 Mon Sep 17 00:00:00 2001 From: Shay Keren Date: Fri, 30 May 2025 05:43:24 +0300 Subject: [PATCH 1/2] feat: improve state management and error handling with retries --- .../petclinic/errors/MonitorService.java | 152 +++++++++++------- 1 file changed, 98 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java b/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java index af0ad68a219..c015cb5eec8 100644 --- a/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java +++ b/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java @@ -7,71 +7,115 @@ import org.springframework.context.SmartLifecycle; import org.springframework.stereotype.Component; -import java.util.InvalidPropertiesFormatException; - @Component public class MonitorService implements SmartLifecycle { - private boolean running = false; - private Thread backgroundThread; - @Autowired - private OpenTelemetry openTelemetry; + public enum ServiceState { + STOPPED, + STARTING, + RUNNING, + STOPPING, + ERROR + } - @Override - public void start() { - var otelTracer = openTelemetry.getTracer("MonitorService"); + private ServiceState currentState = ServiceState.STOPPED; + private Thread backgroundThread; + private static final int MAX_RETRIES = 3; + private static final long RETRY_DELAY_MS = 1000; - running = true; - backgroundThread = new Thread(() -> { - while (running) { + @Autowired + private OpenTelemetry openTelemetry; - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - Span span = otelTracer.spanBuilder("monitor").startSpan(); + @Override + public void start() { + var otelTracer = openTelemetry.getTracer("MonitorService"); + currentState = ServiceState.STARTING; + + backgroundThread = new Thread(() -> { + currentState = ServiceState.RUNNING; + while (currentState == ServiceState.RUNNING) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + + Span span = otelTracer.spanBuilder("monitor").startSpan(); + try { + System.out.println("Background service is running..."); + monitorWithRetry(); + } catch (Exception e) { + span.recordException(e); + span.setStatus(StatusCode.ERROR); + currentState = ServiceState.ERROR; + } finally { + span.end(); + } + } + }); - try { + backgroundThread.start(); + System.out.println("Background service started."); + } - System.out.println("Background service is running..."); - monitor(); - } catch (Exception e) { - span.recordException(e); - span.setStatus(StatusCode.ERROR); - } finally { - span.end(); - } - } - }); + private void monitorWithRetry() { + int attempts = 0; + Exception lastException = null; - // Start the background thread - backgroundThread.start(); - System.out.println("Background service started."); - } + while (attempts < MAX_RETRIES) { + try { + monitor(); + return; + } catch (Exception e) { + lastException = e; + attempts++; + + if (attempts < MAX_RETRIES) { + try { + Thread.sleep(RETRY_DELAY_MS * attempts); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } + } + } - private void monitor() throws InvalidPropertiesFormatException { - Utils.throwException(IllegalStateException.class,"monitor failure"); - } + if (lastException != null) { + currentState = ServiceState.ERROR; + throw new RuntimeException("Monitor failed after " + MAX_RETRIES + " attempts", lastException); + } + } + private void monitor() { + try { + performMonitoring(); + } catch (Exception e) { + throw new RuntimeException("Monitoring operation failed", e); + } + } + private void performMonitoring() { + System.out.println("Performing monitoring checks..."); + } - @Override - public void stop() { - // Stop the background task - running = false; - if (backgroundThread != null) { - try { - backgroundThread.join(); // Wait for the thread to finish - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - System.out.println("Background service stopped."); - } + @Override + public void stop() { + currentState = ServiceState.STOPPING; + if (backgroundThread != null) { + try { + backgroundThread.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + currentState = ServiceState.STOPPED; + System.out.println("Background service stopped."); + } - @Override - public boolean isRunning() { - return false; - } -} + @Override + public boolean isRunning() { + return currentState == ServiceState.RUNNING; + } +} \ No newline at end of file From 841cc40e6d72be2c0ed3e7a288525090ad6037e8 Mon Sep 17 00:00:00 2001 From: Shay Keren Date: Fri, 30 May 2025 21:52:48 +0300 Subject: [PATCH 2/2] fix: implement state management and error handling in MonitorService --- .../petclinic/errors/MonitorService.java | 103 +++++++++--------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java b/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java index c015cb5eec8..6cdcd332242 100644 --- a/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java +++ b/src/main/java/org/springframework/samples/petclinic/errors/MonitorService.java @@ -7,21 +7,27 @@ import org.springframework.context.SmartLifecycle; import org.springframework.stereotype.Component; +import java.util.InvalidPropertiesFormatException; +import java.util.concurrent.atomic.AtomicInteger; + @Component public class MonitorService implements SmartLifecycle { - public enum ServiceState { - STOPPED, + public enum SystemStatus { STARTING, RUNNING, STOPPING, + STOPPED, ERROR } - private ServiceState currentState = ServiceState.STOPPED; + private volatile SystemStatus currentStatus = SystemStatus.STOPPED; + private volatile boolean running = false; private Thread backgroundThread; - private static final int MAX_RETRIES = 3; - private static final long RETRY_DELAY_MS = 1000; + private final AtomicInteger retryCount = new AtomicInteger(0); + private final int MAX_RETRIES = 3; + private final long RETRY_DELAY_MS = 1000; + private volatile Exception lastError; @Autowired private OpenTelemetry openTelemetry; @@ -29,26 +35,40 @@ public enum ServiceState { @Override public void start() { var otelTracer = openTelemetry.getTracer("MonitorService"); - currentState = ServiceState.STARTING; - + currentStatus = SystemStatus.STARTING; + running = true; + retryCount.set(0); + lastError = null; + backgroundThread = new Thread(() -> { - currentState = ServiceState.RUNNING; - while (currentState == ServiceState.RUNNING) { + while (running) { try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } - + Span span = otelTracer.spanBuilder("monitor").startSpan(); try { - System.out.println("Background service is running..."); - monitorWithRetry(); + currentStatus = SystemStatus.RUNNING; + monitor(); + retryCount.set(0); } catch (Exception e) { + lastError = e; span.recordException(e); span.setStatus(StatusCode.ERROR); - currentState = ServiceState.ERROR; + currentStatus = SystemStatus.ERROR; + + if (retryCount.incrementAndGet() <= MAX_RETRIES) { + try { + Thread.sleep(RETRY_DELAY_MS * retryCount.get()); + continue; + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } } finally { span.end(); } @@ -59,63 +79,40 @@ public void start() { System.out.println("Background service started."); } - private void monitorWithRetry() { - int attempts = 0; - Exception lastException = null; - - while (attempts < MAX_RETRIES) { - try { - monitor(); - return; - } catch (Exception e) { - lastException = e; - attempts++; - - if (attempts < MAX_RETRIES) { - try { - Thread.sleep(RETRY_DELAY_MS * attempts); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - break; - } - } - } - } - - if (lastException != null) { - currentState = ServiceState.ERROR; - throw new RuntimeException("Monitor failed after " + MAX_RETRIES + " attempts", lastException); - } - } - - private void monitor() { + private void monitor() throws InvalidPropertiesFormatException { try { - performMonitoring(); + Utils.throwException(IllegalStateException.class, "monitor failure"); } catch (Exception e) { - throw new RuntimeException("Monitoring operation failed", e); + throw new InvalidPropertiesFormatException("Monitor operation failed: " + e.getMessage()); } } - private void performMonitoring() { - System.out.println("Performing monitoring checks..."); - } - @Override public void stop() { - currentState = ServiceState.STOPPING; + currentStatus = SystemStatus.STOPPING; + running = false; if (backgroundThread != null) { + backgroundThread.interrupt(); try { - backgroundThread.join(); + backgroundThread.join(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } - currentState = ServiceState.STOPPED; + currentStatus = SystemStatus.STOPPED; System.out.println("Background service stopped."); } @Override public boolean isRunning() { - return currentState == ServiceState.RUNNING; + return running && currentStatus == SystemStatus.RUNNING; + } + + public SystemStatus getCurrentStatus() { + return currentStatus; + } + + public Exception getLastError() { + return lastError; } } \ No newline at end of file