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.concurrent.TimeUnit; 020 import java.util.concurrent.locks.Condition; 021 import java.util.concurrent.locks.Lock; 022 import java.util.concurrent.locks.ReentrantLock; 023 024 import org.apache.camel.Exchange; 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.processor.resequencer.ResequencerEngine; 029 import org.apache.camel.processor.resequencer.SequenceElementComparator; 030 import org.apache.camel.processor.resequencer.SequenceSender; 031 import org.apache.camel.spi.ExceptionHandler; 032 import org.apache.camel.util.ServiceHelper; 033 034 /** 035 * A resequencer that re-orders a (continuous) stream of {@link Exchange}s. The 036 * algorithm implemented by {@link ResequencerEngine} is based on the detection 037 * of gaps in a message stream rather than on a fixed batch size. Gap detection 038 * in combination with timeouts removes the constraint of having to know the 039 * number of messages of a sequence (i.e. the batch size) in advance. 040 * <p> 041 * Messages must contain a unique sequence number for which a predecessor and a 042 * successor is known. For example a message with the sequence number 3 has a 043 * predecessor message with the sequence number 2 and a successor message with 044 * the sequence number 4. The message sequence 2,3,5 has a gap because the 045 * sucessor of 3 is missing. The resequencer therefore has to retain message 5 046 * until message 4 arrives (or a timeout occurs). 047 * <p> 048 * Instances of this class poll for {@link Exchange}s from a given 049 * <code>endpoint</code>. Resequencing work and the delivery of messages to 050 * the next <code>processor</code> is done within the single polling thread. 051 * 052 * @author Martin Krasser 053 * 054 * @version $Revision: 765731 $ 055 * 056 * @see ResequencerEngine 057 */ 058 public class StreamResequencer extends ServiceSupport implements SequenceSender<Exchange>, Processor { 059 060 private static final long DELIVERY_ATTEMPT_INTERVAL = 1000L; 061 062 private ExceptionHandler exceptionHandler; 063 private ResequencerEngine<Exchange> engine; 064 private Processor processor; 065 private Delivery delivery; 066 private int capacity; 067 068 /** 069 * Creates a new {@link StreamResequencer} instance. 070 * 071 * @param endpoint 072 * endpoint to poll exchanges from. 073 * @param processor 074 * next processor that processes re-ordered exchanges. 075 * @param comparator 076 * a sequence element comparator for exchanges. 077 */ 078 public StreamResequencer(Processor processor, SequenceElementComparator<Exchange> comparator) { 079 this.exceptionHandler = new LoggingExceptionHandler(getClass()); 080 this.engine = new ResequencerEngine<Exchange>(comparator); 081 this.engine.setSequenceSender(this); 082 this.processor = processor; 083 } 084 085 /** 086 * Returns this resequencer's exception handler. 087 * 088 * @return this resequencer's exception handler. 089 */ 090 public ExceptionHandler getExceptionHandler() { 091 return exceptionHandler; 092 } 093 094 /** 095 * Returns the next processor. 096 * 097 * @return the next processor. 098 */ 099 public Processor getProcessor() { 100 return processor; 101 } 102 103 /** 104 * Returns this resequencer's capacity. The capacity is the maximum number 105 * of exchanges that can be managed by this resequencer at a given point in 106 * time. If the capacity if reached, polling from the endpoint will be 107 * skipped for <code>timeout</code> milliseconds giving exchanges the 108 * possibility to time out and to be delivered after the waiting period. 109 * 110 * @return this resequencer's capacity. 111 */ 112 public int getCapacity() { 113 return capacity; 114 } 115 116 /** 117 * Returns this resequencer's timeout. This sets the resequencer engine's 118 * timeout via {@link ResequencerEngine#setTimeout(long)}. This value is 119 * also used to define the polling timeout from the endpoint. 120 * 121 * @return this resequencer's timeout. 122 * (Processor) 123 * @see ResequencerEngine#setTimeout(long) 124 */ 125 public long getTimeout() { 126 return engine.getTimeout(); 127 } 128 129 public void setCapacity(int capacity) { 130 this.capacity = capacity; 131 } 132 133 public void setTimeout(long timeout) { 134 engine.setTimeout(timeout); 135 } 136 137 @Override 138 public String toString() { 139 return "StreamResequencer[to: " + processor + "]"; 140 } 141 142 @Override 143 protected void doStart() throws Exception { 144 ServiceHelper.startServices(processor); 145 delivery = new Delivery(); 146 engine.start(); 147 delivery.start(); 148 } 149 150 @Override 151 protected void doStop() throws Exception { 152 // let's stop everything in the reverse order 153 // no need to stop the worker thread -- it will stop automatically when this service is stopped 154 engine.stop(); 155 ServiceHelper.stopServices(processor); 156 } 157 158 /** 159 * Sends the <code>exchange</code> to the next <code>processor</code>. 160 * 161 * @param o 162 * exchange to send. 163 */ 164 public void sendElement(Exchange o) throws Exception { 165 processor.process(o); 166 } 167 168 public void process(Exchange exchange) throws Exception { 169 while (engine.size() >= capacity) { 170 Thread.sleep(getTimeout()); 171 } 172 engine.insert(exchange); 173 delivery.request(); 174 } 175 176 private class Delivery extends Thread { 177 178 private Lock deliveryRequestLock = new ReentrantLock(); 179 private Condition deliveryRequestCondition = deliveryRequestLock.newCondition(); 180 181 public Delivery() { 182 super("Delivery Thread"); 183 } 184 185 @Override 186 public void run() { 187 while (true) { 188 try { 189 deliveryRequestLock.lock(); 190 try { 191 deliveryRequestCondition.await(DELIVERY_ATTEMPT_INTERVAL, TimeUnit.MILLISECONDS); 192 } finally { 193 deliveryRequestLock.unlock(); 194 } 195 } catch (InterruptedException e) { 196 break; 197 } 198 try { 199 engine.deliver(); 200 } catch (Exception e) { 201 exceptionHandler.handleException(e); 202 } 203 } 204 } 205 206 public void cancel() { 207 interrupt(); 208 } 209 210 public void request() { 211 deliveryRequestLock.lock(); 212 try { 213 deliveryRequestCondition.signal(); 214 } finally { 215 deliveryRequestLock.unlock(); 216 } 217 } 218 219 } 220 221 }