When I shut down Tomcat, I observe a correct shutdown and cleanup of the Spring WebApplicationContext. However, when I redeploy my Spring-based WAR (by copying the new WAR to webapps), normal shutdown does not occur. This is a problem for me due to all the ensuing resource leaks:
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named [hz.hazelcast-swipe-instance.scheduled] but has failed to stop it. This is very likely to create a memory leak.
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named [hz.hazelcast-swipe-instance.operation.thread-0] but has failed to stop it. This is very likely to create a memory leak.
... and many more. I am using XML-less configuration, this is my WebApplicationInitializer:
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
}
#Override protected Class<?>[] getServletConfigClasses() { return null; }
#Override protected String[] getServletMappings() { return new String[] { "/" }; }
#Override public void onStartup(ServletContext ctx) throws ServletException {
ctx.setInitParameter("spring.profiles.active", "production");
super.onStartup(ctx);
}
}
There is no configuration specific to controlling the behavior upon servlet context reload, and I assume this should have worked out of the box.
Is there a way to make the WebApplicationContext close properly before continuing the servlet context reloading procedure?
I am on Spring 4.0.5, Tomcat 7.0.54, Hazelcast 3.2.1, Hibernate 4.3.4.Final.
Update
I have added a Spring application listener for the ContextClosedEvent and printed the stack trace of its invocation:
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:333) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:880) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:841) [spring-context-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.destroy(FrameworkServlet.java:819) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
at org.apache.catalina.core.StandardWrapper.unload(StandardWrapper.java:1486) [catalina.jar:7.0.54]
at org.apache.catalina.core.StandardWrapper.stopInternal(StandardWrapper.java:1847) [catalina.jar:7.0.54]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232) [catalina.jar:7.0.54]
at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5647) [catalina.jar:7.0.54]
at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232) [catalina.jar:7.0.54]
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1575) [catalina.jar:7.0.54]
at org.apache.catalina.core.ContainerBase$StopChild.call(ContainerBase.java:1564) [catalina.jar:7.0.54]
This indicates that the Spring shutdown occurs in its Servlet#destroy method. This is the relevant snippet from AbstractApplicationContext#close():
if (logger.isInfoEnabled()) {
logger.info("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
try {
getLifecycleProcessor().onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
synchronized (this.activeMonitor) {
this.active = false;
}
I see the log entry from the start of this snippet, and I get my ContextClosedEvent. I also see an entry DefaultLifecycleProcessor - Stopping beans in phase 2147483647, which probably comes from the getLifecycleProcessor.onClose() line. It seems that some error occurs downstream from that. Some exception may be swallowed.
Update 2
As requested, this is how I configure Hazelcast:
#Bean(destroyMethod="shutdown") public HazelcastInstance hazelcast() {
final Config c = hzConfig();
final JoinConfig join = c.getNetworkConfig().getJoin();
join.getMulticastConfig().setEnabled(false);
join.getTcpIpConfig().setEnabled(true);
return getOrCreateHazelcastInstance(c);
}
hzConfig() is a method where instance name, group name and password, map names, and map indices are configured, so I don't think it is of interest here.
And this is my Hibernate SessionFactory config:
#Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean b = new LocalSessionFactoryBean();
b.setDataSource(dataSource);
b.setHibernateProperties(props(
"hibernate.connection.release_mode", "on_close",
"hibernate.id.new_generator_mappings", "true",
"hibernate.hbm2ddl.auto", "update",
"hibernate.order_inserts", "true",
"hibernate.order_updates", "true",
"hibernate.max_fetch_depth", "0",
"hibernate.jdbc.fetch_size", "200",
"hibernate.jdbc.batch_size", "50",
"hibernate.jdbc.batch_versioned_data", "true",
"hibernate.jdbc.use_streams_for_binary", "true",
"hibernate.use_sql_comments", "true"
));
return b;
}
At some point, you mentioned that there was a NoClassDefFoundError for Logback. You got this fixed by removing this dependency, but then the problem moved to a another class - one of Spring's own classes.
This can mean that either one of the libraries you have does something buggy with class loaders or maybe Tomcat needs instructions not to keep locks on some resources. See here more about Tomcat resources being locked and the <Context> setting to try: in your Tomcat's conf/context.xml place a antiResourceLocking="true" to the element.
Have you tried upping unloadDelay (defaults to 2000ms) for Tomcat contexts? See http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
UPDATE: I see that you are having issues with logback as well, it might be worth the shot to try and register this listener as well:
class LogbackShutdownListener implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent event) {
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
System.out.println("Shutting down Logback context '" + loggerContext.getName() + "' for " + contextRootFor(event));
loggerContext.stop();
}
#Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("Logback context shutdown listener registered for " + contextRootFor(event));
}
private String contextRootFor(ServletContextEvent event) {
return event.getServletContext().getContextPath();
}
}
Be sure to declare this listener before the spring context loader listener so that it is invoked after the context listener upon shutdown.
UPDATE 2: Also it might be worth the try to register another bean to handle closing of the Hazelcast stuff manually (be sure to also remove destroyMethod from the hazelcast bean):
#Component
class HazelcastDestructor {
#Autowired
private HazelcastInstance instance;
#PreDestroy
public void shutdown() {
try {
instance.shutdown();
} catch (Exception e) {
System.out.println("Hazelcast failed to shutdown(): " + e);
throw e;
}
}
}
UPDATE 3: Just out of curiosity, have you tried parallel deployment: http://www.javacodegeeks.com/2011/06/zero-downtime-deployment-and-rollback.html. It might behave differently than reloading the very same context. At the very least you should be able to undeploy the old version lazily and see if that makes a difference.
There is a similar issue on the dangling threads while container restarting here.
Of all the answers, one particular answer of interest was by Howard - which shows the way these threads are cleared.
There is some good discussion and reasoning as to how this can terminate the threads here.
Now implement ServletContextListener and take care of these threads in the contextDestroyed() method as:
public class YourListener implements ServletContextListener{
....
#Override
public void contextDestroyed(ServletContextEvent event) {
//Call the immolate method here
}
}
Register this listener in WebApplicationInitilizer as:
ctx.addListener(new YourListener());
So when server is restarted - contextDestroyed method is called and this takes care of all these threads.
From Web App development point of view, ServletContainer can only notify the before started and before end process of app.
It is using ServletContextListener.
Config ServletContextListener in web.xml
<listener>
<listener-class>com.var.YourListener</listener-class>
</listener>
YourListener.java
public class YourListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
//initialization process
}
public void contextDestroyed(ServletContextEvent sce) {
//destory process
}
}
Update -XML Less
Programmatically
#Override
public void onStartup(ServletContext ctx) throws ServletException {
ctx.addListener(new YourContextListener());
ctx.setInitParameter("spring.profiles.active", "production");
super.onStartup(ctx);
}
Annotation
#WebListener / #WebServletContextListener
public class YourContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
Update- ShoutDown Hook In Spring
I never use it before my app development, we can register shoutdownhook event into AbstractApplicationContext of Spring.
I am not sure it will be ok or not for you.
AbstractApplicationContext context = ...
context.registerShutdownHook();
Reference 1 Reference 2
Related
I am seeing a strange problem when using JAVA AWS s3 client. When I make multiple deployments(tomcat is not restarted, just war files are updated), the heap size remains the same but non heap size keeps on increasing. Turns out the classes are not unloaded when the application is undeployed.
My application has a simple context listener, which initialises an AWS S3 client and shuts it down when the application context is destroyed.
Here is the code:
#WebListener
public class ContainerContextClosedHandler implements ServletContextListener {
private static AmazonS3 s3Client;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
s3Client = AmazonS3ClientBuilder.standard().build();
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
try {
System.out.println("shutting down s3Client");
if (s3Client != null) {
s3Client.shutdown();
}
com.amazonaws.http.IdleConnectionReaper.shutdown();
} catch (Throwable t) {
// log the error
}
}
}
How can I unload the classes when the application is undeployed.
As posted in my previous comment, I found that AwsSdkMetrics bean was the culprit.
So I added this statement in my contextDestroyed method
AwsSdkMetrics.unregisterMetricAdminMBean();
to unregister MetricAdminMBean.
Thanks a ton to Mattias for suggesting his wonderful library which helped me figure the root cause.
BackGround
After reading from 1 2 3 4 5 6 Links I reached the following conclusion-
As Spring mvc designed over standered servlets,and facilitate same functionality of servlet context and application context.In spring there is two type of context ApplicationContext and WebApplicationContext-
ApplicationContext initialise by ContextLoaderListener,single instanse per application.
WebApplicationContext loaded by per DispatcherServlet.
We can understand above like this ApplicationContext extends by WebApplicationContext so what ever stuff associated with ApplicationContext at the end this is part of WebApplicationContext.
Doubts
ApplicationContextAware offers which context object.
public class SomeThing implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeanException {
//this context object is `ApplicationContext` or `WebApplicationContext`?
}
}
context and container seems synonyms to most of us,I want to
give an example.Let say we have two dispatcher servlet one for
rest and other for mvc.
First Dispatcher-
public class RestInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[] { "/rest/*" };
}
}
Second Dispatcher-
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[] {
"/mvc/*"
};
}
}
than here there is two instance of WebApplicationContext,those
common part is loaded by ContextLoaderListner as define in
rootContext.
I am not sure, but there must not be 2 IocContainer in a single SpringApplication.
BeanFactory ie SpringIocContainer is,where all the bean object
lives,what ever objects we associates with WebApplicationContext is
part of Spring container,how does this container initialised by
WebApplicationContext?I want to want to know how does they both
associated with each other?
And whenever we did ctx.getBean()- this returns object from spring
container,how does this communication between context and container
happens?
There is a similar answer that denies the both are same,it says
Spring comes with several container implementations,Both load bean definitions, wire beans together, and dispense beans upon request,but an ApplicationContext offers much more.
So my point is why Both load bean definitions, wire beans together,this is kind of rework?
One more thing even though web-app is spring driven or not, there must be a context which standard servlet provides and used in Http communication......
Spring follows this or spring handles this in some other manner.And in spring context means a just IOC container, of which some part is loaded by DispacherServlet and some part is loaded by ContextLoaderListner and can facilitate much more such as I18N,access to static resource etc..
Basically, in a spring MVC application the spring contexts are registered in the servlet context of the web application. You can do that in the web.xml file setting the spring ContextLoaderListener or with java configuration. In the comments I pointed out this link where it explains how this is done via java configuration classes:
spring: where does `#autowired` look for beans?
There you can see how the 'connection' is done. Then, you asked in the comments what this achieves:
WebApplicationContextUtils.getWebApplicationContext(myServlet.getServletContext())
If you check the code of that class you can see it gets the WebApplicationContext from the attributes of the ServletContext. These attributes are set in the initialization of the web application. If you notice, in the ContextLoader class (parent of ContextLoaderListener), in the initWebApplicationContext method it sets these attributes to the servlet context:
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{#link #CONTEXT_CLASS_PARAM contextClass}" and
* "{#link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* #param servletContext current servlet context
* #return the new WebApplicationContext
* #see #ContextLoader(WebApplicationContext)
* #see #CONTEXT_CLASS_PARAM
* #see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
That is done in this line:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
As you can see, it is stored in the same place where you are trying to get it with the WebApplicationContextUtils.getWebApplicationContext(myServlet.getServletContext()) :
/**
* Find the root {#code WebApplicationContext} for this web app, typically
* loaded via {#link org.springframework.web.context.ContextLoaderListener}.
* <p>Will rethrow an exception that happened on root context startup,
* to differentiate between a failed context startup and no context at all.
* #param sc ServletContext to find the web application context for
* #return the root WebApplicationContext for this web app, or {#code null} if none
* #see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
*/
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
So as you can see all the answers to your doubts are in the code that spring executes during the startup of the web application.
Hope I answered your question.
For Your Doubt 1
In an spring application there is a single instance of context Which is WebAplicationCntext per DispatcherServlet.Which can be refer by a super Interface ApplicationContext-
public class SomeThing implements ApplicationContextAware{
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeanException{
//this context object is `WebApplicationContext` which is refer by `ApplicationContext`.
}
}
In spring , context means a just IOC container, of which some part is loaded by DispacherServlet and some part is loaded by ContextLoaderListner and can facilitate much more such as I18N,access to static resource etc
Your above understanding is almost correct.In Spring All the WebApplicationContext object shares some common reference which is rootContext.
This answer desn't include answer of doubt2, doubt3 ,and why all context perform same task.
I'd like to create configuration/bean to automatically start H2DB in my development profile. I'd like to have it running as a tcp server. It's needed to be started before any DataSource configuration. Can someone tell me how to achieve this?
Wha have I done is
#Profile("h2")
#Component
public class H2DbServerConfiguration implements SmartLifecycle {
private static final Logger logger = LoggerFactory.getLogger(H2DbServerConfiguration.class);
private Server server;
#Override
public boolean isAutoStartup() {
return true;
}
#Override
public void stop(Runnable callback) {
stop();
new Thread(callback).start();
}
#Override
public void start() {
logger.debug("############################################");
logger.debug("############################################");
logger.debug("STARTING SERVER");
logger.debug("############################################");
logger.debug("############################################");
try {
server = Server.createTcpServer("-web", "-webAllowOthers", "-webPort", "8082").start();
} catch (SQLException e) {
throw new RuntimeException("Unable to start H2 server", e);
}
}
#Override
public void stop() {
logger.debug("############################################");
logger.debug("############################################");
logger.debug("STOPPING SERVER");
logger.debug("############################################");
logger.debug("############################################");
if (server != null)
if (server.isRunning(true))
server.stop();
}
#Override
public boolean isRunning() {
return server != null ? server.isRunning(true) : false;
}
#Override
public int getPhase() {
return 0;
}
}
but this isn't an option for me because component is created after datasource (I have liquibase setup so it's too late) and Phase is still the same that means FIFO order and I'd like to be FILO.
Mix #Profile and #Component seams to me a bad idea. Profiles are designed to work with Configuration (documentation)
Do you really need profile? In my opinion it makes sense if you have several possible configurations, one based on H2, and if you want be able to switch between these configurations (typically at start time by setting a properties...)
Manage the H2 server with a bean (documentation) seams correct to me (as suggested by Stefen). Maybe you will prefer annotations... If you want a spring profile, then you will need a Configuration object too. It will simply load the H2 server bean (in my opinion it's better to manage the H2 server lifecycle with a bean than with a context/config).
Create your server as a bean :
#Bean(initMethod = "start", destroyMethod = "stop")
Server h2Server() throws Exception {
return Server.createTcpServer("-tcp","-tcpAllowOthers","-tcpPort","9192");
}
Now you can configure spring to create other beans (e.g the datasource)
after the bean h2Server using #DependsOn
#DependsOn("h2Server")
#Bean
DataSource dataSource(){
...
}
Hi, what about using spring boot? It has automatically configured datasource so I don't want to reconfigure it.
You are right, to use the above approach you have to create your own datasource in order to annotate it with #DependsOn .
But it looks like this is not really necessary.
In one of my projects I am creating the h2Server as a bean as described.
I use the datasource created by spring, so without any #DependsOn.
It works perfectly. Just give it a try.
Your solution with SmartLifecycle does not work, because it creates the server on ApplicationContext refresh, which happens after all beans (including the datasource ) were created.
i have a problem is:
java.lang.Exception: ServletConfig has not been initialized
I searched for it nearly 2 days but i did not have a solution for me. Every one had said that
super.init(config) must be used. I have tried this, but there is nothing change for me.
My init method;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
AppServiceServlet service = new AppServiceServlet();
try {
service.getir();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
AutoCheckStatus.autoCheckStatus(600000);
}
and my AppServiceServlet;
public List<SswAppServiceDto> getir() throws Exception {
try {
final WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this
.getServletContext());
setiAppServiceBusinessManager((IAppServiceBusinessManager) context.getBean(BEAN_ADI));
List<SswAppService> result = getiAppServiceBusinessManager().getir();
List<SswAppServiceDto> list = DtoConverter.convertSswAppServiceDto(result);
for (int i = 0; i < result.size(); i++) {
AppService appService = new AppService();
appService.setServiceName(result.get(i).getName());
appService.setUid(result.get(i).getServiceUid());
appService.setHost(result.get(i).getHost());
appService.setPort((int) result.get(i).getPort());
SystemConfiguration.appServiceList.put(appService.getUid(), appService);
}
return list;
} catch (RuntimeException e) {
throw new Exception(e.getMessage(), e.getCause());
}
}
The exception is thrown in this line;
final WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
in AppServiceServlet and says:
java.lang.Exception: ServletConfig has not been initialized.
Pls help.
This call:
AppServiceServlet service = new AppServiceServlet();
Instantiates a servlet instance via new, which circumvents the normal, container managed creation of a servlet. As such, critical class variables (for example, the servlet config) don't get properly initialized.
Later on, you are making a call to getServletContext, which simply redirects to getServletConfig().getServletContext(), but because the servlet configuration was never completed you get an exception.
Infact, calling new on a servlet the way you are is non-compliant with the specification - servlets are supposed to be maintained by the web app container. The proper way to launch a startup servlet is either via configuration in your web.xml file, or via annotation.
I'm embedding Jetty in a similar manner as described here. When the RequestLogHandler can't open the specified logfile, it throws an exception which is unfortunately caught by org.eclipse.jetty.server.Server and swallowed (but logged first, at least). This means that there's no obvious way for me to tell if the log handler was started correctly.
Is there a way that I'm missing to detect when a handler couldn't start?
This idea is based on the implementation of WebAppContext where you can use WebAppContext.getUnavailableException() to determine whether the context was initialized successfully.
Simply replace the default implementation of Server and Context with your own:
public static class MyContext extends Context {
private Exception _exception;
#Override
protected void doStart() throws Exception {
try {
super.doStart();
} catch (final Exception e) {
_exception = e;
}
}
#Override
protected void doStop() throws Exception {
try {
super.doStop();
} finally {
_exception = null;
}
}
public Exception getException() {
return _exception;
}
}
public static class MyServer extends Server implements InitializingBean {
public void afterPropertiesSet() throws Exception {
start();
for (final Handler h : getHandlers()) {
if (h instanceof MyContext) {
final MyContext c = (MyContext) h;
if (c.getException() != null) {
throw new RuntimeException("failed to init context " + c.getDisplayName(),
c.getException());
}
}
}
}
}
In your beans.xml, simply replace org.mortbay.jetty.Server (and remove init-method="start") and org.mortbay.jetty.servlet.Context with your own implementations.
This code is for Jetty 6 though (as is the example you linked to), as that's what I have around. I didn't test it though, but it's pretty much the same as we are successfully using in conjunction with WebAppContext. In order to extend this to RequestLogHandler, you could either do the same for just any handler you are using or create a decorator to wrap any handler. You may want to look at org.mortbay.jetty.handler.HandlerWrapper for this purpose.
How about modifying the jetty code? You could add some simple println statements in strategic places in the RequestLogHandler which would indicate to you whether or not the handler was started.