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    }