Project

General

Profile

1 4951 daigle
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that handles scheduling tasks
4
 *  Copyright: 2009 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Michael Daigle
7
 *
8
 *   '$Author: daigle $'
9
 *     '$Date: 2009-03-25 13:41:15 -0800 (Wed, 25 Mar 2009) $'
10
 * '$Revision: 4861 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26
27
package edu.ucsb.nceas.metacat.scheduler;
28
29 5027 daigle
import java.io.PrintWriter;
30 4951 daigle
import java.util.Calendar;
31 4959 daigle
import java.util.HashMap;
32 4951 daigle
import java.util.Vector;
33
34
import org.apache.log4j.Logger;
35
36
import org.quartz.Job;
37 4959 daigle
import org.quartz.JobDataMap;
38 4951 daigle
import org.quartz.JobDetail;
39
import org.quartz.Scheduler;
40
import org.quartz.SchedulerException;
41
import org.quartz.SchedulerFactory;
42 5016 daigle
import org.quartz.SimpleTrigger;
43 4951 daigle
import org.quartz.Trigger;
44
import org.quartz.TriggerUtils;
45
46 5015 daigle
import edu.ucsb.nceas.metacat.shared.AccessException;
47
import edu.ucsb.nceas.metacat.shared.BaseService;
48
import edu.ucsb.nceas.metacat.shared.ServiceException;
49 5005 daigle
import edu.ucsb.nceas.utilities.DateUtil;
50 4959 daigle
import edu.ucsb.nceas.utilities.StatusUtil;
51 5005 daigle
import edu.ucsb.nceas.utilities.UtilException;
52 4951 daigle
53
public class SchedulerService extends BaseService {
54
55
	private static SchedulerService schedulerService = null;
56
57
	private static Logger logMetacat = Logger.getLogger(SchedulerService.class);
58
59
	private static Scheduler sched = null;
60
61
	/**
62
	 * private constructor since this is a singleton
63
	 */
64 4959 daigle
	private SchedulerService() throws ServiceException {
65
		start();
66
	}
67 4951 daigle
68
	/**
69
	 * Get the single instance of SchedulerService.
70
	 *
71
	 * @return the single instance of SchedulerService
72
	 */
73
	public static SchedulerService getInstance() throws ServiceException {
74
		if (schedulerService == null) {
75
			schedulerService = new SchedulerService();
76
		}
77
		return schedulerService;
78
	}
79
80 4971 daigle
	// this is a refreshable class
81 4951 daigle
	public boolean refreshable() {
82
		return true;
83
	}
84 4971 daigle
85
	// do the refresh
86 4981 daigle
	public void doRefresh() throws ServiceException {
87 4951 daigle
		stop();
88 4971 daigle
		start();
89 4951 daigle
	}
90
91 4971 daigle
	// initialize the service
92 4981 daigle
	public void start() throws ServiceException {
93 4951 daigle
		try {
94 4971 daigle
			// get the Quartz scheduler factory
95 4951 daigle
			SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
96
97 4971 daigle
			// get the scheduler
98 4951 daigle
			sched = schedFact.getScheduler();
99
			sched.start();
100 4967 daigle
101 4971 daigle
			// get all existing jobs from the database
102 4967 daigle
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
103
			HashMap<Long, ScheduledJobDAO> allJobsMap = jobAccess.getAllJobs(null);
104
105 4971 daigle
			// reschedule each job that is in a SCHEDULED state.
106 4967 daigle
			for (Long jobId : allJobsMap.keySet()) {
107
				ScheduledJobDAO jobDAO = allJobsMap.get(jobId);
108
				String[] groups = {"scheduler_group"};
109
				if (jobDAO.getStatus().equals(StatusUtil.SCHEDULED)) {
110 4971 daigle
					// send false as the last param so the reschedule method will not
111
					// complain that the job is already in a SCHEDULED state.
112 4967 daigle
					rescheduleJob(jobDAO, "scheduler_user", groups, false);
113
				}
114
			}
115
116
		} catch (AccessException ae) {
117 4971 daigle
			throw new ServiceException("SchedulerService.start - DB Access issue when starting scheduler: ", ae);
118 4951 daigle
		} catch (SchedulerException se) {
119 4971 daigle
			throw new ServiceException("SchedulerService.start - Scheduler engine issue when starting scheduler: " + se.getMessage());
120 4951 daigle
		}
121
	}
122
123 4971 daigle
	// Stop the scheduler
124 4981 daigle
	public void stop() throws ServiceException {
125 4951 daigle
		try {
126 5044 daigle
			sched.shutdown();
127 4951 daigle
			sched = null;
128
		} catch (SchedulerException se) {
129 4971 daigle
			throw new ServiceException("SchedulerService.stop - Could not shut down scheduler: " + se.getMessage());
130 4951 daigle
		}
131
	}
132
133 4971 daigle
	// this will eventually return the scheduler status
134 4951 daigle
	protected Vector<String> getStatus() throws ServiceException {
135
		return new Vector<String>();
136
	}
137
138 4971 daigle
	/**
139
	 * Schedule a job
140
	 *
141
	 * @param jobDAO
142
	 *            the job data object to schedule
143
	 * @param username
144
	 *            the user that we will use to schedule
145
	 * @param groups
146
	 *            the user group that we will use to schedule
147
	 * @return a message saying that the job was scheduled
148
	 */
149 4967 daigle
	public String scheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups) throws ServiceException {
150 4951 daigle
151 4971 daigle
		// convert the start time to a calendar object
152 4967 daigle
		Calendar startTimeCal = Calendar.getInstance();
153
        startTimeCal.setTime(jobDAO.getStartTime());
154
155 5012 daigle
		// convert the start time to a calendar object
156
		Calendar endTimeCal = Calendar.getInstance();
157
        endTimeCal.setTime(jobDAO.getEndTime());
158
159 4971 daigle
        // extract the job parameters from their data objects and put into a string map
160 4967 daigle
        HashMap<String, String> jobParams = new HashMap<String, String>();
161
        HashMap<String, ScheduledJobParamDAO> jobParamDAOs = jobDAO.getAllJobParams();
162
        for (String paramName : jobParamDAOs.keySet()) {
163
        	jobParams.put(paramName, jobParamDAOs.get(paramName).getValue());
164
        }
165
166 4971 daigle
        // schedule the job
167 5012 daigle
		return scheduleJob(jobDAO.getName(), startTimeCal, endTimeCal, jobDAO.getIntervalValue(),
168
				jobDAO.getIntervalUnit(), jobDAO.getClassName(), jobDAO.getGroupName(),
169 5057 daigle
				jobParams);
170 4967 daigle
	}
171
172 4971 daigle
	/**
173
	 * schedule a job
174
	 *
175
	 * @param jobName
176
	 *            the name of the job
177
	 * @param startCal
178
	 *            a calendar holding the start date of the job
179 5016 daigle
	 * @param endCal
180
	 *            a calendar holding the end date of the job
181 4971 daigle
	 * @param intervalValue
182
	 *            the run interval for the job
183
	 * @param intervalUnit
184
	 *            the unit of the run interval for the job
185
	 * @param jobClassName
186
	 *            the job class name
187
	 * @param jobGroup
188
	 *            the job group name
189
	 * @param jobParams
190
	 *            a map of additional job parameters
191
	 * @param username
192
	 *            the user name
193
	 * @param groups
194
	 *            the user's group name
195
	 * @return a message saying that the job was scheduled
196
	 */
197 5116 daigle
	@SuppressWarnings("unchecked")
198 5012 daigle
	public String scheduleJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue,
199 5057 daigle
			String intervalUnit, String jobClassName, String jobGroup, HashMap<String, String> jobParams)
200
	throws ServiceException {
201 4967 daigle
202 4951 daigle
        Class<Job> jobClass = null;
203 5005 daigle
        try {
204
			jobClass = (Class<Job>) Class.forName(jobClassName);
205
206 5013 daigle
			String startTimeStr = DateUtil.getHumanReadable(startCal);
207 5005 daigle
			logMetacat.info("SchedulerService.scheduleJob - Scheduling job -- name: "
208
					+ jobName + ", class: " + jobClassName + ", start time: "
209
					+ startTimeStr + ", interval value: " + intervalValue
210
					+ ", interval unit: " + intervalUnit);
211
212
			// start the job in the job scheduler
213 5012 daigle
			startJob(jobName, startCal, endCal, intervalValue, intervalUnit, jobClass, jobGroup,
214 5005 daigle
					jobParams);
215
216
			// get a database access object and create the job in the database
217
218 4951 daigle
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
219 5012 daigle
			jobAccess.createJob(jobName, jobName, jobGroup, jobClass, startCal, endCal,
220 5005 daigle
					intervalValue, intervalUnit, jobParams);
221 4951 daigle
		} catch (AccessException ae) {
222
			try {
223 5057 daigle
				deleteJob(jobName);
224 4959 daigle
			} catch (Exception e) {
225 4951 daigle
				// Not much we can do here but log this
226 5005 daigle
				logMetacat.error("SchedulerService.scheduleJob - An access exception was thrown when writing job: "
227
						+ jobName + "to the db, and another exception was thrown when trying to remove the "
228 4959 daigle
						+ "job from the scheduler.  The db and scheduler may be out of sync: " + e.getMessage());
229 4951 daigle
			}
230 4959 daigle
			throw new ServiceException("SchedulerService.scheduleJob - Error accessing db: ", ae);
231 5005 daigle
		} catch (ClassNotFoundException cnfe) {
232
			throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
233
							+ jobClassName + " : " + cnfe.getMessage());
234
		} catch (UtilException ue) {
235
			throw new ServiceException("SchedulerService.scheduleJob - Could not schedule "
236
							+ "job due to a utility issue: " + ue.getMessage());
237 4951 daigle
		}
238
239 4967 daigle
		return "Scheduled: " + jobName;
240 4951 daigle
	}
241
242 4971 daigle
	/**
243 5016 daigle
	 * schedule a job one time with a given delay. The job is registered in the
244
	 * scheduler, but not persisted to the database. The delay is calculated
245
	 * from the time that this method is called.
246
	 *
247
	 * @param jobName
248
	 *            the name of the job
249
	 * @param delay
250
	 *            the delay in seconds, minutes, hours or days from the time
251
	 *            this method is called (i.e. 30s, 5m, 2h, 1d)
252
	 * @param jobClassName
253
	 *            the job class name
254
	 * @param jobGroup
255
	 *            the job group name
256
	 * @param jobParams
257
	 *            a map of additional job parameters
258
	 * @param username
259
	 *            the user name
260
	 * @param groups
261
	 *            the user's group name
262
	 * @return a message saying that the job was scheduled
263
	 */
264 5116 daigle
	@SuppressWarnings("unchecked")
265 5016 daigle
	public String scheduleDelayedJob(String jobName, String delay, String jobClassName,
266
			String jobGroup, HashMap<String, String> jobParams,
267
			String username, String[] groups) throws ServiceException {
268
269
        Class<Job> jobClass = null;
270
        try {
271
			jobClass = (Class<Job>) Class.forName(jobClassName);
272
273
			Calendar startCal = getStartDateFromDelay(delay);
274
275
			logMetacat.info("SchedulerService.scheduleDelayedJob - Scheduling job -- name: "
276
					+ jobName + ", delay: " + delay + ", job class name: " + jobClassName);
277
278
			// start the job in the job scheduler
279
			startOneTimeJob(jobName, startCal, jobClass, jobGroup,
280
					jobParams);
281
282
		} catch (ClassNotFoundException cnfe) {
283
			throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
284
							+ jobClassName + " : " + cnfe.getMessage());
285
		}
286
287
		return "Scheduled: " + jobName;
288
	}
289
290
	/**
291 4971 daigle
	 * Unschedule a job. This removed it from the scheduler in memory and
292
	 * changed it's status to unscheduled in the database.
293
	 *
294
	 * @param jobName
295
	 *            the name of the job to unschedule
296
	 * @param username
297
	 *            the user name
298
	 * @param groups
299
	 *            the user's group name
300
	 * @return a message saying the job was unscheduled
301
	 */
302 5044 daigle
	public String unscheduleJob(String jobName, String username,
303 4967 daigle
			String[] groups) throws ServiceException {
304 5013 daigle
305
		ScheduledJobDAO jobDAO = null;
306 4951 daigle
		try {
307
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
308 5013 daigle
			jobDAO = jobAccess.getJobByName(jobName);
309 4967 daigle
			if (jobDAO == null) {
310
				throw new ServiceException("SchedulerService.unscheduleJob - Could "
311
						+ "not find job with name: " + jobName);
312
			}
313 4951 daigle
314 4971 daigle
			// remove the job from the scheduler
315 4967 daigle
			sched.deleteJob(jobDAO.getName(), jobDAO.getGroupName());
316 4951 daigle
317 4971 daigle
			// change the status of the job to unscheduled in the database.
318 4967 daigle
			jobDAO.setStatus(StatusUtil.UNSCHEDULED);
319 4951 daigle
			jobAccess.updateJobStatus(jobDAO);
320
		} catch (SchedulerException se) {
321
			throw new ServiceException("SchedulerService.unscheduleJob - Could not create "
322
							+ "scheduled job because of service issue: " + se.getMessage());
323
		} catch (AccessException ae) {
324
			throw new ServiceException("SchedulerService.unscheduleJob - Could not create "
325 5013 daigle
							+ "scheduled job : " + jobDAO.getName() + " because of db access issue: ", ae);
326 4951 daigle
		}
327 4967 daigle
328
		return "Unscheduled: " + jobName;
329 4951 daigle
	}
330
331 4971 daigle
	/**
332
	 * Reschedule a job. This call will always check to make sure the status is not SCHEDULED
333
	 * @param jobDAO the job data object holding the information about the job to reschedule
334
	 * @param username
335
	 *            the user name
336
	 * @param groups
337
	 *            the user's group name
338
	 * @return a message saying that the job was rescheduled
339
	 */
340 4967 daigle
	public String rescheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups) throws ServiceException {
341
		return rescheduleJob(jobDAO, username, groups, true);
342
	}
343
344 4971 daigle
	/**
345
	 * Reschedule a job.
346
	 *
347
	 * @param jobDAO
348
	 *            the job data object holding the information about the job to
349
	 *            reschedule
350
	 * @param username
351
	 *            the user name
352
	 * @param groups
353
	 *            the user's group name
354
	 * @param checkStatus
355
	 *            if set to true, the method will check to make sure the status
356
	 *            is UNSCHEDULED before restarting. Otherwise, the method will
357
	 *            not check. This is so that we can restart a service at startup
358
	 *            that was running when metacat was shut down.
359
	 * @return a message saying that the job was rescheduled
360
	 */
361 5116 daigle
	@SuppressWarnings("unchecked")
362 4967 daigle
	public String rescheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups, boolean checkStatus) throws ServiceException {
363
364
		try {
365
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
366
367
			if (jobDAO == null) {
368
				throw new ServiceException("SchedulerService.reScheduleJob - Cannot reschedule nonexistant job.");
369
			}
370
371 4971 daigle
			// if we are checking status, make sure the job is in an UNSCHEDULED state in the db
372 4967 daigle
			if (checkStatus && !jobDAO.getStatus().equals(StatusUtil.UNSCHEDULED)) {
373
				throw new ServiceException("SchedulerService.reScheduleJob - Cannot reschedule a job with status: "
374
						+ jobDAO.getStatus() + ". Status must be 'unscheduled'.");
375
			}
376
377
			Calendar startCal = Calendar.getInstance();
378
	        startCal.setTime(jobDAO.getStartTime());
379
380 5012 daigle
			Calendar endCal = Calendar.getInstance();
381
	        endCal.setTime(jobDAO.getEndTime());
382
383 4967 daigle
	        HashMap<String, String> jobParams = new HashMap<String, String>();
384
	        HashMap<String, ScheduledJobParamDAO> jobParamDAOs = jobDAO.getAllJobParams();
385
	        for (String paramName : jobParamDAOs.keySet()) {
386
	        	jobParams.put(paramName, jobParamDAOs.get(paramName).getValue());
387
	        }
388
389
	        Class<Job> jobClass = null;
390
	        String jobClassName = jobDAO.getClassName();
391
	        try {
392
	        	jobClass = (Class<Job>)Class.forName(jobClassName);
393
	        } catch (ClassNotFoundException cnfe) {
394
	        	throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
395
	        			+ jobDAO.getClassName() + " : " + cnfe.getMessage());
396
	        }
397
398 5013 daigle
	        String startTimeStr = DateUtil.getHumanReadable(startCal);
399 4967 daigle
	        logMetacat.info("SchedulerService.rescheduleJob - name: " + jobDAO.getName() + ", class: " + jobClassName
400 5005 daigle
	        		+ ", start time: " + startTimeStr + ", interval value: " + jobDAO.getIntervalValue()
401 4967 daigle
	        		+ ", interval unit: " + jobDAO.getIntervalUnit());
402
403 4971 daigle
	        // start the job in the scheduler
404 5012 daigle
			startJob(jobDAO.getName(), startCal, endCal, jobDAO.getIntervalValue(), jobDAO.getIntervalUnit(), jobClass, jobDAO.getGroupName(), jobParams);
405 4967 daigle
406 4971 daigle
			// update the status in the database
407 4967 daigle
			jobDAO.setStatus(StatusUtil.SCHEDULED);
408
			jobAccess.updateJobStatus(jobDAO);
409
410
		} catch (AccessException ae) {
411
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
412 5013 daigle
					+ "job : " + jobDAO.getName() + " because of db access issue: ", ae);
413 5005 daigle
		} catch (UtilException ue) {
414
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
415 5013 daigle
					+ "job : " + jobDAO.getName() + " due to a utility issue: " + ue.getMessage());
416 4967 daigle
		}
417
418 5044 daigle
		return "Rescheduled: " + jobDAO.getName();
419 4967 daigle
	}
420 5013 daigle
421
	/**
422
	 * Remove the job from the scheduler and set the job status to deleted in the database
423
	 * @param jobName
424
	 *            the string holding the name of the job to delete
425
	 * @param username
426
	 *            the user name
427
	 * @param groups
428
	 *            the user's group name
429
	 * @return a message saying that the job was deleted
430
	 */
431 5057 daigle
	public String deleteJob(String jobName) throws ServiceException {
432 5013 daigle
433
		ScheduledJobDAO jobDAO = null;
434
		try {
435
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
436
			jobDAO = jobAccess.getJobByName(jobName);
437
		} catch (AccessException ae) {
438
			throw new ServiceException("SchedulerService.deleteJob - Could not delete "
439
					+ "scheduled job : " + jobDAO.getName() + " because of db access issue: ", ae);
440
		}
441
442 5057 daigle
		return deleteJob(jobDAO);
443 5013 daigle
	}
444 4967 daigle
445 4971 daigle
	/**
446
	 * Remove the job from the scheduler and set the job status to deleted in the database
447
	 * @param jobDAO
448 5013 daigle
	 *            the job data object holding the information about the job to delete
449 4971 daigle
	 * @param username
450
	 *            the user name
451
	 * @param groups
452
	 *            the user's group name
453
	 * @return a message saying that the job was deleted
454
	 */
455 5057 daigle
	public String deleteJob(ScheduledJobDAO jobDAO) throws ServiceException {
456 4967 daigle
457 4951 daigle
		String groupName = "";
458
		try {
459 4959 daigle
460 5013 daigle
			sched.deleteJob(jobDAO.getName(), groupName);
461 4967 daigle
462
			jobDAO.setStatus(StatusUtil.DELETED);
463 5013 daigle
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
464 4967 daigle
			jobAccess.updateJobStatus(jobDAO);
465 4951 daigle
		} catch (SchedulerException se) {
466 5013 daigle
			throw new ServiceException("SchedulerService.deleteJob - Could not delete job: " + jobDAO.getName()
467 4959 daigle
							+ " for group: " + groupName + " : " + se.getMessage());
468 4951 daigle
		} catch (AccessException ae) {
469
			throw new ServiceException("SchedulerService.deleteJob - Could not delete "
470 5013 daigle
					+ "scheduled job: " + jobDAO.getName() + " because of db access issue: ", ae);
471 4959 daigle
		}
472 4951 daigle
473 5013 daigle
		return "Deleted: " + jobDAO.getName();
474 4951 daigle
	}
475
476 4971 daigle
	/**
477
	 * Get information about the job in XML format
478
	 *
479
	 * @param jobId
480
	 *            the job for which we want the information
481
	 * @return an XML representation of the job
482
	 */
483 4959 daigle
	public String getJobInfoXML(Long jobId) throws ServiceException {
484
		String jobInfoXML = "";
485
486
		try {
487
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
488
			ScheduledJobDAO scheduledJobDAO = jobAccess.getJob(jobId);
489
490
			jobInfoXML +=
491
				"<scheduledJobs>" + jobToXML(scheduledJobDAO) + "</scheduledJobs>";
492
493
		} catch (AccessException ae) {
494
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get job info for job: "
495
					+ jobId, ae);
496
		}
497
498
		return jobInfoXML;
499
	}
500
501 4971 daigle
	/**
502
	 * Get the information for jobs in a group in an xml format. A parameter
503
	 * key/value pair can be provided as well to limit the jobs returned.
504
	 *
505
	 * @param groupName
506
	 *            the job group that we are searching for
507
	 * @param paramName
508
	 *            the parameter name that we are looking for. this is ignored if
509
	 *            null
510
	 * @param paramValue
511
	 *            the parameter value that we are looking for. this is ignored
512
	 *            if null
513
	 * @return an XML representation of the jobs.
514
	 */
515 4967 daigle
	public String getJobsInfoXML(String groupName, String paramName, String paramValue) throws ServiceException {
516 4959 daigle
		String jobInfoXML = "";
517
518
		try {
519
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
520 4967 daigle
			HashMap<Long, ScheduledJobDAO> JobDAOMap = jobAccess.getJobsWithParameter(groupName, paramName, paramValue);
521 4959 daigle
522 4967 daigle
			jobInfoXML += "<scheduledWorkflowResultset>";
523 4959 daigle
			for (Long jobDAOId : JobDAOMap.keySet()) {
524 4967 daigle
				ScheduledJobDAO jobDAO = JobDAOMap.get(jobDAOId);
525
				if (paramValue != null && paramName != null) {
526
					ScheduledJobParamDAO jobParamDAO = jobDAO.getJobParam(paramName);
527
					if(jobParamDAO != null && jobParamDAO.getValue().equals(paramValue)) {
528
						jobInfoXML +=  jobToXML(JobDAOMap.get(jobDAOId));
529
					}
530
				}
531 4959 daigle
			}
532 4967 daigle
			jobInfoXML += "</scheduledWorkflowResultset>";
533 4959 daigle
534
		} catch (AccessException ae) {
535
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get jobs info for group: "
536
					+ groupName, ae);
537
		}
538
539
		return jobInfoXML;
540
	}
541
542 4971 daigle
	/**
543 5027 daigle
	 * Get the information for jobs in a group in an xml format. A parameter
544
	 * key/value pair can be provided as well to limit the jobs returned.
545
	 *
546
	 * @param groupName
547
	 *            the job group that we are searching for
548
	 * @param paramName
549
	 *            the parameter name that we are looking for. this is ignored if
550
	 *            null
551
	 * @param paramValue
552
	 *            the parameter value that we are looking for. this is ignored
553
	 *            if null
554
	 * @return an XML representation of the jobs.
555
	 */
556
	public void getJobsInfoXML(String groupName, String paramName, String paramValue, PrintWriter pw) throws ServiceException {
557
558
		try {
559
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
560
			HashMap<Long, ScheduledJobDAO> JobDAOMap = jobAccess.getJobsWithParameter(groupName, paramName, paramValue);
561
562
			pw.print("<scheduledWorkflowResultset>");
563
			for (Long jobDAOId : JobDAOMap.keySet()) {
564
				ScheduledJobDAO jobDAO = JobDAOMap.get(jobDAOId);
565
				if (paramValue != null && paramName != null) {
566
					ScheduledJobParamDAO jobParamDAO = jobDAO.getJobParam(paramName);
567
					if(jobParamDAO != null && jobParamDAO.getValue().equals(paramValue)) {
568
						pw.print(jobToXML(JobDAOMap.get(jobDAOId)));
569
					}
570
				}
571
			}
572
			pw.print("</scheduledWorkflowResultset>");
573
574
		} catch (AccessException ae) {
575
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get jobs info for group: "
576
					+ groupName, ae);
577
		}
578
	}
579
580
	/**
581 4971 daigle
	 * Convert a single job to XML
582
	 * @param scheduledJobDAO the job we want to convert
583
	 * @return an XML representation of the job
584
	 */
585 4967 daigle
	public String jobToXML(ScheduledJobDAO scheduledJobDAO) throws ServiceException {
586 4959 daigle
		String jobXML = "";
587 4951 daigle
588 4959 daigle
		if (scheduledJobDAO != null) {
589
			jobXML += "<scheduledJob>";
590
			jobXML += "<id>" + scheduledJobDAO.getId() + "</id>";
591
			jobXML += "<createTime>" + scheduledJobDAO.getCreateTime() + "</createTime>";
592
			jobXML += "<modTime>" + scheduledJobDAO.getModTime() + "</modTime>";
593
			jobXML += "<status>" + scheduledJobDAO.getStatus() + "</status>";
594
			jobXML += "<name>" + scheduledJobDAO.getName() + "</name>";
595
			jobXML += "<triggerName>" + scheduledJobDAO.getName() + "</triggerName>";
596
			jobXML += "<groupName>" + scheduledJobDAO.getGroupName() + "</groupName>";
597 4967 daigle
			jobXML += "<className>" + scheduledJobDAO.getClassName() + "</className>";
598 5005 daigle
			String startTimeString = null;
599
			try {
600
				startTimeString =
601 5013 daigle
					DateUtil.getHumanReadable(scheduledJobDAO.getStartTime());
602 5005 daigle
			} catch (UtilException ue) {
603
				throw new ServiceException("SchedulerService.jobToXML - error getting human readable date for job: "
604
						+ scheduledJobDAO.getId() + " ; " + ue.getMessage());
605
			}
606 5013 daigle
			jobXML += "<startTime>" + startTimeString + "</startTime>";
607
608
			String endTimeString = null;
609
			try {
610
				if (scheduledJobDAO.getEndTime() != null) {
611
					endTimeString = DateUtil.getHumanReadable(scheduledJobDAO.getEndTime());
612
				}
613
			} catch (UtilException ue) {
614
				throw new ServiceException("SchedulerService.jobToXML - error getting human readable date for job: "
615
						+ scheduledJobDAO.getId() + " ; " + ue.getMessage());
616
			}
617
			jobXML += "<endTime>" + endTimeString + "</endTime>";
618
			jobXML += "<intervalValue>" + scheduledJobDAO.getIntervalValue() + "</intervalValue>";
619
			jobXML += "<intervalUnit>" + scheduledJobDAO.getIntervalUnit() + "</intervalUnit>";
620 4959 daigle
621
			HashMap<String, ScheduledJobParamDAO> jobParams = scheduledJobDAO
622
					.getAllJobParams();
623
			for (String jobParamKey : jobParams.keySet()) {
624 4967 daigle
				jobXML += "<jobParam name='" + jobParams.get(jobParamKey).getKey() + "'>";
625 4959 daigle
				jobXML += "<id>" + jobParams.get(jobParamKey).getId() + "</id>";
626
				jobXML += "<createTime>" + jobParams.get(jobParamKey).getCreateTime()
627
						+ "</createTime>";
628
				jobXML += "<modTime>" + jobParams.get(jobParamKey).getModTime()
629
						+ "</modTime>";
630
				jobXML += "<status>" + jobParams.get(jobParamKey).getStatus()
631
						+ "</status>";
632
				jobXML += "<jobId>" + jobParams.get(jobParamKey).getJobId() + "</jobId>";
633
				jobXML += "<key>" + jobParams.get(jobParamKey).getKey() + "</key>";
634
				jobXML += "<value>" + jobParams.get(jobParamKey).getValue() + "</value>";
635
				jobXML += "</jobParam>";
636
			}
637
			jobXML += "</scheduledJob>";
638
		}
639
640
		return jobXML;
641
	}
642
643 4971 daigle
	/**
644
	 * Start a job in the scheduler
645
	 *
646
	 * @param jobName
647
	 *            the name of the job
648
	 * @param startCal
649
	 *            a calendar holding the start date of the job
650
	 * @param intervalValue
651
	 *            the run interval for the job
652
	 * @param intervalUnit
653
	 *            the unit of the run interval for the job
654
	 * @param jobClassName
655
	 *            the job class name
656
	 * @param jobGroup
657
	 *            the job group name
658
	 * @param jobParams
659
	 *            a map of additional job parameters
660
	 * @param username
661
	 *            the user name
662
	 * @param groups
663
	 *            the user's group name
664
	 */
665 5012 daigle
	private void startJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue, String intervalUnit,
666 4959 daigle
			Class<Job> jobClass, String jobGroup, HashMap<String, String> jobParams) throws ServiceException {
667
668
		JobDetail jobDetail = new JobDetail(jobName, jobGroup, jobClass);
669
		jobDetail.setJobDataMap(new JobDataMap(jobParams));
670
671 4971 daigle
		// call the appropriate scheduling method depending on the schedule interval unit
672 5116 daigle
		if (intervalUnit.equals("sec")) {
673 5012 daigle
			scheduleSecondlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
674 5116 daigle
		} else if (intervalUnit.equals("min")) {
675 5012 daigle
			scheduleMinutelyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
676 5116 daigle
		} else if (intervalUnit.equals("hour")) {
677 5012 daigle
			scheduleHourlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
678 5116 daigle
		} else if (intervalUnit.equals("day")) {
679 5012 daigle
			scheduleDailyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
680 5116 daigle
		} else if (intervalUnit.equals("week")) {
681
			scheduleWeeklyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
682
		} else if (intervalUnit.equals("mon")) {
683
			scheduleMonthlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
684
		} else {
685 4959 daigle
			throw new ServiceException("SchedulerService.scheduleJob - Could not interpret interval unit: "
686 5139 daigle
					+ intervalUnit + ". Unit must be sec, min, hour, day, week or mon");
687 4959 daigle
		}
688
	}
689
690 4971 daigle
	/**
691
	 * Schedule a job in the scheduler that has an interval based in seconds
692
	 *
693
	 * @param jobName
694
	 *            the name of the job
695
	 * @param jobClass
696
	 *            the job class object
697
	 * @param startTime
698
	 *            the time of the first run
699 5016 daigle
	 * @param jobGroup
700
	 *            the group of this job
701
	 * @param jobDetail
702
	 *            the job detail object
703
	 */
704
	private void startOneTimeJob(String jobName, Calendar startTime, Class<Job> jobClass, String jobGroup, HashMap<String, String> jobParams) throws ServiceException {
705
706
		JobDetail jobDetail = new JobDetail(jobName, jobGroup, jobClass);
707
		jobDetail.setJobDataMap(new JobDataMap(jobParams));
708
709
		SimpleTrigger trigger = new SimpleTrigger();
710
		trigger.setName(jobName);
711
		trigger.setStartTime(startTime.getTime());
712
		trigger.setRepeatCount(1);
713
714
		try {
715
			sched.scheduleJob(jobDetail, trigger);
716
		} catch (SchedulerException se) {
717
			throw new ServiceException("SchedulerService.scheduleSecondlyJob - Could not create "
718
					+ "scheduler: " + se.getMessage());
719
		}
720
	}
721
722
	/**
723
	 * Schedule a job in the scheduler that has an interval based in seconds
724
	 *
725
	 * @param jobName
726
	 *            the name of the job
727
	 * @param jobClass
728
	 *            the job class object
729
	 * @param startTime
730
	 *            the time of the first run
731 4971 daigle
	 * @param interval
732
	 *            the interval in seconds between runs
733
	 * @param jobGroup
734
	 *            the group of this job
735
	 * @param jobDetail
736
	 *            the job detail object
737
	 */
738 5012 daigle
	private void scheduleSecondlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
739 4959 daigle
740 4951 daigle
		Trigger trigger = TriggerUtils.makeSecondlyTrigger(interval);
741 4959 daigle
		trigger.setName(jobName);
742 4951 daigle
		trigger.setStartTime(startTime.getTime());
743 5012 daigle
		if (endTime != null) {
744
			trigger.setEndTime(endTime.getTime());
745
		}
746 4951 daigle
747
		try {
748
			sched.scheduleJob(jobDetail, trigger);
749
		} catch (SchedulerException se) {
750
			throw new ServiceException("SchedulerService.scheduleSecondlyJob - Could not create "
751
					+ "scheduler: " + se.getMessage());
752
		}
753
	}
754
755 4971 daigle
	/**
756
	 * Schedule a job in the scheduler that has an interval based in minutes
757
	 *
758
	 * @param jobName
759
	 *            the name of the job
760
	 * @param jobClass
761
	 *            the job class object
762
	 * @param startTime
763
	 *            the time of the first run
764
	 * @param interval
765
	 *            the interval in minutes between runs
766
	 * @param jobGroup
767
	 *            the group of this job
768
	 * @param jobDetail
769
	 *            the job detail object
770
	 */
771 5012 daigle
	private void scheduleMinutelyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
772 4951 daigle
773
		Trigger trigger = TriggerUtils.makeMinutelyTrigger(interval);
774 4959 daigle
		trigger.setName(jobName);
775 4951 daigle
		trigger.setStartTime(startTime.getTime());
776 5012 daigle
		if (endTime != null) {
777
			trigger.setEndTime(endTime.getTime());
778
		}
779 4951 daigle
780
		try {
781
			sched.scheduleJob(jobDetail, trigger);
782
		} catch (SchedulerException se) {
783
			throw new ServiceException("SchedulerService.scheduleMinutelyJob - Could not create "
784
					+ "scheduler: " + se.getMessage());
785
		}
786
	}
787
788 4971 daigle
	/**
789
	 * Schedule a job in the scheduler that has an interval based in hours
790
	 *
791
	 * @param jobName
792
	 *            the name of the job
793
	 * @param jobClass
794
	 *            the job class object
795
	 * @param startTime
796
	 *            the time of the first run
797
	 * @param interval
798
	 *            the interval in hours between runs
799
	 * @param jobGroup
800
	 *            the group of this job
801
	 * @param jobDetail
802
	 *            the job detail object
803
	 */
804 5012 daigle
	private void scheduleHourlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
805 4951 daigle
806
		Trigger trigger = TriggerUtils.makeHourlyTrigger(interval);
807 4959 daigle
		trigger.setName(jobName);
808 4951 daigle
		trigger.setStartTime(startTime.getTime());
809 5012 daigle
		if (endTime != null) {
810
			trigger.setEndTime(endTime.getTime());
811
		}
812 4951 daigle
813
		try {
814
			sched.scheduleJob(jobDetail, trigger);
815
		} catch (SchedulerException se) {
816
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
817
					+  "scheduler: " + se.getMessage());
818
		}
819
	}
820
821 4971 daigle
	/**
822
	 * Schedule a job in the scheduler that has an interval based in days
823
	 *
824
	 * @param jobName
825
	 *            the name of the job
826
	 * @param jobClass
827
	 *            the job class object
828
	 * @param startTime
829
	 *            the time of the first run
830
	 * @param interval
831
	 *            the interval in days between runs
832
	 * @param jobGroup
833
	 *            the group of this job
834
	 * @param jobDetail
835
	 *            the job detail object
836
	 */
837 5012 daigle
	private void scheduleDailyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
838 4951 daigle
839 5116 daigle
		Trigger trigger =
840
			TriggerUtils.makeDailyTrigger(startTime.get(Calendar.HOUR), startTime.get(Calendar.MINUTE));
841 4959 daigle
		trigger.setName(jobName);
842 4951 daigle
		trigger.setStartTime(startTime.getTime());
843 5012 daigle
		if (endTime != null) {
844
			trigger.setEndTime(endTime.getTime());
845
		}
846 4951 daigle
847
		try {
848
			sched.scheduleJob(jobDetail, trigger);
849
		} catch (SchedulerException se) {
850
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
851
					+ "scheduler: " + se.getMessage());
852
		}
853
	}
854
855 4971 daigle
	/**
856 5116 daigle
	 * Schedule a job in the scheduler that has an interval based in days
857
	 *
858
	 * @param jobName
859
	 *            the name of the job
860
	 * @param jobClass
861
	 *            the job class object
862
	 * @param startTime
863
	 *            the time of the first run
864
	 * @param interval
865
	 *            the interval in days between runs
866
	 * @param jobGroup
867
	 *            the group of this job
868
	 * @param jobDetail
869
	 *            the job detail object
870
	 */
871
	private void scheduleWeeklyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
872
873
		Trigger trigger =
874
			TriggerUtils.makeWeeklyTrigger(startTime.get(Calendar.DAY_OF_WEEK),
875
					startTime.get(Calendar.HOUR), startTime.get(Calendar.MINUTE));
876
		trigger.setName(jobName);
877
		trigger.setStartTime(startTime.getTime());
878
		if (endTime != null) {
879
			trigger.setEndTime(endTime.getTime());
880
		}
881
882
		try {
883
			sched.scheduleJob(jobDetail, trigger);
884
		} catch (SchedulerException se) {
885
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
886
					+ "scheduler: " + se.getMessage());
887
		}
888
	}
889
890
	/**
891
	 * Schedule a job in the scheduler that has an interval based in days
892
	 *
893
	 * @param jobName
894
	 *            the name of the job
895
	 * @param jobClass
896
	 *            the job class object
897
	 * @param startTime
898
	 *            the time of the first run
899
	 * @param interval
900
	 *            the interval in days between runs
901
	 * @param jobGroup
902
	 *            the group of this job
903
	 * @param jobDetail
904
	 *            the job detail object
905
	 */
906
	private void scheduleMonthlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
907
908
		Trigger trigger =
909
			TriggerUtils.makeMonthlyTrigger(startTime.get(Calendar.DAY_OF_MONTH),
910
					startTime.get(Calendar.HOUR), startTime.get(Calendar.MINUTE));
911
		trigger.setName(jobName);
912
		trigger.setStartTime(startTime.getTime());
913
		if (endTime != null) {
914
			trigger.setEndTime(endTime.getTime());
915
		}
916
917
		try {
918
			sched.scheduleJob(jobDetail, trigger);
919
		} catch (SchedulerException se) {
920
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
921
					+ "scheduler: " + se.getMessage());
922
		}
923
	}
924
925
	/**
926 4971 daigle
	 * Extract the start date from the delay value
927
	 *
928
	 * @param delay
929
	 *            a string representing the start delay in <value><unit>
930
	 *            notation where value is an integer and unit is one of s,m,h or
931
	 *            d
932
	 * @return the calendar object holding the start date
933
	 */
934 4959 daigle
	public Calendar getStartDateFromDelay(String delay) throws ServiceException {
935 4951 daigle
		Calendar cal = Calendar.getInstance();
936
937 4959 daigle
		char delayUnit = delay.trim().charAt(delay.length() - 1);
938 4951 daigle
		String delayStrValue = delay.trim().substring(0, delay.length() - 1);
939
		int delayValue;
940
		try {
941
			delayValue = Integer.parseInt(delayStrValue);
942
		} catch (NumberFormatException nfe) {
943
			throw new ServiceException("SchedulerService.getStartDateFromDelay - Could not "
944 4959 daigle
					+ "parse delay value into an integer: " + delayStrValue + " : " + nfe.getMessage());
945 4951 daigle
		}
946
947
		switch (delayUnit) {
948
		case 's':
949
		case 'S':
950
			cal.add(Calendar.SECOND, delayValue);
951
			break;
952
		case 'm':
953
		case 'M':
954
			cal.add(Calendar.MINUTE, delayValue);
955
			break;
956
		case 'h':
957
		case 'H':
958
			cal.add(Calendar.HOUR, delayValue);
959
			break;
960
		case 'd':
961
		case 'D':
962
			cal.add(Calendar.DAY_OF_YEAR, delayValue);
963
			break;
964
		default:
965
			throw new ServiceException("SchedulerService.getStartDateFromDelay - Could not "
966
					+ "interpret delay unit: " + delayUnit + ". Unit must be s, m, h or d");
967
		}
968
969
		return cal;
970 4971 daigle
	}
971 4951 daigle
}