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.component.seda;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.concurrent.BlockingQueue;
022 import java.util.concurrent.ExecutorService;
023 import java.util.concurrent.TimeUnit;
024
025 import org.apache.camel.Consumer;
026 import org.apache.camel.Endpoint;
027 import org.apache.camel.Exchange;
028 import org.apache.camel.Processor;
029 import org.apache.camel.ShutdownRunningTask;
030 import org.apache.camel.impl.LoggingExceptionHandler;
031 import org.apache.camel.impl.ServiceSupport;
032 import org.apache.camel.processor.MulticastProcessor;
033 import org.apache.camel.spi.ExceptionHandler;
034 import org.apache.camel.spi.ShutdownAware;
035 import org.apache.camel.util.ServiceHelper;
036 import org.apache.camel.util.concurrent.ExecutorServiceHelper;
037 import org.apache.commons.logging.Log;
038 import org.apache.commons.logging.LogFactory;
039
040 /**
041 * A Consumer for the SEDA component.
042 *
043 * @version $Revision: 893983 $
044 */
045 public class SedaConsumer extends ServiceSupport implements Consumer, Runnable, ShutdownAware {
046 private static final transient Log LOG = LogFactory.getLog(SedaConsumer.class);
047
048 private SedaEndpoint endpoint;
049 private Processor processor;
050 private ExecutorService executor;
051 private Processor multicast;
052 private ExceptionHandler exceptionHandler;
053
054 public SedaConsumer(SedaEndpoint endpoint, Processor processor) {
055 this.endpoint = endpoint;
056 this.processor = processor;
057 }
058
059 @Override
060 public String toString() {
061 return "SedaConsumer[" + endpoint.getEndpointUri() + "]";
062 }
063
064 public Endpoint getEndpoint() {
065 return endpoint;
066 }
067
068 public ExceptionHandler getExceptionHandler() {
069 if (exceptionHandler == null) {
070 exceptionHandler = new LoggingExceptionHandler(getClass());
071 }
072 return exceptionHandler;
073 }
074
075 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
076 this.exceptionHandler = exceptionHandler;
077 }
078
079 public Processor getProcessor() {
080 return processor;
081 }
082
083 public boolean deferShutdown(ShutdownRunningTask shutdownRunningTask) {
084 // deny stopping on shutdown as we want seda consumers to run in case some other queues
085 // depend on this consumer to run, so it can complete its exchanges
086 return true;
087 }
088
089 public int getPendingExchangesSize() {
090 // number of pending messages on the queue
091 return endpoint.getQueue().size();
092 }
093
094 public void run() {
095 BlockingQueue<Exchange> queue = endpoint.getQueue();
096 while (queue != null && isRunAllowed()) {
097 final Exchange exchange;
098 try {
099 exchange = queue.poll(1000, TimeUnit.MILLISECONDS);
100 } catch (InterruptedException e) {
101 if (LOG.isDebugEnabled()) {
102 LOG.debug("Sleep interrupted, are we stopping? " + (isStopping() || isStopped()));
103 }
104 continue;
105 }
106 if (exchange != null) {
107 if (isRunAllowed()) {
108 try {
109 sendToConsumers(exchange);
110
111 // log exception if an exception occurred and was not handled
112 if (exchange.getException() != null) {
113 getExceptionHandler().handleException("Error processing exchange", exchange, exchange.getException());
114 }
115 } catch (Exception e) {
116 getExceptionHandler().handleException("Error processing exchange", exchange, e);
117 }
118 } else {
119 if (LOG.isWarnEnabled()) {
120 LOG.warn("This consumer is stopped during polling an exchange, so putting it back on the seda queue: " + exchange);
121 }
122 try {
123 queue.put(exchange);
124 } catch (InterruptedException e) {
125 if (LOG.isDebugEnabled()) {
126 LOG.debug("Sleep interrupted, are we stopping? " + (isStopping() || isStopped()));
127 }
128 continue;
129 }
130 }
131 }
132 }
133 }
134
135 /**
136 * Send the given {@link Exchange} to the consumer(s).
137 * <p/>
138 * If multiple consumers then they will each receive a copy of the Exchange.
139 * A multicast processor will send the exchange in parallel to the multiple consumers.
140 * <p/>
141 * If there is only a single consumer then its dispatched directly to it using same thread.
142 *
143 * @param exchange the exchange
144 * @throws Exception can be thrown if processing of the exchange failed
145 */
146 protected void sendToConsumers(Exchange exchange) throws Exception {
147 int size = endpoint.getConsumers().size();
148
149 // if there are multiple consumers then multicast to them
150 if (size > 1) {
151
152 if (LOG.isDebugEnabled()) {
153 LOG.debug("Multicasting to " + endpoint.getConsumers().size() + " consumers for Exchange: " + exchange);
154 }
155
156 // use a multicast processor to process it
157 Processor mp = getMulticastProcessor();
158 mp.process(exchange);
159 } else {
160 // use the regular processor
161 processor.process(exchange);
162 }
163 }
164
165 protected synchronized Processor getMulticastProcessor() {
166 if (multicast == null) {
167 int size = endpoint.getConsumers().size();
168
169 List<Processor> processors = new ArrayList<Processor>(size);
170 for (SedaConsumer consumer : endpoint.getConsumers()) {
171 processors.add(consumer.getProcessor());
172 }
173
174 ExecutorService multicastExecutor = ExecutorServiceHelper.newFixedThreadPool(size, endpoint.getEndpointUri() + "(multicast)", true);
175 multicast = new MulticastProcessor(processors, null, true, multicastExecutor, false, false);
176 }
177 return multicast;
178 }
179
180 protected void doStart() throws Exception {
181 int poolSize = endpoint.getConcurrentConsumers();
182 executor = ExecutorServiceHelper.newFixedThreadPool(poolSize, endpoint.getEndpointUri(), true);
183 for (int i = 0; i < poolSize; i++) {
184 executor.execute(this);
185 }
186 endpoint.onStarted(this);
187 }
188
189 protected void doStop() throws Exception {
190 endpoint.onStopped(this);
191 executor.shutdownNow();
192 executor = null;
193
194 if (multicast != null) {
195 ServiceHelper.stopServices(multicast);
196 }
197 }
198
199 }