Методы @Schedule в EJB не выполняются после исключений - PullRequest
10 голосов
/ 11 апреля 2011

У меня есть метод в Java EE6 Singleton Session Bean, который вызывается таймером @Schedule (...) каждые 30 секунд.Это работает, как и ожидалось, до тех пор, пока исключение не будет сгенерировано и перехвачено в методе (исключение - throw и перехват в блоке try-catch).После возникновения исключения таймер перестает вызывать метод.

Как я могу заставить таймер снова вызывать метод, независимо от того, произошло исключение или нет?

С уважением,

христианская

Ответы [ 6 ]

12 голосов
/ 15 мая 2012

Связанное обсуждение можно найти по адресу http://www.java.net/node/706287.

Оказывается, что если в методе @Schedule возникает исключение, этот метод будет вызван снова через 5 секунд, а если THATне работает, таймер просто умирает.Больше никаких звонков.

Это плохо, если вы, например, регулярно хотите наблюдать.

Так что мое решение - заключить весь код в метод @Schedule в блок try и перехватить ALLИсключения, регистрируйте их и возвращайте, как будто все в порядке.

Если есть более элегантный способ сделать это, я хотел бы услышать это.

Редактировать Нет, просто добавление try {...} catch (Exception e) {...} не является водонепроницаемым решением.Я до сих пор иногда получаю мертвый @Schedule.Возможно, контейнер JEE делает что-то неясное, когда возникает проблема с транзакцией базы данных?Наян: не могли бы вы пояснить, что вы говорите по этому вопросу?

5 голосов
/ 03 мая 2011

Как упомянуто в документации:

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

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

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

3 голосов
/ 13 января 2016

Если вы используете сервер приложений Glassfish (хотя я не уверен, что он в первую очередь специфичен для конкретного сервера), взгляните на следующий ответ на вопрос Избегайте удаления таймера на glassfish .Это мне очень помогло.

РЕДАКТИРОВАТЬ (объяснение в случае мертвой ссылки)

В связанном ответе автор Карло Пеллегрини объяснил, что проблема в том, что исключениепроисходит в транзакции запланированной задачи.Если кто-то может передать бизнес-логику в другой EJB, который выполняется в другой транзакции (@TransactionAttribute(REQUIRES_NEW)), проблема исчезнет.Более того,

Это дает следующие преимущества:

  • Не выбрасывать исключение в транзакции, инициированной контейнером (таким образом избегая удаления)
  • Улучшенная регистрацияИсключения
  • Четкое разграничение «реальной» бизнес-операции
1 голос
/ 22 сентября 2014

Я думаю, у меня есть решение этой проблемы. Моя проблема была также в том, что таймер был удален, если произошли ошибки EJB / JPA.

Я только что попытался решить эту проблему с событиями CDI, потому что я хотел, чтобы Исключение могло быть выброшено в другом месте, чтобы @Schedule не трогали. Я думал, что использование событий cdi будет отделить @Schedule от startSomeEvent -Metod. Это не. (Я не знаю, почему программирование иногда является наукой о каменном веке) Но трюк делает @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. 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 {

}

Итак, я долго искал решение этой проблемы. Теперь это работает для меня. Он запускает исключение и пытается это снова, снова и снова.

0 голосов
/ 18 сентября 2015

Одним из возможных решений является использование 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;
}
0 голосов
/ 30 марта 2015

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

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

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

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

https://web.archive.org/web/20160511085332/http://www.javahelp.info:80/2009/11/01/using-transactionattribute-in-submethods-on-same-ejb3-beans/

...