diff --git a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java index 3b09f07..15a4eac 100644 --- a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java @@ -2,6 +2,8 @@ package com.billpaymentservice.bbps.exception; import java.util.*; import java.util.stream.Collectors; +import java.net.ConnectException; +import java.net.SocketTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +14,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.client.ResourceAccessException; import com.billpaymentservice.bbps.dto.ResponseModel; @@ -161,6 +164,79 @@ public class GlobalExceptionHandler { return ResponseEntity.status(httpStatus).body(resp); } + // ------------------------- RESOURCE ACCESS / TIMEOUT / CONNECT ------------------------ + @ExceptionHandler(ResourceAccessException.class) + public ResponseEntity> handleResourceAccess( + ResourceAccessException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("ResourceAccessException → {}", context, ex); + + Throwable cause = ex.getCause(); + String message; + HttpStatus status; + + if (cause instanceof SocketTimeoutException) { + message = "Server timeout - server may be down"; + status = HttpStatus.GATEWAY_TIMEOUT; // 504 + } else if (cause instanceof ConnectException) { + message = "Server unavailable - connection refused"; + status = HttpStatus.SERVICE_UNAVAILABLE; // 503 + } else { + message = "Server unreachable: " + ex.getMessage(); + status = HttpStatus.SERVICE_UNAVAILABLE; + } + + ResponseModel resp = new ResponseModel<>( + message, + "FAILED", + status.value(), + null + ); + + return ResponseEntity.status(status).body(resp); + } + + @ExceptionHandler(SocketTimeoutException.class) + public ResponseEntity> handleSocketTimeout( + SocketTimeoutException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("SocketTimeoutException → {}", context, ex); + + ResponseModel resp = new ResponseModel<>( + "Server timeout - server is down", + "FAILED", + HttpStatus.GATEWAY_TIMEOUT.value(), + null + ); + + return ResponseEntity.status(HttpStatus.GATEWAY_TIMEOUT).body(resp); + } + + @ExceptionHandler(ConnectException.class) + public ResponseEntity> handleConnectException( + ConnectException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String context = buildErrorContext(ex, request, handlerMethod); + logger.error("ConnectException → {}", context, ex); + + ResponseModel resp = new ResponseModel<>( + "Server unavailable - connection refused", + "FAILED", + HttpStatus.SERVICE_UNAVAILABLE.value(), + null + ); + + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(resp); + } + // ------------------------- GENERIC EXCEPTION ------------------------ @ExceptionHandler(Exception.class) public ResponseEntity> handleAll( diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java index 9541c06..32aa1a7 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/HttpService.java @@ -2,6 +2,7 @@ package com.billpaymentservice.bbps.serviceImpl; import java.util.HashMap; +import java.net.ConnectException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestClient; +import org.springframework.web.client.ResourceAccessException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,6 +26,15 @@ public class HttpService { private final RestClient restClient; private final ObjectMapper objectMapper = new ObjectMapper(); + // Helper to produce server-down/timeout style responses + private HashMap buildServerDownResponse(String code, String message, Exception e) { + HashMap response = new HashMap<>(); + response.put("respCode", code); + response.put("data", message); + log.error("❌ {} - {}", code, message, e); + return response; + } + public HashMap postRequest(String url, String payload, HttpHeaders customHeaders) { @@ -60,6 +71,15 @@ public class HttpService { response.put("respCode", "403"); response.put("data", "FORBIDDEN - Check credentials in HttpService"); + } catch (ResourceAccessException e) { + // This often wraps timeouts or connection refused + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } + } catch (Exception e) { log.error("❌ HttpService Exception: {}", e.getMessage()); response.put("respCode", "500"); @@ -97,6 +117,13 @@ public class HttpService { log.error("❌ HTTP Error: {}", httpException.getMessage(), httpException); response.put("respCode", String.valueOf(httpException.getStatusCode().value())); response.put("data", httpException.getResponseBodyAsString()); + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ Exception during GET call: {}", e.getMessage(), e); response.put("respCode", "500"); @@ -133,6 +160,13 @@ public class HttpService { log.error("❌ HTTP Error: {}", httpException.getMessage(), httpException); response.put("respCode", String.valueOf(httpException.getStatusCode().value())); response.put("data", httpException.getResponseBodyAsString()); + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ Exception during GET call: {}", e.getMessage(), e); response.put("respCode", "500"); @@ -171,6 +205,13 @@ public class HttpService { response.put("respCode", String.valueOf(e.getStatusCode().value())); response.put("data", e.getResponseBodyAsString()); // still HTML or error string + } catch (ResourceAccessException e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) { + return buildServerDownResponse("503", "Server unavailable - connection refused", e); + } else { + return buildServerDownResponse("503", "Server unreachable: " + e.getMessage(), e); + } } catch (Exception e) { log.error("❌ FORM POST Exception: {}", e.getMessage(), e); @@ -181,7 +222,4 @@ public class HttpService { return response; } - - - } \ No newline at end of file