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