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 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