View Javadoc

1   /*
2    * Copyright 2008 University Corporation for Advanced Internet Development, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * A thread-safe implementation of {@link StorageService} that publishes event when items are added or removed from the
30   * service.
31   * 
32   * An {@link AddEntryEvent} is published after an item has been added to the storage service. A {@link RemoveEntryEvent}
33   * is published after an item has been removed from the storage service. These events are published in the root
34   * application context, that is the highest ancestor, of the application context presented to this class.
35   * 
36   * @param <KeyType> object type of the keys
37   * @param <ValueType> object type of the values
38   */
39  public class EventingMapBasedStorageService<KeyType, ValueType> implements StorageService<KeyType, ValueType>,
40          ApplicationContextAware {
41  
42      /** Spring application context. */
43      private ApplicationContext appCtx;
44  
45      /** Backing map. */
46      private Map<String, Map<KeyType, ValueType>> store;
47  
48      /** Constructor. */
49      public EventingMapBasedStorageService() {
50          store = new ConcurrentHashMap<String, Map<KeyType, ValueType>>();
51      }
52  
53      /** {@inheritDoc} */
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      /** {@inheritDoc} */
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      /** {@inheritDoc} */
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      /** {@inheritDoc} */
93      public Iterator<String> getPartitions() {
94          return this.new PartitionIterator();
95      }
96  
97      /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** {@inheritDoc} */
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     /** An event indicating an item has been added to an storage service. */
142     public static class AddEntryEvent<KeyType, ValueType> extends ApplicationEvent {
143 
144         /** Serial version UID. */
145         private static final long serialVersionUID = -1939512157260059492L;
146 
147         /** Storage service to which the item was added. */
148         private StorageService<KeyType, ValueType> storageService;
149 
150         /** Storage partition to which the item was added. */
151         private String partition;
152 
153         /** Key to the added item. */
154         private KeyType key;
155 
156         /** The added item. */
157         private ValueType value;
158 
159         /**
160          * Constructor.
161          * 
162          * @param storageService storage service to which an item was added
163          * @param partition partition to which the entry was added
164          * @param key key of the added item
165          * @param value added item
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          * Gets the storage service to which an item was added.
178          * 
179          * @return storage service to which an item was added
180          */
181         public StorageService<KeyType, ValueType> getStorageService() {
182             return storageService;
183         }
184 
185         /**
186          * Gets the partition to which the entry was added.
187          * 
188          * @return partition to which the entry was added
189          */
190         public String getPartition() {
191             return partition;
192         }
193 
194         /**
195          * Gets the key of the added item.
196          * 
197          * @return key of the added item
198          */
199         public KeyType getKey() {
200             return key;
201         }
202 
203         /**
204          * Gets the added item.
205          * 
206          * @return added item
207          */
208         public ValueType getValue() {
209             return value;
210         }
211     }
212 
213     /** An event indicating an item has been removed from an storage service. */
214     public static class RemoveEntryEvent<KeyType, ValueType> extends ApplicationEvent {
215 
216         /** Serial version UID. */
217         private static final long serialVersionUID = 7414605158323325366L;
218 
219         /** Storage service to which the item was removed. */
220         private StorageService<KeyType, ValueType> storageService;
221 
222         /** Storage partition to which the item was removed. */
223         private String partition;
224 
225         /** Key to the removed item. */
226         private KeyType key;
227 
228         /** The removed item. */
229         private ValueType value;
230 
231         /**
232          * Constructor.
233          * 
234          * @param storageService storage service to which an item was removed
235          * @param partition partition to which the entry was removed
236          * @param key key of the removed item
237          * @param value removed item
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          * Gets the storage service to which an item was removed.
250          * 
251          * @return storage service to which an item was removed
252          */
253         public StorageService<KeyType, ValueType> getStorageService() {
254             return storageService;
255         }
256 
257         /**
258          * Gets the partition to which the entry was removed.
259          * 
260          * @return partition to which the entry was removed
261          */
262         public String getPartition() {
263             return partition;
264         }
265 
266         /**
267          * Gets the key of the removed item.
268          * 
269          * @return key of the removed item
270          */
271         public KeyType getKey() {
272             return key;
273         }
274 
275         /**
276          * Gets the removed item.
277          * 
278          * @return removed item
279          */
280         public ValueType getValue() {
281             return value;
282         }
283     }
284 
285     /** An iterator over the partitions of the storage service. */
286     public class PartitionIterator implements Iterator<String> {
287 
288         /** Iterator over the partitions in the backing store. */
289         private Iterator<String> partitionItr;
290 
291         /** Current partition. */
292         private String currentParition;
293 
294         /** Constructor. */
295         public PartitionIterator() {
296             partitionItr = store.keySet().iterator();
297         }
298 
299         /** {@inheritDoc} */
300         public boolean hasNext() {
301             return partitionItr.hasNext();
302         }
303 
304         /** {@inheritDoc} */
305         public String next() {
306             currentParition = partitionItr.next();
307             return currentParition;
308         }
309 
310         /** {@inheritDoc} */
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     /** An iterator over the entries of a partition of the storage service. */
322     public class PartitionEntryIterator implements Iterator<KeyType> {
323 
324         /** Partition on which we are operating. */
325         private String partition;
326 
327         /** Iterator of keys within the partition. */
328         private Iterator<KeyType> keysItr;
329 
330         /** Current key within the iteration. */
331         private KeyType currentKey;
332 
333         /**
334          * Constructor.
335          * 
336          * @param partition partition upon which this iterator operates
337          */
338         public PartitionEntryIterator(String partition) {
339             this.partition = partition;
340             keysItr = store.get(partition).keySet().iterator();
341         }
342 
343         /** {@inheritDoc} */
344         public boolean hasNext() {
345             return keysItr.hasNext();
346         }
347 
348         /** {@inheritDoc} */
349         public KeyType next() {
350             currentKey = keysItr.next();
351             return currentKey;
352         }
353 
354         /** {@inheritDoc} */
355         public void remove() {
356             EventingMapBasedStorageService.this.remove(partition, currentKey);
357         }
358     }
359 }