I'm in the "how-to" phase with Apache CXF and would like to know if there's a way to call a method when the server is started.
It would be similar to a JSF web application, when I use a #ApplicationScoped managed bean with eager=true: when the container is started, the annotated class is instantiated and I can call whatever I want from its constructor.
Any help?
So, If you are using CXF Servlet to serve Web Service request, then you can create ServletContextListener and contextInitialized method will be called on deployment or on server start up if the application is already deployed.
To do that create class which will implement ServletContextListener:
public class YourContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
//This method is called by the container on start up
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
Then define that listener in your web.xml:
<listener>
<listener-class>your.package.YourContextListener</listener-class>
</listener>
In the contextInitialized method you can get servlet context by using:
ServletContext context = sce.getServletContext();
And you can set as many attributes as you want to be available into whole application scope.
Related
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.
Need to add a Java class (named HistoryBean in my project) to the ServletContext. I do not want to create new instance of the HistoryBean class in the different Servlets that I have inside my project. I want to get it from the ServletContext. Please help me with suggestions.
You can do that in a ServletContextListener:
public void contextInitialized(ServletContextEvent e) {
e.getServletContext().setAttribute("historyBean", new HistoryBean());
}
Register your listener with #WebListener or with <listener>..</listener> in web.xml.
As you're using JSF, just register it as an application scoped bean.
#ManagedBean(eager=true)
#ApplicationScoped
public class HistoryBean {
// ...
}
(note the eager=true, this autoconstructs the bean on webapp's startup without the need to reference it in some view or bean, you don't need a ServletContextListener for this)
This way it's not only available in JSF context the usual way as #{historyBean}, but it's in servlets also available as a servlet context attribute with the managed bean name as key as follows:
HistoryBean historyBean = (HistoryBean) getServletContext().getAttribute("historyBean");
This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
I would like to perform some action as soon as my application (Enterprise Application with Business Logic, EJB, and a Client, Web) is deployed.
For example I would like to make some entity in a persistent state, or otherwise create a file.
How can I do that?
Thanks.
Configure SerlvetContextListener and override contextInitilized()
in your web application description , web.xml
<web-app ...>
<listener>
<listener-class>com.someCompany.AppNameServletContextListener</listener-class>
</listener>
</web-app
package com.someCompany;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class AppNameServletContextListener implements ServletContextListener{
#Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("ServletContextListener destroyed");
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("ServletContextListener started");
// do the things here
}
}
The "default" way is to have a servlet with an init() method. Then in the servlet-descriptor you mark this servlet as load-on-startup 1:
Example:
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
As soon as the servlet is deployed (which happens after the EJBs are deployed), that init() method is called and you can execute the task you want.
With present web application in your ear, the easiest and simplest would be to use ServletContextListener, otherwise in EJB 3.1 you could use automatic timers or startup singleton session beans.
This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
The reason I'm asking this is that I want to write code that initializes the application once it starts and cleans up later on.
I dont want to use a servlet init() method since it is per servlet.
There is no main() method in Servlet.
If
The reason I'm asking this is that I want to write code that initializes the application once it starts and cleans up later on.
You can use ServletContextListener implemented
public class MyServletContext implements ServletContextListener{
ServletContext context;
public void contextInitialized(ServletContextEvent contextEvent) {
System.out.println("Context Created");
}
public void contextDestroyed(ServletContextEvent contextEvent) {
System.out.println("Context Destroyed");
}
}
web.xml
<listener>
<listener-class>
com.yourpackage.MyServletContext
</listener-class>
</listener>
There is no main() method, because the components are managed and the container invokes other methods - like the init() on servlets and filters. The container itself is started through a main method, but even that's hidden from you.
For per-application and initialization you can use a ServletContextListener
You have to map it in web.xml using <listener><listener-class>...</listener-class></listener>. In contextInitialized(..) and contextDestroyed(..) you can do initialization and cleanup respectively.
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.