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.Exchange;
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.Trigger;
038
039 /**
040 * A <a href="http://activemq.apache.org/quartz.html">Quartz Endpoint</a>
041 *
042 * @version $Revision:520964 $
043 */
044 public class QuartzEndpoint extends DefaultEndpoint {
045 private static final transient Log LOG = LogFactory.getLog(QuartzEndpoint.class);
046
047 private Scheduler scheduler;
048 private LoadBalancer loadBalancer;
049 private Trigger trigger;
050 private JobDetail jobDetail;
051 private boolean started;
052 private boolean stateful;
053
054 public QuartzEndpoint() {
055 }
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 detail.getJobDataMap().put(QuartzConstants.QUARTZ_ENDPOINT, isStateful() ? getEndpointUri() : this);
095 if (detail.getJobClass() == null) {
096 detail.setJobClass(isStateful() ? StatefulCamelJob.class : CamelJob.class);
097 }
098 if (detail.getName() == null) {
099 detail.setName(getEndpointUri());
100 }
101 getScheduler().scheduleJob(detail, trigger);
102 }
103
104 public void removeTrigger(final Trigger trigger, final JobDetail jobDetail) throws SchedulerException {
105 getScheduler().unscheduleJob(trigger.getName(), trigger.getGroup());
106 }
107
108 /**
109 * This method is invoked when a Quartz job is fired.
110 *
111 * @param jobExecutionContext the Quartz Job context
112 */
113 public void onJobExecute(final JobExecutionContext jobExecutionContext) throws JobExecutionException {
114 if (LOG.isDebugEnabled()) {
115 LOG.debug("Firing Quartz Job with context: " + jobExecutionContext);
116 }
117 Exchange exchange = createExchange(jobExecutionContext);
118 try {
119 getLoadBalancer().process(exchange);
120 } catch (JobExecutionException e) {
121 throw e;
122 } catch (Exception e) {
123 throw new JobExecutionException(e);
124 }
125 }
126
127 public Exchange createExchange(final JobExecutionContext jobExecutionContext) {
128 Exchange exchange = createExchange();
129 exchange.setIn(new QuartzMessage(exchange, jobExecutionContext));
130 return exchange;
131 }
132
133 public Producer createProducer() throws Exception {
134 throw new UnsupportedOperationException("You cannot send messages to this endpoint");
135 }
136
137 public QuartzConsumer createConsumer(Processor processor) throws Exception {
138 return new QuartzConsumer(this, processor);
139 }
140
141 @Override
142 protected String createEndpointUri() {
143 return "quartz://" + getTrigger().getGroup() + "/" + getTrigger().getName();
144 }
145
146 // Properties
147 // -------------------------------------------------------------------------
148
149 @Override
150 public QuartzComponent getComponent() {
151 return (QuartzComponent) super.getComponent();
152 }
153
154 public boolean isSingleton() {
155 return true;
156 }
157
158 public Scheduler getScheduler() {
159 return scheduler;
160 }
161
162 public LoadBalancer getLoadBalancer() {
163 if (loadBalancer == null) {
164 loadBalancer = createLoadBalancer();
165 }
166 return loadBalancer;
167 }
168
169 public void setLoadBalancer(final LoadBalancer loadBalancer) {
170 this.loadBalancer = loadBalancer;
171 }
172
173 public JobDetail getJobDetail() {
174 if (jobDetail == null) {
175 jobDetail = createJobDetail();
176 }
177 return jobDetail;
178 }
179
180 public void setJobDetail(final JobDetail jobDetail) {
181 this.jobDetail = jobDetail;
182 }
183
184 public Trigger getTrigger() {
185 return trigger;
186 }
187
188 public void setTrigger(final Trigger trigger) {
189 this.trigger = trigger;
190 }
191
192 public boolean isStateful() {
193 return this.stateful;
194 }
195
196 public void setStateful(final boolean stateful) {
197 this.stateful = stateful;
198 }
199
200 public void setScheduler(Scheduler scheduler) {
201 this.scheduler = scheduler;
202 }
203
204 // Implementation methods
205 // -------------------------------------------------------------------------
206 public synchronized void consumerStarted(final QuartzConsumer consumer) throws SchedulerException {
207 ObjectHelper.notNull(trigger, "trigger");
208 getLoadBalancer().addProcessor(consumer.getProcessor());
209
210 // if we have not yet added our default trigger, then lets do it
211 if (!started) {
212 addTrigger(getTrigger(), getJobDetail());
213 started = true;
214 }
215 }
216
217 public synchronized void consumerStopped(final QuartzConsumer consumer) throws SchedulerException {
218 ObjectHelper.notNull(trigger, "trigger");
219 getLoadBalancer().removeProcessor(consumer.getProcessor());
220 if (getLoadBalancer().getProcessors().isEmpty() && started) {
221 removeTrigger(getTrigger(), getJobDetail());
222 started = false;
223 }
224 }
225
226 protected LoadBalancer createLoadBalancer() {
227 return new RoundRobinLoadBalancer();
228 }
229
230 protected JobDetail createJobDetail() {
231 return new JobDetail();
232 }
233
234 }