/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.selector;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.integration.core.MessageSelector;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
/**
* A message selector implementation that passes incoming messages through a
* chain of selectors. Whether the Message is {@link #accept(Message) accepted}
* is based upon the tallied results of the individual selectors' responses in
* accordance with this chain's {@link VotingStrategy}.
*
* @author Mark Fisher
* @author Gary Russell
*/
public class MessageSelectorChain implements MessageSelector {
public enum VotingStrategy { ALL, ANY, MAJORITY, MAJORITY_OR_TIE };
private volatile VotingStrategy votingStrategy = VotingStrategy.ALL;
private final List<MessageSelector> selectors = new CopyOnWriteArrayList<MessageSelector>();
/**
* Specify the voting strategy for this selector chain.
* <p>The default is {@link VotingStrategy#ALL}.
*
* @param votingStrategy The voting strategy.
*/
public void setVotingStrategy(VotingStrategy votingStrategy) {
Assert.notNull(votingStrategy, "votingStrategy must not be null");
this.votingStrategy = votingStrategy;
}
/**
* Add a selector to the end of the chain.
*
* @param selector The message selector.
*/
public void add(MessageSelector selector) {
this.selectors.add(selector);
}
/**
* Add a selector to the chain at the specified index.
*
* @param index The index.
* @param selector The message selector.
*/
public void add(int index, MessageSelector selector) {
this.selectors.add(index, selector);
}
/**
* Initialize the selector chain. Removes any existing selectors.
*
* @param selectors The message selectors.
*/
public void setSelectors(List<MessageSelector> selectors) {
Assert.notEmpty(selectors, "selectors must not be empty");
synchronized (this.selectors) {
this.selectors.clear();
this.selectors.addAll(selectors);
}
}
/**
* Pass the message through the selector chain. Whether the Message is
* {@link #accept(Message) accepted} is based upon the tallied results of
* the individual selectors' responses in accordance with this chain's
* {@link VotingStrategy}.
*/
@Override
public final boolean accept(Message<?> message) {
int count = 0;
int accepted = 0;
for (MessageSelector next : this.selectors) {
count++;
if (next.accept(message)) {
if (this.votingStrategy.equals(VotingStrategy.ANY)) {
return true;
}
accepted++;
}
else if (this.votingStrategy.equals(VotingStrategy.ALL)) {
return false;
}
}
return this.decide(accepted, count);
}
private boolean decide(int accepted, int total) {
if (accepted == 0) {
return false;
}
switch (this.votingStrategy) {
case ANY:
return true;
case ALL:
return (accepted == total);
case MAJORITY:
return (2 * accepted) > total;
case MAJORITY_OR_TIE:
return (2 * accepted) >= total;
default:
throw new IllegalArgumentException("unsupported voting strategy " + this.votingStrategy);
}
}
}