Как закрыть HikariCP JNDI DataSource при выключении/повторном развертывании

Я использую HikariCP 2.3.3 с Spring и Jetty 9 и пытаюсь решить тот факт, что при горячем развертывании нового файла войны все подключения пула базы данных Hikari к MySQL остаются открытыми и неактивными. Я использую поиск JNDI в моем файле приложения Spring для извлечения источника данных из файла контекста Jetty.

Поскольку я не могу указать метод уничтожения в jndi-lookup, как если бы я определял bean-компонент dataSource, я сослался на этот вопрос: Должен ли я закрыть источник данных, полученный JNDI?, где упоминается, что вы можете попытаться закрыть источник данных в методе contextDestroyed() ServletContextListener. В этом случае они использовали tomcat и c3po, поэтому я не уверен, насколько этот пример относится к делу.

Я пробовал следующее в своем методе contextDestroyed:

InitialContext initial;
DataSource ds;     

try
{
   initial = new InitialContext();
   ds = (DataSource) initial.lookup("jdbc/myDB");
   if (ds.getConnection() == null)
   {
       throw new RuntimeException("Failed to find the JNDI Datasource");
   }

   HikariDataSource hds = (HikariDataSource) ds;

   hds.close();
} catch (NamingException | SQLException ex)
{
   Logger.getLogger(SettingsInitializer.class.getName()).log(Level.SEVERE, null, ex);
}

Но в HikariDataSource hds = (HikariDataSource) ds; Я получаю следующее исключение: java.lang.ClassCastException: com.zaxxer.hikari.HikariDataSource cannot be cast to com.zaxxer.hikari.HikariDataSource

Я также попробовал следующее после прочтения этой проблемы на GitHub: Обязательно ли вызывать shutdown() для HikariDataSource? :

InitialContext initial;
DataSource ds;     

try 
{
     initial = new InitialContext();

     ds = (DataSource) initial.lookup("jdbc/myDB");
     ds.unwrap(HikariDataSource.class).close();

} catch (NamingException | SQLException ex) 
{
     Logger.getLogger(SettingsInitializer.class.getName()).log(Level.SEVERE, null, ex);
}

Но я получаю следующее исключение: java.sql.SQLException: Wrapped connection is not an instance of class com.zaxxer.hikari.HikariDataSource at com.zaxxer.hikari.HikariDataSource.unwrap(HikariDataSource.java:177)

Я чувствую, что близок к рабочему решению, но не могу его понять. Как правильно закрыть источник данных JNDI HikariCP, будь то в contextDestroyed() или в другом месте?


person Simon    schedule 12.06.2015    source источник


Ответы (1)


Я не могу найти файл код 2.3.3 совпадает с номером строки HikariDataSource.java:177 выше. Одним из предложений является обновление до последней версии HikariCP, 2.3.8.

Хотя ваш код выглядит правильно, я подозреваю, что вы столкнулись с проблемой загрузчика классов, из-за чего HikariDataSource (класс), загруженный загрузчиком классов Jetty/Spring и зарегистрированный в JNDI, не является тем же загрузчиком классов, который загружает HikariDataSource в ваше веб-приложение.

Один из быстрых способов проверить — зарегистрировать/распечатать оба экземпляра класса следующим образом:

...
ds = (DataSource) initial.lookup("jdbc/myDB");
logger.info("JNDI HikariDataSource : " + System.identityHashCode(ds.getClass()));
logger.info("Local HikariDataSource: " + System.identityHashCode(HikariDataSource.class));
...

Если два объекта класса имеют разные хэш-коды, они не относятся к одному и тому же классу. В этом случае вам придется выяснить, зарегистрирован ли экземпляр JNDI в «глобальном контексте JNDI». Если это так, этот источник данных может совместно использоваться экземплярами веб-приложения, и ваше веб-приложение не может в одностороннем порядке закрывать его.

ОБНОВЛЕНИЕ: Извините, что пропустил. Перечитав ваш вопрос, моя догадка была верна. Ваша исходная ошибка:

java.lang.ClassCastException: com.zaxxer.hikari.HikariDataSource cannot be cast to
    com.zaxxer.hikari.HikariDataSource

является явным признаком того, что есть два загрузчика классов, которые загрузили два отдельных экземпляра класса HikariDataSource. Первый — это загрузчик классов Jetty (JNDI), а второй — загрузчик классов вашего веб-приложения.

Это указывает на то, что пул является общим для веб-приложений, и вам, вероятно, не следует пытаться закрыть его из контекста вашего приложения.

person brettw    schedule 15.06.2015
comment
Вы правы, два объекта вернули разные хэш-коды. Мы намерены настроить источник данных так, чтобы он ограничивался контекстом веб-приложения. Я использовал класс org.eclipse.jetty.webapp.WebAppContext в нашем файле контекста, но не установил аргумент области. Однако теперь, когда я установил ссылку на WebAppContext, я не могу найти источник данных с помощью jdbc/myDB в моем contextDestroyed(). Я получаю javax.naming.NameNotFoundException; remaining name 'jdbc/myDB' Однако он по-прежнему находится правильно, когда мое приложение запускается с использованием ‹jee:jndi-lookup id=dataSource jndi-name=jdbc/myDB/› - person Simon; 22.06.2015
comment
Если это уместно, я использую версию зависимости hibernate-hikaricp 4.3.10.Final, которая включает HikariCP-java6 версии 2.3.3. - person Simon; 22.06.2015
comment
Я избегаю исключения, если изменяю его на это: initContext = new InitialContext(); envContext = (Context) initContext.lookup("java:comp/env"); ds = (DataSource) envContext.lookup("jdbc/myDB"); Но позже я все равно получаю разные хэши и то же самое исключение приведения. - person Simon; 23.06.2015
comment
Вероятно, это уже не проблема HikariCP, а просто проблема Spring/Jetty/JNDI. Вы можете получить помощь быстрее, если сможете упростить вопрос, основанный на JNDI в целом, и задать его снова. Что-то вроде проблемы поиска Jetty + JNDI DataSource... - person brettw; 23.06.2015
comment
Сделаю. Спасибо, что направил меня в правильном направлении. - person Simon; 23.06.2015