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.impl; 018 019import java.util.concurrent.ArrayBlockingQueue; 020import java.util.concurrent.BlockingQueue; 021import java.util.concurrent.LinkedBlockingQueue; 022import java.util.concurrent.RejectedExecutionException; 023import java.util.concurrent.TimeUnit; 024 025import org.apache.camel.Consumer; 026import org.apache.camel.Endpoint; 027import org.apache.camel.Exchange; 028import org.apache.camel.ExchangeTimedOutException; 029import org.apache.camel.IsSingleton; 030import org.apache.camel.PollingConsumerPollingStrategy; 031import org.apache.camel.Processor; 032import org.apache.camel.spi.ExceptionHandler; 033import org.apache.camel.support.LoggingExceptionHandler; 034import org.apache.camel.util.ServiceHelper; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038/** 039 * A default implementation of the {@link org.apache.camel.PollingConsumer} which uses the normal 040 * asynchronous consumer mechanism along with a {@link BlockingQueue} to allow 041 * the caller to pull messages on demand. 042 * 043 * @version 044 */ 045public class EventDrivenPollingConsumer extends PollingConsumerSupport implements Processor, IsSingleton { 046 private static final Logger LOG = LoggerFactory.getLogger(EventDrivenPollingConsumer.class); 047 private final BlockingQueue<Exchange> queue; 048 private ExceptionHandler interruptedExceptionHandler; 049 private Consumer consumer; 050 private boolean blockWhenFull = true; 051 private long blockTimeout; 052 private final int queueCapacity; 053 054 public EventDrivenPollingConsumer(Endpoint endpoint) { 055 this(endpoint, 1000); 056 } 057 058 public EventDrivenPollingConsumer(Endpoint endpoint, int queueSize) { 059 super(endpoint); 060 this.queueCapacity = queueSize; 061 if (queueSize <= 0) { 062 this.queue = new LinkedBlockingQueue<>(); 063 } else { 064 this.queue = new ArrayBlockingQueue<>(queueSize); 065 } 066 this.interruptedExceptionHandler = new LoggingExceptionHandler(endpoint.getCamelContext(), EventDrivenPollingConsumer.class); 067 } 068 069 public EventDrivenPollingConsumer(Endpoint endpoint, BlockingQueue<Exchange> queue) { 070 super(endpoint); 071 this.queue = queue; 072 this.queueCapacity = queue.remainingCapacity(); 073 this.interruptedExceptionHandler = new LoggingExceptionHandler(endpoint.getCamelContext(), EventDrivenPollingConsumer.class); 074 } 075 076 public boolean isBlockWhenFull() { 077 return blockWhenFull; 078 } 079 080 public void setBlockWhenFull(boolean blockWhenFull) { 081 this.blockWhenFull = blockWhenFull; 082 } 083 084 public long getBlockTimeout() { 085 return blockTimeout; 086 } 087 088 public void setBlockTimeout(long blockTimeout) { 089 this.blockTimeout = blockTimeout; 090 } 091 092 /** 093 * Gets the queue capacity. 094 */ 095 public int getQueueCapacity() { 096 return queueCapacity; 097 } 098 099 /** 100 * Gets the current queue size (no of elements in the queue). 101 */ 102 public int getQueueSize() { 103 return queue.size(); 104 } 105 106 public Exchange receiveNoWait() { 107 return receive(0); 108 } 109 110 public Exchange receive() { 111 // must be started 112 if (!isRunAllowed() || !isStarted()) { 113 throw new RejectedExecutionException(this + " is not started, but in state: " + getStatus().name()); 114 } 115 116 while (isRunAllowed()) { 117 // synchronizing the ordering of beforePoll, poll and afterPoll as an atomic activity 118 synchronized (this) { 119 try { 120 beforePoll(0); 121 // take will block waiting for message 122 return queue.take(); 123 } catch (InterruptedException e) { 124 handleInterruptedException(e); 125 } finally { 126 afterPoll(); 127 } 128 } 129 } 130 LOG.trace("Consumer is not running, so returning null"); 131 return null; 132 } 133 134 public Exchange receive(long timeout) { 135 // must be started 136 if (!isRunAllowed() || !isStarted()) { 137 throw new RejectedExecutionException(this + " is not started, but in state: " + getStatus().name()); 138 } 139 140 // synchronizing the ordering of beforePoll, poll and afterPoll as an atomic activity 141 synchronized (this) { 142 try { 143 // use the timeout value returned from beforePoll 144 timeout = beforePoll(timeout); 145 return queue.poll(timeout, TimeUnit.MILLISECONDS); 146 } catch (InterruptedException e) { 147 handleInterruptedException(e); 148 return null; 149 } finally { 150 afterPoll(); 151 } 152 } 153 } 154 155 public void process(Exchange exchange) throws Exception { 156 if (isBlockWhenFull()) { 157 try { 158 if (getBlockTimeout() <= 0) { 159 queue.put(exchange); 160 } else { 161 boolean added = queue.offer(exchange, getBlockTimeout(), TimeUnit.MILLISECONDS); 162 if (!added) { 163 throw new ExchangeTimedOutException(exchange, getBlockTimeout()); 164 } 165 } 166 } catch (InterruptedException e) { 167 // ignore 168 log.debug("Put interrupted, are we stopping? {}", isStopping() || isStopped()); 169 } 170 } else { 171 queue.add(exchange); 172 } 173 } 174 175 public ExceptionHandler getInterruptedExceptionHandler() { 176 return interruptedExceptionHandler; 177 } 178 179 public void setInterruptedExceptionHandler(ExceptionHandler interruptedExceptionHandler) { 180 this.interruptedExceptionHandler = interruptedExceptionHandler; 181 } 182 183 public Consumer getDelegateConsumer() { 184 return consumer; 185 } 186 187 protected void handleInterruptedException(InterruptedException e) { 188 getInterruptedExceptionHandler().handleException(e); 189 } 190 191 protected long beforePoll(long timeout) { 192 if (consumer instanceof PollingConsumerPollingStrategy) { 193 PollingConsumerPollingStrategy strategy = (PollingConsumerPollingStrategy) consumer; 194 try { 195 timeout = strategy.beforePoll(timeout); 196 } catch (Exception e) { 197 LOG.debug("Error occurred before polling " + consumer + ". This exception will be ignored.", e); 198 } 199 } 200 return timeout; 201 } 202 203 protected void afterPoll() { 204 if (consumer instanceof PollingConsumerPollingStrategy) { 205 PollingConsumerPollingStrategy strategy = (PollingConsumerPollingStrategy) consumer; 206 try { 207 strategy.afterPoll(); 208 } catch (Exception e) { 209 LOG.debug("Error occurred after polling " + consumer + ". This exception will be ignored.", e); 210 } 211 } 212 } 213 214 protected Consumer getConsumer() { 215 return consumer; 216 } 217 218 protected Consumer createConsumer() throws Exception { 219 return getEndpoint().createConsumer(this); 220 } 221 222 protected void doStart() throws Exception { 223 // lets add ourselves as a consumer 224 consumer = createConsumer(); 225 226 // if the consumer has a polling strategy then invoke that 227 if (consumer instanceof PollingConsumerPollingStrategy) { 228 PollingConsumerPollingStrategy strategy = (PollingConsumerPollingStrategy) consumer; 229 strategy.onInit(); 230 } else { 231 ServiceHelper.startService(consumer); 232 } 233 } 234 235 protected void doStop() throws Exception { 236 ServiceHelper.stopService(consumer); 237 } 238 239 protected void doShutdown() throws Exception { 240 ServiceHelper.stopAndShutdownService(consumer); 241 queue.clear(); 242 } 243 244 @Override 245 // As the consumer could take the messages at once, so we cannot release the consumer 246 public boolean isSingleton() { 247 return true; 248 } 249}