Power of Redis®* for your supersonic Quarkus/Java application

dewanahmed

Dewan Ahmed

Posted on August 10, 2022

Power of Redis®* for your supersonic Quarkus/Java application

When building applications that run on Kubernetes, you want the start-up time and footprint to be small. Traditional Java applications like to consume more than their constrained memory and CPU and you might have seen OOMKilled errors in your production applications. Brace yourself as this blog takes you on a "supersonic" journey with Quarkus (a fast and lightweight Java framework) and Redis®* (fast in-memory data structure store).

Building a small one-time-password application

Imagine that you're building a one-time-password feature as part of a larger project. Your team is using Java, which they're familiar with, and Quarkus as the framework because they want the application to be Kubernetes-compatible (in terms of size and startup time).

Besides a relational database, the team is also using Redis for fast querying of the generated one-time-passwords. This example uses Aiven for Redis and you can sign up for a free trial if you want to try it out.

An important consideration for your team is that both Quarkus and Redis are open-source projects.

Before you start

To follow the hands-on portion of this blog, here are the software requirements:

Time to build the app

Create a Maven Project

The following command creates a new Maven project and adds the required extensions to your project. The Quarkus redis-client extension allows the user to connect to a Redis server and run Redis commands. The resteasy-jackson extension allows to build RESTful web services and to process JSON data format. The resteasy-mutiny extension helps to build reactive APIs for asynchronous systems.

mvn io.quarkus.platform:quarkus-maven-plugin:2.7.5.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=one-time-password \
    -Dextensions="redis-client,resteasy-jackson,resteasy-mutiny" \
    -DnoCode
cd one-time-password
Enter fullscreen mode Exit fullscreen mode

Use the following command to add the redis-client dependency to your pom.xml file:

./mvnw quarkus:add-extension -Dextensions="redis-client"
Enter fullscreen mode Exit fullscreen mode

Getting the Redis server details

Create an Aiven for Redis instance. For this exercise, any service plan will do. Once the Redis service is running, copy the Service URI from the Overview > Connection information tab.

Go to your favorite code editor and open the one-time-password project. Paste the Service URI to the src > main > resources > application.properties file:

quarkus.redis.hosts=[YOUR REDIS CONNECTION INFORMATION GOES HERE]
Enter fullscreen mode Exit fullscreen mode

If you're running Redis on your local machine, use the following settings:

quarkus.redis.hosts=redis://localhost:6379 
Enter fullscreen mode Exit fullscreen mode

Create the Otp POJO (Plain Old Java Object)

The Otp class creates a POJO (Plain Old Java Object) to hold the session_key and otp_value. The generateRandomOtp method generates a random value within a given range.

Create the src/main/java/org/acme/redis/Otp.java file and add the following:

package org.acme.redis;
import java.util.Random;

public class Otp {
    final int lowRange = 100000;
    final int highRange = 999999;
    public String session_key;
    public int otp_value;

    public Otp(String session_key) {
        this.session_key = session_key;
        this.otp_value = generateRandomOtp(lowRange, highRange);
    }

    public Otp() {}

    private static int generateRandomOtp(int low, int high) {

        // Generate random int value from $low to ($high - 1)
        return low + new Random().nextInt(high - low);
    }
}
Enter fullscreen mode Exit fullscreen mode

Create the Otp Service

You are going to create the OtpService which will play the role of a Redis client. This class will help you perform the GET, EXISTS, SETEX, SETNX, and TTL Redis commands. For documentation on these see the official Redis commands page.

Create the src/main/java/org/acme/redis/OtpService.java file and add the following:

package org.acme.redis;
import io.quarkus.redis.client.RedisClient;
import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
class OtpService {
    final int timeInSeconds = 20;

    @Inject
    RedisClient redisClient;

    public String getOtp(String session_key) {
        return redisClient.get(session_key).toString();
    }

    public void newOtp(String session_key) {
        Otp otp = new Otp(session_key);

        // SETNX will only create a key if it doesn't already exist
        // - so we won't overwrite an existing OTP value
        // Unfortunately SETNX can't set the TTL/expiration time
        if (redisClient.setnx(otp.session_key.toString(),
                String.valueOf(otp.otp_value)).toBoolean()) {

            // Only update TTL/expiration if the OTP value was set
            redisClient.setex(otp.session_key.toString(),
                String.valueOf(timeInSeconds),
                String.valueOf(otp.otp_value));
        }
    }

    public String getOtpTTL(String session_key) {
        return redisClient.ttl(session_key).toString();
    }

    public boolean keyExists(String session_key) {
        return redisClient.exists(Arrays.asList(session_key)).toBoolean();
    }
}
Enter fullscreen mode Exit fullscreen mode

Create the Otp Resource

Create the src/main/java/org/acme/redis/OtpResource.java file where you define the HTTP endpoints for your one-time-password service.

Observe the @Inject annotation to easily create the service instance and @Path("/otp") to indicate the creation of the HTTP endpoints. The same endpoint is used for both GET and POST calls. The great thing of using a framework like Quarkus is that without the annotations, you would have to write multiple lines of code to achieve the same outcome.

package org.acme.redis;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.jboss.resteasy.annotations.jaxrs.QueryParam;
import io.vertx.core.json.JsonObject;

@Path("/otp")
public class OtpResource {
    @Inject
    OtpService service;

    @GET
    public JsonObject getOtp(@QueryParam String session_key) {

        // If the key doesn't exist, return an error response rather than the usual
        // object response
        if (!service.keyExists(session_key)) {
            return errorResponse();
        }
        JsonObject result = new JsonObject();
        result.put("OTP: ", service.getOtp(session_key));
        result.put("TTL: ", service.getOtpTTL(session_key));
        return result;
    }

    JsonObject errorResponse() {
        JsonObject result = new JsonObject();
        result.put("Message: ", "The OTP key doesn't exist.");
        return result;
    }

    @POST
    public void newOtp(@QueryParam String session_key) {
        service.newOtp(session_key);
    }
}
Enter fullscreen mode Exit fullscreen mode

Run your new Quarkus app

From the root of your one-time-password project, execute the following command:

 ./mvnw quarkus:dev
Enter fullscreen mode Exit fullscreen mode

Navigate to http://localhost:8080/otp?session_key=[SOME_KEY], replacing [SOME_KEY] with any text. For your initial run, you'll receive an error response like this:

"Message: ": "The OTP key doesn't exist."
Enter fullscreen mode Exit fullscreen mode

This is because this key doesn't exist in Redis yet. From your terminal, make the following POST request to create your key:

curl --location --request POST 'http://localhost:8080/otp?session_key=[SOME_KEY]'
Enter fullscreen mode Exit fullscreen mode

Now reload your browser and you should receive a random 6-digit number as a one-time-password that expires in 20 seconds. If you wait more than 20 seconds, it will expire and you'll receive the error response again.

If you keep refreshing the page, you'll be getting the same OTP until the (time-to-live) TTL runs out.

You might also realize that you didn't have to do any server setup/handling for this application as Quarkus took care of that for you. Pretty cool, eh?

Further learning

In this blog, I provided you with a getting started experience with Redis and demoed a use case by building a simple one-time-password application using the Quarkus framework. The design and implementation of actual one-time-password software is far more complex than the simple application mentioned here; for instance, it would probably not want to give the same OTP to different users.

To learn more about Quarkus:

To learn more about Redis:

*Redis is a registered trademark of Redis Ltd. and the Redis box logo is a mark of Redis Ltd. Any rights therein are reserved to Redis Ltd. Any use by Aiven is for referential purposes only and does not indicate any sponsorship, endorsement or affiliation between Redis and Aiven. All product and service names used in this website are for identification purposes only and do not imply endorsement.

💖 💪 🙅 🚩
dewanahmed
Dewan Ahmed

Posted on August 10, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related