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 5012 daigle
	public String scheduleJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue,
198 5057 daigle
			String intervalUnit, String jobClassName, String jobGroup, HashMap<String, String> jobParams)
199
	throws ServiceException {
200 4967 daigle
201 4951 daigle
        Class<Job> jobClass = null;
202 5005 daigle
        try {
203
			jobClass = (Class<Job>) Class.forName(jobClassName);
204
205 5013 daigle
			String startTimeStr = DateUtil.getHumanReadable(startCal);
206 5005 daigle
			logMetacat.info("SchedulerService.scheduleJob - Scheduling job -- name: "
207
					+ jobName + ", class: " + jobClassName + ", start time: "
208
					+ startTimeStr + ", interval value: " + intervalValue
209
					+ ", interval unit: " + intervalUnit);
210
211
			// start the job in the job scheduler
212 5012 daigle
			startJob(jobName, startCal, endCal, intervalValue, intervalUnit, jobClass, jobGroup,
213 5005 daigle
					jobParams);
214
215
			// get a database access object and create the job in the database
216
217 4951 daigle
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
218 5012 daigle
			jobAccess.createJob(jobName, jobName, jobGroup, jobClass, startCal, endCal,
219 5005 daigle
					intervalValue, intervalUnit, jobParams);
220 4951 daigle
		} catch (AccessException ae) {
221
			try {
222 5057 daigle
				deleteJob(jobName);
223 4959 daigle
			} catch (Exception e) {
224 4951 daigle
				// Not much we can do here but log this
225 5005 daigle
				logMetacat.error("SchedulerService.scheduleJob - An access exception was thrown when writing job: "
226
						+ jobName + "to the db, and another exception was thrown when trying to remove the "
227 4959 daigle
						+ "job from the scheduler.  The db and scheduler may be out of sync: " + e.getMessage());
228 4951 daigle
			}
229 4959 daigle
			throw new ServiceException("SchedulerService.scheduleJob - Error accessing db: ", ae);
230 5005 daigle
		} catch (ClassNotFoundException cnfe) {
231
			throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
232
							+ jobClassName + " : " + cnfe.getMessage());
233
		} catch (UtilException ue) {
234
			throw new ServiceException("SchedulerService.scheduleJob - Could not schedule "
235
							+ "job due to a utility issue: " + ue.getMessage());
236 4951 daigle
		}
237
238 4967 daigle
		return "Scheduled: " + jobName;
239 4951 daigle
	}
240
241 4971 daigle
	/**
242 5016 daigle
	 * schedule a job one time with a given delay. The job is registered in the
243
	 * scheduler, but not persisted to the database. The delay is calculated
244
	 * from the time that this method is called.
245
	 *
246
	 * @param jobName
247
	 *            the name of the job
248
	 * @param delay
249
	 *            the delay in seconds, minutes, hours or days from the time
250
	 *            this method is called (i.e. 30s, 5m, 2h, 1d)
251
	 * @param jobClassName
252
	 *            the job class name
253
	 * @param jobGroup
254
	 *            the job group name
255
	 * @param jobParams
256
	 *            a map of additional job parameters
257
	 * @param username
258
	 *            the user name
259
	 * @param groups
260
	 *            the user's group name
261
	 * @return a message saying that the job was scheduled
262
	 */
263
	public String scheduleDelayedJob(String jobName, String delay, String jobClassName,
264
			String jobGroup, HashMap<String, String> jobParams,
265
			String username, String[] groups) throws ServiceException {
266
267
        Class<Job> jobClass = null;
268
        try {
269
			jobClass = (Class<Job>) Class.forName(jobClassName);
270
271
			Calendar startCal = getStartDateFromDelay(delay);
272
273
			logMetacat.info("SchedulerService.scheduleDelayedJob - Scheduling job -- name: "
274
					+ jobName + ", delay: " + delay + ", job class name: " + jobClassName);
275
276
			// start the job in the job scheduler
277
			startOneTimeJob(jobName, startCal, jobClass, jobGroup,
278
					jobParams);
279
280
		} catch (ClassNotFoundException cnfe) {
281
			throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
282
							+ jobClassName + " : " + cnfe.getMessage());
283
		}
284
285
		return "Scheduled: " + jobName;
286
	}
287
288
	/**
289 4971 daigle
	 * Unschedule a job. This removed it from the scheduler in memory and
290
	 * changed it's status to unscheduled in the database.
291
	 *
292
	 * @param jobName
293
	 *            the name of the job to unschedule
294
	 * @param username
295
	 *            the user name
296
	 * @param groups
297
	 *            the user's group name
298
	 * @return a message saying the job was unscheduled
299
	 */
300 5044 daigle
	public String unscheduleJob(String jobName, String username,
301 4967 daigle
			String[] groups) throws ServiceException {
302 5013 daigle
303
		ScheduledJobDAO jobDAO = null;
304 4951 daigle
		try {
305
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
306 5013 daigle
			jobDAO = jobAccess.getJobByName(jobName);
307 4967 daigle
			if (jobDAO == null) {
308
				throw new ServiceException("SchedulerService.unscheduleJob - Could "
309
						+ "not find job with name: " + jobName);
310
			}
311 4951 daigle
312 4971 daigle
			// remove the job from the scheduler
313 4967 daigle
			sched.deleteJob(jobDAO.getName(), jobDAO.getGroupName());
314 4951 daigle
315 4971 daigle
			// change the status of the job to unscheduled in the database.
316 4967 daigle
			jobDAO.setStatus(StatusUtil.UNSCHEDULED);
317 4951 daigle
			jobAccess.updateJobStatus(jobDAO);
318
		} catch (SchedulerException se) {
319
			throw new ServiceException("SchedulerService.unscheduleJob - Could not create "
320
							+ "scheduled job because of service issue: " + se.getMessage());
321
		} catch (AccessException ae) {
322
			throw new ServiceException("SchedulerService.unscheduleJob - Could not create "
323 5013 daigle
							+ "scheduled job : " + jobDAO.getName() + " because of db access issue: ", ae);
324 4951 daigle
		}
325 4967 daigle
326
		return "Unscheduled: " + jobName;
327 4951 daigle
	}
328
329 4971 daigle
	/**
330
	 * Reschedule a job. This call will always check to make sure the status is not SCHEDULED
331
	 * @param jobDAO the job data object holding the information about the job to reschedule
332
	 * @param username
333
	 *            the user name
334
	 * @param groups
335
	 *            the user's group name
336
	 * @return a message saying that the job was rescheduled
337
	 */
338 4967 daigle
	public String rescheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups) throws ServiceException {
339
		return rescheduleJob(jobDAO, username, groups, true);
340
	}
341
342 4971 daigle
	/**
343
	 * Reschedule a job.
344
	 *
345
	 * @param jobDAO
346
	 *            the job data object holding the information about the job to
347
	 *            reschedule
348
	 * @param username
349
	 *            the user name
350
	 * @param groups
351
	 *            the user's group name
352
	 * @param checkStatus
353
	 *            if set to true, the method will check to make sure the status
354
	 *            is UNSCHEDULED before restarting. Otherwise, the method will
355
	 *            not check. This is so that we can restart a service at startup
356
	 *            that was running when metacat was shut down.
357
	 * @return a message saying that the job was rescheduled
358
	 */
359 4967 daigle
	public String rescheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups, boolean checkStatus) throws ServiceException {
360
361
		try {
362
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
363
364
			if (jobDAO == null) {
365
				throw new ServiceException("SchedulerService.reScheduleJob - Cannot reschedule nonexistant job.");
366
			}
367
368 4971 daigle
			// if we are checking status, make sure the job is in an UNSCHEDULED state in the db
369 4967 daigle
			if (checkStatus && !jobDAO.getStatus().equals(StatusUtil.UNSCHEDULED)) {
370
				throw new ServiceException("SchedulerService.reScheduleJob - Cannot reschedule a job with status: "
371
						+ jobDAO.getStatus() + ". Status must be 'unscheduled'.");
372
			}
373
374
			Calendar startCal = Calendar.getInstance();
375
	        startCal.setTime(jobDAO.getStartTime());
376
377 5012 daigle
			Calendar endCal = Calendar.getInstance();
378
	        endCal.setTime(jobDAO.getEndTime());
379
380 4967 daigle
	        HashMap<String, String> jobParams = new HashMap<String, String>();
381
	        HashMap<String, ScheduledJobParamDAO> jobParamDAOs = jobDAO.getAllJobParams();
382
	        for (String paramName : jobParamDAOs.keySet()) {
383
	        	jobParams.put(paramName, jobParamDAOs.get(paramName).getValue());
384
	        }
385
386
	        Class<Job> jobClass = null;
387
	        String jobClassName = jobDAO.getClassName();
388
	        try {
389
	        	jobClass = (Class<Job>)Class.forName(jobClassName);
390
	        } catch (ClassNotFoundException cnfe) {
391
	        	throw new ServiceException("SchedulerService.scheduleJob - Could not find class with name: "
392
	        			+ jobDAO.getClassName() + " : " + cnfe.getMessage());
393
	        }
394
395 5013 daigle
	        String startTimeStr = DateUtil.getHumanReadable(startCal);
396 4967 daigle
	        logMetacat.info("SchedulerService.rescheduleJob - name: " + jobDAO.getName() + ", class: " + jobClassName
397 5005 daigle
	        		+ ", start time: " + startTimeStr + ", interval value: " + jobDAO.getIntervalValue()
398 4967 daigle
	        		+ ", interval unit: " + jobDAO.getIntervalUnit());
399
400 4971 daigle
	        // start the job in the scheduler
401 5012 daigle
			startJob(jobDAO.getName(), startCal, endCal, jobDAO.getIntervalValue(), jobDAO.getIntervalUnit(), jobClass, jobDAO.getGroupName(), jobParams);
402 4967 daigle
403 4971 daigle
			// update the status in the database
404 4967 daigle
			jobDAO.setStatus(StatusUtil.SCHEDULED);
405
			jobAccess.updateJobStatus(jobDAO);
406
407
		} catch (AccessException ae) {
408
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
409 5013 daigle
					+ "job : " + jobDAO.getName() + " because of db access issue: ", ae);
410 5005 daigle
		} catch (UtilException ue) {
411
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
412 5013 daigle
					+ "job : " + jobDAO.getName() + " due to a utility issue: " + ue.getMessage());
413 4967 daigle
		}
414
415 5044 daigle
		return "Rescheduled: " + jobDAO.getName();
416 4967 daigle
	}
417 5013 daigle
418
	/**
419
	 * Remove the job from the scheduler and set the job status to deleted in the database
420
	 * @param jobName
421
	 *            the string holding the name of the job to delete
422
	 * @param username
423
	 *            the user name
424
	 * @param groups
425
	 *            the user's group name
426
	 * @return a message saying that the job was deleted
427
	 */
428 5057 daigle
	public String deleteJob(String jobName) throws ServiceException {
429 5013 daigle
430
		ScheduledJobDAO jobDAO = null;
431
		try {
432
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
433
			jobDAO = jobAccess.getJobByName(jobName);
434
		} catch (AccessException ae) {
435
			throw new ServiceException("SchedulerService.deleteJob - Could not delete "
436
					+ "scheduled job : " + jobDAO.getName() + " because of db access issue: ", ae);
437
		}
438
439 5057 daigle
		return deleteJob(jobDAO);
440 5013 daigle
	}
441 4967 daigle
442 4971 daigle
	/**
443
	 * Remove the job from the scheduler and set the job status to deleted in the database
444
	 * @param jobDAO
445 5013 daigle
	 *            the job data object holding the information about the job to delete
446 4971 daigle
	 * @param username
447
	 *            the user name
448
	 * @param groups
449
	 *            the user's group name
450
	 * @return a message saying that the job was deleted
451
	 */
452 5057 daigle
	public String deleteJob(ScheduledJobDAO jobDAO) throws ServiceException {
453 4967 daigle
454 4951 daigle
		String groupName = "";
455
		try {
456 4959 daigle
457 5013 daigle
			sched.deleteJob(jobDAO.getName(), groupName);
458 4967 daigle
459
			jobDAO.setStatus(StatusUtil.DELETED);
460 5013 daigle
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
461 4967 daigle
			jobAccess.updateJobStatus(jobDAO);
462 4951 daigle
		} catch (SchedulerException se) {
463 5013 daigle
			throw new ServiceException("SchedulerService.deleteJob - Could not delete job: " + jobDAO.getName()
464 4959 daigle
							+ " for group: " + groupName + " : " + se.getMessage());
465 4951 daigle
		} catch (AccessException ae) {
466
			throw new ServiceException("SchedulerService.deleteJob - Could not delete "
467 5013 daigle
					+ "scheduled job: " + jobDAO.getName() + " because of db access issue: ", ae);
468 4959 daigle
		}
469 4951 daigle
470 5013 daigle
		return "Deleted: " + jobDAO.getName();
471 4951 daigle
	}
472
473 4971 daigle
	/**
474
	 * Get information about the job in XML format
475
	 *
476
	 * @param jobId
477
	 *            the job for which we want the information
478
	 * @return an XML representation of the job
479
	 */
480 4959 daigle
	public String getJobInfoXML(Long jobId) throws ServiceException {
481
		String jobInfoXML = "";
482
483
		try {
484
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
485
			ScheduledJobDAO scheduledJobDAO = jobAccess.getJob(jobId);
486
487
			jobInfoXML +=
488
				"<scheduledJobs>" + jobToXML(scheduledJobDAO) + "</scheduledJobs>";
489
490
		} catch (AccessException ae) {
491
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get job info for job: "
492
					+ jobId, ae);
493
		}
494
495
		return jobInfoXML;
496
	}
497
498 4971 daigle
	/**
499
	 * Get the information for jobs in a group in an xml format. A parameter
500
	 * key/value pair can be provided as well to limit the jobs returned.
501
	 *
502
	 * @param groupName
503
	 *            the job group that we are searching for
504
	 * @param paramName
505
	 *            the parameter name that we are looking for. this is ignored if
506
	 *            null
507
	 * @param paramValue
508
	 *            the parameter value that we are looking for. this is ignored
509
	 *            if null
510
	 * @return an XML representation of the jobs.
511
	 */
512 4967 daigle
	public String getJobsInfoXML(String groupName, String paramName, String paramValue) throws ServiceException {
513 4959 daigle
		String jobInfoXML = "";
514
515
		try {
516
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
517 4967 daigle
			HashMap<Long, ScheduledJobDAO> JobDAOMap = jobAccess.getJobsWithParameter(groupName, paramName, paramValue);
518 4959 daigle
519 4967 daigle
			jobInfoXML += "<scheduledWorkflowResultset>";
520 4959 daigle
			for (Long jobDAOId : JobDAOMap.keySet()) {
521 4967 daigle
				ScheduledJobDAO jobDAO = JobDAOMap.get(jobDAOId);
522
				if (paramValue != null && paramName != null) {
523
					ScheduledJobParamDAO jobParamDAO = jobDAO.getJobParam(paramName);
524
					if(jobParamDAO != null && jobParamDAO.getValue().equals(paramValue)) {
525
						jobInfoXML +=  jobToXML(JobDAOMap.get(jobDAOId));
526
					}
527
				}
528 4959 daigle
			}
529 4967 daigle
			jobInfoXML += "</scheduledWorkflowResultset>";
530 4959 daigle
531
		} catch (AccessException ae) {
532
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get jobs info for group: "
533
					+ groupName, ae);
534
		}
535
536
		return jobInfoXML;
537
	}
538
539 4971 daigle
	/**
540 5027 daigle
	 * Get the information for jobs in a group in an xml format. A parameter
541
	 * key/value pair can be provided as well to limit the jobs returned.
542
	 *
543
	 * @param groupName
544
	 *            the job group that we are searching for
545
	 * @param paramName
546
	 *            the parameter name that we are looking for. this is ignored if
547
	 *            null
548
	 * @param paramValue
549
	 *            the parameter value that we are looking for. this is ignored
550
	 *            if null
551
	 * @return an XML representation of the jobs.
552
	 */
553
	public void getJobsInfoXML(String groupName, String paramName, String paramValue, PrintWriter pw) throws ServiceException {
554
555
		try {
556
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
557
			HashMap<Long, ScheduledJobDAO> JobDAOMap = jobAccess.getJobsWithParameter(groupName, paramName, paramValue);
558
559
			pw.print("<scheduledWorkflowResultset>");
560
			for (Long jobDAOId : JobDAOMap.keySet()) {
561
				ScheduledJobDAO jobDAO = JobDAOMap.get(jobDAOId);
562
				if (paramValue != null && paramName != null) {
563
					ScheduledJobParamDAO jobParamDAO = jobDAO.getJobParam(paramName);
564
					if(jobParamDAO != null && jobParamDAO.getValue().equals(paramValue)) {
565
						pw.print(jobToXML(JobDAOMap.get(jobDAOId)));
566
					}
567
				}
568
			}
569
			pw.print("</scheduledWorkflowResultset>");
570
571
		} catch (AccessException ae) {
572
			throw new ServiceException("SchedulerService.getJobInfoXML - Could not get jobs info for group: "
573
					+ groupName, ae);
574
		}
575
	}
576
577
	/**
578 4971 daigle
	 * Convert a single job to XML
579
	 * @param scheduledJobDAO the job we want to convert
580
	 * @return an XML representation of the job
581
	 */
582 4967 daigle
	public String jobToXML(ScheduledJobDAO scheduledJobDAO) throws ServiceException {
583 4959 daigle
		String jobXML = "";
584 4951 daigle
585 4959 daigle
		if (scheduledJobDAO != null) {
586
			jobXML += "<scheduledJob>";
587
			jobXML += "<id>" + scheduledJobDAO.getId() + "</id>";
588
			jobXML += "<createTime>" + scheduledJobDAO.getCreateTime() + "</createTime>";
589
			jobXML += "<modTime>" + scheduledJobDAO.getModTime() + "</modTime>";
590
			jobXML += "<status>" + scheduledJobDAO.getStatus() + "</status>";
591
			jobXML += "<name>" + scheduledJobDAO.getName() + "</name>";
592
			jobXML += "<triggerName>" + scheduledJobDAO.getName() + "</triggerName>";
593
			jobXML += "<groupName>" + scheduledJobDAO.getGroupName() + "</groupName>";
594 4967 daigle
			jobXML += "<className>" + scheduledJobDAO.getClassName() + "</className>";
595 5005 daigle
			String startTimeString = null;
596
			try {
597
				startTimeString =
598 5013 daigle
					DateUtil.getHumanReadable(scheduledJobDAO.getStartTime());
599 5005 daigle
			} catch (UtilException ue) {
600
				throw new ServiceException("SchedulerService.jobToXML - error getting human readable date for job: "
601
						+ scheduledJobDAO.getId() + " ; " + ue.getMessage());
602
			}
603 5013 daigle
			jobXML += "<startTime>" + startTimeString + "</startTime>";
604
605
			String endTimeString = null;
606
			try {
607
				if (scheduledJobDAO.getEndTime() != null) {
608
					endTimeString = DateUtil.getHumanReadable(scheduledJobDAO.getEndTime());
609
				}
610
			} catch (UtilException ue) {
611
				throw new ServiceException("SchedulerService.jobToXML - error getting human readable date for job: "
612
						+ scheduledJobDAO.getId() + " ; " + ue.getMessage());
613
			}
614
			jobXML += "<endTime>" + endTimeString + "</endTime>";
615
			jobXML += "<intervalValue>" + scheduledJobDAO.getIntervalValue() + "</intervalValue>";
616
			jobXML += "<intervalUnit>" + scheduledJobDAO.getIntervalUnit() + "</intervalUnit>";
617 4959 daigle
618
			HashMap<String, ScheduledJobParamDAO> jobParams = scheduledJobDAO
619
					.getAllJobParams();
620
			for (String jobParamKey : jobParams.keySet()) {
621 4967 daigle
				jobXML += "<jobParam name='" + jobParams.get(jobParamKey).getKey() + "'>";
622 4959 daigle
				jobXML += "<id>" + jobParams.get(jobParamKey).getId() + "</id>";
623
				jobXML += "<createTime>" + jobParams.get(jobParamKey).getCreateTime()
624
						+ "</createTime>";
625
				jobXML += "<modTime>" + jobParams.get(jobParamKey).getModTime()
626
						+ "</modTime>";
627
				jobXML += "<status>" + jobParams.get(jobParamKey).getStatus()
628
						+ "</status>";
629
				jobXML += "<jobId>" + jobParams.get(jobParamKey).getJobId() + "</jobId>";
630
				jobXML += "<key>" + jobParams.get(jobParamKey).getKey() + "</key>";
631
				jobXML += "<value>" + jobParams.get(jobParamKey).getValue() + "</value>";
632
				jobXML += "</jobParam>";
633
			}
634
			jobXML += "</scheduledJob>";
635
		}
636
637
		return jobXML;
638
	}
639
640 4971 daigle
	/**
641
	 * Start a job in the scheduler
642
	 *
643
	 * @param jobName
644
	 *            the name of the job
645
	 * @param startCal
646
	 *            a calendar holding the start date of the job
647
	 * @param intervalValue
648
	 *            the run interval for the job
649
	 * @param intervalUnit
650
	 *            the unit of the run interval for the job
651
	 * @param jobClassName
652
	 *            the job class name
653
	 * @param jobGroup
654
	 *            the job group name
655
	 * @param jobParams
656
	 *            a map of additional job parameters
657
	 * @param username
658
	 *            the user name
659
	 * @param groups
660
	 *            the user's group name
661
	 */
662 5012 daigle
	private void startJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue, String intervalUnit,
663 4959 daigle
			Class<Job> jobClass, String jobGroup, HashMap<String, String> jobParams) throws ServiceException {
664
665
		JobDetail jobDetail = new JobDetail(jobName, jobGroup, jobClass);
666
		jobDetail.setJobDataMap(new JobDataMap(jobParams));
667
668 4967 daigle
		char intervalChar = intervalUnit.charAt(0);
669
670 4971 daigle
		// call the appropriate scheduling method depending on the schedule interval unit
671 4967 daigle
		switch (intervalChar) {
672 4959 daigle
		case 's':
673
		case 'S':
674 5012 daigle
			scheduleSecondlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
675 4959 daigle
			break;
676
		case 'm':
677
		case 'M':
678 5012 daigle
			scheduleMinutelyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
679 4959 daigle
			break;
680
		case 'h':
681
		case 'H':
682 5012 daigle
			scheduleHourlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
683 4959 daigle
			break;
684
		case 'd':
685
		case 'D':
686 5012 daigle
			scheduleDailyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
687 4959 daigle
			break;
688
		default:
689
			throw new ServiceException("SchedulerService.scheduleJob - Could not interpret interval unit: "
690
					+ intervalUnit + ". Unit must be s, m, h or d");
691
		}
692
	}
693
694 4971 daigle
	/**
695
	 * Schedule a job in the scheduler that has an interval based in seconds
696
	 *
697
	 * @param jobName
698
	 *            the name of the job
699
	 * @param jobClass
700
	 *            the job class object
701
	 * @param startTime
702
	 *            the time of the first run
703 5016 daigle
	 * @param jobGroup
704
	 *            the group of this job
705
	 * @param jobDetail
706
	 *            the job detail object
707
	 */
708
	private void startOneTimeJob(String jobName, Calendar startTime, Class<Job> jobClass, String jobGroup, HashMap<String, String> jobParams) throws ServiceException {
709
710
		JobDetail jobDetail = new JobDetail(jobName, jobGroup, jobClass);
711
		jobDetail.setJobDataMap(new JobDataMap(jobParams));
712
713
		SimpleTrigger trigger = new SimpleTrigger();
714
		trigger.setName(jobName);
715
		trigger.setStartTime(startTime.getTime());
716
		trigger.setRepeatCount(1);
717
718
		try {
719
			sched.scheduleJob(jobDetail, trigger);
720
		} catch (SchedulerException se) {
721
			throw new ServiceException("SchedulerService.scheduleSecondlyJob - Could not create "
722
					+ "scheduler: " + se.getMessage());
723
		}
724
	}
725
726
	/**
727
	 * Schedule a job in the scheduler that has an interval based in seconds
728
	 *
729
	 * @param jobName
730
	 *            the name of the job
731
	 * @param jobClass
732
	 *            the job class object
733
	 * @param startTime
734
	 *            the time of the first run
735 4971 daigle
	 * @param interval
736
	 *            the interval in seconds between runs
737
	 * @param jobGroup
738
	 *            the group of this job
739
	 * @param jobDetail
740
	 *            the job detail object
741
	 */
742 5012 daigle
	private void scheduleSecondlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
743 4959 daigle
744 4951 daigle
		Trigger trigger = TriggerUtils.makeSecondlyTrigger(interval);
745 4959 daigle
		trigger.setName(jobName);
746 4951 daigle
		trigger.setStartTime(startTime.getTime());
747 5012 daigle
		if (endTime != null) {
748
			trigger.setEndTime(endTime.getTime());
749
		}
750 4951 daigle
751
		try {
752
			sched.scheduleJob(jobDetail, trigger);
753
		} catch (SchedulerException se) {
754
			throw new ServiceException("SchedulerService.scheduleSecondlyJob - Could not create "
755
					+ "scheduler: " + se.getMessage());
756
		}
757
	}
758
759 4971 daigle
	/**
760
	 * Schedule a job in the scheduler that has an interval based in minutes
761
	 *
762
	 * @param jobName
763
	 *            the name of the job
764
	 * @param jobClass
765
	 *            the job class object
766
	 * @param startTime
767
	 *            the time of the first run
768
	 * @param interval
769
	 *            the interval in minutes between runs
770
	 * @param jobGroup
771
	 *            the group of this job
772
	 * @param jobDetail
773
	 *            the job detail object
774
	 */
775 5012 daigle
	private void scheduleMinutelyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
776 4951 daigle
777
		Trigger trigger = TriggerUtils.makeMinutelyTrigger(interval);
778 4959 daigle
		trigger.setName(jobName);
779 4951 daigle
		trigger.setStartTime(startTime.getTime());
780 5012 daigle
		if (endTime != null) {
781
			trigger.setEndTime(endTime.getTime());
782
		}
783 4951 daigle
784
		try {
785
			sched.scheduleJob(jobDetail, trigger);
786
		} catch (SchedulerException se) {
787
			throw new ServiceException("SchedulerService.scheduleMinutelyJob - Could not create "
788
					+ "scheduler: " + se.getMessage());
789
		}
790
	}
791
792 4971 daigle
	/**
793
	 * Schedule a job in the scheduler that has an interval based in hours
794
	 *
795
	 * @param jobName
796
	 *            the name of the job
797
	 * @param jobClass
798
	 *            the job class object
799
	 * @param startTime
800
	 *            the time of the first run
801
	 * @param interval
802
	 *            the interval in hours between runs
803
	 * @param jobGroup
804
	 *            the group of this job
805
	 * @param jobDetail
806
	 *            the job detail object
807
	 */
808 5012 daigle
	private void scheduleHourlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
809 4951 daigle
810
		Trigger trigger = TriggerUtils.makeHourlyTrigger(interval);
811 4959 daigle
		trigger.setName(jobName);
812 4951 daigle
		trigger.setStartTime(startTime.getTime());
813 5012 daigle
		if (endTime != null) {
814
			trigger.setEndTime(endTime.getTime());
815
		}
816 4951 daigle
817
		try {
818
			sched.scheduleJob(jobDetail, trigger);
819
		} catch (SchedulerException se) {
820
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
821
					+  "scheduler: " + se.getMessage());
822
		}
823
	}
824
825 4971 daigle
	/**
826
	 * Schedule a job in the scheduler that has an interval based in days
827
	 *
828
	 * @param jobName
829
	 *            the name of the job
830
	 * @param jobClass
831
	 *            the job class object
832
	 * @param startTime
833
	 *            the time of the first run
834
	 * @param interval
835
	 *            the interval in days between runs
836
	 * @param jobGroup
837
	 *            the group of this job
838
	 * @param jobDetail
839
	 *            the job detail object
840
	 */
841 5012 daigle
	private void scheduleDailyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
842 4951 daigle
843
		Trigger trigger = TriggerUtils.makeHourlyTrigger(interval * 24);
844 4959 daigle
		trigger.setName(jobName);
845 4951 daigle
		trigger.setStartTime(startTime.getTime());
846 5012 daigle
		if (endTime != null) {
847
			trigger.setEndTime(endTime.getTime());
848
		}
849 4951 daigle
850
		try {
851
			sched.scheduleJob(jobDetail, trigger);
852
		} catch (SchedulerException se) {
853
			throw new ServiceException("SchedulerService.scheduleHourlyJob - Could not create "
854
					+ "scheduler: " + se.getMessage());
855
		}
856
	}
857
858 4971 daigle
	/**
859
	 * Extract the start date from the delay value
860
	 *
861
	 * @param delay
862
	 *            a string representing the start delay in <value><unit>
863
	 *            notation where value is an integer and unit is one of s,m,h or
864
	 *            d
865
	 * @return the calendar object holding the start date
866
	 */
867 4959 daigle
	public Calendar getStartDateFromDelay(String delay) throws ServiceException {
868 4951 daigle
		Calendar cal = Calendar.getInstance();
869
870 4959 daigle
		char delayUnit = delay.trim().charAt(delay.length() - 1);
871 4951 daigle
		String delayStrValue = delay.trim().substring(0, delay.length() - 1);
872
		int delayValue;
873
		try {
874
			delayValue = Integer.parseInt(delayStrValue);
875
		} catch (NumberFormatException nfe) {
876
			throw new ServiceException("SchedulerService.getStartDateFromDelay - Could not "
877 4959 daigle
					+ "parse delay value into an integer: " + delayStrValue + " : " + nfe.getMessage());
878 4951 daigle
		}
879
880
		switch (delayUnit) {
881
		case 's':
882
		case 'S':
883
			cal.add(Calendar.SECOND, delayValue);
884
			break;
885
		case 'm':
886
		case 'M':
887
			cal.add(Calendar.MINUTE, delayValue);
888
			break;
889
		case 'h':
890
		case 'H':
891
			cal.add(Calendar.HOUR, delayValue);
892
			break;
893
		case 'd':
894
		case 'D':
895
			cal.add(Calendar.DAY_OF_YEAR, delayValue);
896
			break;
897
		default:
898
			throw new ServiceException("SchedulerService.getStartDateFromDelay - Could not "
899
					+ "interpret delay unit: " + delayUnit + ". Unit must be s, m, h or d");
900
		}
901
902
		return cal;
903 4971 daigle
	}
904 4951 daigle
}