This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-3.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-3.x by this push:
new b2f0e66c77b CAMEL-20115: Support for Start Date and End Date in
camel-quartz (#12098)
b2f0e66c77b is described below
commit b2f0e66c77b11aa6c1c6af13c002588290c631e4
Author: Prasanth Rao <[email protected]>
AuthorDate: Fri Nov 24 19:38:03 2023 +0530
CAMEL-20115: Support for Start Date and End Date in camel-quartz (#12098)
* CAMEL-20115: Support for Start Date and End Date in camel-quartz
* Fix for exact match in CamelJob for fireTime
* Move to Date with Timezone format
* Updated adoc and removed string concatenation from Test
---------
Co-authored-by: Prasanth Rao <[email protected]>
---
.../src/main/docs/quartz-component.adoc | 21 +++++++
.../apache/camel/component/quartz/CamelJob.java | 30 ++++++++++
.../camel/component/quartz/QuartzEndpoint.java | 35 +++++++++---
.../QuartzCronRouteWithStartDateEndDateTest.java | 65 ++++++++++++++++++++++
4 files changed, 143 insertions(+), 8 deletions(-)
diff --git a/components/camel-quartz/src/main/docs/quartz-component.adoc
b/components/camel-quartz/src/main/docs/quartz-component.adoc
index 28ba9e8522a..03ffb9507e2 100644
--- a/components/camel-quartz/src/main/docs/quartz-component.adoc
+++ b/components/camel-quartz/src/main/docs/quartz-component.adoc
@@ -163,6 +163,27 @@
quartz://groupName/timerName?cron=0+0/5+12-18+?+*+MON-FRI&trigger.timeZone=Europ
The timeZone value is the values accepted by `java.util.TimeZone`.
+== Specifying start date
+
+The Quartz Scheduler allows you to configure start date per trigger. You can
provide the start date
+in the date format yyyy-MM-dd'T'HH:mm:ssz.
+
+----
+quartz://groupName/timerName?cron=0+0/5+12-18+?+*+MON-FRI&trigger.startAt=2023-11-22T14:32:36UTC
+----
+
+== Specifying end date
+
+The Quartz Scheduler allows you to configure end date per trigger. You can
provide the end date
+in the date format yyyy-MM-dd'T'HH:mm:ssz.
+
+----
+quartz://groupName/timerName?cron=0+0/5+12-18+?+*+MON-FRI&trigger.endAt=2023-11-22T14:32:36UTC
+----
+
+Note: Start and end dates may be affected by time drifts and unpredictable
behavior during
+daylight saving time changes. Exercise caution, especially in environments
where precise timing is critical.
+
== Configuring misfire instructions
The quartz scheduler can be configured with a misfire instruction
diff --git
a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/CamelJob.java
b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/CamelJob.java
index 13e3181dca3..4587513c6e1 100644
---
a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/CamelJob.java
+++
b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/CamelJob.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.quartz;
import java.util.Collection;
+import java.util.Date;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
@@ -53,6 +54,12 @@ public class CamelJob implements Job, InterruptableJob {
public void execute(JobExecutionContext context) throws
JobExecutionException {
Exchange exchange = null;
try {
+ if (hasTriggerExpired(context)) {
+ LOG.warn("Trigger exists outside StartTime={} and EndTime={}.
Skipping CamelJob jobExecutionContext={}",
+ context.getTrigger().getStartTime(),
context.getTrigger().getEndTime(), context);
+ return;
+ }
+
if (LOG.isDebugEnabled()) {
LOG.debug("Running CamelJob jobExecutionContext={}", context);
}
@@ -94,6 +101,29 @@ public class CamelJob implements Job, InterruptableJob {
}
}
+ /**
+ * Validates if the Fire Time lies within the Start Time and End Time
+ *
+ * @param context
+ *
+ * @return
+ */
+ private boolean hasTriggerExpired(JobExecutionContext context) {
+ Date fireTime = context.getFireTime();
+
+ // Trigger valid if Start Time is null or before Fire Time
+ Date startTime = context.getTrigger().getStartTime();
+ boolean validStartTime
+ = context.getTrigger().getStartTime() == null ||
fireTime.equals(startTime) || fireTime.after(startTime);
+
+ // Trigger valid if End Time is null or after Fire Time
+ Date endTime = context.getTrigger().getEndTime();
+ boolean validEndTime
+ = context.getTrigger().getEndTime() == null ||
fireTime.equals(endTime) || fireTime.before(endTime);
+
+ return !(validStartTime && validEndTime);
+ }
+
protected CamelContext getCamelContext(JobExecutionContext context) throws
JobExecutionException {
SchedulerContext schedulerContext = getSchedulerContext(context);
String camelContextName =
context.getMergedJobDataMap().getString(QuartzConstants.QUARTZ_CAMEL_CONTEXT_NAME);
diff --git
a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java
b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java
index 1f905afa12f..6f242e14273 100644
---
a/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java
+++
b/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.component.quartz;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@@ -382,14 +383,7 @@ public class QuartzEndpoint extends DefaultEndpoint {
}
} else {
try {
- // calculate whether the trigger can be triggered in the future
- Calendar cal = null;
- if (trigger.getCalendarName() != null) {
- cal = scheduler.getCalendar(trigger.getCalendarName());
- }
- OperableTrigger ot = (OperableTrigger) trigger;
- Date ft = ot.computeFirstFireTime(cal);
- if (ft == null && ignoreExpiredNextFireTime) {
+ if (hasTriggerExpired(scheduler, trigger)) {
scheduled = false;
LOG.warn(
"Job {} (cron={}, triggerType={}, jobClass={}) not
scheduled, because it will never fire in the future",
@@ -433,6 +427,22 @@ public class QuartzEndpoint extends DefaultEndpoint {
jobAdded.set(true);
}
+ private boolean hasTriggerExpired(Scheduler scheduler, Trigger trigger)
throws SchedulerException {
+ Calendar cal = null;
+ if (trigger.getCalendarName() != null) {
+ cal = scheduler.getCalendar(trigger.getCalendarName());
+ }
+ OperableTrigger ot = (OperableTrigger) trigger;
+
+ // check if current time is past the Trigger EndDate
+ if (ot.getEndTime() != null && new Date().after(ot.getEndTime())) {
+ return true;
+ }
+ // calculate whether the trigger can be triggered in the future
+ Date ft = ot.computeFirstFireTime(cal);
+ return (ft == null && ignoreExpiredNextFireTime);
+ }
+
private boolean hasTriggerChanged(Trigger oldTrigger, Trigger newTrigger) {
if (newTrigger instanceof CronTrigger && oldTrigger instanceof
CronTrigger) {
CronTrigger newCron = (CronTrigger) newTrigger;
@@ -471,6 +481,15 @@ public class QuartzEndpoint extends DefaultEndpoint {
}
if (cron != null) {
LOG.debug("Creating CronTrigger: {}", cron);
+ final String startAt = (String) copy.get("startAt");
+ SimpleDateFormat dateFormat = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
+ if (startAt != null) {
+ triggerBuilder.startAt(dateFormat.parse(startAt));
+ }
+ final String endAt = (String) copy.get("endAt");
+ if (endAt != null) {
+ triggerBuilder.endAt(dateFormat.parse(endAt));
+ }
final String timeZone = (String) copy.get("timeZone");
if (timeZone != null) {
if (ObjectHelper.isNotEmpty(customCalendar)) {
diff --git
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCronRouteWithStartDateEndDateTest.java
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCronRouteWithStartDateEndDateTest.java
new file mode 100644
index 00000000000..b39084a2073
--- /dev/null
+++
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCronRouteWithStartDateEndDateTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.quartz;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.hamcrest.CoreMatchers;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * This test the CronTrigger as a timer endpoint in a route.
+ */
+public class QuartzCronRouteWithStartDateEndDateTest extends BaseQuartzTest {
+
+ @Test
+ public void testQuartzCronRouteWithStartDateEndDateTest() throws Exception
{
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMinimumMessageCount(2);
+ mock.await(5, TimeUnit.SECONDS);
+
+ MockEndpoint.assertIsSatisfied(context);
+ assertThat(mock.getReceivedExchanges().size() <= 3,
CoreMatchers.is(true));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ SimpleDateFormat dateFormat = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+ calendar.add(Calendar.SECOND, 3);
+ Date startDate = calendar.getTime();
+ calendar.add(Calendar.SECOND, 2);
+ Date endDate = calendar.getTime();
+
+ // triggers every 1th second at precise 00,01,02,03..59 with
startAt and endAt exactly 2 second apart.
+ // configuration will create a maximum of three messages
+ fromF("quartz://myGroup/myTimerName?cron=0/1 * * * *
?&trigger.startAt=%s&trigger.endAt=%s",
+ dateFormat.format(startDate),
dateFormat.format(endDate)).to("mock:result");
+ }
+ };
+ }
+}