Injecting a bean at startup in Spring - spring

I have a class RunBackgroundServices which runs some background services at startup. I need a new object BackgroundServices in it. So I am using WebApplicationContext to get the bean. But it's not working.
RunBackgroundServices.java
#WebListener
public class RunBackgroundServices implements ServletContextListener {
private ExecutorService executor;
public void contextInitialized(ServletContextEvent event) {
final WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
BackgroundServices backgroundServices = springContext.getBean(BackgroundServices.class);
executor = Executors.newSingleThreadExecutor();
executor.submit(backgroundServices); // Task should implement Runnable.
}
public void contextDestroyed(ServletContextEvent event) {
executor.shutdown();
}
}
BackgroundServices.java
#Service
public class BackgroundServices extends Thread {
#Autowired
ServerListener serverListener;
#Autowired
ClientListener clientListener;
private static final Logger logger = LoggerFactory
.getLogger(BackgroundServices.class);
public void run() {
logger.debug("BackgroundServices :: run");
try {
serverListener.start();
} catch (InterruptedException e) {
logger.error(e.getStackTrace().toString());
}
try {
clientListener.start();
} catch (InterruptedException e) {
logger.error(e.getStackTrace().toString());
}
}
}
I am getting the following error -
Exception sending context initialized event to listener instance of class com.emc.hl7.common.RunBackgroundServices
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emc.hl7.common.BackgroundServices] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:295)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1125)
at com.emc.hl7.common.RunBackgroundServices.contextInitialized(RunBackgroundServices.java:20)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4961)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5455)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:634)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:671)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1840)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

There is an easier way to perform bootstraping operations using Spring.
All you have to do is have Spring bean that implements ApplicationListener<ContextRefreshedEvent>
Your code would look like:
#Component
public class ContextStartupListener implements ApplicationListener<ContextRefreshedEvent> {
private final BackgroundServices backgroundServices;
private ExecutorService executor;
#Autowired
public ContextStartupListener(BackgroundServices backgroundServices) {
this.backgroundServices= backgroundServices;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(!isRootApplicationContext(event)) {
return;
}
executor = Executors.newSingleThreadExecutor();
executor.submit(backgroundServices);
}
private boolean isRootApplicationContext(ContextRefreshedEvent event) {
return null == event.getApplicationContext().getParent();
}
}
Note the use of isRootApplicationContext. It is needed if you have multiple application contexts and do no want to run the bootstrapping operation on each one.
Using the above code you are bootstrapping using Spring's events, not the Servlet container's events.

Related

How to trigger listener after 1 particular bean initialized

Does anybody know if it is possible to create Spring Boot listener that would be called once only 1 particular bean has been initialized?
I only know how to create listener that is triggered once all beans have been initialized:
#EventListener(ContextRefreshedEvent.class)
public void myListener(ContextRefreshedEvent event) {...}
But that listener will be triggered for every single bean in the app instead of 1 particular bean I am looking for.
Any ideas?
Here's how this could be done.
First make your bean publish an event when it's initialized by implementing InitializingBean or having a #PostConstruct method:
public class SomeBeanInitializedEvent extends ApplicationEvent {
...
public SomeBeanInitializedEvent(Object source) {
super(source);
}
}
#RequiredArgsConstructor
public class SomeBean implements InitializingBean {
private final ApplicationEventPublisher applicationEventPublisher;
#Override
public void afterPropertiesSet() {
applicationEventPublisher.publishEvent(new SomeBeanInitializedEvent(this));
}
}
Or as a #Bean method use the standard ApplicationEventPublisher available in the context:
#Bean
public SomeBean someBean(ApplicationEventPublisher publisher) {
SomeBean someBean = ...
publisher.publishEvent(new SomeBeanInitializedEvent(someBean));
return someBean;
}
Then create an event listener for your event:
private class SomeBeanInitializedEventApplicationListener implements ApplicationListener<SomeBeanInitializedEvent> {
#Override
public void onApplicationEvent(SomeBeanInitializedEvent event) {
log.info("Got SomeBeanInitializedEvent: {}", event);
}
}
Then register this application event listener via spring.factories or the setter method on SpringApplication/SpringApplicationBuilder:
#SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SomeApplication.class)
.listeners(new SomeBeanInitializedEventApplicationListener())
.run(args);
}
...
You cannot use an #EventListener annotated method in this case because it'll be registered as a listener too late, after an event from your bean has already been fired.

kafkaendpointlistenerregistry.start() throws null pointer exception

I have a requirement where I want to start Kakfa consumer manually.
Code :
class Dummy implements
ConsumerSeekAware
{
#Autowired
KafkaListenerEndpointRegistry registry;
CountDownLatch latch;
#Autowired
ConcurrentKafkaListenerContainerFactory factory;
onIdleEvent(){
latch.countdown()
}
#KafkaListener(id="myContainer",
topics="mytopic",
autoStartup="false")
public void listen() {}
#Scheduled(cron=" some time ")
void do_some_consumption(){
latch = new CountDownLatch(1);
this.registry.getListenerContainer("myContainer").start();
latch.await();
do processing
this.registry.getListenerContainer("myContainer").stop()
}
}
I have made the bean of
ConcurrentKafkaListenerContainerFactory with all props in my other Config class which I am Autowiring here.
However, I get a null pointer exception when I start my container
using this.registry.getListenerContainer("myContainer").start()
java.lang.NullPointerException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
I just copied your code into a Spring Boot app (which auto configures the factories); and everything works perfectly as expected...
#SpringBootApplication
#EnableScheduling
public class So62412316Application {
public static void main(String[] args) {
SpringApplication.run(So62412316Application.class, args);
}
#Bean
public ApplicationRunner runner(KafkaTemplate<String, String> template) {
return args -> {
template.send("mytopic", "foo");
};
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("mytopic").partitions(1).replicas(1).build();
}
}
#Component
class Dummy implements ConsumerSeekAware {
#Autowired
KafkaListenerEndpointRegistry registry;
CountDownLatch latch;
#Autowired
ConcurrentKafkaListenerContainerFactory factory;
#EventListener
public void onIdleEvent(ListenerContainerIdleEvent event) {
System.out.println(event);
latch.countDown();
}
#KafkaListener(id = "myContainer", topics = "mytopic", autoStartup = "false")
public void listen(String in) {
System.out.println(in);
}
#Scheduled(initialDelay = 5_000, fixedDelay = 60_000)
void do_some_consumption() throws InterruptedException {
latch = new CountDownLatch(1);
this.registry.getListenerContainer("myContainer").start();
latch.await();
this.registry.getListenerContainer("myContainer").stop();
}
}
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.listener.idle-event-interval=5000

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 );}
}

Using Spring Scheduler to return rows from DB throws Null pointer Exception

I am using Spring scheduling to schedule a task that performs some DB operation every hour. This throws a NullPointerException every time I try to call a function that triggers DB specific operations.
Configuration File :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.bt.rtddashboard")
/*#EnableJpaRepositories(basePackages = {
"com.bt.rtddashboard"
})*/
#EnableScheduling
#PropertySource("classpath:jdbc.properties")
public class RTDDashboardConfiguration extends WebMvcConfigurerAdapter {
#Resource
private Environment env;
#Bean(name = "dataSource", destroyMethod="close")
public DataSource getDataSource() {
//DB code
}
private Properties getHibernateProperties() {
//Hibernate code
}
#Bean(name = "sessionFactory")
#Scope("singleton")
public LocalSessionFactoryBean getSessionFactory() {
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getHibernateTransactionManager(SessionFactory factory) {
;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/").setCachePeriod(31556926);
}
#Scheduled(fixedRate=1000)
public void work() {
// task execution logic
System.out.println("Scheduled Task");
DashboardController controller=new DashboardController();
controller.performTask();
}
}
Dashboard Controller :
#RestController
public class DashboardController {
#Autowired
private InlineService service;
#RequestMapping(value="/rtd/getAllServers", method=RequestMethod.GET)
public ResponseEntity<List<ServerMonitor>> getAllServers(){
List<ServerMonitor> ilsList=new ArrayList<ServerMonitor>();
ResponseEntity<List<ServerMonitor>> response=null;
try{
ilsList=service.getAllServers();
response=new ResponseEntity<List<ServerMonitor>>(ilsList,HttpStatus.OK);
System.out.println("Scheduled Time");
}catch(Exception e){
}
return response;
}
public void performTask(){
System.out.println("Scheduled Task Starts in Contrroller");
List<IlsStats> ilsStats =new ArrayList<IlsStats>();
List<IlsStatsHistory> ilsStatsHisList=new ArrayList<IlsStatsHistory>();
try{
//get All the ILS Stats
ilsStats=service.getAllInlineStats();
System.out.println("No of rows to Copy : " + ilsStats.size());
ilsStatsHisList=formILSHistoryList(ilsStats);
int flag=service.insertInIlsStatsHistory(ilsStatsHisList);
//List<ServerMonitor> ilsList=service.getAllServers();
}catch(Exception e){
e.printStackTrace();
}
}
Here, ilsStats=service.getAllInlineStats(); throws NullPointerException.
Even though the rest webservice created on top of it is working fine.
Stack Trace :
Scheduled Task
Scheduled Task Starts in Contrroller
java.lang.NullPointerException
at com.bt.rtddashboard.controller.DashboardController.performTask(DashboardController.java:226)
at com.bt.rtddashboard.configuration.RTDDashboardConfiguration.work(RTDDashboardConfiguration.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
The same stack trace comes up every 1 sec now.
Creating an #Scheduled method in a configuration class is a very bad idea (imho). Next to that I also think that having a scheduler calling a controller method (a method tied to a web related component!) is generally a bad idea/design.
Nonetheless the problem is that you do new DashboardController(). That will obviously create a bean outside the scope of spring and will never be dependency injected. Instead inject the into the class and use that instance.
#Autowired
private DashboardController controller;
#Scheduled(fixedRate=1000)
public void work() {
controller.performTask();
}
Or even better just remove that method all together and simply place #Scheduled(fixedRate=1000) on the performTask method instead.

Exiting a SpringBoot app - how do I get a reference to SpringApplication?

I realize that to programmatically exit a SpringBoot 4 application I want to call the exit() method of SpringApplication, but how can I get a reference to it?
Of course I have access to it in my main() method, but I ask because if I'm in some class that is loading a resource and fails, I want to terminate the app, but from that class I can't figure out how to access SpringApplication.
Thanks...
A cleaner approach for this use case is to use events & listeners wherein you have to add your listener to SpringApplication class which will listens to an event like in your case a resource load failure and then subsequently act accordingly i.e. exit the application. You can get application context handle by implementing the ApplicationContextAware interface. Details on event & listener can be found here.
MyEvent class :-
public class MyEvent extends ContextRefreshedEvent {
private static final long serialVersionUID = 1L;
#Autowired
public MyEvent(ApplicationContext source) {
super(source);
}
}
MyEvent listener class :-
#Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event instanceof MyEvent){
SpringApplication.exit(event.getApplicationContext(), new ExitCodeGenerator() {
#Override
public int getExitCode() {
return 2;
}
});
}
}
}
Resource loader class:-
#Component
public class MyResourceLoader implements ApplicationContextAware, CommandLineRunner {
private ApplicationContext ctx ;
#Autowired
private ApplicationEventPublisher publisher;
#Override
public void run(String... args) throws Exception {
//inside RUN for resource load failure
publisher.publishEvent(new MyEvent(ctx));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
}

Resources