Question

When I shutdown my webApp. Tomcat constantly shows SEVERE warning:

02-Mar-2014 23:07:30.890 SEVERE [http-apr-8080-exec-4]  org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks The web application   [/movie-collection-0.0.2] created a ThreadLocal with
key of type [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1] (value [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1@520a38a8]) and a value of type [com.microsoft.sqlserver.jdbc.ActivityId] (val
ue [3488cbb4-f0e2-4505-93c4-78248b161847-2]) 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.
02-Mar-2014 23:07:30.891 SEVERE [http-apr-8080-exec-4]  org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks The web application [/movie-collection-0.0.2] created a ThreadLocal with
key of type [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1] (value  [com.microsoft.sqlserver.jdbc.ActivityCorrelator$1@520a38a8]) and a value of type  [com.microsoft.sqlserver.jdbc.ActivityId] (val
ue [ba8aeba9-de9e-49de-8732-5290ae65167b-0]) 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.
02-Mar-2014 23:07:31.848 INFO [http-apr-8080-exec-4] org.apache.catalina.startup.HostConfig.undeploy Undeploying context [/movie-collection-0.0.2]

I clearly see the message which describe the reason of a problem. But after struggling for 5 hours I still can not eliminate it. Here is my persistence config:

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class PersistenceContext {

private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";

private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT = "hibernate.use_sql_comments";
private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";

private static final String PROPERTY_NAME_JDBC_BATCH_SIZE = "hibernate.jdbc.batch_size";
private static final String PROPERTY_NAME_CACHE_PROVIDER_CLASS = "hibernate.cache.provider_class";

private static final String PROPERTY_NAME_C3P0_MIN_SIZE = "hibernate.c3p0.min_size";
private static final String PROPERTY_NAME_C3P0_MAX_SIZE = "hibernate.c3p0.max_size";
private static final String PROPERTY_NAME_C3P0_TIMEOUT_SIZE = "hibernate.c3p0.timeout";
private static final String PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE = "hibernate.c3p0.max_statements";
private static final String PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE = "hibernate.c3p0.idle_test_period";

private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

@Resource
private Environment environment;

@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
    dataSource.setUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
    dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
    dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
    return dataSource;
}

@Bean
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setHibernateProperties(getHibernateProperties());
    sessionFactory.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
    return sessionFactory;
}

@Bean
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory().getObject());
    return transactionManager;
}

private Properties getHibernateProperties() {
    Properties props = new Properties();
    props.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
    props.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
    props.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
    props.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
    props.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
    props.put(PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_USE_SQL_COMMENT));
    props.put(PROPERTY_NAME_JDBC_BATCH_SIZE, environment.getRequiredProperty(PROPERTY_NAME_JDBC_BATCH_SIZE));
    props.put(PROPERTY_NAME_CACHE_PROVIDER_CLASS, environment.getRequiredProperty(PROPERTY_NAME_CACHE_PROVIDER_CLASS));
    props.put(PROPERTY_NAME_C3P0_MIN_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MIN_SIZE));
    props.put(PROPERTY_NAME_C3P0_MAX_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MAX_SIZE));
    props.put(PROPERTY_NAME_C3P0_TIMEOUT_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_TIMEOUT_SIZE));
    props.put(PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_MAX_STATEMENTS_SIZE));
    props.put(PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE, environment.getRequiredProperty(PROPERTY_NAME_C3P0_IDLE_TEST_PERIOD_SIZE));

    return props;
}
}

And my properties:

#Database Configuration
 db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
 db.url=jdbc:sqlserver://localhost;integratedSecurity=true;databaseName=Abc
 db.username=""
 db.password=""

#Hibernate Configuration
 hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
 hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
 hibernate.cache.provider_class=org.hibernate.cache.internal.NoCachingRegionFactory
 hibernate.hbm2ddl.auto=validate
 hibernate.jdbc.batch_size=50

#Connection pool config
 hibernate.c3p0.min_size=2
 hibernate.c3p0.max_size=20
 hibernate.c3p0.timeout=300
 hibernate.c3p0.max_statements=50
 hibernate.c3p0.idle_test_period=3000

#SQL output format
 hibernate.format_sql=true
 hibernate.show_sql=true
 hibernate.use_sql_comments=false

#Declares the base package of the entity classes
 entitymanager.packages.to.scan=com.adc.domain

I would appreciate any ideas.

UPDATE

I added ServletContextListener with appropriate listener to deregister JDBC drivers. Still no results... I guess I will have to find a way to clear those ThreadLocals

public class DbConnectionCleaner implements ServletContextListener {

private static final Logger log = LogManager.getLogger(DbConnectionCleaner.class);

@Override
public void contextInitialized(ServletContextEvent sce) {
    log.info("Servlet context initialized");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    log.info("Cleaning DB connections");
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        try {
            DriverManager.deregisterDriver(driver);
            log.info(String.format("Deregistering jdbc driver: %s", driver));
        } catch (SQLException ex) {
            log.error(String.format("Error deregistering driver %s", driver), ex);
        }

    }
}
}
Was it helpful?

Solution

If your JDBC driver's JAR file is in Tomcat's lib/ directory, then there should probably be no leak: you won't be pinning the WebappClassLoader in memory. Also note that Tomcat is protecting you from the problem by recycling (i.e. destroying and re-creating) all the threads in the thread pool to remove the ThreadLocals bound to them.

Truthfully, this is a bug in the JDBC driver: no ThreadLocals should remain bound to any threads after the execution terminates. You might want to check with Microsoft to see if they have an update that fixes this problem. You could also check to see if you are using some kind of feature that requires the ThreadLocal to be used -- something to do with an "activity correlator". If you disable that feature, you may avoid the error.

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