FailureAnalyzer in Spring Boot

FailureAnalyzer in Spring Boot

In this post, we will be exploring  FailureAnalyzer in Spring Boot. We will also learn to Create a Custom FailureAnalyzer in Spring Boot.

 

Introduction

Most of the time when we encounter an exception at the server startup time, we need to read it very closely to understand what went wrong before we try to fix it.

Through FailureAnalyzer, Spring Boot provides a great way to intercept exception at the startup and will turn them in more human readable format (We don’t have to scroll through the entire stack trace). Spring Boot comes with a number of  FailureAnalyzer starting from application context related exceptions, JSR-303 validations and more.

Here is an example where port 8080 was already in use and when we tried to run our Spring Boot application on port 8080, PortInUseFailureAnalyzer intercepted this exception and provided a more readable and user-friendly error message.

***************************
APPLICATION FAILED TO START
***************************
Description:
Embedded servlet container failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Here is a list of the few FailureAnalyzer provided by Spring Boot

  1. PortInUseFailureAnalyzer
  2. NoUniqueBeanDefinitionFailureAnalyzer
  3. BeanCurrentlyInCreationFailureAnalyzer

You can find a complete list of FailureAnalyzer by checking org.springframework.boot.diagnostics package under Spring Boot. 

Spring Boot provides an easy way to create our own custom FailureAnalyzer.

 

1. Creating Custom FailureAnalyzer

To create our own custom FailureAnalyzer, we can use AbstractFailureAnalyzer as our convenient extension point.AbstractFailureAnalyzer will check if a specified exception is present and will allow our custom analyzer to handle it.

Let’s create a custom FailureAnalyzer in Spring Boot for the following use case

  1. We will try to inject a different bean for a given dependencies.
  2. When we will try to inject it, Spring will throw BeanNotOfRequiredTypeException since we are trying to inject a different bean.

Here is our sample FailureAnalyzer code

public class CustomFailureAnalyzer extends AbstractFailureAnalyzer<BeanNotOfRequiredTypeException> {
/**
* Returns an analysis of the given {@code failure}, or {@code null} if no analysis
* was possible.
*
* @param rootFailure the root failure passed to the analyzer
* @param cause       the actual found cause
* @return the analysis or {@code null}
*/
@Override
protected FailureAnalysis analyze(Throwable rootFailure, BeanNotOfRequiredTypeException cause) {
String message ="####################### This is a custom fail Message ################ %n"+
getDescription(cause);
return new FailureAnalysis(message , (String)null, cause);
}
private String getDescription(BeanNotOfRequiredTypeException ex) {
StringWriter description = new StringWriter();
PrintWriter printer = new PrintWriter(description);
printer.printf(
"The bean %s could not be injected"
+ "due to %s",
ex.getBeanName(), ex.getRequiredType().getName());
return description.toString();
}
}

The main point is the analyze() method which will get triggered and Spring Boot will pass on a Throwable object along with the case (Exception which was thrown by Spring for our case).  We can get detail information from the cause instance to print a user-friendly message.

 

2. Registering Custom FailureAnalyzer

We need a special way to register our custom FailureAnalyzer with Spring Boot so as Spring Boot should be able to call our custom FailureAnalyzer in case exception is thrown by the system. We need to register it using spring.factories property file in META-INF folder.

In case META-INF or spring.factories properties file is not present, we need to create it manually. To register custom FailureAnalyzer, add following entry in spring.factories

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer

if you have multipleFailureAnalyzer You can register all of those in as a comma separated entries

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer,\ com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer1

At this point, we are all set with our CustomFailureAnalyzer and ready to test it.

 

3. FailureAnalyzer in Action

I have created 2 classes in my demo project with the following signature

public class AdminDAO {
public void helloAdmin(){
System.out.println("Hello Admin");
}
}
@Repository("adminDAO")
public class AdminDAOImpl {
public void setupAdmin(){
//default implimentation
}
}

I have created another Controller where I am trying to inject “adminDAO” bean to AdminDAO.

@RestController
public class HelloWorldController {
@Resource(name = "adminDAO")
   private AdminDAO adminDAO;
//some methods
}

If I run my Spring Boot Application, Spring will try to inject adminDao of type AdminDAOImpl in AdminDAO, since these are not compatible, Spring will throw BeanNotOfRequiredTypeException. In the current use case, Spring Boot will check to determine a valid FailureAnalyzer is registered and will pass on information to registered FailureAnalyzer.

In our case, we have registered CustomFailureAnalyzer to handle such case, Spring Boot will pass on this information to our custom FailureAnalyzer to produce a more user-friendly message.

Here is the output when we will run our application
***************************
APPLICATION FAILED TO START
***************************
Description:
################# This is a custom fail Message ################ %nThe bean adminDAO could not be injecteddue to com.umeshawasthi.service.AdminDAO

Summary

In this article, we explored an interesting feature provided by Spring Boot. We saw how Spring Boot FailureAnalyzer work and how to create our own custom FailureAnalyzer.

All the code of this article is available Over on Github. This is a Maven-based project.

You May Also Like