I am tring to use EntryExpiredListener to do an action using another service once cache entry expired, but I couldn't add the listener to the cache if it is calling a service, and the service is calling any repositry in the system
The dependencies of some of the beans in the application context form a cycle:
filterChainExceptionHandler (field private org.springframework.web.servlet.HandlerExceptionResolver sb.practice.configs.security.filters.FilterChainExceptionHandler.resolver)
↓
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
↓
openEntityManagerInViewInterceptorConfigurer defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration.class]
↓
openEntityManagerInViewInterceptor defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration.class]
┌─────┐
| cacheManager defined in class path resource [org/springframework/boot/autoconfigure/cache/HazelcastCacheConfiguration.class]
↑ ↓
| cacheService defined in file [D:\Dev\Locals\pt-spring-boot\target\classes\sb\practice\services\impl\CacheService.class]
↑ ↓
| activeUserCacheService_Hazelcast defined in file [D:\Dev\Locals\pt-spring-boot\target\classes\sb\practice\services\impl\caches\ActiveUserCacheService_Hazelcast.class]
↑ ↓
| masterRoleRepository defined in sb.practice.repositories.masters.MasterRoleRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
↑ ↓
| (inner bean)#28f48a7c
└─────┘
This is my CacheListener:
@Slf4j
public class CacheListener implements EntryExpiredListener<String, String>{
public ActiveUserCacheService_Hazelcast activeUsersCache;
@Override
public void entryExpired(EntryEvent<String, String> event) {
log.info("entryEvicted. Cache: {}. Value: {}. \nEvent: {} ", event.getName(), event.getOldValue(), event);
}
}
CacheService:
@Service
@RequiredArgsConstructor
public class CacheService {
@Value("${hazelcast.cache.cluster.nodes}")
String[] clusteringNodes;
private final ActiveUserCacheService_Hazelcast activeUserCacheService_Hazelcast;
public <T> boolean containsKey(long key, String cacheName) {
IMap<Long, T> map = hazelcastInstance().getMap(cacheName);
return map.containsKey(key);
}
private static MapConfig mapConfig(String name, Integer timeToLive) {
MapConfig mapConfig = new MapConfig(name);
mapConfig.setTimeToLiveSeconds(timeToLive);
//
// EntryListenerConfig entryListenerConfig = new EntryListenerConfig();
// entryListenerConfig.setImplementation(new CacheListener());
// mapConfig.addEntryListenerConfig(entryListenerConfig);
final EvictionConfig evictionConfig = new EvictionConfig();
evictionConfig.setEvictionPolicy(EvictionPolicy.LRU);
evictionConfig.setMaxSizePolicy(MaxSizePolicy.PER_NODE);
evictionConfig.setSize(500);
mapConfig.setEvictionConfig(evictionConfig);
return mapConfig;
}
@Bean
HazelcastInstance hazelcastInstance() {
Config config = new Config();
CacheConstants.CACHES.forEach((name, timeToLive) -> config.addMapConfig(mapConfig(name, timeToLive)));
NetworkConfig network = config.getNetworkConfig();
network.setPort(5701).setPortCount(20);
network.setPortAutoIncrement(true);
JoinConfig join = network.getJoin();
join.getMulticastConfig().setEnabled(false);
join.getTcpIpConfig().setEnabled(true);
for (String clusteringNode : clusteringNodes) {
join.getTcpIpConfig().addMember(clusteringNode);
}
return Hazelcast.newHazelcastInstance(config);
}
}
The service is only containing an injection to masterRoleRepository whick cuase the issue. Is there a way to use the EventListener as a component and call a service to do logic?
Thank you in advance!
If you have a config like this you can create the listener as bean
@Configuration
@EnableAsync
public class HazelcastInstanceConfiguration {
public static final String MAP_NAME = "myMap";
@Bean
public Config hazelcastConfig() {
Config config = new Config();
// Configure the map
MapConfig mapConfig = config.getMapConfig(MAP_NAME);
mapConfig.setTimeToLiveSeconds(30);
EntryListenerConfig entryListenerConfig = new EntryListenerConfig();
entryListenerConfig.setImplementation(cacheListener());
mapConfig.addEntryListenerConfig(entryListenerConfig);
return config;
}
@Bean
CacheListener cacheListener() {
return new CacheListener();
}
}
In the listener inject the service with a setter
@Slf4j
@RequiredArgsConstructor
public class CacheListener implements EntryExpiredListener<String, String> {
private MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
@Override
public void entryExpired(EntryEvent<String, String> event) {
log.info("entryEvicted. Cache: {}. Value: {}. \nEvent: {} ",
event.getName(), event.getOldValue(), event);
myService.deleteFromRepository(event.getKey());
}
}
Make the service async in order not to block the hazelcast thread
@Service
@Slf4j
public class MyService {
@Async
public void deleteFromRepository(String key) {
log.info("deleteFromRepository is called for key : {}", key);
}
}