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    }