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.impl;
018
019 import java.util.concurrent.ExecutorService;
020 import java.util.concurrent.ScheduledExecutorService;
021 import java.util.concurrent.ScheduledFuture;
022 import java.util.concurrent.TimeUnit;
023
024 import org.apache.camel.Endpoint;
025 import org.apache.camel.Processor;
026 import org.apache.camel.SuspendableService;
027 import org.apache.camel.spi.PollingConsumerPollStrategy;
028 import org.apache.camel.util.ObjectHelper;
029 import org.apache.camel.util.concurrent.ExecutorServiceHelper;
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032
033 /**
034 * A useful base class for any consumer which is polling based
035 *
036 * @version $Revision: 896858 $
037 */
038 public abstract class ScheduledPollConsumer extends DefaultConsumer implements Runnable, SuspendableService {
039 private static final int DEFAULT_THREADPOOL_SIZE = 10;
040 private static final transient Log LOG = LogFactory.getLog(ScheduledPollConsumer.class);
041
042 private final ScheduledExecutorService executor;
043 private ScheduledFuture<?> future;
044
045 // if adding more options then align with ScheduledPollEndpoint#configureScheduledPollConsumerProperties
046 private long initialDelay = 1000;
047 private long delay = 500;
048 private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
049 private boolean useFixedDelay;
050 private PollingConsumerPollStrategy pollStrategy = new DefaultPollingConsumerPollStrategy();
051 private volatile boolean suspended;
052
053 public ScheduledPollConsumer(DefaultEndpoint endpoint, Processor processor) {
054 super(endpoint, processor);
055
056 ScheduledExecutorService scheduled;
057 ExecutorService service = endpoint.getExecutorService();
058 if (service instanceof ScheduledExecutorService) {
059 scheduled = (ScheduledExecutorService) service;
060 } else {
061 scheduled = ExecutorServiceHelper.newScheduledThreadPool(DEFAULT_THREADPOOL_SIZE, getEndpoint().getEndpointUri(), true);
062 }
063
064 this.executor = scheduled;
065 ObjectHelper.notNull(executor, "executor");
066 }
067
068 public ScheduledPollConsumer(Endpoint endpoint, Processor processor, ScheduledExecutorService executor) {
069 super(endpoint, processor);
070 this.executor = executor;
071 ObjectHelper.notNull(executor, "executor");
072 }
073
074 /**
075 * Invoked whenever we should be polled
076 */
077 public void run() {
078 if (suspended) {
079 if (LOG.isTraceEnabled()) {
080 LOG.trace("Cannot start to poll: " + this.getEndpoint() + " as its suspended");
081 }
082 return;
083 }
084
085 int retryCounter = -1;
086 boolean done = false;
087
088 while (!done) {
089 try {
090 // eager assume we are done
091 done = true;
092 if (isPollAllowed()) {
093
094 if (retryCounter == -1) {
095 if (LOG.isTraceEnabled()) {
096 LOG.trace("Starting to poll: " + this.getEndpoint());
097 }
098 } else {
099 if (LOG.isDebugEnabled()) {
100 LOG.debug("Retrying attempt " + retryCounter + " to poll: " + this.getEndpoint());
101 }
102 }
103
104 pollStrategy.begin(this, getEndpoint());
105 retryCounter++;
106 poll();
107 pollStrategy.commit(this, getEndpoint());
108 }
109
110 if (LOG.isTraceEnabled()) {
111 LOG.trace("Finished polling: " + this.getEndpoint());
112 }
113 } catch (Exception e) {
114 try {
115 boolean retry = pollStrategy.rollback(this, getEndpoint(), retryCounter, e);
116 if (retry) {
117 done = false;
118 }
119 } catch (Exception re) {
120 throw ObjectHelper.wrapRuntimeCamelException(re);
121 }
122 } catch (Error e) {
123 // log the fatal error as the JDK itself may not log it for us
124 log.fatal("Consumer " + this + " could not poll endpoint: " + getEndpoint().getEndpointUri() + " caused by: " + e.getMessage(), e);
125 throw e;
126 }
127 }
128 }
129
130 // Properties
131 // -------------------------------------------------------------------------
132
133 protected boolean isPollAllowed() {
134 return isRunAllowed() && !isSuspended();
135 }
136
137 public long getInitialDelay() {
138 return initialDelay;
139 }
140
141 public void setInitialDelay(long initialDelay) {
142 this.initialDelay = initialDelay;
143 }
144
145 public long getDelay() {
146 return delay;
147 }
148
149 public void setDelay(long delay) {
150 this.delay = delay;
151 }
152
153 public TimeUnit getTimeUnit() {
154 return timeUnit;
155 }
156
157 public void setTimeUnit(TimeUnit timeUnit) {
158 this.timeUnit = timeUnit;
159 }
160
161 public boolean isUseFixedDelay() {
162 return useFixedDelay;
163 }
164
165 public void setUseFixedDelay(boolean useFixedDelay) {
166 this.useFixedDelay = useFixedDelay;
167 }
168
169 public PollingConsumerPollStrategy getPollStrategy() {
170 return pollStrategy;
171 }
172
173 public void setPollStrategy(PollingConsumerPollStrategy pollStrategy) {
174 this.pollStrategy = pollStrategy;
175 }
176
177 public void suspend() {
178 suspended = true;
179 }
180
181 public void resume() {
182 suspended = false;
183 }
184
185 public boolean isSuspended() {
186 return suspended;
187 }
188
189 // Implementation methods
190 // -------------------------------------------------------------------------
191
192 /**
193 * The polling method which is invoked periodically to poll this consumer
194 *
195 * @throws Exception can be thrown if an exception occurred during polling
196 */
197 protected abstract void poll() throws Exception;
198
199 @Override
200 protected void doStart() throws Exception {
201 super.doStart();
202 if (isUseFixedDelay()) {
203 future = executor.scheduleWithFixedDelay(this, getInitialDelay(), getDelay(), getTimeUnit());
204 } else {
205 future = executor.scheduleAtFixedRate(this, getInitialDelay(), getDelay(), getTimeUnit());
206 }
207 }
208
209 @Override
210 protected void doStop() throws Exception {
211 if (future != null) {
212 future.cancel(false);
213 }
214 super.doStop();
215 }
216 }