Получение таймера дружественным к JavaEE способом, который работает и на JavaSE (для использования в драйвере JDBC) - PullRequest
7 голосов
/ 15 декабря 2011

Мне интересно поработать над драйвером JDBC PostgreSQL, чтобы помочь с реализацией Statement.setQueryTimeout(...), одной из наиболее проблемных дыр в спецификации спецификации в драйвере.Для этого мне нужен портативный способ получить таймер или установить будильник / обратный вызов, который работает на всех серверах приложений Java, контейнерах сервлетов и в средах Java SE.

Кажется, что это не так просто, кактак и должно быть, и я застрял настолько, что намекаю на вашу милость. Как, черт возьми, я могу сделать простой обратный вызов таймера, который работает в контейнерах Java SE, Java EE и сервлетов?

При необходимости я могу выдержать выполнение отдельных выпусков -ee и -se,но это крайне нежелательно.Релизы для каждого контейнера абсолютно непрактичны, хотя автоматически выбираемые адаптеры для каждого контейнера могут быть приемлемыми, если они сильно нежелательны.

Драйвер PgJDBC должен работать на старых серверах приложений в старых версиях JVM, но я немалейшее беспокойство, если время ожидания оператора доступно только в версии драйвера JDBC4 для современных контейнеров и JVM.Уже имеется инфраструктура условной компиляции, позволяющая выпускать драйверы JDBC3 / JDK 1.4 и JDBC4 / JDK 1.5, поэтому код, который работает только под 1.5 или даже 1.6, не является проблемой.

(правка): Anдобавленная сложность заключается в том, что драйвер JDBC может быть развернут пользователями:

  • как модуль контейнера или встроенный компонент, который запускается при запуске контейнера;
  • как отдельный объект развертывания, который можетбыть развернутым и повторно развернутым во время выполнения;или
  • Встроенный в их приложение war или ear

... и нам необходимо поддерживать все эти сценарии, желательно без необходимости настраивать пользовательские приложения!Если мы не можем поддерживать все эти сценарии, ему нужно, по крайней мере, работать без поддержки времени ожидания оператора и терпеть неудачу из-за невозможности поддержки времени ожидания оператора.

Ах, напишите один раз, запуститеanwwhere ....

Я не могу просто использовать java.util.Timer или java.util.concurrent:

Я вижу общие утверждения, что использование java.util.Timer или утилит параллелизма Java SE (JSR-166) в пакете java.util.concurrent не рекомендуется в Java EE, но редко какие-либо подробности. Предложение JSR 236 говорит, что:

java.util.Timer, java.lang.Thread и утилиты параллелизма Java SE (JSR-166) в java.util.concurrency (sic).) пакет никогда не должен использоваться в управляемых средах, так как он создает потоки вне области действия контейнера.

Чуть больше чтения говорит о том, что вызовы из неуправляемых потоков не получат контейнерные сервисы, поэтому во всем приложении все может сломаться захватывающими и неожиданными способами.Учитывая, что вызов таймера может привести к возникновению исключения, генерируемого PgJDBC и распространяющегося в код пользовательского приложения, это важно.

(edit): Сам драйвер JDBC не требует каких-либо контейнерных сервисов поэтому мне все равно, работают ли они в его потоках таймера, если эти потоки никогда не запускают какой-либо пользовательский код.Проблема заключается в надежном обеспечении того, что они этого не делают.

Уровень абстракции таймера JSR 236 не функционирует

JSR 236 не функционирует, и я не вижу замены, удовлетворяющей тем же требованиям дляпереносимые таймеры.

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

EJBтаймеры не подходят

Существуют таймеры EJB, но они не подходят для низкоуровневых вещей, таких как реализация драйвера JDBC, потому что они:

  • постоянны при перезапуске контейнера или машины
  • высокие издержки
  • могут быть реализованы с использованием базы данных для сохранения таймера
  • ориентировано на рабочее время, а не на машинное время
  • недоступно в простых контейнерах сервлетов
  • недоступно на серверах приложений EE "веб-профиль"

Так что таймеры EJB могут быть полностью исключены из списка.

Я не могу накатить свою нить таймера

Те же проблемы, которые не позволяют использовать java.util.Timer и друзей, не позволяют мне запускать собственный поток таймеров и управлять своими собственными таймерами. Это не стартер.

Спецификация Java EE гласит:

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

и в учебнике EE написано :

Адаптеры ресурсов, которые неправильно используют потоки, могут поставить под угрозу всю среду сервера приложений. Например, адаптер ресурсов может создать слишком много потоков или может неправильно освободить созданные им потоки. Плохая обработка потоков препятствует завершению работы сервера приложений и влияет на производительность сервера приложений, поскольку создание и удаление потоков являются дорогостоящими операциями.

WorkManager не работает с таймерами

Существует javax.resource.spi.work.WorkManager , но (а) он предназначен для использования на стороне поставщика услуг, а не на стороне приложения, и (б) он не предназначен для таймеров. Таймер, вероятно, может быть взломан при использовании элемента «Работа», который спит с таймаутом, но в лучшем случае он уродлив и, вероятно, будет весьма неэффективным.

Не похоже, что он будет работать и на Java SE.

Архитектура Java Connector (JCA)

Как указано в руководстве по Java EE , архитектура коннектора может быть жизнеспособным вариантом для контейнеров EE. Однако, опять же, контейнеры сервлетов, такие как Tomcat или Jetty, могут не поддерживать его.

Я также обеспокоен влиянием производительности по этому маршруту.

Так что я застрял

Как мне выполнить эту простую задачу?

Мне нужно написать новый ThreadPoolExecutor, который получает потоки из контейнера через JNDI, а затем использовать его в качестве основы для нового ScheduledThreadPoolExecutor? Если да, есть ли переносимый способ получения потоков из контейнера или мне нужен поиск JNDI для каждого контейнера и код адаптера?

Мне не хватает чего-то глупого и ослепительно очевидного?

Как другие библиотеки, которым требуется асинхронная работа, таймеры или обратные вызовы, обрабатывают переносимость между Java EE и Java SE?

1 Ответ

3 голосов
/ 15 декабря 2011

Timer действительно очень плохая идея в среде Java EE.Например, если оно выдает исключение, оно полностью уничтожается, и вам нужно будет перезапустить весь сервер, чтобы он снова заработал.Но ScheduledExecutorService должен это делать, если использовать его с умом и предельной осторожностью.

Вам нужно будет создать его на уровне приложения, а не внутри какого-либо EJB или какого-либо другого управляемого контейнера класса (как спецификация Java EE).пытается сказать вам).Более того, большинство эталонных реализаций Java EE также используют исполнителей приложений под прикрытием для ускорения загрузки.Вспомните, например, Glassfish и Mojarra.

Что касается ловушки запуска / завершения работы всего приложения, которая работает как в Java EE, так и в Java SE, драйвер, совместимый с JDBC4, автоматически загружается механизмом ServiceLoader с помощью файла /META-INF/services/java.sql.Driver, также в WAR-файлах.Он выгружается только при отключении JVM.Для Java EE вы можете использовать /META-INF/services/javax.servlet.ServletContainerInitializer для программного добавления ServletContextListener, который явно отменяет регистрацию драйвера и отключает его пул потоков на contextDestroyed().

, относящийся:

...