Source code: org/activemq/filter/DestinationMap.java
1 /**
2 *
3 * Copyright 2004 Protique Ltd
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.activemq.filter;
19
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import org.activemq.message.ActiveMQDestination;
26 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
27
28 /**
29 * A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination}
30 * and retrieved by destination - supporting both * and > style of wildcard
31 * as well as composite destinations.
32 * <br>
33 * This class assumes that the index changes rarely but that fast lookup into the index is required.
34 * So this class maintains a pre-calculated index for destination steps. So looking up the values
35 * for "TEST.*" or "*.TEST" will be pretty fast.
36 * <br>
37 * Looking up of a value could return a single value or a List of matching values if a wildcard or
38 * composite destination is used.
39 *
40 * @version $Revision: 1.1.1.1 $
41 */
42 public class DestinationMap {
43 private DestinationMapNode rootNode = new DestinationMapNode();
44 private Map vanillaDestinations = new ConcurrentHashMap();
45 private boolean containsWildCards = false;
46 protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT;
47 protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD;
48 private Set nullAnswer = new HashSet();
49
50 /**
51 * Looks up the value(s) matching the given Destination key. For simple destinations
52 * this is typically a List of one single value, for wildcards or composite destinations this will typically be
53 * a List of matching values.
54 *
55 * @param key the destination to lookup
56 * @return a List of matching values or an empty list if there are no matching values.
57 */
58 public synchronized Set get(ActiveMQDestination key) {
59 if (key.isComposite()) {
60 List childDestinations = key.getChildDestinations();
61 Set answer = new HashSet(childDestinations.size());
62 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
63 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
64 Object value = get(childDestination);
65 if (value instanceof Set) {
66 answer.addAll((Set) value);
67 }
68 else if (value != null) {
69 answer.add(value);
70 }
71 return answer;
72 }
73 }
74 return findWildcardMatches(key);
75 }
76
77 /**
78 * add destination to the map
79 * @param key
80 * @param value
81 */
82 public synchronized void put(ActiveMQDestination key, Object value) {
83 if (key.isComposite()) {
84 List childDestinations = key.getChildDestinations();
85 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
86 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
87 put(childDestination, value);
88 }
89 return;
90 }
91 String[] paths = key.getDestinationPaths();
92 rootNode.add(paths, 0, value);
93 if (key.isWildcard()){
94 containsWildCards = true;
95 }
96 addToVanillaDestinations(key, value);
97 }
98
99 /**
100 * Removes the value from the associated destination
101 * @param key
102 * @param value
103 */
104 public synchronized void remove(ActiveMQDestination key, Object value) {
105 if (key.isComposite()) {
106 List childDestinations = key.getChildDestinations();
107 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
108 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
109 remove(childDestination, value);
110 }
111 return;
112 }
113 String[] paths = key.getDestinationPaths();
114 rootNode.remove(paths, 0, value);
115 removeFromVanillaDestinations(key,value);
116
117
118 }
119
120 // Implementation methods
121 //-------------------------------------------------------------------------
122 protected Set findWildcardMatches(ActiveMQDestination key) {
123 Set answer = nullAnswer;
124 if (!containsWildCards && !key.isWildcard()){
125 answer = getFromVanillaDestinations(key);
126 }else {
127 answer = new HashSet();
128 String[] paths = key.getDestinationPaths();
129 rootNode.appendMatchingValues(answer, paths, 0);
130 }
131 return answer;
132 }
133
134 /**
135 * remove all destinations associated with a key
136 * @param key
137 */
138 public void removeAll(ActiveMQDestination key) {
139 if (key.isComposite()) {
140 List childDestinations = key.getChildDestinations();
141 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
142 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
143 removeAll(childDestination);
144 }
145 return;
146 }
147 String[] paths = key.getDestinationPaths();
148 rootNode.removeAll(paths, 0);
149 removeAllFromVanillaDestinations(key);
150 }
151
152 synchronized private void addToVanillaDestinations(ActiveMQDestination key, Object value) {
153 Set set = new HashSet();
154 Set oldSet = (Set)vanillaDestinations.get(key);
155 if (oldSet != null) {
156 set.addAll(oldSet);
157 }
158 set.add(value);
159 vanillaDestinations.put(key,set);
160 }
161
162 synchronized private void removeFromVanillaDestinations(ActiveMQDestination key,Object value){
163
164 Set oldSet = (Set)vanillaDestinations.get(key);
165 if (oldSet != null) {
166 Set set = new HashSet(oldSet);
167 set.remove(value);
168 if (set.isEmpty()){
169 vanillaDestinations.remove(key);
170 }
171 vanillaDestinations.put(key,set);
172 }
173
174 validateContainsWildCards(key);
175 }
176
177 private void removeAllFromVanillaDestinations(ActiveMQDestination key){
178 vanillaDestinations.remove(key);
179 validateContainsWildCards(key);
180 }
181
182 private Set getFromVanillaDestinations(ActiveMQDestination key){
183 Set answer = (Set)vanillaDestinations.get(key);
184 return answer != null ? answer : nullAnswer;
185 }
186
187 private void validateContainsWildCards(ActiveMQDestination key){
188 if (containsWildCards && key.isWildcard()){
189 containsWildCards = false;
190 for (Iterator i = vanillaDestinations.keySet().iterator(); i.hasNext();){
191 ActiveMQDestination dest = (ActiveMQDestination)i.next();
192 if (dest.isWildcard()){
193 containsWildCards = true;
194 break;
195 }
196 }
197 }
198 }
199
200 }