Is there a good example of zalando-problem implementation in spring boot? - java

We are using zalando-problem for exception handling in our spring-boot application. but looks like our problem handlers are never called. Instead spring boot returns 500 Internal server error for all the exceptions. If you can provide some examples it will be helpful. I couldn't find a good example of zalando-problem implementation in spring boot
If the user is not logged in, the code is throwing SSOAuthenticationException exception.
#Immutable
public class SSOAuthenticationException extends AbstractThrowableProblem {
private final String errorMessage;
public SSOAuthenticationException( final String errorMessage ) {
super( ErrorConstants.SSO_CACHE_AUTHENTICATION_FAILED, errorMessage, Status.UNAUTHORIZED );
this.errorMessage = errorMessage;
}
public String getErrorMessage(){
return errorMessage;
}
#Override
public String toString() {
return "SSOAuthenticationException{}";
}
}
And the Exception handling code:
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling {
#Override
public ResponseEntity<Problem> process(#Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
if (entity == null) {
return entity;
}
Problem problem = entity.getBody();
if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
return entity;
}
ProblemBuilder builder = Problem.builder()
.withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
.withStatus(problem.getStatus())
.withTitle(problem.getTitle())
.with("path", request.getNativeRequest(HttpServletRequest.class).getRequestURI());
if (problem instanceof ConstraintViolationProblem) {
builder
.with("violations", ((ConstraintViolationProblem) problem).getViolations())
.with("message", ErrorConstants.ERR_VALIDATION);
} else {
builder
.withCause(((DefaultProblem) problem).getCause())
.withDetail(problem.getDetail())
.withInstance(problem.getInstance());
problem.getParameters().forEach(builder::with);
if (!problem.getParameters().containsKey("message") && problem.getStatus() != null) {
builder.with("message", "error.http." + problem.getStatus().getStatusCode());
}
}
return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
}
#ExceptionHandler(SSOAuthenticationException.class)
#ResponseBody
public ResponseEntity<Problem> handleUnAuthenticatedUser(SSOAuthenticationException ex, NativeWebRequest request) {
Problem problem = Problem.builder()
.withStatus(Status.UNAUTHORIZED)
.with("message", ErrorConstants.SSO_CACHE_AUTHENTICATION_FAILED)
.build();
return create(ex, problem, request);
}
}
When I run in debugger, I notice that the exception handler is never called. instead the code thinks there is no handler registered (In ServletInitialHandler.java, it goes to the else section which is for exception not handled) and changes the status code to INTERNAL_SERVER_ERROR. So for all the exceptions, the application throws error 500. What is wrong in the exception handling code? Do we have to include a AdviceTrait? I tried that as well. but looks like that is also not working. If you could explain the right way of handling this exception and an example, it helps. Thanks

Using Problem Spring Web 0.25.2, I first created a new AdviceTrait similar to the existing ones:
public interface CustomAdviceTrait extends AdviceTrait {
#ExceptionHandler
default ResponseEntity<Problem> handleCustomException(final CustomException exception, final NativeWebRequest request) {
return create(Status.INTERNAL_SERVER_ERROR, exception, request);
}
}
This is also where a conversion into a Problem could take place, if needed.
Then, likewise to enabling dedicated built-in advice traits I enable them by implementing the respective interface on my ExceptionHandler:
#ControllerAdvice
public class FootlooseServiceExceptionHandler implements ProblemHandling, CustomAdviceTrait { }

Related

feign error handling by using feign decoder

I have created service 'A', which needs to be calls by the service 'B' by using feign client
And if service 'A' fails due to some validation, then service 'A' send back the error response which contains the below details,
(1)http status code
(2)error message
(3) custom error map which contains the custom errorcode and their error message
for example, <"Emp-1001", "invalid employee Id">
From Service 'B' we are using feigndecoder for handling feign exception, but it only provides the http status code not the custom error code
And, In my case, for different-different scenario, the http status code is same but custom error map value is different.
on the combination of both(http status code + custom error map), we have to handle the exception in service 'B'.
kindly provide some suggestions on this
You can enable circuit breaker and also configure your application to apply different fallback methods depending on the error returned, follow the next steps:
1.- Enable the circuit breaker itself
#SpringBootApplication
#EnableFeignClients("com.perritotutorials.feign.client")
#EnableCircuitBreaker
public class FeignDemoClientApplication {
2.- Create your fallback bean
#Slf4j
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PetAdoptionClientFallbackBean implements PetAdoptionClient {
#Setter
private Throwable cause;
#Override
public void savePet(#RequestBody Map<String, ?> pet) {
log.error("You are on fallback interface!!! - ERROR: {}", cause);
}
}
Some things you must keep in mind for fallback implementations:
Must be marked as #Component, they are unique across the application.
Fallback bean should have a Prototype scope because we want a new one to be created for each exception.
Use constructor injection for testing purposes.
3.- Your ErrorDecoder, to implement fallback startegies depending on the HTTP error returned:
public class MyErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new MyCustomBadRequestException();
}
if (response.status() >= 500) {
return new RetryableException();
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
4.- In your configuration class, add the Retryer and the ErrorDecoder into the Spring context:
#Bean
public MyErrorDecoder myErrorDecoder() {
return new MyErrorDecoder();
}
#Bean
public Retryer retryer() {
return new Retryer.Default();
}
You can also add customization to the Retryer:
class CustomRetryer implements Retryer {
private final int maxAttempts;
private final long backoff;
int attempt;
public CustomRetryer() {
this(2000, 5); //5 times, each 2 seconds
}
public CustomRetryer(long backoff, int maxAttempts) {
this.backoff = backoff;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
try {
Thread.sleep(backoff);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
#Override
public Retryer clone() {
return new CustomRetryer(backoff, maxAttempts);
}
}
If you want to get a functional example about how to implement Feign in your application, read this article.

How to utilize the #ResponseStatus annotation when handling the ResourceNotFoundException

I have the following service class:
#Service
public class CitiesServiceImpl implements CitiesService {
#Autowired
private CitiesRepository citiesRepository;
#Override
public City getCityById(Integer cityId) {
return citiesRepository.findById(cityId)
.orElseThrow(ResourceNotFoundException::new);
}
}
and it is used in my controller:
#RestController
#RequestMapping("/cities")
public class CitiesController {
#Autowired
private CitiesService citiesService;
#GetMapping("/{cityId}")
public City readCity(#PathVariable Integer cityId) {
return citiesService.getCityById(cityId);
}
#ExceptionHandler(ResourceNotFoundException.class)
String handleResourceNotFound(Exception e) {
return e.getMessage();
}
}
So when the readCity is called with a nonexistent cityID, the ResourceNotFoundException will be thrown and then handled by the handleResourceNotFound exception handler.
However, when the ResouceNotFoundException is handled, the status code in the response is still 202, i.e. OK. Seems that #ResponseStatus annotation in the ResourceNotFoundException is not utilized at run time. This can be fixed by adding the #ResponseStatus(value=HttpStatus.NOT_FOUND) to the method handleResourceNotFound, but such code is duplicate as the #ResponseStatus annotation is already in the ResourceNotFoundException.
Question: How to utilize the ResponseStatus annotation of the ResourceNotFoundException instead of adding duplicate code?
Remove that handleResourceNotFound and let framework handle that for you, or return proper Response from handleResourceNotFound method.
By declaring such handler you are saying that you will handle such case, thus framework is backing off.

Nested exception is not correctly reported in the ControllerAdvice

In a rest application, i create a class to manage error
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ProcessPaymentException.class)
private ResponseEntity < String > handleProcessPaymentException(ProcessPaymentException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
#ExceptionHandler(Exception.class)
private ResponseEntity < String > defaultExceptionHandler(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
In my service layer
#Transactional
#Override
public void processPayment(Long paymentId, PaymentModeEnum paymentMode) throws ProcessPaymentException {
processCreditCardPayment(paymentId, paymentMode);
}
private void processCreditCardPayment(Long paymentId, PaymentModeEnum paymentMode) throws ProcessPaymentException {
try{
chargePayment(paymentId)
}catch(ProcessPaymentException ppe){
throw new ProcessPaymentException(ppe.getMessage()); #1
}
}
private ResolverReceipt chargeMemberCreditCard(Long paymentId, PaymentGatewayConfig paymentGateway) throws ProcessPaymentException {
...
if(memberId==null){
throw new ProcessPaymentException("error process payment");#2
}
}
When i get a ProcessPaymentException, i see in debug mode, when i go in the RestResponseEntityExceptionHandler, i pass by defaultExceptionHandler.
I don't understand why, i was thinking to pass handleProcessPaymentException method.
The debug message i see is:
Target object must not be null; nested exception is java.lang.IllegalArgumentException: Target object must not be null.
e= (org.springframework.dao.InvalidDataAccessApiUsageException) org.springframework.dao.InvalidDataAccessApiUsageException: Target object must not be null; nested exception is java.lang.IllegalArgumentException: Target object must not be null
I was thinking to get: error process payment
According to this article, if you have used custom resolver, you need to define following lines to use #ExceptionHandler.
#Component
public class AnnotatedExceptionResolver extends AnnotationMethodHandlerExceptionResolver
{
public AnnotatedExceptionResolver() {
setOrder(HIGHEST_PRECEDENCE);
}
}

Order of #ExceptionHandler

I use #ControllerAdvice to handle all my app exceptions :
#ControllerAdvice
public class ExceptionHandlingController {
#ExceptionHandler({UnauthorizedException.class})
public String unauthorizedException() {
.........
}
#ExceptionHandler({UnauthorizedAjaxException.class})
#ResponseBody
public void unauthorizedAjaxException() {
.........
}
#ExceptionHandler({Exception.class})
public String globalException(){
.........
}
}
And somewhere in my code i do throw new UnauthorizedException();
#Around("#annotation(Authenticated)")
public Object profilingAuthentication(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if( request.getSession().getAttribute("idContact") == null ) {
if( "XMLHttpRequest".equals(request.getHeader("X-Requested-With")) )
throw new UnauthorizedAjaxException();
throw new UnauthorizedException();
}
return pjp.proceed();
}
But sadly Spring MVC appears to be acting random by using the most generic case (Exception) rather than more specific ones (UnauthorizedException for example). And sometimes he choose the correct one !
How the order works works ? and is there any way to specify the order ?
UnauthorizedException is a custom exception
public class UnauthorizedException extends Exception {
public UnauthorizedException(){
super();
}
public UnauthorizedException(String message){
super(message);
}
}
UPDATE
i found out that the order it's not rondom actually the methods who throw UnauthorizedException works normally but the others not !
#Authenticated
#RequestMapping(value="/favoris")
public String favoris(ModelMap model, HttpServletRequest request)
throws UnauthorizedException {
....
}
#Authenticated
#RequestMapping(value="/follow")
public String follow(ModelMap model, HttpServletRequest request) {
.....
}
So i have to add throws UnauthorizedException manually or there is some other solution ?
we are using exception handler in following way and never order get mixed and it work as expected.
So it could be possible if you will use it as following example then it will solve your problems
**********handler class******************
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public boolean handle1(Exception exc) {
System.out.println("#####Global Exception###" + exc);
exc.printStackTrace(System.out);
return true;
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = CustomException.class)
public boolean handle2(CustomException exc) {
System.out.println("###custom exception######" + exc);
exc.printStackTrace(System.out);
return true;
}
}
***************Controller class************
#RestController("test")
#RequestMapping("/test1")
public class TestController {
#RequestMapping("/t1")
public boolean test() {
if (true) {
throw new CustomException();
}
return true;
}
}
In above example exception habdler is handle2 because 1st of all it will search for matching exception if not found then go for parrent handler
If we throw new NullPointerException() then it will search for matching handler but not found in this case then go for parrent that is handle1
for more you can refer here
I hope it will help you. Thanks
Use an annotation #Order or implement an interface Ordered for #ControllerAdvice.
See implementations of:
ExceptionHandlerMethodResolver class
ExceptionHandlerExceptionResolver class (method initExceptionHandlerAdviceCache)
AnnotationAwareOrderComparator class
There is no order/priority as long as you have a single controlleradvice class in your project. But if you have multiple controlleradvice classes, you can set the Order. But here, in your case, the order is not applicable as the two exceptions are handled differently (i.e., UnauthorizedException and Exception).
The good thing is, Spring will automatically find the respective custom Exception class (if any,
otherwise generic Exception) and invoke the corresponding method.
Please refer for more information on Spring Controller Advice and Exception handling:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Java Jersey PathParams Checking and NotFoundException custom message

I am using Jersey for rest API, JerseyTests to unit test.
I have been following what seems to be conventional practice over the internet for PathParams checking and Exception Handling, but I don't quite understand what I am doing wrong here:
RoomApplicationResource.java
#Path("demandes")
public class RoomApplicationResource {
#GET
#Path("/{email}/{requestNumber}")
public Response getRoomApplication(
#PathParam("email") String email,
#PathParam("requestNumber") String requestNumber) throws NoRoomApplicationFoundException {
if (email == "wrong#email.com" || requestNumber == "wrong") {
throw new NoRoomApplicationFoundException("bad request");
}
String response =requestNumber+" is valid for "+email;
return Response.ok(response).build();
}
}
I handle Exceptions like this:
NotFoundMapper.java
#Provider
public class NotFoundMapper implements ExceptionMapper<NoRoomApplicationFoundException>{
#Override
public Response toResponse(NoRoomApplicationFoundException e) {
return Response.status(Response.Status.NOT_FOUND)
.entity(e.getMessage()).build();
}
}
NoRoomApplicationFoundException.java
public class NoRoomApplicationFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public NoRoomApplicationFoundException() {
super();
}
public NoRoomApplicationFoundException(String msg) {
super(msg);
}
public NoRoomApplicationFoundException(String msg, Exception e) {
super(msg, e);
}
}
And I test like this:
RoomApplicationResourceTest.java
public class RoomApplicationResourceTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(RoomApplicationResource.class, NotFoundMapper.class);
}
// This test works fine as expected
#Test
public void whenParametersAreExistantReturnTheOkResponse() {
final Response res = target("demandes").path("valid#email.com").path("12345").request().get();
assertEquals(200, res.getStatus());
assertEquals("12345 is valid for valid#email.com", res.readEntity(String.class));
}
// This does not work as expected
#Test
public void whenEmailParameterDoNotMatchToAnyRoomApplicationThenReturns404() {
final Response res = target("demandes").path("wrong#email.com").path("12345").request().get();
assertEquals(404, res.getStatus());
assertEquals("bad request", res.readEntity(String.class));
}
}
Question 1: Is this way of doing conditional checking on params wrong? The result of the second test where the email is invalid should throw my custom exception and return a 404, but instead returns a 200 and the valid message.
Question 2: How should I handle missing parameters in this case? It seems Jersey throws a NotFoundException by default. Is there a simple way to customize the message of that error or perhaps use my custom exception as the throws NoRoomApplicationFoundException at the end of my resource method does not seem to be doing anything?
Thanks in Advance. Alex
Question 1
Yes. The problem is your use of == to compare Strings. You should instead be using String.equals(). See How do I compare Strings in Java?
if ("wrong#email.com".equals(email) || "wrong".equals(requestNumber)) {
throw new NoRoomApplicationFoundException("bad request");
}
Question 2:
This question seems to be related to your first question. But for me, as a general rule (this is just me), if I am authoring the exception class and the exception is specific to my JAX-RS application (meaning I will have no use for it outside the JAX-RS application), I will just make the exception extend WebApplicationException. This exception will be handled by default, and you can create the Response in that class. No need for any ExceptionMapper. For example
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
public class NoRoomApplicationFoundException extends WebApplicationException {
private static final long serialVersionUID = 1L;
public NoRoomApplicationFoundException() {
this("Room not found", 400);
}
public NoRoomApplicationFoundException(String msg, int status) {
this(Response.status(status).entity(msg).build());
}
public NoRoomApplicationFoundException(Response response) {
super(response);
}
}
You could completely get rid of the NotFoundMapper and this would work just fine.
if ("wrong#email.com".equals(email) || "wrong".equals(requestNumber)) {
throw new NoRoomApplicationFoundException();
}
Some Resources:
Jersey Documentation for Exception Handling
WebApplicationException Javadoc to see different constructors. There are alot more ways you can construct your exception and chain up to the super class
What should I return if my object is null?. In general, if a resource can't be found, the general rule of thumb is to return a 404 Not Found. Not sure it applies to this specific case though.

Categories

Resources