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