How to get the context instance of a web app. context? - java

I don't know exactly how to get the web application ctx. instance within a Spring web application. For e.x. We usually do the configuration like :
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
...
}
I'm aware of the ApplicationContextAware, I've implemented it before. The thing is it was implemented to get the Application context and not specifically Web application context. For e.x. :
public class ApplicationContextProvider implements ApplicationContextAware{
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
}
And later in handler/controller classes I obtained it with smtg like this :
MrBean tb = ApplicationContextProvider.getApplicationContext().getBean("MrBean", MrBean.class);
In this e.x the context is not a web app. context rather than a global app. context (not specifically a web context). I need your help in retrieving the web context instance because I need to active some profiling beans already configured from the configuration. Thanks in advance.

If you have the HttpServletRequest object you can retrieve the WebApplicationContext via RequestContextUtils#findWebApplicationContext()
Look for the WebApplicationContext associated with the DispatcherServlet that has initiated request processing, and for the global context if none was found associated with the current request. The global context will be found via the ServletContext or via ContextLoader's current context. NOTE: This variant requires Servlet 3.0+ and is generally recommended for forward-looking custom user code.
E.g.
WebApplicationContext webApplicationContext = RequestContextUtils.findWebApplicationContext(request);
But this only works within spring mvc/webflow context because the Spring DispatcherServlet ensures that the WebApplicationContext instance is available in the currently running thread.

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.
}
}

Run code after Spring context has been initialized in web app deployed on Tomcat

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.

Spring root application context and servlet context confusion

I know that I need to register classes annotated with #Controller in my servlet context to make my webapp accesible. Usually, I will do it the following way:
#Configuration
#EnableWebMvc
#ComponentScan({"foo.bar.controller"})
public class WebConfig extends WebMvcConfigurerAdapter {
//other stuff like ViewResolvers, MessageResolvers, MessageConverters, etc.
}
All other configuration classes I added to my root application context. Here is how my dispatcher initializer usually look like:
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class, ServiceConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
But things are getting more interesting when I started to use WebSockets. To get websockets working, you have to put WebSoketConfig.class to servlet context. Here is my example of WebSocketConfig:
#Configuration
#EnableScheduling
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
#Override
public void configureClientInboundChannel(ChannelRegistration channelRegistration) {
channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8);
}
#Override
public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {
channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
Also, I've created a service to send a message to the topic:
#Service
public class TimeServiceWsImpl implements TimeServiceWs {
#Autowired
private SimpMessagingTemplate messagingTemplate;
#Override
public void sentCurrentTime() {
long currentTime = System.currentTimeMillis();
String destination = "/topic/chatty";
logger.info("sending current time to websocket /topic/time : " + currentTime);
this.messagingTemplate.convertAndSend(destination, currentTime);
}
}
I need to use this service in some other services (Autowire it). And now I'm in a deadlock:
If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException
If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service, because root context can't see servlet context beans(as far as I know)
If I move all my configurations to servlet context, all beans are successfully created, but I get the following exception: java.lang.IllegalStateException: No WebApplicationContext found and can't access my webapp
What am I supposed to do? What should be inside root context? What should be inside servlet context? And could you please clarify the difference between these context one more time please?
If you will need any additional information, just let me know.
Most Spring MVC applications have one root context containing all service layer / DAO layer beans, and one servlet context per spring dispatcher servlet of the application, which contains (at least) the controllers of each servlet.
The idea being that is that one application might have several servlet dispatchers, for example one for URL /shopping/* and the other for URL /reporting/*, each with it's own set of controllers.
The controllers of one servlet dispatcher are isolated from each other, meaning although they are also Spring beans, they cannot be injected in each other.
Service layer and DAO beans in the root context are visible in all servlet contexts, so Service layer beans can be injected in any controller, but not the other way around.
The root context is said to be the parent of the controller servlet context/contexts.
It's all meant to be a mechanism of isolating groups of beans from each other to ensure no unmeant dependencies are possible.
Given this and going through the questions:
If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException: Move the SimpleMessagingTemplate to the root context, it's a bean like a DAO that can be useful anywhere in the application so it should be in the shared root context.
If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service: If it's meant to be autowired to other services, leave it in the root context then.
- If I move all my configurations to servlet context, all beans are successfully created, but I get java.lang.IllegalStateException: No WebApplicationContext found: Do the opposite, move basically all beans to the root context, and leave on the servlet context only the beans that are specific of that part of the application, many times only the controllers.
WebSocket-related config belongs to the DispatcherServlet configuration one way or another. After all the HTTP handshake is processed by the DispatcherServlet through its handler mappings.
You should be able to go with a single Spring context in a deployment scenario where there is only one DispatcherServlet in the web application. Consolidating the configuration into the root context makes more sense if using Spring Security for example although there was a bug with the AbstractAnnotationConfigDispatcherServletInitializer (see SPR-11357). Consolidating into the DispatcherServlet context should also be possible but you wrote that you got exceptions. Can you provide the exception details?
It is also an option to have both root and DispatcherServlet contexts. In that case the WebSocket configuration will be in the DispatcherServlet context and it's not possible to inject the SimpMessagingTemplate into beans in the root context. That actually makes sense since there is one SimpMessagingTemplate to go with each DispatcherServlet (or some other servlet). What's needed is a web layer component, perhaps a thin wrapper around service layer beans (like TimeServiceWs the above example) that can also be injected with the SimpMessagingTemplate. This web layer component essentially serves as a bridge.

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.

Categories

Resources