В одной из прошлых статей я уже рассказывал как использовать встроенные планировщик WebSphere.Сейчас я хочу вернуться к этому вопросу и показать практический пример из реальной жизни.
А в реальной жизни нам потребовались следующие возможности планирования:
Мы напишем обработчик запланированных задач (EJB 2.1 бин), который будет рассматривать имя запланированной задачи как JNDI имя бина с интерфейсом ScheduledTask.
Class : ScheduledTask.class
В процессе обработки задачи мы выполняем JNDI-lookup и находим реализацию этой задачи (или не находим - если нам задали некорректное имя). У полученного экземпляра достаточно выполнить метод execute и наша цель будет достигнута:
Class : TaskHandlerBean.class
Если мы хотим планировать задачи из произвольного класса, в этом случае в качестве имени задачи можно передавать полное имя класса реализации (FQCN): например net.nixdev.was.scheduler.tasks.ConsoleWriterScheduled и далее использовать Class.forName для создания экземпляра задачи.
Структура WAS для планирования задач не содержит полей для передачи параметров, поэтому мы воспользовались аналогом GET запроса и добавили все параметры в форме:
?<key1>=<value1>&<key2>=<value2>
Далее эту конструкцию можно распарсить и использовать аргументы для планирования.
Class : ScheduledTask.class
Class : TaskHandlerBean.class
К сожалению WAS не предоставляет API для динамического изменения параметров уже запланированной задачи. А вариант изменения данных в базе очень рискован блокировками (планировщик блокирует записи в таблице в время выполнения задач) и затруднен тем что часть информации о задачи хранится в виде сериализованных классов WAS.
Как я уже говорил - вариант создания и изменения задач напрямую в БД оказался недоступным для использования, поэтому задачи можно создавать только программно из контекта WAS.
А вот внешний интерфейс для создания задач вы можете реализовать на свой вкус - мы сделали WebService и JMX доступ.
А в реальной жизни нам потребовались следующие возможности планирования:
- - Планировать произвольные задачи (EJB 3.0 бины)
- - Передавать задачам параметры выполнения
1. Планирование произвольных задач
1.1. Планирование EJB 3.0 бинов
Планировщик WebSphere работает с EJB 2.1 бинами и в WebSphere 7.0 в этом направлении ничего не изменилось. Поэтому мы будем использовать обходные пути для решения наших задач.Мы напишем обработчик запланированных задач (EJB 2.1 бин), который будет рассматривать имя запланированной задачи как JNDI имя бина с интерфейсом ScheduledTask.
Class : ScheduledTask.class
@Remote public interface ScheduledTask { public void execute(); }
В процессе обработки задачи мы выполняем JNDI-lookup и находим реализацию этой задачи (или не находим - если нам задали некорректное имя). У полученного экземпляра достаточно выполнить метод execute и наша цель будет достигнута:
Class : TaskHandlerBean.class
public class TaskHandlerBean implements javax.ejb.SessionBean { static final long serialVersionUID = 3206093459760846163L; private javax.ejb.SessionContext mySessionCtx; /** * Внутренний логгер */ private final Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName()); /** * getSessionContext */ public javax.ejb.SessionContext getSessionContext() { return mySessionCtx; } /** * setSessionContext */ @Override public void setSessionContext(javax.ejb.SessionContext ctx) { mySessionCtx = ctx; } public ScheduledTask resolveJNDITaskName(final String jndiName) { PpuScheduledTask taskHandlerHome = null; try { // Create new Task with TaskHandlerHome & NotificationSinkHome callbacks Object object = new InitialContext().lookup(jndiName); taskHandlerHome = (ScheduledTask) PortableRemoteObject.narrow(object, ScheduledTask.class); } catch (NamingException e) { // TODO: handle exception } return taskHandlerHome; } public void process(TaskStatus arg) { String name = arg.getName(); logger.info("Обработка запланированной задачи " + name + "."); ScheduledTask taskHandlerHome = resolveJNDITaskName(name); if (taskHandlerHome != null) { taskHandlerHome.execute(argsMap); } else { logger.warn("Не найдена реализация задачи " + name + "."); } logger.info("Завершена обработка запланированной задачи " + name + "."); } /** * ejbCreate */ public void ejbCreate() throws javax.ejb.CreateException { } /** * ejbActivate */ @Override public void ejbActivate() { } /** * ejbPassivate */ @Override public void ejbPassivate() { } /** * ejbRemove */ @Override public void ejbRemove() { } }
1.2. Планирование POJO задач
Если мы хотим планировать задачи из произвольного класса, в этом случае в качестве имени задачи можно передавать полное имя класса реализации (FQCN): например net.nixdev.was.scheduler.tasks.ConsoleWriterScheduled и далее использовать Class.forName для создания экземпляра задачи.
2. Передача аргументов задачам
Следующей интересной проблемой стал вопрос передачи параметров задачам при планировании.Структура WAS для планирования задач не содержит полей для передачи параметров, поэтому мы воспользовались аналогом GET запроса и добавили все параметры в форме:
?<key1>=<value1>&<key2>=<value2>
Далее эту конструкцию можно распарсить и использовать аргументы для планирования.
Class : ScheduledTask.class
@Remote public interface ScheduledTask { public void execute(Mapargs); }
Class : TaskHandlerBean.class
public class TaskHandlerBean implements javax.ejb.SessionBean { .... public void process(TaskStatus arg) { String name = arg.getName(); String argsList = ""; MapКонечно такой подход накладывает ограничения на имена и значения аргументов, но в нашем примере этими ограничениями можно принебречь. А в случае необходимости использования нестандартных символов - использовать BASE64 encoding параметров.argsMap = null; int index = arg.getName().indexOf("?"); if (index > 0) { name = arg.getName().substring(0, index); argsList = arg.getName().substring(index + 1); argsMap = getParamsMap(argsList); } logger.info("Обработка запланированной задачи " + name + ". Аргументы: " + argsList); ScheduledTask taskHandlerHome = resolveJNDITaskName(name); if (taskHandlerHome != null) { taskHandlerHome.execute(argsMap); } else { logger.warn("Не найдена реализация задачи " + name + "."); } logger.info("Завершена обработка запланированной задачи " + name + "."); } .... }
3. Дополнительные вопросы
3.1. Динамическое изменение параметров запланированной задачи
К сожалению WAS не предоставляет API для динамического изменения параметров уже запланированной задачи. А вариант изменения данных в базе очень рискован блокировками (планировщик блокирует записи в таблице в время выполнения задач) и затруднен тем что часть информации о задачи хранится в виде сериализованных классов WAS.
3.2. Массовое планирование задач / внешнее API
Как я уже говорил - вариант создания и изменения задач напрямую в БД оказался недоступным для использования, поэтому задачи можно создавать только программно из контекта WAS.
А вот внешний интерфейс для создания задач вы можете реализовать на свой вкус - мы сделали WebService и JMX доступ.
Здравствуйте!
ОтветитьУдалитьНапише пожайлуста о том как реализовать планирование задачи, которое будет выполняться не сессионным бином, а MDB (т.е. через JMS).
К сожалению я в данный момент очень далек от темы WebSphere и Java EE. Поэтому не могу помочь примером.
Удалить