quartz執行緒管理的原始碼分析
概述
quartz啟動後有多個執行緒同時在跑。啟動時會啟動主執行緒、叢集執行緒、檢漏執行緒、工作執行緒。主執行緒負責查詢到需要觸發的執行緒,並放入到執行緒佇列。
一、主執行緒QuartzScheduleThread
關於QuartzScheduleThread是quartz啟動時開始啟動,用於trigger的獲取、觸發,並放入到執行緒池中執行。
二、執行緒池SimpleThreadPool
執行緒池的初使化:new 執行緒並放入執行緒池的連結串列中。
執行執行緒:把任務放到一個執行緒中執行。
執行緒結束:修改執行緒狀態。
執行緒池關閉:每個執行緒關閉,正在執行的執行緒等執行緒執行完了再關閉。
三、工作執行緒WorkThread
執行緒池中執行的工作執行緒,可以通過配置檔案quartz.properties來配置大小。
JobRunShell實現runnable介面,放入到workThread下執行。
原始碼分析
1、執行緒池介面
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.spi;
import org.quartz.SchedulerConfigException;
/**
* <p>
* The interface to be implemented by classes that want to provide a thread
* pool for the <code>{@link org.quartz.core.QuartzScheduler}</code>'s use.
* </p>
*
* <p>
* <code>ThreadPool</code> implementation instances should ideally be made
* for the sole use of Quartz. Most importantly, when the method
* <code>blockForAvailableThreads()</code> returns a value of 1 or greater,
* there must still be at least one available thread in the pool when the
* method <code>runInThread(Runnable)</code> is called a few moments (or
* many moments) later. If this assumption does not hold true, it may
* result in extra JobStore queries and updates, and if clustering features
* are being used, it may result in greater imballance of load.
* </p>
*
* @see org.quartz.core.QuartzScheduler
*
* @author James House
*/
public interface ThreadPool {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Execute the given <code>{@link java.lang.Runnable}</code> in the next
* available <code>Thread</code>.
* </p>
*
* <p>
* The implementation of this interface should not throw exceptions unless
* there is a serious problem (i.e. a serious misconfiguration). If there
* are no immediately available threads <code>false</code> should be returned.
* </p>
*
* @return true, if the runnable was assigned to run on a Thread.
*/
boolean runInThread(Runnable runnable);
/**
* <p>
* Determines the number of threads that are currently available in in
* the pool. Useful for determining the number of times
* <code>runInThread(Runnable)</code> can be called before returning
* false.
* </p>
*
* <p>The implementation of this method should block until there is at
* least one available thread.</p>
*
* @return the number of currently available threads
*/
int blockForAvailableThreads();
/**
* <p>
* Must be called before the <code>ThreadPool</code> is
* used, in order to give the it a chance to initialize.
* </p>
*
* <p>Typically called by the <code>SchedulerFactory</code>.</p>
*/
void initialize() throws SchedulerConfigException;
/**
* <p>
* Called by the QuartzScheduler to inform the <code>ThreadPool</code>
* that it should free up all of it's resources because the scheduler is
* shutting down.
* </p>
*/
void shutdown(boolean waitForJobsToComplete);
/**
* <p>Get the current number of threads in the <code>ThreadPool</code>.</p>
*/
int getPoolSize();
/**
* <p>Inform the <code>ThreadPool</code> of the Scheduler instance's Id,
* prior to initialize being invoked.</p>
*
* @since 1.7
*/
void setInstanceId(String schedInstId);
/**
* <p>Inform the <code>ThreadPool</code> of the Scheduler instance's name,
* prior to initialize being invoked.</p>
*
* @since 1.7
*/
void setInstanceName(String schedName);
}
2、預設的執行緒池實現類SimpleThreadPool和工作執行緒WorkerThread
重點關注createWorkerThreads()方法和shutDown()方法即可。
在初始化執行緒池SimpleThreadPool時,會建立並執行所有的工作執行緒WorkerThread,擷取initialize()方法如下:
// create the worker threads and start them
Iterator<WorkerThread> workerThreads = createWorkerThreads(count).iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.start();
availWorkers.add(wt);
}
工作執行緒WorkerThread線上程池初始化時就開始執行,這聽上去像是少了什麼。是的,每個工作執行緒的業務邏輯都還沒設定,怎麼就開始執行了呢?事實上,每個工作執行緒都有一個裝載業務邏輯的Runnable變數,如果該Runnable變數為null,則該工作執行緒一直處於輪詢-等待狀態,直至設定了Runnable變數為止,然後才是呼叫Runnable#run()方法執行業務邏輯。擷取WorkerThread的run方法如下:
synchronized(this) {
while (runnable == null && run.get()) {
this.wait(500);
}
if (runnable != null) {
ran = true;
runnable.run();
}
}
由此我們可以看出,執行緒池中的工作執行緒應該分為2種:
- 一種是處於輪詢-等待狀態的工作執行緒;
- 另一種是真正在執行業務邏輯的工作執行緒;
所以,線上程池SimpleThreadPool中,有2個集合變數分別儲存這2種工作執行緒:
private LinkedList<WorkerThread> availWorkers = new LinkedList<WorkerThread>();
private LinkedList<WorkerThread> busyWorkers = new LinkedList<WorkerThread>();
執行緒池SimpleThreadPool也提供了runInThread(Runnable runnable)方法,用於從空閒的工作執行緒中,以先進先出的順序獲取一個空閒工作執行緒,為它設定裝載業務邏輯的Runnable變數。擷取runInThread()方法的部分程式碼如下:
if (!isShutdown) {
WorkerThread wt = (WorkerThread)availWorkers.removeFirst();
busyWorkers.add(wt);
wt.run(runnable);
}
因為是以先進先出的順序獲取空閒工作執行緒,這也就解釋了在一些定時任務的日誌中,每一次起任務的工作執行緒是不一樣的,而且是有序的。如:
2019-02-03 21:16:05 [ INFO] [DefaultQuartzScheduler_Worker-1] gen.common.CommandRunner:32 out:
2019-02-04 21:15:48 [ INFO] [DefaultQuartzScheduler_Worker-2] gen.common.CommandRunner:32 out:
2019-02-05 21:15:00 [ INFO] [DefaultQuartzScheduler_Worker-3] gen.common.CommandRunner:32 out:
。。。。
2019-02-11 21:23:45 [ INFO] [DefaultQuartzScheduler_Worker-9] gen.common.CommandRunner:32 out:
2019-02-12 21:15:00 [ INFO] [DefaultQuartzScheduler_Worker-10] gen.common.CommandRunner:32 out:
類SimpleThreadPool的原始碼如下:
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.simpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.SchedulerConfigException;
import org.quartz.spi.ThreadPool;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* <p>
* This is class is a simple implementation of a thread pool, based on the
* <code>{@link org.quartz.spi.ThreadPool}</code> interface.
* </p>
*
* <p>
* <CODE>Runnable</CODE> objects are sent to the pool with the <code>{@link #runInThread(Runnable)}</code>
* method, which blocks until a <code>Thread</code> becomes available.
* </p>
*
* <p>
* The pool has a fixed number of <code>Thread</code>s, and does not grow or
* shrink based on demand.
* </p>
*
* @author James House
* @author Juergen Donnerstag
*/
public class SimpleThreadPool implements ThreadPool {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private int count = -1;
private int prio = Thread.NORM_PRIORITY;
private boolean isShutdown = false;
private boolean handoffPending = false;
private boolean inheritLoader = false;
private boolean inheritGroup = true;
private boolean makeThreadsDaemons = false;
private ThreadGroup threadGroup;
private final Object nextRunnableLock = new Object();
private List<WorkerThread> workers;
private LinkedList<WorkerThread> availWorkers = new LinkedList<WorkerThread>();
private LinkedList<WorkerThread> busyWorkers = new LinkedList<WorkerThread>();
private String threadNamePrefix;
private final Logger log = LoggerFactory.getLogger(getClass());
private String schedulerInstanceName;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* Create a new (unconfigured) <code>SimpleThreadPool</code>.
* </p>
*
* @see #setThreadCount(int)
* @see #setThreadPriority(int)
*/
public SimpleThreadPool() {
}
/**
* <p>
* Create a new <code>SimpleThreadPool</code> with the specified number
* of <code>Thread</code> s that have the given priority.
* </p>
*
* @param threadCount
* the number of worker <code>Threads</code> in the pool, must
* be > 0.
* @param threadPriority
* the thread priority for the worker threads.
*
* @see java.lang.Thread
*/
public SimpleThreadPool(int threadCount, int threadPriority) {
setThreadCount(threadCount);
setThreadPriority(threadPriority);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public Logger getLog() {
return log;
}
public int getPoolSize() {
return getThreadCount();
}
/**
* <p>
* Set the number of worker threads in the pool - has no effect after
* <code>initialize()</code> has been called.
* </p>
*/
public void setThreadCount(int count) {
this.count = count;
}
/**
* <p>
* Get the number of worker threads in the pool.
* </p>
*/
public int getThreadCount() {
return count;
}
/**
* <p>
* Set the thread priority of worker threads in the pool - has no effect
* after <code>initialize()</code> has been called.
* </p>
*/
public void setThreadPriority(int prio) {
this.prio = prio;
}
/**
* <p>
* Get the thread priority of worker threads in the pool.
* </p>
*/
public int getThreadPriority() {
return prio;
}
//設定執行緒池中的執行緒的名字字首
public void setThreadNamePrefix(String prfx) {
this.threadNamePrefix = prfx;
}
//獲取執行緒池中的執行緒的名字字首
public String getThreadNamePrefix() {
if(threadNamePrefix == null) {
threadNamePrefix = schedulerInstanceName + "-SimpleThreadPoolWorker";
}
return threadNamePrefix;
}
/**
* @return Returns the
* threadsInheritContextClassLoaderOfInitializingThread.
*/
public boolean isThreadsInheritContextClassLoaderOfInitializingThread() {
return inheritLoader;
}
/**
* @param inheritLoader
* The threadsInheritContextClassLoaderOfInitializingThread to
* set.
*/
public void setThreadsInheritContextClassLoaderOfInitializingThread(
boolean inheritLoader) {
this.inheritLoader = inheritLoader;
}
public boolean isThreadsInheritGroupOfInitializingThread() {
return inheritGroup;
}
public void setThreadsInheritGroupOfInitializingThread(
boolean inheritGroup) {
this.inheritGroup = inheritGroup;
}
/**
* @return Returns the value of makeThreadsDaemons.
*/
public boolean isMakeThreadsDaemons() {
return makeThreadsDaemons;
}
/**
* @param makeThreadsDaemons
* The value of makeThreadsDaemons to set.
*/
public void setMakeThreadsDaemons(boolean makeThreadsDaemons) {
this.makeThreadsDaemons = makeThreadsDaemons;
}
public void setInstanceId(String schedInstId) {
}
public void setInstanceName(String schedName) {
schedulerInstanceName = schedName;
}
public void initialize() throws SchedulerConfigException {
if(workers != null && workers.size() > 0) // already initialized...
return;
if (count <= 0) {
throw new SchedulerConfigException(
"Thread count must be > 0");
}
if (prio <= 0 || prio > 9) {
throw new SchedulerConfigException(
"Thread priority must be > 0 and <= 9");
}
if(isThreadsInheritGroupOfInitializingThread()) {
threadGroup = Thread.currentThread().getThreadGroup();
} else {
// follow the threadGroup tree to the root thread group.
threadGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parent = threadGroup;
while ( !parent.getName().equals("main") ) {
threadGroup = parent;
parent = threadGroup.getParent();
}
threadGroup = new ThreadGroup(parent, schedulerInstanceName + "-SimpleThreadPool");
if (isMakeThreadsDaemons()) {
threadGroup.setDaemon(true);
}
}
if (isThreadsInheritContextClassLoaderOfInitializingThread()) {
getLog().info(
"Job execution threads will use class loader of thread: "
+ Thread.currentThread().getName());
}
// create the worker threads and start them
Iterator<WorkerThread> workerThreads = createWorkerThreads(count).iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.start();
availWorkers.add(wt);
}
}
//建立工作執行緒WorkerThread
protected List<WorkerThread> createWorkerThreads(int createCount) {
workers = new LinkedList<WorkerThread>();
for (int i = 1; i<= createCount; ++i) {
//new一個WorkerThread物件,引數分別為執行緒所在的組,執行緒名稱,執行緒權重,以及是否後臺執行緒標誌
WorkerThread wt = new WorkerThread(this, threadGroup,
getThreadNamePrefix() + "-" + i,
getThreadPriority(),
isMakeThreadsDaemons());
if (isThreadsInheritContextClassLoaderOfInitializingThread()) {
wt.setContextClassLoader(Thread.currentThread()
.getContextClassLoader());
}
workers.add(wt);
}
return workers;
}
/**
* <p>
* Terminate any worker threads in this thread group.
* </p>
*
* <p>
* Jobs currently in progress will complete.
* </p>
*/
public void shutdown() {
shutdown(true);
}
/**
* <p>
* Terminate any worker threads in this thread group.
* </p>
*
* <p>
* Jobs currently in progress will complete.
* </p>
*/
public void shutdown(boolean waitForJobsToComplete) {
synchronized (nextRunnableLock) {
getLog().debug("Shutting down threadpool...");
isShutdown = true;
if(workers == null) // case where the pool wasn't even initialize()ed
return;
// signal each worker thread to shut down
Iterator<WorkerThread> workerThreads = workers.iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = workerThreads.next();
wt.shutdown();
availWorkers.remove(wt);
}
// Give waiting (wait(1000)) worker threads a chance to shut down.
// Active worker threads will shut down after finishing their
// current job.
nextRunnableLock.notifyAll();
if (waitForJobsToComplete == true) {
// wait for hand-off in runInThread to complete...
while(handoffPending) {
try { nextRunnableLock.wait(100); } catch(Throwable t) {}
}
// Wait until all worker threads are shut down
while (busyWorkers.size() > 0) {
WorkerThread wt = (WorkerThread) busyWorkers.getFirst();
try {
getLog().debug(
"Waiting for thread " + wt.getName()
+ " to shut down");
// note: with waiting infinite time the
// application may appear to 'hang'.
nextRunnableLock.wait(2000);
} catch (InterruptedException ex) {
}
}
workerThreads = workers.iterator();
while(workerThreads.hasNext()) {
WorkerThread wt = (WorkerThread) workerThreads.next();
try {
wt.join();
workerThreads.remove();
} catch (InterruptedException ignore) {
}
}
getLog().debug("No executing jobs remaining, all threads stopped.");
}
getLog().debug("Shutdown of threadpool complete.");
}
}
/**
* <p>
* Run the given <code>Runnable</code> object in the next available
* <code>Thread</code>. If while waiting the thread pool is asked to
* shut down, the Runnable is executed immediately within a new additional
* thread.
* </p>
*
* @param runnable
* the <code>Runnable</code> to be added.
*/
public boolean runInThread(Runnable runnable) {
if (runnable == null) {
return false;
}
synchronized (nextRunnableLock) {
handoffPending = true;
// Wait until a worker thread is available
while ((availWorkers.size() < 1) && !isShutdown) {
try {
nextRunnableLock.wait(500);
} catch (InterruptedException ignore) {
}
}
if (!isShutdown) {
WorkerThread wt = (WorkerThread)availWorkers.removeFirst();
busyWorkers.add(wt);
wt.run(runnable);
} else {
// If the thread pool is going down, execute the Runnable
// within a new additional worker thread (no thread from the pool).
WorkerThread wt = new WorkerThread(this, threadGroup,
"WorkerThread-LastJob", prio, isMakeThreadsDaemons(), runnable);
busyWorkers.add(wt);
workers.add(wt);
wt.start();
}
nextRunnableLock.notifyAll();
handoffPending = false;
}
return true;
}
public int blockForAvailableThreads() {
synchronized(nextRunnableLock) {
while((availWorkers.size() < 1 || handoffPending) && !isShutdown) {
try {
nextRunnableLock.wait(500);
} catch (InterruptedException ignore) {
}
}
return availWorkers.size();
}
}
protected void makeAvailable(WorkerThread wt) {
synchronized(nextRunnableLock) {
if(!isShutdown) {
availWorkers.add(wt);
}
busyWorkers.remove(wt);
nextRunnableLock.notifyAll();
}
}
protected void clearFromBusyWorkersList(WorkerThread wt) {
synchronized(nextRunnableLock) {
busyWorkers.remove(wt);
nextRunnableLock.notifyAll();
}
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* WorkerThread Class.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* <p>
* A Worker loops, waiting to execute tasks.
* </p>
*/
class WorkerThread extends Thread {
// A flag that signals the WorkerThread to terminate.
private AtomicBoolean run = new AtomicBoolean(true);
private SimpleThreadPool tp;
private Runnable runnable = null;
private boolean runOnce = false;
/**
* <p>
* Create a worker thread and start it. Waiting for the next Runnable,
* executing it, and waiting for the next Runnable, until the shutdown
* flag is set.
* </p>
*/
WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name,
int prio, boolean isDaemon) {
this(tp, threadGroup, name, prio, isDaemon, null);
}
/**
* <p>
* Create a worker thread, start it, execute the runnable and terminate
* the thread (one time execution).
* </p>
*/
WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name,
int prio, boolean isDaemon, Runnable runnable) {
super(threadGroup, name);
this.tp = tp;
this.runnable = runnable;
if(runnable != null)
runOnce = true;
setPriority(prio);
setDaemon(isDaemon);
}
/**
* <p>
* Signal the thread that it should terminate.
* </p>
*/
void shutdown() {
run.set(false);
}
public void run(Runnable newRunnable) {
synchronized(this) {
if(runnable != null) {
throw new IllegalStateException("Already running a Runnable!");
}
runnable = newRunnable;
this.notifyAll();
}
}
/**
* <p>
* Loop, executing targets as they are received.
* </p>
*/
@Override
public void run() {
boolean ran = false;
while (run.get()) {
try {
synchronized(this) {
while (runnable == null && run.get()) {
this.wait(500);
}
if (runnable != null) {
ran = true;
runnable.run();
}
}
} catch (InterruptedException unblock) {
// do nothing (loop will terminate if shutdown() was called
try {
getLog().error("Worker thread was interrupt()'ed.", unblock);
} catch(Exception e) {
// ignore to help with a tomcat glitch
}
} catch (Throwable exceptionInRunnable) {
try {
getLog().error("Error while executing the Runnable: ",
exceptionInRunnable);
} catch(Exception e) {
// ignore to help with a tomcat glitch
}
} finally {
synchronized(this) {
runnable = null;
}
// repair the thread in case the runnable mucked it up...
if(getPriority() != tp.getThreadPriority()) {
setPriority(tp.getThreadPriority());
}
if (runOnce) {
run.set(false);
clearFromBusyWorkersList(this);
} else if(ran) {
ran = false;
makeAvailable(this);
}
}
}
//if (log.isDebugEnabled())
try {
getLog().debug("WorkerThread is shut down.");
} catch(Exception e) {
// ignore to help with a tomcat glitch
}
}
}
}
3、quartz的預設配置
執行緒池的預設配置如下:
quartz的執行緒池中預設執行緒數為10個:
# 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.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
4、排程工廠StdSchedulerFactory
設定了私有環境變數PROPERTIES_FILE,預設載入屬性檔案quartz.properties檔案
重點關注方法->:private Scheduler instantiate() throws SchedulerException
該方法佔據了該類的主要篇幅。
它會通過反射生成執行緒池SimpleThreadPool,並對其進行初始化。
擷取該方法的這部分程式碼如下:
rsrcs.setThreadPool(tp);
if(tp instanceof SimpleThreadPool) {
((SimpleThreadPool)tp).setThreadNamePrefix(schedName + "_Worker");
if(threadsInheritInitalizersClassLoader)
((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader);
}
tp.initialize();
tpInited = true;
schedName預設為quartz.properties檔案中的值——DefaultQuartzScheduler
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
所以quartz執行緒池中的執行緒名稱預設為:
DefaultQuartzScheduler_Worker-{i} ,i為序號
該類的原始碼如下:
/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.impl;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.AccessControlException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Locale;
import java.util.Properties;
import org.quartz.JobListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerConfigException;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerListener;
import org.quartz.core.JobRunShellFactory;
import org.quartz.core.QuartzScheduler;
import org.quartz.core.QuartzSchedulerResources;
import org.quartz.ee.jta.JTAAnnotationAwareJobRunShellFactory;
import org.quartz.ee.jta.JTAJobRunShellFactory;
import org.quartz.ee.jta.UserTransactionHelper;
import org.quartz.impl.jdbcjobstore.JobStoreSupport;
import org.quartz.impl.jdbcjobstore.Semaphore;
import org.quartz.impl.jdbcjobstore.TablePrefixAware;
import org.quartz.impl.matchers.EverythingMatcher;
import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.ClassLoadHelper;
import org.quartz.spi.InstanceIdGenerator;
import org.quartz.spi.JobFactory;
import org.quartz.spi.JobStore;
import org.quartz.spi.SchedulerPlugin;
import org.quartz.spi.ThreadExecutor;
import org.quartz.spi.ThreadPool;
import org.quartz.utils.ConnectionProvider;
import org.quartz.utils.DBConnectionManager;
import org.quartz.utils.JNDIConnectionProvider;
import org.quartz.utils.PoolingConnectionProvider;
import org.quartz.utils.PropertiesParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* An implementation of <code>{@link org.quartz.SchedulerFactory}</code> that
* does all of its work of creating a <code>QuartzScheduler</code> instance
* based on the contents of a <code>Properties</code> file.
* </p>
*
* <p>
* By default a properties file named "quartz.properties" is loaded from the
* 'current working directory'. If that fails, then the "quartz.properties"
* file located (as a resource) in the org/quartz package is loaded. If you
* wish to use a file other than these defaults, you must define the system
* property 'org.quartz.properties' to point to the file you want.
* </p>
*
* <p>
* Alternatively, you can explicitly initialize the factory by calling one of
* the <code>initialize(xx)</code> methods before calling <code>getScheduler()</code>.
* </p>
*
* <p>
* See the sample properties files that are distributed with Quartz for
* information about the various settings available within the file.
* Full configuration documentation can be found at
* http://www.quartz-scheduler.org/docs/configuration/index.html
* </p>
*
* <p>
* Instances of the specified <code>{@link org.quartz.spi.JobStore}</code>,
* <code>{@link org.quartz.spi.ThreadPool}</code>, and other SPI classes will be created
* by name, and then any additional properties specified for them in the config
* file will be set on the instance by calling an equivalent 'set' method. For
* example if the properties file contains the property
* 'org.quartz.jobStore.myProp = 10' then after the JobStore class has been
* instantiated, the method 'setMyProp()' will be called on it. Type conversion
* to primitive Java types (int, long, float, double, boolean, and String) are
* performed before calling the property's setter method.
* </p>
*
* <p>
* One property can reference another property's value by specifying a value
* following the convention of "$@other.property.name", for example, to reference
* the scheduler's instance name as the value for some other property, you
* would use "$@org.quartz.scheduler.instanceName".
* </p>
*
* @author James House
* @author Anthony Eden
* @author Mohammad Rezaei
*/
public class StdSchedulerFactory implements SchedulerFactory {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constants.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static final String PROPERTIES_FILE = "org.quartz.properties";
public static final String PROP_SCHED_INSTANCE_NAME = "org.quartz.scheduler.instanceName";
public static final String PROP_SCHED_INSTANCE_ID = "org.quartz.scheduler.instanceId";
public static final String PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX = "org.quartz.scheduler.instanceIdGenerator";
public static final String PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS =
PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX + ".class";
public static final String PROP_SCHED_THREAD_NAME = "org.quartz.scheduler.threadName";
public static final String PROP_SCHED_SKIP_UPDATE_CHECK = "org.quartz.scheduler.skipUpdateCheck";
public static final String PROP_SCHED_BATCH_TIME_WINDOW = "org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow";
public static final String PROP_SCHED_MAX_BATCH_SIZE = "org.quartz.scheduler.batchTriggerAcquisitionMaxCount";
public static final String PROP_SCHED_JMX_EXPORT = "org.quartz.scheduler.jmx.export";
public static final String PROP_SCHED_JMX_OBJECT_NAME = "org.quartz.scheduler.jmx.objectName";
public static final String PROP_SCHED_JMX_PROXY = "org.quartz.scheduler.jmx.proxy";
public static final String PROP_SCHED_JMX_PROXY_CLASS = "org.quartz.scheduler.jmx.proxy.class";
public static final String PROP_SCHED_RMI_EXPORT = "org.quartz.scheduler.rmi.export";
public static final String PROP_SCHED_RMI_PROXY = "org.quartz.scheduler.rmi.proxy";
public static final String PROP_SCHED_RMI_HOST = "org.quartz.scheduler.rmi.registryHost";
public static final String PROP_SCHED_RMI_PORT = "org.quartz.scheduler.rmi.registryPort";
public static final String PROP_SCHED_RMI_SERVER_PORT = "org.quartz.scheduler.rmi.serverPort";
public static final String PROP_SCHED_RMI_CREATE_REGISTRY = "org.quartz.scheduler.rmi.createRegistry";
public static final String PROP_SCHED_RMI_BIND_NAME = "org.quartz.scheduler.rmi.bindName";
public static final String PROP_SCHED_WRAP_JOB_IN_USER_TX = "org.quartz.scheduler.wrapJobExecutionInUserTransaction";
public static final String PROP_SCHED_USER_TX_URL = "org.quartz.scheduler.userTransactionURL";
public static final String PROP_SCHED_IDLE_WAIT_TIME = "org.quartz.scheduler.idleWaitTime";
public static final String PROP_SCHED_DB_FAILURE_RETRY_INTERVAL = "org.quartz.scheduler.dbFailureRetryInterval";
public static final String PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON = "org.quartz.scheduler.makeSchedulerThreadDaemon";
public static final String PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD = "org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer";
public static final String PROP_SCHED_CLASS_LOAD_HELPER_CLASS = "org.quartz.scheduler.classLoadHelper.class";
public static final String PROP_SCHED_JOB_FACTORY_CLASS = "org.quartz.scheduler.jobFactory.class";
public static final String PROP_SCHED_JOB_FACTORY_PREFIX = "org.quartz.scheduler.jobFactory";
public static final String PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN = "org.quartz.scheduler.interruptJobsOnShutdown";
public static final String PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT = "org.quartz.scheduler.interruptJobsOnShutdownWithWait";
public static final String PROP_SCHED_CONTEXT_PREFIX = "org.quartz.context.key";
public static final String PROP_THREAD_POOL_PREFIX = "org.quartz.threadPool";
public static final String PROP_THREAD_POOL_CLASS = "org.quartz.threadPool.class";
public static final String PROP_JOB_STORE_PREFIX = "org.quartz.jobStore";
public static final String PROP_JOB_STORE_LOCK_HANDLER_PREFIX = PROP_JOB_STORE_PREFIX + ".lockHandler";
public static final String PROP_JOB_STORE_LOCK_HANDLER_CLASS = PROP_JOB_STORE_LOCK_HANDLER_PREFIX + ".class";
public static final String PROP_TABLE_PREFIX = "tablePrefix";
public static final String PROP_SCHED_NAME = "schedName";
public static final String PROP_JOB_STORE_CLASS = "org.quartz.jobStore.class";
public static final String PROP_JOB_STORE_USE_PROP = "org.quartz.jobStore.useProperties";
public static final String PROP_DATASOURCE_PREFIX = "org.quartz.dataSource";
public static final String PROP_CONNECTION_PROVIDER_CLASS = "connectionProvider.class";
public static final String PROP_DATASOURCE_DRIVER = "driver";
public static final String PROP_DATASOURCE_URL = "URL";
public static final String PROP_DATASOURCE_USER = "user";
public static final String PROP_DATASOURCE_PASSWORD = "password";
public static final String PROP_DATASOURCE_MAX_CONNECTIONS = "maxConnections";
public static final String PROP_DATASOURCE_VALIDATION_QUERY = "validationQuery";
public static final String PROP_DATASOURCE_JNDI_URL = "jndiURL";
public static final String PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP = "jndiAlwaysLookup";
public static final String PROP_DATASOURCE_JNDI_INITIAL = "java.naming.factory.initial";
public static final String PROP_DATASOURCE_JNDI_PROVDER = "java.naming.provider.url";
public static final String PROP_DATASOURCE_JNDI_PRINCIPAL = "java.naming.security.principal";
public static final String PROP_DATASOURCE_JNDI_CREDENTIALS = "java.naming.security.credentials";
public static final String PROP_PLUGIN_PREFIX = "org.quartz.plugin";
public static final String PROP_PLUGIN_CLASS = "class";
public static final String PROP_JOB_LISTENER_PREFIX = "org.quartz.jobListener";
public static final String PROP_TRIGGER_LISTENER_PREFIX = "org.quartz.triggerListener";
public static final String PROP_LISTENER_CLASS = "class";
public static final String DEFAULT_INSTANCE_ID = "NON_CLUSTERED";
public static final String AUTO_GENERATE_INSTANCE_ID = "AUTO";
public static final String PROP_THREAD_EXECUTOR = "org.quartz.threadExecutor";
public static final String PROP_THREAD_EXECUTOR_CLASS = "org.quartz.threadExecutor.class";
public static final String SYSTEM_PROPERTY_AS_INSTANCE_ID = "SYS_PROP";
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private SchedulerException initException = null;
private String propSrc = null;
private PropertiesParser cfg;
private final Logger log = LoggerFactory.getLogger(getClass());
// private Scheduler scheduler;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/**
* Create an uninitialized StdSchedulerFactory.
*/
public StdSchedulerFactory() {
}
/**
* Create a StdSchedulerFactory that has been initialized via
* <code>{@link #initialize(Properties)}</code>.
*
* @see #initialize(Properties)
*/
public StdSchedulerFactory(Properties props) throws SchedulerException {
initialize(props);
}
/**
* Create a StdSchedulerFactory that has been initialized via
* <code>{@link #initialize(String)}</code>.
*
* @see #initialize(String)
*/
public StdSchedulerFactory(String fileName) throws SchedulerException {
initialize(fileName);
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public Logger getLog() {
return log;
}
/**
* <p>
* Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
* the contents of a <code>Properties</code> file and overriding System
* properties.
* </p>
*
* <p>
* By default a properties file named "quartz.properties" is loaded from
* the 'current working directory'. If that fails, then the
* "quartz.properties" file located (as a resource) in the org/quartz
* package is loaded. If you wish to use a file other than these defaults,
* you must define the system property 'org.quartz.properties' to point to
* the file you want.
* </p>
*
* <p>
* System properties (environment variables, and -D definitions on the
* command-line when running the JVM) override any properties in the
* loaded file. For this reason, you may want to use a different initialize()
* method if your application security policy prohibits access to
* <code>{@link java.lang.System#getProperties()}</code>.
* </p>
*/
public void initialize() throws SchedulerException {
// short-circuit if already initialized
if (cfg != null) {
return;
}
if (initException != null) {
throw initException;
}
String requestedFile = System.getProperty(PROPERTIES_FILE);
String propFileName = requestedFile != null ? requestedFile
: "quartz.properties";
File propFile = new File(propFileName);
Properties props = new Properties();
InputStream in = null;
try {
if (propFile.exists()) {
try {
if (requestedFile != null) {
propSrc = "specified file: '" + requestedFile + "'";
} else {
propSrc = "default file in current working dir: 'quartz.properties'";
}
in = new BufferedInputStream(new FileInputStream(propFileName));
props.load(in);
} catch (IOException ioe) {
initException = new SchedulerException("Properties file: '"
+ propFileName + "' could not be read.", ioe);
throw initException;
}
} else if (requestedFile != null) {
in =
Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile);
if(in == null) {
initException = new SchedulerException("Properties file: '"
+ requestedFile + "' could not be found.");
throw initException;
}
propSrc = "specified file: '" + requestedFile + "' in the class resource path.";
in = new BufferedInputStream(in);
try {
props.load(in);
} catch (IOException ioe) {
initException = new SchedulerException("Properties file: '"
+ requestedFile + "' could not be read.", ioe);
throw initException;
}
} else {
propSrc = "default resource file in Quartz package: 'quartz.properties'";
ClassLoader cl = getClass().getClassLoader();
if(cl == null)
cl = findClassloader();
if(cl == null)
throw new SchedulerConfigException("Unable to find a class loader on the current thread or class.");
in = cl.getResourceAsStream(
"quartz.properties");
if (in == null) {
in = cl.getResourceAsStream(
"/quartz.properties");
}
if (in == null) {
in = cl.getResourceAsStream(
"org/quartz/quartz.properties");
}
if (in == null) {
initException = new SchedulerException(
"Default quartz.properties not found in class path");
throw initException;
}
try {
props.load(in);
} catch (IOException ioe) {
initException = new SchedulerException(
"Resource properties file: 'org/quartz/quartz.properties' "
+ "could not be read from the classpath.", ioe);
throw initException;
}
}
} finally {
if(in != null) {
try { in.close(); } catch(IOException ignore) { /* ignore */ }
}
}
initialize(overrideWithSysProps(props));
}
/**
* Add all System properties to the given <code>props</code>. Will override
* any properties that already exist in the given <code>props</code>.
*/
private Properties overrideWithSysProps(Properties props) {
Properties sysProps = null;
try {
sysProps = System.getProperties();
} catch (AccessControlException e) {
getLog().warn(
"Skipping overriding quartz properties with System properties " +
"during initialization because of an AccessControlException. " +
"This is likely due to not having read/write access for " +
"java.util.PropertyPermission as required by java.lang.System.getProperties(). " +
"To resolve this warning, either add this permission to your policy file or " +
"use a non-default version of initialize().",
e);
}
if (sysProps != null) {
props.putAll(sysProps);
}
return props;
}
/**
* <p>
* Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
* the contents of the <code>Properties</code> file with the given
* name.
* </p>
*/
public void initialize(String filename) throws SchedulerException {
// short-circuit if already initialized
if (cfg != null) {
return;
}
if (initException != null) {
throw initException;
}
InputStream is = null;
Properties props = new Properties();
is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
try {
if(is != null) {
is = new BufferedInputStream(is);
propSrc = "the specified file : '" + filename + "' from the class resource path.";
} else {
is = new BufferedInputStream(new FileInputStream(filename));
propSrc = "the specified file : '" + filename + "'";
}
props.load(is);
} catch (IOException ioe) {
initException = new SchedulerException("Properties file: '"
+ filename + "' could not be read.", ioe);
throw initException;
}
finally {
if(is != null)
try { is.close(); } catch(IOException ignore) {}
}
initialize(props);
}
/**
* <p>
* Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
* the contents of the <code>Properties</code> file opened with the
* given <code>InputStream</code>.
* </p>
*/
public void initialize(InputStream propertiesStream)
throws SchedulerException {
// short-circuit if already initialized
if (cfg != null) {
return;
}
if (initException != null) {
throw initException;
}
Properties props = new Properties();
if (propertiesStream != null) {
try {
props.load(propertiesStream);
propSrc = "an externally opened InputStream.";
} catch (IOException e) {
initException = new SchedulerException(
"Error loading property data from InputStream", e);
throw initException;
}
} else {
initException = new SchedulerException(
"Error loading property data from InputStream - InputStream is null.");
throw initException;
}
initialize(props);
}
/**
* <p>
* Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
* the contents of the given <code>Properties</code> object.
* </p>
*/
public void initialize(Properties props) throws SchedulerException {
if (propSrc == null) {
propSrc = "an externally provided properties instance.";
}
this.cfg = new PropertiesParser(props);
}
private Scheduler instantiate() throws SchedulerException {
if (cfg == null) {
initialize();
}
if (initException != null) {
throw initException;
}
JobStore js = null;
ThreadPool tp = null;
QuartzScheduler qs = null;
DBConnectionManager dbMgr = null;
String instanceIdGeneratorClass = null;
Properties tProps = null;
String userTXLocation = null;
boolean wrapJobInTx = false;
boolean autoId = false;
long idleWaitTime = -1;
long dbFailureRetry = 15000L; // 15 secs
String classLoadHelperClass;
String jobFactoryClass;
ThreadExecutor threadExecutor;
SchedulerRepository schedRep = SchedulerRepository.getInstance();
// Get Scheduler Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String schedName = cfg.getStringProperty(PROP_SCHED_INSTANCE_NAME,
"QuartzScheduler");
String threadName = cfg.getStringProperty(PROP_SCHED_THREAD_NAME,
schedName + "_QuartzSchedulerThread");
String schedInstId = cfg.getStringProperty(PROP_SCHED_INSTANCE_ID,
DEFAULT_INSTANCE_ID);
if (schedInstId.equals(AUTO_GENERATE_INSTANCE_ID)) {
autoId = true;
instanceIdGeneratorClass = cfg.getStringProperty(
PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS,
"org.quartz.simpl.SimpleInstanceIdGenerator");
}
else if (schedInstId.equals(SYSTEM_PROPERTY_AS_INSTANCE_ID)) {
autoId = true;
instanceIdGeneratorClass =
"org.quartz.simpl.SystemPropertyInstanceIdGenerator";
}
userTXLocation = cfg.getStringProperty(PROP_SCHED_USER_TX_URL,
userTXLocation);
if (userTXLocation != null && userTXLocation.trim().length() == 0) {
userTXLocation = null;
}
classLoadHelperClass = cfg.getStringProperty(
PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
"org.quartz.simpl.CascadingClassLoadHelper");
wrapJobInTx = cfg.getBooleanProperty(PROP_SCHED_WRAP_JOB_IN_USER_TX,
wrapJobInTx);
jobFactoryClass = cfg.getStringProperty(
PROP_SCHED_JOB_FACTORY_CLASS, null);
idleWaitTime = cfg.getLongProperty(PROP_SCHED_IDLE_WAIT_TIME,
idleWaitTime);
if(idleWaitTime > -1 && idleWaitTime < 1000) {
throw new SchedulerException("org.quartz.scheduler.idleWaitTime of less than 1000ms is not legal.");
}
dbFailureRetry = cfg.getLongProperty(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry);
if (dbFailureRetry < 0) {
throw new SchedulerException(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL + " of less than 0 ms is not legal.");
}
boolean makeSchedulerThreadDaemon =
cfg.getBooleanProperty(PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON);
boolean threadsInheritInitalizersClassLoader =
cfg.getBooleanProperty(PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD);
boolean skipUpdateCheck = cfg.getBooleanProperty(PROP_SCHED_SKIP_UPDATE_CHECK, false);
long batchTimeWindow = cfg.getLongProperty(PROP_SCHED_BATCH_TIME_WINDOW, 0L);
int maxBatchSize = cfg.getIntProperty(PROP_SCHED_MAX_BATCH_SIZE, 1);
boolean interruptJobsOnShutdown = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN, false);
boolean interruptJobsOnShutdownWithWait = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT, false);
boolean jmxExport = cfg.getBooleanProperty(PROP_SCHED_JMX_EXPORT);
String jmxObjectName = cfg.getStringProperty(PROP_SCHED_JMX_OBJECT_NAME);
boolean jmxProxy = cfg.getBooleanProperty(PROP_SCHED_JMX_PROXY);
String jmxProxyClass = cfg.getStringProperty(PROP_SCHED_JMX_PROXY_CLASS);
boolean rmiExport = cfg.getBooleanProperty(PROP_SCHED_RMI_EXPORT, false);
boolean rmiProxy = cfg.getBooleanProperty(PROP_SCHED_RMI_PROXY, false);
String rmiHost = cfg.getStringProperty(PROP_SCHED_RMI_HOST, "localhost");
int rmiPort = cfg.getIntProperty(PROP_SCHED_RMI_PORT, 1099);
int rmiServerPort = cfg.getIntProperty(PROP_SCHED_RMI_SERVER_PORT, -1);
String rmiCreateRegistry = cfg.getStringProperty(
PROP_SCHED_RMI_CREATE_REGISTRY,
QuartzSchedulerResources.CREATE_REGISTRY_NEVER);
String rmiBindName = cfg.getStringProperty(PROP_SCHED_RMI_BIND_NAME);
if (jmxProxy && rmiProxy) {
throw new SchedulerConfigException("Cannot proxy both RMI and JMX.");
}
Properties schedCtxtProps = cfg.getPropertyGroup(PROP_SCHED_CONTEXT_PREFIX, true);
// If Proxying to remote scheduler, short-circuit here...
// ~~~~~~~~~~~~~~~~~~
if (rmiProxy) {
if (autoId) {
schedInstId = DEFAULT_INSTANCE_ID;
}
String uid = (rmiBindName == null) ? QuartzSchedulerResources.getUniqueIdentifier(
schedName, schedInstId) : rmiBindName;
RemoteScheduler remoteScheduler = new RemoteScheduler(uid, rmiHost, rmiPort);
schedRep.bind(remoteScheduler);
return remoteScheduler;
}
// Create class load helper
ClassLoadHelper loadHelper = null;
try {
loadHelper = (ClassLoadHelper) loadClass(classLoadHelperClass)
.newInstance();
} catch (Exception e) {
throw new SchedulerConfigException(
"Unable to instantiate class load helper class: "
+ e.getMessage(), e);
}
loadHelper.initialize();
// If Proxying to remote JMX scheduler, short-circuit here...
// ~~~~~~~~~~~~~~~~~~
if (jmxProxy) {
if (autoId) {
schedInstId = DEFAULT_INSTANCE_ID;
}
if (jmxProxyClass == null) {
throw new SchedulerConfigException("No JMX Proxy Scheduler class provided");
}
RemoteMBeanScheduler jmxScheduler = null;
try {
jmxScheduler = (RemoteMBeanScheduler)loadHelper.loadClass(jmxProxyClass)
.newInstance();
} catch (Exception e) {
throw new SchedulerConfigException(
"Unable to instantiate RemoteMBeanScheduler class.", e);
}
if (jmxObjectName == null) {
jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId);
}
jmxScheduler.setSchedulerObjectName(jmxObjectName);
tProps = cfg.getPropertyGroup(PROP_SCHED_JMX_PROXY, true);
try {
setBeanProps(jmxScheduler, tProps);
} catch (Exception e) {
initException = new SchedulerException("RemoteMBeanScheduler class '"
+ jmxProxyClass + "' props could not be configured.", e);
throw initException;
}
jmxScheduler.initialize();
schedRep.bind(jmxScheduler);
return jmxScheduler;
}
JobFactory jobFactory = null;
if(jobFactoryClass != null) {
try {
jobFactory = (JobFactory) loadHelper.loadClass(jobFactoryClass)
.newInstance();
} catch (Exception e) {
throw new SchedulerConfigException(
"Unable to instantiate JobFactory class: "
+ e.getMessage(), e);
}
tProps = cfg.getPropertyGroup(PROP_SCHED_JOB_FACTORY_PREFIX, true);
try {
setBeanProps(jobFactory, tProps);
} catch (Exception e) {
initException = new SchedulerException("JobFactory class '"
+ jobFactoryClass + "' props could not be configured.", e);
throw initException;
}
}
InstanceIdGenerator instanceIdGenerator = null;
if(instanceIdGeneratorClass != null) {
try {
instanceIdGenerator = (InstanceIdGenerator) loadHelper.loadClass(instanceIdGeneratorClass)
.newInstance();
} catch (Exception e) {
throw new SchedulerConfigException(
"Unable to instantiate InstanceIdGenerator class: "
+ e.getMessage(), e);
}
tProps = cfg.getPropertyGroup(PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX, true);
try {
setBeanProps(instanceIdGenerator, tProps);
} catch (Exception e) {
initException = new SchedulerException("InstanceIdGenerator class '"
+ instanceIdGeneratorClass + "' props could not be configured.", e);
throw initException;
}
}
// Get ThreadPool Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
if (tpClass == null) {
initException = new SchedulerException(
"ThreadPool class not specified. ");
throw initException;
}
try {
tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' could not be instantiated.", e);
throw initException;
}
tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true);
try {
setBeanProps(tp, tProps);
} catch (Exception e) {
initException = new SchedulerException("ThreadPool class '"
+ tpClass + "' props could not be configured.", e);
throw initException;
}
// Get JobStore Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String jsClass = cfg.getStringProperty(PROP_JOB_STORE_CLASS,
RAMJobStore.class.getName());
if (jsClass == null) {
initException = new SchedulerException(
"JobStore class not specified. ");
throw initException;
}
try {
js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' could not be instantiated.", e);
throw initException;
}
SchedulerDetailsSetter.setDetails(js, schedName, schedInstId);
tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
try {
setBeanProps(js, tProps);
} catch (Exception e) {
initException = new SchedulerException("JobStore class '" + jsClass
+ "' props could not be configured.", e);
throw initException;
}
if (js instanceof JobStoreSupport) {
// Install custom lock handler (Semaphore)
String lockHandlerClass = cfg.getStringProperty(PROP_JOB_STORE_LOCK_HANDLER_CLASS);
if (lockHandlerClass != null) {
try {
Semaphore lockHandler = (Semaphore)loadHelper.loadClass(lockHandlerClass).newInstance();
tProps = cfg.getPropertyGroup(PROP_JOB_STORE_LOCK_HANDLER_PREFIX, true);
// If this lock handler requires the table prefix, add it to its properties.
if (lockHandler instanceof TablePrefixAware) {
tProps.setProperty(
PROP_TABLE_PREFIX, ((JobStoreSupport)js).getTablePrefix());
tProps.setProperty(
PROP_SCHED_NAME, schedName);
}
try {
setBeanProps(lockHandler, tProps);
} catch (Exception e) {
initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
+ "' props could not be configured.", e);
throw initException;
}
((JobStoreSupport)js).setLockHandler(lockHandler);
getLog().info("Using custom data access locking (synchronization): " + lockHandlerClass);
} catch (Exception e) {
initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
+ "' could not be instantiated.", e);
throw initException;
}
}
}
// Set up any DataSources
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);
for (int i = 0; i < dsNames.length; i++) {
PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(
PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));
String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);
// custom connectionProvider...
if(cpClass != null) {
ConnectionProvider cp = null;
try {
cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException("ConnectionProvider class '" + cpClass
+ "' could not be instantiated.", e);
throw initException;
}
try {
// remove the class name, so it isn't attempted to be set
pp.getUnderlyingProperties().remove(
PROP_CONNECTION_PROVIDER_CLASS);
setBeanProps(cp, pp.getUnderlyingProperties());
} catch (Exception e) {
initException = new SchedulerException("ConnectionProvider class '" + cpClass
+ "' props could not be configured.", e);
throw initException;
}
dbMgr = DBConnectionManager.getInstance();
dbMgr.addConnectionProvider(dsNames[i], cp);
} else {
String dsJndi = pp.getStringProperty(PROP_DATASOURCE_JNDI_URL, null);
if (dsJndi != null) {
boolean dsAlwaysLookup = pp.getBooleanProperty(
PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP);
String dsJndiInitial = pp.getStringProperty(
PROP_DATASOURCE_JNDI_INITIAL);
String dsJndiProvider = pp.getStringProperty(
PROP_DATASOURCE_JNDI_PROVDER);
String dsJndiPrincipal = pp.getStringProperty(
PROP_DATASOURCE_JNDI_PRINCIPAL);
String dsJndiCredentials = pp.getStringProperty(
PROP_DATASOURCE_JNDI_CREDENTIALS);
Properties props = null;
if (null != dsJndiInitial || null != dsJndiProvider
|| null != dsJndiPrincipal || null != dsJndiCredentials) {
props = new Properties();
if (dsJndiInitial != null) {
props.put(PROP_DATASOURCE_JNDI_INITIAL,
dsJndiInitial);
}
if (dsJndiProvider != null) {
props.put(PROP_DATASOURCE_JNDI_PROVDER,
dsJndiProvider);
}
if (dsJndiPrincipal != null) {
props.put(PROP_DATASOURCE_JNDI_PRINCIPAL,
dsJndiPrincipal);
}
if (dsJndiCredentials != null) {
props.put(PROP_DATASOURCE_JNDI_CREDENTIALS,
dsJndiCredentials);
}
}
JNDIConnectionProvider cp = new JNDIConnectionProvider(dsJndi,
props, dsAlwaysLookup);
dbMgr = DBConnectionManager.getInstance();
dbMgr.addConnectionProvider(dsNames[i], cp);
} else {
String dsDriver = pp.getStringProperty(PROP_DATASOURCE_DRIVER);
String dsURL = pp.getStringProperty(PROP_DATASOURCE_URL);
String dsUser = pp.getStringProperty(PROP_DATASOURCE_USER, "");
String dsPass = pp.getStringProperty(PROP_DATASOURCE_PASSWORD, "");
int dsCnt = pp.getIntProperty(PROP_DATASOURCE_MAX_CONNECTIONS, 10);
String dsValidation = pp.getStringProperty(PROP_DATASOURCE_VALIDATION_QUERY);
if (dsDriver == null) {
initException = new SchedulerException(
"Driver not specified for DataSource: "
+ dsNames[i]);
throw initException;
}
if (dsURL == null) {
initException = new SchedulerException(
"DB URL not specified for DataSource: "
+ dsNames[i]);
throw initException;
}
try {
PoolingConnectionProvider cp = new PoolingConnectionProvider(
dsDriver, dsURL, dsUser, dsPass, dsCnt,
dsValidation);
dbMgr = DBConnectionManager.getInstance();
dbMgr.addConnectionProvider(dsNames[i], cp);
} catch (SQLException sqle) {
initException = new SchedulerException(
"Could not initialize DataSource: " + dsNames[i],
sqle);
throw initException;
}
}
}
}
// Set up any SchedulerPlugins
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String[] pluginNames = cfg.getPropertyGroups(PROP_PLUGIN_PREFIX);
SchedulerPlugin[] plugins = new SchedulerPlugin[pluginNames.length];
for (int i = 0; i < pluginNames.length; i++) {
Properties pp = cfg.getPropertyGroup(PROP_PLUGIN_PREFIX + "."
+ pluginNames[i], true);
String plugInClass = pp.getProperty(PROP_PLUGIN_CLASS, null);
if (plugInClass == null) {
initException = new SchedulerException(
"SchedulerPlugin class not specified for plugin '"
+ pluginNames[i] + "'");
throw initException;
}
SchedulerPlugin plugin = null;
try {
plugin = (SchedulerPlugin)
loadHelper.loadClass(plugInClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException(
"SchedulerPlugin class '" + plugInClass
+ "' could not be instantiated.", e);
throw initException;
}
try {
setBeanProps(plugin, pp);
} catch (Exception e) {
initException = new SchedulerException(
"JobStore SchedulerPlugin '" + plugInClass
+ "' props could not be configured.", e);
throw initException;
}
plugins[i] = plugin;
}
// Set up any JobListeners
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Class<?>[] strArg = new Class[] { String.class };
String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX);
JobListener[] jobListeners = new JobListener[jobListenerNames.length];
for (int i = 0; i < jobListenerNames.length; i++) {
Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "."
+ jobListenerNames[i], true);
String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);
if (listenerClass == null) {
initException = new SchedulerException(
"JobListener class not specified for listener '"
+ jobListenerNames[i] + "'");
throw initException;
}
JobListener listener = null;
try {
listener = (JobListener)
loadHelper.loadClass(listenerClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException(
"JobListener class '" + listenerClass
+ "' could not be instantiated.", e);
throw initException;
}
try {
Method nameSetter = null;
try {
nameSetter = listener.getClass().getMethod("setName", strArg);
}
catch(NoSuchMethodException ignore) {
/* do nothing */
}
if(nameSetter != null) {
nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } );
}
setBeanProps(listener, lp);
} catch (Exception e) {
initException = new SchedulerException(
"JobListener '" + listenerClass
+ "' props could not be configured.", e);
throw initException;
}
jobListeners[i] = listener;
}
// Set up any TriggerListeners
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String[] triggerListenerNames = cfg.getPropertyGroups(PROP_TRIGGER_LISTENER_PREFIX);
TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length];
for (int i = 0; i < triggerListenerNames.length; i++) {
Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "."
+ triggerListenerNames[i], true);
String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null);
if (listenerClass == null) {
initException = new SchedulerException(
"TriggerListener class not specified for listener '"
+ triggerListenerNames[i] + "'");
throw initException;
}
TriggerListener listener = null;
try {
listener = (TriggerListener)
loadHelper.loadClass(listenerClass).newInstance();
} catch (Exception e) {
initException = new SchedulerException(
"TriggerListener class '" + listenerClass
+ "' could not be instantiated.", e);
throw initException;
}
try {
Method nameSetter = null;
try {
nameSetter = listener.getClass().getMethod("setName", strArg);
}
catch(NoSuchMethodException ignore) { /* do nothing */ }
if(nameSetter != null) {
nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } );
}
setBeanProps(listener, lp);
} catch (Exception e) {
initException = new SchedulerException(
"TriggerListener '" + listenerClass
+ "' props could not be configured.", e);
throw initException;
}
triggerListeners[i] = listener;
}
boolean tpInited = false;
boolean qsInited = false;
// Get ThreadExecutor Properties
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String threadExecutorClass = cfg.getStringProperty(PROP_THREAD_EXECUTOR_CLASS);
if (threadExecutorClass != null) {
tProps = cfg.getPropertyGroup(PROP_THREAD_EXECUTOR, true);
try {
threadExecutor = (ThreadExecutor) loadHelper.loadClass(threadExecutorClass).newInstance();
log.info("Using custom implementation for ThreadExecutor: " + threadExecutorClass);
setBeanProps(threadExecutor, tProps);
} catch (Exception e) {
initException = new SchedulerException(
"ThreadExecutor class '" + threadExecutorClass + "' could not be instantiated.", e);
throw initException;
}
} else {
log.info("Using default implementation for ThreadExecutor");
threadExecutor = new DefaultThreadExecutor();
}
// Fire everything up
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
try {
JobRunShellFactory jrsf = null; // Create correct run-shell factory...
if (userTXLocation != null) {
UserTransactionHelper.setUserTxLocation(userTXLocation);
}
if (wrapJobInTx) {
jrsf = new JTAJobRunShellFactory();
} else {
jrsf = new JTAAnnotationAwareJobRunShellFactory();
}
if (autoId) {
try {
schedInstId = DEFAULT_INSTANCE_ID;
if (js.isClustered()) {
schedInstId = instanceIdGenerator.generateInstanceId();
}
} catch (Exception e) {
getLog().error("Couldn't generate instance Id!", e);
throw new IllegalStateException("Cannot run without an instance id.");
}
}
if (js.getClass().getName().startsWith("org.terracotta.quartz")) {
try {
String uuid = (String) js.getClass().getMethod("getUUID").invoke(js);
if(schedInstId.equals(DEFAULT_INSTANCE_ID)) {
schedInstId = "TERRACOTTA_CLUSTERED,node=" + uuid;
if (jmxObjectName == null) {
jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId);
}
} else if(jmxObjectName == null) {
jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId + ",node=" + uuid);
}
} catch(Exception e) {
throw new RuntimeException("Problem obtaining node id from TerracottaJobStore.", e);
}
if(null == cfg.getStringProperty(PROP_SCHED_JMX_EXPORT)) {
jmxExport = true;
}
}
if (js instanceof JobStoreSupport) {
JobStoreSupport jjs = (JobStoreSupport)js;
jjs.setDbRetryInterval(dbFailureRetry);
if(threadsInheritInitalizersClassLoader)
jjs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
jjs.setThreadExecutor(threadExecutor);
}
QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
rsrcs.setName(schedName);
rsrcs.setThreadName(threadName);
rsrcs.setInstanceId(schedInstId);
rsrcs.setJobRunShellFactory(jrsf);
rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
rsrcs.setRunUpdateCheck(!skipUpdateCheck);
rsrcs.setBatchTimeWindow(batchTimeWindow);
rsrcs.setMaxBatchSize(maxBatchSize);
rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown);
rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait);
rsrcs.setJMXExport(jmxExport);
rsrcs.setJMXObjectName(jmxObjectName);
if (rmiExport) {
rsrcs.setRMIRegistryHost(rmiHost);
rsrcs.setRMIRegistryPort(rmiPort);
rsrcs.setRMIServerPort(rmiServerPort);
rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry);
rsrcs.setRMIBindName(rmiBindName);
}
SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId);
rsrcs.setThreadExecutor(threadExecutor);
threadExecutor.initialize();
rsrcs.setThreadPool(tp);
if(tp instanceof SimpleThreadPool) {
((SimpleThreadPool)tp).setThreadNamePrefix(schedName + "_Worker");
if(threadsInheritInitalizersClassLoader)
((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader);
}
tp.initialize();
tpInited = true;
rsrcs.setJobStore(js);
// add plugins
for (int i = 0; i < plugins.length; i++) {
rsrcs.addSchedulerPlugin(plugins[i]);
}
qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry);
qsInited = true;
// Create Scheduler ref...
Scheduler scheduler = instantiate(rsrcs, qs);
// set job factory if specified
if(jobFactory != null) {
qs.setJobFactory(jobFactory);
}
// Initialize plugins now that we have a Scheduler instance.
for (int i = 0; i < plugins.length; i++) {
plugins[i].initialize(pluginNames[i], scheduler);
}
// add listeners
for (int i = 0; i < jobListeners.length; i++) {
qs.getListenerManager().addJobListener(jobListeners[i], EverythingMatcher.allJobs());
}
for (int i = 0; i < triggerListeners.length; i++) {
qs.getListenerManager().addTriggerListener(triggerListeners[i], EverythingMatcher.allTriggers());
}
// set scheduler context data...
for(Object key: schedCtxtProps.keySet()) {
String val = schedCtxtProps.getProperty((String) key);
scheduler.getContext().put((String)key, val);
}
// fire up job store, and runshell factory
js.setInstanceId(schedInstId);
js.setInstanceName(schedName);
js.setThreadPoolSize(tp.getPoolSize());
js.initialize(loadHelper, qs.getSchedulerSignaler());
jrsf.initialize(scheduler);
qs.initialize();
getLog().info(
"Quartz scheduler '" + scheduler.getSchedulerName()
+ "' initialized from " + propSrc);
getLog().info("Quartz scheduler version: " + qs.getVersion());
// prevents the repository from being garbage collected
qs.addNoGCObject(schedRep);
// prevents the db manager from being garbage collected
if (dbMgr != null) {
qs.addNoGCObject(dbMgr);
}
schedRep.bind(scheduler);
return scheduler;
}
catch(SchedulerException e) {
if(qsInited)
qs.shutdown(false);
else if(tpInited)
tp.shutdown(false);
throw e;
}
catch(RuntimeException re) {
if(qsInited)
qs.shutdown(false);
else if(tpInited)
tp.shutdown(false);
throw re;
}
catch(Error re) {
if(qsInited)
qs.shutdown(false);
else if(tpInited)
tp.shutdown(false);
throw re;
}
}
protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
Scheduler scheduler = new StdScheduler(qs);
return scheduler;
}
private void setBeanProps(Object obj, Properties props)
throws NoSuchMethodException, IllegalAccessException,
java.lang.reflect.InvocationTargetException,
IntrospectionException, SchedulerConfigException {
props.remove("class");
BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propDescs = bi.getPropertyDescriptors();
PropertiesParser pp = new PropertiesParser(props);
java.util.Enumeration<Object> keys = props.keys();
while (keys.hasMoreElements()) {
String name = (String) keys.nextElement();
String c = name.substring(0, 1).toUpperCase(Locale.US);
String methName = "set" + c + name.substring(1);
java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs);
try {
if (setMeth == null) {
throw new NoSuchMethodException(
"No setter for property '" + name + "'");
}
Class<?>[] params = setMeth.getParameterTypes();
if (params.length != 1) {
throw new NoSuchMethodException(
"No 1-argument setter for property '" + name + "'");
}
// does the property value reference another property's value? If so, swap to look at its value
PropertiesParser refProps = pp;
String refName = pp.getStringProperty(name);
if(refName != null && refName.startsWith("$@")) {
refName = refName.substring(2);
refProps = cfg;
}
else
refName = name;
if (params[0].equals(int.class)) {
setMeth.invoke(obj, new Object[]{Integer.valueOf(refProps.getIntProperty(refName))});
} else if (params[0].equals(long.class)) {
setMeth.invoke(obj, new Object[]{Long.valueOf(refProps.getLongProperty(refName))});
} else if (params[0].equals(float.class)) {
setMeth.invoke(obj, new Object[]{Float.valueOf(refProps.getFloatProperty(refName))});
} else if (params[0].equals(double.class)) {
setMeth.invoke(obj, new Object[]{Double.valueOf(refProps.getDoubleProperty(refName))});
} else if (params[0].equals(boolean.class)) {
setMeth.invoke(obj, new Object[]{Boolean.valueOf(refProps.getBooleanProperty(refName))});
} else if (params[0].equals(String.class)) {
setMeth.invoke(obj, new Object[]{refProps.getStringProperty(refName)});
} else {
throw new NoSuchMethodException(
"No primitive-type setter for property '" + name
+ "'");
}
} catch (NumberFormatException nfe) {
throw new SchedulerConfigException("Could not parse property '"
+ name + "' into correct data type: " + nfe.toString());
}
}
}
private java.lang.reflect.Method getSetMethod(String name,
PropertyDescriptor[] props) {
for (int i = 0; i < props.length; i++) {
java.lang.reflect.Method wMeth = props[i].getWriteMethod();
if (wMeth != null && wMeth.getName().equals(name)) {
return wMeth;
}
}
return null;
}
private Class<?> loadClass(String className) throws ClassNotFoundException, SchedulerConfigException {
try {
ClassLoader cl = findClassloader();
if(cl != null)
return cl.loadClass(className);
throw new SchedulerConfigException("Unable to find a class loader on the current thread or class.");
} catch (ClassNotFoundException e) {
if(getClass().getClassLoader() != null)
return getClass().getClassLoader().loadClass(className);
throw e;
}
}
private ClassLoader findClassloader() {
// work-around set context loader for windows-service started jvms (QUARTZ-748)
if(Thread.currentThread().getContextClassLoader() == null && getClass().getClassLoader() != null) {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
}
return Thread.currentThread().getContextClassLoader();
}
private String getSchedulerName() {
return cfg.getStringProperty(PROP_SCHED_INSTANCE_NAME,
"QuartzScheduler");
}
/**
* <p>
* Returns a handle to the Scheduler produced by this factory.
* </p>
*
* <p>
* If one of the <code>initialize</code> methods has not be previously
* called, then the default (no-arg) <code>initialize()</code> method
* will be called by this method.
* </p>
*/
public Scheduler getScheduler() throws SchedulerException {
if (cfg == null) {
initialize();
}
SchedulerRepository schedRep = SchedulerRepository.getInstance();
Scheduler sched = schedRep.lookup(getSchedulerName());
if (sched != null) {
if (sched.isShutdown()) {
schedRep.remove(getSchedulerName());
} else {
return sched;
}
}
sched = instantiate();
return sched;
}
/**
* <p>
* Returns a handle to the default Scheduler, creating it if it does not
* yet exist.
* </p>
*
* @see #initialize()
*/
public static Scheduler getDefaultScheduler() throws SchedulerException {
StdSchedulerFactory fact = new StdSchedulerFactory();
return fact.getScheduler();
}
/**
* <p>
* Returns a handle to the Scheduler with the given name, if it exists (if
* it has already been instantiated).
* </p>
*/
public Scheduler getScheduler(String schedName) throws SchedulerException {
return SchedulerRepository.getInstance().lookup(schedName);
}
/**
* <p>
* Returns a handle to all known Schedulers (made by any
* StdSchedulerFactory instance.).
* </p>
*/
public Collection<Scheduler> getAllSchedulers() throws SchedulerException {
return SchedulerRepository.getInstance().lookupAll();
}
}
QuartzSchedulerThread#run()
run方法比較長,大意就是根據Trigger找出要執行的job然後線上程池中執行。擷取run方法如下:
核心程式碼片段是:
qsRsrcs.getThreadPool().runInThread(shell)
擷取run方法如下:
JobRunShell shell = null;
try {
shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
shell.initialize(qs);
} catch (SchedulerException se) {
try {
qsRsrcs.getJobStore().triggeredJobComplete(
triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
} catch (SchedulerException se2) {
qs.notifySchedulerListenersError(
"An error occurred while placing job's triggers in error state '"
+ triggers.get(i).getKey() + "'", se2);
// db connection must have failed... keep retrying
// until it's up...
errorTriggerRetryLoop(bndle);
}
continue;
}
if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
try {
// this case should never happen, as it is indicative of the
// scheduler being shutdown or a bug in the thread pool or
// a thread pool being used concurrently - which the docs
// say not to do...
getLog().error("ThreadPool.runInThread() return false!");
qsRsrcs.getJobStore().triggeredJobComplete(
triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
} catch (SchedulerException se2) {
qs.notifySchedulerListenersError(
"An error occurred while placing job's triggers in error state '"
+ triggers.get(i).getKey() + "'", se2);
// db connection must have failed... keep retrying
// until it's up...
releaseTriggerRetryLoop(triggers.get(i));
}
}
相關文章
- 執行緒池原始碼分析執行緒原始碼
- dubbo原始碼-執行緒池分析原始碼執行緒
- quartz2.3任務管理的原始碼分析quartz原始碼
- netty原始碼分析-執行緒池Netty原始碼執行緒
- quartz的執行緒池機制quartz執行緒
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- Dubbo RPC執行緒模型 原始碼分析RPC執行緒模型原始碼
- libgo原始碼分析之多執行緒協程管理和排程Go原始碼執行緒
- Android 原始碼分析(三)安卓中的執行緒Android原始碼安卓執行緒
- 詳解Java執行緒池的ctl(執行緒池控制狀態)【原始碼分析】Java執行緒原始碼
- Python執行緒池ThreadPoolExecutor原始碼分析Python執行緒thread原始碼
- jdk1.8 執行緒池部分原始碼分析JDK執行緒原始碼
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- 多執行緒基礎(十九):Semaphore原始碼分析執行緒原始碼
- JUC(4)---java執行緒池原理及原始碼分析Java執行緒原始碼
- Java排程執行緒池ScheduledThreadPoolExecutor原始碼分析Java執行緒thread原始碼
- ConcurrentHashMap執行緒安全機制以及原始碼分析HashMap執行緒原始碼
- EventBus原始碼分析(四):執行緒模型分析(2.4版本)原始碼執行緒模型
- 執行緒池原始碼探究執行緒原始碼
- [10]elasticsearch原始碼深入分析——執行緒池的封裝Elasticsearch原始碼執行緒封裝
- 從原始碼分析ConcurrentHashMap執行緒安全和高效的特性原始碼HashMap執行緒
- 比特幣原始碼分析:多執行緒檢查指令碼比特幣原始碼執行緒指令碼
- 多執行緒--執行緒管理執行緒
- 執行緒池執行模型原始碼全解析執行緒模型原始碼
- 從原始碼的角度解析執行緒池執行原理原始碼執行緒
- 使用 Executors,ThreadPoolExecutor,建立執行緒池,原始碼分析理解thread執行緒原始碼
- Netty原始碼分析之Reactor執行緒模型詳解Netty原始碼React執行緒模型
- 執行流程原始碼分析原始碼
- JDK執行緒池原始碼研究JDK執行緒原始碼
- 執行緒池的建立和使用,執行緒池原始碼初探(篇一)執行緒原始碼
- 從原始碼角度分析建立執行緒池究竟有哪些方式原始碼執行緒
- JUC之執行緒池基礎與簡單原始碼分析執行緒原始碼
- 執行緒管理執行緒
- 執行緒池的實現程式碼分析執行緒
- 多執行緒程式設計-分析阻塞佇列的原始碼實現執行緒程式設計佇列原始碼
- netty原始碼分析之揭開reactor執行緒的面紗(一)Netty原始碼React執行緒
- netty原始碼分析之揭開reactor執行緒的面紗(二)Netty原始碼React執行緒