@ Schedule методы в EJBs не выполняются после исключений
У меня есть метод в Одноэлементном сеансе Java EE6, который вызывается @Schedule(...) таймер каждые 30 секунд. Это работает так, как ожидалось, пока в методе не будет создано исключение (исключение-throw и catched в блоке try-catch). После возникновения исключения таймер прекращает вызов метода.
Как я могу заставить таймер снова вспомнить метод, независимо от того, произошло ли исключение или нет?
лучшие с уважением,
христианин
6 ответов
Как указано в документации :
необязательный постоянный элемент принимает логическое значение и используется для указания, должен ли автоматический таймер пережить перезапуск или сбой сервера. По умолчанию все автоматические таймеры являются постоянными.
корпоративный компонент обычно создает таймер в транзакции. Если эта транзакция откатывается, создание таймера также откатывается. Аналогично, если bean отменяет таймер в пределах a транзакция, которая получает откат, отмена таймера откатывается. В этом случае длительность таймера сбрасывается, как если бы отмена никогда не происходила.
Если таймер не работает после обнаружения исключения, то возможный подход слева будет создавать новый таймер вручную в блоке catch.
обсуждении находится в http://www.java.net/node/706287 .
оказывается, что если исключение выбрасывается внутри метода @Schedule, этот метод будет вызван снова через 5 секунд, и если это не удается, таймер просто умирает. Больше никаких звонков.
Это baad, если вы, например, регулярно хотите сделать некоторый надзор.
поэтому мое решение заключается в том, чтобы заключить весь код в метод @Schedule в блок try и поймать все исключения, журнал и возвращайтесь, как будто все в порядке.
Если есть более элегантный способ сделать это, я хотел бы услышать его.
редактировать: нет, просто добавляя try {...} catch (исключение e) {...} не является водонепроницаемым решением. Я все еще получаю мертвый @расписание иногда. Возможно, контейнер JEE делает что-то неясное, когда есть проблема транзакции базы данных? Не могли бы вы пояснить, что вы говорите по этому поводу?
Если вы используете сервер приложений Glassfish (где я не уверен, что это сервер, специфичный в первую очередь), посмотрите на следующий ответ на вопрос избегайте удаления таймера на glassfish. Это мне очень помогло.
редактировать (объяснение в случае мертвой ссылки)
в связанном ответа автора Карло Пеллегрини объяснил, что проблема заключается в том, что исключение происходит в сделке задания расписания. Если можно передать бизнес-логику на аутсорсинг в другой EJB, который выполняется в другой транзакции (@TransactionAttribute(REQUIRES_NEW)
) проблема исчезнет. Более того!--4-->
это приходит с преимуществом:
- не бросать исключение в контейнере-initated транзакции (таким образом, избегая удаления)
- лучшее ведение журнала исключения
- четкая демаркация "реальной" бизнес-сделки
Я думаю, у меня есть решение для этой проблемы. Моя проблема заключалась также в том, что таймер был удален, если произошли ошибки EJB/JPA.
Я только что попытался решить эту проблему с событиями CDI, потому что я хотел, чтобы исключение, которое может быть брошено в другое место, так что @Schedule
не трогать. Я думал, что использование событий cdi отделит @Schedule
С startSomeEvent
-метод. Это не так. (Я не знаю, почему программирование иногда является такой наукой каменного века) но трюк делает @Asynchronous
-аннотации на startSomeEvent
-метод.
Итак, я реализовал @Singleton
TimerService
, который используется для всех таймеров, у меня есть в моем приложении:
@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
. The 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 относительно водонепроницаемого решения исключения, я объясню возможное решение здесь.
У меня была аналогичная проблема. У меня есть обновление расписания для моих кэшей. Однако график не должен умирать ни при каких исключениях. Потому что тогда приложение необходимо перезапускать каждый раз, когда возникает ошибка, чтобы снова включить планировщик.
Это может быть решено в моем случае, отделяя транзакции контейнера от вызов базы данных с новой транзакцией. Вы можете обрабатывать все исключения в отдельной транзакции и перенаправлять только те, которые вы ожидаете/можете обработать в запланированном методе.
вот пример, который я нашел, который описывает решение с интерфейсом. Он работает и без него. В моем случае я просто ввел одноэлементный боб в себя снова и позвонил он.