001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.quartz;
018
019 import java.util.Date;
020 import java.util.Map;
021 import java.util.Set;
022
023 import org.apache.camel.ExchangePattern;
024 import org.apache.camel.Processor;
025 import org.apache.camel.Producer;
026 import org.apache.camel.impl.DefaultEndpoint;
027 import org.apache.camel.processor.loadbalancer.LoadBalancer;
028 import org.apache.camel.processor.loadbalancer.RoundRobinLoadBalancer;
029 import org.apache.camel.util.ObjectHelper;
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.quartz.JobDetail;
033 import org.quartz.JobExecutionContext;
034 import org.quartz.JobExecutionException;
035 import org.quartz.Scheduler;
036 import org.quartz.SchedulerException;
037 import org.quartz.SimpleTrigger;
038 import org.quartz.Trigger;
039
040 /**
041 * A <a href="http://activemq.apache.org/quartz.html">Quartz Endpoint</a>
042 *
043 * @version $Revision:520964 $
044 */
045 public class QuartzEndpoint extends DefaultEndpoint<QuartzExchange> {
046 public static final String ENDPOINT_KEY = "org.apache.camel.quartz";
047 public static final String CONTEXT_KEY = "org.apache.camel.CamelContext";
048
049 private static final transient Log LOG = LogFactory.getLog(QuartzEndpoint.class);
050 private Scheduler scheduler;
051 private LoadBalancer loadBalancer;
052 private Trigger trigger;
053 private JobDetail jobDetail;
054 private boolean started;
055 private boolean stateful;
056
057 public QuartzEndpoint(final String endpointUri, final QuartzComponent component, final Scheduler scheduler) {
058 super(endpointUri, component);
059 this.scheduler = scheduler;
060 }
061
062 public QuartzEndpoint(final String endpointUri, final Scheduler scheduler) {
063 super(endpointUri);
064 this.scheduler = scheduler;
065 }
066
067 public void addTriggers(final Map<Trigger, JobDetail> triggerMap) throws SchedulerException {
068 if (triggerMap != null) {
069 Set<Map.Entry<Trigger, JobDetail>> entries = triggerMap.entrySet();
070 for (Map.Entry<Trigger, JobDetail> entry : entries) {
071 Trigger key = entry.getKey();
072 JobDetail value = entry.getValue();
073 ObjectHelper.notNull(key, "key");
074 ObjectHelper.notNull(value, "value");
075
076 addTrigger(key, value);
077 }
078 }
079 }
080
081 public void addTrigger(final Trigger trigger, final JobDetail detail) throws SchedulerException {
082 // lets default the trigger name to the job name
083 if (trigger.getName() == null) {
084 trigger.setName(detail.getName());
085 }
086 // lets default the trigger group to the job group
087 if (trigger.getGroup() == null) {
088 trigger.setGroup(detail.getGroup());
089 }
090 // default start time to now if not specified
091 if (trigger.getStartTime() == null) {
092 trigger.setStartTime(new Date());
093 }
094 if (isStateful()) {
095 detail.getJobDataMap().put(ENDPOINT_KEY, getEndpointUri());
096 } else {
097 detail.getJobDataMap().put(ENDPOINT_KEY, this);
098 }
099 if (null == detail.getJobClass()) {
100 if (isStateful()) {
101 detail.setJobClass(StatefulCamelJob.class);
102 } else {
103 detail.setJobClass(CamelJob.class);
104 }
105 }
106 if (detail.getName() == null) {
107 detail.setName(getEndpointUri());
108 }
109 getScheduler().scheduleJob(detail, trigger);
110 }
111
112 public void removeTrigger(final Trigger trigger, final JobDetail jobDetail) throws SchedulerException {
113 getScheduler().unscheduleJob(trigger.getName(), trigger.getGroup());
114 }
115
116 /**
117 * This method is invoked when a Quartz job is fired.
118 *
119 * @param jobExecutionContext the Quartz Job context
120 */
121 public void onJobExecute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
122 if (LOG.isDebugEnabled()) {
123 LOG.debug("Firing Quartz Job with context: " + jobExecutionContext);
124 }
125 QuartzExchange exchange = createExchange(jobExecutionContext);
126 try {
127 getLoadBalancer().process(exchange);
128 } catch (JobExecutionException e) {
129 throw e;
130 } catch (Exception e) {
131 throw new JobExecutionException(e);
132 }
133 }
134
135 @Override
136 public QuartzExchange createExchange(final ExchangePattern pattern) {
137 return new QuartzExchange(getCamelContext(), pattern, null);
138 }
139
140 public QuartzExchange createExchange(final JobExecutionContext jobExecutionContext) {
141 return new QuartzExchange(getCamelContext(), getExchangePattern(), jobExecutionContext);
142 }
143
144 public Producer<QuartzExchange> createProducer() throws Exception {
145 throw new UnsupportedOperationException("You cannot send messages to this endpoint");
146 }
147
148 public QuartzConsumer createConsumer(final Processor processor) throws Exception {
149 return new QuartzConsumer(this, processor);
150 }
151
152 // Properties
153 // -------------------------------------------------------------------------
154
155 @Override
156 public QuartzComponent getComponent() {
157 return (QuartzComponent)super.getComponent();
158 }
159
160 public boolean isSingleton() {
161 return true;
162 }
163
164 public Scheduler getScheduler() {
165 return scheduler;
166 }
167
168 public LoadBalancer getLoadBalancer() {
169 if (loadBalancer == null) {
170 loadBalancer = createLoadBalancer();
171 }
172 return loadBalancer;
173 }
174
175 public void setLoadBalancer(final LoadBalancer loadBalancer) {
176 this.loadBalancer = loadBalancer;
177 }
178
179 public JobDetail getJobDetail() {
180 if (jobDetail == null) {
181 jobDetail = createJobDetail();
182 }
183 return jobDetail;
184 }
185
186 public void setJobDetail(final JobDetail jobDetail) {
187 this.jobDetail = jobDetail;
188 }
189
190 public Trigger getTrigger() {
191 if (trigger == null) {
192 trigger = createTrigger();
193 }
194 return trigger;
195 }
196
197 public void setTrigger(final Trigger trigger) {
198 this.trigger = trigger;
199 }
200
201 /**
202 * @return the stateful
203 */
204 public boolean isStateful() {
205 return this.stateful;
206 }
207
208 /**
209 * @param stateful the stateful to set
210 */
211 public void setStateful(final boolean stateful) {
212 this.stateful = stateful;
213 }
214
215 // Implementation methods
216 // -------------------------------------------------------------------------
217 public synchronized void consumerStarted(final QuartzConsumer consumer) throws SchedulerException {
218 getLoadBalancer().addProcessor(consumer.getProcessor());
219
220 // if we have not yet added our default trigger, then lets do it
221 if (!started) {
222 addTrigger(getTrigger(), getJobDetail());
223 started = true;
224 }
225 }
226
227 public synchronized void consumerStopped(final QuartzConsumer consumer) throws SchedulerException {
228 getLoadBalancer().removeProcessor(consumer.getProcessor());
229 if (getLoadBalancer().getProcessors().isEmpty() && started) {
230 removeTrigger(getTrigger(), getJobDetail());
231 started = false;
232 }
233 }
234
235 protected LoadBalancer createLoadBalancer() {
236 return new RoundRobinLoadBalancer();
237 }
238
239 protected JobDetail createJobDetail() {
240 return new JobDetail();
241 }
242
243 protected Trigger createTrigger() {
244 return new SimpleTrigger();
245 }
246 }