Unable to refresh existing singleton bean in Spring boot application - java

I have a configuration defined in my spring boot applocation as follows:
#Configuration
public class RuleEngineConfiguration {
private final DatabaseRuleLoader databaseRuleLoader;
public RuleEngineConfiguration(
DatabaseRuleLoader databaseRuleLoader) {
this.databaseRuleLoader = databaseRuleLoader;
}
#Bean
public RuleEngineManager ruleEngine() {
return RuleEngineManagerFactory.getRuleEngineManager(this.databaseRuleLoader);
}
Now I would like to refresh RuleEngineManager bean in my spring boot application on creating/update of a row in a given table in DB with refresh function as defined below:
public void refresh() {
databaseRuleLoader.refresh(); <---THIS RELOADS ROWS FROM DB
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext
.getAutowireCapableBeanFactory();
RuleEngineManager ruleEngineManager = RuleEngineManagerFactory
.getRuleEngineManager(databaseRuleLoader);
registry.removeBeanDefinition("ruleEngine");
((SingletonBeanRegistry) registry).registerSingleton("ruleEngine", ruleEngineManager);
}
And in my application, where I need RuleEngineManager bean, I am getting the bean as follows:
((RuleEngineManager) applicationContext.getBean("ruleEngine"))
Even though the refresh function is getting executed every time, I am creating/updating any rows in DB, but, I am not seeing any changes. It seems the existing RuleEnginemanager bean is getting injected as a dependency. I am not able to figure out what I am missing here.
Could anyone please help here? Thanks.

What I was suggesting is to use a factory (design pattern)... for example in this way:
// you already have this class
public class RuleEngineManagerFactory{
private DatabaseRuleLoader databaseRuleLoader;
private RuleEngineManager ruleEngineManager;
public RuleEngineManagerFactory(DatabaseRuleLoader databaseRuleLoader) {
this.databaseRuleLoader = databaseRuleLoader;
}
public RuleEngineManager getRuleEngineManager(){
if(this.ruleEngineManager == null){
ruleEngineManager = new RuleEngineManager(this.databaseRuleLoader);
}
return this.ruleEngineManager;
}
public void refresh(){
ruleEngineManager = new RuleEngineManager(this.databaseRuleLoader);
}
}
In this way, you will inject RuleEngineManagerFactory wherever you need, and you can refresh the RuleEngineManager... so you will have to have a singleton for the factory of the manager, and not for the manager itself

Related

Store data into a bean from database

In spring boot, I want to take data from database and store it into a bean object. This needs to be done once (cache), and for further request bean object needs to be used, not to make database call again.
Example
/*
"DataFromDB" -> bean should have the values
*/
List<Users> uList = ApplicationContext.getBean("DataFromDB");
Is there any way to achieve this ?
Thank you
During your application boot, you can simply create a bean List<Users> uList and populate it with your required info.
Bean creation will happen once and whenever you want to reuse it, just get that bean. Spring will take care of the rest.
Somewhere in a config file, declare the bean:
#Component
public class InitialConfiguration {
#Bean
public List<Users> ulist() {
List<Users> uList = null;
// ulist = populate it from db
return uList;
}
}
Spring will create a ulist bean and store it. Now whenever you want to use it, you can simply autowire it into your variables:
#Service
public class SomeRandomClass {
#Autowire
List<Users> ulist;
public void performOperationOnUList() {
ulist.get(0); // use it
}
}
you can use a caching mechanism like ehcache
To add Ehcache to your application, here is the very basic approach you can follow.
Add Ehcache through your build tool. Here is an example for Gradle.
dependencies {
compile("org.hibernate:hibernate-ehcache:5.2.12.Final")
}
Add Ehcache configuration, Here I'm using annotation-based bean configuration.
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean
public EhCacheManagerFactoryBean ehCacheManagerFactory() {
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
#Bean
public EhCacheCacheManager ehCacheCacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactory().getObject());
cacheManager.setTransactionAware(true);
return cacheManager;
}
}
Define cache Regions. Here you can define individual caches for each repository you want to cache. create file named ehcache.xml and place in classpath.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ehcache>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<cache name="usercache" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true"/>
</ehcache>
Add Cachable annotation to transactional methods where you want to cache the DB operation.
#Cacheable(value = "userCache", key = "#p0")
public Company find(Long id) {
//db operation in here
}
From your problem statement, what I can understand is you want to cache objects from database, caching should be done only once (preferably on application start-up) and should be accessible anywhere in the context.
For this, you can store the data from db in a static final collection. The caching operation can be done on application startup via EventListener annotation.
#Component
public class DbCache {
public static final List<Object> dbCache = new ArrayList<>();
#EventListener(value = ApplicationReadyEvent.class)
private void initCache() {
List<Object> dataFromDB = // data fetched from DB
dbCache.addAll(dataFromDB);
}
public static List<Object> getDbCache() {
return dbCache;
}
}
You can use the DbCache.getDbCache() anywhere in your code now to fetch the data.

Java EE 7 Batch API : produce job scoped CDI Bean

I'm currently working on a Java EE 7 Batch API application, and I would like the lifecycle of one of my CDI Bean be related to the current job.
Actually I would like this bean to have a #JobScoped scope (but it doesn't exist in the API). Also I would like this bean to be injectable in any of my jobs class.
At first, I wanted to create my own #JobScoped scope, with a JobScopedContext, etc. But then I came with the idea that Batch API has the JobContext bean with a unique job id per bean.
So I wonder if I could manage the lifecycle of my job scoped bean with this JobContext.
For example, I would have my bean that I want to be job scoped :
#Alternative
public class JobScopedBean
{
private String m_value;
public String getValue()
{
return m_value;
}
public void setValue(String p_value)
{
m_value = p_value;
}
}
Then I would have the producer of this bean which will return the JobScopedBean associated to the current job (thanks to the JobContext which is unique per job)
public class ProducerJobScopedBean
{
#Inject
private JobContext m_jobContext;// this is the JobContext of Batch API
#Inject
private JobScopedManager m_manager;
#Produces
public JobScopedBean getObjectJobScoped() throws Exception
{
if (null == m_jobContext)
{
throw new Exception("Job Context not active");
}
return m_manager.get(m_jobContext.getExecutionId());
}
}
And the manager which holds the map of my JobScopedBean :
#ApplicationScoped
public class JobScopedManager
{
private final ConcurrentMap<Long, JobScopedBean> mapObjets = new ConcurrentHashMap<Long, JobScopedBean>();
public JobScopedBean get(final long jobId)
{
JobScopedBean returnObject = mapObjets.get(jobId);
if (null == returnObject)
{
final JobScopedBean ajout = new JobScopedBean();
returnObject = mapObjets.putIfAbsent(jobId, ajout);
if (null == returnObject)
{
returnObject = ajout;
}
}
return returnObject;
}
Of course, I will manage the destruction of the JobScopedBean at the end of each job (through a JobListener and a CDI Event).
Can you tell me if I'm wrong with this solution?
It looks correct to me but maybe I'm missing something?
May be there is a better way to handle this?
Thanks.
So it boils down to creating #Dependent scoped beans that are based on a job on creation. Works fine for beans with a lifespan shorter than the job, so for the standard scopes only #Dependent (#Request/#Session/#Converstion might be ok but do not apply here).
It will cause problems for other Scopes, especially #ApplicationScoped/#Singleton. If you inject the JobScopedBean into one of them. You might be (un)lucky to have an active Job when you need them the first time, but the beans will always be attached to that initial job (#Dependent scope beans are not pseudoscoped so will not create proxies to get the contextual instance)
If you want something like that, create a customscope.

DeltaSpike custom ConfigSource with CDI

I am trying to define a custom DeltaSpike ConfigSource. The custom config source will have the highest priority and check the database for the config parameter.
I have a ConfigParameter entity, that simply has a key and a value.
#Entity
#Cacheable
public class ConfigParameter ... {
private String key;
private String value;
}
I have a #Dependent DAO that finds all config parameters.
What I am trying to do now, is define a custom ConfigSource, that is able to get the config parameter from the database. Therefore, I want to inject my DAO in the ConfigSource. So basically something like
#ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {
#Inject
private ConfigParameterDao configParameterDao;
....
}
However, when registering the ConfigSource via META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource, the class will be instantiated and CDI will not work.
Is there any way to get CDI working in this case?
Thanks in advance, if you need any further information, please let me know.
The main problem is, that the ConfigSource gets instantiated very early on when the BeanManager is not available yet. Even the JNDI lookup does not work at that point in time. Thus, I need to delay the injection/lookup.
What I did now, is add a static boolean to my config source, that I set manually. We have a InitializerService that makes sure that the system is setup properly. At the end of the initialization process, I call allowInitialization() in order to tell the config source, that the bean is injectable now. Next time the ConfigSource is asked, it will be able to inject the bean using BeanProvider.injectFields.
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
#Inject
private ConfigParameterProvider configParameterProvider;
#Override
public int getOrdinal() {
return 500;
}
#Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
I have a request-scoped bean that holds all my config variables for type-safe access.
#RequestScoped
public class Configuration {
#Inject
#ConfigProperty(name = "myProperty")
private String myProperty;
#Inject
#ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
When injecting the Configuration class in a different bean, each ConfigProperty will be resolved. Since my custom DatabaseConfigSource has the highest ordinal (500), it will be used for property resolution first. If the property is not found, it will delegate the resolution to the next ConfigSource.
For each ConfigProperty the getPropertyValue function from the DatabaseConfigSource is called. Since I do not want to retreive the parameters from the database for each config property, I moved the config property resolution to a request-scoped bean.
#RequestScoped
public class ConfigParameterProvider {
#Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
#PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
I could sure change the request-scoped ConfigParameterProvider to ApplicationScoped. However, we have a multi-tenant setup and the parameters need to be resolved per request.
As you can see, this is a bit hacky, because we need to explicitly tell the ConfigSource, when it is allowed to be instantiated properly (inject the bean).
I would prefer a standarized solution from DeltaSpike for using CDI in a ConfigSource. If you have any idea on how to properly realise this, please let me know.
Even though this post has been answered already I'd like to suggest another possible solution for this problem.
I managed to load properties from my db service by creating an #Signleton #Startup EJB which extends the org.apache.deltaspike.core.impl.config.BaseConfigSource and injects my DAO as delegate which I then registered into the org.apache.deltaspike.core.api.config.ConfigResolver.
#Startup
#Singleton
public class DatabaseConfigSourceBean extends BaseConfigSource {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private #Inject PropertyService delegateService;
#PostConstruct
public void onStartup() {
ConfigResolver.addConfigSources(Collections.singletonList(this));
logger.info("Registered the DatabaseConfigSourceBean in the ConfigSourceProvider ...");
}
#Override
public Map<String, String> getProperties() {
return delegateService.getProperties();
}
#Override
public String getPropertyValue(String key) {
return delegateService.getPropertyValue(key);
}
#Override
public String getConfigName() {
return DatabaseConfigSourceBean.class.getSimpleName();
}
#Override
public boolean isScannable() {
return true;
}
}
I know that creating an EJB for this purpose basically produces a way too big overhead, but I think it's a bit of a cleaner solution instead of handling this problem by some marker booleans with static accessors ...
DS is using the java se spi mechanism for this which is not CD'Injectable'. One solution would be to use the BeanProvider to get hold of your DatabaseConfigSource and delegate operations to it.

Building a Spring ConfigurationRetriever to load beans from spring context file for a command line program. How to use #Autowired in this clas?

I am building a command-line program in java with the spring framework. Based on the arguments passed into the command-line program the relevant beans would need to be loaded and executed. So I rely on this ConfigurationRetriever class I've shown below. But I'm having to do a get bean everywhere in the ConfigurationRetriever (it works fine with calling getBean like I've shown in the code). Ideally I would like to use #Autowired. How can I do that?
Thank you in advance!
public class ConfigurationRetriever {
private ApplicationContext context;
public ConfigurationRetriever() {
context = new ClassPathXmlApplicationContext("beans-service.xml");
}
public ServiceNameRunProperties getSearchIndexServices(String service_name) {
ServiceNameRunPropertiesDao dao = (ServiceNameRunPropertiesDao) context
.getBean("serviceNameRunPropertiesDaoImpl");
ServiceNameRunProperties serviceNameRunProperties = new ServiceNameRunProperties();
serviceNameRunProperties = dao
.getServiceRunPropertiesByName(service_name);
return serviceNameRunProperties;
}
public String getQueryExecutorService() {
QueryExecutorService queryExecutorService = (QueryExecutorService) context
.getBean("queryExecutorImpl");
return queryExecutorService.executeQuery();
}
}

Spring - Replacing the bean property values with new Property File values

I have a property file and using Spring property place holder, I set values to the Spring beans. Now, this property file may be modified during the run time. Is there a way to refresh the properties of the Spring beans with this newly modified property value? Especially, I have many singleton beans? How can I refresh them with the new values? Is there already a solution to this or should it be custom coded? If it doesn't already exist, can someone please give the best approach to achieve this? Thanks!
PS: My application is a batch application. I use Spring based Quartz configuration to schedule the batches.
I'll leave this in for reference, but the updated answer is below the divider:
Well the ConfigurableApplicationContext interface contains a refresh() method, which should be what you want, but the question is: how to access that method. Whichever way you do it, you'll start with a bean that has a dependency of type ConfigurableApplicationContext:
private ConfigurableApplicationContext context;
#Autowired
public void setContext(ConfigurableApplicationContext ctx){
this.context = ctx;
}
Now the two basic options I'd suggest would be to either
use the Task Execution Framework and let your bean watch the property resources regularly, refreshing the ApplicationContext when it finds changes or
expose the bean to JMX, allowing you to manually trigger the refresh.
Referring to comments: since it seems impossible to refresh the entire context, an alternative strategy would be to create a properties factory bean and inject that into all other beans.
public class PropertiesFactoryBean implements FactoryBean<Properties>{
public void setPropertiesResource(Resource propertiesResource){
this.propertiesResource = propertiesResource;
}
private Properties value=null;
long lastChange = -1L;
private Resource propertiesResource;
#Override
public Properties getObject() throws Exception{
synchronized(this){
long resourceModification = propertiesResource.lastModified();
if(resourceModification != lastChange){
Properties newProps = new Properties();
InputStream is = propertiesResource.getInputStream();
try{
newProps.load(is);
} catch(IOException e){
throw e;
} finally{
IOUtils.closeQuietly(is);
}
value=newProps;
lastChange= resourceModification;
}
}
// you might want to return a defensive copy here
return value;
}
#Override
public Class<?> getObjectType(){
return Properties.class;
}
#Override
public boolean isSingleton(){
return false;
}
}
You could inject this properties bean into all your other beans, however, you would have to be careful to always use prototype scope. This is particularly tricky inside singleton beans, a solution can be found here.
If you don't want to inject lookup methods all over the place, you could also inject a PropertyProvider bean like this:
public class PropertiesProvider implements ApplicationContextAware{
private String propertyBeanName;
private ApplicationContext applicationContext;
public void setPropertyBeanName(final String propertyBeanName){
this.propertyBeanName = propertyBeanName;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException{
this.applicationContext = applicationContext;
}
public String getProperty(final String propertyName){
return ((Properties) applicationContext.getBean(propertyBeanName)).getProperty(propertyName);
}
}

Categories