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 */
017package org.apache.camel.component.timer;
018
019import java.util.Date;
020import java.util.Timer;
021import java.util.TimerTask;
022import java.util.concurrent.atomic.AtomicLong;
023
024import org.apache.camel.AsyncCallback;
025import org.apache.camel.CamelContext;
026import org.apache.camel.Exchange;
027import org.apache.camel.Processor;
028import org.apache.camel.StartupListener;
029import org.apache.camel.impl.DefaultConsumer;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * The timer consumer.
035 *
036 * @version 
037 */
038public class TimerConsumer extends DefaultConsumer implements StartupListener {
039    private static final Logger LOG = LoggerFactory.getLogger(TimerConsumer.class);
040    private final TimerEndpoint endpoint;
041    private volatile TimerTask task;
042    private volatile boolean configured;
043
044    public TimerConsumer(TimerEndpoint endpoint, Processor processor) {
045        super(endpoint, processor);
046        this.endpoint = endpoint;
047    }
048
049    @Override
050    public TimerEndpoint getEndpoint() {
051        return (TimerEndpoint) super.getEndpoint();
052    }
053
054    @Override
055    protected void doStart() throws Exception {
056        task = new TimerTask() {
057            // counter
058            private final AtomicLong counter = new AtomicLong();
059
060            @Override
061            public void run() {
062                if (!isTaskRunAllowed()) {
063                    // do not run timer task as it was not allowed
064                    LOG.debug("Run now allowed for timer: {}", endpoint);
065                    return;
066                }
067
068                try {
069                    long count = counter.incrementAndGet();
070
071                    boolean fire = endpoint.getRepeatCount() <= 0 || count <= endpoint.getRepeatCount();
072                    if (fire) {
073                        sendTimerExchange(count);
074                    } else {
075                        // no need to fire anymore as we exceeded repeat count
076                        LOG.debug("Cancelling {} timer as repeat count limit reached after {} counts.", endpoint.getTimerName(), endpoint.getRepeatCount());
077                        cancel();
078                    }
079                } catch (Throwable e) {
080                    // catch all to avoid the JVM closing the thread and not firing again
081                    LOG.warn("Error processing exchange. This exception will be ignored, to let the timer be able to trigger again.", e);
082                }
083            }
084        };
085
086        // only configure task if CamelContext already started, otherwise the StartupListener
087        // is configuring the task later
088        if (!configured && endpoint.getCamelContext().getStatus().isStarted()) {
089            Timer timer = endpoint.getTimer(this);
090            configureTask(task, timer);
091        }
092    }
093
094    @Override
095    protected void doStop() throws Exception {
096        if (task != null) {
097            task.cancel();
098        }
099        task = null;
100        configured = false;
101
102        // remove timer
103        endpoint.removeTimer(this);
104    }
105
106    @Override
107    public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
108        if (task != null && !configured) {
109            Timer timer = endpoint.getTimer(this);
110            configureTask(task, timer);
111        }
112    }
113
114    /**
115     * Whether the timer task is allow to run or not
116     */
117    protected boolean isTaskRunAllowed() {
118        // only allow running the timer task if we can run and are not suspended,
119        // and CamelContext must have been fully started
120        return endpoint.getCamelContext().getStatus().isStarted() && isRunAllowed() && !isSuspended();
121    }
122
123    protected void configureTask(TimerTask task, Timer timer) {
124        if (endpoint.isFixedRate()) {
125            if (endpoint.getTime() != null) {
126                timer.scheduleAtFixedRate(task, endpoint.getTime(), endpoint.getPeriod());
127            } else {
128                timer.scheduleAtFixedRate(task, endpoint.getDelay(), endpoint.getPeriod());
129            }
130        } else {
131            if (endpoint.getTime() != null) {
132                if (endpoint.getPeriod() > 0) {
133                    timer.schedule(task, endpoint.getTime(), endpoint.getPeriod());
134                } else {
135                    timer.schedule(task, endpoint.getTime());
136                }
137            } else {
138                if (endpoint.getPeriod() > 0) {
139                    timer.schedule(task, endpoint.getDelay(), endpoint.getPeriod());
140                } else {
141                    timer.schedule(task, endpoint.getDelay());
142                }
143            }
144        }
145        configured = true;
146    }
147
148    protected void sendTimerExchange(long counter) {
149        final Exchange exchange = endpoint.createExchange();
150        exchange.setProperty(Exchange.TIMER_COUNTER, counter);
151        exchange.setProperty(Exchange.TIMER_NAME, endpoint.getTimerName());
152        exchange.setProperty(Exchange.TIMER_TIME, endpoint.getTime());
153        exchange.setProperty(Exchange.TIMER_PERIOD, endpoint.getPeriod());
154
155        Date now = new Date();
156        exchange.setProperty(Exchange.TIMER_FIRED_TIME, now);
157        // also set now on in header with same key as quartz to be consistent
158        exchange.getIn().setHeader("firedTime", now);
159
160        if (LOG.isTraceEnabled()) {
161            LOG.trace("Timer {} is firing #{} count", endpoint.getTimerName(), counter);
162        }
163
164        if (!endpoint.isSynchronous()) {
165            getAsyncProcessor().process(exchange, new AsyncCallback() {
166                @Override
167                public void done(boolean doneSync) {
168                    // handle any thrown exception
169                    if (exchange.getException() != null) {
170                        getExceptionHandler().handleException("Error processing exchange", exchange, exchange.getException());
171                    }
172                }
173            });
174        } else {
175            try {
176                getProcessor().process(exchange);
177            } catch (Exception e) {
178                exchange.setException(e);
179            }
180
181            // handle any thrown exception
182            if (exchange.getException() != null) {
183                getExceptionHandler().handleException("Error processing exchange", exchange, exchange.getException());
184            }
185        }
186    }
187}