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