From 699d8572a98e4713357816d6e3e65a30914a2219 Mon Sep 17 00:00:00 2001 From: Lincpay Tech Date: Tue, 9 Dec 2025 19:11:42 +0530 Subject: [PATCH] added retry for the send otp --- .../exception/GlobalExceptionHandler.java | 49 ++++++++++-- .../bbps/serviceImpl/AuthServiceImpl.java | 79 +++++++++++++------ 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java index a904a52..6fac62d 100644 --- a/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/billpaymentservice/bbps/exception/GlobalExceptionHandler.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.*; +import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; @@ -75,6 +76,35 @@ public class GlobalExceptionHandler { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); } + // ------------------------- MALFORMED/INVALID JSON ------------------------ + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity> handleInvalidJson( + HttpMessageNotReadableException ex, + HttpServletRequest request, + HandlerMethod handlerMethod) { + + String endpoint = (request != null) ? request.getMethod() + " " + request.getRequestURI() : "unknown"; + // prefer the root Jackson message if present + String rootMsg = ex.getMostSpecificCause() != null ? ex.getMostSpecificCause().getMessage() : ex.getMessage(); + String shortMsg = (rootMsg != null && rootMsg.length() > 200) ? rootMsg.substring(0, 200) + "..." : rootMsg; + + logger.warn("Invalid JSON received for endpoint {}: {}", endpoint, shortMsg); + + Map data = Map.of( + "exception", ex.getClass().getName(), + "message", shortMsg + ); + + ResponseModel resp = new ResponseModel<>( + "[" + endpoint + "] Invalid JSON received", + "FAILED", + HttpStatus.BAD_REQUEST.value(), + data + ); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); + } + // ------------------------- NO HANDLER (404 NOT FOUND) ------------------------ @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity> handleNotFound(NoHandlerFoundException ex) { @@ -121,14 +151,17 @@ public class GlobalExceptionHandler { String context = buildErrorContext(ex, request, handlerMethod); logger.warn("CustomException → {}", context); - ResponseModel resp = new ResponseModel<>( - ex.getMessage(), - "FAILED", - HttpStatus.BAD_REQUEST.value(), - null - ); + // Use the ResponseModel carried by CustomException so callers get the original status/code/data + ResponseModel resp = ex.getResponseModel(); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resp); + HttpStatus httpStatus = HttpStatus.BAD_REQUEST; + try { + httpStatus = HttpStatus.valueOf(resp.getStatusCode()); + } catch (Exception e) { + // fallback + } + + return ResponseEntity.status(httpStatus).body(resp); } // ------------------------- GENERIC EXCEPTION ------------------------ @@ -150,4 +183,4 @@ public class GlobalExceptionHandler { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); } -} +} \ No newline at end of file diff --git a/src/main/java/com/billpaymentservice/bbps/serviceImpl/AuthServiceImpl.java b/src/main/java/com/billpaymentservice/bbps/serviceImpl/AuthServiceImpl.java index 0753ff2..dcf6772 100644 --- a/src/main/java/com/billpaymentservice/bbps/serviceImpl/AuthServiceImpl.java +++ b/src/main/java/com/billpaymentservice/bbps/serviceImpl/AuthServiceImpl.java @@ -46,7 +46,7 @@ public class AuthServiceImpl implements AuthService { @Autowired public AuthServiceImpl(UserRepository userRepository, OtpClient otpClient, Helper helper, - UsersDetailsRepository userRepo, JwtUtil jwtUtil) { + UsersDetailsRepository userRepo, JwtUtil jwtUtil) { this.userRepository = userRepository; this.otpClient = otpClient; this.helper = helper; @@ -56,21 +56,18 @@ public class AuthServiceImpl implements AuthService { @Override public ResponseModel sendOtp(LoginRequest request) { - try { - OtpSendResponse resp = otpClient.sendOtp(request.getMobile()); - if (resp == null) { - return ResponseModel.builder().message("OTP send service returned null").status("Failure") - .statusCode(500).data(null).build(); - } - String status = resp.isSuccess() ? "Success" : "Failure"; - int code = resp.isSuccess() ? 200 : 400; - return ResponseModel.builder().message(resp.getMessage()).status(status).statusCode(code).data(resp) - .build(); - } catch (Exception ex) { - LOGGER.error("Error in sendOtp: {}", ex.getMessage(), ex); - return ResponseModel.builder().message("An unexpected error occurred while sending OTP").status("Failure") - .statusCode(500).data(null).build(); + // validate minimal inputs + if (request == null || request.getMobile() == null || request.getMobile().isBlank()) { + throw new CustomException("Mobile number is required", "FAILED", 400, "missing mobile"); } + + // Delegate attempts to helper — it will throw CustomException on unrecoverable issues + OtpSendResponse resp = attemptSendOtpWithRetry(request.getMobile(), 1); + + // If the downstream returned a response indicating failure, return that as a ResponseModel + String status = resp.isSuccess() ? "Success" : "Failure"; + int code = resp.isSuccess() ? 200 : 400; + return ResponseModel.builder().message(resp.getMessage()).status(status).statusCode(code).data(resp).build(); } @Override @@ -121,6 +118,40 @@ public class AuthServiceImpl implements AuthService { } } + /** + * Helper that attempts to send OTP and retries up to `retryCount` times on null/unsuccessful responses. + * Throws CustomException when the OTP service returns null after retries or when unexpected errors occur. + */ + private OtpSendResponse attemptSendOtpWithRetry(String mobile, int retryCount) { + int attempts = 0; + OtpSendResponse resp = null; + while (attempts <= retryCount) { + attempts++; + try { + LOGGER.info("Attempt #{} to send OTP to {}", attempts, mobile); + resp = otpClient.sendOtp(mobile); + if (resp != null && resp.isSuccess()) { + LOGGER.info("OTP send succeeded on attempt #{} for {}", attempts, mobile); + return resp; + } + // If response is non-null but indicates failure, we still may not want to retry for logical errors + if (resp != null && !resp.isSuccess()) { + LOGGER.warn("OTP service responded with failure on attempt #{} for {}: {}", attempts, mobile, resp.getMessage()); + // Do not retry for application-level failures (invalid number etc.) + return resp; + } + } catch (Exception ex) { + LOGGER.error("Exception while sending OTP on attempt #{} for {}: {}", attempts, mobile, ex.getMessage(), ex); + // continue to retry if attempts remain + } + // loop continues to retry if attempts <= retryCount + } + + // After retries, if resp is null (no response received), treat as unrecoverable and let global handler handle it via exception + LOGGER.error("OTP service returned null after {} attempts for {}", attempts - 1, mobile); + throw new CustomException("OTP send service returned null", "FAILED", 500, "OtpClient returned null after retries"); + } + @Override @Transactional public ResponseModel registerUser(RegisterRequest request) { @@ -172,23 +203,23 @@ public class AuthServiceImpl implements AuthService { @Override public ResponseModel authenticateUser(LoginRequest loginRequest) { // LOGGER.info("Processing login for Request: {}", loginRequest); -// +// // try { // String mobileNo = loginRequest.getMobile(); -// +// // Optional byMobileNo = userRepo.findByUserMobNo(mobileNo); -// +// // if (byMobileNo.isPresent()) { // UsersDetails user = (UsersDetails) byMobileNo.get(); // String token = jwtUtil.generateToken(user.getUserMobNo()); -// +// // LOGGER.info("Generated JWT token for mobileNo: " + mobileNo); -// +// // Object userDetailsDTO = null; -// +// // if (user != null) { // userDetailsDTO = UserDetailsDTO.builder().id(user.getId()).userEmailId(user.getUserEmailId()) -// .userMobNo(user.getUserMobNo()).userName(user.getUserName()).build(); +// .userMobNo(user.getUserMobNo()).userName(user.getUserName()).build(); // } // AuthResponses authResp = new AuthResponses(token, "Success", "Login Done", 200, userDetailsDTO); // return ResponseModel.builder().message("Login Done").status("Success").statusCode(200).data(authResp) @@ -199,7 +230,7 @@ public class AuthServiceImpl implements AuthService { // .data(authResp).build(); // } // -// +// // } catch (Exception ex) { // LOGGER.error("Error occurred during login for mobile no: {} Exception: {}", loginRequest.getMobile(), // ex.getMessage(), ex); @@ -213,4 +244,4 @@ public class AuthServiceImpl implements AuthService { public UserEntity findByMobile(String mobile) { return userRepository.findByMobile(mobile).orElse(null); } -} \ No newline at end of file +}