From e6062ba74916dec399212a5e5437df3dbb4e73b9 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Sun, 28 Jun 2009 22:26:10 +0000 Subject: [PATCH] INT-684, Added new OrderAwareLinkedHashSet and modified AbstractDispatcher --- .../dispatcher/AbstractDispatcher.java | 30 +--- .../dispatcher/OrderedAwareLinkedHashSet.java | 117 ++++++++++++++ .../OrderedAwareLinkedHashSetTests.java | 145 ++++++++++++++++++ 3 files changed, 267 insertions(+), 25 deletions(-) create mode 100644 org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSetTests.java diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/AbstractDispatcher.java b/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/AbstractDispatcher.java index 870d0d650f..3baf50fcf2 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/AbstractDispatcher.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/AbstractDispatcher.java @@ -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 handlers = new LinkedList(); + private volatile Set handlers = new OrderedAwareLinkedHashSet(); @SuppressWarnings("unchecked") private volatile Comparator comparator = new OrderComparator(); @@ -85,32 +85,12 @@ public abstract class AbstractDispatcher implements MessageDispatcher { } protected List 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 orderedHandlers = new ArrayList(); - orderedHandlers.add(handler); - List handlerList = new ArrayList(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); } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java b/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java new file mode 100644 index 0000000000..0c359b282e --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSet.java @@ -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 extends LinkedHashSet { + 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 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; + } + } +} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSetTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSetTests.java new file mode 100644 index 0000000000..5b34fbbd4e --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/dispatcher/OrderedAwareLinkedHashSetTests.java @@ -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; + } + } +}