Author: davsclaus Date: Fri Jan 7 14:22:08 2011 New Revision: 1056333 URL: http://svn.apache.org/viewvc?rev=1056333&view=rev Log: CAMEL-3239: quartz component now enforces trigger name/group being unique. This does not apply for clustered. Thanks to Tracy Snell for patch.
Added: camel/trunk/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzNameCollisionTest.java Modified: camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzComponent.java camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java Modified: camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzComponent.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzComponent.java?rev=1056333&r1=1056332&r2=1056333&view=diff ============================================================================== --- camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzComponent.java (original) +++ camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzComponent.java Fri Jan 7 14:22:08 2011 @@ -111,8 +111,19 @@ public class QuartzComponent extends Def Map<String, Object> triggerParameters = IntrospectionSupport.extractProperties(parameters, "trigger."); Map<String, Object> jobParameters = IntrospectionSupport.extractProperties(parameters, "job."); - // create the trigger either cron or simple Trigger trigger; + + // if we're starting up and not running in Quartz clustered mode then check for a name conflict. + if (!isClustered()) { + // check to see if this trigger already exists + trigger = getScheduler().getTrigger(name, group); + if (trigger != null) { + String msg = "A Quartz job already exists with the name/group: " + name + "/" + group; + throw new IllegalArgumentException(msg); + } + } + + // create the trigger either cron or simple if (ObjectHelper.isNotEmpty(cron)) { trigger = createCronTrigger(cron); } else { @@ -232,29 +243,37 @@ public class QuartzComponent extends Def } } - public void removeJob(JobDetail job, Trigger trigger) throws SchedulerException { + public void pauseJob(Trigger trigger) throws SchedulerException { JOBS.decrementAndGet(); if (isClustered()) { - // do not remove jobs which are clustered, as we want the jobs to continue running on the other nodes + // do not pause jobs which are clustered, as we want the jobs to continue running on the other nodes if (LOG.isDebugEnabled()) { - LOG.debug("Cannot removing job using trigger: " + trigger.getGroup() + "/" + trigger.getName() + " as the JobStore is clustered."); + LOG.debug("Cannot pause job using trigger: " + trigger.getGroup() + "/" + trigger.getName() + " as the JobStore is clustered."); } - return; + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Pausing job using trigger: " + trigger.getGroup() + "/" + trigger.getName()); + } + getScheduler().pauseTrigger(trigger.getName(), trigger.getGroup()); + getScheduler().pauseJob(trigger.getName(), trigger.getGroup()); } + } - // only unschedule volatile jobs - if (job.isVolatile()) { + public void deleteJob(String name, String group) throws SchedulerException { + if (isClustered()) { + // do not pause jobs which are clustered, as we want the jobs to continue running on the other nodes if (LOG.isDebugEnabled()) { - LOG.debug("Removing job using trigger: " + trigger.getGroup() + "/" + trigger.getName()); + LOG.debug("Cannot delete job using trigger: " + group + "/" + name + " as the JobStore is clustered."); } - getScheduler().unscheduleJob(trigger.getName(), trigger.getGroup()); } else { - // but pause jobs so we can resume them if the application restarts - if (LOG.isDebugEnabled()) { - LOG.debug("Pausing job using trigger: " + trigger.getGroup() + "/" + trigger.getName()); + Trigger trigger = getScheduler().getTrigger(name, group); + if (trigger != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Deleting job using trigger: " + group + "/" + name); + } + getScheduler().unscheduleJob(name, group); } - getScheduler().pauseTrigger(trigger.getName(), trigger.getGroup()); } } Modified: camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java?rev=1056333&r1=1056332&r2=1056333&view=diff ============================================================================== --- camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java (original) +++ camel/trunk/components/camel-quartz/src/main/java/org/apache/camel/component/quartz/QuartzEndpoint.java Fri Jan 7 14:22:08 2011 @@ -21,7 +21,7 @@ import java.util.Date; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.Producer; -import org.apache.camel.Service; +import org.apache.camel.ShutdownableService; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.impl.ServiceSupport; import org.apache.camel.processor.loadbalancer.LoadBalancer; @@ -42,7 +42,7 @@ import org.quartz.Trigger; * * @version $Revision:520964 $ */ -public class QuartzEndpoint extends DefaultEndpoint implements Service { +public class QuartzEndpoint extends DefaultEndpoint implements ShutdownableService { private static final transient Log LOG = LogFactory.getLog(QuartzEndpoint.class); private LoadBalancer loadBalancer; @@ -80,8 +80,12 @@ public class QuartzEndpoint extends Defa getComponent().addJob(detail, trigger); } - public void removeTrigger(final Trigger trigger, final JobDetail detail) throws SchedulerException { - getComponent().removeJob(detail, trigger); + public void pauseTrigger(final Trigger trigger) throws SchedulerException { + getComponent().pauseJob(trigger); + } + + public void deleteTrigger(final Trigger trigger) throws SchedulerException { + getComponent().deleteJob(trigger.getName(), trigger.getGroup()); } /** @@ -218,7 +222,7 @@ public class QuartzEndpoint extends Defa public synchronized void consumerStopped(final QuartzConsumer consumer) throws SchedulerException { ObjectHelper.notNull(trigger, "trigger"); if (started) { - removeTrigger(getTrigger(), getJobDetail()); + pauseTrigger(getTrigger()); started = false; } @@ -245,4 +249,8 @@ public class QuartzEndpoint extends Defa ServiceHelper.stopService(loadBalancer); } + public void shutdown() throws Exception { + ObjectHelper.notNull(trigger, "trigger"); + deleteTrigger(getTrigger()); + } } Added: camel/trunk/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzNameCollisionTest.java URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzNameCollisionTest.java?rev=1056333&view=auto ============================================================================== --- camel/trunk/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzNameCollisionTest.java (added) +++ camel/trunk/components/camel-quartz/src/test/java/org/apache/camel/component/quartz/QuartzNameCollisionTest.java Fri Jan 7 14:22:08 2011 @@ -0,0 +1,138 @@ +/** + * 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 org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.quartz.Scheduler; +import org.quartz.Trigger; + +/** + * Check for duplicate name/group collision. + */ +public class QuartzNameCollisionTest { + private DefaultCamelContext camel1; + private DefaultCamelContext camel2; + + @Test + public void testDupeName() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + QuartzComponent component2 = new QuartzComponent(camel2); + + try { + component2.createEndpoint("quartz://myGroup/myTimerName"); + Assert.fail("Should have thrown an exception"); + } catch (IllegalArgumentException e) { + Assert.assertEquals("A Quartz job already exists with the name/group: myTimerName/myGroup", e.getMessage()); + } + } + + + /** + * Make sure a resume doesn't trigger a dupe name error. + */ + @Test + public void testRestart() throws Exception { + DefaultCamelContext camel = new DefaultCamelContext(); + + camel.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + + // traverse a litany of states + camel.start(); + Thread.sleep(100); + camel.suspend(); + Thread.sleep(100); + camel.resume(); + Thread.sleep(100); + camel.stop(); + Thread.sleep(100); + camel.start(); + Thread.sleep(100); + camel.stop(); + } + + + /** + * Confirm the quartz trigger is removed on route stop. + */ + @Test + public void testRemoveJob() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + camel2.setName("camel-2"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz://myGroup2/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel2.start(); + + QuartzComponent component = (QuartzComponent) camel1.getComponent("quartz"); + Scheduler scheduler = component.getScheduler(); + Trigger trigger = scheduler.getTrigger("myTimerName", "myGroup"); + Assert.assertNotNull(trigger); + + camel1.stop(); + + trigger = scheduler.getTrigger("myTimerName", "myGroup"); + Assert.assertNull(trigger); + + camel2.stop(); + } + + @After + public void cleanUp() throws Exception { + if (camel1 != null) { + camel1.stop(); + camel1 = null; + } + + if (camel2 != null) { + camel2.stop(); + camel2 = null; + } + } + +}