INT-684, Added new OrderAwareLinkedHashSet and modified AbstractDispatcher

This commit is contained in:
Oleg Zhurakousky
2009-06-28 22:26:10 +00:00
parent 037b74c881
commit e6062ba749
3 changed files with 267 additions and 25 deletions

View File

@@ -19,12 +19,11 @@ package org.springframework.integration.dispatcher;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.task.TaskExecutor;
@@ -46,12 +45,13 @@ import org.springframework.util.Assert;
*
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
*/
public abstract class AbstractDispatcher implements MessageDispatcher {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile List<MessageHandler> handlers = new LinkedList<MessageHandler>();
private volatile Set<MessageHandler> handlers = new OrderedAwareLinkedHashSet<MessageHandler>();
@SuppressWarnings("unchecked")
private volatile Comparator<Object> comparator = new OrderComparator();
@@ -85,32 +85,12 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
}
protected List<MessageHandler> getHandlers() {
return Collections.unmodifiableList(this.handlers);
return Collections.unmodifiableList(new ArrayList(this.handlers));
}
public boolean addHandler(MessageHandler handler) {
if (this.handlers.contains(handler)) {
return false;
}
if (!this.hasOrder(handler)) {
synchronized (this.handlerListMonitor) {
return this.handlers.add(handler);
}
}
synchronized (this.handlerListMonitor) {
List<MessageHandler> orderedHandlers = new ArrayList<MessageHandler>();
orderedHandlers.add(handler);
List<MessageHandler> handlerList = new ArrayList<MessageHandler>(handlers);
for (MessageHandler nextHandler : handlerList) {
if (this.hasOrder(nextHandler)) {
orderedHandlers.add(nextHandler);
}
}
handlerList.removeAll(orderedHandlers);
Collections.sort(orderedHandlers, this.comparator);
orderedHandlers.addAll(handlerList);
this.handlers = orderedHandlers;
return true;
return this.handlers.add(handler);
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2002-2009 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.dispatcher;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
*
* Special Set that maintains the following semantics:
* All elements that are un-ordered (do not implement {@link Ordered} interface or annotated {@link Order} annotation)
* will be stored in the order in which they were added, maintaining the semantics of the {@link LinkedHashSet}.
* However, there is a special {@link Comparator} (instantiated by default) for this implementation of {@link Set},
* which is aware of the {@link Ordered} interface or and {@link Order} annotation. Those elements will have
* precedence over un-ordered elements. If elements have the same order but themselves do not equal to one another
* they will be placed to the right (appended next to) of the element with the same order, thus preserving the order
* of the insertion and maintaining {@link LinkedHashSet} semantics.
*
* @author Oleg Zhurakousky
* @since 1.0.3
*/
class OrderedAwareLinkedHashSet<E> extends LinkedHashSet<E> {
private TreeSet orderedSet = new TreeSet(new EqualsAwareOrderComparator());
/**
* Every time when Ordered element is added via this method
* this Set will be re-sorted, otherwise the element is simply added to the end of the stack.
* If adding multiple objects for performance reasons it is recommended to use
* addAll(Collection c) method which first adds all the elements to this set
* and then calls reinitializeThis() method.
*/
public boolean add(E o){
boolean present = this.doAdd(o);
if (o instanceof Ordered){
this.reinitializeThis();
}
return present;
}
/**
* Adds all elements in this Collection and then resorts this set
* via call to the reinitializeThis() method
*/
public boolean addAll(Collection<? extends E> c){
Assert.notNull(c,"Can not merge with NULL set");
if (CollectionUtils.isEmpty(c)){
return false;
}
for (E object : c) {
this.doAdd(object);
}
this.reinitializeThis();
// due to the nature of previous method
// at this point collection will always be modified
return true;
}
/**
*
* @param o
* @return
*/
private boolean doAdd(E o){
if (o instanceof Ordered){
return orderedSet.add(o);
} else {
return super.add(o);
}
}
/**
*
*/
private void reinitializeThis(){
E[] tempUnorderedElements = (E[]) super.toArray();
E[] tempOrderedElements = (E[]) orderedSet.toArray();
super.clear();
for (E object : tempOrderedElements) {
super.add(object);
}
for (E object : tempUnorderedElements) {
super.add(object);
}
}
/**
* Will reuse most of the functionality of OrderComparator, however if
* elements have the same order, then 1 will be returned, thus positioning
* such element to the right of the existing element.
*/
private static class EqualsAwareOrderComparator extends OrderComparator {
public int compare(Object o1, Object o2) {
int value = super.compare(o1, o2);
// see if objects are not equal
if (value == 0 && !o1.equals(o2)){
return 1;
}
return value;
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2002-2008 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.dispatcher;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.core.Ordered;
import static org.junit.Assert.assertEquals;
/**
* @author Oleg Zhurakousky
* @since 1.0.3
*/
public class OrderedAwareLinkedHashSetTests {
/**
* Tests that semantics of the LinkedHashSet were not broken
*/
@Test
public void testAddUnordered(){
OrderedAwareLinkedHashSet setToTest = new OrderedAwareLinkedHashSet();
setToTest.add("foo");
setToTest.add("bar");
setToTest.add("baz");
assertEquals(3, setToTest.size());
Object[] elements = setToTest.toArray();
assertEquals("foo", elements[0]);
assertEquals("bar", elements[1]);
assertEquals("baz", elements[2]);
}
/**
* Tests that semantics of TreeSet(Comparator) were not broken.
* However, there is a special Comparator (instantiated by default) for this implementation of Set,
* which allows elements with the same "order" as long as these elements themselves are not equal.
* In this case element with the same order will be placed to the right (appended next to) of
* the already existing element, thus preserving the order of insertion (LinkedHashset semantics)
* within the elements that have the same "order" value.
*/
@Test
public void testAddOrdered(){
OrderedAwareLinkedHashSet setToTest = new OrderedAwareLinkedHashSet();
Object o1 = new Foo(3);
Object o2 = new Foo(1);
Object o3 = new Foo(2);
Object o4 = new Foo(2);
Object o5 = new Foo(Ordered.LOWEST_PRECEDENCE);
Object o6 = new Foo(Ordered.LOWEST_PRECEDENCE);
Object o7 = new Foo(Ordered.HIGHEST_PRECEDENCE);
Object o8 = new Foo(Ordered.HIGHEST_PRECEDENCE);
Object o9 = new Foo(4);
Object o10 = new Foo(2);
setToTest.add(o1);
setToTest.add(o2);
setToTest.add(o3);
setToTest.add(o4);
setToTest.add(o5);
setToTest.add(o6);
setToTest.add(o7);
setToTest.add(o8);
setToTest.add(o9);
setToTest.add(o10);
assertEquals(10, setToTest.size());
System.out.println(setToTest);
Object[] elements = setToTest.toArray();
assertEquals(o7, elements[0]);
assertEquals(o8, elements[1]);
assertEquals(o2, elements[2]);
assertEquals(o3, elements[3]);
assertEquals(o4, elements[4]);
assertEquals(o10, elements[5]);
assertEquals(o1, elements[6]);
assertEquals(o9, elements[7]);
assertEquals(o5, elements[8]);
assertEquals(o6, elements[9]);
}
@Test
public void testAddAllOrderedUnordered(){
List tempList = new ArrayList();
Object o1 = new Foo(3);
Object o2 = new Foo(1);
Object o3 = "FooA";
Object o4 = new Foo(2);
Object o5 = new Foo(Ordered.LOWEST_PRECEDENCE);
Object o6 = new Foo(Ordered.LOWEST_PRECEDENCE);
Object o7 = new Foo(Ordered.HIGHEST_PRECEDENCE);
Object o8 = new Foo(Ordered.HIGHEST_PRECEDENCE);
Object o9 = new Foo(4);
Object o10 = "FooB";
tempList.add(o1);
tempList.add(o2);
tempList.add(o3);
tempList.add(o4);
tempList.add(o5);
tempList.add(o6);
tempList.add(o7);
tempList.add(o8);
tempList.add(o9);
tempList.add(o10);
assertEquals(10, tempList.size());
OrderedAwareLinkedHashSet orderAwareSet = new OrderedAwareLinkedHashSet();
orderAwareSet.addAll(tempList);
System.out.println(orderAwareSet);
Object[] elements = orderAwareSet.toArray();
assertEquals(o7, elements[0]);
assertEquals(o8, elements[1]);
assertEquals(o2, elements[2]);
assertEquals(o4, elements[3]);
assertEquals(o1, elements[4]);
assertEquals(o9, elements[5]);
assertEquals(o5, elements[6]);
assertEquals(o6, elements[7]);
assertEquals(o3, elements[8]);
assertEquals(o10, elements[9]);
}
private static class Foo implements Ordered {
private int order;
public Foo(int order){
this.order = order;
}
public int getOrder() {
return order;
}
public String toString(){
return "Foo-" + order;
}
}
}