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}