После создания экземпляра компонента, сохраняющего состояние, контейнер внедряет ссылку на источник данных, используемый по умолчанию (более подробно источники данных мы рассмотрим далее, а также в следующей главе) для атрибута ds. После того как внедрение будет выполнено, контейнер вызовет метод, аннотированный @PostConstruct (init()), который создает соединение с базой данных. Если контейнеру придется пассивизировать экземпляр, будет вызван метод Close() (@PrePassivate). Его цель — закрыть JDBC-соединение, которое имеет собственные ресурсы и никогда больше не понадобится, пока экземпляр пассивен. Когда клиент вызывает бизнес-метод компонента, контейнер активизирует его и снова вызывает метод Init() (@PostActivate). Когда клиент вызывает метод checkout() (с аннотацией @Remove), контейнер удаляет экземпляр, но сначала вновь будет вызван метод Close() (@PreDestroy). Обратите внимание, что для удобства чтения я опустил в этих методах обработку исключений, генерируемых SQL.
Служба таймера
Некоторые приложения Java EE должны планировать задачи, чтобы обеспечивать уведомления в определенное время. Так, например, приложение CD-Bookstore должно отправлять поздравительные письма своим покупателям каждый год в их день рождения, печатать ежемесячные статистические данные о продаваемых товарах, генерировать отчеты об уровнях инвентаризации каждую ночь и освежать технический кэш каждые 30 секунд.
В результате в EJB 2.1 было введено средство планирования — служба таймера, так как клиенты не могли использовать напрямую API Thread. По сравнению с другими проприетарными инструментами или фреймворками (UNIX-утилита cron, Quartz и др.) служба таймера имеет меньше возможностей. Только в версии 3.1 спецификации EJB можно заметить резкое улучшение службы таймера. Сегодня служба конкурирует с другими продуктами, поскольку выполняет большинство задач по планированию.
Служба таймера в EJB представляет собой службу контейнера, которая позволяет компонентам регистрироваться для использования методов обратного вызова. Уведомления EJB могут быть назначены в соответствии с графиком на основе календаря, в определенное время, по истечении определенного промежутка времени, или в конкретные повторяющиеся интервалы. Контейнер ведет учет всех таймеров и вызывает соответствующий метод экземпляра компонента, когда таймер отработал. На рис. 8.3 показаны два этапа, связанных с работой службы таймера. Во-первых, EJB необходимо создать таймер (автоматически или программно) и зарегистрироваться для использования метода обратного вызова, затем служба таймера вызывает зарегистрированный метод экземпляра EJB.
Таймеры предназначены для длительных бизнес-процессов и по умолчанию постоянны. Это означает, что они переживают отключения сервера и, как только сервер снова начинает работать, выполняются так, как если бы отключения не произошло. По желанию вы можете указать, что таймеры будут непостоянными.
Рис. 8.3. Взаимодействие между службой таймера и сессионным компонентом
Компоненты, не сохраняющие состояния, синглтоны и MDB могут быть зарегистрированы службой таймера, но компоненты, сохраняющие состояние, такой возможности не имеют, а также не могут пользоваться API для планирования.
Таймеры могут быть созданы автоматически контейнером во время развертывания, если компонент имеет методы, аннотированные @Schedule. Но таймеры также могут быть созданы программно и в таком случае должны предоставить один метод обратного вызова с аннотацией @Timeout.
Выражения на основе календаря
Служба таймера использует синтаксис, основанный на календаре, который впервые появился в UNIX-утилите cron. Этот синтаксис применяется для программного создания таймера (с помощью класса ScheduleExpression) и для автоматического создания таймера (с помощью аннотации @Schedule или дескриптора развертывания). Выражение для планирования может выглядеть следующим образом:
year = "2008,2012,2016" dayOfWeek = "Sat,Sun" minute = "0-10,30,40"
В табл. 8.2 и 8.3 определены атрибуты создания выражений на основе календаря.