In this post, I am going to explain how to get the cache events by implementing CacheEventListener.
Supported event types
org.ehcache.event.EventType enum define below event types.
a. EVICTED : Represents a cache entry being evicted
b. EXPIRED : Represents a cache entry being expired
c. REMOVED : Represents a cache entry being removed
d. CREATED : Represents a cache entry being created
e. UPDATED : Represents a cache entry being updated
Supported event firing modes
org.ehcache.event.EventFiring enum defines below event firing modes.
a. ASYNCHRONOUS: Events will fire asynchronously. The mutating thread does not have to wait for firing to complete.
b. SYNCHRONOUS: Events will fire synchronously. The mutating thread will wait for firing to complete.
Supported event ordering modes
org.ehcache.event.EventOrdering define below event ordering modes.
a. UNORDERED: Events may be observed out of order.
b. ORDERED: Ordering of events is guaranteed on a per-key basis.
Let’s define custom cache event listener by implementing CacheEventListener interface.
public class MyCacheEnventListener implements CacheEventListener<Object, Object> {
	private static final Logger LOGGER = LoggerFactory.getLogger(MyCacheEnventListener.class);
	public void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {
		LOGGER.info("\n-----------------------------------------------");
		LOGGER.info("Event received");
		LOGGER.info("event type: {}", cacheEvent.getType());
		LOGGER.info("event key: {}", cacheEvent.getKey());
		LOGGER.info("old value: {}", cacheEvent.getOldValue());
		LOGGER.info("new value: {}", cacheEvent.getNewValue());
		LOGGER.info("-----------------------------------------------");
	}
}
Next configure this ‘MyCacheEnventListener’ in ehcache.xml file.
ehcache.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.ehcache.org/v3"
	xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
	xsi:schemaLocation="
            http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
            http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
	<cache alias="myEmployeeCache">
		<key-type>java.lang.Object</key-type>
		<value-type>java.lang.Object</value-type>
		<expiry>
			<ttl unit="seconds">10</ttl>
		</expiry>
		<listeners>
			<listener>
				<class>com.sample.app.listeners.MyCacheEnventListener</class>
				<event-firing-mode>SYNCHRONOUS</event-firing-mode>
				<event-ordering-mode>UNORDERED</event-ordering-mode>
				<events-to-fire-on>CREATED</events-to-fire-on>
				<events-to-fire-on>EXPIRED</events-to-fire-on>
			</listener>
		</listeners>
		<resources>
			<heap unit="entries">2</heap>
			<offheap unit="MB">10</offheap>
		</resources>
	</cache>
</config>
As you observe the definition of xml file, I set event-firing-mode to SYNCHRONOUS for the demo purpose, you can set this to 'ASYNCHRONOUS' in production.
Specify ehcache.xml file location in application.yml file.
spring:
  cache:
    jcache:
      config: classpath:ehcache.xml 
Follow below step-by-step procedure to build complete working application.
Step 1: Create new maven project ‘spring-cache-event-listener’.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>spring-cache-event-listener</artifactId>
	<version>1</version>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.6</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.cache</groupId>
			<artifactId>cache-api</artifactId>
		</dependency>
	</dependencies>
</project>
Step 3: Create ehcache.xml file under src/main/resources folder.
ehcache.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.ehcache.org/v3"
	xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
	xsi:schemaLocation="
            http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
            http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
	<cache alias="myEmployeeCache">
		<key-type>java.lang.Object</key-type>
		<value-type>java.lang.Object</value-type>
		<expiry>
			<ttl unit="seconds">10</ttl>
		</expiry>
		<listeners>
			<listener>
				<class>com.sample.app.listeners.MyCacheEnventListener</class>
				<event-firing-mode>SYNCHRONOUS</event-firing-mode>
				<event-ordering-mode>ORDERED</event-ordering-mode>
				<events-to-fire-on>EVICTED</events-to-fire-on>
				<events-to-fire-on>EXPIRED</events-to-fire-on>
				<events-to-fire-on>REMOVED</events-to-fire-on>
				<events-to-fire-on>CREATED</events-to-fire-on>
				<events-to-fire-on>UPDATED</events-to-fire-on>
			</listener>
		</listeners>
		<resources>
			<heap unit="entries">2</heap>
			<offheap unit="MB">10</offheap>
		</resources>
	</cache>
</config>
Step 4: Create application.yml file under src/main/resources folder.
application.yml
spring:
  cache:
    jcache:
      config: classpath:ehcache.xml
Step 5: Create Employee model class.
Employee.java
package com.sample.app.model;
import java.io.Serializable;
public class Employee implements Serializable{
	private int id;
	private String firstName;
	private String lastName;
	public Employee(int id, String firstName, String lastName) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";
	}
}
Step 6: Define EmployeeService class.
EmployeeService.java
package com.sample.app.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.sample.app.model.Employee;
@Service
@CacheConfig(cacheNames = "myEmployeeCache")
public class EmployeeService {
	private static List<Employee> emps = new ArrayList<>();
	static {
		emps.add(new Employee(1, "Ram", "Kota"));
		emps.add(new Employee(2, "Raj", "Majety"));
		emps.add(new Employee(3, "PTR", "Navakotla"));
		emps.add(new Employee(4, "Krishna", "Boppana"));
	}
	@Cacheable(key = "{#id}")
	public Employee getEmployeeById(int id) {
		System.out.println("getEmployeeById: Getting the informaiton from internal list");
		
		for (Employee emp : emps) {
			if (id == emp.getId()) {
				return emp;
			}
		}
		return null;
	}
	@Cacheable(key = "{#name}")
	public Employee getEmployeeByFirstName(String name) {
		System.out.println("getEmployeeByFirstName: Getting the informaiton from internal list");
		
		for (Employee emp : emps) {
			if (name.equals(emp.getFirstName())) {
				return emp;
			}
		}
		return null;
	}
}
Step 7: Define MyCacheEnventListener class.
MyCacheEnventListener.java
package com.sample.app.listeners;
import org.ehcache.event.CacheEvent;
import org.ehcache.event.CacheEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyCacheEnventListener implements CacheEventListener<Object, Object> {
	private static final Logger LOGGER = LoggerFactory.getLogger(MyCacheEnventListener.class);
	public void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {
		LOGGER.info("\n-----------------------------------------------");
		LOGGER.info("Event received");
		LOGGER.info("event type: {}", cacheEvent.getType());
		LOGGER.info("event key: {}", cacheEvent.getKey());
		LOGGER.info("old value: {}", cacheEvent.getOldValue());
		LOGGER.info("new value: {}", cacheEvent.getNewValue());
		LOGGER.info("-----------------------------------------------");
	}
}
Step 8: Define CacheConfig class.
CacheConfig.java
package com.sample.app.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
} 
Step 9: Define main application class.
App.java
package com.sample.app;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.sample.app.model.Employee;
import com.sample.app.service.EmployeeService;
@SpringBootApplication
public class App {
	private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
	@Autowired
	private EmployeeService empService;
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
	public void getEmployeeDetails() {
		System.out.println("\n");
		LOGGER.info("\nGetting employee details with id 1");
		Employee emp = empService.getEmployeeById(1);
		LOGGER.info("emp : {}", emp);
		System.out.println("\n");
		LOGGER.info("Getting employee details with name 'Krishna'");
		emp = empService.getEmployeeByFirstName("Krishna");
		LOGGER.info("emp : {}", emp);
	}
	@Bean
	public CommandLineRunner demo() {
		return (args) -> {
			getEmployeeDetails();
			LOGGER.info("Sleeping for 4 seconds");
			TimeUnit.SECONDS.sleep(4);
			getEmployeeDetails();
			LOGGER.info("Sleeping for 4 seconds");
			TimeUnit.SECONDS.sleep(4);
			getEmployeeDetails();
			LOGGER.info("Sleeping for 4 seconds");
			TimeUnit.SECONDS.sleep(4);
			getEmployeeDetails();
		};
	}
}
Total project structure looks like below.
Run App.java, you will see below messages in console.
Getting employee details with id 1 getEmployeeById: Getting the informaiton from internal list 2022-06-30 16:15:17.063 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: CREATED 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [1] 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: null 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:17.064 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:17.065 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:17.065 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with name 'Krishna' getEmployeeByFirstName: Getting the informaiton from internal list 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: CREATED 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [Krishna] 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: null 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:17.066 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:17.066 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:17.067 INFO 82133 --- [ main] com.sample.app.App : Sleeping for 4 seconds 2022-06-30 16:15:21.069 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with id 1 2022-06-30 16:15:21.077 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:21.078 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with name 'Krishna' 2022-06-30 16:15:21.078 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:21.078 INFO 82133 --- [ main] com.sample.app.App : Sleeping for 4 seconds 2022-06-30 16:15:25.079 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with id 1 2022-06-30 16:15:25.080 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:25.080 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with name 'Krishna' 2022-06-30 16:15:25.080 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:25.080 INFO 82133 --- [ main] com.sample.app.App : Sleeping for 4 seconds 2022-06-30 16:15:29.082 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with id 1 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: EXPIRED 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [1] 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: null 2022-06-30 16:15:29.085 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- getEmployeeById: Getting the informaiton from internal list 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: CREATED 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [1] 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: null 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:29.087 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.087 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=1, firstName=Ram, lastName=Kota] 2022-06-30 16:15:29.087 INFO 82133 --- [ main] com.sample.app.App : Getting employee details with name 'Krishna' 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: EXPIRED 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [Krishna] 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: null 2022-06-30 16:15:29.088 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- getEmployeeByFirstName: Getting the informaiton from internal list 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : Event received 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event type: CREATED 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : event key: [Krishna] 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : old value: null 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : new value: Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:29.089 INFO 82133 --- [e [_default_]-0] c.s.app.listeners.MyCacheEnventListener : ----------------------------------------------- 2022-06-30 16:15:29.089 INFO 82133 --- [ main] com.sample.app.App : emp : Employee [id=4, firstName=Krishna, lastName=Boppana] 2022-06-30 16:15:59.100 INFO 82133 --- [ionShutdownHook] org.ehcache.core.EhcacheManager : Cache 'myEmployeeCache' removed from EhcacheManager.
You can download complete working application from this link.
 

No comments:
Post a Comment