mirror of https://github.com/apache/activemq.git
fixed the DestinationMap so that it can hold queues and topics with the same name without intermixing the two trees
git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@365888 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0a8efc977b
commit
8b28367f3d
|
@ -26,31 +26,35 @@ import java.util.TreeSet;
|
|||
import org.apache.activemq.command.ActiveMQDestination;
|
||||
|
||||
/**
|
||||
* A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination}
|
||||
* and retrieved by destination - supporting both * and > style of wildcard
|
||||
* as well as composite destinations.
|
||||
* <br>
|
||||
* This class assumes that the index changes rarely but that fast lookup into the index is required.
|
||||
* So this class maintains a pre-calculated index for destination steps. So looking up the values
|
||||
* for "TEST.*" or "*.TEST" will be pretty fast.
|
||||
* <br>
|
||||
* Looking up of a value could return a single value or a List of matching values if a wildcard or
|
||||
* composite destination is used.
|
||||
*
|
||||
* A Map-like data structure allowing values to be indexed by
|
||||
* {@link ActiveMQDestination} and retrieved by destination - supporting both *
|
||||
* and > style of wildcard as well as composite destinations. <br>
|
||||
* This class assumes that the index changes rarely but that fast lookup into
|
||||
* the index is required. So this class maintains a pre-calculated index for
|
||||
* destination steps. So looking up the values for "TEST.*" or "*.TEST" will be
|
||||
* pretty fast. <br>
|
||||
* Looking up of a value could return a single value or a List of matching
|
||||
* values if a wildcard or composite destination is used.
|
||||
*
|
||||
* @version $Revision: 1.3 $
|
||||
*/
|
||||
public class DestinationMap {
|
||||
private DestinationMapNode rootNode = new DestinationMapNode(null);
|
||||
protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT;
|
||||
protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD;
|
||||
|
||||
private DestinationMapNode queueRootNode = new DestinationMapNode(null);
|
||||
private DestinationMapNode topicRootNode = new DestinationMapNode(null);
|
||||
|
||||
/**
|
||||
* Looks up the value(s) matching the given Destination key. For simple destinations
|
||||
* this is typically a List of one single value, for wildcards or composite destinations this will typically be
|
||||
* a List of matching values.
|
||||
*
|
||||
* @param key the destination to lookup
|
||||
* @return a List of matching values or an empty list if there are no matching values.
|
||||
* Looks up the value(s) matching the given Destination key. For simple
|
||||
* destinations this is typically a List of one single value, for wildcards
|
||||
* or composite destinations this will typically be a List of matching
|
||||
* values.
|
||||
*
|
||||
* @param key
|
||||
* the destination to lookup
|
||||
* @return a List of matching values or an empty list if there are no
|
||||
* matching values.
|
||||
*/
|
||||
public synchronized Set get(ActiveMQDestination key) {
|
||||
if (key.isComposite()) {
|
||||
|
@ -81,7 +85,7 @@ public class DestinationMap {
|
|||
return;
|
||||
}
|
||||
String[] paths = key.getDestinationPaths();
|
||||
rootNode.add(paths, 0, value);
|
||||
getRootNode(key).add(paths, 0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,20 +101,24 @@ public class DestinationMap {
|
|||
return;
|
||||
}
|
||||
String[] paths = key.getDestinationPaths();
|
||||
rootNode.remove(paths, 0, value);
|
||||
getRootNode(key).remove(paths, 0, value);
|
||||
|
||||
}
|
||||
|
||||
public int getRootChildCount() {
|
||||
return rootNode.getChildCount();
|
||||
public int getTopicRootChildCount() {
|
||||
return topicRootNode.getChildCount();
|
||||
}
|
||||
|
||||
public int getQueueRootChildCount() {
|
||||
return queueRootNode.getChildCount();
|
||||
}
|
||||
|
||||
// Implementation methods
|
||||
//-------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A helper method to allow the destination map to be populated from a dependency injection
|
||||
* framework such as Spring
|
||||
* A helper method to allow the destination map to be populated from a
|
||||
* dependency injection framework such as Spring
|
||||
*/
|
||||
protected void setEntries(List entries) {
|
||||
for (Iterator iter = entries.iterator(); iter.hasNext();) {
|
||||
|
@ -125,11 +133,12 @@ public class DestinationMap {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of the allowed entries which can be set via the {@link #setEntries(List)} method.
|
||||
* This allows derived classes to further restrict the type of allowed entries to make a type safe
|
||||
* destination map for custom policies.
|
||||
* Returns the type of the allowed entries which can be set via the
|
||||
* {@link #setEntries(List)} method. This allows derived classes to further
|
||||
* restrict the type of allowed entries to make a type safe destination map
|
||||
* for custom policies.
|
||||
*/
|
||||
protected Class getEntryClass() {
|
||||
return DestinationMapEntry.class;
|
||||
|
@ -138,7 +147,7 @@ public class DestinationMap {
|
|||
protected Set findWildcardMatches(ActiveMQDestination key) {
|
||||
String[] paths = key.getDestinationPaths();
|
||||
Set answer = new HashSet();
|
||||
rootNode.appendMatchingValues(answer, paths, 0);
|
||||
getRootNode(key).appendMatchingValues(answer, paths, 0);
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
@ -154,15 +163,16 @@ public class DestinationMap {
|
|||
return;
|
||||
}
|
||||
String[] paths = key.getDestinationPaths();
|
||||
rootNode.removeAll(paths, 0);
|
||||
getRootNode(key).removeAll(paths, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value which matches the given destination or null if there is no matching
|
||||
* value. If there are multiple values, the results are sorted and the last item (the biggest)
|
||||
* is returned.
|
||||
* Returns the value which matches the given destination or null if there is
|
||||
* no matching value. If there are multiple values, the results are sorted
|
||||
* and the last item (the biggest) is returned.
|
||||
*
|
||||
* @param destination the destination to find the value for
|
||||
* @param destination
|
||||
* the destination to find the value for
|
||||
* @return the largest matching value or null if no value matches
|
||||
*/
|
||||
public Object chooseValue(ActiveMQDestination destination) {
|
||||
|
@ -174,4 +184,15 @@ public class DestinationMap {
|
|||
return sortedSet.last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root node for the given destination type
|
||||
*/
|
||||
protected DestinationMapNode getRootNode(ActiveMQDestination key) {
|
||||
if (key.isQueue()) {
|
||||
return queueRootNode;
|
||||
}
|
||||
else {
|
||||
return topicRootNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.activemq.filter;
|
|||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.activemq.command.ActiveMQDestination;
|
||||
import org.apache.activemq.command.ActiveMQQueue;
|
||||
import org.apache.activemq.command.ActiveMQTopic;
|
||||
import org.apache.activemq.filter.DestinationMap;
|
||||
|
||||
|
@ -45,16 +46,15 @@ public class DestinationMapTest extends TestCase {
|
|||
protected Object v5 = "value5";
|
||||
protected Object v6 = "value6";
|
||||
|
||||
|
||||
public void testCompositeDestinations() throws Exception {
|
||||
|
||||
ActiveMQDestination d1 = createDestination("TEST.BAR.D2");
|
||||
ActiveMQDestination d2 = createDestination("TEST.BAR.D3");
|
||||
map.put(d1,d1);
|
||||
map.put(d2,d2);
|
||||
map.put(d1, d1);
|
||||
map.put(d2, d2);
|
||||
map.get(createDestination("TEST.BAR.D2,TEST.BAR.D3"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void testSimpleDestinations() throws Exception {
|
||||
map.put(d1, v1);
|
||||
map.put(d2, v2);
|
||||
|
@ -65,6 +65,17 @@ public class DestinationMapTest extends TestCase {
|
|||
assertMapValue(d3, v3);
|
||||
}
|
||||
|
||||
public void testQueueAndTopicWithSameName() throws Exception {
|
||||
ActiveMQQueue q1 = new ActiveMQQueue("foo");
|
||||
ActiveMQTopic t1 = new ActiveMQTopic("foo");
|
||||
|
||||
map.put(q1, v1);
|
||||
map.put(t1, v2);
|
||||
|
||||
assertMapValue(q1, v1);
|
||||
assertMapValue(t1, v2);
|
||||
}
|
||||
|
||||
public void testSimpleDestinationsWithMultipleValues() throws Exception {
|
||||
map.put(d1, v1);
|
||||
map.put(d2, v2);
|
||||
|
@ -122,7 +133,7 @@ public class DestinationMapTest extends TestCase {
|
|||
map.put(d2, v2);
|
||||
map.put(d3, v3);
|
||||
|
||||
List allValues = Arrays.asList(new Object[]{v1, v2, v3});
|
||||
List allValues = Arrays.asList(new Object[] { v1, v2, v3 });
|
||||
|
||||
assertMapValue(">", allValues);
|
||||
assertMapValue("TEST.>", allValues);
|
||||
|
@ -131,7 +142,6 @@ public class DestinationMapTest extends TestCase {
|
|||
assertMapValue("FOO.>", null);
|
||||
}
|
||||
|
||||
|
||||
public void testStoreWildcardWithOneStepPath() throws Exception {
|
||||
put("TEST.*", v1);
|
||||
put("TEST.D1", v2);
|
||||
|
@ -165,48 +175,46 @@ public class DestinationMapTest extends TestCase {
|
|||
put("TEST.XYZ.D4", v4);
|
||||
put("TEST.BAR.D3", v5);
|
||||
put("TEST.*.D2", v6);
|
||||
|
||||
|
||||
|
||||
assertMapValue("TEST.*.D3", v2, v3, v5);
|
||||
assertMapValue("TEST.*.D4", v2, v4);
|
||||
|
||||
|
||||
assertMapValue("TEST.*", v1, v2);
|
||||
assertMapValue("TEST.*.*", v2, v3, v4, v5, v6);
|
||||
assertMapValue("*.*.D3", v2, v3, v5);
|
||||
assertMapValue("TEST.BAR.*", v2, v5, v6);
|
||||
|
||||
|
||||
assertMapValue("TEST.BAR.D2", v2, v6);
|
||||
assertMapValue("TEST.*.D2", v2, v6);
|
||||
assertMapValue("TEST.BAR.*", v2, v5, v6);
|
||||
}
|
||||
|
||||
|
||||
public void testAnyPathWildcardInMap() throws Exception {
|
||||
put("TEST.FOO.>", v1);
|
||||
|
||||
|
||||
assertMapValue("TEST.FOO.BAR.WHANOT.A.B.C", v1);
|
||||
assertMapValue("TEST.FOO.BAR.WHANOT", v1);
|
||||
assertMapValue("TEST.FOO.BAR", v1);
|
||||
|
||||
|
||||
assertMapValue("TEST.*.*", v1);
|
||||
assertMapValue("TEST.BAR", null);
|
||||
|
||||
|
||||
assertMapValue("TEST.FOO", v1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSimpleAddRemove() throws Exception {
|
||||
put("TEST.D1", v2);
|
||||
|
||||
assertEquals("Root child count", 1, map.getRootChildCount());
|
||||
|
||||
|
||||
assertEquals("Root child count", 1, map.getTopicRootChildCount());
|
||||
|
||||
assertMapValue("TEST.D1", v2);
|
||||
|
||||
|
||||
remove("TEST.D1", v2);
|
||||
|
||||
assertEquals("Root child count", 0, map.getRootChildCount());
|
||||
|
||||
assertEquals("Root child count", 0, map.getTopicRootChildCount());
|
||||
assertMapValue("TEST.D1", null);
|
||||
}
|
||||
|
||||
|
||||
public void testStoreAndLookupAllWildcards() throws Exception {
|
||||
loadSample2();
|
||||
|
||||
|
@ -301,7 +309,6 @@ public class DestinationMapTest extends TestCase {
|
|||
assertMapValue("TEST.BAR.*", v3, v4);
|
||||
}
|
||||
|
||||
|
||||
protected void put(String name, Object value) {
|
||||
map.put(createDestination(name), value);
|
||||
}
|
||||
|
@ -311,26 +318,25 @@ public class DestinationMapTest extends TestCase {
|
|||
map.remove(destination, value);
|
||||
}
|
||||
|
||||
|
||||
protected void assertMapValue(String destinationName, Object expected) {
|
||||
ActiveMQDestination destination = createDestination(destinationName);
|
||||
assertMapValue(destination, expected);
|
||||
}
|
||||
|
||||
protected void assertMapValue(String destinationName, Object expected1, Object expected2) {
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2}));
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[] { expected1, expected2 }));
|
||||
}
|
||||
|
||||
protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3) {
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3}));
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[] { expected1, expected2, expected3 }));
|
||||
}
|
||||
|
||||
protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3, Object expected4) {
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3, expected4}));
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[] { expected1, expected2, expected3, expected4 }));
|
||||
}
|
||||
|
||||
protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3, Object expected4, Object expected5) {
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3, expected4, expected5}));
|
||||
assertMapValue(destinationName, Arrays.asList(new Object[] { expected1, expected2, expected3, expected4, expected5 }));
|
||||
}
|
||||
|
||||
protected void assertMapValue(ActiveMQDestination destination, Object expected) {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<strictOrderDispatchPolicy />
|
||||
</dispatchPolicy>
|
||||
<deadLetterStrategy>
|
||||
<individualDeadLetterStrategy queuePrefix="Test.DLQ." topicPrefix="Test.DLQ." />
|
||||
<individualDeadLetterStrategy topicPrefix="Test.DLQ." />
|
||||
</deadLetterStrategy>
|
||||
</policyEntry>
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
<strictOrderDispatchPolicy />
|
||||
</dispatchPolicy>
|
||||
<deadLetterStrategy>
|
||||
<individualDeadLetterStrategy queuePrefix="Test.DLQ." topicPrefix="Test.DLQ." />
|
||||
<individualDeadLetterStrategy queuePrefix="Test.DLQ."/>
|
||||
</deadLetterStrategy>
|
||||
</policyEntry>
|
||||
|
||||
|
|
Loading…
Reference in New Issue