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