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