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