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 }