Using ServletContextListener and JNDI to run a clean Web Application shutdown

Key Facts and Overview

  • ServletContextListener runs contextInitialized() during a deployment operation
  • ServletContextListener runs contextDestroyed() during a un-deployment operation
  • This allows to cleanup or initialize certain objects factories, loggers, ..
  • Without a clean shutdown you may encounter following Exception during redeployment of a WEB applications
     12:54:21.020 Object: com.hhu.wfjpa2el.Emp2[ empno=9997 ] is not a known entity type.
     12:54:21.020 java.lang.IllegalArgumentException: Object: com.hhu.wfjpa2el.Emp2[ empno=9997 ] is not a known entity type.
         at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectForPersist(UnitOfWorkImpl.java:4228)
         at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:496)
         at com.hhu.wfjpa2el.JPATestBean.runJPA(JPATestBean.java:137)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

  • To solve this reboot your Webserver or shutdown() the Entity Manager Factory

JAVA Code

  • JPATestBean() constructor stores/binds a object reference of the current  JPATestBean() reference via JNDI
  • shutdown() is invoked by  ServletContextListener function contextDestroyed() and stops the Entity Manager Factory.
  • ServletContextListener is using JNDI lookup to get the object reference
JPATestBean.java  
public JPATestBean() throws Exception
      {
        ctx = new InitialContext();
        ctx.rebind(jndiName, this);  
        logger.info("Constructor JPATestBean()  called and bound to JNDI !");
      }
 
 static 
      {
        emf = Persistence.createEntityManagerFactory("jpaELPU");
        threadLocal = new ThreadLocal<EntityManager>();       
      }
 public void shutdown()
      {
        logger.info("\nWeb Application shutdown ..." );
        closeEntityManager();
        logger.info("Closing Entitiy Manager Factory ..." );
        emf.close();
        emf=null; 
        System.gc();
        logger.info("Leaving Web Application shutdown" );
      }
...

Implementing ServletContextListener 
  -  contextDestroyed() uses JNDI lookup to read an object reference from our  JPATestBean() instance
  -  contextDestroyed() invokes shutdown methode to run a clean application shutdown

web.xml entry
./src/main/webapp/WEB-INF/web.xml
..
  <listener>
        <listener-class>com.hhu.wfjpa2el.ServletContextListenerImpl</listener-class>
    </listener>


Java Code for ServletContextListener
package com.hhu.wfjpa2el;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.slf4j.LoggerFactory;

public class ServletContextListenerImpl implements ServletContextListener 
  {
    final static org.slf4j.Logger logger = LoggerFactory.getLogger(ServletContextListenerImpl.class);
    private JPATestBean jtb;    
    private final String jndiName = "java:global/myJPATestBean";
    private InitialContext ctx;
    
    @Override
    public void contextInitialized(ServletContextEvent arg) {
        logger.info("+++ ServletContextListener : contextInitialized - no action \n");
 
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg) 
     {
        System.out.println("+++ ServletContextListener: running contextDestroyed()....");
        try
          {
            ctx = new InitialContext();
            jtb = (JPATestBean)ctx.lookup(jndiName);
            jtb.shutdown();
            logger.info("+++ ServletContextListener:  Leaving contextDestroyed() without Exception\n");
          } catch (NamingException ex)
          {
            //Logger.getLogger(ServletContextListenerImpl.class.getName()).log(Level.SEVERE, null, ex);
            logger.error("+++ Error in contextDestroyed()\n" + ex);
          }
     }
}

Reference

 

Leave a Reply

Your email address will not be published. Required fields are marked *