Getting started with Failsafe library

September 09, 2020 No comments Failsafe Library Java

1. Introduction

In this article, we are going to show how to start using the Failsafe library for handling failures in Java applications. Failsafe is a lightweight, zero-dependency library created by Jonathan Halterman and other great developers for handling everyday use cases. Need to mention that it is released under Apache 2.0 license so you can use it commercial projects.

2. Setup Failsafe library

To start using Failsafe library simply add dependecy to your project:

<dependency>
  <groupId>net.jodah</groupId>
  <artifactId>failsafe</artifactId>
  <version>2.4.0</version>
</dependency>

Check available versions here: net.jodah : failsafe

3. Getting started

The Failsafe library supports four policies, that determine which execution results or failures to handle and how to handle them:

  • Retry,
  • Timeout,
  • Fallback,
  • Circuit Breaker.

In this tutorial, we are going to use a simple Retry policy.

RetryPolicy<Object> retryPolicy = new RetryPolicy<>().handle(ConnectException.class)
                                                     .withDelay(Duration.ofSeconds(2))
                                                     .withMaxRetries(5);

This sample Retry policy will handle ConnectionException. We configured the maximum number of retries to 5 and delay between attempts to 2 seconds. When the ConnectionException occurs, the system waits for 2s and tries to reconnect again until we reach the maximum number of retries.

Method connect() that we use in this example will be responsible for connection to the some kind of external system (like Database or FTP):

public Connection connect() throws ConnectException {
    // try to connect to the external system
}

Failsafe library allows us to execute Runnable or Supplier with retries. The following code shows how to use the Runnable approach:

Failsafe.with(retryPolicy).run(() -> connect());

The example with Supplier is as follows:

Connection connection = Failsafe.with(retryPolicy).get(() -> connect());

Both examples using Failsafe to handle ConnectionException that might occur in the connect() method. According to the defined policy, the program will be trying to connect at most 5 times, and it will wait 2 seconds after each try.

We could also execute a Runnable or Supplier asynchronously with a policy we choose as follows:

CompletableFuture<Void> future = Failsafe.with(retryPolicy).runAsync(() -> connect());
CompletableFuture<Connection> future = Failsafe.with(retryPolicy).getAsync(() -> connect());

4. JUnit Test

The mockito test for our use case could look as follows:

The DatabaseConnection represents the object that holds the connection to external system.

package com.frontbackend.libraries.failsafe;

import java.net.ConnectException;

public class DatabaseConnection {

    public DatabaseConnection connect() throws ConnectException {
        // try to connect to the external system
        return this;
    }
}

The mockito test that make a use of RetryPolicy has the following structure:

package com.frontbackend.libraries.failsafe;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.net.ConnectException;
import java.time.Duration;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;

@RunWith(MockitoJUnitRunner.class)
public class RetryPolicyTest {

    @Mock
    DatabaseConnection connection = new DatabaseConnection();

    @Test
    public void testMaxRetries() throws ConnectException {
        // given
        RetryPolicy<Object> retryPolicy = new RetryPolicy<>().handle(ConnectException.class)
                                                             .withDelay(Duration.ofSeconds(1))
                                                             .withMaxRetries(3);

        when(connection.connect()).thenThrow(ConnectException.class)
                                  .thenReturn(connection);

        // when
        Failsafe.with(retryPolicy)
                .run(connection::connect);

        // then
        verify(connection, Mockito.times(2)).connect();
    }
}

In this test, we use mockito to simulate connection problems. The first attempt will throw the ConnectionException and the second one will return the actual DatabaseConnection instance.

This is set up in the following code:

when(connection.connect()).thenThrow(ConnectException.class)
                          .thenReturn(connection);

Test verify if system calls connect() method only two times (because the second call didn't throw an exception):

verify(connection, Mockito.times(2)).connect();

5. Conclusion

In this article, we presented a simple example of using the Failsafe library to handle failures in the Java program. Setting up this library is easy, and we don't need extra dependencies to make use of it. The library could be used to reconnect to the external system that is not stable and needs several attempts.

{{ message }}

{{ 'Comments are closed.' | trans }}