1 17 18 21 package org.quartz.xml; 22 23 import java.beans.PropertyDescriptor ; 24 import java.io.File ; 25 import java.io.FileInputStream ; 26 import java.io.FileNotFoundException ; 27 import java.io.IOException ; 28 import java.io.InputStream ; 29 import java.net.URL ; 30 import java.net.URLDecoder ; 31 import java.text.DateFormat ; 32 import java.text.ParseException ; 33 import java.text.SimpleDateFormat ; 34 import java.util.ArrayList ; 35 import java.util.Collection ; 36 import java.util.Collections ; 37 import java.util.Date ; 38 import java.util.HashMap ; 39 import java.util.Iterator ; 40 import java.util.LinkedList ; 41 import java.util.List ; 42 import java.util.Map ; 43 import java.util.TimeZone ; 44 45 import javax.xml.parsers.ParserConfigurationException ; 46 47 import org.apache.commons.beanutils.ConversionException; 48 import org.apache.commons.beanutils.Converter; 49 import org.apache.commons.beanutils.DynaBean; 50 import org.apache.commons.beanutils.DynaProperty; 51 import org.apache.commons.beanutils.PropertyUtils; 52 import org.apache.commons.digester.BeanPropertySetterRule; 53 import org.apache.commons.digester.Digester; 54 import org.apache.commons.digester.RuleSetBase; 55 import org.apache.commons.logging.Log; 56 import org.apache.commons.logging.LogFactory; 57 import org.quartz.CronTrigger; 58 import org.quartz.JobDataMap; 59 import org.quartz.JobDetail; 60 import org.quartz.JobListener; 61 import org.quartz.ObjectAlreadyExistsException; 62 import org.quartz.Scheduler; 63 import org.quartz.SchedulerException; 64 import org.quartz.SimpleTrigger; 65 import org.quartz.Trigger; 66 import org.xml.sax.InputSource ; 67 import org.xml.sax.SAXException ; 68 import org.xml.sax.SAXParseException ; 69 import org.xml.sax.helpers.DefaultHandler ; 70 71 72 92 public class JobSchedulingDataProcessor extends DefaultHandler { 93 100 101 public static final String QUARTZ_PUBLIC_ID = "-//Quartz Enterprise Job Scheduler//DTD Job Scheduling Data 1.5//EN"; 102 103 public static final String QUARTZ_SYSTEM_ID = "http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.dtd"; 104 105 public static final String QUARTZ_DTD = "/org/quartz/xml/job_scheduling_data_1_5.dtd"; 106 107 public static final String QUARTZ_NS = "http://www.opensymphony.com/quartz/JobSchedulingData"; 108 109 public static final String QUARTZ_SCHEMA = "http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"; 110 111 public static final String QUARTZ_XSD = "/org/quartz/xml/job_scheduling_data_1_5.xsd"; 112 113 public static final String QUARTZ_SYSTEM_ID_DIR_PROP = "quartz.system.id.dir"; 114 115 public static final String QUARTZ_XML_FILE_NAME = "quartz_jobs.xml"; 116 117 public static final String QUARTZ_SYSTEM_ID_PREFIX = "jar:"; 118 119 protected static final String TAG_QUARTZ = "quartz"; 120 121 protected static final String TAG_OVERWRITE_EXISTING_JOBS = "overwrite-existing-jobs"; 122 123 protected static final String TAG_JOB_LISTENER = "job-listener"; 124 125 protected static final String TAG_CALENDAR = "calendar"; 126 127 protected static final String TAG_CLASS_NAME = "class-name"; 128 129 protected static final String TAG_DESCRIPTION = "description"; 130 131 protected static final String TAG_BASE_CALENDAR = "base-calendar"; 132 133 protected static final String TAG_MISFIRE_INSTRUCTION = "misfire-instruction"; 134 135 protected static final String TAG_CALENDAR_NAME = "calendar-name"; 136 137 protected static final String TAG_JOB = "job"; 138 139 protected static final String TAG_JOB_DETAIL = "job-detail"; 140 141 protected static final String TAG_NAME = "name"; 142 143 protected static final String TAG_GROUP = "group"; 144 145 protected static final String TAG_JOB_CLASS = "job-class"; 146 147 protected static final String TAG_JOB_LISTENER_REF = "job-listener-ref"; 148 149 protected static final String TAG_VOLATILITY = "volatility"; 150 151 protected static final String TAG_DURABILITY = "durability"; 152 153 protected static final String TAG_RECOVER = "recover"; 154 155 protected static final String TAG_JOB_DATA_MAP = "job-data-map"; 156 157 protected static final String TAG_ENTRY = "entry"; 158 159 protected static final String TAG_KEY = "key"; 160 161 protected static final String TAG_ALLOWS_TRANSIENT_DATA = "allows-transient-data"; 162 163 protected static final String TAG_VALUE = "value"; 164 165 protected static final String TAG_TRIGGER = "trigger"; 166 167 protected static final String TAG_SIMPLE = "simple"; 168 169 protected static final String TAG_CRON = "cron"; 170 171 protected static final String TAG_JOB_NAME = "job-name"; 172 173 protected static final String TAG_JOB_GROUP = "job-group"; 174 175 protected static final String TAG_START_TIME = "start-time"; 176 177 protected static final String TAG_END_TIME = "end-time"; 178 179 protected static final String TAG_REPEAT_COUNT = "repeat-count"; 180 181 protected static final String TAG_REPEAT_INTERVAL = "repeat-interval"; 182 183 protected static final String TAG_CRON_EXPRESSION = "cron-expression"; 184 185 protected static final String TAG_TIME_ZONE = "time-zone"; 186 187 193 protected static final String XSD_DATE_FORMAT = "yyyy-MM-dd'T'hh:mm:ss"; 194 195 198 protected static final String DTD_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss a"; 199 200 207 208 protected Map scheduledJobs = new HashMap (); 209 210 protected List jobsToSchedule = new LinkedList (); 211 protected List calsToSchedule = new LinkedList (); 212 protected List listenersToSchedule = new LinkedList (); 213 214 protected Collection validationExceptions = new ArrayList (); 215 216 protected Digester digester; 217 218 private boolean overWriteExistingJobs = true; 219 220 private ThreadLocal schedLocal = new ThreadLocal (); 221 222 private final Log log = LogFactory.getLog(getClass()); 223 224 231 232 235 public JobSchedulingDataProcessor() { 236 this(true, true, true); 237 } 238 239 246 public JobSchedulingDataProcessor(boolean useContextClassLoader, boolean validating, boolean validatingSchema) { 247 initDigester(useContextClassLoader, validating, validatingSchema); 248 } 249 250 257 protected void initDigester(boolean useContextClassLoader, boolean validating, boolean validatingSchema) { 258 digester = new Digester(); 259 digester.setNamespaceAware(true); 260 digester.setUseContextClassLoader(useContextClassLoader); 261 digester.setValidating(validating); 262 initSchemaValidation(validatingSchema); 263 digester.setEntityResolver(this); 264 digester.setErrorHandler(this); 265 266 if (addCustomDigesterRules(digester)) { 267 addDefaultDigesterRules(digester); 268 } 269 } 270 271 274 protected void addDefaultDigesterRules(Digester digester) { 275 digester.addSetProperties(TAG_QUARTZ, TAG_OVERWRITE_EXISTING_JOBS, "overWriteExistingJobs"); 276 digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB_LISTENER, "jobListener","class-name"); 277 digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,"setName",1); 278 digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,0,"name"); 279 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB_LISTENER,"addListenerToSchedule"); 280 digester.addRuleSet(new CalendarRuleSet(TAG_QUARTZ + "/" + TAG_CALENDAR, "addCalendarToSchedule")); 281 digester.addRuleSet(new CalendarRuleSet("*/" + TAG_BASE_CALENDAR, "setBaseCalendar")); 282 digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB, JobSchedulingBundle.class); 283 digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL, JobDetail.class); 284 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_NAME, "name"); 285 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_GROUP, "group"); 286 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_DESCRIPTION, "description"); 287 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_CLASS, "jobClass"); 288 digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_LISTENER_REF,"addJobListener",0 ); 289 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_VOLATILITY, "volatility"); 290 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_DURABILITY, "durability"); 291 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_RECOVER, "requestsRecovery"); 292 digester.addObjectCreate(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP, JobDataMap.class); 293 digester.addCallMethod(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY, "put", 2, new Class [] { Object .class, Object .class }); 294 digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_KEY, 0); 295 digester.addCallParam(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_VALUE, 1); 296 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL + "/" + TAG_JOB_DATA_MAP, "setJobDataMap"); 297 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_JOB_DETAIL, "setJobDetail"); 298 digester.addRuleSet(new TriggerRuleSet(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE, SimpleTrigger.class)); 299 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE + "/" + TAG_REPEAT_COUNT, "repeatCount"); 300 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE + "/" + TAG_REPEAT_INTERVAL, "repeatInterval"); 301 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_SIMPLE, "addTrigger"); 302 digester.addRuleSet(new TriggerRuleSet(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON, CronTrigger.class)); 303 digester.addBeanPropertySetter(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON + "/" + TAG_CRON_EXPRESSION, "cronExpression"); 304 digester.addRule(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON + "/" + TAG_TIME_ZONE, new SimpleConverterRule("timeZone", new TimeZoneConverter(), TimeZone .class)); 305 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB + "/" + TAG_TRIGGER + "/" + TAG_CRON, "addTrigger"); 306 digester.addSetNext(TAG_QUARTZ + "/" + TAG_JOB, "addJobToSchedule"); 307 } 308 309 319 protected boolean addCustomDigesterRules(Digester digester) { 320 return true; 322 } 323 324 329 protected void initSchemaValidation(boolean validatingSchema) { 330 if (validatingSchema) { 331 String schemaUri = null; 332 URL url = getClass().getResource(QUARTZ_XSD); 333 if (url != null) { 334 schemaUri = url.toExternalForm(); 335 } else { 336 schemaUri = QUARTZ_SCHEMA; 337 } 338 digester.setSchema(schemaUri); 339 } 340 } 341 342 protected Log getLog() { 343 return log; 344 } 345 346 351 public boolean getUseContextClassLoader() { 352 return digester.getUseContextClassLoader(); 353 } 354 355 360 public void setUseContextClassLoader(boolean useContextClassLoader) { 361 digester.setUseContextClassLoader(useContextClassLoader); 362 } 363 364 369 public boolean getOverWriteExistingJobs() { 370 return overWriteExistingJobs; 371 } 372 373 378 public void setOverWriteExistingJobs(boolean overWriteExistingJobs) { 379 this.overWriteExistingJobs = overWriteExistingJobs; 380 } 381 382 389 390 395 public void processFile() throws Exception { 396 processFile(QUARTZ_XML_FILE_NAME); 397 } 398 399 405 public void processFile(String fileName) throws Exception { 406 processFile(fileName, getSystemIdForFileName(fileName)); 407 } 408 409 419 protected String getSystemIdForFileName(String fileName) { 420 InputStream fileInputStream = null; 421 try { 422 String urlPath = null; 423 424 File file = new File (fileName); if (!file.exists()) { 426 URL url = getURL(fileName); 427 if (url != null) { 428 urlPath = URLDecoder.decode(url.getPath()); 430 try { 431 fileInputStream = url.openStream(); 432 } catch (IOException ignore) { 433 } 434 } 435 } else { 436 try { 437 fileInputStream = new FileInputStream (file); 438 }catch (FileNotFoundException ignore) { 439 } 440 } 441 442 if (fileInputStream == null) { 443 getLog().debug("Unable to resolve '" + fileName + "' to full path, so using it as is for system id."); 444 return fileName; 445 } else { 446 return (urlPath != null) ? urlPath : file.getAbsolutePath(); 447 } 448 } finally { 449 try { 450 if (fileInputStream != null) { 451 fileInputStream.close(); 452 } 453 } catch (IOException ioe) { 454 getLog().warn("Error closing jobs file: " + fileName, ioe); 455 } 456 } 457 } 458 459 466 protected URL getURL(String fileName) { 467 return Thread.currentThread().getContextClassLoader().getResource(fileName); 468 } 469 470 479 public void processFile(String fileName, String systemId) 480 throws ValidationException, ParserConfigurationException , 481 SAXException , IOException , SchedulerException, 482 ClassNotFoundException , ParseException { 483 clearValidationExceptions(); 484 485 scheduledJobs.clear(); 486 jobsToSchedule.clear(); 487 calsToSchedule.clear(); 488 489 getLog().info("Parsing XML file: " + fileName + 490 " with systemId: " + systemId + 491 " validating: " + digester.getValidating() + 492 " validating schema: " + digester.getSchema()); 493 InputSource is = new InputSource (getInputStream(fileName)); 494 is.setSystemId(systemId); 495 digester.push(this); 496 digester.parse(is); 497 498 maybeThrowValidationException(); 499 } 500 501 510 public void processStream(InputStream stream, String systemId) 511 throws ValidationException, ParserConfigurationException , 512 SAXException , IOException , SchedulerException, 513 ClassNotFoundException , ParseException { 514 clearValidationExceptions(); 515 516 scheduledJobs.clear(); 517 jobsToSchedule.clear(); 518 calsToSchedule.clear(); 519 520 getLog().info("Parsing XML from stream with systemId: " + systemId + 521 " validating: " + digester.getValidating() + 522 " validating schema: " + digester.getSchema()); 523 InputSource is = new InputSource (stream); 524 is.setSystemId(systemId); 525 digester.push(this); 526 digester.parse(is); 527 528 maybeThrowValidationException(); 529 } 530 531 536 public void processFileAndScheduleJobs(Scheduler sched, 537 boolean overWriteExistingJobs) throws SchedulerException, Exception { 538 processFileAndScheduleJobs(QUARTZ_XML_FILE_NAME, sched, 539 overWriteExistingJobs); 540 } 541 542 549 public void processFileAndScheduleJobs(String fileName, Scheduler sched, 550 boolean overWriteExistingJobs) throws Exception { 551 processFileAndScheduleJobs(fileName, getSystemIdForFileName(fileName), sched, overWriteExistingJobs); 552 } 553 554 561 public void processFileAndScheduleJobs(String fileName, String systemId, 562 Scheduler sched, boolean overWriteExistingJobs) throws Exception { 563 schedLocal.set(sched); 564 try { 565 processFile(fileName, systemId); 566 scheduleJobs(getScheduledJobs(), sched, overWriteExistingJobs); 567 } finally { 568 schedLocal.set(null); 569 } 570 } 571 572 581 public void scheduleJobs(Map jobBundles, Scheduler sched, 582 boolean overWriteExistingJobs) throws Exception { 583 getLog().info("Scheduling " + jobsToSchedule.size() + " parsed jobs."); 584 585 Iterator itr = calsToSchedule.iterator(); 586 while (itr.hasNext()) { 587 CalendarBundle bndle = (CalendarBundle) itr.next(); 588 addCalendar(sched, bndle); 589 } 590 591 itr = jobsToSchedule.iterator(); 592 while (itr.hasNext()) { 593 JobSchedulingBundle bndle = (JobSchedulingBundle) itr.next(); 594 scheduleJob(bndle, sched, overWriteExistingJobs); 595 } 596 597 itr = listenersToSchedule.iterator(); 598 while (itr.hasNext()) { 599 JobListener listener = (JobListener) itr.next(); 600 getLog().info("adding listener "+listener.getName()+" of class "+listener.getClass().getName()); 601 sched.addJobListener(listener); 602 } 603 getLog().info(jobBundles.size() + " scheduled jobs."); 604 } 605 606 614 public Map getScheduledJobs() { 615 return Collections.unmodifiableMap(scheduledJobs); 616 } 617 618 625 public JobSchedulingBundle getScheduledJob(String name) { 626 return (JobSchedulingBundle) getScheduledJobs().get(name); 627 } 628 629 636 protected InputStream getInputStream(String fileName) { 637 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 638 639 InputStream is = cl.getResourceAsStream(fileName); 640 641 return is; 642 } 643 644 653 public void scheduleJob(JobSchedulingBundle job) 654 throws SchedulerException { 655 scheduleJob(job, (Scheduler) schedLocal.get(), getOverWriteExistingJobs()); 656 } 657 658 659 public void addJobToSchedule(JobSchedulingBundle job) { 660 jobsToSchedule.add(job); 661 } 662 663 public void addCalendarToSchedule(CalendarBundle cal) { 664 calsToSchedule.add(cal); 665 } 666 667 public void addListenerToSchedule(JobListener listener) { 668 listenersToSchedule.add(listener); 669 } 670 671 684 public void scheduleJob(JobSchedulingBundle job, Scheduler sched, boolean localOverWriteExistingJobs) 685 throws SchedulerException { 686 if ((job != null) && job.isValid()) { 687 JobDetail detail = job.getJobDetail(); 688 689 JobDetail dupeJ = sched.getJobDetail(detail.getName(), detail.getGroup()); 690 691 if ((dupeJ != null) && !localOverWriteExistingJobs) { 692 getLog().info("Not overwriting existing job: " + dupeJ.getFullName()); 693 return; 694 } 695 696 if (dupeJ != null) { 697 getLog().info("Replacing job: " + detail.getFullName()); 698 } else { 699 getLog().info("Adding job: " + detail.getFullName()); 700 } 701 702 if (job.getTriggers().size() == 0 && !job.getJobDetail().isDurable()) { 703 if (dupeJ == null) { 704 throw new SchedulerException( 705 "A new job defined without any triggers must be durable: " + 706 detail.getFullName()); 707 } 708 709 if ((dupeJ.isDurable() && 710 (sched.getTriggersOfJob( 711 detail.getName(), detail.getGroup()).length == 0))) { 712 throw new SchedulerException( 713 "Can't make a durable job without triggers non-durable: " + 714 detail.getFullName()); 715 } 716 } 717 718 sched.addJob(detail, true); 719 720 for (Iterator iter = job.getTriggers().iterator(); iter.hasNext(); ) { 721 Trigger trigger = (Trigger)iter.next(); 722 723 trigger.setJobName(detail.getName()); 724 trigger.setJobGroup(detail.getGroup()); 725 726 if(trigger.getStartTime() == null) { 727 trigger.setStartTime(new Date ()); 728 } 729 730 boolean addedTrigger = false; 731 while (addedTrigger == false) { 732 Trigger dupeT = sched.getTrigger(trigger.getName(), trigger.getGroup()); 733 if (dupeT != null) { 734 if (getLog().isDebugEnabled()) { 735 getLog().debug( 736 "Rescheduling job: " + detail.getFullName() + " with updated trigger: " + trigger.getFullName()); 737 } 738 if(!dupeT.getJobGroup().equals(trigger.getJobGroup()) || !dupeT.getJobName().equals(trigger.getJobName())) { 739 getLog().warn("Possibly duplicately named triggers in jobs xml file!"); 740 } 741 sched.rescheduleJob(trigger.getName(), trigger.getGroup(), trigger); 742 } else { 743 if (getLog().isDebugEnabled()) { 744 getLog().debug( 745 "Scheduling job: " + detail.getFullName() + " with trigger: " + trigger.getFullName()); 746 } 747 748 try { 749 sched.scheduleJob(trigger); 750 } catch (ObjectAlreadyExistsException e) { 751 if (getLog().isDebugEnabled()) { 752 getLog().debug( 753 "Adding trigger: " + trigger.getFullName() + " for job: " + detail.getFullName() + 754 " failed because the trigger already existed. " + 755 "This is likely due to a race condition between multiple instances " + 756 "in the cluster. Will try to reschedule instead."); 757 } 758 continue; 759 } 760 } 761 addedTrigger = true; 762 } 763 } 764 765 addScheduledJob(job); 766 } 767 } 768 769 775 protected void addScheduledJob(JobSchedulingBundle job) { 776 scheduledJobs.put(job.getFullName(), job); 777 } 778 779 786 public void addCalendar(Scheduler sched, CalendarBundle calendarBundle) throws SchedulerException { 787 sched.addCalendar( 788 calendarBundle.getCalendarName(), 789 calendarBundle.getCalendar(), 790 calendarBundle.getReplace(), 791 true); 792 } 793 794 833 public InputSource resolveEntity(String publicId, String systemId) { 834 InputSource inputSource = null; 835 836 InputStream is = null; 837 838 URL url = null; 839 840 try { 841 if (publicId == null) { 842 if (systemId != null) { 843 if (QUARTZ_SCHEMA.equals(systemId)) { 845 is = getClass().getResourceAsStream(QUARTZ_XSD); 846 } else { 847 is = getInputStream(systemId); 848 849 if (is == null) { 850 int start = systemId.indexOf(QUARTZ_SYSTEM_ID_PREFIX); 851 852 if (start > -1) { 853 String fileName = systemId 854 .substring(QUARTZ_SYSTEM_ID_PREFIX.length()); 855 is = getInputStream(fileName); 856 } else { 857 if (systemId.indexOf(':') == -1) { 858 File file = new java.io.File (systemId); 859 url = file.toURL(); 860 } else { 861 url = new URL (systemId); 862 } 863 864 is = url.openStream(); 865 } 866 } 867 } 868 } 869 } else { 870 if (QUARTZ_PUBLIC_ID.equals(publicId)) { 872 is = getClass().getResourceAsStream(QUARTZ_DTD); 873 } else { 874 url = new URL (systemId); 875 is = url.openStream(); 876 } 877 } 878 } catch (Exception e) { 879 e.printStackTrace(); 880 } finally { 881 if (is != null) { 882 inputSource = new InputSource (is); 883 inputSource.setPublicId(publicId); 884 inputSource.setSystemId(systemId); 885 } 886 887 } 888 889 return inputSource; 890 } 891 892 902 public void warning(SAXParseException e) throws SAXException { 903 addValidationException(e); 904 } 905 906 916 public void error(SAXParseException e) throws SAXException { 917 addValidationException(e); 918 } 919 920 930 public void fatalError(SAXParseException e) throws SAXException { 931 addValidationException(e); 932 } 933 934 940 protected void addValidationException(SAXException e) { 941 validationExceptions.add(e); 942 } 943 944 947 protected void clearValidationExceptions() { 948 validationExceptions.clear(); 949 } 950 951 958 protected void maybeThrowValidationException() throws ValidationException { 959 if (validationExceptions.size() > 0) { 960 throw new ValidationException(validationExceptions); 961 } 962 } 963 964 969 public class CalendarRuleSet extends RuleSetBase { 970 protected String prefix; 971 protected String setNextMethodName; 972 973 public CalendarRuleSet(String prefix, String setNextMethodName) { 974 super(); 975 this.prefix = prefix; 976 this.setNextMethodName = setNextMethodName; 977 } 978 979 public void addRuleInstances(Digester digester) { 980 digester.addObjectCreate(prefix, CalendarBundle.class); 981 digester.addSetProperties(prefix, TAG_CLASS_NAME, "className"); 982 digester.addBeanPropertySetter(prefix + "/" + TAG_NAME, "calendarName"); 983 digester.addBeanPropertySetter(prefix + "/" + TAG_DESCRIPTION, "description"); 984 digester.addSetNext(prefix, setNextMethodName); 985 } 986 } 987 988 993 public class TriggerRuleSet extends RuleSetBase { 994 protected String prefix; 995 protected Class clazz; 996 997 public TriggerRuleSet(String prefix, Class clazz) { 998 super(); 999 this.prefix = prefix; 1000 if (!Trigger.class.isAssignableFrom(clazz)) { 1001 throw new IllegalArgumentException ("Class must be an instance of Trigger"); 1002 } 1003 this.clazz = clazz; 1004 } 1005 1006 public void addRuleInstances(Digester digester) { 1007 digester.addObjectCreate(prefix, clazz); 1008 digester.addBeanPropertySetter(prefix + "/" + TAG_NAME, "name"); 1009 digester.addBeanPropertySetter(prefix + "/" + TAG_GROUP, "group"); 1010 digester.addBeanPropertySetter(prefix + "/" + TAG_DESCRIPTION, "description"); 1011 digester.addBeanPropertySetter(prefix + "/" + TAG_VOLATILITY, "volatility"); 1012 digester.addRule(prefix + "/" + TAG_MISFIRE_INSTRUCTION, new MisfireInstructionRule("misfireInstruction")); 1013 digester.addBeanPropertySetter(prefix + "/" + TAG_CALENDAR_NAME, "calendarName"); 1014 digester.addObjectCreate(prefix + "/" + TAG_JOB_DATA_MAP, JobDataMap.class); 1015 digester.addCallMethod(prefix + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY, "put", 2, new Class [] { Object .class, Object .class }); 1016 digester.addCallParam(prefix + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_KEY, 0); 1017 digester.addCallParam(prefix + "/" + TAG_JOB_DATA_MAP + "/" + TAG_ENTRY + "/" + TAG_VALUE, 1); 1018 digester.addSetNext(prefix + "/" + TAG_JOB_DATA_MAP, "setJobDataMap"); 1019 digester.addBeanPropertySetter(prefix + "/" + TAG_JOB_NAME, "jobName"); 1020 digester.addBeanPropertySetter(prefix + "/" + TAG_JOB_GROUP, "jobGroup"); 1021 Converter converter = new DateConverter(new String [] { XSD_DATE_FORMAT, DTD_DATE_FORMAT }); 1022 digester.addRule(prefix + "/" + TAG_START_TIME, new SimpleConverterRule("startTime", converter, Date .class)); 1023 digester.addRule(prefix + "/" + TAG_END_TIME, new SimpleConverterRule("endTime", converter, Date .class)); 1024 } 1025 } 1026 1027 1049 public class SimpleConverterRule extends BeanPropertySetterRule { 1050 private Converter converter; 1051 private Class clazz; 1052 1053 1060 public SimpleConverterRule(String propertyName, Converter converter, Class clazz) { 1061 this.propertyName = propertyName; 1062 if (converter == null) { 1063 throw new IllegalArgumentException ("Converter must not be null"); 1064 } 1065 this.converter = converter; 1066 if (clazz == null) { 1067 throw new IllegalArgumentException ("Class must not be null"); 1068 } 1069 this.clazz = clazz; 1070 } 1071 1072 1084 public void end(String namespace, String name) throws Exception { 1085 1086 String property = propertyName; 1087 1088 if (property == null) { 1089 property = name; 1092 } 1093 1094 Object top = this.digester.peek(); 1096 1097 if (getDigester().getLogger().isDebugEnabled()) { 1099 getDigester().getLogger().debug("[BeanPropertySetterRule]{" + getDigester().getMatch() + 1100 "} Set " + top.getClass().getName() + " property " + 1101 property + " with text " + bodyText); 1102 } 1103 1104 if (top instanceof DynaBean) { 1107 DynaProperty desc = 1108 ((DynaBean) top).getDynaClass().getDynaProperty(property); 1109 if (desc == null) { 1110 throw new NoSuchMethodException ("Bean has no property named " + property); 1111 } 1112 } else { 1113 PropertyDescriptor desc = 1114 PropertyUtils.getPropertyDescriptor(top, property); 1115 if (desc == null) { 1116 throw new NoSuchMethodException ("Bean has no property named " + property); 1117 } 1118 } 1119 1120 Object value = converter.convert(clazz, bodyText); 1122 PropertyUtils.setProperty(top, property, value); 1123 } 1124 } 1125 1126 1138 public class MisfireInstructionRule extends BeanPropertySetterRule { 1139 1144 public MisfireInstructionRule(String propertyName) { 1145 this.propertyName = propertyName; 1146 } 1147 1148 1158 public void body(String namespace, String name, String text) 1159 throws Exception { 1160 super.body(namespace, name, text); 1161 this.bodyText = getConstantValue(bodyText); 1162 } 1163 1164 1172 private String getConstantValue(String constantName) { 1173 String value = "0"; 1174 1175 Object top = this.digester.peek(); 1176 if (top != null) { 1177 Class clazz = top.getClass(); 1178 try { 1179 java.lang.reflect.Field field = clazz.getField(constantName); 1180 Object fieldValue = field.get(top); 1181 if (fieldValue != null) { 1182 value = fieldValue.toString(); 1183 } 1184 } catch (Exception e) { 1185 } 1187 } 1188 1189 return value; 1190 } 1191 } 1192 1193 1199 public final class DateConverter implements Converter { 1200 1201 1203 1207 public DateConverter() { 1208 this.defaultValue = null; 1209 this.useDefault = false; 1210 } 1211 1212 1218 public DateConverter(Object defaultValue) { 1219 this.defaultValue = defaultValue; 1220 this.useDefault = true; 1221 } 1222 1223 public DateConverter(String [] formats) { 1224 this(); 1225 1226 int len = formats.length; 1227 dateFormats = new DateFormat [len]; 1228 for (int i = 0; i < len; i++) { 1229 dateFormats[i] = new SimpleDateFormat (formats[i]); 1230 } 1231 } 1232 1233 1235 1238 private Object defaultValue = null; 1239 1240 1243 private boolean useDefault = true; 1244 1245 private DateFormat [] dateFormats; 1246 1247 1249 1259 public Object convert(Class type, Object value) { 1260 1261 if (value == null) { 1262 if (useDefault) { 1263 return (defaultValue); 1264 } else { 1265 return (null); 1266 } 1267 } 1268 1269 if (String .class.equals(type)) { 1270 if ((value instanceof Date ) && (dateFormats != null)) { 1271 return (dateFormats[0].format((Date ) value)); 1272 } else { 1273 return (value.toString()); 1274 } 1275 } 1276 1277 if (value instanceof Date ) { 1278 return (value); 1279 } 1280 1281 try { 1282 if (Date .class.isAssignableFrom(type) && dateFormats != null) { 1283 return parseDate(value); 1284 } else { 1285 return (value.toString()); 1286 } 1287 } catch (Exception e) { 1288 if (useDefault) { 1289 return (defaultValue); 1290 } else { 1291 throw new ConversionException(e); 1292 } 1293 } 1294 } 1295 1296 protected Date parseDate(Object value) throws ParseException { 1297 Date date = null; 1298 1299 int len = dateFormats.length; 1300 for (int i = 0; i < len; i++) { 1301 1302 try { 1303 date = (dateFormats[i].parse(value.toString())); 1304 break; 1305 } catch (ParseException e) { 1306 if (i == (len - 1)) { 1308 throw e; 1309 } 1310 } 1311 } 1312 1313 return date; 1314 } 1315 } 1316 1317 1322 public final class TimeZoneConverter implements Converter { 1323 1325 1329 public TimeZoneConverter() { 1330 } 1331 1332 1334 1344 public Object convert(Class type, Object value) { 1345 1346 if (value == null) { 1347 return (null); 1348 } 1349 1350 if (value instanceof TimeZone ) { 1351 return (value); 1352 } 1353 1354 try { 1355 if (String .class.equals(value.getClass())) { 1356 return (TimeZone.getTimeZone((String ) value)); 1357 } else { 1358 return (value.toString()); 1359 } 1360 } catch (Exception e) { 1361 throw new ConversionException(e); 1362 } 1363 } 1364 } 1365} 1366 | Popular Tags |