/** * Utility method to clone a map, preserving immutable instances * * @param <K> the map key type, which must be {@link Serializable} * @param <V> the map value type, which must be {@link Serializable} * @param map the map to copy * @param immutableClasses a set of classes that can be considered immutable over and above the * {@link #DEFAULT_IMMUTABLE_CLASSES default set} */ public static <K extends Serializable, V extends Serializable> Map<K, V> cloneMap( Map<K, V> map, Set<Class<?>> immutableClasses) { Map<K, V> copy = new HashMap<K, V>((int) (map.size() * 1.3)); for (Map.Entry<K, V> element : map.entrySet()) { K key = element.getKey(); V value = element.getValue(); // Clone as necessary key = ValueProtectingMap.protectValue(key, immutableClasses); value = ValueProtectingMap.protectValue(value, immutableClasses); copy.put(key, value); } return copy; }
/** * Called by methods that need to force the map into a safe state. * * <p>This method can be called without any locks being active. */ private void cloneMap() { readLock.lock(); try { // Check that it hasn't been copied already if (cloned) { return; } } finally { readLock.unlock(); } /* * Note: This space here is a window during which some code could have made * a copy. Therefore we will do a cautious double-check. */ // Put in a write lock before cloning the map writeLock.lock(); try { // Check that it hasn't been copied already if (cloned) { return; } Map<K, V> copy = ValueProtectingMap.cloneMap(map, immutableClasses); // Discard the original this.map = copy; this.cloned = true; } finally { writeLock.unlock(); } }
@Override public V get(Object key) { readLock.lock(); try { V value = map.get(key); return ValueProtectingMap.protectValue(value, immutableClasses); } finally { readLock.unlock(); } }