Потребовалось использовать в одном проекте данный планировщик ( http://quartz-scheduler.org/). Запускается на ура в виде бина в JBoss… Но вот тут с размаху врезался лбом в косяк:
- планировщик регистрируется в JNDI
- я успешно получаю инстанс планировщика
- добавляю свои задачу со своим воркером
- …
И кряк вам с хреном, а не профит: ClassNotFoundException…
“Централизованный” инстанс планировщика не видит ваших классов-реализаций интерфейса Job. При ближайшем рассмотрении оно и понятно. При дальнейшем - не понятно зачем тогда нужно запускать Quartz как сервис, может кто объяснит?
В результате сделал, что бы внутри приложения запускался свой инстанс Quartz.
Для сих нужд уже есть два класса:
- QuartzInitializerListener
- QuartzInitializerServlet
Как их использовать - есть в документации на API. Я немного расширил QuartzInitializerListener, сделав регистрацию полученного инстанса планировщика в JNDI, выглядит примерно так:
package net.homelinux.hatred.quartz;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import org.jboss.naming.NonSerializableFactory;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Обёртка для стандартного QuartzInitializerListener, что бы зарегистрировать фабрику в том числе
* за пределами Сервлет-контекстов
* <p/>
* Для подробностей смотреть параметры конфигурации org.quartz.ee.servlet.QuartzInitializerListener:
* http://quartz-scheduler.org/api/2.1.0/org/quartz/ee/servlet/QuartzInitializerListener.html
*
* @author Alexander 'hatred' Drozdov
* <p/>
* Date: 19.03.12
* Time: 11:08
*/
public class QuartzInitializerListener extends org.quartz.ee.servlet.QuartzInitializerListener
{
// Logger
private static final Logger log = LoggerFactory.getLogger(QuartzInitializerListener.class);
// TODO: хранить где-то в другом месте
public static final String JNDI_NAME = "MyAppQuartzScheduler";
@Override
public void contextInitialized(ServletContextEvent sce)
{
super.contextInitialized(sce);
ServletContext ctx = sce.getServletContext();
StdSchedulerFactory factory = (StdSchedulerFactory) ctx
.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
try
{
rebind(factory, JNDI_NAME);
}
catch (NamingException e)
{
e.printStackTrace();
}
catch (SchedulerException e)
{
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
unbind(JNDI_NAME);
super.contextDestroyed(sce);
}
///
//
// Биндимся к JNDI
//
///
private void rebind(StdSchedulerFactory factory, String name)
throws NamingException, SchedulerException
{
InitialContext rootCtx = null;
try
{
rootCtx = new InitialContext();
Name fullName = rootCtx.getNameParser("").parse(name);
Scheduler scheduler = factory.getScheduler();
NonSerializableFactory.rebind(fullName, scheduler, true);
}
finally
{
if (rootCtx != null)
{
try
{
rootCtx.close();
}
catch (NamingException ignore)
{
}
}
}
}
private void unbind(String name)
{
InitialContext rootCtx = null;
try
{
rootCtx = new InitialContext();
rootCtx.unbind(name);
NonSerializableFactory.unbind(name);
}
catch (NamingException e)
{
log.warn("Failed to unbind scheduler with jndiName: " + name, e);
}
finally
{
if (rootCtx != null)
{
try
{
rootCtx.close();
}
catch (NamingException ignore)
{
}
}
}
}
}
после чего получить доступ к фабрике можно по JNDI имени “MyAppQuartzScheduler”.
Затем настройки самого Quatrz.
В случае JBoss 6.0, я их сохранил в директории ./conf
:
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
#org.quartz.scheduler.classLoadHelper.class = org.quartz.simpl.CascadingClassLoadHelper
#org.quartz.scheduler.classLoadHelper.class = org.quartz.simpl.LoadingLoaderClassLoadHelper
org.quartz.scheduler.instanceName = PodryadQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.xaTransacted = false
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer = true
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 4
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = QUARTZ
org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.dataSource.QUARTZ.jndiURL = java:jdbc/quartz_scheduler
org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:jdbc/quartz_scheduler
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.dataSource = QUARTZ
#org.quartz.jobStore.tablePrefix = QRTZ_
#org.quartz.dataSource.QUARTZ.jndiURL = java:jdbc/quartz_scheduler
Отдельно стоит отметить и описание Datasource для подключения к базе.
Если вы его объявите как ’local-tx-datasource’ получите небольшой обломс, поэтому нужно объявлять как ‘xa-datasource’, примерно следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<!-- See http://www.jboss.org/community/wiki/Multiple1PC for information about local-tx-datasource -->
<!-- $Id: mysql-ds.xml 97536 2009-12-08 14:05:07Z jesper.pedersen $ -->
<!-- Datasource config for MySQL using 3.0.9 available from:
http://www.mysql.com/downloads/api-jdbc-stable.html
-->
<datasources>
...
<!-- Quartz datasource, must be 'xa-datasource', but no 'local-tx-datasource' -->
<xa-datasource>
<jndi-name>jdbc/quartz_scheduler</jndi-name>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:mysql://127.0.0.1:3306/jboss_quartz?useEncoding=true&characterEncoding=UTF-8</xa-datasource-property>
<xa-datasource-property name="User">DB_USER</xa-datasource-property>
<xa-datasource-property name="Password">DB_PASSWORD</xa-datasource-property>
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
<new-connection-sql>SELECT 1</new-connection-sql>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</xa-datasource>
...
</datasources>
PS если кто объяснит, для чего всё же нужен Quartz в виде сервиса в JBoss, буду премного благодарен. PPS ещё, ориентировочно с версии Quartz 2.0, несколько инстансев оного могут использовать одну базу, нужно только, что бы имена инстансев различались, дабы не было конфликтов.