View Javadoc

1   /*
2    * Licensed to the University Corporation for Advanced Internet Development, 
3    * Inc. (UCAID) under one or more contributor license agreements.  See the 
4    * NOTICE file distributed with this work for additional information regarding
5    * copyright ownership. The UCAID licenses this file to You under the Apache 
6    * License, Version 2.0 (the "License"); you may not use this file except in 
7    * compliance with the License.  You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package edu.internet2.middleware.shibboleth.common.util;
19  
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.concurrent.ConcurrentHashMap;
23  
24  import org.opensaml.util.storage.StorageService;
25  import org.springframework.context.ApplicationContext;
26  import org.springframework.context.ApplicationContextAware;
27  import org.springframework.context.ApplicationEvent;
28  
29  /**
30   * A thread-safe implementation of {@link StorageService} that publishes event when items are added or removed from the
31   * service.
32   * 
33   * An {@link AddEntryEvent} is published after an item has been added to the storage service. A {@link RemoveEntryEvent}
34   * is published after an item has been removed from the storage service. These events are published in the root
35   * application context, that is the highest ancestor, of the application context presented to this class.
36   * 
37   * @param <KeyType> object type of the keys
38   * @param <ValueType> object type of the values
39   */
40  public class EventingMapBasedStorageService<KeyType, ValueType> implements StorageService<KeyType, ValueType>,
41          ApplicationContextAware {
42  
43      /** Spring application context. */
44      private ApplicationContext appCtx;
45  
46      /** Backing map. */
47      private Map<String, Map<KeyType, ValueType>> store;
48  
49      /** Constructor. */
50      public EventingMapBasedStorageService() {
51          store = new ConcurrentHashMap<String, Map<KeyType, ValueType>>();
52      }
53  
54      /** {@inheritDoc} */
55      public boolean contains(String partition, Object key) {
56          if (partition == null || key == null) {
57              return false;
58          }
59  
60          if (store.containsKey(partition)) {
61              return store.get(partition).containsKey(key);
62          }
63  
64          return false;
65      }
66  
67      /** {@inheritDoc} */
68      public ValueType get(String partition, Object key) {
69          if (partition == null || key == null) {
70              return null;
71          }
72  
73          if (store.containsKey(partition)) {
74              return store.get(partition).get(key);
75          }
76  
77          return null;
78      }
79  
80      /** {@inheritDoc} */
81      public Iterator<KeyType> getKeys(String partition) {
82          if(partition == null){
83              return null;
84          }
85          
86          if(store.containsKey(partition)){
87              return this.new PartitionEntryIterator(partition);
88          }
89          
90          return null;
91      }
92  
93      /** {@inheritDoc} */
94      public Iterator<String> getPartitions() {
95          return this.new PartitionIterator();
96      }
97  
98      /** {@inheritDoc} */
99      public ValueType put(String partition, KeyType key, ValueType value) {
100         if (partition == null || key == null) {
101             return null;
102         }
103 
104         Map<KeyType, ValueType> partitionMap;
105         synchronized (store) {
106             partitionMap = store.get(partition);
107             if (partitionMap == null) {
108                 partitionMap = new ConcurrentHashMap<KeyType, ValueType>();
109                 store.put(partition, partitionMap);
110             }
111         }
112 
113         ValueType replacedEntry = partitionMap.put(key, value);
114         appCtx.publishEvent(new AddEntryEvent(this, partition, key, value));
115         return replacedEntry;
116     }
117 
118     /** {@inheritDoc} */
119     public ValueType remove(String partition, KeyType key) {
120         if (partition == null || key == null) {
121             return null;
122         }
123 
124         if (store.containsKey(partition)) {
125             ValueType removedEntry = store.get(partition).remove(key);
126             appCtx.publishEvent(new RemoveEntryEvent(this, partition, key, removedEntry));
127             return removedEntry;
128         }
129 
130         return null;
131     }
132 
133     /** {@inheritDoc} */
134     public void setApplicationContext(ApplicationContext ctx) {
135         ApplicationContext rootContext = ctx;
136         while (rootContext.getParent() != null) {
137             rootContext = rootContext.getParent();
138         }
139         appCtx = rootContext;
140     }
141 
142     /** An event indicating an item has been added to an storage service. */
143     public static class AddEntryEvent<KeyType, ValueType> extends ApplicationEvent {
144 
145         /** Serial version UID. */
146         private static final long serialVersionUID = -1939512157260059492L;
147 
148         /** Storage service to which the item was added. */
149         private StorageService<KeyType, ValueType> storageService;
150 
151         /** Storage partition to which the item was added. */
152         private String partition;
153 
154         /** Key to the added item. */
155         private KeyType key;
156 
157         /** The added item. */
158         private ValueType value;
159 
160         /**
161          * Constructor.
162          * 
163          * @param storageService storage service to which an item was added
164          * @param partition partition to which the entry was added
165          * @param key key of the added item
166          * @param value added item
167          */
168         public AddEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
169                 ValueType value) {
170             super(storageService);
171             this.storageService = storageService;
172             this.partition = partition;
173             this.key = key;
174             this.value = value;
175         }
176 
177         /**
178          * Gets the storage service to which an item was added.
179          * 
180          * @return storage service to which an item was added
181          */
182         public StorageService<KeyType, ValueType> getStorageService() {
183             return storageService;
184         }
185 
186         /**
187          * Gets the partition to which the entry was added.
188          * 
189          * @return partition to which the entry was added
190          */
191         public String getPartition() {
192             return partition;
193         }
194 
195         /**
196          * Gets the key of the added item.
197          * 
198          * @return key of the added item
199          */
200         public KeyType getKey() {
201             return key;
202         }
203 
204         /**
205          * Gets the added item.
206          * 
207          * @return added item
208          */
209         public ValueType getValue() {
210             return value;
211         }
212     }
213 
214     /** An event indicating an item has been removed from an storage service. */
215     public static class RemoveEntryEvent<KeyType, ValueType> extends ApplicationEvent {
216 
217         /** Serial version UID. */
218         private static final long serialVersionUID = 7414605158323325366L;
219 
220         /** Storage service to which the item was removed. */
221         private StorageService<KeyType, ValueType> storageService;
222 
223         /** Storage partition to which the item was removed. */
224         private String partition;
225 
226         /** Key to the removed item. */
227         private KeyType key;
228 
229         /** The removed item. */
230         private ValueType value;
231 
232         /**
233          * Constructor.
234          * 
235          * @param storageService storage service to which an item was removed
236          * @param partition partition to which the entry was removed
237          * @param key key of the removed item
238          * @param value removed item
239          */
240         public RemoveEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key,
241                 ValueType value) {
242             super(storageService);
243             this.storageService = storageService;
244             this.partition = partition;
245             this.key = key;
246             this.value = value;
247         }
248 
249         /**
250          * Gets the storage service to which an item was removed.
251          * 
252          * @return storage service to which an item was removed
253          */
254         public StorageService<KeyType, ValueType> getStorageService() {
255             return storageService;
256         }
257 
258         /**
259          * Gets the partition to which the entry was removed.
260          * 
261          * @return partition to which the entry was removed
262          */
263         public String getPartition() {
264             return partition;
265         }
266 
267         /**
268          * Gets the key of the removed item.
269          * 
270          * @return key of the removed item
271          */
272         public KeyType getKey() {
273             return key;
274         }
275 
276         /**
277          * Gets the removed item.
278          * 
279          * @return removed item
280          */
281         public ValueType getValue() {
282             return value;
283         }
284     }
285 
286     /** An iterator over the partitions of the storage service. */
287     public class PartitionIterator implements Iterator<String> {
288 
289         /** Iterator over the partitions in the backing store. */
290         private Iterator<String> partitionItr;
291 
292         /** Current partition. */
293         private String currentParition;
294 
295         /** Constructor. */
296         public PartitionIterator() {
297             partitionItr = store.keySet().iterator();
298         }
299 
300         /** {@inheritDoc} */
301         public boolean hasNext() {
302             return partitionItr.hasNext();
303         }
304 
305         /** {@inheritDoc} */
306         public String next() {
307             currentParition = partitionItr.next();
308             return currentParition;
309         }
310 
311         /** {@inheritDoc} */
312         public void remove() {
313             Iterator<KeyType> partitionEntries = getKeys(currentParition);
314             while (partitionEntries.hasNext()) {
315                 partitionEntries.next();
316                 partitionEntries.remove();
317             }
318             store.remove(currentParition);
319         }
320     }
321 
322     /** An iterator over the entries of a partition of the storage service. */
323     public class PartitionEntryIterator implements Iterator<KeyType> {
324 
325         /** Partition on which we are operating. */
326         private String partition;
327 
328         /** Iterator of keys within the partition. */
329         private Iterator<KeyType> keysItr;
330 
331         /** Current key within the iteration. */
332         private KeyType currentKey;
333 
334         /**
335          * Constructor.
336          * 
337          * @param partition partition upon which this iterator operates
338          */
339         public PartitionEntryIterator(String partition) {
340             this.partition = partition;
341             keysItr = store.get(partition).keySet().iterator();
342         }
343 
344         /** {@inheritDoc} */
345         public boolean hasNext() {
346             return keysItr.hasNext();
347         }
348 
349         /** {@inheritDoc} */
350         public KeyType next() {
351             currentKey = keysItr.next();
352             return currentKey;
353         }
354 
355         /** {@inheritDoc} */
356         public void remove() {
357             EventingMapBasedStorageService.this.remove(partition, currentKey);
358         }
359     }
360 }