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.processor;
018
019 import java.util.Collection;
020 import java.util.Iterator;
021
022 import org.apache.camel.Endpoint;
023 import org.apache.camel.Exchange;
024 import org.apache.camel.PollingConsumer;
025 import org.apache.camel.Processor;
026 import org.apache.camel.impl.LoggingExceptionHandler;
027 import org.apache.camel.impl.ServiceSupport;
028 import org.apache.camel.spi.ExceptionHandler;
029 import org.apache.camel.util.ServiceHelper;
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032
033 /**
034 * A base class for any kind of {@link Processor} which implements some kind of
035 * batch processing.
036 *
037 * @version $Revision: 1.1 $
038 */
039 public class BatchProcessor extends ServiceSupport implements Runnable {
040 private static final transient Log LOG = LogFactory.getLog(Resequencer.class);
041 private Endpoint endpoint;
042 private Processor processor;
043 private Collection<Exchange> collection;
044 private long batchTimeout = 1000L;
045 private int batchSize = 100;
046 private PollingConsumer consumer;
047 private ExceptionHandler exceptionHandler;
048
049 public BatchProcessor(Endpoint endpoint, Processor processor, Collection<Exchange> collection) {
050 this.endpoint = endpoint;
051 this.processor = processor;
052 this.collection = collection;
053 }
054
055 @Override
056 public String toString() {
057 return "BatchProcessor[to: " + processor + "]";
058 }
059
060 public void run() {
061 LOG.debug("Starting thread for " + this);
062 while (!isStopped() && !isStopping()) {
063 try {
064 processBatch();
065 } catch (Exception e) {
066 getExceptionHandler().handleException(e);
067 }
068 }
069 collection.clear();
070 }
071
072 // Properties
073 // -------------------------------------------------------------------------
074 public ExceptionHandler getExceptionHandler() {
075 if (exceptionHandler == null) {
076 exceptionHandler = new LoggingExceptionHandler(getClass());
077 }
078 return exceptionHandler;
079 }
080
081 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
082 this.exceptionHandler = exceptionHandler;
083 }
084
085 public int getBatchSize() {
086 return batchSize;
087 }
088
089 public void setBatchSize(int batchSize) {
090 this.batchSize = batchSize;
091 }
092
093 public long getBatchTimeout() {
094 return batchTimeout;
095 }
096
097 public void setBatchTimeout(long batchTimeout) {
098 this.batchTimeout = batchTimeout;
099 }
100
101 public Endpoint getEndpoint() {
102 return endpoint;
103 }
104
105 public Processor getProcessor() {
106 return processor;
107 }
108
109 /**
110 * A transactional method to process a batch of messages up to a timeout
111 * period or number of messages reached.
112 */
113 protected synchronized void processBatch() throws Exception {
114 long start = System.currentTimeMillis();
115 long end = start + batchTimeout;
116 for (int i = 0; i < batchSize; i++) {
117 long timeout = end - System.currentTimeMillis();
118
119 Exchange exchange = consumer.receive(timeout);
120 if (exchange == null) {
121 break;
122 }
123 collection.add(exchange);
124 }
125
126 if (LOG.isDebugEnabled()) {
127 LOG.debug("Finsihed batch size: " + batchSize + " timeout: " + batchTimeout + " so sending set: "
128 + collection);
129 }
130
131 // lets send the batch
132 Iterator<Exchange> iter = collection.iterator();
133 while (iter.hasNext()) {
134 Exchange exchange = iter.next();
135 iter.remove();
136 processExchange(exchange);
137 }
138 }
139
140 /**
141 * Strategy Method to process an exchange in the batch. This method allows
142 * derived classes to perform custom processing before or after an
143 * individual exchange is processed
144 */
145 protected void processExchange(Exchange exchange) throws Exception {
146 processor.process(exchange);
147 }
148
149 protected void doStart() throws Exception {
150 consumer = endpoint.createPollingConsumer();
151
152 ServiceHelper.startServices(processor, consumer);
153
154 Thread thread = new Thread(this, this + " Polling Thread");
155 thread.start();
156 }
157
158 protected void doStop() throws Exception {
159 ServiceHelper.stopServices(consumer, processor);
160 collection.clear();
161 }
162
163 protected Collection<Exchange> getCollection() {
164 return collection;
165 }
166 }