Java tutorial
/* * Licensed to the University Corporation for Advanced Internet Development, * Inc. (UCAID) under one or more contributor license agreements. See the * NOTICE file distributed with this work for additional information regarding * copyright ownership. The UCAID licenses this file to You under the Apache * License, Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.internet2.middleware.shibboleth.common.util; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.opensaml.util.storage.StorageService; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; /** * A thread-safe implementation of {@link StorageService} that publishes event when items are added or removed from the * service. * * An {@link edu.internet2.middleware.shibboleth.common.util.EventingMapBasedStorageService.AddEntryEvent} is published after an item has been added to the storage service. A {@link edu.internet2.middleware.shibboleth.common.util.EventingMapBasedStorageService.RemoveEntryEvent} * is published after an item has been removed from the storage service. These events are published in the root * application context, that is the highest ancestor, of the application context presented to this class. * * @param <KeyType> object type of the keys * @param <ValueType> object type of the values */ public class EventingMapBasedStorageService<KeyType, ValueType> implements StorageService<KeyType, ValueType>, ApplicationContextAware { /** Spring application context. */ private ApplicationContext appCtx; /** Backing map. */ private Map<String, Map<KeyType, ValueType>> store; /** Constructor. */ public EventingMapBasedStorageService() { store = new ConcurrentHashMap<String, Map<KeyType, ValueType>>(); } /** {@inheritDoc} */ public boolean contains(String partition, Object key) { if (partition == null || key == null) { return false; } if (store.containsKey(partition)) { return store.get(partition).containsKey(key); } return false; } /** {@inheritDoc} */ public ValueType get(String partition, Object key) { if (partition == null || key == null) { return null; } if (store.containsKey(partition)) { return store.get(partition).get(key); } return null; } /** {@inheritDoc} */ public Iterator<KeyType> getKeys(String partition) { if (partition == null) { return null; } if (store.containsKey(partition)) { return this.new PartitionEntryIterator(partition); } return null; } /** {@inheritDoc} */ public Iterator<String> getPartitions() { return this.new PartitionIterator(); } /** {@inheritDoc} */ public ValueType put(String partition, KeyType key, ValueType value) { if (partition == null || key == null) { return null; } Map<KeyType, ValueType> partitionMap; synchronized (store) { partitionMap = store.get(partition); if (partitionMap == null) { partitionMap = new ConcurrentHashMap<KeyType, ValueType>(); store.put(partition, partitionMap); } } ValueType replacedEntry = partitionMap.put(key, value); appCtx.publishEvent(new AddEntryEvent(this, partition, key, value)); return replacedEntry; } /** {@inheritDoc} */ public ValueType remove(String partition, KeyType key) { if (partition == null || key == null) { return null; } if (store.containsKey(partition)) { ValueType removedEntry = store.get(partition).remove(key); appCtx.publishEvent(new RemoveEntryEvent(this, partition, key, removedEntry)); return removedEntry; } return null; } /** {@inheritDoc} */ public void setApplicationContext(ApplicationContext ctx) { ApplicationContext rootContext = ctx; while (rootContext.getParent() != null) { rootContext = rootContext.getParent(); } appCtx = rootContext; } /** An event indicating an item has been added to an storage service. */ public static class AddEntryEvent<KeyType, ValueType> extends ApplicationEvent { /** Serial version UID. */ private static final long serialVersionUID = -1939512157260059492L; /** Storage service to which the item was added. */ private StorageService<KeyType, ValueType> storageService; /** Storage partition to which the item was added. */ private String partition; /** Key to the added item. */ private KeyType key; /** The added item. */ private ValueType value; /** * Constructor. * * @param storageService storage service to which an item was added * @param partition partition to which the entry was added * @param key key of the added item * @param value added item */ public AddEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key, ValueType value) { super(storageService); this.storageService = storageService; this.partition = partition; this.key = key; this.value = value; } /** * Gets the storage service to which an item was added. * * @return storage service to which an item was added */ public StorageService<KeyType, ValueType> getStorageService() { return storageService; } /** * Gets the partition to which the entry was added. * * @return partition to which the entry was added */ public String getPartition() { return partition; } /** * Gets the key of the added item. * * @return key of the added item */ public KeyType getKey() { return key; } /** * Gets the added item. * * @return added item */ public ValueType getValue() { return value; } } /** An event indicating an item has been removed from an storage service. */ public static class RemoveEntryEvent<KeyType, ValueType> extends ApplicationEvent { /** Serial version UID. */ private static final long serialVersionUID = 7414605158323325366L; /** Storage service to which the item was removed. */ private StorageService<KeyType, ValueType> storageService; /** Storage partition to which the item was removed. */ private String partition; /** Key to the removed item. */ private KeyType key; /** The removed item. */ private ValueType value; /** * Constructor. * * @param storageService storage service to which an item was removed * @param partition partition to which the entry was removed * @param key key of the removed item * @param value removed item */ public RemoveEntryEvent(StorageService<KeyType, ValueType> storageService, String partition, KeyType key, ValueType value) { super(storageService); this.storageService = storageService; this.partition = partition; this.key = key; this.value = value; } /** * Gets the storage service to which an item was removed. * * @return storage service to which an item was removed */ public StorageService<KeyType, ValueType> getStorageService() { return storageService; } /** * Gets the partition to which the entry was removed. * * @return partition to which the entry was removed */ public String getPartition() { return partition; } /** * Gets the key of the removed item. * * @return key of the removed item */ public KeyType getKey() { return key; } /** * Gets the removed item. * * @return removed item */ public ValueType getValue() { return value; } } /** An iterator over the partitions of the storage service. */ public class PartitionIterator implements Iterator<String> { /** Iterator over the partitions in the backing store. */ private Iterator<String> partitionItr; /** Current partition. */ private String currentParition; /** Constructor. */ public PartitionIterator() { partitionItr = store.keySet().iterator(); } /** {@inheritDoc} */ public boolean hasNext() { return partitionItr.hasNext(); } /** {@inheritDoc} */ public String next() { currentParition = partitionItr.next(); return currentParition; } /** {@inheritDoc} */ public void remove() { Iterator<KeyType> partitionEntries = getKeys(currentParition); while (partitionEntries.hasNext()) { partitionEntries.next(); partitionEntries.remove(); } store.remove(currentParition); } } /** An iterator over the entries of a partition of the storage service. */ public class PartitionEntryIterator implements Iterator<KeyType> { /** Partition on which we are operating. */ private String partition; /** Iterator of keys within the partition. */ private Iterator<KeyType> keysItr; /** Current key within the iteration. */ private KeyType currentKey; /** * Constructor. * * @param partition partition upon which this iterator operates */ public PartitionEntryIterator(String partition) { this.partition = partition; keysItr = store.get(partition).keySet().iterator(); } /** {@inheritDoc} */ public boolean hasNext() { return keysItr.hasNext(); } /** {@inheritDoc} */ public KeyType next() { currentKey = keysItr.next(); return currentKey; } /** {@inheritDoc} */ public void remove() { EventingMapBasedStorageService.this.remove(partition, currentKey); } } }