Injecting externalized value from properties file by annotation into Spring - spring

This method doesn't work
How can I read my properties file and inject my variable in my Scheduled like:
#Scheduled(fixedRateString ="${frequence.move}")
#Configuration
#PropertySource( "resources.properties")
public class Scheduler {
private static ApplicationContext applicationContext=new AnnotationConfigApplicationContext(
Scheduler.class);
#Autowired
public Environment envi;
public static final String time=applicationContext.getBean(Environment.class).getProperty("frequence.move";
#Scheduled(fixedRateString ="${frequence.move}")
public void doTask() {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(BatchConfigEMS.class);
JobLauncher launcher = (JobLauncher) applicationContext.getBean(JobLauncher.class);
Job job = (Job) applicationContext.getBean(Job.class);
try {
test();
launcher.run(job, new JobParameters());
} catch (JobExecutionAlreadyRunningException e) {
e.printStackTrace();
} catch (JobRestartException e) {
e.printStackTrace();
} catch (JobInstanceAlreadyCompleteException e) {
e.printStackTrace();
} catch (JobParametersInvalidException e) {
e.printStackTrace();
}
}

Your code is flawed in a couple of ways. Basically as soon as you feel the need to create an instance of an ApplicationContext you should stop, think and act. As generally that is a sign you are doing things wrong.
To get beans use auto wiring, wire the beans into the classes as they need.
First make your Scheduler a #Component instead of a #Configuration and auto wire the needed beans.
#Component
public class Scheduler {
#Autowired
private JobLauncher launcher;
#Autowired
private Job job;
#Scheduled(fixedRateString ="${frequence.move}")
public void doTask() throws JobExecutionException {
test();
launcher.run(job, new JobParameters());
}
}
In your BatchConfigEMS add the #PropertySource and make sure you have a public static bean of the type PropertySourcesPlaceholderConfigurer.
#Configuration
#PropertySource( "resources.properties")
public class BatchConfigEMS {
#Bean
public static PropertySourcesPlaceholderConfigurer configurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Assuming you have a web application load the configuration ones and everything else should be auto wired. The placeholders should get replaced because of the added configurer.

this is my file (resource.properties)
fournisseur.key =false
produit.key = true
frequence.move = 5000
frequence.delete = 5000
i want just take this key[frequence.move] from my file properties
and use it here :
#Scheduled(fixedRateString = "${frequence.move}")
public void doTask() {
all the code here it work
}
I write what you advice me and that doesn't work

Related

ThreadPoolExecutor and Spring Async

Initially I was using regular java Multithreading using the 'implements' method. However #Autowired does not work when a class is created with new in Spring, so I am trying to change it to using Spring'sAsync method. This is what I have so far. How would I go about adding the threads to the ThreadPoolExecutor?
The class that should create the threads
#Component
public class ScheduledCountyScraper {
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
// #Scheduled(cron = "0 0 */3 * * *")
#EventListener(ApplicationReadyEvent.class)
public void scrapeCountyLinks() {
System.out.println("Scrape county links ran!");
try {
List<String> stateLinks = stateScrapeQueueRepository.getStatesLinks(website);
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
//what to do here?
executor.shutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("---------------------");
}
}
}
The Async class
#Component
#EnableAsync
public class CountyScraper {
volatile private String stateLink;
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
public CountyScraper() {
}
public CountyScraper(String stateLink) {
this.stateLink = stateLink;
}
#Async("countyScraper")
public void run() {
try {
// other code
stateScrapeQueueRepository.updateScrapeTimestamp(stateLink);
countyScrapeRepository.insertCountyLinks(countyLinks, website);
} catch (Exception e) {
e.printStackTrace();
}
}
}
By default Spring uses a SimpleAsyncTaskExecutor to execute async methods. This will by default spawn a new thread for every operation.
To define your own executor for use with async tasks, create a bean that implements the TaskExecutor interface or an Executor bean named "taskExecutor".
If you'd like to have your own custom executor just for this component, you can implement AsyncConfigurer and provide your own executor service:
#Override
public Executor getAsyncExecutor() {
return MY_EXECUTOR;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return MY_EXCEPTION_HANDLER;
}

Spring Autowired Shared Queue NullPointerException

I'm using Spring for the first time and am trying to implement a shared queue wherein a Kafka listener puts messages on the shared queue, and a ThreadManager that will eventually do something multithreaded with the items it takes off the shared queue. Here is my current implementation:
The Listener:
#Component
public class Listener {
#Autowired
private QueueConfig queueConfig;
private ExecutorService executorService;
private List<Future> futuresThread1 = new ArrayList<>();
public Listener() {
Properties appProps = new AppProperties().get();
this.executorService = Executors.newFixedThreadPool(Integer.parseInt(appProps.getProperty("listenerThreads")));
}
//TODO: how can I pass an approp into this annotation?
#KafkaListener(id = "id0", topics = "bose.cdp.ingest.marge.boseaccount.normalized")
public void listener(ConsumerRecord<?, ?> record) throws InterruptedException, ExecutionException
{
futuresThread1.add(executorService.submit(new Runnable() {
#Override public void run() {
try{
queueConfig.blockingQueue().put(record);
// System.out.println(queueConfig.blockingQueue().take());
} catch (Exception e){
System.out.print(e.toString());
}
}
}));
}
}
The Queue:
#Configuration
public class QueueConfig {
private Properties appProps = new AppProperties().get();
#Bean
public BlockingQueue<ConsumerRecord> blockingQueue() {
return new ArrayBlockingQueue<>(
Integer.parseInt(appProps.getProperty("blockingQueueSize"))
);
}
}
The ThreadManager:
#Component
public class ThreadManager {
#Autowired
private QueueConfig queueConfig;
private int threads;
public ThreadManager() {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
queueConfig.blockingQueue().take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Lastly, the main thread where everything is started from:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
ThreadManager threadManager = new ThreadManager();
try{
threadManager.run();
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
The problem
I can tell when running this in the debugger that the Listener is adding things to the queue. When the ThreadManager takes off the shared queue, it tells me the queue is null and I get an NPE. It seems like autowiring isn't working to connect the queue the listener is using to the ThreadManager. Any help appreciated.
This is the problem:
ThreadManager threadManager = new ThreadManager();
Since you are creating the instance manually, you cannot use the DI provided by Spring.
One simple solution is implement a CommandLineRunner, that will be executed after the complete SourceAccountListenerApp initialization:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
}
// Create the CommandLineRunner Bean and inject ThreadManager
#Bean
CommandLineRunner runner(ThreadManager manager){
return args -> {
manager.run();
};
}
}
You use SpringĀ“s programatic, so called 'JavaConfig', way of setting up Spring beans (classes annotated with #Configuration with methods annotated with #Bean). Usually at application startup Spring will call those #Bean methods under the hood and register them in it's application context (if scope is singleton - the default - this will happen only once!). No need to call those #Bean methods anywhere in your code directly... you must not, otherwise you will get a separate, fresh instance that possibly is not fully configured!
Instead, you need to inject the BlockingQueue<ConsumerRecord> that you 'configured' in your QueueConfig.blockingQueue() method into your ThreadManager. Since the queue seems to be a mandatory dependency for the ThreadManager to work, I'd let Spring inject it via constructor:
#Component
public class ThreadManager {
private int threads;
// add instance var for queue...
private BlockingQueue<ConsumerRecord> blockingQueue;
// you could add #Autowired annotation to BlockingQueue param,
// but I believe it's not mandatory...
public ThreadManager(BlockingQueue<ConsumerRecord> blockingQueue) {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
this.blockingQueue = blockingQueue;
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
this.blockingQueue.take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Just to clarify one more thing: by default the method name of a #Bean method is used by Spring to assign this bean a unique ID (method name == bean id). So your method is called blockingQueue, means your BlockingQueue<ConsumerRecord> instance will also be registered with id blockingQueue in application context. The new constructor parameter is also named blockingQueue and it's type matches BlockingQueue<ConsumerRecord>. Simplified, that's one way Spring looks up and injects/wires dependencies.

Spring not injecting a bean into thread

1.How to inject a spring bean into thread
2.How to start a thread inside spring bean.
here is my code.
MyThread.java
#Component
public class MyThread implements Runnable {
#Autowired
ApplicationContext applicationContext;
#Autowired
SessionFactory sessionFactory;
public void run() {
while (true) {
System.out.println("Inside run()");
try {
System.out.println("SessionFactory : " + sessionFactory);
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(10000);
System.out.println(Arrays.asList(applicationContext.getBeanDefinitionNames()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
i am calling run method from below class like (Please suggest if i am following wrong appraoch for calling a thread inside spring bean )
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
Thread t = new Thread(new MyThread());
t.start();
}
}
}
spring is not performing dependency injection on MyThread class
There are a couple of things wrong with your setup.
You shouldn't be creating and managing threads yourself, Java has nice features for that use those.
You are creating new bean instances yourself and expect Spring to know about them and inject dependencies, that isn't going to work.
Spring provides an abstraction to execute tasks, the TaskExecutor. You should configure one and use that to execute your task not create a thread yourself.
Add this to your #Configuration class.
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
Your MyThread should be annotated with #Scope("prototype").
#Component
#Scope("prototype")
public class MyThread implements Runnable { ... }
Now you can inject these beans and an ApplicationContext into your MyServiceCreationListener
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ApplicationContext ctx;
#Autowired
private TaskExecutor taskExecutor;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
taskExecutor.execute(ctx.getBean(MyThread.class));
}
}
}
This will give you a pre-configured, fresh instance of MyThread and execute it on a Thread selected by the TaskExecutor at hand.
Your MyThread is created manually rather than via spring context new Thread(new MyThread()); so no chance for spring to inject a bean.
Instead you can add a trick with static access to spring context where you can get a necessary bean from the context (see here or here).
Alternatively you can use ThreadLocal or InheritableThreadLocal to store necessary objects to be used in the thread.
You are creating Thread t = new Thread(new MyThread());.Spring container will not inject the dependency and also not maintain the life cycle of bean.
Example :
#Component
#Scope("prototype")
public class PrintThread extends Thread{
#Override
public void run() {
System.out.println(getName() + " is running");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " is running");
}
}
to access the thread object from spring context.
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
private static final String USER_THREAD = "printThread";
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
public static UserService getUserService(){return ctx.getBean(USER_THREAD );}
}

Issue with #ConditionalOnProperty

I need some help in the understanding usage of #ConditionalOnProperty in spring boot. My requirement is to start cron jobs on only one node using ##Scheduled annotation. I am setting a system property programmatically using node IP
#Component("interation")
public class IntegrationConfiguration {
private #Value("${integration.prinarynode}")
String PRIMARY_NODE ;
private final Logger log = LoggerFactory.getLogger(IntegrationConfiguration.class);
#PostConstruct
public void setProperty() {
String ip = null;
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
log.debug("Primary Nod:"+PRIMARY_NODE);
if(PRIMARY_NODE == null || ip.equals(PRIMARY_NODE))
{
log.debug("Setting integrations true");
System.setProperty("exception.clearance.allowed", "true");
} else {
System.setProperty("exception.clearance.allowed", "false");
}
}
}
and my scheduled code
#DependsOn ({"integration"})
#Component
#ConditionalOnProperty(prefix = "exception.clearance", name="allowed", matchIfMissing = false)
public class ScheduledTasks {
private final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
/** The services. */
#Autowired
// this is to
private IntegrationConfiguration intConfig;
#Scheduled(fixedRate = 5000)
public void ExceptionClear() {
log.debug("in the sc task");
}
}
method ExceptionClear is cron method and it should execute only on one node
In fact you set the property after ScheduledTasks bean initializing. The changing in #PostConstruct is futile.
Instead I would suggest to add your own PropertyCondition
public class PropertyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ... place your root detecting logic here ...
}
}
and use it to annotate class
#Conditional(PropertyCondition.class)

Why spring bean can't be injected in quartz job?

I am now writing a quartz job to start at 3:00 am every day to save data in database and redis. But now I got a problem and can't get it solved even though I tried a lot of times.
The job:
public class QuartzScheduler implements ServletContextListener {
private static Logger logger = Logger.getLogger(QuartzScheduler.class);
#Autowired
private ExchangeRateConfigBean exchangeRateConfigBean;
private Scheduler scheduler = null;
#Override
public void contextInitialized(ServletContextEvent servletContext) {
try {
ExchangeRateConfig exchangeRateConfig = exchangeRateConfigBean.setUpConfigurationFromConfigFile();
if(!exchangeRateConfig.getJob_fire()) {
logger.info("ExchangeRate Job Info: Current Exchange Rate Job flag is not open. Wont start the job!");
return;
}
} catch (Exception e) {
logger.error("ExchangeRate Job Info: Something wrong when analyzing config.properties file!");
logger.error(e.getMessage());
logger.error(e.getStackTrace());
logger.error(e.getCause());
return;
}
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
logger.info("ExchangeRate Job Info: Exchange Rate job starting......");
} catch (SchedulerException ex) {
logger.error(ex);
}
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
try {
scheduler.shutdown();
} catch (SchedulerException e) {
logger.error(e);
}
}
In this job , I autowired the component ExchangeRateConfigBean, but when I debugged in, I saw that the instance is null.
the spring config below:
<bean id="exchangeRateConfigBean" class="com.letv.exchangerate.bean.impl.ExchangeRateConfigBeanImpl">
<constructor-arg ref="exchangeRateErrorBean" />
</bean>
<bean id="exchangeRateErrorBean" class="com.letv.exchangerate.bean.impl.ExchangeRateErrorBeanImpl">
</bean>
The config Bean below:
public class ExchangeRateConfigBeanImpl implements ExchangeRateConfigBean {
public ExchangeRateConfigBeanImpl(){}
public ExchangeRateConfigBeanImpl(ExchangeRateErrorBean exchangeRateErrorBean)
{
this.exchangeRateErrorBean = exchangeRateErrorBean;
}
private static Logger logger = Logger.getLogger(ExchangeRateConfigBeanImpl.class.getClass());
private ExchangeRateErrorBean exchangeRateErrorBean;
#Override
public ExchangeRateConfig setUpConfigurationFromConfigFile() throws IOException {
InputStream inputStream = null;
ExchangeRateConfig exchangeRateConfig = null;
try {
Properties prop = new Properties();
String propFileName = "config.properties";
inputStream = getClass().getClassLoader().getResourceAsStream(propFileName);
exchangeRateConfig = new ExchangeRateConfig();
if (inputStream != null) {
prop.load(inputStream);
} else {
logger.error("property file '" + propFileName + "' not found in the classpath");
throw new FileNotFoundException("property file '" + propFileName + "' not found in the classpath");
}
String job_fire_tmp = prop.getProperty("job_fire");
exchangeRateConfig.setJob_fire(job_fire_tmp.isEmpty() ? false : Boolean.parseBoolean(job_fire_tmp));
return exchangeRateConfig;
} catch (IOException e) {
logger.error(e.getStackTrace());
return exchangeRateConfig;
} finally {
inputStream.close();
}
}
In the config bean, I used the constructor injection to inject the error bean above. the error bean's code lists below:
public class ExchangeRateErrorBeanImpl implements ExchangeRateErrorBean{
public ExchangeRateErrorBeanImpl(){}
#Override
public ExchangeRateError getExchangeRateError(String content) {
if (content.isEmpty())
return null;
if (!content.contains(":"))
return null;
String[] strArray = content.split(":");
return new ExchangeRateError(strArray[0], strArray[1]);
}
}
Anyone can help? why I autowired the Config bean in the job class, it will create null instance? thx.
And before I post this, I have read this in detail, Why is my Spring #Autowired field null?
but it didn't save my time.
Your code shows QuartzScheduler as an instance of ServletContextListener. This will only create a Java bean. Your Quartz Scheduler class must be a bean in a Spring applicationContext. Only then will the other dependencies be autowired. Refer this for example - http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-quartz

Resources