This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 2a90c154861 CAMEL-20115: Support for Start Date and End Date in 
camel-quartz (#12098)
2a90c154861 is described below

commit 2a90c1548615e8652996864a9993a896dd9e908f
Author: Prasanth Rao <uvce.prasa...@gmail.com>
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 <prasanth....@sap.com>
---
 .../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 85b42b1f984..f97c3b52539 100644
--- a/components/camel-quartz/src/main/docs/quartz-component.adoc
+++ b/components/camel-quartz/src/main/docs/quartz-component.adoc
@@ -164,6 +164,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 cd2297a5b45..667ca8c4fad 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");
+            }
+        };
+    }
+}

Reply via email to