001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.camel.processor;
019
020 import org.apache.camel.Endpoint;
021 import org.apache.camel.Exchange;
022 import org.apache.camel.Expression;
023 import org.apache.camel.PollingConsumer;
024 import org.apache.camel.Processor;
025 import org.apache.camel.impl.LoggingExceptionHandler;
026 import org.apache.camel.impl.ServiceSupport;
027 import org.apache.camel.spi.ExceptionHandler;
028 import org.apache.camel.util.ExpressionComparator;
029 import org.apache.camel.util.ExpressionListComparator;
030 import org.apache.camel.util.ServiceHelper;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033
034 import java.util.Comparator;
035 import java.util.Iterator;
036 import java.util.List;
037 import java.util.Set;
038 import java.util.TreeSet;
039
040 /**
041 * An implementation of the <a href="http://activemq.apache.org/camel/resequencer.html">Resequencer</a>
042 *
043 * @version $Revision: 1.1 $
044 */
045 public class Resequencer extends ServiceSupport implements Runnable {
046 private static final transient Log log = LogFactory.getLog(Resequencer.class);
047 private Endpoint endpoint;
048 private Processor processor;
049 private Set<Exchange> set;
050 private long batchTimeout = 1000L;
051 private int batchSize = 100;
052 private PollingConsumer consumer;
053 private ExceptionHandler exceptionHandler;
054
055 public Resequencer(Endpoint endpoint, Processor processor, Expression<Exchange> expression) {
056 this(endpoint, processor, createSet(expression));
057 }
058
059 public Resequencer(Endpoint endpoint, Processor processor, List<Expression<Exchange>> expressions) {
060 this(endpoint, processor, createSet(expressions));
061 }
062
063 public Resequencer(Endpoint endpoint, Processor processor, Set<Exchange> set) {
064 this.endpoint = endpoint;
065 this.processor = processor;
066 this.set = set;
067 }
068
069 @Override
070 public String toString() {
071 return "Resequencer[to: " + processor + "]";
072 }
073
074 public void run() {
075 log.debug("Starting thread for " + this);
076 while (!isStopped() && !isStopping()) {
077 try {
078 processBatch();
079 }
080 catch (Exception e) {
081 getExceptionHandler().handleException(e);
082 }
083 }
084 set.clear();
085 }
086
087 // Properties
088 //-------------------------------------------------------------------------
089 public ExceptionHandler getExceptionHandler() {
090 if (exceptionHandler == null) {
091 exceptionHandler = new LoggingExceptionHandler(getClass());
092 }
093 return exceptionHandler;
094 }
095
096 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
097 this.exceptionHandler = exceptionHandler;
098 }
099
100 public int getBatchSize() {
101 return batchSize;
102 }
103
104 public void setBatchSize(int batchSize) {
105 this.batchSize = batchSize;
106 }
107
108 public long getBatchTimeout() {
109 return batchTimeout;
110 }
111
112 public void setBatchTimeout(long batchTimeout) {
113 this.batchTimeout = batchTimeout;
114 }
115
116 // Implementation methods
117 //-------------------------------------------------------------------------
118
119 /**
120 * A transactional method to process a batch of messages up to a timeout period
121 * or number of messages reached.
122 */
123 protected synchronized void processBatch() throws Exception {
124 long start = System.currentTimeMillis();
125 long end = start + batchTimeout;
126 for (int i = 0; i < batchSize; i++) {
127 long timeout = end - System.currentTimeMillis();
128
129 Exchange exchange = consumer.receive(timeout);
130 if (exchange == null) {
131 break;
132 }
133 set.add(exchange);
134 }
135
136 if (log.isDebugEnabled()) {
137 log.debug("Finsihed batch size: " + batchSize + " timeout: " + batchTimeout + " so sending set: " + set);
138 }
139
140 // lets send the batch
141 Iterator<Exchange> iter = set.iterator();
142 while (iter.hasNext()) {
143 Exchange exchange = iter.next();
144 iter.remove();
145 processor.process(exchange);
146 }
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 consumer = null;
161 }
162
163 protected static Set<Exchange> createSet(Expression<Exchange> expression) {
164 return createSet(new ExpressionComparator<Exchange>(expression));
165 }
166
167 protected static Set<Exchange> createSet(List<Expression<Exchange>> expressions) {
168 if (expressions.size() == 1) {
169 return createSet(expressions.get(0));
170 }
171 return createSet(new ExpressionListComparator<Exchange>(expressions));
172 }
173
174 protected static Set<Exchange> createSet(Comparator<? super Exchange> comparator) {
175 return new TreeSet<Exchange>(comparator);
176 }
177 }