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..544dde0364d4e
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/CamelJob.java
@@ -0,0 +1,122 @@
+/**
+ * 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.CamelExchangeException;
+import org.apache.camel.Exchange;
+import org.apache.camel.Route;
+import org.quartz.*;
+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);
+
+ @Override
+ public void execute(JobExecutionContext context) throws JobExecutionException {
+ Exchange exchange = null;
+ try {
+ if (LOG.isDebugEnabled())
+ 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();
+ 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
+ for (Route route : camelContext.getRoutes()) {
+ if (route.getEndpoint() instanceof QuartzEndpoint) {
+ QuartzEndpoint quartzEndpoint = (QuartzEndpoint) route.getEndpoint();
+ TriggerKey checkTriggerKey = quartzEndpoint.getTriggerKey();
+ if (LOG.isTraceEnabled())
+ 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) {
+ 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);
+ 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..8d78991875ec7
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzComponent.java
@@ -0,0 +1,327 @@
+/**
+ * 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.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;
+
+/**
+ * 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;
+ private Scheduler scheduler;
+ private Properties properties;
+ private String propertiesFile;
+ private int startDelayedSeconds;
+ private boolean autoStartScheduler = true;
+ private boolean prefixJobNameWithEndpointId = false;
+
+ 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;
+
+ Boolean prefixJobNameWithEndpointId = getAndRemoveParameter(parameters, "prefixJobNameWithEndpointId", Boolean.class);
+ if (prefixJobNameWithEndpointId != null)
+ this.prefixJobNameWithEndpointId = prefixJobNameWithEndpointId;
+
+ // 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
+ QuartzEndpoint result = new QuartzEndpoint(uri, this);
+ TriggerKey triggerKey = createTriggerKey(uri, remaining, result);
+ result.setTriggerKey(triggerKey);
+ result.setTriggerParameters(triggerParameters);
+ result.setJobParameters(jobParameters);
+ return result;
+ }
+
+ private TriggerKey createTriggerKey(String uri, String remaining, QuartzEndpoint endpoint) 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 this context's unique name
+ String name, group;
+ if (ObjectHelper.isNotEmpty(path) && ObjectHelper.isNotEmpty(host)) {
+ group = host;
+ name = path;
+ } else {
+ String camelContextName = getCamelContext().getManagementName();
+ group = camelContextName == null ? "Camel" : "Camel_" + camelContextName;
+ name = host;
+ }
+
+
+ if (prefixJobNameWithEndpointId)
+ name = endpoint.getId() + "_" + name;
+
+ 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();
+ }
+ }
+ }
+ }
+}
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..feb6ce4d7f763
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConstants.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.component.quartz2;
+
+/**
+ * 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";
+
+ 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..a7b5f3f435240
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzConsumer.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.Endpoint;
+import org.apache.camel.Processor;
+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);
+ }
+
+ 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
new file mode 100644
index 0000000000000..bebb8b55047cc
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java
@@ -0,0 +1,351 @@
+/**
+ * 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.*;
+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.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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;
+ 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
+
+ // 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;
+ }
+
+ 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);
+ return result;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return false;
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ addJobInScheduler();
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ removeJobInScheduler();
+ }
+
+ private void removeJobInScheduler() throws Exception {
+ Scheduler scheduler = getComponent().getScheduler();
+ if (scheduler == null)
+ return;
+
+ if (deleteJob) {
+ 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;
+ 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.
+ 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 {
+ ensureNoDupTriggerKey();
+
+ // 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 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();
+ 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.debug("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 extends Job> 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.debug("Created jobDetail={}", result);
+ return result;
+ }
+
+ @Override
+ public QuartzComponent getComponent() {
+ return (QuartzComponent)super.getComponent();
+ }
+
+ public void pauseTrigger() throws Exception {
+ if (jobPaused.get())
+ return;
+ jobPaused.set(true);
+
+ Scheduler scheduler = getComponent().getScheduler();
+ if (scheduler != null) {
+ LOG.info("Pausing trigger {}", triggerKey);
+ scheduler.pauseTrigger(triggerKey);
+ }
+ }
+
+ public void resumeTrigger() throws Exception {
+ if (!jobPaused.get())
+ return;
+ jobPaused.set(false);
+
+ Scheduler scheduler = getComponent().getScheduler();
+ if (scheduler != null) {
+ LOG.info("Resuming trigger {}", triggerKey);
+ scheduler.resumeTrigger(triggerKey);
+ }
+ }
+
+ public void onConsumerStart(QuartzConsumer quartzConsumer) throws Exception {
+ getConsumerLoadBalancer().addProcessor(quartzConsumer.getProcessor());
+ if (!jobAdded.get()) {
+ addJobInScheduler();
+ } else {
+ resumeTrigger();
+ }
+ }
+
+ public void onConsumerStop(QuartzConsumer quartzConsumer) throws Exception {
+ getConsumerLoadBalancer().removeProcessor(quartzConsumer.getProcessor());
+ if (jobAdded.get()) {
+ pauseTrigger();
+ }
+ }
+}
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..89fa90751c372
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzMessage.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.Exchange;
+import org.apache.camel.impl.DefaultMessage;
+import org.quartz.JobExecutionContext;
+import org.quartz.Trigger;
+
+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;
+
+ 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..53b6785a66a25
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/StatefulCamelJob.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;
+
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.PersistJobDataAfterExecution;
+
+/**
+ * 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
+public class StatefulCamelJob extends CamelJob {
+
+ private static final long serialVersionUID = 25L;
+
+}
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..e56d284a50cfa
--- /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().toString());
+ 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..755496f8076ac
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRouteDetails.java
@@ -0,0 +1,95 @@
+/**
+ * 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.JobKey;
+import org.quartz.TriggerKey;
+
+public class ScheduledRouteDetails {
+ 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 JobKey getStartJobKey() {
+ return startJobKey;
+ }
+
+ public void setStartJobKey(JobKey startJobKey) {
+ this.startJobKey = startJobKey;
+ }
+
+ public JobKey getStopJobKey() {
+ return stopJobKey;
+ }
+
+ public void setStopJobKey(JobKey stopJobKey) {
+ this.stopJobKey = stopJobKey;
+ }
+
+ public JobKey getSuspendJobKey() {
+ return suspendJobKey;
+ }
+
+ public void setSuspendJobKey(JobKey suspendJobKey) {
+ this.suspendJobKey = suspendJobKey;
+ }
+
+ public JobKey getResumeJobKey() {
+ return resumeJobKey;
+ }
+
+ public void setResumeJobKey(JobKey resumeJobKey) {
+ this.resumeJobKey = resumeJobKey;
+ }
+
+ public TriggerKey getStartTriggerKey() {
+ return startTriggerKey;
+ }
+
+ public void setStartTriggerKey(TriggerKey startTriggerKey) {
+ this.startTriggerKey = startTriggerKey;
+ }
+
+ public TriggerKey getStopTriggerKey() {
+ return stopTriggerKey;
+ }
+
+ public void setStopTriggerKey(TriggerKey stopTriggerKey) {
+ this.stopTriggerKey = stopTriggerKey;
+ }
+
+ public TriggerKey getSuspendTriggerKey() {
+ return suspendTriggerKey;
+ }
+
+ public void setSuspendTriggerKey(TriggerKey suspendTriggerKey) {
+ this.suspendTriggerKey = suspendTriggerKey;
+ }
+
+ public TriggerKey getResumeTriggerKey() {
+ return resumeTriggerKey;
+ }
+
+ 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
new file mode 100644
index 0000000000000..c14c342cf3338
--- /dev/null
+++ b/components/camel-quartz2/src/main/java/org/apache/camel/routepolicy/quartz2/ScheduledRoutePolicy.java
@@ -0,0 +1,250 @@
+/**
+ * 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;
+
+/**
+ * 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.
+ * - ScheduledJobState is stored using full JobKey.toString() instead of just jobName.
+ *
+ * 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();
+ 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 {
+ TriggerKey triggerKey = retrieveTriggerKey(action, routeId);
+
+ getScheduler().pauseTrigger(triggerKey);
+
+ LOG.debug("Scheduled trigger: {} is paused", triggerKey);
+ }
+
+ public void resumeRouteTrigger(Action action, String routeId) throws SchedulerException {
+ TriggerKey triggerKey = retrieveTriggerKey(action, routeId);
+
+ getScheduler().resumeTrigger(triggerKey);
+
+ LOG.debug("Scheduled trigger: {} is resumed", triggerKey);
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ for (ScheduledRouteDetails scheduledRouteDetails : scheduledRouteDetailsMap.values()) {
+ if (scheduledRouteDetails.getStartJobKey() != null) {
+ deleteRouteJob(Action.START, scheduledRouteDetails);
+ }
+ if (scheduledRouteDetails.getStopJobKey() != null) {
+ deleteRouteJob(Action.STOP, scheduledRouteDetails);
+ }
+ if (scheduledRouteDetails.getSuspendJobKey() != null) {
+ deleteRouteJob(Action.SUSPEND, scheduledRouteDetails);
+ }
+ if (scheduledRouteDetails.getResumeJobKey() != null) {
+ deleteRouteJob(Action.RESUME, scheduledRouteDetails);
+ }
+ }
+ }
+
+ public void deleteRouteJob(Action action, ScheduledRouteDetails scheduledRouteDetails) throws SchedulerException {
+ JobKey jobKey = retrieveJobKey(action, scheduledRouteDetails);
+
+ if (!getScheduler().isShutdown()) {
+ getScheduler().deleteJob(jobKey);
+ }
+
+ LOG.debug("Scheduled Job: {} is deleted", jobKey);
+ }
+
+ 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.setStartJobKey(jobDetail.getKey());
+ scheduledRouteDetails.setStartTriggerKey(trigger.getKey());
+ } else if (action == Action.STOP) {
+ scheduledRouteDetails.setStopJobKey(jobDetail.getKey());
+ scheduledRouteDetails.setStopTriggerKey(trigger.getKey());
+ } else if (action == Action.SUSPEND) {
+ scheduledRouteDetails.setSuspendJobKey(jobDetail.getKey());
+ scheduledRouteDetails.setSuspendTriggerKey(trigger.getKey());
+ } else if (action == Action.RESUME) {
+ scheduledRouteDetails.setResumeJobKey(jobDetail.getKey());
+ scheduledRouteDetails.setResumeTriggerKey(trigger.getKey());
+ }
+ }
+
+ protected void loadCallbackDataIntoSchedulerContext(JobDetail jobDetail, Action action, Route route) throws SchedulerException {
+ getScheduler().getContext().put(jobDetail.getKey().toString(), new ScheduledJobState(action, route));
+ }
+
+ public TriggerKey retrieveTriggerKey(Action action, String routeId) {
+ ScheduledRouteDetails scheduledRouteDetails = getScheduledRouteDetails(routeId);
+ TriggerKey result = null;
+
+ if (action == Action.START) {
+ result = scheduledRouteDetails.getStartTriggerKey();
+ } else if (action == Action.STOP) {
+ result = scheduledRouteDetails.getStopTriggerKey();
+ } else if (action == Action.SUSPEND) {
+ result = scheduledRouteDetails.getSuspendTriggerKey();
+ } else if (action == Action.RESUME) {
+ result = scheduledRouteDetails.getResumeTriggerKey();
+ }
+
+ return result;
+ }
+
+ public JobKey retrieveJobKey(Action action, ScheduledRouteDetails scheduledRouteDetails) {
+ JobKey result = null;
+
+ if (action == Action.START) {
+ result = scheduledRouteDetails.getStartJobKey();
+ } else if (action == Action.STOP) {
+ result = scheduledRouteDetails.getStopJobKey();
+ } else if (action == Action.SUSPEND) {
+ result = scheduledRouteDetails.getSuspendJobKey();
+ } else if (action == Action.RESUME) {
+ result = scheduledRouteDetails.getResumeJobKey();
+ }
+
+ return result;
+ }
+
+ 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/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..70277145ed0e7
--- /dev/null
+++ b/components/camel-quartz2/src/test/java/org/apache/camel/component/quartz2/QuartzNameCollisionTest.java
@@ -0,0 +1,190 @@
+/**
+ * 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.FailedToCreateRouteException;
+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();
+
+ try {
+ 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);
+ }
+ }
+
+ @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();
+ 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();
+ }
+
+ /**
+ * 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();
+ 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.
+ }
+
+ /**
+ * 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.PAUSED, 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/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..87ae238e4dd0b
--- /dev/null
+++ b/components/camel-quartz2/src/test/java/org/apache/camel/routepolicy/quartz2/SimpleScheduledRoutePolicyTest.java
@@ -0,0 +1,257 @@
+/**
+ * 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);
+ policy.setRouteSuspendRepeatInterval(3000);
+ 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/log4j.properties b/components/camel-quartz2/src/test/resources/log4j.properties
new file mode 100644
index 0000000000000..86e4b3e88add4
--- /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, 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
+
+# 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/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
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)
+);
+
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