Spring recursively loading application context - java

I want to invoke a method after the application context is loaded. I used ApplicationListener interface and implemented onApplicationEvent.
applicationContext.xml
<beans>
<bean id="loaderContext" class="com.util.Loader" />
<bean id="testServiceHandler" class="com.bofa.crme.deals.rules.handler.TestServiceHandlerImpl">
</beans>
Loader.java
public class Loader implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TestHandlerServiceImpl test = (TestServiceHandlerImpl)context.getBean("testServiceHandler");
}
}
But the above code goes recursive. Is it possible to get a bean from application context inside onApplicationEvent function ?

Instead of creating a new context on the listener, implement the interface ApplicationContextAware, your context will be injected.

If you are using Spring 3 or higher:
As of Spring 3.0, an ApplicationListener can generically declare the
event type that it is interested in. When registered with a Spring
ApplicationContext, events will be filtered accordingly, with the
listener getting invoked for matching event objects only.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationListener.html#onApplicationEvent-E-
which would look like the following. Note also that this solution will ensure it is only executed on this event (i.e. start/load): it looks to me like even if you inject the context to your original class it would be executed for any event.
public class Loader implements ApplicationListener<ContextStartedEvent> {
public void onApplicationEvent(ContextStartedEvent event) {
ApplicationContext context = event.getApplicationContext();
}
}
See examples here:
http://www.tutorialspoint.com/spring/event_handling_in_spring.htm

Related

How to run method once on startup in Spring without #PostConsrtuct/init/#EventListener?

I have an Spring app that needs to load files from resource folder to external ftp storage on startup. So applications is starting -> files are uploaded ->application starts successfully -> bean which is responsible for uploading is destroyed. The thing is I don't want to use something like #PostConsrtuct/init for some bean and have idle bean in my context which did his job on startup.
I don't want to use #EventListener that would listen on refresh context and runs logic because this listener would perform this uploading every context refresh even after startup.
My app is pure Spring app (not a Spring Boot), so I can't use CommandLineRunner/AppStartupRunner interfaces.
Is that possible to setup some bean that runs once method and dies ?
Well, I should check behaviour of CommandLineRunner in Spring App before posting question. It looks like CommandLineRunner perfectly working even not in the spring boot application. So CommandLineRunner perfectly fits all my needs.
You can have your class implement InitializingBean and implement your logic in afterPropertiesSet()
Deleting the bean at runtime is not recommended, although it can be done.
#Component
public class ABean implements InitializingBean, ApplicationContextAware {
private ApplicationContext context;
#Autowired
private AnyBean anyBean;
#Override
public void afterPropertiesSet() throws Exception {
// code
// at end destroy bean like
context.getAutowireCapableBeanFactory().destroyBean(this);
}
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
this.context=context;
}
}

Ensure spring bean loaded from non spring context

I have spring application alongside (jersey 2.6 classes and ) servlets .
I need to get Spring bean(s) from jersey/non spring context,
Similar question suggested to get context in a static wrapper of context
public static ApplicationContext getContext() {
return context;
}
How can I be sure the context is already loaded or not null?
If I can't, how should I wait/check until it spring context is loaded?
In case of calling from jersey context or calling bean from a simple HttpServlet code
EDIT
Jersey is working fine using jersey-spring3 dependency jar, so my question is only about Servlets out of Spring control
EDIT 2
The application is loading spring different than #entpnerd suggested article
It register a Servlet implementing a WebApplicationInitializer
public class MyWebAppInitializer implements WebApplicationInitializer {
But also have DispatcherServlet configured in web.xml
How can the DispatcherServlet loaded only after Spring loaded?
Because we add Autowiring capabilities on its init method:
WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
Is adding a timeout before serving requests is the most prefer solution or is there a tweak in class loading that can take care of it?
EDIT 3
I found answers and answers of injecting, but not why Spring is loaded before Servlet.
The idea is quite simple, although the actual implementation may vary depending on an exact way of Spring boot and Jersery initialization.
An idea:
Spring boot, as being a purely runtime framework, is all about proper loading the application context (from the question standpoint).
So, bottom line, when it's loaded there is an application context somewhere in memory, and its possible to access beans from this application context.
Now, since you say that Jersey is not spring/spring-boot driven, this application context has to be reachable from some kind of static global variable by Jersey, it's quite ugly but should work.
So the idea has two steps:
Put an application context reference to some static holder accessible from Jersey.
Read this value in some infrastructure level code from Jersey component.
A Possible Implementation
Technically step one can be done by implementing some kind of spring boot listener that will store application context in some kind of singleton:
enum ApplicationContextHolder {
INSTANCE;
private ApplicationContext ctx;
void setApplicationContext(ApplicationContext ctx) {
this.ctx = ctx;
}
ApplicationContext getCtx() {
return this.ctx;
}
}
// and a listener (spring boot provides many ways to register one, but the
// implementation should be something like this):
// The main point is that its managed by spring boot, and hence and access to
// the application context
class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContextHolder
.INSTANCE
.setApplicationContext(event.getApplicationContext());
}
}
Now the step 2 is:
class MyJerseyOrWhateverComponentThatWantsToAccessApplicationContext {
public void foo() {
ApplicationContext ctx = ApplicationContextHolder.INSTANCE.getCtx();
...
ctx.getBean(...);
}
}
So a viable solution for this could happen in two stages:
A Spring bean gets the ApplicationContext instance and sends it to a static singleton outside of the Spring context.
Your standalone servlet gets the ApplicationContext instance from the static singleton and verifies that the right beans have been loaded.
Consider the following code as an example:
SpringMetaBean.java
// #Component so that it's part of the Spring context
// Implement ApplicationContextAware so that the ApplicationContext will be loaded
// correctly
#Component
public class SpringMetaBean implements ApplicationContextAware {
private ApplicationContext appCtx;
public setApplicationContext(ApplicationContext appCtx) {
this.appCtx = appCtx;
}
// #PostConstruct so that when loaded into the Spring context, this method will
// automatically execute and notify ApplicationContextHolder with a reference to
// the ApplicationContext
#PostConstruct
public void setup() {
ApplicationContextHolder.set(this.appCtx);
}
}
ApplicationContextHolder.java
public class ApplicationContextHolder {
// ensure the reference is thread-safe because Spring and standalone Servlet will
// probably be running on different threads.
private final AtomicReference<ApplicationContext> appCtxContainer = new AtomicReference<>();
public void set(ApplicationContext appCtx) {
this.appCtxContainer.set(appCtx);
}
public ApplicationContext get() {
return this.appCtxContainer.get();
}
}
MyStandaloneServlet.java
public class MyStandaloneServlet {
// my request handler method
public void getResponse(HttpServletRequest rq) {
ApplicationContext springAppCtx = ApplicationContextHolder.get();
// if not null, we know that Spring has been loaded and we can dig into the
// application context.
}
}

Spring autowire a class on server startup

I have a spring application. I am autowiring classes and they are working fine.
For e.g
#Controller
public class SearchController {
#Autowired
private EnvironmentControl envControl;
#Autowired
private SearchControl searchControl;
...
But now i have on server startup class called ScheduleServlet which uses init method to schedule something...
public class SchedulerServlet extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.LOGGER.info("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager) ic.lookup("java:comp/env/tm/TimerManager");
Timer timer = tm.schedule(new GlobalTemplateScheduler(), 0, 3600000);// one hour interval
System.out.println("Timer..... " + timer);
}
...
In this my GlobalTemplateScheduler class has timerExpired method which is scheduled to execute after every one hour interval.
public class GlobalTemplateScheduler implements TimerListener {
#Autowired
private TemplateControl templateControl;
#Override
public void timerExpired(Timer timer) {
try {
templateControl.updateMappings(names);
} catch (Exception e) {
this.LOGGER.error(e.getMessage());
e.printStackTrace();
}
...
So i have to autowire templateControl which i am getting null. This should happen on server startup.
Further inside updateMappings there's a datasource object which is also autowired as constructor-arg(This is working fine on browser request but need to do it on server startup).
Note: I cannot use the ApplicationListener interface.
Any suggestions would really help.
Thankyou.
On application startup beans initialization would not be completed, beans can be used after the application context refresh or after the intialization of the bean, it will make no sense to execute a logic which requires the bean on the startup unless you detect whether the bean is ready or not.
You can execute some logic using #PostConstruct in the bean which will be executed after the initialization of the bean so you can manipulate your logic in a way to do so after the intialization of the bean or you could detect and execute logic after the ContextRefreshedEvent by impelementing applicationListener and put your logic in onAppplicationEvent method.
One solution would be to use Spring's Container within your servlet. There are many implementations for this purpose, for instance the AnnotationConfigApplicationContext. Spring's documentation describes how to use the ClassPathXmlApplicationContext
Suppose GlobalTemplateScheduler is also a bean, then the key point is this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
GlobalTemplateScheduler sheduler = context.getBean("sheduler", GlobalTemplateScheduler.class);
sheduler.someMethod();
The content of the XML, which is used by the ClassPathXmlApplicationContext is small. But you need to enable component scan:
<context:component-scan base-package="foo.bar.baz" />
Another approach, I could suggest, is to use Spring's DispatcherServlet to wire all the beans together. It can use the same XML, it is just a matter of loading it. The benefit is that is you don't need to load the application context by yourself and launch a bean as an entry point
There are plenty of tutorials how to use this servlet.
If you dont't like to write XML, you could use the WebApplicationInitializer
As i said the beans which i was autowiring were working fine. I just needed those beans in my Scheduler Servlet.
Here's the solution which worked...
In my scheduler servlet i got the application context xml and used the beans which were required...
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
GlobalTemplateControl globalTemplateControlObject = context.getBean("globalTemplateControl", GlobalTemplateControl.class);
Thanks #duffymo #Amer Qarabsa #Spindizzy for your suggestions :)

Spring application context

Is there any other way in J6SE to get spring (spring 3.0) application context than implement ApplicationContextAware interface?
Sorry I must improve my question. I have running application context in my J6SE app and in some classes I need it.
After reading your question, I know you're looking for an alternative to ApplicationContextAware but I read it that you have a goal of many classes using the ApplicationContext but want to avoid implementing the interface for all these classes. This approach still uses the ApplicationContextAware but encapsulates it into a single class for reuses.
I typically load the configuration at application start up via a ContextLoaderListener in the web.xml. After this occurs, I set "MyApplicationContext" as the contextApplicationContextProvider.
<bean id="contextApplicationContextProvider" class="pkg.MyApplicationContext"></bean>
The class must implement ApplicationContextAware as you already suggested:
public class MyApplicationContext implements ApplicationContextAware {
private static ApplicationContext appContext;
/* (non-Javadoc)
* #see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
#Override
public void setApplicationContext(ApplicationContext globalAppContext)
throws BeansException {
this.appContext = globalAppContext;
}
public static ApplicationContext getApplicationContext() {
return appContext;
}
}
The key here is that you now have a static reference to the single instance of the ApplicationContext object. Retrieving it is simple by using the static method call MyApplicationContext.getApplicationContext() for any class, spring-managed or not.
You can load it from the CLASSPATH.
new FileSystemXmlApplicationContext(APPLICATION_CONTEXT_FILE);
#Inject
private ApplicationContext ctx;
(or #Autowired instead of #Inject). This is the annotation replacement of ApplicationContextAware. This of course mean that the objects needs to be a spring bean.

How to add context parameter after loading spring context

I want to add a servlet context parameter/attribute through spring configuration. I need this because the value I want to add in servlet context is available only after spring container loads. I'm adding the value inside the servlet context as I need the value in almost all my .jsp files.
Essentially I need a mechanism opposite to this
Assuming you are using a properly configured Spring Web Application Context, you could try implementing a bean that implements org.springframework.web.context.ServletContextAware and org.springframework.beans.factory.InitializingBean so that you could add whatever you want to the ServletContext in the afterPropertiesSet method implementation.
public class ServletContextInjector implements ServletContextAware,InitializingBean {
private ServletContext servletContext;
public void setServletContext(ServletContext sc){ this.servletContext = sc; }
public void afterPropertiesSet(){
servletContext.setAttribute( /* whatever */ );
}
}
Hope this helps.

Categories

Resources