/**
* Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved.
*
* Licensed 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 com.springsource.insight.plugin.cassandra;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
/**
* Used to store PreparedStatements and associated JdbcOperations in memory
* via WeakReference.
* <p/>
* If a user calls connection.prepareStatement() and never calls execute(),
* the prepared statement must still be purged from memory.
* <p/>
* This class provides thread-safe, synchronized access.
* <p/>
* You must be confident that these keys and values are not purged
* too early. See the notes on their use in other classes to verify this
* behavior.
* <p/>
* We may also be able to replace this with the standard WeakHashMap
* implementation, though that implementation should then be synchronized.
*/
public class WeakKeyHashMap<K, V> {
private final ConcurrentHashMap<WeakRef<K, V>, WeakRef<K, V>> map = new ConcurrentHashMap<WeakRef<K, V>, WeakRef<K, V>>();
private final ReferenceQueue<V> refQueue = new ReferenceQueue<V>();
public WeakKeyHashMap() {
super();
}
public int size() {
return map.size();
}
public V put(K key, V val) {
WeakRef<K, V> newRef = new WeakRef<K, V>(key, refQueue, val);
WeakRef<K, V> oldRef = map.put(newRef, newRef);
V prev = null;
if (oldRef != null) {
// System.out.println("** Overwrote old reference to statement=" + dumpObj(key));
prev = oldRef.getHardRef();
}
cleanupRefQueue();
return prev;
}
public V get(K key) {
WeakRef<K, V> lookupRef = new WeakRef<K, V>(key, refQueue, null);
WeakRef<K, V> ref = map.get(lookupRef);
if (ref == null) {
// Reference lost
// System.out.println("** Lost reference to statement=" + dumpObj(key));
return null;
} else {
return ref.getHardRef();
}
}
public V remove(K key) {
WeakRef<K, V> lookupRef = new WeakRef<K, V>(key, refQueue, null);
WeakRef<K, V> ref = map.remove(lookupRef);
if (ref == null) {
// System.out.println("** Reference no longer available anyway to statement=" + dumpObj(key));
return null;
} else {
return ref.getHardRef();
}
}
private void cleanupRefQueue() {
@SuppressWarnings("rawtypes")
Reference ref;
while ((ref = refQueue.poll()) != null) {
@SuppressWarnings("unchecked")
WeakRef<K, V> softRef = (WeakRef<K, V>) ref;
if (map.remove(softRef) == null) {
// System.out.println("Remove cleared ref, but it wasn't found in our map!");
}
}
}
public static String dumpObj(Object obj) {
return obj.getClass() + ":" + System.identityHashCode(obj);
}
private static class WeakRef<A, B> extends WeakReference<A> {
private final int hashCode;
private final B hardRef;
@SuppressWarnings({"unchecked", "rawtypes"})
public WeakRef(A obj, ReferenceQueue queue, B hardReference) {
super(obj, queue);
this.hashCode = System.identityHashCode(obj);
this.hardRef = hardReference;
}
public B getHardRef() {
return hardRef;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
@SuppressWarnings("rawtypes")
WeakRef o = (WeakRef) other;
return o.get() == get();
}
}
}