среда, 16 февраля 2011 г.

WebSphere: Реальное использование планировщика WebSphere 7.0

В одной из прошлых статей я уже рассказывал как использовать встроенные планировщик WebSphere.Сейчас я хочу вернуться к этому вопросу и показать практический пример из реальной жизни.
А в реальной жизни нам потребовались следующие возможности планирования:
  • - Планировать произвольные задачи (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(Map args);
}

Class : TaskHandlerBean.class
public class TaskHandlerBean implements javax.ejb.SessionBean {

    ....

    public void process(TaskStatus arg) {
        String name = arg.getName();
        String argsList = "";
        
        Map 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 + ".");
    }
    
    ....
}
Конечно такой подход накладывает ограничения на имена и значения аргументов, но в нашем примере этими ограничениями можно принебречь. А в случае необходимости использования нестандартных символов - использовать BASE64 encoding параметров.

3. Дополнительные вопросы


3.1. Динамическое изменение параметров запланированной задачи


К сожалению WAS не предоставляет API для динамического изменения параметров уже запланированной задачи. А вариант изменения данных в базе очень рискован блокировками (планировщик блокирует записи в таблице в время выполнения задач) и затруднен тем что часть информации о задачи хранится в виде сериализованных классов WAS.

3.2. Массовое планирование задач / внешнее API


Как я уже говорил - вариант создания и изменения задач напрямую в БД оказался недоступным для использования, поэтому задачи можно создавать только программно из контекта WAS.

А вот внешний интерфейс для создания задач вы можете реализовать на свой вкус - мы сделали WebService и JMX доступ.

2 комментария:

  1. Здравствуйте!
    Напише пожайлуста о том как реализовать планирование задачи, которое будет выполняться не сессионным бином, а MDB (т.е. через JMS).

    ОтветитьУдалить
    Ответы
    1. К сожалению я в данный момент очень далек от темы WebSphere и Java EE. Поэтому не могу помочь примером.

      Удалить