Within my Servlet-based application, I would like to log events for startup and shutdown.
I've tried to implement the ServletContextListener interface to do this:
public class DiagnosticListener
implements ServletContextListener {
private static final Logger LOG = LogManager.getLogger(DiagnosticListener.class);
#Override
public void contextInitialized( final ServletContextEvent sce ) {
LOG.info("Context initialized.");
}
#Override
public void contextDestroyed( final ServletContextEvent sce ) {
LOG.info("Context destroyed.");
}
}
The initialized event is logged as expected, but the destroyed event never appears. I am assuming this is to do with how log4j2 manages its lifecycle using a similar listener, that logging infrastructure is no longer available during this event.
Is there a way to log an event for the application being shut down?
We clashed against a similar issue with Logback.
You have to write your own web.xml to fix that, because there's no alternatives to define listeners order.
We disabled the LogbackServletContextListener with:
<context-param>
<param-name>logbackDisableServletContainerInitializer</param-name>
<param-value>true</param-value>
</context-param>
then add the LogbackServletContextListener by hand as the first listener:
<listener>
<listener-class>ch.qos.logback.classic.servlet.LogbackServletContextListener</listener-class>
</listener>
and then all the other listeners.
No idea about log4j, but I think there's something similar...
edit: yes, there is:
<context-param>
<param-name>isLog4jAutoInitializationDisabled</param-name>
<param-value>true</param-value>
</context-param>
source: https://logging.apache.org/log4j/2.x/manual/webapp.html
If you configured Log4j's ServletContextListener before yours in web.xml then Log4j should be initialized before your ServletContextListener and be shutdown after yours is.
Related
I am trying to setup a configurable log4j configuration path for our webapp, so that by default this setting applies:
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/log4j2.xml</param-value>
</context-param>
and if, lets say, some environment variable is defined, then log4j2-test.xml is used. Can I somehow achieve this?
I've tried defining my own listener, which will take care of this, as follows:
web.xml contents:
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>/WEB-INF/log4j2.xml</param-value>
</context-param>
<context-param>
<param-name>isLog4jAutoInitializationDisabled</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>com.bla.blob.listener.Log4j2InitListener</listener-class>
</listener>
And the listener class:
public class Log4j2InitListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
if (System.getProperty("log4j.configurationFile") != null) { //override what is defined in web.xml
ctx.setInitParameter("log4jConfiguration", System.getProperty("log4j.configurationFile"));
}
ctx.addListener(new Log4jServletContextListener());
final FilterRegistration.Dynamic filter = ctx.addFilter("log4jServletFilter",
new Log4jServletFilter());
if (filter != null) {
filter.setAsyncSupported(true);
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
#Override
public void contextDestroyed(ServletContextEvent sce) { }
}
But for some strange reason this solution is not tomcat compatible (though works on jetty) -
java.lang.IllegalArgumentException: Once the first ServletContextListener has been called, no more ServletContextListeners may be added.
at org.apache.catalina.core.ApplicationContext.addListener(ApplicationContext.java:1386)
at org.apache.catalina.core.ApplicationContextFacade.addListener(ApplicationContextFacade.java:659)
Also, apache moved everything webapp related out of core, making everything package private, so you can't fiddle with classes by yourself anymore (yet http://logging.apache.org/log4j/2.x/manual/webapp.html still suggest you can use Log4jWebLifeCycle, for instance).
Any help will be greatly appreciated.
I want to insert some data into the database using the application API if the application has started up with some empty database tables. How do I go about this?
I'm using Spring 3.1, Hibernate 4.1.1.
[edit]
Thanks to AlexR the answer is to sublcass ContextLoaderListener, call super in contextInitialized and then do whatever it is you need to do:
public class MyContextLoaderListener extends ContextLoaderListener {
public void contextInitialized(final ServletContextEvent event) {
super.contextInitialized(event);
// ... doStuff();
}
}
You may also need to wire this up in web.xml instead of the Spring one:
<listener>
<listener-class>com.example.MyContextLoaderListener</listener-class>
</listener>
You can use spring context listener.
Take a look on: http://fusesource.com/docs/framework/2.2/deploy_guide/CXFServletDeploySpring.html
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/context/ContextLoaderListener.html
http://forum.springsource.org/showthread.php?88896-Spring-MVC-3-is-ContextLoaderListener-needed
Is there any spring specific way in the framework to perform initialization when MVC loads up?
Say I need to create global objects based on configuration files, is there a place to do this or do I just create my own servlet and do this in oninit?
What about standard #PostConstruct?
#Service
class AnySpringBean {
#PostConstruct
public void init() {
//run when bean is created
}
}
Works on #Controllers as well.
UPDATE: The more global place would be to subclass ContextLoaderListener) and override contextInitialized() and use it in web.xml (see user1076371 answer). I don't like this approach much, but at least the initialization is not tied to any Spring bean.
There is an ApplicationListener interface you can implement to hook into the startup completion event. I use this in my app to do things after I know Spring has finished starting up. I have a few different classes that I want to kick off background threads after the system is "up" and each implements this interface to do their particular post startup stuff.
It's sent after the app as a whole is done, but it's each listening spring bean that gets an event, so you'd could hook it into some existing bean or to create something like a PostStartupBean that exists only to implement this one method.
public void onApplicationEvent( ApplicationEvent applicationEvent )
{
if ( applicationEvent instanceof ContextRefreshedEvent )
{
..do stuff here..
}
}
Best thing would be to leave it to Spring. Add your global object beans (which could be singletons etc..) to your application context. Make sure Spring is initialized when the application is loaded by adding ContextLoaderListener to the web.xml.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4">
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
You can access your global objects from the WebApplicationContext anywhere in the application.
How do I automatically trigger Java function to stop Quartz scheduler jobs when I deploy/undeploy/redeploy JEE5 application in Glassfish.
Implement ServletContextListener and hook on contextDestroyed().
Basic example:
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Write code here which should be executed on webapp startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Write code here which should be executed on webapp shutdown.
}
}
and register it as a <listener> in web.xml.
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
Once you get to JAVA EE-6+, annotate a class with #WebListener and implement ServletContextListener on that class to get a shutdown notification. No need to deal with web.xml. See here
I need to remove temp files on Tomcat startup, the pass to a folder which contains temp files is in applicationContext.xml.
Is there a way to run a method/class only on Tomcat startup?
You could write a ServletContextListener which calls your method from the contextInitialized() method. You attach the listener to your webapp in web.xml, e.g.
<listener>
<listener-class>my.Listener</listener-class>
</listener>
and
package my;
public class Listener implements javax.servlet.ServletContextListener {
public void contextInitialized(ServletContext context) {
MyOtherClass.callMe();
}
}
Strictly speaking, this is only run once on webapp startup, rather than Tomcat startup, but that may amount to the same thing.
You can also use (starting Servlet v3) an annotated aproach (no need to add anything to web.xml):
#WebListener
public class InitializeListner implements ServletContextListener {
#Override
public final void contextInitialized(final ServletContextEvent sce) {
}
#Override
public final void contextDestroyed(final ServletContextEvent sce) {
}
}
I'm sure there must be a better way to do it as part of the container's lifecycle (edit: Hank has the answer - I was wondering why he was suggesting a SessonListener before I answered), but you could create a Servlet which has no other purpose than to perform one-time actions when the server is started:
<servlet>
<description>Does stuff on container startup</description>
<display-name>StartupServlet</display-name>
<servlet-name>StartupServlet</servlet-name>
<servlet-class>com.foo.bar.servlets.StartupServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>