PortfolioExceptionHandler.java
package com.taxonomy.portfolio.controller;
import com.taxonomy.portfolio.service.PortfolioException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.net.URI;
/** RFC 9457 responses for the project portfolio API. */
@RestControllerAdvice(basePackages = "com.taxonomy.portfolio")
public class PortfolioExceptionHandler {
@ExceptionHandler(PortfolioException.class)
public ResponseEntity<ProblemDetail> handlePortfolioException(PortfolioException exception) {
HttpStatus status = switch (exception.getKind()) {
case NOT_FOUND -> HttpStatus.NOT_FOUND;
case CONFLICT -> HttpStatus.CONFLICT;
case VALIDATION -> HttpStatus.BAD_REQUEST;
case PAYLOAD_TOO_LARGE -> HttpStatus.PAYLOAD_TOO_LARGE;
case ANALYSIS_FAILED -> HttpStatus.UNPROCESSABLE_ENTITY;
case UNAVAILABLE -> HttpStatus.SERVICE_UNAVAILABLE;
};
ProblemDetail problem = ProblemDetail.forStatusAndDetail(status, exception.getMessage());
problem.setTitle(switch (exception.getKind()) {
case NOT_FOUND -> "Portfolio resource not found";
case CONFLICT -> "Portfolio state conflict";
case VALIDATION -> "Invalid portfolio request";
case PAYLOAD_TOO_LARGE -> "AI prompt budget exceeded";
case ANALYSIS_FAILED -> "Portfolio analysis payload failure";
case UNAVAILABLE -> "Portfolio analysis capacity unavailable";
});
problem.setType(URI.create("urn:taxonomy:portfolio:"
+ exception.getKind().name().toLowerCase()));
if (exception.getCode() != null) {
problem.setProperty("code", exception.getCode());
}
return ResponseEntity.status(status).body(problem);
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ProblemDetail> handleMalformedRequest(
HttpMessageNotReadableException exception) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.BAD_REQUEST,
"The portfolio request body is malformed or contains an unsupported value.");
problem.setTitle("Invalid portfolio request body");
problem.setType(URI.create("urn:taxonomy:portfolio:malformed-request"));
return ResponseEntity.badRequest().body(problem);
}
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<ProblemDetail> handleConstraintViolation(
DataIntegrityViolationException exception) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.CONFLICT,
"The requested portfolio change violates a uniqueness or reference constraint.");
problem.setTitle("Portfolio constraint conflict");
problem.setType(URI.create("urn:taxonomy:portfolio:constraint-conflict"));
return ResponseEntity.status(HttpStatus.CONFLICT).body(problem);
}
}