Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
[QTZ-252] Provide rfc2445 format to represent recurrence rules of triggers Created: 28/Nov/11 Updated: 07/Jan/14 Status: Project: Component/s: Affects Version/s: Fix Version/s: New Quartz Scheduler (Historical - Do Not File New Issues Here - See GitHub) Triggers 2.2 Type: Reporter: Resolution: Labels: Remaining Estimate: Time Spent: Original Estimate: New Feature Philip Weber Unresolved None Not Specified Attachments: quartz-rfc5545_20131212.zip quartz-rfc5545_20131213.zip rfc5545.zip Pending Terracotta Target: 3.0 Priority: Assignee: Votes: 2 Major James House 8 Not Specified Not Specified quartz-rfc5545_20131212.zip quartz-rfc5545.zip quartz- Description Quartz scheduler does provide the creation of a trigger by cronSchedule like this: trigger = newTrigger() .withIdentity("trigger3", "group1") .startNow() .withSchedule(cronSchedule("0 0 15 ? * WED")) // fire every wednesday at 15:00 .build(); Because it is not possible to represent every recurrence rule of triggers by CronTrigger as described in http://www.quartz-scheduler.org/documentation/quartz2.1.x/cookbook/BiDailyTrigger it would be nice to have support for rfc2445 format that should cover every possible rule. Please have a look at tools.ietf.org/html/rfc2445#section-4.3.10 for further information. Example: trigger = newTrigger() .withIdentity("trigger3", "group1") .startAt(todayAt(15, 0, 0)) // at 15:00 .withSchedule(rfcSchedule("RRULE:FREQ=WEEKLY;BYDAY=WE")) // fire every wednesday .build(); Concerning persistence of a trigger, it also would be useful to have an export to rfc2445 format. Comments Comment by Nestor Urquiza [ 08/Apr/13 ] Hi, This is marked as "Fix Version 3.0" however I do not see a branch for that version in http://svn.terracotta.org/svn starting this feature? This is a highly needed feature as far as I can tell. Thanks! Nestor Comment by James House [ 08/Apr/13 ] This feature is unlikely to be worked on in the next calendar year - unless a community member wants to donate Comment by Nestor Urquiza [ 08/Apr/13 ] Thanks you James for the fast response. http://tools.ietf.org/html/rfc5545 has made obsolete rfc2445, then there http://sourceforge.net/projects/ical4j/ which could be used to parse ics files. You can filter events through http://wiki.modularity.net.au/ical4j/index.php?title=Examples#Filtering_events so matches the ICS entry. So I guess there are two questions: 1. Will the ical4j license (http://build.mnode.org/projects/ical4j/license.html) be OK to become a candidate for a 2. As a raw estimate how long would it take to use Filtering Events from Quartz in a way that a stored RRC2445 Thanks again for your feedback, Nestor Comment by Olivier Queyrut [ 19/Jun/13 ] Hi, I am new in this community but very interested in the Quartz features. For one of my project, I'd like to have a trigger based on the recurrence rule as defined by RFC 5545 because I fi expression. I tried to use ical4j but the results are not those I expected (from my point of view and according to the tests I did algorithm implemented in the Recur class). So, I decided to implement the RFC 5545 "RRULE" from scratch. I also implemented a trigger named Recurren Here is the result of my job that I can donate if you are interested in ! Please send me your feedbacks. Comment by James House [ 20/Jun/13 ] Thanks for your participation! To consider acceptance of your contribution, we would need you to fill-out and return a contributor's agreement. http://www.terracotta.org/confluence/download/attachments/27918462/Individual+Contributor+Agreement.pdf? Comment by Olivier Queyrut [ 21/Jun/13 ] Hi, Since Wednesday, I have done some other tests of the RecurrenceRuleTrigger and I have fixed a few bugs. Now The updated source code is attached. Regards, Olivier Comment by Narayanan Raghuvaran [ 06/Dec/13 ] Hi Olivier Thanks a million for the source code for recurrence rules. Howevere when I tried to save the recurrence rule to Q exception. The recurrence rule is "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU" Please help and the exception I get is org.quartz.JobPersistenceException: Couldn't store trigger 'DEFAULT.6da64b5bd2ee'DEFAULT. A Simple Task_junituser_1_1_1393448440' job:org.quartz.RecurrenceRule$ByDay [See nested ex org.quartz.RecurrenceRule$ByDay] at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1212) at org.quartz.impl.jdbcjobstore.JobStoreSupport$2.execute(JobStoreSupport.java:1052) at org.quartz.impl.jdbcjobstore.JobStoreSupport$45.execute(JobStoreSupport.java:3726) at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:245) at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3722) at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJobAndTrigger(JobStoreSupport.java:1047) at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:840) at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:250) at com.riverbed.ucx.job.service.JobServiceImpl._submitJob(JobServiceImpl.java:180) at com.riverbed.ucx.job.service.JobServiceImpl.submitJob(JobServiceImpl.java:103) at com.riverbed.ucx.job.service.JobServiceImpl.submitJob(JobServiceImpl.java:876) at com.riverbed.ucx.job.service.JobServiceImpl$$FastClassByCGLIB$$cb8d23b2.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy. at com.riverbed.ucx.job.service.JobServiceImpl$$EnhancerByCGLIB$$c22adc9d.submitJob(<generated>) at com.riverbed.ucx.scheduler.service.TaskServiceImpl.addTask(TaskServiceImpl.java:176) at com.riverbed.ucx.scheduler.service.TaskServiceImpl$$FastClassByCGLIB$$fff0d8b4.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProx at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java: at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java: at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy. at com.riverbed.ucx.scheduler.service.TaskServiceImpl$$EnhancerByCGLIB$$38b6c35f.addTask(<generated> at com.riverbed.ucx.scheduler.service.TaskServiceTest.testCreateTask(TaskServiceTest.java:367) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestM at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMe at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:23 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestCla at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassC at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: java.io.NotSerializableException: org.quartz.RecurrenceRule$ByDay at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346) at java.util.ArrayList.writeObject(ArrayList.java:710) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:975) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1480) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346) at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.insertBlobTrigger(StdJDBCDelegate.java:1131) at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.insertTrigger(StdJDBCDelegate.java:1100) at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1209) ... 54 more Comment by Olivier Queyrut [ 11/Dec/13 ] Hello, I have updated the source code so that the RecurrenceRule class is properly serialized / deserialized. Unfortunately, I haven't a test case with a database. Could you please try the updated version and send me a feedback ? Question to the quartz team : any idea when this feature could be integrated in the Quartz library ? Thanks, Olivier Comment by Olivier Queyrut [ 11/Dec/13 ] Erratum with previous zip file. Use this one. Sorry for inconvenience. Comment by Narayanan Raghuvaran [ 12/Dec/13 ] Hi Olivier Thanks a million for the help so far. With the change, serialization of by day works, but deserialization of any re cannot be deserialized i.e. when I call nextFireTime, it is failing for any recurrence rule (not just by day) We are Raghu Comment by Narayanan Raghuvaran [ 12/Dec/13 ] So the serialization issues are with Daily, recur every week day weekly i.e Monday, Tuesday etc Monthly every first weekday, first sunday of the month etc Comment by Narayanan Raghuvaran [ 12/Dec/13 ] Olivier Here is what I found out, when I deserialize , the vaidateRecurreneRule gets called and then it calls isNumericVa returns a list. It is suppossed to return a empty list, but since dayList is transient now, it is set to null, so we are g Comment by Narayanan Raghuvaran [ 12/Dec/13 ] Hi Oliver Thanks for your help so far. I fixed the issue. In readObject of RecurrenceRule, I am initializing the collections i transient keyword fix for byday Here is the code private void initCollections() { if (secondList == null) { secondList = new BoundedIntegerList(MIN_SECOND, MAX_SECOND,false); } if (minuteList == null) { minuteList = new BoundedIntegerList(MIN_MINUTE, MAX_MINUTE,false); } if (hourList == null) { hourList = new BoundedIntegerList(MIN_HOUR, MAX_HOUR, false); } if (dayList == null) { dayList = new ArrayList<ByDay>(); } if (monthDayList == null) { monthDayList = new BoundedIntegerList(MIN_MONTHDAY, MAX_MONTHDAY, true); } if (yearDayList == null) { yearDayList = new BoundedIntegerList(MIN_YEARDAY, MAX_YEARDAY, true); } if (weekNoList == null) { weekNoList = new BoundedIntegerList(MIN_WEEKNO, MAX_WEEKNO, true); } if (monthList == null) { monthList = new BoundedIntegerList(MIN_MONTH, MAX_MONTH, false); } if (setPosList == null) { setPosList = new BoundedIntegerList(MIN_SETPOS, MAX_SETPOS, true); } } private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); initCollections(); try { buildRecurrenceRule(recurrenceRuleExpr); } catch (ParseException ignore) { // never happens } } Comment by Olivier Queyrut [ 13/Dec/13 ] Hi Raghu, Thanks a lot for your help debugging the code. I have just posted an update of the source code with an extra junit test that verify serialization/deserialization of Regards, Olivier Comment by Chaitra Suresh [ 23/Dec/13 ] Hi, There is a getTimesTriggered() on a SimpleTrigger. But I don't see such a method on RecurrenceTrigger. I want rule trigger fired. Is there a way to find that out? Thanks, Chaitra Comment by Olivier Queyrut [ 07/Jan/14 ] Hi, Currently there is no way to get the number of times the trigger has fired. This is probably a feature that lacks... b Probably, it would also be useful to extend the RecurrenceRuleTrigger behavior upon a mis-fire situation, such a MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT, MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT, MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT, MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT. This is not so e RecurrenceRule. As soon as I have time, I take a look at it ! Regards, Olivier Generated at Sat Apr 29 12:36:14 PDT 2017 using JIRA 6.2.4#6261sha1:4d2e6f6f26064845673c8e7ffe9b6b84b45a6e79.