001 /**
002 *
003 * Copyright 2004 Protique Ltd
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 **/
018 package org.activemq.filter;
019
020 import java.util.HashSet;
021 import java.util.Iterator;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.Set;
025 import org.activemq.message.ActiveMQDestination;
026 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
027
028 /**
029 * A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination}
030 * and retrieved by destination - supporting both * and > style of wildcard
031 * as well as composite destinations.
032 * <br>
033 * This class assumes that the index changes rarely but that fast lookup into the index is required.
034 * So this class maintains a pre-calculated index for destination steps. So looking up the values
035 * for "TEST.*" or "*.TEST" will be pretty fast.
036 * <br>
037 * Looking up of a value could return a single value or a List of matching values if a wildcard or
038 * composite destination is used.
039 *
040 * @version $Revision: 1.1.1.1 $
041 */
042 public class DestinationMap {
043 private DestinationMapNode rootNode = new DestinationMapNode();
044 private Map vanillaDestinations = new ConcurrentHashMap();
045 private boolean containsWildCards = false;
046 protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT;
047 protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD;
048 private Set nullAnswer = new HashSet();
049
050 /**
051 * Looks up the value(s) matching the given Destination key. For simple destinations
052 * this is typically a List of one single value, for wildcards or composite destinations this will typically be
053 * a List of matching values.
054 *
055 * @param key the destination to lookup
056 * @return a List of matching values or an empty list if there are no matching values.
057 */
058 public synchronized Set get(ActiveMQDestination key) {
059 if (key.isComposite()) {
060 List childDestinations = key.getChildDestinations();
061 Set answer = new HashSet(childDestinations.size());
062 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
063 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
064 Object value = get(childDestination);
065 if (value instanceof Set) {
066 answer.addAll((Set) value);
067 }
068 else if (value != null) {
069 answer.add(value);
070 }
071 return answer;
072 }
073 }
074 return findWildcardMatches(key);
075 }
076
077 /**
078 * add destination to the map
079 * @param key
080 * @param value
081 */
082 public synchronized void put(ActiveMQDestination key, Object value) {
083 if (key.isComposite()) {
084 List childDestinations = key.getChildDestinations();
085 for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
086 ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
087 put(childDestination, value);
088 }
089 return;
090 }
091 String[] paths = key.getDestinationPaths();
092 rootNode.add(paths, 0, value);
093 if (key.isWildcard()){
094 containsWildCards = true;
095 }
096 addToVanillaDestinations(key, value);
097 }
098
099 /**
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 }