Infinispan + Spring Boot - Doesn't Cache - spring-boot

Trying to Implement infinispan base cache on spring boot using custom annotation:
#Aspect
#Configuration
#Slf4j
public class CacheAnnotationAspect {
Logger logger = LoggerFactory.getLogger(CacheAnnotationAspect.class);
#Autowired
InfinispanCacheService cacheService;
#Around("#annotation(com.calsoft.lib.cache.CacheResult)")
public Object cacheResult(ProceedingJoinPoint joinPoint)throws Throwable{
logger.info("Cache Operation :: CacheResult annotation advice invoked...");
CacheResult cacheResult=(CacheResult) getAnnotation(joinPoint,CacheResult.class);
CacheConfig cacheConfig=CacheConfig.from(cacheResult);
Object resultFromCache=getFromCache(joinPoint,cacheConfig);
if(resultFromCache!= null){
return resultFromCache;
}
Object result=joinPoint.proceed(joinPoint.getArgs());
storeInCache(result,joinPoint,cacheConfig);
return result;
}
private void storeInCache(Object result, ProceedingJoinPoint joinPoint, CacheConfig cacheConfig) {
if(result==null){
log.info("Cache op :: null values not cached");
return;
}
CacheService cacheService=getCacheService();
if(cacheService==null){
logger.info("Cache op :: CacheGet Failed : No CacheService available for use..");
}
DefaultCacheKey defaultCacheKey=getKey(joinPoint,cacheConfig);
String cacheName=getCacheName(cacheConfig.getCacheName(),joinPoint);
long lifeSpan=cacheConfig.getLifespan();
if(lifeSpan== CommonConstant.CACHE_DEFAULT_LIFE){
cacheService.put(cacheName,defaultCacheKey,result);
}else{
cacheService.put(cacheName,defaultCacheKey,result,lifeSpan,cacheConfig.getUnit());
}
logger.info("Cache Op :: Result cached :: {} ",cacheConfig);
}
private DefaultCacheKey getKey(ProceedingJoinPoint joinPoint, CacheConfig cacheConfig) {
List<Object> keys=new ArrayList<>();
Object target=joinPoint.getTarget();
MethodSignature methodSignature=MethodSignature.class.cast(joinPoint.getSignature());
Method method=methodSignature.getMethod();
Annotation[][] parameterAnnotations=method.getParameterAnnotations();
if(isEmpty(trim(cacheConfig.getKeyPrefix()))){
keys.add(target.getClass().getName());
keys.add(method.getName());
}else{
keys.add(cacheConfig.getKeyPrefix());
}
if(isCacheKeySpecified(parameterAnnotations)){
keys.addAll(getCacheKeys(joinPoint,parameterAnnotations));
}else{
keys.addAll(Arrays.asList(joinPoint.getArgs()));
}
return new DefaultCacheKey(keys.toArray());
}
private Collection<?> getCacheKeys(ProceedingJoinPoint joinPoint, Annotation[][] parameterAnnotations) {
Object[] args=joinPoint.getArgs();
List<Object> result=new ArrayList<>();
int i=0;
for(Annotation[] annotations: parameterAnnotations){
for(Annotation annotation: annotations){
if(annotation instanceof CacheKey){
result.add(args[i]);
break;
}
}
i++;
}
return result;
}
private boolean isCacheKeySpecified(Annotation[][] parameterAnnotations) {
for(Annotation[] annotations:parameterAnnotations){
for(Annotation annotation:annotations){
if(annotation instanceof CacheKey) {
return true;
}
}
}
return false;
}
private Object getFromCache(ProceedingJoinPoint joinPoint, CacheConfig cacheConfig) {
CacheService cacheService = getCacheService();
if (cacheService == null) {
logger.info("Cache op :: CacheGet Failed : No CacheService available for use..");
}
String cacheName=getCacheName(cacheConfig.getCacheName(),joinPoint);
DefaultCacheKey defaultCacheKey=getKey(joinPoint,cacheConfig);
return cacheService.get(cacheName,defaultCacheKey);
}
private String getCacheName(String cacheName, ProceedingJoinPoint joinPoint) {
boolean nameNotDefined=isEmpty(trim(cacheName));
if(nameNotDefined){
logger.error("Cache op :: Cache Name not defined");
}else{
CacheService cacheService=getCacheService();
if(!cacheService.cacheExists(cacheName)){
throw new RuntimeException("Cache with the name "+ cacheName+" does not exists");
}
}
return cacheName;
}
private CacheService getCacheService() {
return cacheService;
}
private Annotation getAnnotation(ProceedingJoinPoint joinPoint, Class type) {
MethodSignature methodSignature=MethodSignature.class.cast(joinPoint.getSignature());
Method method=methodSignature.getMethod();
return method.getAnnotation(type);
}
}
Above class << CacheAnnotationAspect >> is custom annotation #CacheResult Aspect implementation where it will first try to retrieve from cache and if not found will make actual dao call and then store in cache.
Below is the of the implementation of InfinispanCacheService which invokes cachemager to get/put cache entries.
#Service
public class InfinispanCacheService implements CacheService {
Logger logger = LoggerFactory.getLogger(InfinispanCacheService.class);
#Autowired
private DefaultCacheManagerWrapper cacheManagerWrapper;
private DefaultCacheManager infiniCacheManager;
private DefaultCacheManager initializeCacheManager(){
if(infiniCacheManager==null){
infiniCacheManager=cacheManagerWrapper.getCacheManager();
}
return infiniCacheManager;
}
#PostConstruct
public void start(){
logger.info("Initializing...InifinispanCacheService ....");
initializeCacheManager();
for(String cacheName : infiniCacheManager.getCacheNames()){
infiniCacheManager.startCache(cacheName);
}
}
#Override
public Object get(String cacheName, Object key) {
return getCache(cacheName).get(key);
}
#Override
public void put(String cacheName, Object key, Object value, long lifespan, TimeUnit unit) {
Cache cache=getCache(cacheName);
cache.put(key,value,lifespan,unit);
}
#Override
public void put(String cacheName, Object key, Object value) {
Cache cache=getCache(cacheName);
cache.put(key,value);
}
private Cache<Object,Object> getCache(String cacheName) {
Cache<Object,Object> cache;
if(isEmpty(trim(cacheName))){
cache=infiniCacheManager.getCache();
}else{
cache=infiniCacheManager.getCache(cacheName,false);
}
return cache;
}
#Override
public boolean cacheExists(String cacheName) {
return infiniCacheManager.cacheExists(cacheName);
}
}
<<<<<< The DefaultCacheManager below is one which during startup initializes the DefaultCacheManager by loading the infispan.xml configuration >>>>>
#Component
public class DefaultCacheManagerWrapper {
Logger logger = LoggerFactory.getLogger(DefaultCacheManagerWrapper.class);
// #Value("${classpath:spring.cache.infinispan.config}")
private String fileName="file:\\calsoft\\devlabs\\ecom2\\ecom-svc-admin\\src\\main\\resources\\infinispan.xml";
private DefaultCacheManager infiniCacheManager;
#PostConstruct
public void start(){
logger.info(" Received File Name :: {} ",fileName);
try{
URL fileUrl=new URL(fileName);
URLConnection urlConnection=fileUrl.openConnection();
InputStream inputStream=urlConnection.getInputStream();
infiniCacheManager=new DefaultCacheManager(inputStream);
infiniCacheManager.start();
logger.info("Cache Manager Initialized....");
}catch(MalformedURLException mue){
logger.error("Error creating file url ",mue.getMessage());
} catch (IOException e) {
logger.error("Error creating file url ",e.getMessage());
}
}
public void stop() { infiniCacheManager.stop();}
public DefaultCacheManager getCacheManager(){
return infiniCacheManager;
}
}
<<<< Infinispan.xml configuration >>
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:scehmaLocation="
urn:infinispan:config:7.2
http://www.infinispan.org/schemas/infinispan-config-7.2.xsd"
xmlns="urn:infinispan:config:7.2">
<cache-container default-cache="attributeset-cache">
<!-- template configurations -->
<local-cache-configuration name="local-template">
<expiration interval="10000" lifespan="50000" max-idle="50000"/>
</local-cache-configuration>
<!-- cache definitions -->
<local-cache name="attributeset-cache" configuration="local-template"/>
</cache-container>
</infinispan>
Annotation at controller level:
#CacheResult(cacheName= CommonConstant.ATTRIBUTE_SET_CACHE,lifespan=10,unit = TimeUnit.MINUTES)
#GetMapping("/eavattributeset")
public List<EavAttributeSet> fetchAllAttributes() {
return eavAttributeService.fetchAllEavattributesets();
}
<< EavAttributeService >>
#Service
public class EavAttributeService {
Logger logger = LoggerFactory.getLogger(EavAttributeService.class);
#Autowired
private EavAttributeJpaRepository eavAttributeJpaRepository;
#Autowired
EavAttributeSetJpaRepository eavAttributeSetJpaRepository;
public List<EavAttributeSet> fetchAllEavattributesets() {
return eavAttributeSetJpaRepository.findAll();
}
}
<< CacheConfig >>
#Data
#Slf4j
#AllArgsConstructor
#NoArgsConstructor
public class CacheConfig {
private String cacheName;
private long lifespan;
private TimeUnit unit;
private String keyPrefix;
public static CacheConfig from(CacheResult cacheResult) {
return new CacheConfig(cacheResult.cacheName(), cacheResult.lifespan(), cacheResult.unit(), cacheResult.keyPrefix());
}
}
Issue : The data is not getting cache, Wherever #CacheResult annotation is used the CacheAnnotationAspect is getting invoked and the check for data also happens in cache but when it tries to store the data in cache it does not cache and every subsequent call of this method does not return any data.

It works fine when i try with below configuration on infinispan.xml.
<expiration lifespan="50000"/>
Can you try with the above config and see if it using the cached data.
I guess it could be the max-idle timeout (10 milliseconds) which could be issue.

Related

AspectJ not executing on Junit methods

Using AOP, I am trying to log test method execution time, but nothing happens when I run a test method.
I've tried to change regex in my pointcut but doesen't seem to be working.
My aspect class:
#Aspect
#Component
public class LoggableAspect {
#Pointcut("execution(public * com.mozzartbet.*.*.*Test.*(..))")
public void publicTestMethod() {}
#Around("publicTestMethod() && #annotation(loggable)")
public Object logTestExecutionTime(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
long t1 = System.currentTimeMillis();
Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName());
StringBuilder prefix = new StringBuilder(joinPoint.getSignature().getName()).append("()");
Object result = null;
try {
if (loggable.detail()) {
prefix.append(": ").append(Joiner.on(",").join(joinPoint.getArgs()));
}
result = joinPoint.proceed();
return result;
} finally {
long t2 = System.currentTimeMillis();
if (loggable.detail()) {
prefix.append(" -> ").append(result);
}
logger.info("{} took {} ms", prefix, t2 - t1);
}
}
}
My test class:
package com.mozzartbet.gameservice.services.impl;
public class PlayerServiceImplTest extends BaseServiceTest {
#Autowired
private PlayerService playerService;
#Test
#Loggable(detail = true)
public void testInsert() {
assertThat(playerService.insert(Player.builder().id("foo").build()), is(1));
}
}
Annotation:
#Retention(RUNTIME)
#Target(METHOD)
public #interface Loggable {
boolean detail() default false;
}
PlayerService insert method
#Override
public int insert(Player player) {
try {
return playerDao.insert(player);
} catch (DuplicateKeyException e) {
throw new PlayerException(PlayerExceptionCode.DUPLICATED_PLAYER_ID, "ID: %s is duplicated!", player.getId());
}
}
Dao insert method:
#Override
public int insert(Player player) {
return playerMapper.insert(player);
}
I am inserting with mybatis.
Your #annotation definition is incorrect.
You should specify the qualified package name of annotation.
#Around("publicTestMethod() && #annotation(your_package.Loggable)")

Spring Boot class cast exception in PostConstruct method

I am running a Spring Boot application with a PostConstruct method to populate a POJO before application initialization. This is to ensure that the database isn't hit by multiple requests to get the POJO content after it starts running.
I'm able to pull the data from Oracle database through Hibernate query and store it in my POJO. The problem arises when I try to access the stored data. The dataset contains a list of objects that contain strings and numbers. Just trying to print the description of the object at the top of the list raises a class cast exception. How should I mitigate this issue?
#Autowired
private TaskDescrBean taskBean;
#PostConstruct
public void loadDescriptions() {
TaskDataLoader taskData = new TaskDataLoader(taskBean.acquireDataSourceParams());
List<TaskDescription> taskList = tdf.getTaskDescription();
taskBean.setTaskDescriptionList(taskList);
System.out.println("Task description size: " + taskBean.getTaskDescriptionList().get(0).getTaskDescription());
}
My POJO class:
#Component
public class TaskDescrBean implements ApplicationContextAware {
#Resource
private Environment environment;
protected List<TaskDescription> taskDescriptionList;
public Properties acquireDataSourceParams() {
Properties dataSource = new Properties();
dataSource.setProperty("hibernate.connection.driver_class", environment.getProperty("spring.datasource.driver-class-name"));
dataSource.setProperty("hibernate.connection.url", environment.getProperty("spring.datasource.url"));
dataSource.setProperty("hibernate.connection.username", environment.getProperty("spring.datasource.username"));
dataSource.setProperty("hibernate.connection.password", environment.getProperty("spring.datasource.password"));
return dataSource;
}
public List<TaskDescription> getTaskDescriptionList() {
return taskDescriptionList;
}
public void setTaskDescriptionList(List<TaskDescription> taskDescriptionList) {
this.taskDescriptionList = taskDescriptionList;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
My DAO class:
public class TaskDataLoader {
private Session session;
private SessionFactory sessionFactory;
public TaskDataLoader(Properties connectionProperties) {
Configuration config = new Configuration().setProperties(connectionProperties);
config.addAnnotatedClass(TaskDescription.class);
sessionFactory = config.buildSessionFactory();
}
#SuppressWarnings("unchecked")
public List<TaskDescription> getTaskDescription() {
List<TaskDescription> taskList = null;
session = sessionFactory.openSession();
try {
String description = "from TaskDescription des";
Query taskDescriptionQuery = session.createQuery(description);
taskList = taskDescriptionQuery.list();
System.out.println("Task description fetched. " + taskList.getClass());
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
return taskList;
}
TaskDescription Entity:
#Entity
#Table(name="TASK_DESCRIPTION")
#JsonIgnoreProperties
public class TaskDescription implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="TASK_DESCRIPTION_ID")
private Long taskDescriptionId;
#Column(name="TASK_DESCRIPTION")
private String taskDescription;
public Long getTaskDescriptionId() {
return taskDescriptionId;
}
public void setTaskDescriptionId(Long taskDescriptionId) {
this.taskDescriptionId = taskDescriptionId;
}
public String getTaskDescription() {
return taskDescription;
}
public void setTaskDescription(String taskDescription) {
this.taskDescription = taskDescription;
}
}
StackTrace
Instead of sending the List in the return statement, I transformed it into a JSON object and sent its String representation which I mapped back to the Object after transforming it using mapper.readValue()

#RefreshScope annotated Bean registered through BeanDefinitionRegistryPostProcessor not getting refreshed on Cloud Config changes

I've a BeanDefinitionRegistryPostProcessor class that registers beans dynamically. Sometimes, the beans being registered have the Spring Cloud annotation #RefreshScope.
However, when the cloud configuration Environment is changed, such beans are not being refreshed. Upon debugging, the appropriate application events are triggered, however, the dynamic beans don't get reinstantiated. Need some help around this. Below is my code:
TestDynaProps:
public class TestDynaProps {
private String prop;
private String value;
public String getProp() {
return prop;
}
public void setProp(String prop) {
this.prop = prop;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("TestDynaProps [prop=").append(prop).append(", value=").append(value).append("]");
return builder.toString();
}
}
TestDynaPropConsumer:
#RefreshScope
public class TestDynaPropConsumer {
private TestDynaProps props;
public void setProps(TestDynaProps props) {
this.props = props;
}
#PostConstruct
public void init() {
System.out.println("Init props : " + props);
}
public String getVal() {
return props.getValue();
}
}
BeanDefinitionRegistryPostProcessor:
public class PropertyBasedDynamicBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private ConfigurableEnvironment environment;
private final Class<?> propertyConfigurationClass;
private final String propertyBeanNamePrefix;
private final String propertyKeysPropertyName;
private Class<?> propertyConsumerBean;
private String consumerBeanNamePrefix;
private List<String> dynaBeans;
public PropertyBasedDynamicBeanDefinitionRegistrar(Class<?> propertyConfigurationClass,
String propertyBeanNamePrefix, String propertyKeysPropertyName) {
this.propertyConfigurationClass = propertyConfigurationClass;
this.propertyBeanNamePrefix = propertyBeanNamePrefix;
this.propertyKeysPropertyName = propertyKeysPropertyName;
dynaBeans = new ArrayList<>();
}
public void setPropertyConsumerBean(Class<?> propertyConsumerBean, String consumerBeanNamePrefix) {
this.propertyConsumerBean = propertyConsumerBean;
this.consumerBeanNamePrefix = consumerBeanNamePrefix;
}
#Override
public void setEnvironment(Environment environment) {
this.environment = (ConfigurableEnvironment) environment;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefRegistry) throws BeansException {
if (environment == null) {
throw new BeanCreationException("Environment must be set to initialize dyna bean");
}
String[] keys = getPropertyKeys();
Map<String, String> propertyKeyBeanNameMapping = new HashMap<>();
for (String k : keys) {
String trimmedKey = k.trim();
String propBeanName = getPropertyBeanName(trimmedKey);
registerPropertyBean(beanDefRegistry, trimmedKey, propBeanName);
propertyKeyBeanNameMapping.put(trimmedKey, propBeanName);
}
if (propertyConsumerBean != null) {
String beanPropertyFieldName = getConsumerBeanPropertyVariable();
for (Map.Entry<String, String> prop : propertyKeyBeanNameMapping.entrySet()) {
registerConsumerBean(beanDefRegistry, prop.getKey(), prop.getValue(), beanPropertyFieldName);
}
}
}
private void registerConsumerBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName, String beanPropertyFieldName) {
String consumerBeanName = getConsumerBeanName(trimmedKey);
AbstractBeanDefinition consumerDefinition = preparePropertyConsumerBeanDefinition(propBeanName, beanPropertyFieldName);
beanDefRegistry.registerBeanDefinition(consumerBeanName, consumerDefinition);
dynaBeans.add(consumerBeanName);
}
private void registerPropertyBean(BeanDefinitionRegistry beanDefRegistry, String trimmedKey, String propBeanName) {
AbstractBeanDefinition propertyBeanDefinition = preparePropertyBeanDefinition(trimmedKey);
beanDefRegistry.registerBeanDefinition(propBeanName, propertyBeanDefinition);
dynaBeans.add(propBeanName);
}
private String getConsumerBeanPropertyVariable() throws IllegalArgumentException {
Field[] beanFields = propertyConsumerBean.getDeclaredFields();
for (Field bField : beanFields) {
if (bField.getType().equals(propertyConfigurationClass)) {
return bField.getName();
}
}
throw new BeanCreationException(String.format("Could not find property of type %s in bean class %s",
propertyConfigurationClass.getName(), propertyConsumerBean.getName()));
}
private AbstractBeanDefinition preparePropertyBeanDefinition(String trimmedKey) {
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(PropertiesConfigurationFactory.class);
bdb.addConstructorArgValue(propertyConfigurationClass);
bdb.addPropertyValue("propertySources", environment.getPropertySources());
bdb.addPropertyValue("conversionService", environment.getConversionService());
bdb.addPropertyValue("targetName", trimmedKey);
return bdb.getBeanDefinition();
}
private AbstractBeanDefinition preparePropertyConsumerBeanDefinition(String propBeanName, String beanPropertyFieldName) {
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(propertyConsumerBean);
bdb.addPropertyReference(beanPropertyFieldName, propBeanName);
return bdb.getBeanDefinition();
}
private String getPropertyBeanName(String trimmedKey) {
return propertyBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1);
}
private String getConsumerBeanName(String trimmedKey) {
return consumerBeanNamePrefix + trimmedKey.substring(0, 1).toUpperCase() + trimmedKey.substring(1);
}
private String[] getPropertyKeys() {
String keysProp = environment.getProperty(propertyKeysPropertyName);
return keysProp.split(",");
}
The Config class:
#Configuration
public class DynaPropsConfig {
#Bean
public PropertyBasedDynamicBeanDefinitionRegistrar dynaRegistrar() {
PropertyBasedDynamicBeanDefinitionRegistrar registrar = new PropertyBasedDynamicBeanDefinitionRegistrar(TestDynaProps.class, "testDynaProp", "dyna.props");
registrar.setPropertyConsumerBean(TestDynaPropConsumer.class, "testDynaPropsConsumer");
return registrar;
}
}
Application.java
#SpringBootApplication
#EnableDiscoveryClient
#EnableScheduling
public class Application extends SpringBootServletInitializer {
private static Class<Application> applicationClass = Application.class;
public static void main(String[] args) {
SpringApplication sa = new SpringApplication(applicationClass);
sa.run(args);
}
}
And, my bootstrap.properties:
spring.cloud.consul.enabled=true
spring.cloud.consul.config.enabled=true
spring.cloud.consul.config.format=PROPERTIES
spring.cloud.consul.config.watch.delay=15000
spring.cloud.discovery.client.health-indicator.enabled=false
spring.cloud.discovery.client.composite-indicator.enabled=false
application.properties
dyna.props=d1,d2
d1.prop=d1prop
d1.value=d1value
d2.prop=d2prop
d2.value=d2value
Here are some guesses:
1) Perhaps the #RefreshScope metadata is not being passed to your metadata for the bean definition. Call setScope()?
2) The RefreshScope is actually implemented by https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java, which itself implements BeanDefinitionRegistryPostProcessor. Perhaps the ordering of these two post processors is issue.
Just guesses.
We finally resolved this by appending the #RefreshScope annotation on the proposed dynamic bean classes using ByteBuddy and then, adding them to Spring Context using Bean Definition Post Processor.
The Post Processor is added to spring.factories so that it loads before any other dynamic bean dependent beans.

spring boot mybatis with redis as cache

I'm using Spring Boot with Mybatis, and I noticed that everytime I fetch some data with the same query, it will connect to db and query without using cache. So I search the solution for Mybatis with Redis, but cannot find one. All the answer is for Spring and xml configuration file, and I think it's better to use annotation. So, how to configure Redis as Cache to Mybatis in Spring Boot.
here is one of my solution, it just doesn't work.
MybatisRedisCache:
public class MybatisRedisCache implements Cache {
private static Logger logger = Logger.getLogger(MybatisRedisCache.class);
private Jedis redisClient = createClient();
/** The ReadWriteLock. */
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private String id;
public MybatisRedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
logger.debug("MybatisRedisCache:id=" + id);
this.id = id;
}
#Override
public String getId() {
return this.id;
}
#Override
public int getSize() {
return Integer.valueOf(redisClient.dbSize().toString());
}
#Override
public void putObject(Object key, Object value) {
logger.debug("putObject:" + key + "=" + value);
redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
}
#Override
public Object getObject(Object key) {
Object value = SerializeUtil.unserialize(redisClient.get(SerializeUtil.serialize(key.toString())));
logger.debug("getObject:" + key + "=" + value);
return value;
}
#Override
public Object removeObject(Object key) {
return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
}
#Override
public void clear() {
redisClient.flushDB();
}
#Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
protected static Jedis createClient() {
try {
JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1");
return pool.getResource();
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException("connect failed");
}
}
And here is LoggingRedisCache
public class LoggingRedisCache extends LoggingCache {
public LoggingRedisCache(String id) {
super(new MybatisRedisCache(id));
}
}

Generate static map from database using a singleton class also using spring configuration #Autowired

I need to create an unmodifiable map generated from data obtained by querying a database. How, or can I, or is there a better way to do this using spring annotations?
I ran into a problem when creating a singleton for my Regions class and then trying to #Autowire in a RegionService to grab the object from the DAO. The problem is that spring can't instantiate the RegionService because it needs to instantiate the static singleton class Regions which needs to get data from the database as shown below in the constructor.
Please see me classes below (I've removed multiple unneeded methods that don't pertain to this question):
public final class Region {
private static final String DEFAULT_SEPERATOR = "-";
private final Integer key;
private final String description;
public Region(Integer pKey, String pDescription) {
this.key = pKey;
this.description = pDescription;
}
public Integer getKey() {
return this.key;
}
public String getValue() {
return this.description;
}
}
Here is my singleton:
public final class Regions {
private static Regions regionsInstance = null;
#Autowired
private RegionService regionService;
static Map<Integer, Region> regions;
private Regions() {
final Map<Integer, Region> tempRegions = new HashMap<Integer, Region>();
for (final Region region : this.regionService.retrieveAll()) {
tempRegions.put(region.getKey(), region);
}
regions = Collections.unmodifiableMap(tempRegions);
}
public static synchronized Regions getRegionsInstance() {
if (regionsInstance == null) {
regionsInstance = new Regions();
}
return regionsInstance;
}
public Region getRegion(final Integer pKey) {
return regions.get(pKey);
}
public List<Region> getRegions() {
return (List<Region>) regions.values();
}
}
My DAO and Service are just interfaces, no need to post those, here are my Impls:
#Service
public class RegionServiceImpl implements RegionService {
#Autowired
private RegionDAO regionDao;
#Override
public List<Region> retrieveAll() {
return this.regionDao.retrieveAll();
}
}
My DAOImpl (tested and works, just posting to give you the full picture):
#Repository
public class RegionDAOImpl implements RegionDAO {
private static final String SQL_RETRIEVE_REGIONS = "some random SQL";
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public List<Region> retrieveAll() {
try {
return this.jdbcTemplate.query(SQL_RETRIEVE_REGIONS, new ResultSetExtractor<List<Region>>() {
#Override
public List<Region> extractData(ResultSet rs) throws SQLException, DataAccessException {
return RegionDAOImpl.this.mapRegionData(rs);
}
});
} catch (final DataAccessException dae) {
throw new DaoException("Could not retrieve regionList from database. " + dae);
}
}
protected final List<Region> mapRegionData(ResultSet rs) throws SQLException {
final List<Region> regionList = new ArrayList<Region>();
while (rs.next()) {
regionList.add(new Region(rs.getInt("REGION_CD"), rs.getString("REGION_TXT")));
}
return Collections.unmodifiableList(regionList);
}
}
Then I run my test(I took out unneeded crap):
#..annotated with things you don't need to know
public class RetrieveRegionsTest {
#Autowired
private Regions r;
#Test
public void getAndLogRegion() {
final List<Region> regionDescriptions = new ArrayList<Region>(this.r.getRegions());
for (final Region region : regionDescriptions) {
LOGGER.info(region.getValue());
}
}
Yes my configuration and classpaths are set up properly. I can get this to work other ways, just not by accessing the Regions singleton which is what I want. Now I know I could take off the #Autowired on the RegionService in my Regions singleton and just create a new instance of RegionService, but that would defeat the purpose of springs #Autowired feature.
Any thoughts, ideas, comments?

Resources