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
021 import org.apache.camel.Exchange;
022 import org.apache.camel.Processor;
023 import org.apache.camel.Producer;
024 import org.apache.camel.ShutdownableService;
025 import org.apache.camel.impl.DefaultEndpoint;
026 import org.apache.camel.impl.ServiceSupport;
027 import org.apache.camel.processor.loadbalancer.LoadBalancer;
028 import org.apache.camel.processor.loadbalancer.RoundRobinLoadBalancer;
029 import org.apache.camel.util.ExchangeHelper;
030 import org.apache.camel.util.ObjectHelper;
031 import org.apache.camel.util.ServiceHelper;
032 import org.quartz.JobDetail;
033 import org.quartz.JobExecutionContext;
034 import org.quartz.JobExecutionException;
035 import org.quartz.SchedulerException;
036 import org.quartz.Trigger;
037 import org.slf4j.Logger;
038 import org.slf4j.LoggerFactory;
039
040 /**
041 * A <a href="http://activemq.apache.org/quartz.html">Quartz Endpoint</a>
042 *
043 * @version
044 */
045 public class QuartzEndpoint extends DefaultEndpoint implements ShutdownableService {
046 private static final transient Logger LOG = LoggerFactory.getLogger(QuartzEndpoint.class);
047
048 private LoadBalancer loadBalancer;
049 private Trigger trigger;
050 private JobDetail jobDetail;
051 private volatile boolean started;
052 private volatile boolean stateful;
053
054 public QuartzEndpoint(final String endpointUri, final QuartzComponent component) {
055 super(endpointUri, component);
056 getJobDetail().setName("quartz-" + getId());
057 }
058
059 public void addTrigger(final Trigger trigger, final JobDetail detail) throws SchedulerException {
060 // lets default the trigger name to the job name
061 if (trigger.getName() == null) {
062 trigger.setName(detail.getName());
063 }
064 // lets default the trigger group to the job group
065 if (trigger.getGroup() == null) {
066 trigger.setGroup(detail.getGroup());
067 }
068 // default start time to now if not specified
069 if (trigger.getStartTime() == null) {
070 trigger.setStartTime(new Date());
071 }
072 detail.getJobDataMap().put(QuartzConstants.QUARTZ_ENDPOINT_URI, getEndpointUri());
073 detail.getJobDataMap().put(QuartzConstants.QUARTZ_CAMEL_CONTEXT_NAME, getCamelContext().getName());
074 if (detail.getJobClass() == null) {
075 detail.setJobClass(isStateful() ? StatefulCamelJob.class : CamelJob.class);
076 }
077 if (detail.getName() == null) {
078 detail.setName(getJobName());
079 }
080 getComponent().addJob(detail, trigger);
081 }
082
083 public void pauseTrigger(final Trigger trigger) throws SchedulerException {
084 getComponent().pauseJob(trigger);
085 }
086
087 public void deleteTrigger(final Trigger trigger) throws SchedulerException {
088 getComponent().deleteJob(trigger.getName(), trigger.getGroup());
089 }
090
091 /**
092 * This method is invoked when a Quartz job is fired.
093 *
094 * @param jobExecutionContext the Quartz Job context
095 */
096 public void onJobExecute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
097 boolean run = true;
098 LoadBalancer balancer = getLoadBalancer();
099 if (balancer instanceof ServiceSupport) {
100 run = ((ServiceSupport) balancer).isRunAllowed();
101 }
102
103 if (!run) {
104 // quartz scheduler could potential trigger during a route has been shutdown
105 LOG.warn("Cannot execute Quartz Job with context: " + jobExecutionContext + " because processor is not started: " + balancer);
106 return;
107 }
108
109 LOG.debug("Firing Quartz Job with context: {}", jobExecutionContext);
110 Exchange exchange = createExchange(jobExecutionContext);
111 try {
112 balancer.process(exchange);
113
114 if (exchange.getException() != null) {
115 // propagate the exception back to Quartz
116 throw new JobExecutionException(exchange.getException());
117 }
118 } catch (Exception e) {
119 // log the error
120 LOG.error(ExchangeHelper.createExceptionMessage("Error processing exchange", exchange, e));
121
122 // and rethrow to let quartz handle it
123 if (e instanceof JobExecutionException) {
124 throw (JobExecutionException) e;
125 }
126 throw new JobExecutionException(e);
127 }
128 }
129
130 public Exchange createExchange(final JobExecutionContext jobExecutionContext) {
131 Exchange exchange = createExchange();
132 exchange.setIn(new QuartzMessage(exchange, jobExecutionContext));
133 return exchange;
134 }
135
136 public Producer createProducer() throws Exception {
137 throw new UnsupportedOperationException("You cannot send messages to this endpoint");
138 }
139
140 public QuartzConsumer createConsumer(Processor processor) throws Exception {
141 return new QuartzConsumer(this, processor);
142 }
143
144 @Override
145 protected String createEndpointUri() {
146 return "quartz://" + getTrigger().getGroup() + "/" + getTrigger().getName();
147 }
148
149 protected String getJobName() {
150 return getJobDetail().getName();
151 }
152
153 // Properties
154 // -------------------------------------------------------------------------
155
156 @Override
157 public QuartzComponent getComponent() {
158 return (QuartzComponent) super.getComponent();
159 }
160
161 public boolean isSingleton() {
162 return true;
163 }
164
165 public LoadBalancer getLoadBalancer() {
166 if (loadBalancer == null) {
167 loadBalancer = createLoadBalancer();
168 }
169 return loadBalancer;
170 }
171
172 public void setLoadBalancer(final LoadBalancer loadBalancer) {
173 this.loadBalancer = loadBalancer;
174 }
175
176 public JobDetail getJobDetail() {
177 if (jobDetail == null) {
178 jobDetail = createJobDetail();
179 }
180 return jobDetail;
181 }
182
183 public void setJobDetail(final JobDetail jobDetail) {
184 this.jobDetail = jobDetail;
185 }
186
187 public Trigger getTrigger() {
188 return trigger;
189 }
190
191 public void setTrigger(final Trigger trigger) {
192 this.trigger = trigger;
193 }
194
195 public boolean isStateful() {
196 return this.stateful;
197 }
198
199 public void setStateful(final boolean stateful) {
200 this.stateful = stateful;
201 }
202
203 // Implementation methods
204 // -------------------------------------------------------------------------
205
206 public synchronized void consumerStarted(final QuartzConsumer consumer) throws SchedulerException {
207 ObjectHelper.notNull(trigger, "trigger");
208 LOG.debug("Adding consumer {}", consumer.getProcessor());
209 getLoadBalancer().addProcessor(consumer.getProcessor());
210
211 // if we have not yet added our default trigger, then lets do it
212 if (!started) {
213 addTrigger(getTrigger(), getJobDetail());
214 started = true;
215 }
216 }
217
218 public synchronized void consumerStopped(final QuartzConsumer consumer) throws SchedulerException {
219 ObjectHelper.notNull(trigger, "trigger");
220 if (started) {
221 pauseTrigger(getTrigger());
222 started = false;
223 }
224
225 LOG.debug("Removing consumer {}", consumer.getProcessor());
226 getLoadBalancer().removeProcessor(consumer.getProcessor());
227 }
228
229 protected LoadBalancer createLoadBalancer() {
230 return new RoundRobinLoadBalancer();
231 }
232
233 protected JobDetail createJobDetail() {
234 return new JobDetail();
235 }
236
237 @Override
238 protected void doStart() throws Exception {
239 ObjectHelper.notNull(getComponent(), "QuartzComponent", this);
240 ServiceHelper.startService(loadBalancer);
241 }
242
243 @Override
244 protected void doStop() throws Exception {
245 ServiceHelper.stopService(loadBalancer);
246 }
247
248 @Override
249 protected void doShutdown() throws Exception {
250 ObjectHelper.notNull(trigger, "trigger");
251 deleteTrigger(getTrigger());
252 }
253
254 }