1 /*
2 * Copyright 2004-2005 OpenSymphony
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 *
16 */
17
18 /*
19 * Previously Copyright (c) 2001-2004 James House
20 */
21 package org.quartz.impl;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.quartz.Scheduler;
26 import org.quartz.SchedulerException;
27 import org.quartz.SchedulerFactory;
28 import org.quartz.core.JobRunShellFactory;
29 import org.quartz.core.QuartzScheduler;
30 import org.quartz.core.QuartzSchedulerResources;
31 import org.quartz.core.SchedulingContext;
32 import org.quartz.simpl.CascadingClassLoadHelper;
33 import org.quartz.simpl.RAMJobStore;
34 import org.quartz.simpl.SimpleThreadPool;
35 import org.quartz.spi.ClassLoadHelper;
36 import org.quartz.spi.JobStore;
37 import org.quartz.spi.SchedulerPlugin;
38 import org.quartz.spi.ThreadPool;
39
40 import java.util.Collection;
41 import java.util.Iterator;
42 import java.util.Map;
43
44 /**
45 * <p>
46 * A singleton implementation of <code>{@link org.quartz.SchedulerFactory}</code>.
47 * </p>
48 *
49 * <p>
50 * Here are some examples of using this class:
51 * </p>
52 * <p>
53 * To create a scheduler that does not write anything to the database (is not
54 * persistent), you can call <code>createVolatileScheduler</code>:
55 *
56 * <pre>
57 * DirectSchedulerFactory.getInstance().createVolatileScheduler(10); // 10 threads * // don't forget to start the scheduler: DirectSchedulerFactory.getInstance().getScheduler().start();
58 * </pre>
59 *
60 *
61 * <p>
62 * Several create methods are provided for convenience. All create methods
63 * eventually end up calling the create method with all the parameters:
64 * </p>
65 *
66 * <pre>
67 * public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)
68 * </pre>
69 *
70 *
71 * <p>
72 * Here is an example of using this method:
73 * </p>
74 * *
75 * * <pre>// create the thread pool SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); threadPool.initialize(); * // create the job store JobStore jobStore = new RAMJobStore(); jobStore.initialize();
76 *
77 * DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); * // don't forget to start the scheduler: DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();
78 * </pre>
79 *
80 *
81 * <p>
82 * You can also use a JDBCJobStore instead of the RAMJobStore:
83 * </p>
84 *
85 * <pre>
86 * DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));
87 *
88 * JDBCJobStore jdbcJobStore = new JDBCJobStore(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance"); jdbcJobStore.initialize();
89 * </pre>
90 *
91 * @author Mohammad Rezaei
92 * @author James House
93 *
94 * @see JobStore
95 * @see ThreadPool
96 */
97 public class DirectSchedulerFactory implements SchedulerFactory {
98
99 /*
100 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101 *
102 * Constants.
103 *
104 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105 */
106 public static final String DEFAULT_INSTANCE_ID = "SIMPLE_NON_CLUSTERED";
107
108 public static final String DEFAULT_SCHEDULER_NAME = "SimpleQuartzScheduler";
109
110 /*
111 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112 *
113 * Data members.
114 *
115 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
116 */
117
118 private boolean initialized = false;
119
120 private static DirectSchedulerFactory instance = new DirectSchedulerFactory();
121
122 private final Log log = LogFactory.getLog(getClass());
123
124 /*
125 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126 *
127 * Constructors.
128 *
129 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130 */
131
132 protected Log getLog() {
133 return log;
134 }
135
136 /**
137 * Constructor
138 */
139 protected DirectSchedulerFactory() {
140 }
141
142 /*
143 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144 *
145 * Interface.
146 *
147 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148 */
149
150 public static DirectSchedulerFactory getInstance() {
151 return instance;
152 }
153
154 /**
155 * Creates an in memory job store (<code>{@link RAMJobStore}</code>)
156 * The thread priority is set to Thread.NORM_PRIORITY
157 *
158 * @param maxThreads
159 * The number of threads in the thread pool
160 * @throws SchedulerException
161 * if initialization failed.
162 */
163 public void createVolatileScheduler(int maxThreads)
164 throws SchedulerException {
165 SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads,
166 Thread.NORM_PRIORITY);
167 threadPool.initialize();
168 JobStore jobStore = new RAMJobStore();
169 this.createScheduler(threadPool, jobStore);
170
171 }
172
173 /**
174 * @deprecated see correctly spelled method.
175 * @see #createVolatileScheduler(int)
176 */
177 public void createVolatileSchduler(int maxThreads)
178 throws SchedulerException {
179 createVolatileScheduler(maxThreads);
180 }
181
182 /**
183 * Creates a proxy to a remote scheduler. This scheduler can be retrieved
184 * via {@link DirectSchedulerFactory#getScheduler()}
185 *
186 * @param rmiHost
187 * The hostname for remote scheduler
188 * @param rmiPort
189 * Port for the remote scheduler. The default RMI port is 1099.
190 * @throws SchedulerException
191 * if the remote scheduler could not be reached.
192 */
193 public void createRemoteScheduler(String rmiHost, int rmiPort)
194 throws SchedulerException {
195 createRemoteScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID,
196 rmiHost, rmiPort);
197 initialized = true;
198 }
199
200 /**
201 * Same as
202 * {@link DirectSchedulerFactory#createRemoteScheduler(String rmiHost, int rmiPort)},
203 * with the addition of specifying the scheduler name and instance ID. This
204 * scheduler can only be retrieved via
205 * {@link DirectSchedulerFactory#getScheduler(String)}
206 *
207 * @param schedulerName
208 * The name for the scheduler.
209 * @param schedulerInstanceId
210 * The instance ID for the scheduler.
211 * @param rmiHost
212 * The hostname for remote scheduler
213 * @param rmiPort
214 * Port for the remote scheduler. The default RMI port is 1099.
215 * @throws SchedulerException
216 * if the remote scheduler could not be reached.
217 */
218 public void createRemoteScheduler(String schedulerName,
219 String schedulerInstanceId, String rmiHost, int rmiPort)
220 throws SchedulerException {
221 createRemoteScheduler(schedulerName,
222 schedulerInstanceId, null, rmiHost, rmiPort);
223 }
224
225 /**
226 * Same as
227 * {@link DirectSchedulerFactory#createRemoteScheduler(String rmiHost, int rmiPort)},
228 * with the addition of specifying the scheduler name, instance ID, and rmi
229 * bind name. This scheduler can only be retrieved via
230 * {@link DirectSchedulerFactory#getScheduler(String)}
231 *
232 * @param schedulerName
233 * The name for the scheduler.
234 * @param schedulerInstanceId
235 * The instance ID for the scheduler.
236 * @param rmiBindName
237 * The name of the remote scheduler in the RMI repository. If null
238 * defaults to the generated unique identifier.
239 * @param rmiHost
240 * The hostname for remote scheduler
241 * @param rmiPort
242 * Port for the remote scheduler. The default RMI port is 1099.
243 * @throws SchedulerException
244 * if the remote scheduler could not be reached.
245 */
246 public void createRemoteScheduler(String schedulerName,
247 String schedulerInstanceId, String rmiBindName, String rmiHost, int rmiPort)
248 throws SchedulerException {
249 SchedulingContext schedCtxt = new SchedulingContext();
250 schedCtxt.setInstanceId(schedulerInstanceId);
251
252 String uid = (rmiBindName != null) ? rmiBindName :
253 QuartzSchedulerResources.getUniqueIdentifier(
254 schedulerName, schedulerInstanceId);
255
256 RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt, uid,
257 rmiHost, rmiPort);
258
259 SchedulerRepository schedRep = SchedulerRepository.getInstance();
260 schedRep.bind(remoteScheduler);
261 }
262
263 /**
264 * Creates a scheduler using the specified thread pool and job store. This
265 * scheduler can be retrieved via
266 * {@link DirectSchedulerFactory#getScheduler()}
267 *
268 * @param threadPool
269 * The thread pool for executing jobs
270 * @param jobStore
271 * The type of job store
272 * @throws SchedulerException
273 * if initialization failed
274 */
275 public void createScheduler(ThreadPool threadPool, JobStore jobStore)
276 throws SchedulerException {
277 createScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID,
278 threadPool, jobStore);
279 initialized = true;
280 }
281
282 /**
283 * Same as
284 * {@link DirectSchedulerFactory#createScheduler(ThreadPool threadPool, JobStore jobStore)},
285 * with the addition of specifying the scheduler name and instance ID. This
286 * scheduler can only be retrieved via
287 * {@link DirectSchedulerFactory#getScheduler(String)}
288 *
289 * @param schedulerName
290 * The name for the scheduler.
291 * @param schedulerInstanceId
292 * The instance ID for the scheduler.
293 * @param threadPool
294 * The thread pool for executing jobs
295 * @param jobStore
296 * The type of job store
297 * @throws SchedulerException
298 * if initialization failed
299 */
300 public void createScheduler(String schedulerName,
301 String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore)
302 throws SchedulerException {
303 createScheduler(schedulerName, schedulerInstanceId, threadPool,
304 jobStore, null, 0, -1, -1);
305 }
306
307 /**
308 * Creates a scheduler using the specified thread pool and job store and
309 * binds it to RMI.
310 *
311 * @param schedulerName
312 * The name for the scheduler.
313 * @param schedulerInstanceId
314 * The instance ID for the scheduler.
315 * @param threadPool
316 * The thread pool for executing jobs
317 * @param jobStore
318 * The type of job store
319 * @param rmiRegistryHost
320 * The hostname to register this scheduler with for RMI. Can use
321 * "null" if no RMI is required.
322 * @param rmiRegistryPort
323 * The port for RMI. Typically 1099.
324 * @param idleWaitTime
325 * The idle wait time in milliseconds. You can specify "-1" for
326 * the default value, which is currently 30000 ms.
327 * @throws SchedulerException
328 * if initialization failed
329 */
330 public void createScheduler(String schedulerName,
331 String schedulerInstanceId, ThreadPool threadPool,
332 JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort,
333 long idleWaitTime, long dbFailureRetryInterval)
334 throws SchedulerException {
335 createScheduler(schedulerName,
336 schedulerInstanceId, threadPool,
337 jobStore, null, // plugins
338 rmiRegistryHost, rmiRegistryPort,
339 idleWaitTime, dbFailureRetryInterval);
340 }
341
342 /**
343 * Creates a scheduler using the specified thread pool, job store, and
344 * plugins, and binds it to RMI.
345 *
346 * @param schedulerName
347 * The name for the scheduler.
348 * @param schedulerInstanceId
349 * The instance ID for the scheduler.
350 * @param threadPool
351 * The thread pool for executing jobs
352 * @param jobStore
353 * The type of job store
354 * @param schedulerPluginMap
355 * Map from a <code>String</code> plugin names to
356 * <code>{@link org.quartz.spi.SchedulerPlugin}</code>s. Can use
357 * "null" if no plugins are required.
358 * @param rmiRegistryHost
359 * The hostname to register this scheduler with for RMI. Can use
360 * "null" if no RMI is required.
361 * @param rmiRegistryPort
362 * The port for RMI. Typically 1099.
363 * @param idleWaitTime
364 * The idle wait time in milliseconds. You can specify "-1" for
365 * the default value, which is currently 30000 ms.
366 * @throws SchedulerException
367 * if initialization failed
368 */
369 public void createScheduler(String schedulerName,
370 String schedulerInstanceId, ThreadPool threadPool,
371 JobStore jobStore, Map schedulerPluginMap,
372 String rmiRegistryHost, int rmiRegistryPort,
373 long idleWaitTime, long dbFailureRetryInterval)
374 throws SchedulerException {
375 // Currently only one run-shell factory is available...
376 JobRunShellFactory jrsf = new StdJobRunShellFactory();
377
378 // Fire everything up
379 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
380 SchedulingContext schedCtxt = new SchedulingContext();
381 schedCtxt.setInstanceId(schedulerInstanceId);
382
383 QuartzSchedulerResources qrs = new QuartzSchedulerResources();
384
385 qrs.setName(schedulerName);
386 qrs.setInstanceId(schedulerInstanceId);
387 qrs.setJobRunShellFactory(jrsf);
388 qrs.setThreadPool(threadPool);
389 qrs.setJobStore(jobStore);
390 qrs.setRMIRegistryHost(rmiRegistryHost);
391 qrs.setRMIRegistryPort(rmiRegistryPort);
392
393 // add plugins
394 if (schedulerPluginMap != null) {
395 for (Iterator pluginIter = schedulerPluginMap.values().iterator(); pluginIter.hasNext();) {
396 qrs.addSchedulerPlugin((SchedulerPlugin)pluginIter.next());
397 }
398 }
399
400 QuartzScheduler qs = new QuartzScheduler(qrs, schedCtxt, idleWaitTime,
401 dbFailureRetryInterval);
402
403 ClassLoadHelper cch = new CascadingClassLoadHelper();
404 cch.initialize();
405
406 jobStore.initialize(cch, qs.getSchedulerSignaler());
407
408 Scheduler scheduler = new StdScheduler(qs, schedCtxt);
409
410 // Initialize plugins now that we have a Scheduler instance.
411 if (schedulerPluginMap != null) {
412 for (Iterator pluginEntryIter = schedulerPluginMap.entrySet().iterator(); pluginEntryIter.hasNext();) {
413 Map.Entry pluginEntry = (Map.Entry)pluginEntryIter.next();
414
415 ((SchedulerPlugin)pluginEntry.getValue()).initialize(
416 (String)pluginEntry.getKey(), scheduler);
417 }
418 }
419
420 jrsf.initialize(scheduler, schedCtxt);
421
422 getLog().info("Quartz scheduler '" + scheduler.getSchedulerName());
423
424 getLog().info("Quartz scheduler version: " + qs.getVersion());
425
426 SchedulerRepository schedRep = SchedulerRepository.getInstance();
427
428 qs.addNoGCObject(schedRep); // prevents the repository from being
429 // garbage collected
430
431 schedRep.bind(scheduler);
432 }
433
434 /*
435 * public void registerSchedulerForRmi(String schedulerName, String
436 * schedulerId, String registryHost, int registryPort) throws
437 * SchedulerException, RemoteException { QuartzScheduler scheduler =
438 * (QuartzScheduler) this.getScheduler(); scheduler.bind(registryHost,
439 * registryPort); }
440 */
441
442 /**
443 * <p>
444 * Returns a handle to the Scheduler produced by this factory.
445 * </p>
446 *
447 * <p>
448 * you must call createRemoteScheduler or createScheduler methods before
449 * calling getScheduler()
450 * </p>
451 */
452 public Scheduler getScheduler() throws SchedulerException {
453 if (!initialized) {
454 throw new SchedulerException(
455 "you must call createRemoteScheduler or createScheduler methods before calling getScheduler()");
456 }
457
458 return getScheduler(DEFAULT_SCHEDULER_NAME);
459 }
460
461 /**
462 * <p>
463 * Returns a handle to the Scheduler with the given name, if it exists.
464 * </p>
465 */
466 public Scheduler getScheduler(String schedName) throws SchedulerException {
467 SchedulerRepository schedRep = SchedulerRepository.getInstance();
468
469 return schedRep.lookup(schedName);
470 }
471
472 /**
473 * <p>
474 * Returns a handle to all known Schedulers (made by any
475 * StdSchedulerFactory instance.).
476 * </p>
477 */
478 public Collection getAllSchedulers() throws SchedulerException {
479 return SchedulerRepository.getInstance().lookupAll();
480 }
481
482 }