1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.util;
18
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.concurrent.ConcurrentHashMap;
22
23 import org.opensaml.util.storage.StorageService;
24 import org.springframework.context.ApplicationContext;
25 import org.springframework.context.ApplicationContextAware;
26 import org.springframework.context.ApplicationEvent;
27
28
29
30
31
32
33
34
35
36
37
38
39 public class EventingMapBasedStorageService<KeyType, ValueType> implements StorageService<KeyType, ValueType>,
40 ApplicationContextAware {
41
42
43 private ApplicationContext appCtx;
44
45
46 private Map<String, Map<KeyType, ValueType>> store;
47
48
49 public EventingMapBasedStorageService() {
50 store = new ConcurrentHashMap<String, Map<KeyType, ValueType>>();
51 }
52
53
54 public boolean contains(String partition, Object key) {
55 if (partition == null || key == null) {
56 return false;
57 }
58
59 if (store.containsKey(partition)) {
60 return store.get(partition).containsKey(key);
61 }
62
63 return false;
64 }
65
66
67 public ValueType get(String partition, Object key) {
68 if (partition == null || key == null) {
69 return null;
70 }
71
72 if (store.containsKey(partition)) {
73 return store.get(partition).get(key);
74 }
75
76 return null;
77 }
78
79
80 public Iterator<KeyType> getKeys(String partition) {
81 if(partition == null){
82 return null;
83 }
84
85 if(store.containsKey(partition)){
86 return this.new PartitionEntryIterator(partition);
87 }
88
89 return null;
90 }
91
92
93 public Iterator<String> getPartitions() {
94 return this.new PartitionIterator();
95 }
96
97
98 public ValueType put(String partition, KeyType key, ValueType value) {
99 if (partition == null || key == null) {
100 return null;
101 }
102
103 Map<KeyType, ValueType> partitionMap;
104 synchronized (store) {
105 partitionMap = store.get(partition);
106 if (partitionMap == null) {
107 partitionMap = new ConcurrentHashMap<KeyType, ValueType>();
108 store.put(partition, partitionMap);
109 }
110 }
111
112 ValueType replacedEntry = partitionMap.put(key, value);
113 appCtx.publishEvent(new AddEntryEvent(this, partition, key, value));
114 return replacedEntry;
115 }
116
117
118 public ValueType remove(String partition, KeyType key) {
119 if (partition == null || key == null) {
120 return null;
121 }
122
123 if (store.containsKey(partition)) {
124 ValueType removedEntry = store.get(partition).remove(key);
125 appCtx.publishEvent(new RemoveEntryEvent(this, partition, key, removedEntry));
126 return removedEntry;
127 }
128
129 return null;
130 }
131
132
133 public void setApplicationContext(ApplicationContext ctx) {
134 ApplicationContext rootContext = ctx;
135 while (rootContext.getParent() != null) {
136 rootContext = rootContext.getParent();
137 }
138 appCtx = rootContext;
139 }
140
141
142 public static class AddEntryEvent<KeyType, ValueType> extends ApplicationEvent {
143
144
145 private static final long serialVersionUID = -1939512157260059492L;
146
147
148 private StorageService<KeyType, ValueType> storageService;
149
150
151 private String partition;
152
153
154 private KeyType key;
155
156
157 private ValueType value;
158
159
160
161
162
163
164
165
166
167 public AddEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
168 ValueType value) {
169 super(storageService);
170 this.storageService = storageService;
171 this.partition = partition;
172 this.key = key;
173 this.value = value;
174 }
175
176
177
178
179
180
181 public StorageService<KeyType, ValueType> getStorageService() {
182 return storageService;
183 }
184
185
186
187
188
189
190 public String getPartition() {
191 return partition;
192 }
193
194
195
196
197
198
199 public KeyType getKey() {
200 return key;
201 }
202
203
204
205
206
207
208 public ValueType getValue() {
209 return value;
210 }
211 }
212
213
214 public static class RemoveEntryEvent<KeyType, ValueType> extends ApplicationEvent {
215
216
217 private static final long serialVersionUID = 7414605158323325366L;
218
219
220 private StorageService<KeyType, ValueType> storageService;
221
222
223 private String partition;
224
225
226 private KeyType key;
227
228
229 private ValueType value;
230
231
232
233
234
235
236
237
238
239 public RemoveEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
240 ValueType value) {
241 super(storageService);
242 this.storageService = storageService;
243 this.partition = partition;
244 this.key = key;
245 this.value = value;
246 }
247
248
249
250
251
252
253 public StorageService<KeyType, ValueType> getStorageService() {
254 return storageService;
255 }
256
257
258
259
260
261
262 public String getPartition() {
263 return partition;
264 }
265
266
267
268
269
270
271 public KeyType getKey() {
272 return key;
273 }
274
275
276
277
278
279
280 public ValueType getValue() {
281 return value;
282 }
283 }
284
285
286 public class PartitionIterator implements Iterator<String> {
287
288
289 private Iterator<String> partitionItr;
290
291
292 private String currentParition;
293
294
295 public PartitionIterator() {
296 partitionItr = store.keySet().iterator();
297 }
298
299
300 public boolean hasNext() {
301 return partitionItr.hasNext();
302 }
303
304
305 public String next() {
306 currentParition = partitionItr.next();
307 return currentParition;
308 }
309
310
311 public void remove() {
312 Iterator<KeyType> partitionEntries = getKeys(currentParition);
313 while (partitionEntries.hasNext()) {
314 partitionEntries.next();
315 partitionEntries.remove();
316 }
317 store.remove(currentParition);
318 }
319 }
320
321
322 public class PartitionEntryIterator implements Iterator<KeyType> {
323
324
325 private String partition;
326
327
328 private Iterator<KeyType> keysItr;
329
330
331 private KeyType currentKey;
332
333
334
335
336
337
338 public PartitionEntryIterator(String partition) {
339 this.partition = partition;
340 keysItr = store.get(partition).keySet().iterator();
341 }
342
343
344 public boolean hasNext() {
345 return keysItr.hasNext();
346 }
347
348
349 public KeyType next() {
350 currentKey = keysItr.next();
351 return currentKey;
352 }
353
354
355 public void remove() {
356 EventingMapBasedStorageService.this.remove(partition, currentKey);
357 }
358 }
359 }