spring-fallback

Library that provides a purpose built @Fallback annotation for Spring

License

License

GroupId

GroupId

me.snowdrop
ArtifactId

ArtifactId

spring-fallback
Last Version

Last Version

0.2.0
Release Date

Release Date

Type

Type

jar
Description

Description

spring-fallback
Library that provides a purpose built @Fallback annotation for Spring
Project URL

Project URL

http://www.snowdrop.me
Project Organization

Project Organization

Red Hat
Source Code Management

Source Code Management

https://github.com/snowdrop/spring-fallback

Download spring-fallback

How to add to project

<!-- https://jarcasting.com/artifacts/me.snowdrop/spring-fallback/ -->
<dependency>
    <groupId>me.snowdrop</groupId>
    <artifactId>spring-fallback</artifactId>
    <version>0.2.0</version>
</dependency>
// https://jarcasting.com/artifacts/me.snowdrop/spring-fallback/
implementation 'me.snowdrop:spring-fallback:0.2.0'
// https://jarcasting.com/artifacts/me.snowdrop/spring-fallback/
implementation ("me.snowdrop:spring-fallback:0.2.0")
'me.snowdrop:spring-fallback:jar:0.2.0'
<dependency org="me.snowdrop" name="spring-fallback" rev="0.2.0">
  <artifact name="spring-fallback" type="jar" />
</dependency>
@Grapes(
@Grab(group='me.snowdrop', module='spring-fallback', version='0.2.0')
)
libraryDependencies += "me.snowdrop" % "spring-fallback" % "0.2.0"
[me.snowdrop/spring-fallback "0.2.0"]

Dependencies

compile (2)

Group / Artifact Type Version
org.springframework : spring-context Optional jar
org.springframework : spring-core jar

test (3)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter-test jar 1.5.17.RELEASE
org.springframework.boot : spring-boot-starter-logging jar 1.5.17.RELEASE
org.aspectj : aspectjweaver jar 1.8.9

Project Modules

There are no modules declared in this project.

CircleCI

Maven Central

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)

me.snowdrop

Snowdrop

Home of Snowdrop

Versions

Version
0.2.0
0.1.1