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 28c1c3b0cc8a CAMEL-24055: Fix custom calendar name collision in
QuartzEndpoint
28c1c3b0cc8a is described below
commit 28c1c3b0cc8a53a15f55d0a5db214950e74942f2
Author: Prasanth Rao <[email protected]>
AuthorDate: Tue Jul 14 21:38:31 2026 +0530
CAMEL-24055: Fix custom calendar name collision in QuartzEndpoint
Replace the hardcoded "CamelQuartzCustomCalendar" scheduler name with a
per-endpoint name derived from the trigger key (group + name). When two or
more routes each configured a customCalendar, the second
addCalendar(replace=true)
silently overwrote the first, causing all triggers to use the
last-registered
calendar instead of their own.
Custom calendars are now also deleted from the scheduler in doStop() to
prevent
stale entries accumulating in JDBC-backed job stores.
Closes #24670
Co-authored-by: Claude <[email protected]>
---
.../camel/component/quartz/QuartzEndpoint.java | 20 ++++--
.../quartz/QuartzCustomCalendarCollisionTest.java | 78 ++++++++++++++++++++++
.../quartz/QuartzCustomCalendarFireTest.java | 5 +-
.../quartz/QuartzCustomCalendarNoFireTest.java | 5 +-
4 files changed, 101 insertions(+), 7 deletions(-)
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 92b9dba5e2ac..7748cdc5d564 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
@@ -310,6 +310,10 @@ public class QuartzEndpoint extends DefaultEndpoint {
this.customCalendar = customCalendar;
}
+ private String customCalendarName() {
+ return QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR + "_" +
triggerKey.getGroup() + "_" + triggerKey.getName();
+ }
+
@Override
public Producer createProducer() throws Exception {
throw new UnsupportedOperationException("Quartz producer is not
supported.");
@@ -328,8 +332,7 @@ public class QuartzEndpoint extends DefaultEndpoint {
throw new IllegalArgumentException("Cannot have both options
deleteJob and pauseJob enabled");
}
if (ObjectHelper.isNotEmpty(customCalendar)) {
-
getComponent().getScheduler().addCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR,
customCalendar, true,
- false);
+ getComponent().getScheduler().addCalendar(customCalendarName(),
customCalendar, true, false);
}
addJobInScheduler();
}
@@ -352,6 +355,13 @@ public class QuartzEndpoint extends DefaultEndpoint {
scheduler.unscheduleJob(triggerKey);
jobAdded.set(false);
+ if (ObjectHelper.isNotEmpty(customCalendar)) {
+ try {
+ scheduler.deleteCalendar(customCalendarName());
+ } catch (SchedulerException e) {
+ LOG.warn("Could not delete calendar {}: {}",
customCalendarName(), e.getMessage(), e);
+ }
+ }
}
} else if (pauseJob) {
pauseTrigger();
@@ -501,7 +511,7 @@ public class QuartzEndpoint extends DefaultEndpoint {
.withSchedule(cronSchedule(cron)
.withMisfireHandlingInstructionFireAndProceed()
.inTimeZone(TimeZone.getTimeZone(timeZone)))
-
.modifiedByCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR);
+ .modifiedByCalendar(customCalendarName());
} else {
triggerBuilder
.withSchedule(cronSchedule(cron)
@@ -514,7 +524,7 @@ public class QuartzEndpoint extends DefaultEndpoint {
triggerBuilder
.withSchedule(cronSchedule(cron)
.withMisfireHandlingInstructionFireAndProceed())
-
.modifiedByCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR);
+ .modifiedByCalendar(customCalendarName());
} else {
triggerBuilder
.withSchedule(cronSchedule(cron)
@@ -547,7 +557,7 @@ public class QuartzEndpoint extends DefaultEndpoint {
triggerBuilder
.withSchedule(simpleSchedule().withMisfireHandlingInstructionFireNow()
.withRepeatCount(repeat).withIntervalInMilliseconds(interval))
-
.modifiedByCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR);
+ .modifiedByCalendar(customCalendarName());
} else {
triggerBuilder
.withSchedule(simpleSchedule().withMisfireHandlingInstructionFireNow()
diff --git
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarCollisionTest.java
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarCollisionTest.java
new file mode 100644
index 000000000000..65a51e77d5f4
--- /dev/null
+++
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarCollisionTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.util.Date;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.quartz.Calendar;
+import org.quartz.Scheduler;
+import org.quartz.impl.calendar.HolidayCalendar;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+
+/**
+ * Regression test: two endpoints with different customCalendars must each
register under a unique scheduler name so
+ * that the second addCalendar() does not overwrite the first.
+ */
+public class QuartzCustomCalendarCollisionTest extends BaseQuartzTest {
+
+ @BindToRegistry("calendarA")
+ public HolidayCalendar loadCalendarA() {
+ HolidayCalendar cal = new HolidayCalendar();
+ // Exclude today — this calendar should suppress triggers
+ cal.addExcludedDate(new Date());
+ return cal;
+ }
+
+ @BindToRegistry("calendarB")
+ public HolidayCalendar loadCalendarB() {
+ // No excluded dates — always-open calendar
+ return new HolidayCalendar();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+
from("quartz://TimerA?customCalendar=#calendarA&cron=05+00+00+*+*+?").routeId("routeA").to("mock:a");
+
from("quartz://TimerB?customCalendar=#calendarB&cron=05+00+00+*+*+?").routeId("routeB").to("mock:b");
+ }
+ };
+ }
+
+ @Test
+ public void testEachEndpointRegistersItsOwnCalendar() throws Exception {
+ QuartzComponent component = context.getComponent("quartz",
QuartzComponent.class);
+ Scheduler scheduler = component.getScheduler();
+
+ QuartzEndpoint endpointA = (QuartzEndpoint)
context.getRoute("routeA").getConsumer().getEndpoint();
+ QuartzEndpoint endpointB = (QuartzEndpoint)
context.getRoute("routeB").getConsumer().getEndpoint();
+
+ Calendar calA =
scheduler.getCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR + "_" +
endpointA.getGroupName()
+ + "_" +
endpointA.getTriggerName());
+ Calendar calB =
scheduler.getCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR + "_" +
endpointB.getGroupName()
+ + "_" +
endpointB.getTriggerName());
+
+ assertNotNull(calA, "Calendar for TimerA must be registered under its
own unique name");
+ assertNotNull(calB, "Calendar for TimerB must be registered under its
own unique name");
+ assertNotSame(calA, calB, "Each endpoint must register a distinct
calendar object");
+ }
+}
diff --git
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarFireTest.java
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarFireTest.java
index 5147f4562c5f..38036cb95df1 100644
---
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarFireTest.java
+++
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarFireTest.java
@@ -42,7 +42,10 @@ public class QuartzCustomCalendarFireTest extends
BaseQuartzTest {
QuartzComponent component = context.getComponent("quartz",
QuartzComponent.class);
Scheduler scheduler = component.getScheduler();
- Calendar c =
scheduler.getCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR);
+ QuartzEndpoint endpoint = (QuartzEndpoint)
context.getRoutes().get(0).getConsumer().getEndpoint();
+ String calendarName = QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR +
"_" + endpoint.getGroupName() + "_"
+ + endpoint.getTriggerName();
+ Calendar c = scheduler.getCalendar(calendarName);
Date now = new Date();
java.util.Calendar tomorrow = java.util.Calendar.getInstance();
tomorrow.setTime(now);
diff --git
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarNoFireTest.java
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarNoFireTest.java
index cd08198cecac..dbe806cfd19e 100644
---
a/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarNoFireTest.java
+++
b/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzCustomCalendarNoFireTest.java
@@ -42,7 +42,10 @@ public class QuartzCustomCalendarNoFireTest extends
BaseQuartzTest {
QuartzComponent component = context.getComponent("quartz",
QuartzComponent.class);
Scheduler scheduler = component.getScheduler();
- Calendar c =
scheduler.getCalendar(QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR);
+ QuartzEndpoint endpoint = (QuartzEndpoint)
context.getRoutes().get(0).getConsumer().getEndpoint();
+ String calendarName = QuartzConstants.QUARTZ_CAMEL_CUSTOM_CALENDAR +
"_" + endpoint.getGroupName() + "_"
+ + endpoint.getTriggerName();
+ Calendar c = scheduler.getCalendar(calendarName);
Date now = new Date();
java.util.Calendar tomorrow = java.util.Calendar.getInstance();
tomorrow.setTime(now);