В последнее время по своей работе мне приходится много общаться с WebSphere 7.0. Т.к. зверь это тяжелый и малоизученный - я буду временами публиковать интересные заметки из нашей с ним совместной жизни.
Сегодня я хочу рассказать о сервисе планирования, который есть в WebSphere с 6ой версии. Основой данной статьи является Using a WebSphere Scheduler.
Я адаптировал данную статью для WebSphere 7.0 под управлением ОС Windows, исправил ряд недочетов автора, описал несколько присыпанных листьями граблей и главное выложу исходный код классов, которые оригинальный автор выложил в виде картинок.
Данная статья промежуточная для стадии получения первого рабочего прототипа, в следующей статье я планирую написать продолжение с переходом на EJB 3.0 и примером из реальной жизни.
Нашей целью будет научиться использовать встроенный в WebSphere сервис планирования для планирования наших собственных задач.
1. Подготавливаем БД
Сервис планирования WebSphere хранит информацию о задачах в БД. В качестве БД можно использовать СУБД: DB2, Derby, Informix, MSSQL, Oracle, Sybase. С остальными СУБД тоже можно запустить, но как минимум DDL скрипт создания БД придется адаптировать самому.
В качестве БД мы будем использовать Apache Derby. Она простая и легкая, а именно это нам и надо в нашем примере. Я взял последнюю версию 10.6.2.1 с официального сайта.
1.1. Установка
Установка Derby просто до безобразия - просто распакуйте её в любую папку на жестком диске. Я установил её в папку D:\devel\derby\10.6.2.1
1.2. Настройка окружения
Чтобы использовать Derby было удобно надо немного настроить окружение Windows. Добавьте следующие переменные окружения:
DERBY_HOME = D:\devel\derby\10.6.2.1 В переменную CLASSPATH добавьте новые значения:
%DERBY_HOME\lib\derbyclient.jar;%DERBY_HOME\lib\derbytools.jar;
1.3. Создание схемы БД
Структуру БД планировщика вы можете найти в папке %WebSphere_HOME%/Scheduler/createSchemaMod1Derby.ddl В этом файле хранится структура БД, необходимо скопировать этот файлв derby.sql и заменить все вхождения @TABLE_QUALIFIER@@TABLE_PREFIX@ на sched_
1.4. Создание БД
После этого вы сможете запустить Derby с помощью команды: java -jar %DERBY_HOME%\lib\derbyrun.jar server start Создать структуру БД можно с помощью команды java -jar %DERBY_HOME%\lib\derbyrun.jar ij запущенной из другого терминала. Вам последовательно надо будет выполнить следующие команды:
#Подключаться к Derby и создать новую БД
connect 'jdbc:derby://localhost:1527/d:\temp\SchedulerDb;create=true';
#Импортировать структуру БД из заранее подготовленной схемы
run '/home/jsears/local/tmp3/derby.sql';
#проверить результат
show tables;
#настроить параметры безопасности
CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.user.user1', 'password1');
CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.connection.requireAuthentication', 'true');
#переподключиться к БД и проверить авторизацию
disconnect;
connect 'jdbc:derby://localhost:1527/d:\temp\SchedulerDb;user=user1;password=password1';
exit;
После этого Derby необходимо перезапустить.
2. Создание JDBC ресурса в WebSphere
В WebSphere как и в других Application Server'ах приложения получают доступ к БД через JDBC Provide / DJBC Datasource. Эти ресурсы настроиваются прямо на сервере приложений и позволяют абстрагировать наше приложения от особенностей подключения к БД.
Необходимо подключить наше БД как JDBC resource в WebSphere. Для этого в Integrated Solutions Console выполняем следующие действия:
2.1. Сохраним credentials для доступа к нашей БД.
Для этого в разделе Security->Global security->JAAS - J2C authentication data нужно создать новый ресурс:
- Alias = derby_user - User ID = user1 - Password = password1Логин и пароль должны совпадать с теми которые вы заводили в БД
2.2. Теперь создаем JDBC провайдера
В разделе Resources->JDBC providers создаем новый ресурс:
Database type = Derby Provider type = Derby JDBC Provider Implementation Type = Connection pool data source Name = Derby JDBC Provider Scheduler
2.3. Создаем новый DataSource:
В разделе Resources->JDBC providers -> Derby JDBC Provider Scheduler -> Data Sources создаем новую запись:
Step 1:
Data source name = Derby JDBC Driver DataSource
JNDI name = jdbc/DerbyNSDS
Step 2:
Database name = d:\temp\SchedulerDb
Step 3:
Component-managed authentication alias = Node01/Derby user
Mapping-configuration alias = none
Container-managed authentication alias = Node01/Derby user
Проверьте соединение с помощью Test Connection. Если все хорошо - вы успешно подключили БД к WebSphere, в противном случае придется искать ошибку.В моем случае я получил следующее сообщение:
"The test connection operation failed for data source Derby JDBC Driver DataSource on server server1 at node Node01 with the following exception: java.sql.SQLException: Failed to start database 'd:\temp\SchedulerDb' with class loader com.ibm.ws.bootstrap.ExtClassLoader@f5e0f5e, see the next exception for details.DSRA0010E: SQL State = XJ040, Error Code = 40,000. View JVM logs for further details."
2.4. Настройка classpath
Самый простой способ это вернуться в настройку JDBC providers и изменить значение полей:
Classpath
с
"${DERBY_JDBC_DRIVER_PATH}/derby.jar"
на
"D:\devel\derby\10.6.2.1\lib\derbyclient.jar"
"Implementation class name" с
"org.apache.derby.jdbc.EmbeddedConnectionPoolDataSource"
на
"org.apache.derby.jdbc.ClientConnectionPoolDataSource"
Теперь сообщение изменилось на:
"The test connection operation failed for data source Derby JDBC Driver DataSource on server server1 at node Node01 with the following exception: java.lang.ClassNotFoundException: org.apache.derby.jdbc.ClientConnectionPoolDataSource. View JVM logs for further details."
Оказалось что значение classpath в JDBC providers не сохралилось.
После этого все успешно заработало и было получено сообщение:
InformationThe test connection operation for data source Derby JDBC Driver DataSource on server server1 at node Node01 was successful.
3. Создание Scheduler в WebSphere
3.1. Создание ресурса - планировщик
Перейдите в раздел Resources->Scheduler и в качестве scope выберите ваш instance сервера.
Выберите команду New и введите следующие параметры:
- Name = MyScheduler - JNDI name = sched/MyScheduler - Data source JNDI name = jdbc/DerbyNSDS - Data source alias = Node01/Derby user - Table prefix = <имя схемы>.<префикс БД> в моем случае это было APP.sched_ - Work manager JNDI name = ws/defaultИ нажмите кнопку "Ok".
4. Использование планировщика из приложений
Теперь пришло время реализовать работу с планировщиком из наших приложений. В оригинале статьи примеры используют EJB 2.1. Поэтому и мы для начала воспользуемся этой версией (а потом разберемся как это реализовать в EJB 3.0)
4.1. Создание EAR
Я рекомендую вам создать новый EAR проект и использовать его для компонентов планировщика, назовем его SchedulerEAR
4.2. Создание сервлета
Также нам нужен сервлет для вызова нашего планировщика, назовем проект SchedulerWeb и в проекте создадим сервлет SchedulerServlet
Class : SchedulerServlet
package ru.jdevel.websphere.scheduler.web;
import java.io.IOException;
import java.util.Calendar;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ibm.websphere.scheduler.BeanTaskInfo;
import com.ibm.websphere.scheduler.NotificationSinkHome;
import com.ibm.websphere.scheduler.Scheduler;
import com.ibm.websphere.scheduler.TaskHandlerHome;
import com.ibm.websphere.scheduler.TaskInfo;
import com.ibm.websphere.scheduler.TaskNotificationInfo;
import com.ibm.websphere.scheduler.TaskStatus;
/**
* Servlet implementation class SchedulerServlet
*/
public class SchedulerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SchedulerServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Handle request");
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL, "corbaloc:iiop:localhost:2809");
Context initialContext = new InitialContext(env);
//get scheduler reference which we register via admin console
Scheduler scheduler = (Scheduler) initialContext.lookup("sched/MyScheduler");
TaskInfo taskInfo = scheduler.getTask("01");
if (taskInfo != null) {
System.out.println("====== Deregister");
int status = taskInfo.getStatus();
System.out.println("====== Task status: " + status + " id: " + taskInfo.getTaskId());
scheduler.purge("01");
} else {
System.out.println("====== Register new task");
//Create new Task with TaskHandlerHome & NotificationSinkHome callbacks
Object object = new InitialContext().lookup("ejb/ejbs/HandlerHome");
TaskHandlerHome taskHandlerHome = (TaskHandlerHome) PortableRemoteObject.narrow(object, TaskHandlerHome.class);
object = new InitialContext().lookup("ejb/ejbs/SinkHome");
NotificationSinkHome notificationSinkHome = (NotificationSinkHome) PortableRemoteObject.narrow(object, NotificationSinkHome.class);
BeanTaskInfo beanTaskInfo = (BeanTaskInfo) scheduler.createTaskInfo(BeanTaskInfo.class);
beanTaskInfo.setTaskHandler(taskHandlerHome);
beanTaskInfo.setName("01");
beanTaskInfo.setNotificationSink(notificationSinkHome, TaskNotificationInfo.ALL_EVENTS);
Calendar now = Calendar.getInstance();
beanTaskInfo.setStartTime(now.getTime());
beanTaskInfo.setNumberOfRepeats(5);
beanTaskInfo.setRepeatInterval("10seconds");
TaskStatus taskStatus = scheduler.create(beanTaskInfo);
System.out.println("status: " + taskStatus.getStatus() + "; " + taskStatus.getTaskId());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
4.3. Создание EJB
Нам надо создать 2 EJB (версии 2.1.) HandlerBean, SinkBean и демкриптор развертывания ejb-jar.xml
Class : HandlerBean
package ru.jdevel.websphere.scheduler;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import com.ibm.websphere.scheduler.TaskStatus;
/**
* Session Bean implementation class HandlerBean
*/
public class HandlerBean implements SessionBean {
private SessionContext ctx;
public SessionContext getSessionContext() {
return ctx;
}
/**
* Default constructor.
*/
public HandlerBean() {
// TODO Auto-generated constructor stub
}
public void process (TaskStatus arg) {
System.out.println("process ==============");
System.out.println("name=" + arg.getName() + "; status=" + arg.getStatus() + "; repeatsLeft=" + arg.getRepeatsLeft());
}
@Override
public void ejbActivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void ejbPassivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void ejbRemove() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException {
this.ctx = ctx;
}
}
Class : SinkBean
package ru.jdevel.websphere.scheduler;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import com.ibm.websphere.scheduler.TaskNotificationInfo;
/**
* Session Bean implementation class SinkBean
*/
public class SinkBean implements SessionBean {
private SessionContext ctx;
public SessionContext getSessionContext() {
return ctx;
}
public void handleEvent(TaskNotificationInfo arg) {
int eventType = arg.getEventType();
String eventName = "UNKNOWN";
switch (eventType) {
case 1: eventName = "SCHEDULED"; break;
case 2: eventName = "PURGED"; break;
}
System.out.println("handleEvent: " + eventName);
}
/**
* Default constructor.
*/
public SinkBean() {
// TODO Auto-generated constructor stub
}
@Override
public void ejbActivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void ejbPassivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void ejbRemove() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
@Override
public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException {
this.ctx = ctx;
}
}
File : ejb-jar.xml
Scheduler Sink com.ibm.websphere.scheduler.NotificationSinkHome com.ibm.websphere.scheduler.NotificationSink ru.nixdev.websphere.scheduler.SinkBean Stateless Container Handler com.ibm.websphere.scheduler.TaskHandlerHome com.ibm.websphere.scheduler.TaskHandler ru.nixdev.websphere.scheduler.HandlerBean Stateless Container SchedulerEJBClient.jar
5. Профит
Теперь полученный EAR можно задеплоить на сервер приложений и получить доступ к планировщику через сервлет. Например у меня он был доступен по адресу http://localhost:9080/SchedulerWeb/SchedulerServlet/
Привет,
ОтветитьУдалитья не пойму где ты ejb/ejbs/HandlerHome декларируешь.
А! Я нашел. Это написано в ibm-ejb-jar-bnd.xmi
Удалить