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    }