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