Методы @ Schedule в EJBs не выполняются после исключений
У меня есть метод в Java Ee6 Singleton Session Bean, который вызывается @Schedule(...) таймер каждые 30 секунд. Это работает как ожидалось, пока исключение не будет брошено и поймано в методе (исключение бросается и поймано в блоке try-catch). После возникновения исключения таймер прекращает вызов метода.
Как заставить таймер снова вызвать метод, независимо от того, возникло ли исключение или нет?
С наилучшими пожеланиями,
Кристиан
6 ответов:
Как указано в документации:
Необязательный персистентный элемент принимает логическое значение и используется для указания, должен ли автоматический таймер пережить перезагрузку сервера или сбой. По умолчанию все автоматические таймеры являются постоянными.
Корпоративный компонент обычно создает таймер внутри транзакции. Если эта транзакция откатывается, то создание таймера также откатывается. Аналогично, если Боб отменяет таймер в транзакции, которая получает откат назад, отмена таймера откатывается назад. В этом случае длительность таймера сбрасывается, как если бы отмена никогда не происходила.
Если таймер выходит из строя после обнаружения исключения,то возможным подходом будет создание нового таймера вручную в блоке catch.
Соответствующее обсуждение находится в http://www.java.net/node/706287 .
Оказывается, что если исключение создается внутри метода @Schedule, то этот метод будет вызван снова через 5 секунд, и если это не удается, таймер просто умирает. Больше никаких звонков, никогда.
Это baad, если вы, например, регулярно хотите делать некоторые наблюдения.
Поэтому мое решение заключается в том, чтобы вложить весь код в метод @ Schedule в блок try и перехватить все исключения, зарегистрировать их и вернуть, как если бы все в порядке.
Если есть более элегантный способ сделать это, я хотел бы услышать его.
Edit : Нет, просто добавляю try {...} catch (исключение e) {...} не является водонепроницаемым решением. У меня до сих пор иногда бывает мертвый график . Возможно, контейнер JEE делает что-то непонятное, когда возникает проблема транзакции базы данных? Не могли бы вы пояснить, что вы говорите по этому поводу?
Если вы используете сервер приложений Glassfish (где я не уверен, что это сервер, специфичный в первую очередь), посмотрите на следующий ответ на вопрос Избегайте удаления таймера на glassfish. Это мне очень помогло.
EDIT (объяснение в случае мертвой ссылки)
В связанном ответе автор Карло Пеллегрини объяснил, что проблема заключается в том, что исключение происходит в транзакции задачи schedueled. Если можно передать бизнес-логика в другой EJB, который выполняется в другой транзакции (
@TransactionAttribute(REQUIRES_NEW)) проблема исчезла бы. Кроме тогоЭто происходит с преимуществом:
- не выбрасывать исключение в транзакции контейнера-initated (таким образом, избегая удаления)
- лучшее протоколирование исключения
- четкое разграничение "реальной" коммерческой сделки
Я думаю, что у меня есть решение этой проблемы. Моя проблема заключалась также в том, что таймер удалялся, если возникали ошибки EJB/JPA.
Я только что попытался решить эту проблему с событиями CDI, потому что я хотел, чтобы исключение, которое может быть брошено, находилось в другом месте, поэтому@Scheduleне трогается. Я думал, что использование событий cdi позволит отделить@ScheduleотstartSomeEvent-метода. Это не так. (Я не знаю, почему программирование иногда является такой наукой каменного века), но трюк делает@Asynchronous-Аннотация к методуstartSomeEvent.Итак, я реализовал
@SingletonTimerService, который используется для всех таймеров, которые у меня есть в моем приложении:@Singleton public class TimerService implements Serializable { @Inject private Event<SomeEvent> someEvent; @Schedule(hour = "*", second = "0", minute = "*", persistent = false) public void fireSomeEvent() { someEvent.fire(new SomeEvent()); } }Событие
SomeEvent-наблюдается методом, который на самом деле делает то, что вы хотите. Здесь в примере это созданиеPersonсusername.username- этоUNIQUE, поэтому во второй раз, когда событие срабатывает, оно выбрасываетMySQLIntegrityConstraintViolationException: Duplicate entry 'uniqueUsername' for key 'USERNAME'. Это убило бы наш@Schedule, но это не так, потому что у нас есть@Asynchronous-Аннотация.@Singleton public class TimerTestService { @PersistenceContext(unitName = "VWMam3PU") private EntityManager entityManager; @Asynchronous public void startSomeEvent(@Observes SomeEvent event) { Person p = new Person(); p.setUsername("uniqueUsername"); entityManager.persist(p); }}
Класс
SomeEvent- это простое POJO:public class SomeEvent { }Итак, я ищу решение, что в течение длительного времени. Теперь это работает на меня. Он запускает исключение и пытается сделать это снова и снова.
Одним из возможных решений является использование ManagedScheduledExecutorService.
@Stateless public class MyService { @Schedule(hour = "*", minute = "*") public void doSome() { final Future<?> future = executorService.submit(() -> { try { doActually(); } catch (final Exception e) { // log it out } }); } @Resource private ManagedScheduledExecutorService executorService; }
Поскольку я не могу прокомментировать предыдущее сообщение от Per Lindberg относительно водонепроницаемого решения исключения, я объясню возможное решение здесь.
У меня была похожая проблема. У меня есть обновление расписания для моих тайников. Однако график не должен умереть ни на одном исключении. Потому что тогда приложение должно быть перезапущено каждый раз, когда возникает ошибка, чтобы снова включить планировщик.
В моем случае это может быть решено путем отделения транзакций контейнера от базы данных вызов с новой транзакцией. Вы можете обрабатывать все исключения в отдельной транзакции и только перенаправлять те, которые вы ожидаете / можете обработать в запланированном методе.
Вот пример, который я нашел, описывающий решение с интерфейсом. Он работает и без него. В моем случае я просто ввел синглетный боб в себя снова и позвонил оно.
Comments