В последнее время по своей работе мне приходится много общаться с 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 { Hashtableenv = 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
Удалить