Monthly Archives: February 2018

Caching: How to use Redis Caching with Spring Boot

In this introductory post, we will show how to use Redis caching in a simple spring boot application. In subsequent posts, we will evaluate different factors of Redis caching. But for now, we will try to focus on the simple problem of providing caching to a rest service that provides companies-related data to the user interface. This data is in a database, but caching will help us improve the performance.

 

What you need

  • Java 8
  • MySQL Database
  • IntelliJ Editor
  • Gradle
  • Redis Server and Redis Desktop Manager

Spring Boot Based Rest Service

As part of this post, we will build a simple spring-boot based rest service. This rest service will provide data related to companies which will be stored in mysql database.

We will be using Gradle to build our dependencies in this project. Important dependencies for this project are spring-boot-starter, spring-boot-jpa and spring-boot-starter-data-redis With all the needed Gradle dependencies, our Gradle script will look like below:

buildscript {
  ext {
    springBootVersion = '1.5.10.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'com.betterjavacode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
  mavenCentral()
}

jar {
    manifest {
        attributes 'Main-Class':'com.betterjavacode.SpringAppCache.SpringAppCacheApplication'
    }
    baseName= 'SpringAppCache'
    version='0.0.1-SNAPSHOT'
}

dependencies {
  compile('org.springframework.boot:spring-boot-starter')
  compile('org.springframework.data:spring-data-jpa')
  compile('org.springframework.boot:spring-boot-starter-data-redis')
  compile('org.springframework.boot:spring-boot-starter-web')
        compile('org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final')
        compile('mysql:mysql-connector-java:5.1.6')
        compile('org.hibernate:hibernate-core:5.2.13.Final')   
        compile('org.aspectj:aspectjweaver:1.8.13')
  testCompile('org.springframework.boot:spring-boot-starter-test')
}

Let’s build a model class for the object Company which will look like below:

package com.betterjavacode.models;

import javax.persistence.*;
import java.io.Serializable;

@Entity(name="Company")
@Table(name="company")
public class Company implements Serializable
{
    public Company()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column(nullable=false)
    private String name;
    @Column(nullable=false)
    private String type;

    public Company(int id, String name, String type)
    {
        this.id = id;
        this.type = type;
        this.name = name;
    }

    public int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getType()
    {
        return type;
    }

    public void setType(String type)
    {
        this.type = type;
    }
}

We will not be showing any of the middle layer code which is mostly how the data is going to be built.

Our RestController will use an autowired CompanyManager to fetch company data from the database.

Before we build RestController, we will show the configuration that we have annotated in SpringAppCacheApplication main class.

package com.betterjavacode.SpringAppCache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.betterjavacode")
@EnableJpaRepositories(basePackages = "com.betterjavacode.repositories")
@EnableCaching
public class SpringAppCacheApplication
{
  public static void main(String[] args) {
    SpringApplication.run(SpringAppCacheApplication.class, args);
  }
}

Here you can see, we have enabled caching with annotation @EnableCaching.

Now in our RestController class CompanyController , this will show annotation of @Cachable that helps decide when to cache data for the incoming request. This annotation caches data that has been fetched for the request based on configuration.

package com.betterjavacode.resources;

import java.util.List;

import com.betterjavacode.interfaces.CompanyManager;
import com.betterjavacode.models.Company;
import org.hibernate.annotations.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;


import javax.websocket.server.PathParam;

@RestController
@RequestMapping(value="/cachedemo/v1")
public class CompanyController
{


    @Autowired
    public CompanyManager companyManager;


    @RequestMapping(value = "/companies", method= RequestMethod.GET,
    produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @Cacheable("companies")
    public List<Company> getAllCompanies()
    {
        return companyManager.getAllCompanies();
    }


    @RequestMapping(value = "/companies/{id}/", method = RequestMethod.GET, produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    @Cacheable(value = "company", key = "#id")
    public Company getCompany(@PathVariable("id") int id)
    {
        return companyManager.getCompany(id);
    }
}

Here is a controller, if you see we are caching the data coming from the database with annotation @Cacheable

To make sure data gets cached with Redis server, we will need certain properties where these annotations will help us to cache the data. The properties to configure Redis server are below:

#########################################################################################
## REDIS CACHE
#########################################################################################
spring.cache.type = redis
spring.redis.host = 127.0.0.1
spring.redis.port = 6379

Once you build the project and run it, we will be able to perform the REST requests to fetch data. If we perform the same requests multiple times, we will be able to see the data in redis.

Conclusion

In this post, we showed how to use redis-caching to cache the data for a spring boot based REST service. The code from this post is available to download github

 

One Tip to Avoid Bouncy Castle Error

java.lang.NoSuchMethodError: org.bouncycastle.math.ec.ECCurve$Fp – Issue

In this post, I will discuss one tip everyone can use to avoid bouncy castle error.

Problem

Recently I was working on building a SOAP webservice where we were using Apache CXF libraries along with Spring boot. We built the webservice, but when we were sending a SOAP request through the client, we kept getting following error:

Caused by: java.lang.NoSuchMethodError: org.bouncycastle.math.ec.ECCurve$Fp.<init>(Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;)V
  at org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertCurve(Unknown Source)
  at org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertPoint(Unknown Source)
  at org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey.<init>(Unknown Source)
  at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePublic(Unknown Source)
  at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
  at sun.security.ssl.HandshakeMessage$ECDH_ServerKeyExchange.<init>(HandshakeMessage.java:1075)
  at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:284)
  at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1026)
  at sun.security.ssl.Handshaker.process_record(Handshaker.java:961)
  at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
  at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
  at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
  at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
  at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:553)
  at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:412)
  at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:179)
  at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:328)
  at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:612)
  at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
  at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:884)
  at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
  at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
  at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
  at org.springframework.ws.transport.http.HttpComponentsConnection.onSendAfterWrite(HttpComponentsConnection.java:121)

A Simple tip to avoid this error

We tried different things to resolve this issue. We tried to exclude bouncycastle jars from cxf-rt-ws-security dependencies we were using as we were pulling them from other dependencies. But this error would still pop up intermittently. Eventually, I figured out the issue. We had to add this dependency of bouncycastle explicitly as below in our Gradle build file. That’s when the error went away.

compile (‘org.bouncycastle:bcprov-jdk15on:1.56’) 

Conclusion

In this post, I showed how to use this one tip to avoid the error caused by the bouncy castle. If you enjoyed this post, subscribe to my blog here.