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