From 45b81c6d95f46fb69aee01a02e0c9ee56a25d182 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Fri, 26 Jul 2013 19:57:34 +0100 Subject: [PATCH 01/16] Added camel-quartz2 component. --- components/camel-quartz2/pom.xml | 107 ++++++ .../camel/component/quartz2/CamelJob.java | 95 ++++++ .../component/quartz2/QuartzComponent.java | 309 ++++++++++++++++++ .../component/quartz2/QuartzConstants.java | 35 ++ .../component/quartz2/QuartzConsumer.java | 11 + .../component/quartz2/QuartzEndpoint.java | 271 +++++++++++++++ .../component/quartz2/QuartzMessage.java | 45 +++ .../component/quartz2/StatefulCamelJob.java | 31 ++ .../src/main/resources/META-INF/LICENSE.txt | 203 ++++++++++++ .../src/main/resources/META-INF/NOTICE.txt | 11 + .../org/apache/camel/component/quartz2 | 18 + .../quartz2/QuartzAddDynamicRouteTest.java | 62 ++++ ...AddRoutesAfterCamelContextStartedTest.java | 49 +++ .../quartz2/QuartzAutoStartTest.java | 61 ++++ .../quartz2/QuartzComponentTest.java | 54 +++ .../quartz2/QuartzCronRouteTest.java | 59 ++++ .../QuartzCronRouteWithSmallCacheTest.java | 69 ++++ .../quartz2/QuartzEndpointConfigureTest.java | 160 +++++++++ .../quartz2/QuartzJobRouteUnderscoreTest.java | 53 +++ .../quartz2/QuartzNameCollisionTest.java | 172 ++++++++++ .../QuartzOneCamelContextRestartTest.java | 69 ++++ ...uartzOneCamelContextSuspendResumeTest.java | 68 ++++ .../quartz2/QuartzPropertiesTest.java | 85 +++++ .../quartz2/QuartzRouteFireNowTest.java | 36 ++ .../quartz2/QuartzRouteRestartTest.java | 65 ++++ .../component/quartz2/QuartzRouteTest.java | 51 +++ .../quartz2/QuartzSimpleRouteTest.java | 57 ++++ .../quartz2/QuartzStartDelayedOptionTest.java | 46 +++ .../quartz2/QuartzStartDelayedTest.java | 48 +++ .../quartz2/QuartzStatefulJobRouteTest.java | 59 ++++ .../QuartzTwoCamelContextRestartTest.java | 86 +++++ ...uartzTwoCamelContextSameNameClashTest.java | 86 +++++ ...uartzTwoCamelContextSuspendResumeTest.java | 85 +++++ .../quartz2/QuartzTwoCamelContextTest.java | 103 ++++++ .../quartz2/SpringQuartzCronRouteTest.java | 43 +++ ...ngQuartzPersistentStoreRestartAppTest.java | 79 +++++ ...QuartzPersistentStoreRestartRouteTest.java | 66 ++++ .../SpringQuartzPersistentStoreTest.java | 43 +++ .../quartz2/StatefulQuartzRouteTest.java | 62 ++++ .../src/test/resources/log4j.properties | 37 +++ .../quartz2/SpringQuartzCronRouteTest.xml | 34 ++ ...SpringQuartzPersistentStoreRestartTest.xml | 67 ++++ .../SpringQuartzPersistentStoreTest.xml | 67 ++++ .../component/quartz2/myquartz.properties | 26 ++ .../src/test/resources/tables_derby.sql | 174 ++++++++++ 45 files changed, 3517 insertions(+) create mode 100644 components/camel-quartz2/pom.xml create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java create mode 100644 components/camel-quartz2/src/main/resources/META-INF/LICENSE.txt create mode 100644 components/camel-quartz2/src/main/resources/META-INF/NOTICE.txt create mode 100644 components/camel-quartz2/src/main/resources/META-INF/services/org/apache/camel/component/quartz2 create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddDynamicRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddRoutesAfterCamelContextStartedTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAutoStartTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzComponentTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteWithSmallCacheTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzEndpointConfigureTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzJobRouteUnderscoreTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextRestartTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextSuspendResumeTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzPropertiesTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteFireNowTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteRestartTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzSimpleRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedOptionTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStatefulJobRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextRestartTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSameNameClashTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSuspendResumeTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartAppTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/StatefulQuartzRouteTest.java create mode 100644 components/camel-quartz2/src/test/resources/log4j.properties create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartTest.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/myquartz.properties create mode 100644 components/camel-quartz2/src/test/resources/tables_derby.sql diff --git a/components/camel-quartz2/pom.xml b/components/camel-quartz2/pom.xml new file mode 100644 index 0000000000000..61041bf2e866d --- /dev/null +++ b/components/camel-quartz2/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.camel + components + 2.12-SNAPSHOT + + + camel-quartz2 + bundle + Camel :: Quartz2 + Camel Quartz2 support + + + + org.apache.camel.component.quartz2.*;${camel.osgi.version}, + org.apache.camel.routepolicy.quartz2.* + + + !org.apache.camel.component.quartz2.*, + !org.apache.camel.routepolicy.quartz2.*, + ${camel.osgi.import.defaults}, + * + + org.apache.camel.spi.ComponentResolver;component=quartz2 + + + + + org.apache.camel + camel-core + + + org.quartz-scheduler + quartz + ${quartz2-version} + + + + + junit + junit + test + + + org.slf4j + slf4j-log4j12 + test + + + org.apache.camel + camel-test-spring + test + + + commons-dbcp + commons-dbcp + test + + + + + org.apache.derby + derby + test + + + org.springframework + spring-jdbc + test + + + org.springframework + spring-context-support + test + + + + + + + maven-surefire-plugin + + pertest + + + + + diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java new file mode 100644 index 0000000000000..b85012f15806e --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java @@ -0,0 +1,95 @@ +package org.apache.camel.component.quartz2; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelExchangeException; +import org.apache.camel.Exchange; +import org.apache.camel.Route; +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CamelJob implements Job { + private static final transient Logger LOG = LoggerFactory.getLogger(CamelJob.class); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + Exchange exchange = null; + try { + LOG.debug("Running CamelJob jobExecutionContext={}", context); + CamelContext camelContext = getCamelContext(context); + QuartzEndpoint endpoint = lookupQuartzEndpoint(camelContext, context); + exchange = endpoint.createExchange(); + exchange.setIn(new QuartzMessage(exchange, context)); + endpoint.getConsumerLoadBalancer().process(exchange); + + if (exchange.getException() != null) { + throw new JobExecutionException(exchange.getException()); + } + } catch (Exception e) { + if (exchange != null) + LOG.error(CamelExchangeException.createExceptionMessage("Error processing exchange", exchange, e)); + else + LOG.error("Failed to execute CamelJob.", e); + + // and rethrow to let quartz handle it + if (e instanceof JobExecutionException) { + throw (JobExecutionException) e; + } + throw new JobExecutionException(e); + } + } + + private CamelContext getCamelContext(JobExecutionContext context) throws JobExecutionException { + SchedulerContext schedulerContext = getSchedulerContext(context); + String camelContextName = context.getMergedJobDataMap().getString(QuartzConstants.QUARTZ_CAMEL_CONTEXT_NAME); + CamelContext result = (CamelContext)schedulerContext.get(QuartzConstants.QUARTZ_CAMEL_CONTEXT + "-" + camelContextName); + if (result == null) { + throw new JobExecutionException("No CamelContext could be found with name: " + camelContextName); + } + return result; + } + + private SchedulerContext getSchedulerContext(JobExecutionContext context) throws JobExecutionException { + try { + return context.getScheduler().getContext(); + } catch (SchedulerException e) { + throw new JobExecutionException("Failed to obtain scheduler context for job " + context.getJobDetail().getKey()); + } + } + + private QuartzEndpoint lookupQuartzEndpoint(CamelContext camelContext, JobExecutionContext quartzContext) throws JobExecutionException { + TriggerKey triggerKey = quartzContext.getTrigger().getKey(); + LOG.debug("Looking up existing QuartzEndpoint with triggerKey={}", triggerKey); + + // check all active routes for the quartz endpoint this task matches + // as we prefer to use the existing endpoint from the routes + for (Route route : camelContext.getRoutes()) { + if (route.getEndpoint() instanceof QuartzEndpoint) { + QuartzEndpoint quartzEndpoint = (QuartzEndpoint) route.getEndpoint(); + TriggerKey checkTriggerKey = quartzEndpoint.getTriggerKey(); + LOG.trace("Checking route endpoint={} with checkTriggerKey={}", quartzEndpoint, checkTriggerKey); + if (triggerKey.equals(checkTriggerKey)) { + return quartzEndpoint; + } + } + } + + // fallback and lookup existing from registry (eg maybe a @Consume POJO with a quartz endpoint, and thus not from a route) + String endpointUri = quartzContext.getMergedJobDataMap().getString(QuartzConstants.QUARTZ_ENDPOINT_URI); + QuartzEndpoint result = null; + + // Even though the same camelContext.getEndpoint call, but if/else display different log. + if (camelContext.hasEndpoint(endpointUri) != null) { + LOG.debug("Getting Endpoint from camelContext."); + result = camelContext.getEndpoint(endpointUri, QuartzEndpoint.class); + } else { + LOG.warn("Cannot find existing QuartzEndpoint with uri: {}. Creating new endpoint instance.", endpointUri); + result = camelContext.getEndpoint(endpointUri, QuartzEndpoint.class); + } + if (result == null) { + throw new JobExecutionException("No QuartzEndpoint could be found with endpointUri: " + endpointUri); + } + + return result; + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java new file mode 100644 index 0000000000000..9e9931868c6a4 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -0,0 +1,309 @@ +package org.apache.camel.component.quartz2; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.StartupListener; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.impl.DefaultComponent; +import org.apache.camel.util.IntrospectionSupport; +import org.apache.camel.util.ObjectHelper; +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +public class QuartzComponent extends DefaultComponent implements StartupListener { + private static final transient Logger LOG = LoggerFactory.getLogger(QuartzComponent.class); + private SchedulerFactory schedulerFactory; + private Scheduler scheduler; + private Properties properties; + private String propertiesFile; + private int startDelayedSeconds; + private boolean autoStartScheduler = true; + + public QuartzComponent() { + } + + public QuartzComponent(CamelContext camelContext) { + super(camelContext); + } + + public int getStartDelayedSeconds() { + return startDelayedSeconds; + } + + public boolean isAutoStartScheduler() { + return autoStartScheduler; + } + + public void setStartDelayedSeconds(int startDelayedSeconds) { + this.startDelayedSeconds = startDelayedSeconds; + } + + public void setAutoStartScheduler(boolean autoStartScheduler) { + this.autoStartScheduler = autoStartScheduler; + } + + public Properties getProperties() { + return properties; + } + + public String getPropertiesFile() { + return propertiesFile; + } + + public void setProperties(Properties properties) { + this.properties = properties; + } + + public void setPropertiesFile(String propertiesFile) { + this.propertiesFile = propertiesFile; + } + + public SchedulerFactory getSchedulerFactory() throws SchedulerException { + if (schedulerFactory == null) { + schedulerFactory = createSchedulerFactory(); + } + return schedulerFactory; + } + + private SchedulerFactory createSchedulerFactory() throws SchedulerException { + SchedulerFactory answer; + + Properties prop = loadProperties(); + if (prop != null) { + + // force disabling update checker (will do online check over the internet) + prop.put("org.quartz.scheduler.skipUpdateCheck", "true"); + + answer = new StdSchedulerFactory(prop); + } else { + // read default props to be able to use a single scheduler per camel context + // if we need more than one scheduler per context use setScheduler(Scheduler) + // or setFactory(SchedulerFactory) methods + + // must use classloader from StdSchedulerFactory to work even in OSGi + InputStream is = StdSchedulerFactory.class.getClassLoader().getResourceAsStream("org/quartz/quartz.properties"); + if (is == null) { + throw new SchedulerException("Quartz properties file not found in classpath: org/quartz/quartz.properties"); + } + prop = new Properties(); + try { + prop.load(is); + } catch (IOException e) { + throw new SchedulerException("Error loading Quartz properties file from classpath: org/quartz/quartz.properties", e); + } + + // camel context name will be a suffix to use one scheduler per context + String identity = getCamelContext().getManagementName(); + + String instName = prop.getProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME); + if (instName == null) { + instName = "scheduler-" + identity; + } else { + instName = instName + "-" + identity; + } + prop.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, instName); + + // force disabling update checker (will do online check over the internet) + prop.put("org.quartz.scheduler.skipUpdateCheck", "true"); + + answer = new StdSchedulerFactory(prop); + } + + if (LOG.isDebugEnabled()) { + String name = prop.getProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME); + LOG.debug("Creating SchedulerFactory: {} with properties: {}", name, prop); + } + return answer; + } + + private Properties loadProperties() throws SchedulerException { + Properties answer = getProperties(); + if (answer == null && getPropertiesFile() != null) { + LOG.info("Loading Quartz properties file from classpath: {}", getPropertiesFile()); + InputStream is = getCamelContext().getClassResolver().loadResourceAsStream(getPropertiesFile()); + if (is == null) { + throw new SchedulerException("Quartz properties file not found in classpath: " + getPropertiesFile()); + } + answer = new Properties(); + try { + answer.load(is); + } catch (IOException e) { + throw new SchedulerException("Error loading Quartz properties file from classpath: " + getPropertiesFile(), e); + } + } + return answer; + } + + public void setSchedulerFactory(SchedulerFactory schedulerFactory) { + this.schedulerFactory = schedulerFactory; + } + + public Scheduler getScheduler() { + return scheduler; + } + + public void setScheduler(Scheduler scheduler) { + this.scheduler = scheduler; + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception { + // Get couple of scheduler settings + Integer startDelayedSeconds = getAndRemoveParameter(parameters, "startDelayedSeconds", Integer.class); + if (startDelayedSeconds != null) { + if (this.startDelayedSeconds != 0 && !(this.startDelayedSeconds == startDelayedSeconds)) { + LOG.warn("A Quartz job is already configured with a different 'startDelayedSeconds' configuration! " + + "All Quartz jobs must share the same 'startDelayedSeconds' configuration! Cannot apply the 'startDelayedSeconds' configuration!"); + } else { + this.startDelayedSeconds = startDelayedSeconds; + } + } + + Boolean autoStartScheduler = getAndRemoveParameter(parameters, "autoStartScheduler", Boolean.class); + if (autoStartScheduler != null) + this.autoStartScheduler = autoStartScheduler; + + // Extract trigger.XXX and job.XXX properties to be set on endpoint below + Map triggerParameters = IntrospectionSupport.extractProperties(parameters, "trigger."); + Map jobParameters = IntrospectionSupport.extractProperties(parameters, "job."); + + // Create quartz endpoint + TriggerKey triggerKey = createTriggerKey(uri, remaining); + QuartzEndpoint result = new QuartzEndpoint(uri, this); + result.setTriggerKey(triggerKey); + result.setTriggerParameters(triggerParameters); + result.setJobParameters(jobParameters); + return result; + } + + private TriggerKey createTriggerKey(String uri, String remaining) throws Exception { + // Parse uri for trigger name and group + URI u = new URI(uri); + String path = ObjectHelper.after(u.getPath(), "/"); + String host = u.getHost(); + + // host can be null if the uri did contain invalid host characters such as an underscore + if (host == null) { + host = ObjectHelper.before(remaining, "/"); + } + + // Trigger group can be optional, if so set it to Camel + String name, group; + if (ObjectHelper.isNotEmpty(path) && ObjectHelper.isNotEmpty(host)) { + group = host; + name = path; + } else { + group = "Camel"; + name = host; + } + + return new TriggerKey(name, group); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + + if (scheduler == null) { + createAndInitScheduler(); + } + } + + private void createAndInitScheduler() throws SchedulerException { + LOG.info("Create and initializing scheduler."); + scheduler = createScheduler(); + + // Store CamelContext into QuartzContext space + SchedulerContext quartzContext = scheduler.getContext(); + String camelContextName = getCamelContext().getManagementName(); + LOG.debug("Storing camelContextName={} into Quartz Context space.", camelContextName); + quartzContext.put(QuartzConstants.QUARTZ_CAMEL_CONTEXT + "-" + camelContextName, getCamelContext()); + + // Set camel job counts to zero. We needed this to prevent shutdown in case there are multiple Camel contexts + // that has not completed yet, and the last one with job counts to zero will eventually shutdown. + AtomicInteger number = (AtomicInteger) quartzContext.get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); + if (number == null) { + number = new AtomicInteger(0); + quartzContext.put(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT, number); + } + } + + private Scheduler createScheduler() throws SchedulerException { + return getSchedulerFactory().getScheduler(); + } + + @Override + protected void doStop() throws Exception { + super.doStop(); + + if (scheduler != null) { + AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); + if (number != null && number.get() > 0) { + LOG.info("Cannot shutdown scheduler: " + scheduler.getSchedulerName() + " as there are still " + number.get() + " jobs registered."); + } else { + LOG.info("Shutting down scheduler. (will wait for all jobs to complete first.)"); + scheduler.shutdown(true); + scheduler = null; + } + } + } + + @Override + public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception { + // If Camel has already started and then user add a route dynamically, we need to ensure + // to create and init the scheduler first. + if (scheduler == null) { + createAndInitScheduler(); + } + + // Now scheduler is ready, let see how we should start it. + if (!autoStartScheduler) { + LOG.info("Not starting scheduler because autoStartScheduler is set to false."); + } else { + if (startDelayedSeconds > 0) { + if (scheduler.isStarted()) { + LOG.warn("The scheduler has already started. Cannot apply the 'startDelayedSeconds' configuration!"); + } else { + LOG.info("Starting scheduler with startDelayedSeconds={}", startDelayedSeconds); + scheduler.startDelayed(startDelayedSeconds); + } + } else { + if (scheduler.isStarted()) { + LOG.info("The scheduler has already been started."); + } else { + LOG.info("Starting scheduler."); + scheduler.start(); + } + } + } + } + + @Override + protected void doSuspend() throws Exception { + super.doSuspend(); + + if (scheduler != null) { + LOG.info("Standby the scheduler (suspend)."); + scheduler.standby(); + } + } + + @Override + protected void doResume() throws Exception { + super.doResume(); + + if (scheduler != null) { + LOG.info("Start the scheduler (resume)."); + scheduler.start(); + } + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java new file mode 100644 index 0000000000000..6c39de8a15b82 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java @@ -0,0 +1,35 @@ +/** + * 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.quartz2; + +/** + * Quartz constants. + */ +public final class QuartzConstants { + public static final String QUARTZ_CAMEL_JOBS_COUNT = "CamelJobsCount"; + + public static final String QUARTZ_ENDPOINT_URI = "CamelQuartzEndpoint"; + + // Note: using the CamelContext management name to ensure its unique in the JVM + public static final String QUARTZ_CAMEL_CONTEXT_NAME = "CamelQuartzCamelContextName"; + + public static final String QUARTZ_CAMEL_CONTEXT = "CamelQuartzCamelContext"; + + private QuartzConstants() { + // Utility class + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java new file mode 100644 index 0000000000000..39c7fa7d2d75d --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java @@ -0,0 +1,11 @@ +package org.apache.camel.component.quartz2; + +import org.apache.camel.Endpoint; +import org.apache.camel.Processor; +import org.apache.camel.impl.DefaultConsumer; + +public class QuartzConsumer extends DefaultConsumer { + public QuartzConsumer(Endpoint endpoint, Processor processor) { + super(endpoint, processor); + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java new file mode 100644 index 0000000000000..ad2b8bfc7a6e6 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java @@ -0,0 +1,271 @@ +package org.apache.camel.component.quartz2; + +import org.apache.camel.Consumer; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.impl.DefaultEndpoint; +import org.apache.camel.processor.loadbalancer.LoadBalancer; +import org.apache.camel.processor.loadbalancer.RoundRobinLoadBalancer; +import org.apache.camel.util.EndpointHelper; +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.quartz.CronScheduleBuilder.cronSchedule; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; + +public class QuartzEndpoint extends DefaultEndpoint { + private static final transient Logger LOG = LoggerFactory.getLogger(QuartzEndpoint.class); + private TriggerKey triggerKey; + private String cron; + private LoadBalancer consumerLoadBalancer; + private Map triggerParameters; + private Map jobParameters; + private boolean stateful; + private boolean fireNow; + private boolean deleteJob = true; + /** In case of scheduler has already started, we want the trigger start slightly after current time to + * ensure endpoint is fully started before the job kicks in. */ + private long triggerStartDelay = 500; // in millis second + + public String getCron() { + return cron; + } + + public boolean isStateful() { + return stateful; + } + + public boolean isFireNow() { + return fireNow; + } + + public long getTriggerStartDelay() { + return triggerStartDelay; + } + + public boolean isDeleteJob() { + return deleteJob; + } + + public void setTriggerStartDelay(long triggerStartDelay) { + this.triggerStartDelay = triggerStartDelay; + } + + public void setDeleteJob(boolean deleteJob) { + this.deleteJob = deleteJob; + } + + public void setFireNow(boolean fireNow) { + this.fireNow = fireNow; + } + + public void setStateful(boolean stateful) { + this.stateful = stateful; + } + + public void setTriggerParameters(Map triggerParameters) { + this.triggerParameters = triggerParameters; + } + + public void setJobParameters(Map jobParameters) { + this.jobParameters = jobParameters; + } + + public LoadBalancer getConsumerLoadBalancer() { + if (consumerLoadBalancer == null) + consumerLoadBalancer = new RoundRobinLoadBalancer(); + return consumerLoadBalancer; + } + + public void setConsumerLoadBalancer(LoadBalancer consumerLoadBalancer) { + this.consumerLoadBalancer = consumerLoadBalancer; + } + + public void setCron(String cron) { + this.cron = cron; + } + + public TriggerKey getTriggerKey() { + return triggerKey; + } + + public void setTriggerKey(TriggerKey triggerKey) { + this.triggerKey = triggerKey; + } + + public QuartzEndpoint(String uri, QuartzComponent quartzComponent) { + super(uri, quartzComponent); + } + + @Override + public Producer createProducer() throws Exception { + throw new UnsupportedOperationException("Quartz producer is not supported."); + } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + QuartzConsumer result = new QuartzConsumer(this, processor); + configureConsumer(result); + getConsumerLoadBalancer().addProcessor(processor); + return result; + } + + @Override + public boolean isSingleton() { + return false; + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + + // Add or use existing trigger to/from scheduler + Scheduler scheduler = getComponent().getScheduler(); + JobDetail jobDetail = null; + Trigger trigger = scheduler.getTrigger(triggerKey); + if (trigger == null) { + jobDetail = createJobDetail(); + trigger = createTrigger(); + + updateJobDataMap(jobDetail); + + // Schedule it now. Remember that scheduler might not be started it, but we can schedule now. + scheduler.scheduleJob(jobDetail, trigger); + } else { + // Update existing jobDetails with current endpoint data to jobDataMap. + jobDetail = scheduler.getJobDetail(trigger.getJobKey()); + updateJobDataMap(jobDetail); + scheduler.addJob(jobDetail, true); + } + + // Increase camel job count for this endpoint + AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); + number.incrementAndGet(); + } + + private void updateJobDataMap(JobDetail jobDetail) { + // Store this camelContext name into the job data + JobDataMap jobDataMap = jobDetail.getJobDataMap(); + String camelContextName = getCamelContext().getManagementName(); + String endpointUri = getEndpointUri(); + LOG.debug("Adding camelContextName={}, endpintUri={} into job data map.", camelContextName, endpointUri); + jobDataMap.put(QuartzConstants.QUARTZ_CAMEL_CONTEXT_NAME, camelContextName); + jobDataMap.put(QuartzConstants.QUARTZ_ENDPOINT_URI, endpointUri); + } + + private Trigger createTrigger() throws Exception { + Trigger result = null; + Date startTime = new Date(); + if (getComponent().getScheduler().isStarted()) + startTime = new Date(System.currentTimeMillis() + triggerStartDelay); + if (cron != null) { + LOG.debug("Creating CronTrigger: {}", cron); + result = TriggerBuilder.newTrigger() + .withIdentity(triggerKey) + .startAt(startTime) + .withSchedule(cronSchedule(cron).withMisfireHandlingInstructionFireAndProceed()) + .build(); + } else { + LOG.debug("Creating SimpleTrigger."); + TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger() + .withIdentity(triggerKey) + .startAt(startTime) + .withSchedule(simpleSchedule().withMisfireHandlingInstructionFireNow()); + + // Enable trigger to fire now by setting startTime in the past. + if (fireNow) { + String intervalString = (String) triggerParameters.get("repeatInterval"); + if (intervalString != null) { + long interval = Long.valueOf(intervalString); + triggerBuilder.startAt(new Date(System.currentTimeMillis() - interval)); + } + } + + result = triggerBuilder.build(); + } + + if (triggerParameters != null && triggerParameters.size() > 0) { + LOG.debug("Setting user extra triggerParameters {}", triggerParameters); + setProperties(result, triggerParameters); + } + + LOG.info("Created trigger={}", result); + return result; + } + + private void setProperties(Object bean, Map parameters) throws Exception { + EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); + EndpointHelper.setProperties(getCamelContext(), bean, parameters); + } + + private JobDetail createJobDetail() throws Exception { + // Camel endpoint timer will assume one to one for JobDetail and Trigger, so let's use same name as trigger + String name = triggerKey.getName(); + String group = triggerKey.getGroup(); + Class jobClass = stateful ? StatefulCamelJob.class : CamelJob.class; + LOG.debug("Creating new {}.", jobClass.getSimpleName()); + + JobDetail result = JobBuilder.newJob(jobClass) + .withIdentity(name, group) + .build(); + + // Let user parameters to further set JobDetail properties. + if (jobParameters != null && jobParameters.size() > 0) { + LOG.debug("Setting user extra jobParameters {}", jobParameters); + setProperties(result, jobParameters); + } + + LOG.info("Created jobDetail={}", result); + return result; + } + + @Override + public QuartzComponent getComponent() { + return (QuartzComponent)super.getComponent(); + } + + @Override + protected void doStop() throws Exception { + super.doStop(); + + Scheduler scheduler = getComponent().getScheduler(); + if (deleteJob && scheduler != null) { + boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); + if (!scheduler.isShutdown() && !isClustered) { + LOG.info("Deleting job {}", triggerKey); + scheduler.unscheduleJob(triggerKey); + } + } + + // Decrement camel job count for this endpoint + AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); + number.decrementAndGet(); + } + + @Override + protected void doSuspend() throws Exception { + super.doSuspend(); + + Scheduler scheduler = getComponent().getScheduler(); + if (scheduler != null) { + LOG.info("Pausing trigger (suspend)."); + scheduler.pauseTrigger(triggerKey); + } + } + + @Override + protected void doResume() throws Exception { + super.doResume(); + + Scheduler scheduler = getComponent().getScheduler(); + if (scheduler != null) { + LOG.info("Resuming trigger (resume)."); + scheduler.resumeTrigger(triggerKey); + } + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java new file mode 100644 index 0000000000000..48791df8362f4 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java @@ -0,0 +1,45 @@ +package org.apache.camel.component.quartz2; + +import org.apache.camel.Exchange; +import org.apache.camel.impl.DefaultMessage; +import org.quartz.JobExecutionContext; +import org.quartz.Trigger; + +import java.util.Map; + +public class QuartzMessage extends DefaultMessage { + private final JobExecutionContext jobExecutionContext; + + public QuartzMessage(Exchange exchange, JobExecutionContext jobExecutionContext) { + this.jobExecutionContext = jobExecutionContext; + setExchange(exchange); + // do not set body as it should be null + } + + public JobExecutionContext getJobExecutionContext() { + return jobExecutionContext; + } + + @Override + protected void populateInitialHeaders(Map map) { + super.populateInitialHeaders(map); + if (jobExecutionContext != null) { + map.put("calendar", jobExecutionContext.getCalendar()); + map.put("fireTime", jobExecutionContext.getFireTime()); + map.put("jobDetail", jobExecutionContext.getJobDetail()); + map.put("jobInstance", jobExecutionContext.getJobInstance()); + map.put("jobRunTime", jobExecutionContext.getJobRunTime()); + map.put("mergedJobDataMap", jobExecutionContext.getMergedJobDataMap()); + map.put("nextFireTime", jobExecutionContext.getNextFireTime()); + map.put("previousFireTime", jobExecutionContext.getPreviousFireTime()); + map.put("refireCount", jobExecutionContext.getRefireCount()); + map.put("result", jobExecutionContext.getResult()); + map.put("scheduledFireTime", jobExecutionContext.getScheduledFireTime()); + map.put("scheduler", jobExecutionContext.getScheduler()); + Trigger trigger = jobExecutionContext.getTrigger(); + map.put("trigger", trigger); + map.put("triggerName", trigger.getKey().getName()); + map.put("triggerGroup", trigger.getKey().getGroup()); + } + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java new file mode 100644 index 0000000000000..01d63383ec17e --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java @@ -0,0 +1,31 @@ +/** + * 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.quartz2; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.PersistJobDataAfterExecution; + +/** + * Stateful job + */ +@PersistJobDataAfterExecution +@DisallowConcurrentExecution +public class StatefulCamelJob extends CamelJob { + + private static final long serialVersionUID = 25L; + +} diff --git a/components/camel-quartz2/src/main/resources/META-INF/LICENSE.txt b/components/camel-quartz2/src/main/resources/META-INF/LICENSE.txt new file mode 100644 index 0000000000000..6b0b1270ff0ca --- /dev/null +++ b/components/camel-quartz2/src/main/resources/META-INF/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + diff --git a/components/camel-quartz2/src/main/resources/META-INF/NOTICE.txt b/components/camel-quartz2/src/main/resources/META-INF/NOTICE.txt new file mode 100644 index 0000000000000..2e215bf2e6b1f --- /dev/null +++ b/components/camel-quartz2/src/main/resources/META-INF/NOTICE.txt @@ -0,0 +1,11 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Apache Camel distribution. == + ========================================================================= + + This product includes software developed by + The Apache Software Foundation (http://www.apache.org/). + + Please read the different LICENSE files present in the licenses directory of + this distribution. diff --git a/components/camel-quartz2/src/main/resources/META-INF/services/org/apache/camel/component/quartz2 b/components/camel-quartz2/src/main/resources/META-INF/services/org/apache/camel/component/quartz2 new file mode 100644 index 0000000000000..c1ffd5cb0ced2 --- /dev/null +++ b/components/camel-quartz2/src/main/resources/META-INF/services/org/apache/camel/component/quartz2 @@ -0,0 +1,18 @@ +# +# 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. +# + +class=org.apache.camel.component.quartz2.QuartzComponent diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddDynamicRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddDynamicRouteTest.java new file mode 100644 index 0000000000000..05bb8276d16e9 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddDynamicRouteTest.java @@ -0,0 +1,62 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * @version + */ +public class QuartzAddDynamicRouteTest extends CamelTestSupport { + protected MockEndpoint resultEndpoint; + + @Test + public void testAddDynamicRoute() throws Exception { + resultEndpoint = getMockEndpoint("mock:result"); + resultEndpoint.expectedMessageCount(1); + + template.sendBody("direct:foo", "Hello World"); + + resultEndpoint.assertIsSatisfied(); + + // reset and add a new dynamic route + resultEndpoint.reset(); + resultEndpoint.expectedMessageCount(2); + + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2&trigger.repeatCount=1").routeId("myRoute") + .to("direct:foo"); + } + }); + + resultEndpoint.assertIsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:foo").to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddRoutesAfterCamelContextStartedTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddRoutesAfterCamelContextStartedTest.java new file mode 100644 index 0000000000000..1f11d23b53d64 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAddRoutesAfterCamelContextStartedTest.java @@ -0,0 +1,49 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * @version + */ +public class QuartzAddRoutesAfterCamelContextStartedTest extends CamelTestSupport { + + @Test + public void testAddRoutes() throws Exception { + // camel context should already be started + assertTrue(context.getStatus().isStarted()); + + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(2); + + // add the quartz router after CamelContext has been started + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=1000&trigger.repeatCount=1").to("mock:result"); + } + }); + + // it should also work + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAutoStartTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAutoStartTest.java new file mode 100644 index 0000000000000..e330413761809 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzAutoStartTest.java @@ -0,0 +1,61 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * @version + */ +public class QuartzAutoStartTest extends CamelTestSupport { + + @Test + public void testQuartzAutoStart() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:one"); + mock.expectedMessageCount(0); + + QuartzComponent quartz = context.getComponent("quartz2", QuartzComponent.class); + assertFalse("Should not have started scheduler", quartz.getScheduler().isStarted()); + + Thread.sleep(2000); + + assertMockEndpointsSatisfied(); + + mock.reset(); + mock.expectedMinimumMessageCount(1); + + // start scheduler + + quartz.getScheduler().start(); + + assertMockEndpointsSatisfied(); + } + + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?&autoStartScheduler=false").to("mock:one"); + } + }; + } +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzComponentTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzComponentTest.java new file mode 100644 index 0000000000000..a24b52906e02e --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzComponentTest.java @@ -0,0 +1,54 @@ +/** + * 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.quartz2; + +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.quartz.Scheduler; +import org.quartz.SchedulerFactory; +import org.quartz.impl.StdSchedulerFactory; + +/** + * @version + */ +public class QuartzComponentTest extends CamelTestSupport { + + @Test + public void testQuartzComponentCustomScheduler() throws Exception { + QuartzComponent comp = new QuartzComponent(); + comp.setCamelContext(context); + + SchedulerFactory fac = new StdSchedulerFactory(); + comp.setSchedulerFactory(fac); + assertSame(fac, comp.getSchedulerFactory()); + + Scheduler sch = fac.getScheduler(); + comp.setScheduler(sch); + assertSame(sch, comp.getScheduler()); + + comp.start(); + comp.stop(); + } + + @Test + public void testQuartzComponent() throws Exception { + QuartzComponent comp = new QuartzComponent(context); + comp.start(); + comp.stop(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteTest.java new file mode 100644 index 0000000000000..9b2225b67f82a --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteTest.java @@ -0,0 +1,59 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.Trigger; + +/** + * This test the CronTrigger as a timer endpoint in a route. + * @version + */ +public class QuartzCronRouteTest extends CamelTestSupport { + + @Test + public void testQuartzCronRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(3); + + assertMockEndpointsSatisfied(); + + Trigger trigger = mock.getReceivedExchanges().get(0).getIn().getHeader("trigger", Trigger.class); + Assert.assertThat(trigger instanceof CronTrigger, CoreMatchers.is(true)); + + JobDetail detail = mock.getReceivedExchanges().get(0).getIn().getHeader("jobDetail", JobDetail.class); + Assert.assertThat(detail.getJobClass().equals(CamelJob.class), CoreMatchers.is(true)); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // triggers every 2th second at precise 00,02,04,06..58 + // notice we must use + as space when configured using URI parameter + from("quartz2://myGroup/myTimerName?cron=0/2+*+*+*+*+?").to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteWithSmallCacheTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteWithSmallCacheTest.java new file mode 100644 index 0000000000000..d1ff451fda032 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzCronRouteWithSmallCacheTest.java @@ -0,0 +1,69 @@ +/** + * 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.quartz2; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Endpoints are stored in a LRU list with a default capacity of 1000. If the list is full, + * then endpoints are removed and should be recreated. + *

+ * We simulate this behavior with a capacity of 1 element. + */ +public class QuartzCronRouteWithSmallCacheTest extends CamelTestSupport { + + private final CountDownLatch latch = new CountDownLatch(3); + + @Test + public void testQuartzCronRouteWithSmallCache() throws Exception { + boolean wait = latch.await(10, TimeUnit.SECONDS); + assertTrue(wait); + assertTrue("Quartz should trigger at least 3 times", latch.getCount() <= 0); + } + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + context.getProperties().put(Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE, "1"); + return context; + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:foo").to("log:foo"); + + from("quartz2://myGroup/myTimerName?cron=0/2+*+*+*+*+?").process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + latch.countDown(); + template.sendBody("direct:foo", "Quartz triggered"); + } + }); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzEndpointConfigureTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzEndpointConfigureTest.java new file mode 100644 index 0000000000000..6b7f9c561423b --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzEndpointConfigureTest.java @@ -0,0 +1,160 @@ +/** + * 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.quartz2; + +import org.apache.camel.Endpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.quartz.*; + +/** + * @version + */ +public class QuartzEndpointConfigureTest extends CamelTestSupport { + + @Test + public void testConfigureGroupAndName() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2://myGroup/myName?trigger.repeatCount=3&trigger.repeatInterval=1000"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(triggerKey.getName(), triggerKey.getGroup())); + + assertEquals("getName()", "myName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + assertEquals("getJobName", "myName", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "myGroup", jobDetail.getKey().getGroup()); + + SimpleTrigger simpleTrigger = assertIsInstanceOf(SimpleTrigger.class, trigger); + assertEquals("getRepeatCount()", 3, simpleTrigger.getRepeatCount()); + } + + @Test + public void testConfigureName() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2://myName"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(triggerKey.getName(), triggerKey.getGroup())); + + assertEquals("getName()", "myName", triggerKey.getName()); + assertEquals("getGroup()", "Camel", triggerKey.getGroup()); + assertEquals("getJobName", "myName", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "Camel", jobDetail.getKey().getGroup()); + } + + @Test + public void testConfigureCronExpression() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2://myGroup/myTimerName?cron=0+0/5+12-18+?+*+MON-FRI"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(triggerKey.getName(), triggerKey.getGroup())); + + assertEquals("getName()", "myTimerName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + assertEquals("getJobName", "myTimerName", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "myGroup", jobDetail.getKey().getGroup()); + + assertIsInstanceOf(CronTrigger.class, trigger); + CronTrigger cronTrigger = (CronTrigger)trigger; + assertEquals("cron expression", "0 0/5 12-18 ? * MON-FRI", cronTrigger.getCronExpression()); + } + + @Test + public void testConfigureAnotherCronExpression() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2://myGroup/myTimerName?cron=0+0+*+*+*+?"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(triggerKey.getName(), triggerKey.getGroup())); + + assertEquals("getName()", "myTimerName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + assertEquals("getJobName", "myTimerName", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "myGroup", jobDetail.getKey().getGroup()); + + assertIsInstanceOf(CronTrigger.class, trigger); + CronTrigger cronTrigger = (CronTrigger)trigger; + assertEquals("cron expression", "0 0 * * * ?", cronTrigger.getCronExpression()); + } + + @Test + public void testConfigureJobName() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2://myGroup/myTimerName?job.name=hadrian&cron=0+0+*+*+*+?"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey("hadrian", triggerKey.getGroup())); + + assertEquals("getName()", "myTimerName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + assertEquals("getJobName", "hadrian", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "myGroup", jobDetail.getKey().getGroup()); + + assertIsInstanceOf(CronTrigger.class, trigger); + } + + @Test + public void testConfigureNoDoubleSlashNoCron() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2:myGroup/myTimerName"); + + TriggerKey triggerKey = endpoint.getTriggerKey(); + assertEquals("getName()", "myTimerName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + } + + @Test + public void testConfigureNoDoubleSlashQuestionCron() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2:myGroup/myTimerName?cron=0+0+*+*+*+?"); + + Scheduler scheduler = endpoint.getComponent().getScheduler(); + TriggerKey triggerKey = endpoint.getTriggerKey(); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(triggerKey.getName(), triggerKey.getGroup())); + + assertEquals("getName()", "myTimerName", triggerKey.getName()); + assertEquals("getGroup()", "myGroup", triggerKey.getGroup()); + assertEquals("getJobName", "myTimerName", jobDetail.getKey().getName()); + assertEquals("getJobGroup", "myGroup", jobDetail.getKey().getGroup()); + + assertIsInstanceOf(CronTrigger.class, trigger); + CronTrigger cronTrigger = (CronTrigger)trigger; + assertEquals("cron expression", "0 0 * * * ?", cronTrigger.getCronExpression()); + } + + @Test + public void testConfigureDeleteJob() throws Exception { + QuartzEndpoint endpoint = resolveMandatoryEndpoint("quartz2:myGroup/myTimerName?cron=0+0+*+*+*+?"); + assertEquals("cron expression", "0 0 * * * ?", endpoint.getCron()); + assertEquals("deleteJob", true, endpoint.isDeleteJob()); + + endpoint = resolveMandatoryEndpoint("quartz2:myGroup/myTimerName2?cron=1+0+*+*+*+?&deleteJob=false"); + assertEquals("cron expression", "1 0 * * * ?", endpoint.getCron()); + assertEquals("deleteJob", false, endpoint.isDeleteJob()); + } + + @Override + protected QuartzEndpoint resolveMandatoryEndpoint(String uri) { + Endpoint endpoint = super.resolveMandatoryEndpoint(uri); + return assertIsInstanceOf(QuartzEndpoint.class, endpoint); + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzJobRouteUnderscoreTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzJobRouteUnderscoreTest.java new file mode 100644 index 0000000000000..d2e440c10d54d --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzJobRouteUnderscoreTest.java @@ -0,0 +1,53 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.quartz.JobDetail; + +/** + * @version + */ +public class QuartzJobRouteUnderscoreTest extends CamelTestSupport { + + @Test + public void testQuartzRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(2); + mock.message(0).header("triggerGroup").isEqualTo("my_group"); + mock.message(0).header("triggerName").isEqualTo("my_timer"); + + assertMockEndpointsSatisfied(); + + JobDetail detail = mock.getReceivedExchanges().get(0).getIn().getHeader("jobDetail", JobDetail.class); + assertNotNull(detail); + assertEquals("my_job", detail.getKey().getName()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("quartz2://my_group/my_timer?trigger.repeatInterval=2&trigger.repeatCount=1&job.name=my_job") + .to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java new file mode 100644 index 0000000000000..0690468eb3c5e --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java @@ -0,0 +1,172 @@ +/** + * 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.quartz2; + +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; +import org.quartz.TriggerKey; + +/** + * 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("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + + QuartzComponent component2 = new QuartzComponent(camel1); + try { + component2.createEndpoint("quartz2://myGroup/myTimerName"); + } catch (IllegalArgumentException e) { + Assert.fail("Should not have thrown an exception. Due to new deleteJob option. Exception: " + e.getMessage()); + } + } + + @Test + public void testDupeNameMultiContext() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + QuartzComponent component2 = new QuartzComponent(camel2); + component2.createEndpoint("quartz2://myGroup/myTimerName"); + } + + /** + * Don't check for a name collision if the job is stateful. + */ + @Test + public void testNoStatefulCollisionError() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?stateful=true&cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + QuartzComponent component2 = new QuartzComponent(camel2); + + component2.createEndpoint("quartz2://myGroup/myTimerName?stateful=true"); + // if no exception is thrown then this test passed. + } + + /** + * 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("quartz2://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("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").id("route-1").to("log:one", "mock:one"); + } + }); + + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup2/myTimerName?cron=0/1+*+*+*+*+?").id("route-2").to("log:one", "mock:one"); + } + }); + + camel1.start(); + + QuartzComponent component = (QuartzComponent) camel1.getComponent("quartz2"); + Scheduler scheduler = component.getScheduler(); + TriggerKey triggerKey = TriggerKey.triggerKey("myTimerName", "myGroup"); + Trigger trigger = scheduler.getTrigger(triggerKey); + Assert.assertNotNull(trigger); + + camel1.stopRoute("route-1"); + + Trigger.TriggerState triggerState = component.getScheduler().getTriggerState(triggerKey); + Assert.assertNotNull(trigger); + Assert.assertEquals(Trigger.TriggerState.NORMAL, triggerState); + } + + @After + public void cleanUp() throws Exception { + if (camel1 != null) { + camel1.stop(); + camel1 = null; + } + + if (camel2 != null) { + camel2.stop(); + camel2 = null; + } + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextRestartTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextRestartTest.java new file mode 100644 index 0000000000000..5ae384540d9fe --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextRestartTest.java @@ -0,0 +1,69 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzOneCamelContextRestartTest { + + private DefaultCamelContext camel1; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("log:one", "mock:one"); + } + }); + camel1.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + } + + @Test + public void testOneCamelContextSuspendResume() throws Exception { + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + mock1.assertIsSatisfied(); + + camel1.stop(); + + // fetch mock endpoint again because we have stopped camel context + mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + // should resume triggers when we start camel 1 again + mock1.expectedMinimumMessageCount(3); + camel1.start(); + + mock1.assertIsSatisfied(); + } + + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextSuspendResumeTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextSuspendResumeTest.java new file mode 100644 index 0000000000000..8973df9563dda --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzOneCamelContextSuspendResumeTest.java @@ -0,0 +1,68 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzOneCamelContextSuspendResumeTest { + + private DefaultCamelContext camel1; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("mock:one"); + } + }); + camel1.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + } + + @Test + public void testOneCamelContextSuspendResume() throws Exception { + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + mock1.assertIsSatisfied(); + + camel1.suspend(); + + // should resume triggers when we start camel 1 again + mock1.reset(); + mock1.expectedMinimumMessageCount(2); + camel1.resume(); + + mock1.assertIsSatisfied(); + } + + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzPropertiesTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzPropertiesTest.java new file mode 100644 index 0000000000000..9ba69cc12de05 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzPropertiesTest.java @@ -0,0 +1,85 @@ +/** + * 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.quartz2; + +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; +import org.quartz.SchedulerException; + +import java.io.InputStream; +import java.util.Properties; + +/** + * @version + */ +public class QuartzPropertiesTest extends CamelTestSupport { + + private QuartzComponent quartz; + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Override + public void tearDown() throws Exception { + quartz.stop(); + super.tearDown(); + } + + @Test + public void testQuartzPropertiesFile() throws Exception { + quartz = context.getComponent("quartz2", QuartzComponent.class); + + quartz.setPropertiesFile("org/apache/camel/component/quartz2/myquartz.properties"); + + quartz.start(); + + assertEquals("MyScheduler", quartz.getScheduler().getSchedulerName()); + assertEquals("2", quartz.getScheduler().getSchedulerInstanceId()); + } + + @Test + public void testQuartzPropertiesFileNotFound() throws Exception { + quartz = context.getComponent("quartz2", QuartzComponent.class); + + quartz.setPropertiesFile("doesnotexist.properties"); + + try { + quartz.start(); + fail("Should have thrown exception"); + } catch (SchedulerException e) { + assertEquals("Quartz properties file not found in classpath: doesnotexist.properties", e.getMessage()); + } + } + + @Test + public void testQuartzProperties() throws Exception { + quartz = context.getComponent("quartz2", QuartzComponent.class); + + Properties prop = new Properties(); + InputStream is = context.getClassResolver().loadResourceAsStream("org/apache/camel/component/quartz2/myquartz.properties"); + prop.load(is); + quartz.setProperties(prop); + + quartz.start(); + + assertEquals("MyScheduler", quartz.getScheduler().getSchedulerName()); + assertEquals("2", quartz.getScheduler().getSchedulerInstanceId()); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteFireNowTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteFireNowTest.java new file mode 100644 index 0000000000000..f6d1c6332016f --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteFireNowTest.java @@ -0,0 +1,36 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; + +/** + * @version + */ +public class QuartzRouteFireNowTest extends QuartzRouteTest { + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // START SNIPPET: example + from("quartz2://myGroup/myTimerName?fireNow=true&trigger.repeatInterval=25000&trigger.repeatCount=2").to("mock:result"); + // END SNIPPET: example + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteRestartTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteRestartTest.java new file mode 100644 index 0000000000000..8931c9f938784 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteRestartTest.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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * @version + */ +public class QuartzRouteRestartTest extends CamelTestSupport { + + @Test + public void testQuartzCronRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(2); + + assertMockEndpointsSatisfied(); + + // restart route + context().stopRoute("trigger"); + mock.reset(); + mock.expectedMessageCount(0); + + // wait a bit + Thread.sleep(2000); + + assertMockEndpointsSatisfied(); + + // start route, and we got messages again + mock.reset(); + mock.expectedMinimumMessageCount(1); + + context().startRoute("trigger"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("quartz2://groupName/timerName?cron=0/1+*+*+*+*+?").routeId("trigger") + .to("mock:result"); + } + }; + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteTest.java new file mode 100644 index 0000000000000..573521a0ab703 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzRouteTest.java @@ -0,0 +1,51 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * @version + */ +public class QuartzRouteTest extends CamelTestSupport { + protected MockEndpoint resultEndpoint; + + @Test + public void testQuartzRoute() throws Exception { + resultEndpoint = getMockEndpoint("mock:result"); + resultEndpoint.expectedMessageCount(2); + resultEndpoint.message(0).header("triggerName").isEqualTo("myTimerName"); + resultEndpoint.message(0).header("triggerGroup").isEqualTo("myGroup"); + + // lets test the receive worked + resultEndpoint.assertIsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // START SNIPPET: example + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2&trigger.repeatCount=1").routeId("myRoute").to("mock:result"); + // END SNIPPET: example + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzSimpleRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzSimpleRouteTest.java new file mode 100644 index 0000000000000..a434cc5c67012 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzSimpleRouteTest.java @@ -0,0 +1,57 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; + +/** + * This not only set SimpleTrigger as a timer endpoint in a route, and also test the trigger.XXX properties setter. + * @version + */ +public class QuartzSimpleRouteTest extends CamelTestSupport { + + @Test + public void testQuartzCronRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(3); + + assertMockEndpointsSatisfied(); + Trigger trigger = mock.getReceivedExchanges().get(0).getIn().getHeader("trigger", Trigger.class); + Assert.assertThat(trigger instanceof SimpleTrigger, CoreMatchers.is(true)); + + JobDetail detail = mock.getReceivedExchanges().get(0).getIn().getHeader("jobDetail", JobDetail.class); + Assert.assertThat(detail.getJobClass().equals(CamelJob.class), CoreMatchers.is(true)); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2000&trigger.repeatCount=-1").to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedOptionTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedOptionTest.java new file mode 100644 index 0000000000000..a97c0f0a68c18 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedOptionTest.java @@ -0,0 +1,46 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class QuartzStartDelayedOptionTest extends CamelTestSupport { + + @Test + public void testStartDelayed() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.setMinimumResultWaitTime(1900); + mock.setResultWaitTime(3000); + mock.expectedMessageCount(2); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?startDelayedSeconds=2&trigger.repeatInterval=2&trigger.repeatCount=1").routeId("myRoute") + .to("mock:result"); + } + }; + } +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedTest.java new file mode 100644 index 0000000000000..80d5d5d8b1202 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStartDelayedTest.java @@ -0,0 +1,48 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class QuartzStartDelayedTest extends CamelTestSupport { + + @Test + public void testStartDelayed() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.setMinimumResultWaitTime(1900); + mock.setResultWaitTime(3000); + mock.expectedMessageCount(2); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + QuartzComponent quartz = context.getComponent("quartz2", QuartzComponent.class); + quartz.setStartDelayedSeconds(2); + + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2&trigger.repeatCount=1").routeId("myRoute").to("mock:result"); + } + }; + } +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStatefulJobRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStatefulJobRouteTest.java new file mode 100644 index 0000000000000..d517659a6eca3 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzStatefulJobRouteTest.java @@ -0,0 +1,59 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.Trigger; + +/** + * This test the CronTrigger as a timer endpoint in a route. + * @version + */ +public class QuartzStatefulJobRouteTest extends CamelTestSupport { + + @Test + public void testQuartzCronRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(3); + + assertMockEndpointsSatisfied(); + + Trigger trigger = mock.getReceivedExchanges().get(0).getIn().getHeader("trigger", Trigger.class); + Assert.assertThat(trigger instanceof CronTrigger, CoreMatchers.is(true)); + + JobDetail detail = mock.getReceivedExchanges().get(0).getIn().getHeader("jobDetail", JobDetail.class); + Assert.assertThat(detail.getJobClass().equals(StatefulCamelJob.class), CoreMatchers.is(true)); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // triggers every 2th second at precise 00,02,04,06..58 + // notice we must use + as space when configured using URI parameter + from("quartz2://myGroup/myTimerName?cron=0/2+*+*+*+*+?&stateful=true").to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextRestartTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextRestartTest.java new file mode 100644 index 0000000000000..bef8a971b68ab --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextRestartTest.java @@ -0,0 +1,86 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzTwoCamelContextRestartTest { + + private DefaultCamelContext camel1; + private DefaultCamelContext camel2; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://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("quartz2://myOtherGroup/myOtherTimerName?cron=0/1+*+*+*+*+?").to("log:two", "mock:two"); + } + }); + camel2.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + camel2.stop(); + } + + @Test + public void testTwoCamelContextRestart() throws Exception { + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + + MockEndpoint mock2 = camel2.getEndpoint("mock:two", MockEndpoint.class); + mock2.expectedMinimumMessageCount(6); + mock1.assertIsSatisfied(); + + camel1.stop(); + + mock2.assertIsSatisfied(); + + // should resume triggers when we start camel 1 again + // fetch mock endpoint again because we have stopped camel context + mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(3); + camel1.start(); + + mock1.assertIsSatisfied(); + } + + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSameNameClashTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSameNameClashTest.java new file mode 100644 index 0000000000000..0a90154018193 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSameNameClashTest.java @@ -0,0 +1,86 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzTwoCamelContextSameNameClashTest extends Assert { + + private DefaultCamelContext camel1; + private DefaultCamelContext camel2; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("myCamel"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?") + .log("Fired one") + .to("mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + camel2.setName("myCamel"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myOtherGroup/myOtherTimerName?cron=0/1+*+*+*+*+?") + .log("Fired two") + .to("mock:two"); + } + }); + camel2.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + camel2.stop(); + } + + @Test + public void testTwoCamelContext() throws Exception { + assertNotSame(camel1.getManagementName(), camel2.getManagementName()); + + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + + MockEndpoint mock2 = camel2.getEndpoint("mock:two", MockEndpoint.class); + mock2.expectedMinimumMessageCount(6); + mock1.assertIsSatisfied(); + + camel1.stop(); + + mock2.assertIsSatisfied(); + + camel2.stop(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSuspendResumeTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSuspendResumeTest.java new file mode 100644 index 0000000000000..c55c8483c9704 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextSuspendResumeTest.java @@ -0,0 +1,85 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzTwoCamelContextSuspendResumeTest { + + private DefaultCamelContext camel1; + private DefaultCamelContext camel2; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + camel2.setName("camel-2"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myOtherGroup/myOtherTimerName?cron=0/1+*+*+*+*+?").to("mock:two"); + } + }); + camel2.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + camel2.stop(); + } + + @Test + public void testTwoCamelContextRestart() throws Exception { + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + + MockEndpoint mock2 = camel2.getEndpoint("mock:two", MockEndpoint.class); + mock2.expectedMinimumMessageCount(6); + mock1.assertIsSatisfied(); + + camel1.suspend(); + + mock2.assertIsSatisfied(); + + // should resume triggers when we start camel 1 again + mock1.reset(); + mock1.expectedMinimumMessageCount(2); + camel1.resume(); + + mock1.assertIsSatisfied(); + } + + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextTest.java new file mode 100644 index 0000000000000..9fbd053c704e7 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzTwoCamelContextTest.java @@ -0,0 +1,103 @@ +/** + * 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.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @version + */ +public class QuartzTwoCamelContextTest extends Assert { + + private DefaultCamelContext camel1; + private DefaultCamelContext camel2; + + @Before + public void setUp() throws Exception { + camel1 = new DefaultCamelContext(); + camel1.setName("camel-1"); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/1+*+*+*+*+?").to("mock:one"); + } + }); + camel1.start(); + + camel2 = new DefaultCamelContext(); + camel2.setName("camel-2"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myOtherGroup/myOtherTimerName?cron=0/1+*+*+*+*+?").to("mock:two"); + } + }); + camel2.start(); + } + + @After + public void tearDown() throws Exception { + camel1.stop(); + camel2.stop(); + } + + @Test + public void testTwoCamelContext() throws Exception { + MockEndpoint mock1 = camel1.getEndpoint("mock:one", MockEndpoint.class); + mock1.expectedMinimumMessageCount(2); + + MockEndpoint mock2 = camel2.getEndpoint("mock:two", MockEndpoint.class); + mock2.expectedMinimumMessageCount(6); + mock1.assertIsSatisfied(); + + camel1.stop(); + + mock2.assertIsSatisfied(); + + camel2.stop(); + } + + @Test + public void testThirdCamelContext() throws Exception { + camel1.stop(); + + camel2.stop(); + + DefaultCamelContext camel3 = new DefaultCamelContext(); + camel3.setName("camel-3"); + camel3.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myThirdGroup/myThirdTimerName?cron=0/1+*+*+*+*+?").to("mock:three"); + } + }); + camel3.start(); + + MockEndpoint mock3 = camel3.getEndpoint("mock:three", MockEndpoint.class); + mock3.expectedMinimumMessageCount(2); + + mock3.assertIsSatisfied(); + camel3.stop(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.java new file mode 100644 index 0000000000000..a81e4ac468864 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.java @@ -0,0 +1,43 @@ +/** + * 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.quartz2; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringQuartzCronRouteTest extends CamelSpringTestSupport { + + @Override + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.xml"); + } + + @Test + public void testQuartzCronRoute() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(3); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartAppTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartAppTest.java new file mode 100644 index 0000000000000..f493b7a11035b --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartAppTest.java @@ -0,0 +1,79 @@ +/** + * 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.quartz2; + +import org.apache.camel.CamelContext; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.TestSupport; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringQuartzPersistentStoreRestartAppTest extends TestSupport { + + @Test + public void testQuartzPersistentStoreRestart() throws Exception { + // load spring app + AbstractXmlApplicationContext app = new ClassPathXmlApplicationContext("org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml"); + + app.start(); + + CamelContext camel = app.getBean("camelContext", CamelContext.class); + assertNotNull(camel); + + MockEndpoint mock = camel.getEndpoint("mock:result", MockEndpoint.class); + mock.expectedMinimumMessageCount(2); + + mock.assertIsSatisfied(); + + app.stop(); + + log.info("Restarting ..."); + log.info("Restarting ..."); + log.info("Restarting ..."); + + // NOTE: + // To test a restart where the app has crashed, then you can in QuartzEndpoint + // in the doShutdown method, then remove the following code line + // deleteTrigger(getTrigger()); + // then when we restart then there is old stale data which QuartzComponent + // is supposed to handle and start again + + // load spring app + AbstractXmlApplicationContext app2 = new ClassPathXmlApplicationContext("org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartTest.xml"); + + app2.start(); + + CamelContext camel2 = app2.getBean("camelContext", CamelContext.class); + assertNotNull(camel2); + + MockEndpoint mock2 = camel2.getEndpoint("mock:result", MockEndpoint.class); + mock2.expectedMinimumMessageCount(2); + + mock2.assertIsSatisfied(); + + app2.stop(); + + // we're done so let's properly close the application contexts + app.close(); + app2.close(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartRouteTest.java new file mode 100644 index 0000000000000..4da993fa2d9f7 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartRouteTest.java @@ -0,0 +1,66 @@ +/** + * 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.quartz2; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringQuartzPersistentStoreRestartRouteTest extends CamelSpringTestSupport { + + @Override + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml"); + } + + @Test + public void testQuartzPersistentStore() throws Exception { + // skip testing on aix + if (isPlatform("aix")) { + return; + } + + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(2); + + assertMockEndpointsSatisfied(); + + // restart route + context().stopRoute("myRoute"); + mock.reset(); + mock.expectedMessageCount(0); + + // wait a bit + Thread.sleep(2000); + + assertMockEndpointsSatisfied(); + + // start route, and we got messages again + mock.reset(); + mock.expectedMinimumMessageCount(2); + + context().startRoute("myRoute"); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.java new file mode 100644 index 0000000000000..957cee49644c5 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.java @@ -0,0 +1,43 @@ +/** + * 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.quartz2; + +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringQuartzPersistentStoreTest extends CamelSpringTestSupport { + + @Override + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml"); + } + + @Test + public void testQuartzPersistentStore() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(3); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/StatefulQuartzRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/StatefulQuartzRouteTest.java new file mode 100644 index 0000000000000..5ee23ac56e8b4 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/StatefulQuartzRouteTest.java @@ -0,0 +1,62 @@ +/** + * 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.quartz2; + +import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +import java.util.List; + +/** + * @version + */ +public class StatefulQuartzRouteTest extends CamelTestSupport { + protected MockEndpoint resultEndpoint; + + @Test + public void testQuartz() throws Exception { + resultEndpoint = getMockEndpoint("mock:result"); + resultEndpoint.expectedMessageCount(2); + resultEndpoint.message(0).header("triggerName").isEqualTo("myTimerName"); + resultEndpoint.message(0).header("triggerGroup").isEqualTo("myGroup"); + + // lets test the receive worked + resultEndpoint.assertIsSatisfied(); + + List list = resultEndpoint.getReceivedExchanges(); + for (Exchange exchange : list) { + Message in = exchange.getIn(); + log.debug("Received: " + in + " with headers: " + in.getHeaders()); + } + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() { + // START SNIPPET: example + from("quartz2://myGroup/myTimerName?trigger.repeatInterval=2&trigger.repeatCount=1&stateful=true").to("mock:result"); + // END SNIPPET: example + } + }; + } +} diff --git a/components/camel-quartz2/src/test/resources/log4j.properties b/components/camel-quartz2/src/test/resources/log4j.properties new file mode 100644 index 0000000000000..c142d6c2c7797 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/log4j.properties @@ -0,0 +1,37 @@ +## ------------------------------------------------------------------------ +## 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. +## ------------------------------------------------------------------------ + +# +# The logging properties used for eclipse testing, We want to see debug output on the console. +# +log4j.rootLogger=INFO, out + +# uncomment the following to enable camel debugging +#log4j.logger.org.apache.camel=DEBUG +log4j.logger.org.apache.camel.component.quartz2=DEBUG +log4j.logger.org.apache.camel.routepolicy.quartz2=DEBUG + +# CONSOLE appender not used by default +log4j.appender.out=org.apache.log4j.ConsoleAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1}:%-4L - %m%n + +# File appender +log4j.appender.file=org.apache.log4j.FileAppender +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.file.file=target/camel-quartz2-test.log diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.xml new file mode 100644 index 0000000000000..57276a4c1d657 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzCronRouteTest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartTest.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartTest.xml new file mode 100644 index 0000000000000..26f18fdde65bb --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreRestartTest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + myscheduler + AUTO + true + org.quartz.impl.jdbcjobstore.StdJDBCDelegate + false + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml new file mode 100644 index 0000000000000..3b055fde7c0f8 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/SpringQuartzPersistentStoreTest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + myscheduler + AUTO + true + org.quartz.impl.jdbcjobstore.StdJDBCDelegate + false + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/myquartz.properties b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/myquartz.properties new file mode 100644 index 0000000000000..5a3c6c17ee9b3 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/component/quartz2/myquartz.properties @@ -0,0 +1,26 @@ +## ------------------------------------------------------------------------ +## 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. +## ------------------------------------------------------------------------ + +org.quartz.scheduler.instanceName = MyScheduler +org.quartz.scheduler.instanceId = 2 +org.quartz.scheduler.rmi.export = false +org.quartz.scheduler.rmi.proxy = false + +org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount = 3 + +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore \ No newline at end of file diff --git a/components/camel-quartz2/src/test/resources/tables_derby.sql b/components/camel-quartz2/src/test/resources/tables_derby.sql new file mode 100644 index 0000000000000..538e577f64df0 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/tables_derby.sql @@ -0,0 +1,174 @@ +-- +-- Apache Derby scripts by Steve Stewart, updated by Ronald Pomeroy +-- Based on Srinivas Venkatarangaiah's file for Cloudscape +-- +-- Known to work with Apache Derby 10.0.2.1, or 10.6.2.1 +-- +-- Updated by Zemian Deng on 08/21/2011 +-- * Fixed nullable fields on qrtz_simprop_triggers table. +-- * Added Derby QuickStart comments and drop tables statements. +-- +-- DerbyDB + Quartz Quick Guide: +-- * Derby comes with Oracle JDK! For Java6, it default install into C:/Program Files/Sun/JavaDB on Windows. +-- 1. Create a derby.properties file under JavaDB directory, and have the following: +-- derby.connection.requireAuthentication = true +-- derby.authentication.provider = BUILTIN +-- derby.user.quartz2=quartz2123 +-- 2. Start the DB server by running bin/startNetworkServer script. +-- 3. On a new terminal, run bin/ij tool to bring up an SQL prompt, then run: +-- connect 'jdbc:derby://localhost:1527/quartz2;user=quartz2;password=quartz2123;create=true'; +-- run 'quartz/docs/dbTables/tables_derby.sql'; +-- Now in quartz.properties, you may use these properties: +-- org.quartz.dataSource.quartzDataSource.driver = org.apache.derby.jdbc.ClientDriver +-- org.quartz.dataSource.quartzDataSource.URL = jdbc:derby://localhost:1527/quartz2 +-- org.quartz.dataSource.quartzDataSource.user = quartz2 +-- org.quartz.dataSource.quartzDataSource.password = quartz2123 +-- + +-- Auto drop and reset tables +-- Derby doesn't support if exists condition on table drop, so user must manually do this step if needed to. +-- drop table qrtz_fired_triggers; +-- drop table qrtz_paused_trigger_grps; +-- drop table qrtz_scheduler_state; +-- drop table qrtz_locks; +-- drop table qrtz_simple_triggers; +-- drop table qrtz_simprop_triggers; +-- drop table qrtz_cron_triggers; +-- drop table qrtz_blob_triggers; +-- drop table qrtz_triggers; +-- drop table qrtz_job_details; +-- drop table qrtz_calendars; + +create table qrtz_job_details ( +sched_name varchar(120) not null, +job_name varchar(200) not null, +job_group varchar(200) not null, +description varchar(250) , +job_class_name varchar(250) not null, +is_durable varchar(5) not null, +is_nonconcurrent varchar(5) not null, +is_update_data varchar(5) not null, +requests_recovery varchar(5) not null, +job_data blob, +primary key (sched_name,job_name,job_group) +); + +create table qrtz_triggers( +sched_name varchar(120) not null, +trigger_name varchar(200) not null, +trigger_group varchar(200) not null, +job_name varchar(200) not null, +job_group varchar(200) not null, +description varchar(250), +next_fire_time bigint, +prev_fire_time bigint, +priority integer, +trigger_state varchar(16) not null, +trigger_type varchar(8) not null, +start_time bigint not null, +end_time bigint, +calendar_name varchar(200), +misfire_instr smallint, +job_data blob, +primary key (sched_name,trigger_name,trigger_group), +foreign key (sched_name,job_name,job_group) references qrtz_job_details(sched_name,job_name,job_group) +); + +create table qrtz_simple_triggers( +sched_name varchar(120) not null, +trigger_name varchar(200) not null, +trigger_group varchar(200) not null, +repeat_count bigint not null, +repeat_interval bigint not null, +times_triggered bigint not null, +primary key (sched_name,trigger_name,trigger_group), +foreign key (sched_name,trigger_name,trigger_group) references qrtz_triggers(sched_name,trigger_name,trigger_group) +); + +create table qrtz_cron_triggers( +sched_name varchar(120) not null, +trigger_name varchar(200) not null, +trigger_group varchar(200) not null, +cron_expression varchar(120) not null, +time_zone_id varchar(80), +primary key (sched_name,trigger_name,trigger_group), +foreign key (sched_name,trigger_name,trigger_group) references qrtz_triggers(sched_name,trigger_name,trigger_group) +); + +create table qrtz_simprop_triggers + ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + str_prop_1 varchar(512), + str_prop_2 varchar(512), + str_prop_3 varchar(512), + int_prop_1 int, + int_prop_2 int, + long_prop_1 bigint, + long_prop_2 bigint, + dec_prop_1 numeric(13,4), + dec_prop_2 numeric(13,4), + bool_prop_1 varchar(5), + bool_prop_2 varchar(5), + primary key (sched_name,trigger_name,trigger_group), + foreign key (sched_name,trigger_name,trigger_group) + references qrtz_triggers(sched_name,trigger_name,trigger_group) +); + +create table qrtz_blob_triggers( +sched_name varchar(120) not null, +trigger_name varchar(200) not null, +trigger_group varchar(200) not null, +blob_data blob, +primary key (sched_name,trigger_name,trigger_group), +foreign key (sched_name,trigger_name,trigger_group) references qrtz_triggers(sched_name,trigger_name,trigger_group) +); + +create table qrtz_calendars( +sched_name varchar(120) not null, +calendar_name varchar(200) not null, +calendar blob not null, +primary key (sched_name,calendar_name) +); + +create table qrtz_paused_trigger_grps + ( + sched_name varchar(120) not null, + trigger_group varchar(200) not null, +primary key (sched_name,trigger_group) +); + +create table qrtz_fired_triggers( +sched_name varchar(120) not null, +entry_id varchar(95) not null, +trigger_name varchar(200) not null, +trigger_group varchar(200) not null, +instance_name varchar(200) not null, +fired_time bigint not null, +sched_time bigint not null, +priority integer not null, +state varchar(16) not null, +job_name varchar(200), +job_group varchar(200), +is_nonconcurrent varchar(5), +requests_recovery varchar(5), +primary key (sched_name,entry_id) +); + +create table qrtz_scheduler_state + ( + sched_name varchar(120) not null, + instance_name varchar(200) not null, + last_checkin_time bigint not null, + checkin_interval bigint not null, +primary key (sched_name,instance_name) +); + +create table qrtz_locks + ( + sched_name varchar(120) not null, + lock_name varchar(40) not null, +primary key (sched_name,lock_name) +); + From 750d4ac4b4bd640e867ca9e5308cbeb324bfd3e9 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Fri, 26 Jul 2013 19:59:39 +0100 Subject: [PATCH 02/16] Modified pom for camel-quartz2. --- components/pom.xml | 1 + parent/pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/components/pom.xml b/components/pom.xml index 3f5713d2d0b54..817b6d4523200 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -136,6 +136,7 @@ camel-printer camel-protobuf camel-quartz + camel-quartz2 camel-quickfix camel-rabbitmq camel-restlet diff --git a/parent/pom.xml b/parent/pom.xml index 5e8fb318ab75d..48944afa1e4fc 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -317,6 +317,7 @@ 0.20 1.8.6_1 1.8.6 + 2.2.0 1.5.3_1 3.1.3 1.4_1 From bc3088c9b88724e296f9d539739e3064ea9e1fc4 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 02:46:10 -0400 Subject: [PATCH 03/16] Added and ported routepolicy for camel-quartz2 from old ones. --- .../quartz2/CronScheduledRoutePolicy.java | 149 ++++++++++ .../routepolicy/quartz2/ScheduledJob.java | 62 ++++ .../quartz2/ScheduledJobState.java | 37 +++ .../quartz2/ScheduledRouteDetails.java | 96 +++++++ .../quartz2/ScheduledRoutePolicy.java | 272 ++++++++++++++++++ .../ScheduledRoutePolicyConstants.java | 41 +++ .../quartz2/SimpleScheduledRoutePolicy.java | 222 ++++++++++++++ .../quartz2/CronScheduledRoutePolicyTest.java | 241 ++++++++++++++++ .../quartz2/MultiplePoliciesOnRouteTest.java | 94 ++++++ .../routepolicy/quartz2/MyRoutePolicy.java | 47 +++ ...eAutoStopFalseCronScheduledPolicyTest.java | 56 ++++ .../camel/routepolicy/quartz2/SimpleDate.java | 33 +++ ...impleScheduledCombinedRoutePolicyTest.java | 74 +++++ .../SimpleScheduledRoutePolicyTest.java | 256 +++++++++++++++++ .../SpringCronScheduledRoutePolicyTest.java | 51 ++++ .../SpringMultiplePoliciesOnRouteTest.java | 50 ++++ .../SpringScheduledRoutePolicyTest.java | 139 +++++++++ .../SpringSimpleScheduledRoutePolicyTest.java | 51 ++++ .../routepolicy/quartz2/CronPolicies.xml | 52 ++++ .../routepolicy/quartz2/MultiplePolicies.xml | 44 +++ .../routepolicy/quartz2/SimplePolicies.xml | 60 ++++ .../routepolicy/quartz2/myquartz.properties | 26 ++ 22 files changed, 2153 insertions(+) create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicy.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJobState.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicyConstants.java create mode 100644 components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicy.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MultiplePoliciesOnRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MyRoutePolicy.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/RouteAutoStopFalseCronScheduledPolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleDate.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledCombinedRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringCronScheduledRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringMultiplePoliciesOnRouteTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringScheduledRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringSimpleScheduledRoutePolicyTest.java create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/CronPolicies.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/MultiplePolicies.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/SimplePolicies.xml create mode 100644 components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/myquartz.properties diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicy.java new file mode 100644 index 0000000000000..ba840cd1a4908 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicy.java @@ -0,0 +1,149 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.util.ObjectHelper; +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; + +import java.util.concurrent.TimeUnit; + +public class CronScheduledRoutePolicy extends ScheduledRoutePolicy implements ScheduledRoutePolicyConstants { + private String routeStartTime; + private String routeStopTime; + private String routeSuspendTime; + private String routeResumeTime; + + public void onInit(Route route) { + try { + doOnInit(route); + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + + protected void doOnInit(Route route) throws Exception { + QuartzComponent quartz = route.getRouteContext().getCamelContext().getComponent("quartz2", QuartzComponent.class); + setScheduler(quartz.getScheduler()); + + // Important: do not start scheduler as QuartzComponent does that automatic + // when CamelContext has been fully initialized and started + + if (getRouteStopGracePeriod() == 0) { + setRouteStopGracePeriod(10000); + } + + if (getTimeUnit() == null) { + setTimeUnit(TimeUnit.MILLISECONDS); + } + + // validate time options has been configured + if ((getRouteStartTime() == null) && (getRouteStopTime() == null) && (getRouteSuspendTime() == null) && (getRouteResumeTime() == null)) { + throw new IllegalArgumentException("Scheduled Route Policy for route {} has no stop/stop/suspend/resume times specified"); + } + + registerRouteToScheduledRouteDetails(route); + if (getRouteStartTime() != null) { + scheduleRoute(Action.START, route); + } + if (getRouteStopTime() != null) { + scheduleRoute(Action.STOP, route); + } + + if (getRouteSuspendTime() != null) { + scheduleRoute(Action.SUSPEND, route); + } + if (getRouteResumeTime() != null) { + scheduleRoute(Action.RESUME, route); + } + } + + public void onRemove(Route route) { + try { + // stop and un-schedule jobs + doStop(); + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + + @Override + protected Trigger createTrigger(Action action, Route route) throws Exception { + CronTrigger trigger = null; + + if (action == Action.START) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_START + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(CronScheduleBuilder.cronSchedule(getRouteStartTime())) + .build(); + } else if (action == Action.STOP) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_STOP + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(CronScheduleBuilder.cronSchedule(getRouteStopTime())) + .build(); + } else if (action == Action.SUSPEND) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_SUSPEND + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(CronScheduleBuilder.cronSchedule(getRouteSuspendTime())) + .build(); + } else if (action == Action.RESUME) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_RESUME + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(CronScheduleBuilder.cronSchedule(getRouteResumeTime())) + .build(); + } + + return trigger; + } + + public void setRouteStartTime(String routeStartTime) { + this.routeStartTime = routeStartTime; + } + + public String getRouteStartTime() { + return routeStartTime; + } + + public void setRouteStopTime(String routeStopTime) { + this.routeStopTime = routeStopTime; + } + + public String getRouteStopTime() { + return routeStopTime; + } + + public void setRouteSuspendTime(String routeSuspendTime) { + this.routeSuspendTime = routeSuspendTime; + } + + public String getRouteSuspendTime() { + return routeSuspendTime; + } + + public void setRouteResumeTime(String routeResumeTime) { + this.routeResumeTime = routeResumeTime; + } + + public String getRouteResumeTime() { + return routeResumeTime; + } + +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java new file mode 100644 index 0000000000000..2731af7d80d31 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java @@ -0,0 +1,62 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; +import org.apache.camel.spi.RoutePolicy; +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.util.List; + +public class ScheduledJob implements Job, Serializable, ScheduledRoutePolicyConstants { + private static final transient Logger LOG = LoggerFactory.getLogger(ScheduledJob.class); + + private static final long serialVersionUID = 26L; + + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + LOG.debug("Running ScheduledJob: jobExecutionContext={}", jobExecutionContext); + + SchedulerContext schedulerContext = getSchedulerContext(jobExecutionContext); + ScheduledJobState state = (ScheduledJobState) schedulerContext.get(jobExecutionContext.getJobDetail().getKey().getName()); + Action storedAction = state.getAction(); + Route storedRoute = state.getRoute(); + + List policyList = storedRoute.getRouteContext().getRoutePolicyList(); + for (RoutePolicy policy : policyList) { + try { + if (policy instanceof ScheduledRoutePolicy) { + ((ScheduledRoutePolicy)policy).onJobExecute(storedAction, storedRoute); + } + } catch (Exception e) { + throw new JobExecutionException("Failed to execute Scheduled Job for route " + storedRoute.getId() + + " with trigger name: " + jobExecutionContext.getTrigger().getKey(), e); + } + } + } + + private SchedulerContext getSchedulerContext(JobExecutionContext jobExecutionContext) throws JobExecutionException { + try { + return jobExecutionContext.getScheduler().getContext(); + } catch (SchedulerException e) { + throw new JobExecutionException("Failed to obtain scheduler context for job " + jobExecutionContext.getJobDetail().getKey()); + } + } + +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJobState.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJobState.java new file mode 100644 index 0000000000000..e628506629623 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJobState.java @@ -0,0 +1,37 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; + +public class ScheduledJobState { + private final ScheduledRoutePolicyConstants.Action action; + private final Route route; + + public ScheduledJobState(ScheduledRoutePolicyConstants.Action action, Route route) { + this.action = action; + this.route = route; + } + + public ScheduledRoutePolicyConstants.Action getAction() { + return action; + } + + public Route getRoute() { + return route; + } +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java new file mode 100644 index 0000000000000..2dbf1c49672e4 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java @@ -0,0 +1,96 @@ +/** + * 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.routepolicy.quartz2; + +import org.quartz.JobDetail; +import org.quartz.Trigger; + +public class ScheduledRouteDetails { + private JobDetail startJobDetail; + private JobDetail stopJobDetail; + private JobDetail suspendJobDetail; + private JobDetail resumeJobDetail; + private Trigger startTrigger; + private Trigger stopTrigger; + private Trigger suspendTrigger; + private Trigger resumeTrigger; + + public JobDetail getStartJobDetail() { + return startJobDetail; + } + + public void setStartJobDetail(JobDetail startJobDetail) { + this.startJobDetail = startJobDetail; + } + + public JobDetail getStopJobDetail() { + return stopJobDetail; + } + + public void setStopJobDetail(JobDetail stopJobDetail) { + this.stopJobDetail = stopJobDetail; + } + + public JobDetail getSuspendJobDetail() { + return suspendJobDetail; + } + + public void setSuspendJobDetail(JobDetail suspendJobDetail) { + this.suspendJobDetail = suspendJobDetail; + } + + public Trigger getStartTrigger() { + return startTrigger; + } + + public void setStartTrigger(Trigger startTrigger) { + this.startTrigger = startTrigger; + } + + public Trigger getStopTrigger() { + return stopTrigger; + } + + public void setStopTrigger(Trigger stopTrigger) { + this.stopTrigger = stopTrigger; + } + + public Trigger getSuspendTrigger() { + return suspendTrigger; + } + + public void setSuspendTrigger(Trigger suspendTrigger) { + this.suspendTrigger = suspendTrigger; + } + + public void setResumeJobDetail(JobDetail resumeJobDetail) { + this.resumeJobDetail = resumeJobDetail; + } + + public JobDetail getResumeJobDetail() { + return resumeJobDetail; + } + + public void setResumeTrigger(Trigger resumeTrigger) { + this.resumeTrigger = resumeTrigger; + } + + public Trigger getResumeTrigger() { + return resumeTrigger; + } + +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java new file mode 100644 index 0000000000000..63537f75cc4aa --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java @@ -0,0 +1,272 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; +import org.apache.camel.ServiceStatus; +import org.apache.camel.impl.RoutePolicySupport; +import org.apache.camel.util.ServiceHelper; +import org.quartz.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public abstract class ScheduledRoutePolicy extends RoutePolicySupport implements ScheduledRoutePolicyConstants { + private static final transient Logger LOG = LoggerFactory.getLogger(ScheduledRoutePolicy.class); + protected Map scheduledRouteDetailsMap = new LinkedHashMap(); + private Scheduler scheduler; + private int routeStopGracePeriod; + private TimeUnit timeUnit; + + protected abstract Trigger createTrigger(Action action, Route route) throws Exception; + + protected void onJobExecute(Action action, Route route) throws Exception { + LOG.debug("Scheduled Event notification received. Performing action: {} on route: {}", action, route.getId()); + + ServiceStatus routeStatus = route.getRouteContext().getCamelContext().getRouteStatus(route.getId()); + if (action == Action.START) { + if (routeStatus == ServiceStatus.Stopped) { + startRoute(route); + // here we just check the states of the Consumer + } else if (ServiceHelper.isSuspended(route.getConsumer())) { + startConsumer(route.getConsumer()); + } + } else if (action == Action.STOP) { + if ((routeStatus == ServiceStatus.Started) || (routeStatus == ServiceStatus.Suspended)) { + stopRoute(route, getRouteStopGracePeriod(), getTimeUnit()); + } else { + LOG.warn("Route is not in a started/suspended state and cannot be stopped. The current route state is {}", routeStatus); + } + } else if (action == Action.SUSPEND) { + if (routeStatus == ServiceStatus.Started) { + stopConsumer(route.getConsumer()); + } else { + LOG.warn("Route is not in a started state and cannot be suspended. The current route state is {}", routeStatus); + } + } else if (action == Action.RESUME) { + if (routeStatus == ServiceStatus.Started) { + if (ServiceHelper.isSuspended(route.getConsumer())) { + startConsumer(route.getConsumer()); + } else { + LOG.warn("The Consumer {} is not suspended and cannot be resumed.", route.getConsumer()); + } + } else { + LOG.warn("Route is not in a started state and cannot be resumed. The current route state is {}", routeStatus); + } + } + } + + public void scheduleRoute(Action action, Route route) throws Exception { + JobDetail jobDetail = createJobDetail(action, route); + Trigger trigger = createTrigger(action, route); + updateScheduledRouteDetails(action, jobDetail, trigger, route); + + loadCallbackDataIntoSchedulerContext(jobDetail, action, route); + getScheduler().scheduleJob(jobDetail, trigger); + + if (LOG.isInfoEnabled()) { + LOG.info("Scheduled trigger: {} for action: {} on route {}", new Object[]{trigger.getKey(), action, route.getId()}); + } + } + + public void pauseRouteTrigger(Action action, String routeId) throws SchedulerException { + String triggerName = retrieveTriggerName(action, routeId); + String triggerGroup = retrieveTriggerGroup(action, routeId); + + getScheduler().pauseTrigger(TriggerKey.triggerKey(triggerName, triggerGroup)); + + LOG.debug("Scheduled trigger: {}.{} is paused", triggerGroup, triggerName); + } + + public void resumeRouteTrigger(Action action, String routeId) throws SchedulerException { + String triggerName = retrieveTriggerName(action, routeId); + String triggerGroup = retrieveTriggerGroup(action, routeId); + + getScheduler().resumeTrigger(TriggerKey.triggerKey(triggerName, triggerGroup)); + + LOG.debug("Scheduled trigger: {}.{} is resumed", triggerGroup, triggerName); + } + + @Override + protected void doStop() throws Exception { + for (ScheduledRouteDetails scheduledRouteDetails : scheduledRouteDetailsMap.values()) { + if (scheduledRouteDetails.getStartJobDetail() != null) { + deleteRouteJob(Action.START, scheduledRouteDetails); + } + if (scheduledRouteDetails.getStopJobDetail() != null) { + deleteRouteJob(Action.STOP, scheduledRouteDetails); + } + if (scheduledRouteDetails.getSuspendJobDetail() != null) { + deleteRouteJob(Action.SUSPEND, scheduledRouteDetails); + } + if (scheduledRouteDetails.getResumeJobDetail() != null) { + deleteRouteJob(Action.RESUME, scheduledRouteDetails); + } + } + } + + public void deleteRouteJob(Action action, ScheduledRouteDetails scheduledRouteDetails) throws SchedulerException { + String jobDetailName = retrieveJobDetailName(action, scheduledRouteDetails); + String jobDetailGroup = retrieveJobDetailGroup(action, scheduledRouteDetails); + + if (!getScheduler().isShutdown()) { + getScheduler().deleteJob(JobKey.jobKey(jobDetailName, jobDetailGroup)); + } + + LOG.debug("Scheduled Job: {}.{} is deleted", jobDetailGroup, jobDetailName); + } + + protected JobDetail createJobDetail(Action action, Route route) throws Exception { + JobDetail jobDetail = null; + + if (action == Action.START) { + jobDetail = JobBuilder.newJob(ScheduledJob.class).withIdentity(JOB_START + route.getId(), JOB_GROUP + route.getId()).build(); + } else if (action == Action.STOP) { + jobDetail = JobBuilder.newJob(ScheduledJob.class).withIdentity(JOB_STOP + route.getId(), JOB_GROUP + route.getId()).build(); + } else if (action == Action.SUSPEND) { + jobDetail = JobBuilder.newJob(ScheduledJob.class).withIdentity(JOB_SUSPEND + route.getId(), JOB_GROUP + route.getId()).build(); + } else if (action == Action.RESUME) { + jobDetail = JobBuilder.newJob(ScheduledJob.class).withIdentity(JOB_RESUME + route.getId(), JOB_GROUP + route.getId()).build(); + } + + return jobDetail; + } + + protected void updateScheduledRouteDetails(Action action, JobDetail jobDetail, Trigger trigger, Route route) throws Exception { + ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(route.getId()); + if (action == Action.START) { + scheduledRouteDetails.setStartJobDetail(jobDetail); + scheduledRouteDetails.setStartTrigger(trigger); + } else if (action == Action.STOP) { + scheduledRouteDetails.setStopJobDetail(jobDetail); + scheduledRouteDetails.setStopTrigger(trigger); + } else if (action == Action.SUSPEND) { + scheduledRouteDetails.setSuspendJobDetail(jobDetail); + scheduledRouteDetails.setSuspendTrigger(trigger); + } else if (action == Action.RESUME) { + scheduledRouteDetails.setResumeJobDetail(jobDetail); + scheduledRouteDetails.setResumeTrigger(trigger); + } + } + + protected void loadCallbackDataIntoSchedulerContext(JobDetail jobDetail, Action action, Route route) throws SchedulerException { + getScheduler().getContext().put(jobDetail.getKey().getName(), new ScheduledJobState(action, route)); + } + + public String retrieveTriggerName(Action action, String routeId) { + ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(routeId); + String triggerName = null; + + if (action == Action.START) { + triggerName = scheduledRouteDetails.getStartTrigger().getKey().getName(); + } else if (action == Action.STOP) { + triggerName = scheduledRouteDetails.getStopTrigger().getKey().getName(); + } else if (action == Action.SUSPEND) { + triggerName = scheduledRouteDetails.getSuspendTrigger().getKey().getName(); + } else if (action == Action.RESUME) { + triggerName = scheduledRouteDetails.getResumeTrigger().getKey().getName(); + } + + return triggerName; + } + + public String retrieveTriggerGroup(Action action, String routeId) { + ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(routeId); + String triggerGroup = null; + + if (action == Action.START) { + triggerGroup = scheduledRouteDetails.getStartTrigger().getKey().getGroup(); + } else if (action == Action.STOP) { + triggerGroup = scheduledRouteDetails.getStopTrigger().getKey().getGroup(); + } else if (action == Action.SUSPEND) { + triggerGroup = scheduledRouteDetails.getSuspendTrigger().getKey().getGroup(); + } else if (action == Action.RESUME) { + triggerGroup = scheduledRouteDetails.getResumeTrigger().getKey().getGroup(); + } + + return triggerGroup; + } + + public String retrieveJobDetailName(Action action, ScheduledRouteDetails scheduledRouteDetails) { + String jobDetailName = null; + + if (action == Action.START) { + jobDetailName = scheduledRouteDetails.getStartJobDetail().getKey().getName(); + } else if (action == Action.STOP) { + jobDetailName = scheduledRouteDetails.getStopJobDetail().getKey().getName(); + } else if (action == Action.SUSPEND) { + jobDetailName = scheduledRouteDetails.getSuspendJobDetail().getKey().getName(); + } else if (action == Action.RESUME) { + jobDetailName = scheduledRouteDetails.getResumeJobDetail().getKey().getName(); + } + + return jobDetailName; + } + + public String retrieveJobDetailGroup(Action action, ScheduledRouteDetails scheduledRouteDetails) { + String jobDetailGroup = null; + + if (action == Action.START) { + jobDetailGroup = scheduledRouteDetails.getStartJobDetail().getKey().getGroup(); + } else if (action == Action.STOP) { + jobDetailGroup = scheduledRouteDetails.getStopJobDetail().getKey().getGroup(); + } else if (action == Action.SUSPEND) { + jobDetailGroup = scheduledRouteDetails.getSuspendJobDetail().getKey().getGroup(); + } else if (action == Action.RESUME) { + jobDetailGroup = scheduledRouteDetails.getResumeJobDetail().getKey().getGroup(); + } + + return jobDetailGroup; + } + + protected void registerRouteToScheduledRouteDetails(Route route) { + ScheduledRouteDetails scheduledRouteDetails = new ScheduledRouteDetails(); + scheduledRouteDetailsMap.put(route.getId(), scheduledRouteDetails); + } + + protected ScheduledRouteDetails getScheduledRouteDetails(String routeId) { + return scheduledRouteDetailsMap.get(routeId); + } + + public void setScheduler(Scheduler scheduler) { + this.scheduler = scheduler; + } + + public Scheduler getScheduler() { + return scheduler; + } + + public void setRouteStopGracePeriod(int routeStopGracePeriod) { + this.routeStopGracePeriod = routeStopGracePeriod; + } + + public int getRouteStopGracePeriod() { + return routeStopGracePeriod; + } + + public void setTimeUnit(TimeUnit timeUnit) { + this.timeUnit = timeUnit; + } + + public TimeUnit getTimeUnit() { + return timeUnit; + } + +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicyConstants.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicyConstants.java new file mode 100644 index 0000000000000..fb89e4d4dca13 --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicyConstants.java @@ -0,0 +1,41 @@ +/** + * 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.routepolicy.quartz2; + +/** + * Quartz constants. + */ +public interface ScheduledRoutePolicyConstants { + enum Action { + START, STOP, SUSPEND, RESUME + }; + + String SCHEDULED_ROUTE = "ScheduledRoute"; + String SCHEDULED_TRIGGER = "ScheduledTrigger"; + String SCHEDULED_ACTION = "ScheduledAction"; + String JOB_START = "job-" + Action.START + "-"; + String JOB_STOP = "job-" + Action.STOP + "-"; + String JOB_SUSPEND = "job-" + Action.SUSPEND + "-"; + String JOB_RESUME = "job-" + Action.RESUME + "-"; + String JOB_GROUP = "jobGroup-"; + String TRIGGER_START = "trigger-" + Action.START + "-"; + String TRIGGER_STOP = "trigger-" + Action.STOP + "-"; + String TRIGGER_SUSPEND = "trigger-" + Action.SUSPEND + "-"; + String TRIGGER_RESUME = "trigger-" + Action.RESUME + "-"; + String TRIGGER_GROUP = "triggerGroup-"; + +} diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicy.java new file mode 100644 index 0000000000000..bc93c579746da --- /dev/null +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicy.java @@ -0,0 +1,222 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.util.ObjectHelper; +import org.quartz.*; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +public class SimpleScheduledRoutePolicy extends ScheduledRoutePolicy { + private Date routeStartDate; + private int routeStartRepeatCount; + private long routeStartRepeatInterval; + private Date routeStopDate; + private int routeStopRepeatCount; + private long routeStopRepeatInterval; + private Date routeSuspendDate; + private int routeSuspendRepeatCount; + private long routeSuspendRepeatInterval; + private Date routeResumeDate; + private int routeResumeRepeatCount; + private long routeResumeRepeatInterval; + + public void onInit(Route route) { + try { + doOnInit(route); + } catch (Exception e) { + throw ObjectHelper.wrapRuntimeCamelException(e); + } + } + + protected void doOnInit(Route route) throws Exception { + QuartzComponent quartz = route.getRouteContext().getCamelContext().getComponent("quartz2", QuartzComponent.class); + setScheduler(quartz.getScheduler()); + + // Important: do not start scheduler as QuartzComponent does that automatic + // when CamelContext has been fully initialized and started + + if (getRouteStopGracePeriod() == 0) { + setRouteStopGracePeriod(10000); + } + + if (getTimeUnit() == null) { + setTimeUnit(TimeUnit.MILLISECONDS); + } + + // validate time options has been configured + if ((getRouteStartDate() == null) && (getRouteStopDate() == null) && (getRouteSuspendDate() == null) && (getRouteResumeDate() == null)) { + throw new IllegalArgumentException("Scheduled Route Policy for route {} has no stop/stop/suspend/resume times specified"); + } + + registerRouteToScheduledRouteDetails(route); + if (getRouteStartDate() != null) { + scheduleRoute(Action.START, route); + } + if (getRouteStopDate() != null) { + scheduleRoute(Action.STOP, route); + } + + if (getRouteSuspendDate() != null) { + scheduleRoute(Action.SUSPEND, route); + } + if (getRouteResumeDate() != null) { + scheduleRoute(Action.RESUME, route); + } + } + + @Override + protected Trigger createTrigger(Action action, Route route) throws Exception { + SimpleTrigger trigger = null; + + if (action == Action.START) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_START + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(getRouteStartRepeatCount()) + .withIntervalInMilliseconds(getRouteStartRepeatInterval())) + .startAt(routeStartDate == null ? new Date() : routeStartDate) + .build(); + } else if (action == Action.STOP) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_STOP + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(getRouteStopRepeatCount()) + .withIntervalInMilliseconds(getRouteStopRepeatInterval())) + .startAt(routeStopDate == null ? new Date() : routeStopDate) + .build(); + } else if (action == Action.SUSPEND) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_SUSPEND + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(getRouteSuspendRepeatCount()) + .withIntervalInMilliseconds(getRouteSuspendRepeatInterval())) + .startAt(routeSuspendDate == null ? new Date() : routeSuspendDate) + .build(); + } else if (action == Action.RESUME) { + trigger = TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_RESUME + route.getId(), TRIGGER_GROUP + route.getId()) + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(getRouteResumeRepeatCount()) + .withIntervalInMilliseconds(getRouteResumeRepeatInterval())) + .startAt(routeResumeDate == null ? new Date() : routeResumeDate) + .build(); + } + + return trigger; + } + + public Date getRouteStartDate() { + return routeStartDate; + } + + public void setRouteStartDate(Date routeStartDate) { + this.routeStartDate = routeStartDate; + } + + public Date getRouteStopDate() { + return routeStopDate; + } + + public void setRouteStopDate(Date routeStopDate) { + this.routeStopDate = routeStopDate; + } + + public Date getRouteSuspendDate() { + return routeSuspendDate; + } + + public void setRouteSuspendDate(Date routeSuspendDate) { + this.routeSuspendDate = routeSuspendDate; + } + + public int getRouteStartRepeatCount() { + return routeStartRepeatCount; + } + + public void setRouteStartRepeatCount(int routeStartRepeatCount) { + this.routeStartRepeatCount = routeStartRepeatCount; + } + + public long getRouteStartRepeatInterval() { + return routeStartRepeatInterval; + } + + public void setRouteStartRepeatInterval(long routeStartRepeatInterval) { + this.routeStartRepeatInterval = routeStartRepeatInterval; + } + + public int getRouteStopRepeatCount() { + return routeStopRepeatCount; + } + + public void setRouteStopRepeatCount(int routeStopRepeatCount) { + this.routeStopRepeatCount = routeStopRepeatCount; + } + + public long getRouteStopRepeatInterval() { + return routeStopRepeatInterval; + } + + public void setRouteStopRepeatInterval(long routeStopRepeatInterval) { + this.routeStopRepeatInterval = routeStopRepeatInterval; + } + + public int getRouteSuspendRepeatCount() { + return routeSuspendRepeatCount; + } + + public void setRouteSuspendRepeatCount(int routeSuspendRepeatCount) { + this.routeSuspendRepeatCount = routeSuspendRepeatCount; + } + + public long getRouteSuspendRepeatInterval() { + return routeSuspendRepeatInterval; + } + + public void setRouteSuspendRepeatInterval(long routeSuspendRepeatInterval) { + this.routeSuspendRepeatInterval = routeSuspendRepeatInterval; + } + + public void setRouteResumeDate(Date routeResumeDate) { + this.routeResumeDate = routeResumeDate; + } + + public Date getRouteResumeDate() { + return routeResumeDate; + } + + public void setRouteResumeRepeatCount(int routeResumeRepeatCount) { + this.routeResumeRepeatCount = routeResumeRepeatCount; + } + + public int getRouteResumeRepeatCount() { + return routeResumeRepeatCount; + } + + public void setRouteResumeRepeatInterval(long routeResumeRepeatInterval) { + this.routeResumeRepeatInterval = routeResumeRepeatInterval; + } + + public long getRouteResumeRepeatInterval() { + return routeResumeRepeatInterval; + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicyTest.java new file mode 100644 index 0000000000000..1d508010df06e --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/CronScheduledRoutePolicyTest.java @@ -0,0 +1,241 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Consumer; +import org.apache.camel.ServiceStatus; +import org.apache.camel.SuspendableService; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.apache.camel.util.ServiceHelper; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +/** + * @version + */ +public class CronScheduledRoutePolicyTest extends CamelTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testScheduledStartRoutePolicyWithTwoRoutes() throws Exception { + MockEndpoint success1 = context.getEndpoint("mock:success1", MockEndpoint.class); + MockEndpoint success2 = context.getEndpoint("mock:success2", MockEndpoint.class); + success1.expectedMessageCount(1); + success2.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStartTime("*/3 * * * * ?"); + + from("direct:start1") + .routeId("test1") + .routePolicy(policy) + .to("mock:success1"); + + from("direct:start2") + .routeId("test2") + .routePolicy(policy) + .to("mock:success2"); + } + }); + context.start(); + context.stopRoute("test1", 0, TimeUnit.MILLISECONDS); + context.stopRoute("test2", 0, TimeUnit.MILLISECONDS); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test1") == ServiceStatus.Started); + assertTrue(context.getRouteStatus("test2") == ServiceStatus.Started); + template.sendBody("direct:start1", "Ready or not, Here, I come"); + template.sendBody("direct:start2", "Ready or not, Here, I come"); + + success1.assertIsSatisfied(); + success2.assertIsSatisfied(); + } + + @Test + public void testScheduledStopRoutePolicyWithTwoRoutes() throws Exception { + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStopTime("*/3 * * * * ?"); + policy.setRouteStopGracePeriod(0); + policy.setTimeUnit(TimeUnit.MILLISECONDS); + + from("direct:start1") + .routeId("test1") + .routePolicy(policy) + .to("mock:unreachable"); + + from("direct:start2") + .routeId("test2") + .routePolicy(policy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(5000); + + assertTrue(context.getRouteStatus("test1") == ServiceStatus.Stopped); + assertTrue(context.getRouteStatus("test2") == ServiceStatus.Stopped); + } + + @Test + public void testScheduledStartRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStartTime("*/3 * * * * ?"); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + context.stopRoute("test", 0, TimeUnit.MILLISECONDS); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Started); + template.sendBody("direct:start", "Ready or not, Here, I come"); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + + @Test + public void testScheduledStopRoutePolicy() throws Exception { + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStopTime("*/3 * * * * ?"); + policy.setRouteStopGracePeriod(0); + policy.setTimeUnit(TimeUnit.MILLISECONDS); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Stopped); + } + + @Test + public void testScheduledStopRoutePolicyWithExtraPolicy() throws Exception { + final MyRoutePolicy myPolicy = new MyRoutePolicy(); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStopTime("*/3 * * * * ?"); + policy.setRouteStopGracePeriod(0); + policy.setTimeUnit(TimeUnit.MILLISECONDS); + + from("direct:start") + .routeId("test") + .routePolicy(policy, myPolicy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(5000); + + assertTrue(context.getRouteStatus("test") == ServiceStatus.Stopped); + assertTrue("Should have called onStart", myPolicy.isStart()); + assertTrue("Should have called onStop", myPolicy.isStop()); + } + + @Test + public void testScheduledSuspendRoutePolicy() throws Exception { + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteSuspendTime("*/3 * * * * ?"); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(5000); + + // when suspending its only the consumer that suspends + // there is a ticket to improve this + Consumer consumer = context.getRoute("test").getConsumer(); + SuspendableService ss = (SuspendableService) consumer; + assertTrue("Consumer should be suspended", ss.isSuspended()); + } + + @Test + public void testScheduledResumeRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteResumeTime("*/3 * * * * ?"); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + + ServiceHelper.suspendService(context.getRoute("test").getConsumer()); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Started); + + template.sendBody("direct:start", "Ready or not, Here, I come"); + + success.assertIsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MultiplePoliciesOnRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MultiplePoliciesOnRouteTest.java new file mode 100644 index 0000000000000..1aeff90e45b42 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MultiplePoliciesOnRouteTest.java @@ -0,0 +1,94 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.ServiceStatus; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.impl.ThrottlingInflightRoutePolicy; +import org.apache.camel.spi.RoutePolicy; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +import java.util.Date; + +/** + * @version + */ +public class MultiplePoliciesOnRouteTest extends CamelTestSupport { + private String url = "seda:foo?concurrentConsumers=20"; + private int size = 100; + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry registry = new JndiRegistry(createJndiContext()); + registry.bind("startPolicy", createRouteStartPolicy()); + registry.bind("throttlePolicy", createThrottlePolicy()); + return registry; + } + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + private RoutePolicy createRouteStartPolicy() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000L; + policy.setRouteStartDate(new Date(startTime)); + policy.setRouteStartRepeatCount(1); + policy.setRouteStartRepeatInterval(3000); + + return policy; + } + + private RoutePolicy createThrottlePolicy() { + ThrottlingInflightRoutePolicy policy = new ThrottlingInflightRoutePolicy(); + policy.setMaxInflightExchanges(10); + return policy; + } + + @Test + public void testMultiplePoliciesOnRoute() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMinimumMessageCount(size - 10); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + from(url) + .routeId("test") + .routePolicyRef("startPolicy, throttlePolicy") + .to("log:foo?groupSize=10") + .to("mock:success"); + } + }); + context.start(); + + assertTrue(context.getRouteStatus("test") == ServiceStatus.Started); + for (int i = 0; i < size; i++) { + template.sendBody(url, "Message " + i); + Thread.sleep(3); + } + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MyRoutePolicy.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MyRoutePolicy.java new file mode 100644 index 0000000000000..5df18612a9f98 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/MyRoutePolicy.java @@ -0,0 +1,47 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.Route; +import org.apache.camel.impl.RoutePolicySupport; + +/** + * + */ +public class MyRoutePolicy extends RoutePolicySupport { + + private boolean start; + private boolean stop; + + @Override + public void onStart(Route route) { + start = true; + } + + @Override + public void onStop(Route route) { + stop = true; + } + + public boolean isStart() { + return start; + } + + public boolean isStop() { + return stop; + } +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/RouteAutoStopFalseCronScheduledPolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/RouteAutoStopFalseCronScheduledPolicyTest.java new file mode 100644 index 0000000000000..2a459a40e7c73 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/RouteAutoStopFalseCronScheduledPolicyTest.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.routepolicy.quartz2; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +/** + * Test CronScheduledRoutePolicy also works if the route has been configured + * with noAutoStartup + */ +public class RouteAutoStopFalseCronScheduledPolicyTest extends CamelTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testCronPolicy() throws Exception { + // send a message on the seda queue so we have a message to start with + template.sendBody("seda:foo", "Hello World"); + + getMockEndpoint("mock:foo").expectedMessageCount(1); + + final CronScheduledRoutePolicy policy = new CronScheduledRoutePolicy(); + policy.setRouteStartTime("*/5 * * * * ?"); + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("seda:foo").routeId("foo").noAutoStartup() + .routePolicy(policy) + .to("mock:foo"); + } + }); + context.start(); + + assertMockEndpointsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleDate.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleDate.java new file mode 100644 index 0000000000000..3a50de7358010 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleDate.java @@ -0,0 +1,33 @@ +/** + * 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.routepolicy.quartz2; + +import java.util.Date; + +public class SimpleDate extends Date { + + private static final long serialVersionUID = 1L; + + public SimpleDate() { + this(3000); + } + + public SimpleDate(long milliseconds) { + super(System.currentTimeMillis() + milliseconds); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledCombinedRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledCombinedRoutePolicyTest.java new file mode 100644 index 0000000000000..17e254b5a8fca --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledCombinedRoutePolicyTest.java @@ -0,0 +1,74 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.ServiceStatus; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +import java.util.Date; + +/** + * @version + */ +public class SimpleScheduledCombinedRoutePolicyTest extends CamelTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testScheduledStartAndStopRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000L; + long stopTime = System.currentTimeMillis() + 8000L; + policy.setRouteStartDate(new Date(startTime)); + policy.setRouteStartRepeatCount(1); + policy.setRouteStartRepeatInterval(3000); + policy.setRouteStopDate(new Date(stopTime)); + policy.setRouteStopRepeatCount(1); + policy.setRouteStopRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Started); + template.sendBody("direct:start", "Ready or not, Here, I come"); + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Stopped); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java new file mode 100644 index 0000000000000..2f84018d3f583 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java @@ -0,0 +1,256 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.ServiceStatus; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.quartz2.QuartzComponent; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.apache.camel.util.ServiceHelper; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * @version + */ +public class SimpleScheduledRoutePolicyTest extends CamelTestSupport { + private static final transient Logger LOG = LoggerFactory.getLogger(SimpleScheduledRoutePolicyTest.class); + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testScheduledStartRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000L; + policy.setRouteStartDate(new Date(startTime)); + policy.setRouteStartRepeatCount(1); + policy.setRouteStartRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + context.stopRoute("test", 0, TimeUnit.MILLISECONDS); + + Thread.sleep(5000); + assertTrue(context.getRouteStatus("test") == ServiceStatus.Started); + template.sendBody("direct:start", "Ready or not, Here, I come"); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + + @Test + public void testScheduledStopRoutePolicy() throws Exception { + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000; + policy.setRouteStopDate(new Date(startTime)); + policy.setRouteStopRepeatCount(1); + policy.setRouteStopRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(4000); + + assertTrue(context.getRouteStatus("test") == ServiceStatus.Stopped); + + boolean consumerStopped = false; + try { + template.sendBody("direct:start", "Ready or not, Here, I come"); + } catch (CamelExecutionException e) { + consumerStopped = true; + } + assertTrue(consumerStopped); + context.getComponent("quartz2", QuartzComponent.class).stop(); + } + + @Test + public void testScheduledSuspendRoutePolicy() throws Exception { + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000L; + policy.setRouteSuspendDate(new Date(startTime)); + policy.setRouteSuspendRepeatCount(1); + policy.setRouteSuspendRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:unreachable"); + } + }); + context.start(); + + Thread.sleep(4000); + + boolean consumerSuspended = false; + try { + template.sendBody("direct:start", "Ready or not, Here, I come"); + } catch (CamelExecutionException e) { + consumerSuspended = true; + } + assertTrue(consumerSuspended); + context.getComponent("quartz2", QuartzComponent.class).stop(); + } + + @Test + public void testScheduledResumeRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long startTime = System.currentTimeMillis() + 3000L; + policy.setRouteResumeDate(new Date(startTime)); + policy.setRouteResumeRepeatCount(1); + policy.setRouteResumeRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + + ServiceHelper.suspendService(context.getRoute("test").getConsumer()); + try { + template.sendBody("direct:start", "Ready or not, Here, I come"); + fail("Should have thrown an exception"); + } catch (CamelExecutionException e) { + LOG.debug("Consumer successfully suspended"); + } + + Thread.sleep(4000); + template.sendBody("direct:start", "Ready or not, Here, I come"); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + + @Test + public void testScheduledSuspendAndResumeRoutePolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long suspendTime = System.currentTimeMillis() + 1000L; + policy.setRouteSuspendDate(new Date(suspendTime)); + policy.setRouteSuspendRepeatCount(0); + long resumeTime = System.currentTimeMillis() + 4000L; + policy.setRouteResumeDate(new Date(resumeTime)); + policy.setRouteResumeRepeatCount(1); + policy.setRouteResumeRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + Thread.sleep(1000); + + try { + template.sendBody("direct:start", "Ready or not, Here, I come"); + fail("Should have thrown an exception"); + } catch (CamelExecutionException e) { + LOG.debug("Consumer successfully suspended"); + } + + Thread.sleep(4000); + template.sendBody("direct:start", "Ready or not, Here, I come"); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + + @Test + public void testScheduledSuspendAndRestartPolicy() throws Exception { + MockEndpoint success = context.getEndpoint("mock:success", MockEndpoint.class); + success.expectedMessageCount(1); + + context.getComponent("quartz2", QuartzComponent.class).setPropertiesFile("org/apache/camel/routepolicy/quartz2/myquartz.properties"); + context.addRoutes(new RouteBuilder() { + public void configure() { + SimpleScheduledRoutePolicy policy = new SimpleScheduledRoutePolicy(); + long suspendTime = System.currentTimeMillis() + 1000L; + policy.setRouteSuspendDate(new Date(suspendTime)); + policy.setRouteSuspendRepeatCount(0); + long startTime = System.currentTimeMillis() + 4000L; + policy.setRouteStartDate(new Date(startTime)); + policy.setRouteResumeRepeatCount(1); + policy.setRouteResumeRepeatInterval(3000); + + from("direct:start") + .routeId("test") + .routePolicy(policy) + .to("mock:success"); + } + }); + context.start(); + Thread.sleep(1000); + + try { + template.sendBody("direct:start", "Ready or not, Here, I come"); + fail("Should have thrown an exception"); + } catch (CamelExecutionException e) { + LOG.debug("Consumer successfully suspended"); + } + + Thread.sleep(4000); + template.sendBody("direct:start", "Ready or not, Here, I come"); + + context.getComponent("quartz2", QuartzComponent.class).stop(); + success.assertIsSatisfied(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringCronScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringCronScheduledRoutePolicyTest.java new file mode 100644 index 0000000000000..2a7d97fdac4f7 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringCronScheduledRoutePolicyTest.java @@ -0,0 +1,51 @@ +/** + * 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.routepolicy.quartz2; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpringCronScheduledRoutePolicyTest extends SpringScheduledRoutePolicyTest { + + @Before + public void setUp() { + setApplicationContext(new ClassPathXmlApplicationContext("org/apache/camel/routepolicy/quartz2/CronPolicies.xml")); + setTestType(TestType.CRON); + } + + @Test + public void testScheduledStartRoutePolicy() throws Exception { + startTest(); + } + + @Test + public void testScheduledStopRoutePolicy() throws Exception { + stopTest(); + } + + @Test + public void testScheduledSuspendRoutePolicy() throws Exception { + suspendTest(); + } + + @Test + public void testScheduledResumeRoutePolicy() throws Exception { + resumeTest(); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringMultiplePoliciesOnRouteTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringMultiplePoliciesOnRouteTest.java new file mode 100644 index 0000000000000..b370301eb1e58 --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringMultiplePoliciesOnRouteTest.java @@ -0,0 +1,50 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.test.spring.CamelSpringTestSupport; +import org.junit.Test; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @version + */ +public class SpringMultiplePoliciesOnRouteTest extends CamelSpringTestSupport { + private String url = "seda:foo?concurrentConsumers=20"; + private int size = 100; + + @Test + public void testMultiplePoliciesOnRoute() throws Exception { + // we use seda which are not persistent and hence can loose a message + // when we get graceful shutdown support we can prevent this + getMockEndpoint("mock:success").expectedMinimumMessageCount(size - 10); + + for (int i = 0; i < size; i++) { + template.sendBody(url, "Message " + i); + Thread.sleep(3); + } + + assertMockEndpointsSatisfied(); + } + + @Override + protected AbstractApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/routepolicy/quartz2/MultiplePolicies.xml"); + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringScheduledRoutePolicyTest.java new file mode 100644 index 0000000000000..b4b40ac7a62bc --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringScheduledRoutePolicyTest.java @@ -0,0 +1,139 @@ +/** + * 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.routepolicy.quartz2; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.ServiceStatus; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.model.ModelCamelContext; +import org.apache.camel.model.RouteDefinition; +import org.apache.camel.spi.RoutePolicy; +import org.apache.camel.test.junit4.TestSupport; +import org.apache.camel.util.ServiceHelper; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @version + */ +public abstract class SpringScheduledRoutePolicyTest extends TestSupport { + protected enum TestType { + SIMPLE, CRON + } + private ClassPathXmlApplicationContext applicationContext; + private TestType testType; + + public void startTest() throws Exception { + CamelContext context = startRouteWithPolicy("startPolicy"); + + MockEndpoint mock = context.getEndpoint("mock:success", MockEndpoint.class); + mock.expectedMinimumMessageCount(1); + + context.stopRoute("testRoute", 0, TimeUnit.MILLISECONDS); + + Thread.sleep(4000); + assertTrue(context.getRouteStatus("testRoute") == ServiceStatus.Started); + context.createProducerTemplate().sendBody("direct:start", "Ready or not, Here, I come"); + + context.stop(); + mock.assertIsSatisfied(); + } + + + public void stopTest() throws Exception { + boolean consumerStopped = false; + + CamelContext context = startRouteWithPolicy("stopPolicy"); + + Thread.sleep(4000); + assertTrue(context.getRouteStatus("testRoute") == ServiceStatus.Stopped); + try { + context.createProducerTemplate().sendBody("direct:start", "Ready or not, Here, I come"); + } catch (CamelExecutionException e) { + consumerStopped = true; + } + context.stop(); + assertTrue(consumerStopped); + } + + + public void suspendTest() throws Exception { + boolean consumerSuspended = false; + + CamelContext context = startRouteWithPolicy("suspendPolicy"); + + Thread.sleep(4000); + try { + context.createProducerTemplate().sendBody("direct:start", "Ready or not, Here, I come"); + } catch (CamelExecutionException e) { + consumerSuspended = true; + } + + context.stop(); + assertTrue(consumerSuspended); + } + + public void resumeTest() throws Exception { + CamelContext context = startRouteWithPolicy("resumePolicy"); + + MockEndpoint mock = context.getEndpoint("mock:success", MockEndpoint.class); + mock.expectedMinimumMessageCount(1); + + ServiceHelper.suspendService(context.getRoute("testRoute").getConsumer()); + + Thread.sleep(4000); + context.createProducerTemplate().sendBody("direct:start", "Ready or not, Here, I come"); + + context.stop(); + mock.assertIsSatisfied(); + } + + @SuppressWarnings("unchecked") + private CamelContext startRouteWithPolicy(String policyBeanName) throws Exception { + CamelContext context = new DefaultCamelContext(); + List routes = (List)applicationContext.getBean("testRouteContext"); + RoutePolicy policy = applicationContext.getBean(policyBeanName, RoutePolicy.class); + assertTrue(getTestType() == TestType.SIMPLE + ? policy instanceof SimpleScheduledRoutePolicy + : policy instanceof CronScheduledRoutePolicy); + routes.get(0).routePolicy(policy); + ((ModelCamelContext)context).addRouteDefinitions(routes); + context.start(); + return context; + } + + public ClassPathXmlApplicationContext getApplicationContext() { + return applicationContext; + } + + public void setApplicationContext(ClassPathXmlApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + public TestType getTestType() { + return testType; + } + + public void setTestType(TestType testType) { + this.testType = testType; + } + +} diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringSimpleScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringSimpleScheduledRoutePolicyTest.java new file mode 100644 index 0000000000000..98e98624df89b --- /dev/null +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SpringSimpleScheduledRoutePolicyTest.java @@ -0,0 +1,51 @@ +/** + * 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.routepolicy.quartz2; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpringSimpleScheduledRoutePolicyTest extends SpringScheduledRoutePolicyTest { + + @Before + public void setUp() { + setApplicationContext(new ClassPathXmlApplicationContext("org/apache/camel/routepolicy/quartz2/SimplePolicies.xml")); + setTestType(TestType.SIMPLE); + } + + @Test + public void testScheduledStartRoutePolicy() throws Exception { + startTest(); + } + + @Test + public void testScheduledStopRoutePolicy() throws Exception { + stopTest(); + } + + @Test + public void testScheduledSuspendRoutePolicy() throws Exception { + suspendTest(); + } + + @Test + public void testScheduledResumeRoutePolicy() throws Exception { + resumeTest(); + } + +} diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/CronPolicies.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/CronPolicies.xml new file mode 100644 index 0000000000000..63a8ea7715808 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/CronPolicies.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/MultiplePolicies.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/MultiplePolicies.xml new file mode 100644 index 0000000000000..c2b5aeefc147b --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/MultiplePolicies.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/SimplePolicies.xml b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/SimplePolicies.xml new file mode 100644 index 0000000000000..ea3aea83a8202 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/SimplePolicies.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/myquartz.properties b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/myquartz.properties new file mode 100644 index 0000000000000..5a3c6c17ee9b3 --- /dev/null +++ b/components/camel-quartz2/src/test/resources/org/apache/camel/routepolicy/quartz2/myquartz.properties @@ -0,0 +1,26 @@ +## ------------------------------------------------------------------------ +## 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. +## ------------------------------------------------------------------------ + +org.quartz.scheduler.instanceName = MyScheduler +org.quartz.scheduler.instanceId = 2 +org.quartz.scheduler.rmi.export = false +org.quartz.scheduler.rmi.proxy = false + +org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount = 3 + +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore \ No newline at end of file From ee664659c8cca27cd62996da38f43963e72a294d Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 08:21:03 -0400 Subject: [PATCH 04/16] Improved logging speed. --- .../apache/camel/component/quartz2/CamelJob.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java index b85012f15806e..69b03f412e4d1 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java @@ -15,7 +15,9 @@ public class CamelJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { Exchange exchange = null; try { - LOG.debug("Running CamelJob jobExecutionContext={}", context); + if (LOG.isDebugEnabled()) + LOG.debug("Running CamelJob jobExecutionContext={}", context); + CamelContext camelContext = getCamelContext(context); QuartzEndpoint endpoint = lookupQuartzEndpoint(camelContext, context); exchange = endpoint.createExchange(); @@ -59,7 +61,8 @@ private SchedulerContext getSchedulerContext(JobExecutionContext context) throws private QuartzEndpoint lookupQuartzEndpoint(CamelContext camelContext, JobExecutionContext quartzContext) throws JobExecutionException { TriggerKey triggerKey = quartzContext.getTrigger().getKey(); - LOG.debug("Looking up existing QuartzEndpoint with triggerKey={}", triggerKey); + if (LOG.isDebugEnabled()) + LOG.debug("Looking up existing QuartzEndpoint with triggerKey={}", triggerKey); // check all active routes for the quartz endpoint this task matches // as we prefer to use the existing endpoint from the routes @@ -67,7 +70,8 @@ private QuartzEndpoint lookupQuartzEndpoint(CamelContext camelContext, JobExecut if (route.getEndpoint() instanceof QuartzEndpoint) { QuartzEndpoint quartzEndpoint = (QuartzEndpoint) route.getEndpoint(); TriggerKey checkTriggerKey = quartzEndpoint.getTriggerKey(); - LOG.trace("Checking route endpoint={} with checkTriggerKey={}", quartzEndpoint, checkTriggerKey); + if (LOG.isTraceEnabled()) + LOG.trace("Checking route endpoint={} with checkTriggerKey={}", quartzEndpoint, checkTriggerKey); if (triggerKey.equals(checkTriggerKey)) { return quartzEndpoint; } @@ -80,7 +84,8 @@ private QuartzEndpoint lookupQuartzEndpoint(CamelContext camelContext, JobExecut // Even though the same camelContext.getEndpoint call, but if/else display different log. if (camelContext.hasEndpoint(endpointUri) != null) { - LOG.debug("Getting Endpoint from camelContext."); + if (LOG.isDebugEnabled()) + LOG.debug("Getting Endpoint from camelContext."); result = camelContext.getEndpoint(endpointUri, QuartzEndpoint.class); } else { LOG.warn("Cannot find existing QuartzEndpoint with uri: {}. Creating new endpoint instance.", endpointUri); From a0b1eda04b7a1eb497862b4ee311b4a7ecd192cb Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 09:04:15 -0400 Subject: [PATCH 05/16] Fixed correct QuartzConsumer start/stop to signal quartz pause/resume job. --- .../component/quartz2/QuartzConsumer.java | 15 +++ .../component/quartz2/QuartzEndpoint.java | 103 +++++++++++++----- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java index 39c7fa7d2d75d..7b525d47199d4 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java @@ -3,9 +3,24 @@ import org.apache.camel.Endpoint; import org.apache.camel.Processor; import org.apache.camel.impl.DefaultConsumer; +import org.quartz.Scheduler; public class QuartzConsumer extends DefaultConsumer { public QuartzConsumer(Endpoint endpoint, Processor processor) { super(endpoint, processor); } + + public QuartzEndpoint getEndpoint() { + return (QuartzEndpoint)super.getEndpoint(); + } + + @Override + protected void doStart() throws Exception { + getEndpoint().onConsumerStart(this); + } + + @Override + protected void doStop() throws Exception { + getEndpoint().onConsumerStop(this); + } } diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java index ad2b8bfc7a6e6..cc14d35eb5e74 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java @@ -13,6 +13,7 @@ import java.util.Date; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static org.quartz.CronScheduleBuilder.cronSchedule; @@ -32,6 +33,10 @@ public class QuartzEndpoint extends DefaultEndpoint { * ensure endpoint is fully started before the job kicks in. */ private long triggerStartDelay = 500; // in millis second + // An internal variables to track whether a job has been in scheduler or not, and has it paused or not. + private AtomicBoolean jobAdded = new AtomicBoolean(false); + private AtomicBoolean jobPaused = new AtomicBoolean(false); + public String getCron() { return cron; } @@ -111,7 +116,6 @@ public Producer createProducer() throws Exception { public Consumer createConsumer(Processor processor) throws Exception { QuartzConsumer result = new QuartzConsumer(this, processor); configureConsumer(result); - getConsumerLoadBalancer().addProcessor(processor); return result; } @@ -122,8 +126,32 @@ public boolean isSingleton() { @Override protected void doStart() throws Exception { - super.doStart(); + addJobInScheduler(); + } + + @Override + protected void doStop() throws Exception { + removeJobInScheduler(); + } + private void removeJobInScheduler() throws Exception { + Scheduler scheduler = getComponent().getScheduler(); + if (deleteJob && scheduler != null) { + boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); + if (!scheduler.isShutdown() && !isClustered) { + LOG.info("Deleting job {}", triggerKey); + scheduler.unscheduleJob(triggerKey); + + jobAdded.set(false); + } + } + + // Decrement camel job count for this endpoint + AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); + number.decrementAndGet(); + } + + private void addJobInScheduler() throws Exception { // Add or use existing trigger to/from scheduler Scheduler scheduler = getComponent().getScheduler(); JobDetail jobDetail = null; @@ -135,17 +163,32 @@ protected void doStart() throws Exception { updateJobDataMap(jobDetail); // Schedule it now. Remember that scheduler might not be started it, but we can schedule now. - scheduler.scheduleJob(jobDetail, trigger); + Date nextFireDate = scheduler.scheduleJob(jobDetail, trigger); + LOG.info("Job {} (triggerType={}, jobClass={}) is scheduled. Next fire date is {}", + new Object[]{ + trigger.getKey(), + trigger.getClass().getSimpleName(), + jobDetail.getJobClass().getSimpleName(), + nextFireDate}); } else { // Update existing jobDetails with current endpoint data to jobDataMap. jobDetail = scheduler.getJobDetail(trigger.getJobKey()); updateJobDataMap(jobDetail); scheduler.addJob(jobDetail, true); + Date nextFireDate = trigger.getNextFireTime(); + LOG.info("Reuse existing Job {} (triggerType={}, jobType={}) is scheduled. Next fire date is {}", + new Object[]{ + trigger.getKey(), + trigger.getClass().getSimpleName(), + jobDetail.getJobClass().getSimpleName(), + nextFireDate}); } // Increase camel job count for this endpoint AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); number.incrementAndGet(); + + jobAdded.set(true); } private void updateJobDataMap(JobDetail jobDetail) { @@ -194,7 +237,7 @@ private Trigger createTrigger() throws Exception { setProperties(result, triggerParameters); } - LOG.info("Created trigger={}", result); + LOG.debug("Created trigger={}", result); return result; } @@ -220,7 +263,7 @@ private JobDetail createJobDetail() throws Exception { setProperties(result, jobParameters); } - LOG.info("Created jobDetail={}", result); + LOG.debug("Created jobDetail={}", result); return result; } @@ -229,43 +272,43 @@ public QuartzComponent getComponent() { return (QuartzComponent)super.getComponent(); } - @Override - protected void doStop() throws Exception { - super.doStop(); + public void pauseTrigger() throws Exception { + if (jobPaused.get()) + return; + jobPaused.set(true); Scheduler scheduler = getComponent().getScheduler(); - if (deleteJob && scheduler != null) { - boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); - if (!scheduler.isShutdown() && !isClustered) { - LOG.info("Deleting job {}", triggerKey); - scheduler.unscheduleJob(triggerKey); - } + if (scheduler != null) { + LOG.info("Pausing trigger {}", triggerKey); + scheduler.pauseTrigger(triggerKey); } - - // Decrement camel job count for this endpoint - AtomicInteger number = (AtomicInteger) scheduler.getContext().get(QuartzConstants.QUARTZ_CAMEL_JOBS_COUNT); - number.decrementAndGet(); } - @Override - protected void doSuspend() throws Exception { - super.doSuspend(); + public void resumeTrigger() throws Exception { + if (!jobPaused.get()) + return; + jobPaused.set(false); Scheduler scheduler = getComponent().getScheduler(); if (scheduler != null) { - LOG.info("Pausing trigger (suspend)."); - scheduler.pauseTrigger(triggerKey); + LOG.info("Resuming trigger {}", triggerKey); + scheduler.resumeTrigger(triggerKey); } } - @Override - protected void doResume() throws Exception { - super.doResume(); + public void onConsumerStart(QuartzConsumer quartzConsumer) throws Exception { + getConsumerLoadBalancer().addProcessor(quartzConsumer.getProcessor()); + if (!jobAdded.get()) { + addJobInScheduler(); + } else { + resumeTrigger(); + } + } - Scheduler scheduler = getComponent().getScheduler(); - if (scheduler != null) { - LOG.info("Resuming trigger (resume)."); - scheduler.resumeTrigger(triggerKey); + public void onConsumerStop(QuartzConsumer quartzConsumer) throws Exception { + getConsumerLoadBalancer().removeProcessor(quartzConsumer.getProcessor()); + if (jobAdded.get()) { + pauseTrigger(); } } } From eec98a2289140ee742f2df861269b00e14585058 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 11:50:18 -0400 Subject: [PATCH 06/16] Removed scheduler standby during component resume. Fixed QuartzNameCollisionTest as PAUSED state. --- .../component/quartz2/QuartzComponent.java | 20 ------------------- .../quartz2/QuartzNameCollisionTest.java | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index 9e9931868c6a4..b11f6543f0adc 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -286,24 +286,4 @@ public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) } } } - - @Override - protected void doSuspend() throws Exception { - super.doSuspend(); - - if (scheduler != null) { - LOG.info("Standby the scheduler (suspend)."); - scheduler.standby(); - } - } - - @Override - protected void doResume() throws Exception { - super.doResume(); - - if (scheduler != null) { - LOG.info("Start the scheduler (resume)."); - scheduler.start(); - } - } } diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java index 0690468eb3c5e..c90941101a811 100644 --- a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java @@ -153,7 +153,7 @@ public void configure() throws Exception { Trigger.TriggerState triggerState = component.getScheduler().getTriggerState(triggerKey); Assert.assertNotNull(trigger); - Assert.assertEquals(Trigger.TriggerState.NORMAL, triggerState); + Assert.assertEquals(Trigger.TriggerState.PAUSED, triggerState); } @After From b42dca8e9ea738a2e4064cf004e5afe1bb3ad6d6 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 12:43:09 -0400 Subject: [PATCH 07/16] Re-added original behavior of throw duplicated trigger key name exception if existing endpoint has found. --- .../component/quartz2/QuartzEndpoint.java | 23 +++++++++-- .../quartz2/QuartzNameCollisionTest.java | 38 ++++++++++++++----- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java index cc14d35eb5e74..35a7fd2488caf 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java @@ -1,8 +1,6 @@ package org.apache.camel.component.quartz2; -import org.apache.camel.Consumer; -import org.apache.camel.Processor; -import org.apache.camel.Producer; +import org.apache.camel.*; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.processor.loadbalancer.LoadBalancer; import org.apache.camel.processor.loadbalancer.RoundRobinLoadBalancer; @@ -136,7 +134,10 @@ protected void doStop() throws Exception { private void removeJobInScheduler() throws Exception { Scheduler scheduler = getComponent().getScheduler(); - if (deleteJob && scheduler != null) { + if (scheduler == null) + return; + + if (deleteJob) { boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); if (!scheduler.isShutdown() && !isClustered) { LOG.info("Deleting job {}", triggerKey); @@ -171,6 +172,8 @@ private void addJobInScheduler() throws Exception { jobDetail.getJobClass().getSimpleName(), nextFireDate}); } else { + ensureNoDupTriggerKey(); + // Update existing jobDetails with current endpoint data to jobDataMap. jobDetail = scheduler.getJobDetail(trigger.getJobKey()); updateJobDataMap(jobDetail); @@ -191,6 +194,18 @@ private void addJobInScheduler() throws Exception { jobAdded.set(true); } + private void ensureNoDupTriggerKey() { + for (Route route : getCamelContext().getRoutes()) { + if (route.getEndpoint() instanceof QuartzEndpoint) { + QuartzEndpoint quartzEndpoint = (QuartzEndpoint) route.getEndpoint(); + TriggerKey checkTriggerKey = quartzEndpoint.getTriggerKey(); + if (triggerKey.equals(checkTriggerKey)) { + throw new IllegalArgumentException("Trigger key " + triggerKey + " is already in used by " + quartzEndpoint); + } + } + } + } + private void updateJobDataMap(JobDetail jobDetail) { // Store this camelContext name into the job data JobDataMap jobDataMap = jobDetail.getJobDataMap(); diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java index c90941101a811..70277145ed0e7 100644 --- a/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java +++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.component.quartz2; +import org.apache.camel.FailedToCreateRouteException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; import org.junit.After; @@ -44,11 +45,17 @@ public void configure() throws Exception { }); camel1.start(); - QuartzComponent component2 = new QuartzComponent(camel1); try { - component2.createEndpoint("quartz2://myGroup/myTimerName"); - } catch (IllegalArgumentException e) { - Assert.fail("Should not have thrown an exception. Due to new deleteJob option. Exception: " + e.getMessage()); + camel1.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?cron=0/2+*+*+*+*+?").to("log:two", "mock:two"); + } + }); + Assert.fail("Should have thrown an exception"); + } catch (FailedToCreateRouteException e) { + String reason = e.getMessage(); + Assert.assertEquals(reason.indexOf("Trigger key myGroup.myTimerName is already in used") >=0, true); } } @@ -65,8 +72,14 @@ public void configure() throws Exception { camel1.start(); camel2 = new DefaultCamelContext(); - QuartzComponent component2 = new QuartzComponent(camel2); - component2.createEndpoint("quartz2://myGroup/myTimerName"); + camel2.setName("camel-2"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName=0/2+*+*+*+*+?").to("log:two", "mock:two"); + } + }); + camel2.start(); } /** @@ -85,9 +98,14 @@ public void configure() throws Exception { camel1.start(); camel2 = new DefaultCamelContext(); - QuartzComponent component2 = new QuartzComponent(camel2); - - component2.createEndpoint("quartz2://myGroup/myTimerName?stateful=true"); + camel2.setName("camel-2"); + camel2.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("quartz2://myGroup/myTimerName?stateful=true").to("log:two", "mock:two"); + } + }); + camel2.start(); // if no exception is thrown then this test passed. } @@ -148,7 +166,7 @@ public void configure() throws Exception { TriggerKey triggerKey = TriggerKey.triggerKey("myTimerName", "myGroup"); Trigger trigger = scheduler.getTrigger(triggerKey); Assert.assertNotNull(trigger); - + camel1.stopRoute("route-1"); Trigger.TriggerState triggerState = component.getScheduler().getTriggerState(triggerKey); From 7f77b1bda3efe5f49687f9fcd00464848c99e323 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 13:15:23 -0400 Subject: [PATCH 08/16] - Default job group to the unique camel management name to avoid more potential name clash. - Added prefixJobNameWithEndpointId option (default=false) for more guarantee of uniqueness of job name. --- .../camel/component/quartz2/QuartzComponent.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index b11f6543f0adc..1f4e281a4a409 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -27,6 +27,7 @@ public class QuartzComponent extends DefaultComponent implements StartupListener private String propertiesFile; private int startDelayedSeconds; private boolean autoStartScheduler = true; + private boolean prefixJobNameWithEndpointId = false; public QuartzComponent() { } @@ -172,13 +173,19 @@ protected Endpoint createEndpoint(String uri, String remaining, Map triggerParameters = IntrospectionSupport.extractProperties(parameters, "trigger."); Map jobParameters = IntrospectionSupport.extractProperties(parameters, "job."); // Create quartz endpoint - TriggerKey triggerKey = createTriggerKey(uri, remaining); QuartzEndpoint result = new QuartzEndpoint(uri, this); + TriggerKey triggerKey = createTriggerKey(uri, remaining); + if (this.prefixJobNameWithEndpointId) + triggerKey = TriggerKey.triggerKey(result.getId() + "_" + triggerKey.getName(), triggerKey.getGroup()); result.setTriggerKey(triggerKey); result.setTriggerParameters(triggerParameters); result.setJobParameters(jobParameters); @@ -196,13 +203,13 @@ private TriggerKey createTriggerKey(String uri, String remaining) throws Excepti host = ObjectHelper.before(remaining, "/"); } - // Trigger group can be optional, if so set it to Camel + // Trigger group can be optional, if so set it to this context's unique name String name, group; if (ObjectHelper.isNotEmpty(path) && ObjectHelper.isNotEmpty(host)) { group = host; name = path; } else { - group = "Camel"; + group = getCamelContext().getManagementName(); name = host; } From bdc08c4c69bd5bbb9f2def59ffd1a56c1eeef6aa Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 13:16:42 -0400 Subject: [PATCH 09/16] Reset log4j settings back to normal --- .../camel-quartz2/src/test/resources/log4j.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/camel-quartz2/src/test/resources/log4j.properties b/components/camel-quartz2/src/test/resources/log4j.properties index c142d6c2c7797..86e4b3e88add4 100644 --- a/components/camel-quartz2/src/test/resources/log4j.properties +++ b/components/camel-quartz2/src/test/resources/log4j.properties @@ -18,12 +18,12 @@ # # The logging properties used for eclipse testing, We want to see debug output on the console. # -log4j.rootLogger=INFO, out +log4j.rootLogger=INFO, file # uncomment the following to enable camel debugging #log4j.logger.org.apache.camel=DEBUG -log4j.logger.org.apache.camel.component.quartz2=DEBUG -log4j.logger.org.apache.camel.routepolicy.quartz2=DEBUG +#log4j.logger.org.apache.camel.component.quartz2=DEBUG +#log4j.logger.org.apache.camel.routepolicy.quartz2=DEBUG # CONSOLE appender not used by default log4j.appender.out=org.apache.log4j.ConsoleAppender From 494002614876af682de08d1e5a3bdcd52546ebcc Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 13:39:50 -0400 Subject: [PATCH 10/16] Improved and fixed createTriggerKey method with default group value. --- .../camel/component/quartz2/QuartzComponent.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index 1f4e281a4a409..3d7777bfb21d4 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -183,16 +183,14 @@ protected Endpoint createEndpoint(String uri, String remaining, Map Date: Sat, 27 Jul 2013 14:28:07 -0400 Subject: [PATCH 11/16] Added Camel_ prefix to group name. --- .../org/apache/camel/component/quartz2/QuartzComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index 3d7777bfb21d4..8348210f713cc 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -207,7 +207,7 @@ private TriggerKey createTriggerKey(String uri, String remaining, QuartzEndpoint group = host; name = path; } else { - group = getCamelContext().getManagementName(); + group = "Camel_" + getCamelContext().getManagementName(); // There are cases where above is NULL, then we simply set to a constant value. if (group == null) From 9e7c777b26478a73f515d8556b1676eca47b0b3e Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 14:29:39 -0400 Subject: [PATCH 12/16] Added Camel_ prefix to group name, #2. --- .../apache/camel/component/quartz2/QuartzComponent.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index 8348210f713cc..fb90852ae4ff9 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -207,12 +207,8 @@ private TriggerKey createTriggerKey(String uri, String remaining, QuartzEndpoint group = host; name = path; } else { - group = "Camel_" + getCamelContext().getManagementName(); - - // There are cases where above is NULL, then we simply set to a constant value. - if (group == null) - group = "Camel"; - + String camelContextName = getCamelContext().getManagementName(); + group = camelContextName == null ? "Camel" : "Camel_" + camelContextName; name = host; } From bf1844048350b5355c9dca9b37def047627e4b63 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 15:04:35 -0400 Subject: [PATCH 13/16] Refactored routepolicy to store JobKey and TriggerKey instead of JobDetail and Trigger objects. --- .../quartz2/ScheduledRouteDetails.java | 85 +++++++------- .../quartz2/ScheduledRoutePolicy.java | 106 ++++++------------ 2 files changed, 77 insertions(+), 114 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java index 2dbf1c49672e4..755496f8076ac 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java @@ -16,81 +16,80 @@ */ package org.apache.camel.routepolicy.quartz2; -import org.quartz.JobDetail; -import org.quartz.Trigger; +import org.quartz.JobKey; +import org.quartz.TriggerKey; public class ScheduledRouteDetails { - private JobDetail startJobDetail; - private JobDetail stopJobDetail; - private JobDetail suspendJobDetail; - private JobDetail resumeJobDetail; - private Trigger startTrigger; - private Trigger stopTrigger; - private Trigger suspendTrigger; - private Trigger resumeTrigger; + private JobKey startJobKey; + private JobKey stopJobKey; + private JobKey suspendJobKey; + private JobKey resumeJobKey; + private TriggerKey startTriggerKey; + private TriggerKey stopTriggerKey; + private TriggerKey suspendTriggerKey; + private TriggerKey resumeTriggerKey; - public JobDetail getStartJobDetail() { - return startJobDetail; + public JobKey getStartJobKey() { + return startJobKey; } - public void setStartJobDetail(JobDetail startJobDetail) { - this.startJobDetail = startJobDetail; + public void setStartJobKey(JobKey startJobKey) { + this.startJobKey = startJobKey; } - public JobDetail getStopJobDetail() { - return stopJobDetail; + public JobKey getStopJobKey() { + return stopJobKey; } - public void setStopJobDetail(JobDetail stopJobDetail) { - this.stopJobDetail = stopJobDetail; + public void setStopJobKey(JobKey stopJobKey) { + this.stopJobKey = stopJobKey; } - public JobDetail getSuspendJobDetail() { - return suspendJobDetail; + public JobKey getSuspendJobKey() { + return suspendJobKey; } - public void setSuspendJobDetail(JobDetail suspendJobDetail) { - this.suspendJobDetail = suspendJobDetail; + public void setSuspendJobKey(JobKey suspendJobKey) { + this.suspendJobKey = suspendJobKey; } - public Trigger getStartTrigger() { - return startTrigger; + public JobKey getResumeJobKey() { + return resumeJobKey; } - public void setStartTrigger(Trigger startTrigger) { - this.startTrigger = startTrigger; + public void setResumeJobKey(JobKey resumeJobKey) { + this.resumeJobKey = resumeJobKey; } - public Trigger getStopTrigger() { - return stopTrigger; + public TriggerKey getStartTriggerKey() { + return startTriggerKey; } - public void setStopTrigger(Trigger stopTrigger) { - this.stopTrigger = stopTrigger; + public void setStartTriggerKey(TriggerKey startTriggerKey) { + this.startTriggerKey = startTriggerKey; } - public Trigger getSuspendTrigger() { - return suspendTrigger; + public TriggerKey getStopTriggerKey() { + return stopTriggerKey; } - public void setSuspendTrigger(Trigger suspendTrigger) { - this.suspendTrigger = suspendTrigger; + public void setStopTriggerKey(TriggerKey stopTriggerKey) { + this.stopTriggerKey = stopTriggerKey; } - public void setResumeJobDetail(JobDetail resumeJobDetail) { - this.resumeJobDetail = resumeJobDetail; + public TriggerKey getSuspendTriggerKey() { + return suspendTriggerKey; } - public JobDetail getResumeJobDetail() { - return resumeJobDetail; + public void setSuspendTriggerKey(TriggerKey suspendTriggerKey) { + this.suspendTriggerKey = suspendTriggerKey; } - public void setResumeTrigger(Trigger resumeTrigger) { - this.resumeTrigger = resumeTrigger; + public TriggerKey getResumeTriggerKey() { + return resumeTriggerKey; } - public Trigger getResumeTrigger() { - return resumeTrigger; + public void setResumeTriggerKey(TriggerKey resumeTriggerKey) { + this.resumeTriggerKey = resumeTriggerKey; } - } diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java index 63537f75cc4aa..fa807922233e0 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java @@ -87,50 +87,47 @@ public void scheduleRoute(Action action, Route route) throws Exception { } public void pauseRouteTrigger(Action action, String routeId) throws SchedulerException { - String triggerName = retrieveTriggerName(action, routeId); - String triggerGroup = retrieveTriggerGroup(action, routeId); + TriggerKey triggerKey = retrieveTriggerKey(action, routeId); - getScheduler().pauseTrigger(TriggerKey.triggerKey(triggerName, triggerGroup)); + getScheduler().pauseTrigger(triggerKey); - LOG.debug("Scheduled trigger: {}.{} is paused", triggerGroup, triggerName); + LOG.debug("Scheduled trigger: {} is paused", triggerKey); } public void resumeRouteTrigger(Action action, String routeId) throws SchedulerException { - String triggerName = retrieveTriggerName(action, routeId); - String triggerGroup = retrieveTriggerGroup(action, routeId); + TriggerKey triggerKey = retrieveTriggerKey(action, routeId); - getScheduler().resumeTrigger(TriggerKey.triggerKey(triggerName, triggerGroup)); + getScheduler().resumeTrigger(triggerKey); - LOG.debug("Scheduled trigger: {}.{} is resumed", triggerGroup, triggerName); + LOG.debug("Scheduled trigger: {} is resumed", triggerKey); } @Override protected void doStop() throws Exception { for (ScheduledRouteDetails scheduledRouteDetails : scheduledRouteDetailsMap.values()) { - if (scheduledRouteDetails.getStartJobDetail() != null) { + if (scheduledRouteDetails.getStartJobKey() != null) { deleteRouteJob(Action.START, scheduledRouteDetails); } - if (scheduledRouteDetails.getStopJobDetail() != null) { + if (scheduledRouteDetails.getStopJobKey() != null) { deleteRouteJob(Action.STOP, scheduledRouteDetails); } - if (scheduledRouteDetails.getSuspendJobDetail() != null) { + if (scheduledRouteDetails.getSuspendJobKey() != null) { deleteRouteJob(Action.SUSPEND, scheduledRouteDetails); } - if (scheduledRouteDetails.getResumeJobDetail() != null) { + if (scheduledRouteDetails.getResumeJobKey() != null) { deleteRouteJob(Action.RESUME, scheduledRouteDetails); } } } public void deleteRouteJob(Action action, ScheduledRouteDetails scheduledRouteDetails) throws SchedulerException { - String jobDetailName = retrieveJobDetailName(action, scheduledRouteDetails); - String jobDetailGroup = retrieveJobDetailGroup(action, scheduledRouteDetails); + JobKey jobKey = retrieveJobKey(action, scheduledRouteDetails); if (!getScheduler().isShutdown()) { - getScheduler().deleteJob(JobKey.jobKey(jobDetailName, jobDetailGroup)); + getScheduler().deleteJob(jobKey); } - LOG.debug("Scheduled Job: {}.{} is deleted", jobDetailGroup, jobDetailName); + LOG.debug("Scheduled Job: {} is deleted", jobKey); } protected JobDetail createJobDetail(Action action, Route route) throws Exception { @@ -152,17 +149,17 @@ protected JobDetail createJobDetail(Action action, Route route) throws Exception protected void updateScheduledRouteDetails(Action action, JobDetail jobDetail, Trigger trigger, Route route) throws Exception { ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(route.getId()); if (action == Action.START) { - scheduledRouteDetails.setStartJobDetail(jobDetail); - scheduledRouteDetails.setStartTrigger(trigger); + scheduledRouteDetails.setStartJobKey(jobDetail.getKey()); + scheduledRouteDetails.setStartTriggerKey(trigger.getKey()); } else if (action == Action.STOP) { - scheduledRouteDetails.setStopJobDetail(jobDetail); - scheduledRouteDetails.setStopTrigger(trigger); + scheduledRouteDetails.setStopJobKey(jobDetail.getKey()); + scheduledRouteDetails.setStopTriggerKey(trigger.getKey()); } else if (action == Action.SUSPEND) { - scheduledRouteDetails.setSuspendJobDetail(jobDetail); - scheduledRouteDetails.setSuspendTrigger(trigger); + scheduledRouteDetails.setSuspendJobKey(jobDetail.getKey()); + scheduledRouteDetails.setSuspendTriggerKey(trigger.getKey()); } else if (action == Action.RESUME) { - scheduledRouteDetails.setResumeJobDetail(jobDetail); - scheduledRouteDetails.setResumeTrigger(trigger); + scheduledRouteDetails.setResumeJobKey(jobDetail.getKey()); + scheduledRouteDetails.setResumeTriggerKey(trigger.getKey()); } } @@ -170,71 +167,38 @@ protected void loadCallbackDataIntoSchedulerContext(JobDetail jobDetail, Action getScheduler().getContext().put(jobDetail.getKey().getName(), new ScheduledJobState(action, route)); } - public String retrieveTriggerName(Action action, String routeId) { + public TriggerKey retrieveTriggerKey(Action action, String routeId) { ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(routeId); - String triggerName = null; + TriggerKey result = null; if (action == Action.START) { - triggerName = scheduledRouteDetails.getStartTrigger().getKey().getName(); + result = scheduledRouteDetails.getStartTriggerKey(); } else if (action == Action.STOP) { - triggerName = scheduledRouteDetails.getStopTrigger().getKey().getName(); + result = scheduledRouteDetails.getStopTriggerKey(); } else if (action == Action.SUSPEND) { - triggerName = scheduledRouteDetails.getSuspendTrigger().getKey().getName(); + result = scheduledRouteDetails.getSuspendTriggerKey(); } else if (action == Action.RESUME) { - triggerName = scheduledRouteDetails.getResumeTrigger().getKey().getName(); + result = scheduledRouteDetails.getResumeTriggerKey(); } - return triggerName; - } - - public String retrieveTriggerGroup(Action action, String routeId) { - ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(routeId); - String triggerGroup = null; - - if (action == Action.START) { - triggerGroup = scheduledRouteDetails.getStartTrigger().getKey().getGroup(); - } else if (action == Action.STOP) { - triggerGroup = scheduledRouteDetails.getStopTrigger().getKey().getGroup(); - } else if (action == Action.SUSPEND) { - triggerGroup = scheduledRouteDetails.getSuspendTrigger().getKey().getGroup(); - } else if (action == Action.RESUME) { - triggerGroup = scheduledRouteDetails.getResumeTrigger().getKey().getGroup(); - } - - return triggerGroup; + return result; } - public String retrieveJobDetailName(Action action, ScheduledRouteDetails scheduledRouteDetails) { - String jobDetailName = null; + public JobKey retrieveJobKey(Action action, ScheduledRouteDetails scheduledRouteDetails) { + JobKey result = null; if (action == Action.START) { - jobDetailName = scheduledRouteDetails.getStartJobDetail().getKey().getName(); + result = scheduledRouteDetails.getStartJobKey(); } else if (action == Action.STOP) { - jobDetailName = scheduledRouteDetails.getStopJobDetail().getKey().getName(); + result = scheduledRouteDetails.getStopJobKey(); } else if (action == Action.SUSPEND) { - jobDetailName = scheduledRouteDetails.getSuspendJobDetail().getKey().getName(); + result = scheduledRouteDetails.getSuspendJobKey(); } else if (action == Action.RESUME) { - jobDetailName = scheduledRouteDetails.getResumeJobDetail().getKey().getName(); + result = scheduledRouteDetails.getResumeJobKey(); } - return jobDetailName; + return result; } - - public String retrieveJobDetailGroup(Action action, ScheduledRouteDetails scheduledRouteDetails) { - String jobDetailGroup = null; - - if (action == Action.START) { - jobDetailGroup = scheduledRouteDetails.getStartJobDetail().getKey().getGroup(); - } else if (action == Action.STOP) { - jobDetailGroup = scheduledRouteDetails.getStopJobDetail().getKey().getGroup(); - } else if (action == Action.SUSPEND) { - jobDetailGroup = scheduledRouteDetails.getSuspendJobDetail().getKey().getGroup(); - } else if (action == Action.RESUME) { - jobDetailGroup = scheduledRouteDetails.getResumeJobDetail().getKey().getGroup(); - } - - return jobDetailGroup; - } protected void registerRouteToScheduledRouteDetails(Route route) { ScheduledRouteDetails scheduledRouteDetails = new ScheduledRouteDetails(); From 5ccf0c1e34df3bc7050ef57cb5716fa331760cc7 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 15:29:01 -0400 Subject: [PATCH 14/16] Added javadoc, copyright and author credits. --- .../camel/component/quartz2/CamelJob.java | 22 +++++++++++++++ .../component/quartz2/QuartzComponent.java | 28 +++++++++++++++++++ .../component/quartz2/QuartzConstants.java | 4 ++- .../component/quartz2/QuartzConsumer.java | 22 +++++++++++++++ .../component/quartz2/QuartzEndpoint.java | 22 +++++++++++++++ .../component/quartz2/QuartzMessage.java | 21 ++++++++++++++ .../component/quartz2/StatefulCamelJob.java | 6 +++- .../quartz2/ScheduledRoutePolicy.java | 13 +++++++++ 8 files changed, 136 insertions(+), 2 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java index 69b03f412e4d1..544dde0364d4e 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java @@ -1,3 +1,19 @@ +/** + * 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.quartz2; import org.apache.camel.CamelContext; @@ -8,6 +24,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * This is a Quartz Job that is scheduled by QuartzEndpoint's Consumer and will call it to + * produce a QuartzMessage sending to a route. + * + * @author Zemian Deng saltnlight5@gmail.com + */ public class CamelJob implements Job { private static final transient Logger LOG = LoggerFactory.getLogger(CamelJob.class); diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java index fb90852ae4ff9..8d78991875ec7 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java @@ -1,3 +1,19 @@ +/** + * 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.quartz2; import org.apache.camel.CamelContext; @@ -19,6 +35,18 @@ import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; +/** + * A factory for QuartzEndpoint. This component will hold a Quartz Scheduler that will provide scheduled timer based + * endpoint that generate a QuartzMessage to a route. Currently it support Cron and Simple trigger scheduling type. + * + *

This component uses Quartz 2.x API and provide all the features from "camel-quartz". It has reused some + * of the code, but mostly has been re-written in attempt to be more easier to maintain, and use Quartz more + * fully.

+ * + * @author All the orignal authors from camel-quartz should get credits as well. + * @author Zemian Deng saltnlight5@gmail.com - ported and re-implemented the camel-quartz2 component. + * @since Jul-27-2013 + */ public class QuartzComponent extends DefaultComponent implements StartupListener { private static final transient Logger LOG = LoggerFactory.getLogger(QuartzComponent.class); private SchedulerFactory schedulerFactory; diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java index 6c39de8a15b82..feb6ce4d7f763 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.java @@ -17,7 +17,9 @@ package org.apache.camel.component.quartz2; /** - * Quartz constants. + * Provide some constants used in this component package. + * + * @author Zemian Deng saltnlight5@gmail.com */ public final class QuartzConstants { public static final String QUARTZ_CAMEL_JOBS_COUNT = "CamelJobsCount"; diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java index 7b525d47199d4..a7b5f3f435240 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.java @@ -1,3 +1,19 @@ +/** + * 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.quartz2; import org.apache.camel.Endpoint; @@ -5,6 +21,12 @@ import org.apache.camel.impl.DefaultConsumer; import org.quartz.Scheduler; +/** + * This consumer process QuartzMessage when scheduler job is executed per scheduled time. When the job runs, it will + * call this consumer's processor to process a new exchange with QuartzMessage. + * + * @author Zemian Deng saltnlight5@gmail.com + */ public class QuartzConsumer extends DefaultConsumer { public QuartzConsumer(Endpoint endpoint, Processor processor) { super(endpoint, processor); diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java index 35a7fd2488caf..bebb8b55047cc 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java @@ -1,3 +1,19 @@ +/** + * 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.quartz2; import org.apache.camel.*; @@ -17,6 +33,12 @@ import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +/** + * This endpoint represent each job to be created in scheduler. When consumer is started or stopped, it will + * call back into doConsumerStart()/Stop() to pause/resume the scheduler trigger. + * + * @author Zemian Deng saltnlight5@gmail.com + */ public class QuartzEndpoint extends DefaultEndpoint { private static final transient Logger LOG = LoggerFactory.getLogger(QuartzEndpoint.class); private TriggerKey triggerKey; diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java index 48791df8362f4..89fa90751c372 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.java @@ -1,3 +1,19 @@ +/** + * 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.quartz2; import org.apache.camel.Exchange; @@ -7,6 +23,11 @@ import java.util.Map; +/** + * A Camel message to be created upon each scheduled job execution. + * + * @author Zemian Deng saltnlight5@gmail.com + */ public class QuartzMessage extends DefaultMessage { private final JobExecutionContext jobExecutionContext; diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java index 01d63383ec17e..53b6785a66a25 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.java @@ -20,7 +20,11 @@ import org.quartz.PersistJobDataAfterExecution; /** - * Stateful job + * A stateful job for CamelJob. For Quartz, this means it will re-save all job data map after each job execution, + * and it will not run concurrently within the Quartz thread pool even if you have multiple triggers or misfired + * instruct to do so. + * + * @author Zemian Deng saltnlight5@gmail.com */ @PersistJobDataAfterExecution @DisallowConcurrentExecution diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java index fa807922233e0..74390b1d71c76 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java @@ -28,6 +28,19 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +/** + * This is Quartz2.x based RoutePolicy implementation that re-use almost identical to "camel-quartz" component. + * + * The following has been updated: + * - Changed and used Quartz 2.x API call on all the area affected. + * - Stored JobKey and TriggerKey instead of JobDetail and Trigger objects in ScheduledRouteDetails. + * + * See org.apache.camel.component.quartz2.QuartzComponent + * + * @author All the orignal authors from camel-quartz should get credits as well. + * @author Zemian Deng saltnlight5@gmail.com - ported and re-implemented the camel-quartz2 component. + * @since Jul-27-2013 + */ public abstract class ScheduledRoutePolicy extends RoutePolicySupport implements ScheduledRoutePolicyConstants { private static final transient Logger LOG = LoggerFactory.getLogger(ScheduledRoutePolicy.class); protected Map scheduledRouteDetailsMap = new LinkedHashMap(); From 1635971f88c82335b8aea5bbcc34e60ee77247a4 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 15:42:53 -0400 Subject: [PATCH 15/16] Store route to Quartz scheduler context using full jobKey instead of just jobName. --- .../org/apache/camel/routepolicy/quartz2/ScheduledJob.java | 2 +- .../apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java index 2731af7d80d31..e56d284a50cfa 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledJob.java @@ -34,7 +34,7 @@ public void execute(JobExecutionContext jobExecutionContext) throws JobExecution LOG.debug("Running ScheduledJob: jobExecutionContext={}", jobExecutionContext); SchedulerContext schedulerContext = getSchedulerContext(jobExecutionContext); - ScheduledJobState state = (ScheduledJobState) schedulerContext.get(jobExecutionContext.getJobDetail().getKey().getName()); + ScheduledJobState state = (ScheduledJobState) schedulerContext.get(jobExecutionContext.getJobDetail().getKey().toString()); Action storedAction = state.getAction(); Route storedRoute = state.getRoute(); diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java index 74390b1d71c76..c14c342cf3338 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java @@ -34,6 +34,7 @@ * The following has been updated: * - Changed and used Quartz 2.x API call on all the area affected. * - Stored JobKey and TriggerKey instead of JobDetail and Trigger objects in ScheduledRouteDetails. + * - ScheduledJobState is stored using full JobKey.toString() instead of just jobName. * * See org.apache.camel.component.quartz2.QuartzComponent * @@ -177,7 +178,7 @@ protected void updateScheduledRouteDetails(Action action, JobDetail jobDetail, T } protected void loadCallbackDataIntoSchedulerContext(JobDetail jobDetail, Action action, Route route) throws SchedulerException { - getScheduler().getContext().put(jobDetail.getKey().getName(), new ScheduledJobState(action, route)); + getScheduler().getContext().put(jobDetail.getKey().toString(), new ScheduledJobState(action, route)); } public TriggerKey retrieveTriggerKey(Action action, String routeId) { From 51b09ad520c79fbfffac52d9d6afdbab41a8bf88 Mon Sep 17 00:00:00 2001 From: Zemain Deng Date: Sat, 27 Jul 2013 15:44:02 -0400 Subject: [PATCH 16/16] Added missing suspendRepeatInterval value on a test. --- .../routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java index 2f84018d3f583..87ae238e4dd0b 100644 --- a/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java +++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java @@ -185,6 +185,7 @@ public void configure() { long suspendTime = System.currentTimeMillis() + 1000L; policy.setRouteSuspendDate(new Date(suspendTime)); policy.setRouteSuspendRepeatCount(0); + policy.setRouteSuspendRepeatInterval(3000); long resumeTime = System.currentTimeMillis() + 4000L; policy.setRouteResumeDate(new Date(resumeTime)); policy.setRouteResumeRepeatCount(1);