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