Project

General

Profile

1
/**
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
import java.io.PrintWriter;
30
import java.util.Calendar;
31
import java.util.HashMap;
32
import java.util.Vector;
33

    
34
import org.apache.log4j.Logger;
35

    
36
import org.quartz.Job;
37
import org.quartz.JobDataMap;
38
import org.quartz.JobDetail;
39
import org.quartz.Scheduler;
40
import org.quartz.SchedulerException;
41
import org.quartz.SchedulerFactory;
42
import org.quartz.SimpleTrigger;
43
import org.quartz.Trigger;
44
import org.quartz.TriggerUtils;
45

    
46
import edu.ucsb.nceas.metacat.shared.AccessException;
47
import edu.ucsb.nceas.metacat.shared.BaseService;
48
import edu.ucsb.nceas.metacat.shared.ServiceException;
49
import edu.ucsb.nceas.utilities.DateUtil;
50
import edu.ucsb.nceas.utilities.StatusUtil;
51
import edu.ucsb.nceas.utilities.UtilException;
52

    
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
	private SchedulerService() throws ServiceException {
65
		start();
66
	}
67
	
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
	// this is a refreshable class
81
	public boolean refreshable() {
82
		return true;
83
	}
84

    
85
	// do the refresh
86
	public void doRefresh() throws ServiceException {
87
		stop();
88
		start();
89
	}
90

    
91
	// initialize the service
92
	public void start() throws ServiceException {
93
		try {
94
			// get the Quartz scheduler factory
95
			SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
96
			
97
			// get the scheduler
98
			sched = schedFact.getScheduler();
99
			sched.start();
100
			
101
			// get all existing jobs from the database
102
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
103
			HashMap<Long, ScheduledJobDAO> allJobsMap = jobAccess.getAllJobs(null);
104
			
105
			// reschedule each job that is in a SCHEDULED state.  
106
			for (Long jobId : allJobsMap.keySet()) {
107
				ScheduledJobDAO jobDAO = allJobsMap.get(jobId);
108
				String[] groups = {"scheduler_group"};
109
				if (jobDAO.getStatus().equals(StatusUtil.SCHEDULED)) {
110
					// send false as the last param so the reschedule method will not 
111
					// complain that the job is already in a SCHEDULED state.
112
					rescheduleJob(jobDAO, "scheduler_user", groups, false);
113
				}
114
			}			
115
			
116
		} catch (AccessException ae) {
117
			throw new ServiceException("SchedulerService.start - DB Access issue when starting scheduler: ", ae);
118
		} catch (SchedulerException se) {
119
			throw new ServiceException("SchedulerService.start - Scheduler engine issue when starting scheduler: " + se.getMessage());
120
		}		
121
	}
122
	
123
	// Stop the scheduler
124
	public void stop() throws ServiceException {
125
		try {
126
			sched.shutdown();
127
			sched = null;
128
		} catch (SchedulerException se) {
129
			throw new ServiceException("SchedulerService.stop - Could not shut down scheduler: " + se.getMessage());
130
		}		
131
	}
132
	
133
	// this will eventually return the scheduler status
134
	protected Vector<String> getStatus() throws ServiceException {
135
		return new Vector<String>();
136
	}
137
	
138
	/**
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
	public String scheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups) throws ServiceException {
150
        
151
		// convert the start time to a calendar object
152
		Calendar startTimeCal = Calendar.getInstance();
153
        startTimeCal.setTime(jobDAO.getStartTime());
154
        
155
		// convert the start time to a calendar object
156
		Calendar endTimeCal = Calendar.getInstance();
157
        endTimeCal.setTime(jobDAO.getEndTime());
158
        
159
        // extract the job parameters from their data objects and put into a string map
160
        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
        // schedule the job
167
		return scheduleJob(jobDAO.getName(), startTimeCal, endTimeCal, jobDAO.getIntervalValue(), 
168
				jobDAO.getIntervalUnit(), jobDAO.getClassName(), jobDAO.getGroupName(), 
169
				jobParams);
170
	}
171
	
172
	/**
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
	 * @param endCal
180
	 *            a calendar holding the end date of the job
181
	 * @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
	@SuppressWarnings("unchecked")
198
	public String scheduleJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue, 
199
			String intervalUnit, String jobClassName, String jobGroup, HashMap<String, String> jobParams) 
200
	throws ServiceException {
201
        
202
        Class<Job> jobClass = null;
203
        try {
204
			jobClass = (Class<Job>) Class.forName(jobClassName);
205

    
206
			String startTimeStr = DateUtil.getHumanReadable(startCal);
207
			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
			startJob(jobName, startCal, endCal, intervalValue, intervalUnit, jobClass, jobGroup,
214
					jobParams);
215

    
216
			// get a database access object and create the job in the database
217

    
218
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
219
			jobAccess.createJob(jobName, jobName, jobGroup, jobClass, startCal, endCal,
220
					intervalValue, intervalUnit, jobParams);
221
		} catch (AccessException ae) {
222
			try {
223
				deleteJob(jobName);
224
			} catch (Exception e) {
225
				// Not much we can do here but log this
226
				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
						+ "job from the scheduler.  The db and scheduler may be out of sync: " + e.getMessage());
229
			}
230
			throw new ServiceException("SchedulerService.scheduleJob - Error accessing db: ", ae);
231
		} 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
		}
238
		
239
		return "Scheduled: " + jobName;
240
	}
241
	
242
	/**
243
	 * 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
	@SuppressWarnings("unchecked")
265
	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
	 * 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
	public String unscheduleJob(String jobName, String username,
303
			String[] groups) throws ServiceException {
304
		
305
		ScheduledJobDAO jobDAO = null;
306
		try {
307
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
308
			jobDAO = jobAccess.getJobByName(jobName);
309
			if (jobDAO == null) {
310
				throw new ServiceException("SchedulerService.unscheduleJob - Could " 
311
						+ "not find job with name: " + jobName);
312
			}
313

    
314
			// remove the job from the scheduler
315
			sched.deleteJob(jobDAO.getName(), jobDAO.getGroupName());
316

    
317
			// change the status of the job to unscheduled in the database.
318
			jobDAO.setStatus(StatusUtil.UNSCHEDULED);
319
			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
							+ "scheduled job : " + jobDAO.getName() + " because of db access issue: ", ae);
326
		}
327
		
328
		return "Unscheduled: " + jobName;
329
	}
330
	
331
	/**
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
	public String rescheduleJob(ScheduledJobDAO jobDAO, String username, String[] groups) throws ServiceException {
341
		return rescheduleJob(jobDAO, username, groups, true);
342
	}
343
	
344
	/**
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
	@SuppressWarnings("unchecked")
362
	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
			// if we are checking status, make sure the job is in an UNSCHEDULED state in the db
372
			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
			Calendar endCal = Calendar.getInstance();
381
	        endCal.setTime(jobDAO.getEndTime());
382
	        
383
	        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
	        String startTimeStr = DateUtil.getHumanReadable(startCal);
399
	        logMetacat.info("SchedulerService.rescheduleJob - name: " + jobDAO.getName() + ", class: " + jobClassName 
400
	        		+ ", start time: " + startTimeStr + ", interval value: " + jobDAO.getIntervalValue() 
401
	        		+ ", interval unit: " + jobDAO.getIntervalUnit());  
402
			
403
	        // start the job in the scheduler
404
			startJob(jobDAO.getName(), startCal, endCal, jobDAO.getIntervalValue(), jobDAO.getIntervalUnit(), jobClass, jobDAO.getGroupName(), jobParams);
405
	        
406
			// update the status in the database
407
			jobDAO.setStatus(StatusUtil.SCHEDULED);
408
			jobAccess.updateJobStatus(jobDAO);
409
			
410
		} catch (AccessException ae) {
411
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
412
					+ "job : " + jobDAO.getName() + " because of db access issue: ", ae);
413
		} catch (UtilException ue) {
414
			throw new ServiceException("SchedulerService.reScheduleJob - Could not reschedule "
415
					+ "job : " + jobDAO.getName() + " due to a utility issue: " + ue.getMessage());
416
		}
417
        		
418
		return "Rescheduled: " + jobDAO.getName();
419
	}
420

    
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
	public String deleteJob(String jobName) throws ServiceException {
432
		
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
		return deleteJob(jobDAO);
443
	}
444
	
445
	/**
446
	 * Remove the job from the scheduler and set the job status to deleted in the database
447
	 * @param jobDAO
448
	 *            the job data object holding the information about the job to delete
449
	 * @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
	public String deleteJob(ScheduledJobDAO jobDAO) throws ServiceException {
456

    
457
		String groupName = "";
458
		try {
459

    
460
			sched.deleteJob(jobDAO.getName(), groupName);
461
			
462
			jobDAO.setStatus(StatusUtil.DELETED);
463
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
464
			jobAccess.updateJobStatus(jobDAO);
465
		} catch (SchedulerException se) {
466
			throw new ServiceException("SchedulerService.deleteJob - Could not delete job: " + jobDAO.getName()
467
							+ " for group: " + groupName + " : " + se.getMessage());
468
		} catch (AccessException ae) {
469
			throw new ServiceException("SchedulerService.deleteJob - Could not delete "
470
					+ "scheduled job: " + jobDAO.getName() + " because of db access issue: ", ae);
471
		}
472
		
473
		return "Deleted: " + jobDAO.getName();
474
	}
475
	
476
	/**
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
	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
	/**
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
	public String getJobsInfoXML(String groupName, String paramName, String paramValue) throws ServiceException {
516
		String jobInfoXML = "";
517
		
518
		try {
519
			ScheduledJobAccess jobAccess = new ScheduledJobAccess();
520
			HashMap<Long, ScheduledJobDAO> JobDAOMap = jobAccess.getJobsWithParameter(groupName, paramName, paramValue);
521
			
522
			jobInfoXML += "<scheduledWorkflowResultset>";
523
			for (Long jobDAOId : JobDAOMap.keySet()) {
524
				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
			}				
532
			jobInfoXML += "</scheduledWorkflowResultset>";
533
			
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
	/**
543
	 * 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
	 * 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
	public String jobToXML(ScheduledJobDAO scheduledJobDAO) throws ServiceException {
586
		String jobXML = "";
587

    
588
		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
			jobXML += "<className>" + scheduledJobDAO.getClassName() + "</className>";
598
			String startTimeString = null;
599
			try {
600
				startTimeString = 
601
					DateUtil.getHumanReadable(scheduledJobDAO.getStartTime());
602
			} catch (UtilException ue) {
603
				throw new ServiceException("SchedulerService.jobToXML - error getting human readable date for job: " 
604
						+ scheduledJobDAO.getId() + " ; " + ue.getMessage());
605
			}
606
			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

    
621
			HashMap<String, ScheduledJobParamDAO> jobParams = scheduledJobDAO
622
					.getAllJobParams();
623
			for (String jobParamKey : jobParams.keySet()) {
624
				jobXML += "<jobParam name='" + jobParams.get(jobParamKey).getKey() + "'>";
625
				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
	/**
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
	private void startJob(String jobName, Calendar startCal, Calendar endCal, int intervalValue, String intervalUnit,
666
			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
		// call the appropriate scheduling method depending on the schedule interval unit
672
		if (intervalUnit.equals("sec")) {
673
			scheduleSecondlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
674
		} else if (intervalUnit.equals("min")) {
675
			scheduleMinutelyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
676
		} else if (intervalUnit.equals("hour")) {
677
			scheduleHourlyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
678
		} else if (intervalUnit.equals("day")) {
679
			scheduleDailyJob(jobName, jobClass, startCal, endCal, intervalValue, jobGroup, jobDetail);
680
		} 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
			throw new ServiceException("SchedulerService.scheduleJob - Could not interpret interval unit: " 
686
					+ intervalUnit + ". Unit must be sec, min, hour, day, week or mon");	
687
		}	
688
	}
689
	
690
	/**
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
	 * @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
	 * @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
	private void scheduleSecondlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
739

    
740
		Trigger trigger = TriggerUtils.makeSecondlyTrigger(interval);
741
		trigger.setName(jobName);
742
		trigger.setStartTime(startTime.getTime());
743
		if (endTime != null) {
744
			trigger.setEndTime(endTime.getTime());
745
		}
746

    
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
	/**
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
	private void scheduleMinutelyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
772

    
773
		Trigger trigger = TriggerUtils.makeMinutelyTrigger(interval);
774
		trigger.setName(jobName);
775
		trigger.setStartTime(startTime.getTime());
776
		if (endTime != null) {
777
			trigger.setEndTime(endTime.getTime());
778
		}
779

    
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
	/**
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
	private void scheduleHourlyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
805

    
806
		Trigger trigger = TriggerUtils.makeHourlyTrigger(interval);
807
		trigger.setName(jobName);
808
		trigger.setStartTime(startTime.getTime());
809
		if (endTime != null) {
810
			trigger.setEndTime(endTime.getTime());
811
		}
812

    
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
	/**
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
	private void scheduleDailyJob(String jobName, Class<Job> jobClass, Calendar startTime, Calendar endTime, int interval, String jobGroup, JobDetail jobDetail) throws ServiceException {
838

    
839
		Trigger trigger = 
840
			TriggerUtils.makeDailyTrigger(startTime.get(Calendar.HOUR), startTime.get(Calendar.MINUTE));	
841
		trigger.setName(jobName);
842
		trigger.setStartTime(startTime.getTime());
843
		if (endTime != null) {
844
			trigger.setEndTime(endTime.getTime());
845
		}
846

    
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
	/**
856
	 * 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
	 * 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
	public Calendar getStartDateFromDelay(String delay) throws ServiceException {
935
		Calendar cal = Calendar.getInstance();
936
	
937
		char delayUnit = delay.trim().charAt(delay.length() - 1);
938
		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
					+ "parse delay value into an integer: " + delayStrValue + " : " + nfe.getMessage());
945
		}
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
	}	
971
}
(7-7/7)