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    }