This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24079 in repository https://gitbox.apache.org/repos/asf/camel.git
commit cd7e748a8d84ceb77cb2375e127690819f1092ea Author: Claus Ibsen <[email protected]> AuthorDate: Wed Jul 15 12:58:09 2026 +0200 CAMEL-24079: Fix QuartzScheduledPollConsumerScheduler ignoring startScheduler=false Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../QuartzScheduledPollConsumerScheduler.java | 74 +++++++++++----------- ...eConsumerQuartzSchedulerStartSchedulerTest.java | 56 ++++++++++++++++ 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/components/camel-quartz/src/main/java/org/apache/camel/pollconsumer/quartz/QuartzScheduledPollConsumerScheduler.java b/components/camel-quartz/src/main/java/org/apache/camel/pollconsumer/quartz/QuartzScheduledPollConsumerScheduler.java index 23e11316d1ee..e3c87949fbfe 100644 --- a/components/camel-quartz/src/main/java/org/apache/camel/pollconsumer/quartz/QuartzScheduledPollConsumerScheduler.java +++ b/components/camel-quartz/src/main/java/org/apache/camel/pollconsumer/quartz/QuartzScheduledPollConsumerScheduler.java @@ -71,6 +71,8 @@ public class QuartzScheduledPollConsumerScheduler extends ServiceSupport private boolean deleteJob = true; private volatile CronTrigger trigger; private volatile JobDetail job; + private volatile boolean scheduled; + private volatile TriggerKey rescheduleKey; @Override public void onInit(Consumer consumer) { @@ -100,16 +102,43 @@ public class QuartzScheduledPollConsumerScheduler extends ServiceSupport @Override public void startScheduler() { - // the quartz component starts the scheduler + if (!scheduled) { + try { + if (rescheduleKey != null) { + LOG.debug("Re-scheduling job: {} with trigger: {}", job, trigger.getKey()); + quartzScheduler.rescheduleJob(rescheduleKey, trigger); + } else { + LOG.debug("Scheduling job: {} with trigger: {}", job, trigger.getKey()); + try { + quartzScheduler.scheduleJob(job, trigger); + } catch (ObjectAlreadyExistsException ex) { + QuartzComponent quartz = getCamelContext().getComponent("quartz", QuartzComponent.class); + if (!(quartz.isClustered())) { + throw ex; + } else { + TriggerKey triggerKey = trigger.getKey(); + trigger = (CronTrigger) quartzScheduler.getTrigger(triggerKey); + if (trigger == null) { + throw new SchedulerException("Trigger could not be found in quartz scheduler."); + } + } + } + } + scheduled = true; + if (LOG.isInfoEnabled()) { + LOG.info("Job {} (triggerType={}, jobClass={}) is scheduled. Next fire date is {}", + trigger.getKey(), trigger.getClass().getSimpleName(), + job.getJobClass().getSimpleName(), trigger.getNextFireTime()); + } + } catch (SchedulerException e) { + throw RuntimeCamelException.wrapRuntimeCamelException(e); + } + } } @Override public boolean isSchedulerStarted() { - try { - return quartzScheduler != null && quartzScheduler.isStarted(); - } catch (SchedulerException e) { - return false; - } + return scheduled; } @Override @@ -244,9 +273,6 @@ public class QuartzScheduledPollConsumerScheduler extends ServiceSupport LOG.debug("Setting user extra triggerParameters {}", copy); PropertyBindingSupport.bindProperties(camelContext, trigger, copy); } - - LOG.debug("Scheduling job: {} with trigger: {}", job, trigger.getKey()); - quartzScheduler.scheduleJob(job, trigger); } else { checkTriggerIsNonConflicting(existingTrigger); @@ -264,36 +290,10 @@ public class QuartzScheduledPollConsumerScheduler extends ServiceSupport .withSchedule(CronScheduleBuilder.cronSchedule(getCron()).inTimeZone(getTimeZone())) .build(); - // Reschedule job if trigger settings were changed if (hasTriggerChanged(existingTrigger, trigger)) { - LOG.debug("Re-scheduling job: {} with trigger: {}", job, trigger.getKey()); - quartzScheduler.rescheduleJob(triggerKey, trigger); - } else { - // Schedule it now. Remember that scheduler might not be started it, but we can schedule now. - LOG.debug("Scheduling job: {} with trigger: {}", job, trigger.getKey()); - try { - // Schedule it now. Remember that scheduler might not be started it, but we can schedule now. - quartzScheduler.scheduleJob(job, trigger); - } catch (ObjectAlreadyExistsException ex) { - // some other VM might may have stored the job & trigger in DB in clustered mode, in the mean time - QuartzComponent quartz = getCamelContext().getComponent("quartz", QuartzComponent.class); - if (!(quartz.isClustered())) { - throw ex; - } else { - trigger = (CronTrigger) quartzScheduler.getTrigger(triggerKey); - if (trigger == null) { - throw new SchedulerException("Trigger could not be found in quartz scheduler."); - } - } - } + rescheduleKey = triggerKey; } } - - if (LOG.isInfoEnabled()) { - LOG.info("Job {} (triggerType={}, jobClass={}) is scheduled. Next fire date is {}", - trigger.getKey(), trigger.getClass().getSimpleName(), - job.getJobClass().getSimpleName(), trigger.getNextFireTime()); - } } @Override @@ -313,6 +313,8 @@ public class QuartzScheduledPollConsumerScheduler extends ServiceSupport quartzScheduler.unscheduleJob(trigger.getKey()); } } + scheduled = false; + rescheduleKey = null; } private void checkTriggerIsNonConflicting(Trigger trigger) { diff --git a/components/camel-quartz/src/test/java/org/apache/camel/pollconsumer/quartz/FileConsumerQuartzSchedulerStartSchedulerTest.java b/components/camel-quartz/src/test/java/org/apache/camel/pollconsumer/quartz/FileConsumerQuartzSchedulerStartSchedulerTest.java new file mode 100644 index 000000000000..b70689d1b328 --- /dev/null +++ b/components/camel-quartz/src/test/java/org/apache/camel/pollconsumer/quartz/FileConsumerQuartzSchedulerStartSchedulerTest.java @@ -0,0 +1,56 @@ +/* + * 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.pollconsumer.quartz; + +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.apache.camel.test.junit6.TestSupport; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.awaitility.Awaitility.await; + +public class FileConsumerQuartzSchedulerStartSchedulerTest extends CamelTestSupport { + @TempDir + Path testDirectory; + + @Test + public void testStartSchedulerFalseMustNotPoll() throws Exception { + template.sendBodyAndHeader(TestSupport.fileUri(testDirectory), "Hello World", Exchange.FILE_NAME, "hello.txt"); + + getMockEndpoint("mock:result").expectedMessageCount(0); + + // startScheduler=false should prevent polling even with scheduler=quartz + await().during(3, TimeUnit.SECONDS).atMost(4, TimeUnit.SECONDS) + .untilAsserted(() -> getMockEndpoint("mock:result").assertIsSatisfied()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from(TestSupport.fileUri(testDirectory, "?scheduler=quartz&scheduler.cron=0/2+*+*+*+*+?&startScheduler=false")) + .to("mock:result"); + } + }; + } +}
