Purpose
The purpose of this project is to provide a simple @Fallback annotation that will provide the ability to provide a fallback value for failed calls
Use cases
A simple use case is to combine this annotation with Istio in order to forgo the need of having to introduce Hystix
Usage
Prerequisites
Add the following dependencies to your pom.xml
<dependency>
    <groupId>me.snowdrop</groupId>
    <artifactId>spring-fallback</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency> 
Enable fallback
spring-fallback is enabled in the same way that caching is enabled in Spring
In one of your configuration classes (or in the main class in case of a Spring Boot application), add @EnableFallback.
For example:
package com.example.demo;
import me.snowdrop.fallback.EnableFallback;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableFallback
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
} 
Examples
The simplest example where all the defaults are used:
@Component
public class Bean {
    @Fallback
    public String example() {
        throw new RuntimeException();
    }
    public String error() {
        return "error";
    }
} 
will result in the return of error when Bean.example is invoked. This is due to the fact that the default error handler used when @Fallback is specified is named error
A different method can be specified for example:
@Component
public class Bean {
    @Fallback(fallbackMethod = "fallback")
    public String example() {
        throw new RuntimeException();
    }
    public String fallback() {
        return "error";
    }
}  
Fallback methods can also utilize the ExecutionContext parameter in order to differentiate error handling For example:
@Component
public class Bean {
    @Fallback
    public String io() throws IOException{
        throw new IOException();
    }
    
    @Fallback
    public String rt() {
        throw new RuntimeException();
    }    
    public String error(ExecutionContext executionContext) {
        final Throwable t = executionContext.getThrowable();
        if (t instanceof IOException) {
            return "io-fallback";
        }
        else if (t instanceof RuntimeException) {
            return "rt-fallback";
        }
        
        return "default-fallback";
    }
} 
The @Fallback annotation can also be used on a class instead of a method. In this case each public method of the spring bean will trigger the fallback if it fails. The aforementioned example could also be rewritten like so:
@Component
@Fallback
public class Bean {
    public String io() throws IOException{
        throw new IOException();
    }
    
    public String rt() {
        throw new RuntimeException();
    }    
    public String error(ExecutionContext executionContext) {
        final Throwable t = executionContext.getThrowable();
        if (t instanceof IOException) {
            return "io-fallback";
        }
        else if (t instanceof RuntimeException) {
            return "rt-fallback";
        }
        
        return "default-fallback";
    }
} 
Furthermore, the @Fallback annotation can be placed on superclasses or interfaces.
The fallback method can be a static method of some other class. For example the following would work:
public final class FallbackUtil {
    private FallbackUtil() {}
    public static String handle(ExecutionContext executionContext) {
        return "fallback from " + executionContext.getMethod().getName();
    }
}
@Component
public class Bean {
    @Fallback(value = FallbackUtil.class, fallbackMethod = "handle")
    public String example() {
        throw new RuntimeException();
    }
}  
Finally, the fallback method can also be a method of some other spring bean as shown in the following example:
@Component
public class FallbackBean {
    public String error(ExecutionContext executionContext) {
        return "fallback from " + executionContext.getMethod().getName();
    }
}
@Component
public class Bean {
    @Fallback(value = FallbackBean.class)
    public String example() {
        throw new RuntimeException();
    }
}  
Multiple fallbacks for a single method / class
The library provides the ability to specify multiple Fallback annotations on a single method or class as can be seen in the following example:
@Fallback(throwable=IOException.class, value = "ioErrorHandler", order = Integer.MIN_VALUE)
@Fallback(throwable=RuntimeException.class, value ="runtimeErrorHandler", order = Integer.MIN_VALUE)
@Fallback("defaultErrorHandler")
public class Example {
    
    public String someMethod() {
        // could throw various exceptions depending on the codepath
    }
    
    public String ioErrorHandler() {
        return "io";
    }
    
    public String runtimeErrorHandler() {
        return "runtime";
    }
    
    public String defaultErrorHandler() {
        return "default";
    }    
} 
In the example above if someMethod throws an IOException (or any of it's subclasses) then ioErrorHandler will handle the error and "io" will be returned. If someMethod throws a RuntimeException (or any of it's subclasses) then runtimeErrorHandler will handle the error and "runtime" will be returned. Any other exception will be handled by defaultErrorHandler.
In cases such as the above it's very important to correctly specify the order (lower values mean that the handler has a higher priority)
 JarCasting
 JarCasting