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 */ 017package org.apache.camel.model; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlElement; 025import javax.xml.bind.annotation.XmlElementRef; 026import javax.xml.bind.annotation.XmlElements; 027import javax.xml.bind.annotation.XmlRootElement; 028import javax.xml.bind.annotation.XmlTransient; 029 030import org.apache.camel.Expression; 031import org.apache.camel.model.config.BatchResequencerConfig; 032import org.apache.camel.model.config.ResequencerConfig; 033import org.apache.camel.model.config.StreamResequencerConfig; 034import org.apache.camel.model.language.ExpressionDefinition; 035import org.apache.camel.processor.resequencer.ExpressionResultComparator; 036import org.apache.camel.spi.Metadata; 037 038/** 039 * Resequences (re-order) messages based on an expression 040 */ 041@Metadata(label = "eip,routing") 042@XmlRootElement(name = "resequence") 043@XmlAccessorType(XmlAccessType.FIELD) 044public class ResequenceDefinition extends ProcessorDefinition<ResequenceDefinition> implements OutputNode { 045 @Metadata(required = false) 046 @XmlElements({@XmlElement(name = "batch-config", type = BatchResequencerConfig.class), @XmlElement(name = "stream-config", type = StreamResequencerConfig.class)}) 047 private ResequencerConfig resequencerConfig; 048 @XmlTransient 049 private BatchResequencerConfig batchConfig; 050 @XmlTransient 051 private StreamResequencerConfig streamConfig; 052 @XmlElementRef 053 @Metadata(required = true) 054 private ExpressionDefinition expression; 055 @XmlElementRef 056 private List<ProcessorDefinition<?>> outputs = new ArrayList<>(); 057 058 public ResequenceDefinition() { 059 } 060 061 public ResequenceDefinition(Expression expression) { 062 if (expression != null) { 063 setExpression(ExpressionNodeHelper.toExpressionDefinition(expression)); 064 } 065 } 066 067 @Override 068 public List<ProcessorDefinition<?>> getOutputs() { 069 return outputs; 070 } 071 072 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 073 this.outputs = outputs; 074 } 075 076 // Fluent API 077 // ------------------------------------------------------------------------- 078 /** 079 * Configures the stream-based resequencing algorithm using the default 080 * configuration. 081 * 082 * @return the builder 083 */ 084 public ResequenceDefinition stream() { 085 return stream(StreamResequencerConfig.getDefault()); 086 } 087 088 /** 089 * Configures the batch-based resequencing algorithm using the default 090 * configuration. 091 * 092 * @return the builder 093 */ 094 public ResequenceDefinition batch() { 095 return batch(BatchResequencerConfig.getDefault()); 096 } 097 098 /** 099 * Configures the stream-based resequencing algorithm using the given 100 * {@link StreamResequencerConfig}. 101 * 102 * @param config the config 103 * @return the builder 104 */ 105 public ResequenceDefinition stream(StreamResequencerConfig config) { 106 this.streamConfig = config; 107 this.batchConfig = null; 108 return this; 109 } 110 111 /** 112 * Configures the batch-based resequencing algorithm using the given 113 * {@link BatchResequencerConfig}. 114 * 115 * @param config the config 116 * @return the builder 117 */ 118 public ResequenceDefinition batch(BatchResequencerConfig config) { 119 this.batchConfig = config; 120 this.streamConfig = null; 121 return this; 122 } 123 124 /** 125 * Sets the timeout 126 * 127 * @param timeout timeout in millis 128 * @return the builder 129 */ 130 public ResequenceDefinition timeout(long timeout) { 131 if (streamConfig != null) { 132 streamConfig.setTimeout(timeout); 133 } else { 134 // initialize batch mode as its default mode 135 if (batchConfig == null) { 136 batch(); 137 } 138 batchConfig.setBatchTimeout(timeout); 139 } 140 return this; 141 } 142 143 /** 144 * Sets the interval in milli seconds the stream resequencer will at most 145 * wait while waiting for condition of being able to deliver. 146 * 147 * @param deliveryAttemptInterval interval in millis 148 * @return the builder 149 */ 150 public ResequenceDefinition deliveryAttemptInterval(long deliveryAttemptInterval) { 151 if (streamConfig == null) { 152 throw new IllegalStateException("deliveryAttemptInterval() only supported for stream resequencer"); 153 } 154 streamConfig.setDeliveryAttemptInterval(deliveryAttemptInterval); 155 return this; 156 } 157 158 /** 159 * Sets the rejectOld flag to throw an error when a message older than the 160 * last delivered message is processed 161 * 162 * @return the builder 163 */ 164 public ResequenceDefinition rejectOld() { 165 if (streamConfig == null) { 166 throw new IllegalStateException("rejectOld() only supported for stream resequencer"); 167 } 168 streamConfig.setRejectOld(true); 169 return this; 170 } 171 172 /** 173 * Sets the in batch size for number of exchanges received 174 * 175 * @param batchSize the batch size 176 * @return the builder 177 */ 178 public ResequenceDefinition size(int batchSize) { 179 if (streamConfig != null) { 180 throw new IllegalStateException("size() only supported for batch resequencer"); 181 } 182 // initialize batch mode as its default mode 183 if (batchConfig == null) { 184 batch(); 185 } 186 batchConfig.setBatchSize(batchSize); 187 return this; 188 } 189 190 /** 191 * Sets the capacity for the stream resequencer 192 * 193 * @param capacity the capacity 194 * @return the builder 195 */ 196 public ResequenceDefinition capacity(int capacity) { 197 if (streamConfig == null) { 198 throw new IllegalStateException("capacity() only supported for stream resequencer"); 199 } 200 streamConfig.setCapacity(capacity); 201 return this; 202 203 } 204 205 /** 206 * Enables duplicates for the batch resequencer mode 207 * 208 * @return the builder 209 */ 210 public ResequenceDefinition allowDuplicates() { 211 if (streamConfig != null) { 212 throw new IllegalStateException("allowDuplicates() only supported for batch resequencer"); 213 } 214 // initialize batch mode as its default mode 215 if (batchConfig == null) { 216 batch(); 217 } 218 batchConfig.setAllowDuplicates(true); 219 return this; 220 } 221 222 /** 223 * Enables reverse mode for the batch resequencer mode. 224 * <p/> 225 * This means the expression for determine the sequence order will be 226 * reversed. Can be used for Z..A or 9..0 ordering. 227 * 228 * @return the builder 229 */ 230 public ResequenceDefinition reverse() { 231 if (streamConfig != null) { 232 throw new IllegalStateException("reverse() only supported for batch resequencer"); 233 } 234 // initialize batch mode as its default mode 235 if (batchConfig == null) { 236 batch(); 237 } 238 batchConfig.setReverse(true); 239 return this; 240 } 241 242 /** 243 * If an incoming {@link org.apache.camel.Exchange} is invalid, then it will 244 * be ignored. 245 * 246 * @return builder 247 */ 248 public ResequenceDefinition ignoreInvalidExchanges() { 249 if (streamConfig != null) { 250 streamConfig.setIgnoreInvalidExchanges(true); 251 } else { 252 // initialize batch mode as its default mode 253 if (batchConfig == null) { 254 batch(); 255 } 256 batchConfig.setIgnoreInvalidExchanges(true); 257 } 258 return this; 259 } 260 261 /** 262 * Sets the comparator to use for stream resequencer 263 * 264 * @param comparator the comparator 265 * @return the builder 266 */ 267 public ResequenceDefinition comparator(ExpressionResultComparator comparator) { 268 if (streamConfig == null) { 269 throw new IllegalStateException("comparator() only supported for stream resequencer"); 270 } 271 streamConfig.setComparator(comparator); 272 return this; 273 } 274 275 @Override 276 public String toString() { 277 return "Resequencer[" + getExpression() + " -> " + getOutputs() + "]"; 278 } 279 280 @Override 281 public String getShortName() { 282 return "resequence"; 283 } 284 285 @Override 286 public String getLabel() { 287 return "resequencer[" + (getExpression() != null ? getExpression().getLabel() : "") + "]"; 288 } 289 290 public ResequencerConfig getResequencerConfig() { 291 return resequencerConfig; 292 } 293 294 /** 295 * To configure the resequencer in using either batch or stream 296 * configuration. Will by default use batch configuration. 297 */ 298 public void setResequencerConfig(ResequencerConfig resequencerConfig) { 299 this.resequencerConfig = resequencerConfig; 300 } 301 302 public BatchResequencerConfig getBatchConfig() { 303 if (batchConfig == null && resequencerConfig != null && resequencerConfig instanceof BatchResequencerConfig) { 304 return (BatchResequencerConfig)resequencerConfig; 305 } 306 return batchConfig; 307 } 308 309 public StreamResequencerConfig getStreamConfig() { 310 if (streamConfig == null && resequencerConfig != null && resequencerConfig instanceof StreamResequencerConfig) { 311 return (StreamResequencerConfig)resequencerConfig; 312 } 313 return streamConfig; 314 } 315 316 public void setBatchConfig(BatchResequencerConfig batchConfig) { 317 this.batchConfig = batchConfig; 318 } 319 320 public void setStreamConfig(StreamResequencerConfig streamConfig) { 321 this.streamConfig = streamConfig; 322 } 323 324 public ExpressionDefinition getExpression() { 325 return expression; 326 } 327 328 /** 329 * Expression to use for re-ordering the messages, such as a header with a 330 * sequence number 331 */ 332 public void setExpression(ExpressionDefinition expression) { 333 this.expression = expression; 334 } 335 336 /** 337 * Expression to use for re-ordering the messages, such as a header with a 338 * sequence number 339 */ 340 public void setExpression(Expression expression) { 341 setExpression(new ExpressionDefinition(expression)); 342 } 343 344}