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 :)
Related
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;
}
}
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.
}
}
I have a web application which I package as a war and I deploy on tomcat 7.
This is a Spring MVC application that I want to add a Quartz scheduler to it. I want to run code that will make use of various Spring beans and populates the scheduler with Jobs that are configured dynamically so I need access to various Beans in code like
#Autowired
private CronDAO cronDAO;
public void loadCronJobs(final Scheduler scheduler) {
LOGGER.info("Vas[{}]: Loading Cron Schedule...", vasName);
final List<Cron> crons = cronDAO.fetchAll();
final Long cronSid = cron.getSid();
final String directive = cron.getDirective();
final String expression = cron.getCronExpression();
...
}
Normally I would put the initialization code for the scheduler in the main function of the application and would use Autowired beans for my application logic. Now with the application server initializing the application I cannot do this.
I tried to add my code to the startup function of the servlet but the Spring context is not yet ready so the autowired beans (the DAO in this example) fail to load.
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
CronManager cron = new CronManagerImpl();
cron.loadCronJobs();
}
}
Is there a way to run code (not only quartz related) when the application start in the container but the Spring context to be complete?
You can write a 'ServletContextListener' and register it in the web.xml. In your implementation you can rely on spring frameworks WebApplicationContextUtil to get a handle to the spring application context.
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class QuartzInitiator implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContextEvent.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
//your logic
}
}
Autowired fields would be correctly initialized by the Util.
Spring is great at avoiding you do deal with low level questions.
At higher level you have :
a number of beans declared in Spring application context
an initialization procedure that must be run
after the beans have been initialized
before the rest of code is run
Well simply declare a new bean in which you inject all the other beans, and do your initialization in the init-method of that last bean. Spring guarantees that this method is called at context initialization time after all dependant beans have been initialized.
That way is totally independant of whether your application is a web application or not : is becomes spring framework's problem and no longer yours.
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
I am developing a spring MVC application. When I try to use AnnotationConfigApplicationContext in my controller class I am getting the following error. I have no idea what this statement exactly means.
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping)
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
Error Message -->
java.lang.IllegalStateException:org.springframework.context.annotation.AnnotationConfigApplicationContext#116b3c0 has not been refreshed yet
Can someone explain me what went wrong here ? I am using Spring 4.0.1.. I am new to spring mvc.
When you are creating a new instance of an ApplicationContext (regardless which type) you are basically creating new instances of each and every bean configured in that ApplicationContext. That is nice the first time, it might work the second and depending on the amount of beans, the type of beans will crash after that. As the context will never be destroy (until the app crashed and is restarted) you will run into possible memory issues, performance issues, strange transactional problems etc.
A general rule of thumb is to never construct a new instance of an ApplicationContext but to use dependency injection instead.
If you really want access to the ApplicationContext put a field of that type in your controller and put #Autowired on it.
#Controller
public class MyController {
#Autowired
private ApplicationContext ctx;
….
}
Then you can do a lookup for the bean you need in the method. This can be handy if you use the ApplicationContext as a factory for your beans. If all the beans you need are singletons it is better to simply inject the bean you need.
#Controller
public class MyController {
#Autowired
private MappingFileGenerator mfg ;
….
}
Now Spring will inject the MappingFileGenerator and it is available for use in your methods. No need to create a new instance of an ApplicationContext.
More information is in the Spring Reference Guide.
#M.Deinum's comment will get quite a few more upvotes.
Think of creating a new ApplicationContext as instantiating a new (instance of an) application. Do you want to do that every time this (or any other method in said application) is called? No, you don't.
I'm guessing you think you do because you need access to your ApplicationContext in this method. To do that - i.e. to get access to the running application context (rather than creating a new one), you want to do
#Controller // or #Service / #Component / ... : tells Spring that this is a bean, and to inject the specified dependencies
class YourClass {
#Autowired // tells Spring that this object is a dependency should should be injected
ApplicationContext ctx;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
public ModelAndView generateMappingFile(#ModelAttribute Mapping mapping) {
MappingFileGenerator mfg = ctx.getBean(MappingFileGenerator.class);
}
The key here is the Autowired annotation, which tells Spring to inject the annotated object as a dependency.
I highly suggest following the links I've included (for starters), as what you're doing here suggests pretty strongly that you haven't wrapped your head around what DI is and does for you, and until you do, using it is likely to be counterproductive toward it's own ends for you.
In case it helps someone, i was having this issue on a new URL Mapping added to a gradle project, i was missing the first slash of the url and that causing this "illegalstate not refreshed yet" on my tests