Question

I am running my application on GlassFish, I use Spring Security and Hibernate. When I run the application the following warning and errors will be shown on GlassFish console. How can I avoid them?

WARNING:   The web application [] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
SEVERE:   The web application [] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@1087985b]) and a value of type [org.hibernate.internal.SessionImpl] (value [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
SEVERE:   The web application [] created a ThreadLocal with key of type [net.sf.json.AbstractJSON$1] (value [net.sf.json.AbstractJSON$1@362386d7]) and a value of type [java.util.HashSet] (value [[]]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
SEVERE:   The web application [] created a ThreadLocal with key of type [net.sf.json.AbstractJSON$1] (value [net.sf.json.AbstractJSON$1@362386d7]) and a value of type [java.util.HashSet] (value [[]]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

hibernate.cfg.xml

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/myproject
        </property>
        <property name="connection.username">root</property>
        <property name="connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">12</property>

        <!-- SQL dialect -->
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>



        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

<!--         Disable the second-level cache -->

<!-- <property name="cache.provider_class">
            org.hibernate.cache.EhCacheProvider
        </property>

        <property name="hibernate.cache.use_query_cache">true</property>-->


        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

HibernateUtil.java

public class HibernateUtil {

   private static ServiceRegistry serviceRegistry;
   private static final ThreadLocal<Session> threadLocal = new ThreadLocal();
   private static SessionFactory sessionFactory;

   private static SessionFactory configureSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.configure();
            serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();

            sessionFactory = configuration.buildSessionFactory(serviceRegistry);

            return sessionFactory;
        } catch (HibernateException e) {
            System.out.append("** Exception in SessionFactory **");
            e.printStackTrace();
        }
       return sessionFactory;
  }     

  static {
    try {
      sessionFactory = configureSessionFactory();
    } catch (Exception e) {
      System.err.println("%%%% Error Creating SessionFactory %%%%");
      e.printStackTrace();
    }
  }

  private HibernateUtil() {
  }

  public static SessionFactory getSessionFactory() {
    return sessionFactory;
  }

  public static Session getSession() throws HibernateException {
    Session session = threadLocal.get();

    if (session == null || !session.isOpen()) {
      if (sessionFactory == null) {
        rebuildSessionFactory();
      }
      session = (sessionFactory != null) ? sessionFactory.openSession() : null;
      threadLocal.set(session);
    }

    return session;
  }

  public static void rebuildSessionFactory() {
    try {
      sessionFactory = configureSessionFactory();
    } catch (Exception e) {
      System.err.println("%%%% Error Creating SessionFactory %%%%");
      e.printStackTrace();
    }
  }

  public static void closeSession() throws HibernateException {
    Session session = (Session) threadLocal.get();
    threadLocal.set(null);

    if (session != null) {
      session.close();
    }
  }
}
Was it helpful?

Solution

These are error messages that can happen in the case of application redeployments while the server is kept running.

If it's a shutdown scenario or a development redeployment these messages can be safely ignored, they only become important if you need redeployments in production, which is rare. Most of the times even in production we want to stop the server process and fully restart it. This is some details on each of the messages meaning:

Message 1 - driver not unregistered when the application stopped:

WARNING: The web application [] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

JDBC drivers are registered at startup in a singleton at the level of the JVM, and this is meant to be done by the server, by publishing the driver jar in a folder at the server level.

In this case the application seems to carry the driver itself, which is not the way drivers are meant to be deployed.

To fix this remove the driver from the application and register it at the level of the server instead. If multiple applications have the same driver this will also cause memory leaks - see this answer as well.

Message 2 - ThreadLocal not cleaned up:

SEVERE: The web application [] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@1087985b]) and a value of type [org.hibernate.internal.SessionImpl] but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

This means that one application spring thread stored a Hibernate session in the thread (each thread as a data store where things can be attached via ThreadLocal).

But the thread did not clean up the session when the application was restarted, so this variable stored at the thread can be visible AFTER the redeployment when the thread gets reused.

This might be surprising but worst the session was pointing to other objects, which themselves where pointing to classes, which where pointing to the old classloader before the redeploy.

This means a large portion of the object tree will NOT be garbage collected due to this leaked 'link' to the objects of the previous deployment. The result is a ClassLoader Memory Leak.

The message says that this scenario MIGHT happen due to uncleaned ThreadLocals, and that some preventive measures are going to be put in place (start killing threads and creating new ones instead of pooling, to get rid of the leaked thread locals).

As a summary, these messages can be safely ignored if you don't need redeployments in production and always restart the server.

OTHER TIPS

To get rid of the JDBC driver warning, run the following on application shutdown:

String url = "your JDBC url";
Driver driver = DriverManager.getDriver(url);
DriverManager.deregisterDriver(driver);

If you are using Spring beans, you can put this in the destroy() method of a DisposableBean.

In a servlet environment, you can use a ServletContextListener:

public class CleanupListener implements ServletContextListener {

    public void contextDestroyed(ServletContextEvent arg0) {
        // enter cleanup code here
    }

    public void contextInitialized(ServletContextEvent arg0) { }
}

Set it up in web.xml:

<listener>
    <listener-class>com.example.CleanupListener</listener-class>
</listener>

JDK6 onward the JDBC driver automatically get loaded if any Driver class found in classpath without Class.forName() method and It may lead the such error messages. Better to write a Listener for your application and de-register every driver at the time of application shutdown. You can get all registered driver using DiverManager#getDrivers() method and can de-register one by one.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top