I’ve moved from Resin-3.0 to Tomcat-5.5 for a while now. So far, it’s actually been easier to deploy and run my web applications. However, I started to see some NotSerializableExceptions during shutdowns, and subsequent startups. After some research, it became clear that I was inadvertently storing Spring-proxied items into http sessions. If they still existed during serialization of the current session, Tomcat would complain, being that Tomcat actually expects truly serializable items in its sessions – how dare they 🙂
I knew the item being placed in the http session held a reference to a spring-wrapped DAO (the item’s a data view helper). So, after some cups of coffee it finally came to me: have my custom listener defined in the web.xml, which currently only implements ServletContextListener, also implement HttpSessionListener. With that, I was able clear out the culprit on sessionDestroyed(…). I wish all my technical problems were this straight forward.
Update: Anjan, in response to your question, I’m embedding the web.xml declaration and the actual Listener code. Hope this helps.
web.xml listener declaration
<!-- Our custom system initializing listener -->
<listener>
<listener-class>com.some.company.SomeInitListener</listener-class>
</listener>
Listener Implementations
public class SomeInitListener extends BosenInitListener {
private static final Logger LOG = Logger.getLogger(SomeInitListener.class);
@Override
protected void customInitForServlet(ServletContextEvent event) {
super.customInitForServlet(event);
ServletContext servletContext = event.getServletContext();
SomeResourceInitializer resourceInitializer = (SomeResourceInitializer) getBean(servletContext, "resourceInitializer");
// Make default menus
String menuConfig = servletContext.getInitParameter(AppConstants.MENU_CONFIG);
if (null != menuConfig) {
...
} else {
LOG.warn("Could not find a menu configuration.");
}
resourceInitializer.init();
}
}
public abstract class BosenInitListener implements ServletContextListener, HttpSessionListener {
private static final Logger LOG = Logger.getLogger(BosenInitListener.class);
public final void contextInitialized(final ServletContextEvent event) {
String servletContextName = event.getServletContext().getServletContextName();
if (LOG.isInfoEnabled()) {
LOG.info("Initializing [ " + servletContextName + " ]");
}
customInitForServlet(event);
if (LOG.isInfoEnabled()) {
LOG.info("Initialized [ " + servletContextName + " ]\n\n");
}
}
public final void contextDestroyed(final ServletContextEvent event) {
String servletContextName = event.getServletContext().getServletContextName();
if (LOG.isInfoEnabled()) {
LOG.info("Destroying [ " + servletContextName + "]");
}
customDestroyForServlet(event);
if (LOG.isInfoEnabled()) {
LOG.info("Destroyed [ " + servletContextName + "]\n\n");
}
}
protected void customInitForServlet(final ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
String appContextName = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object webCtxAttribute = servletContext.getAttribute(appContextName);
if (null != webCtxAttribute) {
if (webCtxAttribute instanceof Exception) {
LOG.error(webCtxAttribute);
}
} else {
throw new IllegalStateException("Could not read applicationContext.xml.");
}
}
protected void customDestroyForServlet(final ServletContextEvent event) {
//no-op
}
public final void sessionCreated(final HttpSessionEvent event) {
String sessionId = event.getSession().getId();
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing a new Session [" + sessionId + "]");
}
customInitForSession(event);
if (LOG.isDebugEnabled()) {
LOG.debug("Initialized a new Session [" + sessionId + "]");
}
}
public final void sessionDestroyed(final HttpSessionEvent event) {
String sessionId = event.getSession().getId();
if (LOG.isDebugEnabled()) {
LOG.debug("Destroying an existing Session [" + sessionId + "]");
}
customDestroyForSession(event);
if (LOG.isDebugEnabled()) {
LOG.debug("Destroyed an existing Session [" + sessionId + "]\n\n");
}
}
protected void customInitForSession(final HttpSessionEvent event) {
//no-op
}
protected void customDestroyForSession(final HttpSessionEvent event) {
HttpSession session = event.getSession();
session.removeValue(BosenConstants.SESSION_PAGINATED_SEARCH_RESULTS);
session.removeAttribute(BosenConstants.SESSION_PAGINATED_SEARCH_RESULTS);
}
protected static ApplicationContext getApplicationContext(ServletContext servletContext) {
String appContextName = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
return (ApplicationContext) servletContext.getAttribute(appContextName);
}
protected static Object getBean(ServletContext servletContext, String name) {
return getApplicationContext(servletContext).getBean(name);
}
hi there,
thanks for sharing with us.
I am getting these errors some times as well and though that I must be having some non-serializable object in the session.
“have my custom listener defined in the web.xml, which currently only implements ServletContextListener, also implement HttpSessionListener. ”
Can you elaborate on that ? I’m not sure I understand exactly what steps you took.
thank you,
BR,
~A
@Anjan: Thanks for your question. I’ve updated the post with an answer.