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          return this.new PartitionEntryIterator(partition);
82      }
83  
84      /** {@inheritDoc} */
85      public Iterator<String> getPartitions() {
86          return this.new PartitionIterator();
87      }
88  
89      /** {@inheritDoc} */
90      public ValueType put(String partition, KeyType key, ValueType value) {
91          if (partition == null || key == null) {
92              return null;
93          }
94  
95          Map<KeyType, ValueType> partitionMap;
96          synchronized (store) {
97              partitionMap = store.get(partition);
98              if (partitionMap == null) {
99                  partitionMap = new ConcurrentHashMap<KeyType, ValueType>();
100                 store.put(partition, partitionMap);
101             }
102         }
103 
104         ValueType replacedEntry = partitionMap.put(key, value);
105         appCtx.publishEvent(this.new AddEntryEvent(this, partition, key, value));
106         return replacedEntry;
107     }
108 
109     /** {@inheritDoc} */
110     public ValueType remove(String partition, KeyType key) {
111         if (partition == null || key == null) {
112             return null;
113         }
114 
115         if (store.containsKey(partition)) {
116             ValueType removedEntry = store.get(partition).remove(key);
117             appCtx.publishEvent(this.new RemoveEntryEvent(this, partition, key, removedEntry));
118             return removedEntry;
119         }
120 
121         return null;
122     }
123 
124     /** {@inheritDoc} */
125     public void setApplicationContext(ApplicationContext ctx) {
126         ApplicationContext rootContext = ctx;
127         while (rootContext.getParent() != null) {
128             rootContext = rootContext.getParent();
129         }
130         appCtx = rootContext;
131     }
132 
133     /** An event indicating an item has been added to an storage service. */
134     public class AddEntryEvent extends ApplicationEvent {
135 
136         /** Serial version UID. */
137         private static final long serialVersionUID = -1939512157260059492L;
138 
139         /** Storage service to which the item was added. */
140         private StorageService<KeyType, ValueType> storageService;
141 
142         /** Storage partition to which the item was added. */
143         private String partition;
144 
145         /** Key to the added item. */
146         private KeyType key;
147 
148         /** The added item. */
149         private ValueType value;
150 
151         /**
152          * Constructor.
153          * 
154          * @param storageService storage service to which an item was added
155          * @param partition partition to which the entry was added
156          * @param key key of the added item
157          * @param value added item
158          */
159         public AddEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
160                 ValueType value) {
161             super(storageService);
162             this.storageService = storageService;
163             this.partition = partition;
164             this.key = key;
165             this.value = value;
166         }
167 
168         /**
169          * Gets the storage service to which an item was added.
170          * 
171          * @return storage service to which an item was added
172          */
173         public StorageService<KeyType, ValueType> getStorageService() {
174             return storageService;
175         }
176 
177         /**
178          * Gets the partition to which the entry was added.
179          * 
180          * @return partition to which the entry was added
181          */
182         public String getPartition() {
183             return partition;
184         }
185 
186         /**
187          * Gets the key of the added item.
188          * 
189          * @return key of the added item
190          */
191         public KeyType getKey() {
192             return key;
193         }
194 
195         /**
196          * Gets the added item.
197          * 
198          * @return added item
199          */
200         public ValueType getValue() {
201             return value;
202         }
203     }
204 
205     /** An event indicating an item has been removed from an storage service. */
206     public class RemoveEntryEvent extends ApplicationEvent {
207 
208         /** Serial version UID. */
209         private static final long serialVersionUID = 7414605158323325366L;
210 
211         /** Storage service to which the item was removed. */
212         private StorageService<KeyType, ValueType> storageService;
213 
214         /** Storage partition to which the item was removed. */
215         private String partition;
216 
217         /** Key to the removed item. */
218         private KeyType key;
219 
220         /** The removed item. */
221         private ValueType value;
222 
223         /**
224          * Constructor.
225          * 
226          * @param storageService storage service to which an item was removed
227          * @param partition partition to which the entry was removed
228          * @param key key of the removed item
229          * @param value removed item
230          */
231         public RemoveEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
232                 ValueType value) {
233             super(storageService);
234             this.storageService = storageService;
235             this.partition = partition;
236             this.key = key;
237             this.value = value;
238         }
239 
240         /**
241          * Gets the storage service to which an item was removed.
242          * 
243          * @return storage service to which an item was removed
244          */
245         public StorageService<KeyType, ValueType> getStorageService() {
246             return storageService;
247         }
248 
249         /**
250          * Gets the partition to which the entry was removed.
251          * 
252          * @return partition to which the entry was removed
253          */
254         public String getPartition() {
255             return partition;
256         }
257 
258         /**
259          * Gets the key of the removed item.
260          * 
261          * @return key of the removed item
262          */
263         public KeyType getKey() {
264             return key;
265         }
266 
267         /**
268          * Gets the removed item.
269          * 
270          * @return removed item
271          */
272         public ValueType getValue() {
273             return value;
274         }
275     }
276 
277     /** An iterator over the partitions of the storage service. */
278     public class PartitionIterator implements Iterator<String> {
279 
280         /** Iterator over the partitions in the backing store. */
281         private Iterator<String> partitionItr;
282 
283         /** Current partition. */
284         private String currentParition;
285 
286         /** Constructor. */
287         public PartitionIterator() {
288             partitionItr = store.keySet().iterator();
289         }
290 
291         /** {@inheritDoc} */
292         public boolean hasNext() {
293             return partitionItr.hasNext();
294         }
295 
296         /** {@inheritDoc} */
297         public String next() {
298             currentParition = partitionItr.next();
299             return currentParition;
300         }
301 
302         /** {@inheritDoc} */
303         public void remove() {
304             Iterator<KeyType> partitionEntries = getKeys(currentParition);
305             while (partitionEntries.hasNext()) {
306                 partitionEntries.next();
307                 partitionEntries.remove();
308             }
309             store.remove(currentParition);
310         }
311     }
312 
313     /** An iterator over the entries of a partition of the storage service. */
314     public class PartitionEntryIterator implements Iterator<KeyType> {
315 
316         /** Partition on which we are operating. */
317         private String partition;
318 
319         /** Iterator of keys within the partition. */
320         private Iterator<KeyType> keysItr;
321 
322         /** Current key within the iteration. */
323         private KeyType currentKey;
324 
325         /**
326          * Constructor.
327          * 
328          * @param partition partition upon which this iterator operates
329          */
330         public PartitionEntryIterator(String partition) {
331             this.partition = partition;
332             keysItr = store.get(partition).keySet().iterator();
333         }
334 
335         /** {@inheritDoc} */
336         public boolean hasNext() {
337             return keysItr.hasNext();
338         }
339 
340         /** {@inheritDoc} */
341         public KeyType next() {
342             currentKey = keysItr.next();
343             return currentKey;
344         }
345 
346         /** {@inheritDoc} */
347         public void remove() {
348             EventingMapBasedStorageService.this.remove(partition, currentKey);
349         }
350     }
351 }