EJB @ Проблема с расписанием

#jakarta-ee #servlets #glassfish #ejb #scheduled-tasks

#Джакарта-ee #сервлеты #glassfish #ejb #запланированные задачи

Вопрос:

Мне нужно запланировать задачу в моем веб-приложении. Задача должна использовать поле элемента Servlet , которое инициализируется во время развертывания. Я использовал EJB @Schedule . Однако при запуске задачи поле участника равно нулю. Я предполагаю, что причина кроется в том факте, что мне пришлось добавить @Stateless аннотацию к сервлету, чтобы выполнить @Schedule работу, и мой сервлет действительно должен сохранять свое состояние?

Если да, как я могу запустить свою задачу простым и эффективным способом? Использование GlassFish 3

Вот снимок моего кода

 @Stateless  // <-- Wrong ??
public class myServlet extends GenericServlet {
    private MemberField myMemberField = new MemberField();

    @Override
    public void init() throws ServletException {
        myMemberField.initialize();
    }
    @Schedule(dayOfWeek = "Mon-Fri", hour = "21", minute = "59", second = "55")
    public void myTask() {
        System.out.println(myMemberField.toString());
    }
    // other stuff
}
  

Редактировать

В руководстве по Java EE говорится:

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

Итак, я пришел к выводу, что этот способ не подходит для использования в a Servlet .


ПРАВКА 2

Сервлет необходим для запуска службы CometD Bayeux: смотрите здесь, Почему. MyMemberField представляет собой уникальный экземпляр класса-оболочки, который обеспечивает взаимодействие с API брокера (это торговое приложение). Этот экземпляр класса-оболочки должен быть уникальным для всех сеансов и пользователей. Я инициализирую его на init() сервлете. Этот класс-оболочка отправляет запросы брокеру и получает асинхронные ответы. Может быть, лучше определить этот класс вне конфигуратора Байе, но я не знаю, как его определить. Как сервлет? Как управляемый компонент? Кроме того, мне нужно работать с планировщиком, чтобы отправлять запланированные сообщения брокеру. Итак, запланированная задача должна знать об экземпляре класса-оболочки брокера.

Ответ №1:

Когда вы аннотируете сервлет @без состояния, вы создали два компонента JavaEE:

  1. Сервлет, управляемый webcontainer. Webcontainer, скорее всего, создаст один экземпляр класса для обслуживания всех запросов.
  2. Сеансовый компонент без состояния, управляемый контейнером EJB. Контейнер EJB, скорее всего, создаст несколько экземпляров класса и объединит их для обработки запросов EJB.

Когда сервлет обрабатывает первоначальный запрос, инициализируется поле экземпляра в сервлете. Когда выполняется @Schedule, поле экземпляра EJB инициализируется чем-то другим.

Моя рекомендация о том, как решить проблему, зависит от того, какие данные хранятся в поле экземпляра. Это данные инициализации для всего приложения? Если это так, то я бы создал отдельный класс @Singleton с @PostConstruct, который инициализирует данные экземпляра, а затем переместил @Schedule в этот класс. Это данные, зависящие от запроса? Если это так, то я бы использовал TimerService.createCalendarTimer и передал данные в метод timer через информационный параметр TimerConfig.

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

Ответ №2:

@bkail попал в самую точку. Вы смешиваете концепции сервлетов и EJB. Разделите их на два отдельных класса.

 @Singleton
public class FooTask {

    private Foo foo;

    @PostConstruct
    public void init() {
        foo = new Foo();
        foo.initialize();
    }

    @Schedule(dayOfWeek = "Mon-Fri", hour = "21", minute = "59", second = "55")
    public void run() {
        System.out.println(foo);
        // ...
    }

    public Foo getFoo() {
        return foo;
    }

}
  

Если вы по какой-то причине хотите иметь доступ foo к каждому запросу сервлета, вам следует ввести его как @EJB в свой сервлет.

 @WebServlet("/foo/*")
public class FooServlet extends HttpServlet {

    @EJB
    private FooTask fooTask;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(fooTask.getFoo());
        // ...
    }

}