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