/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.api.caching.impl;
import com.sun.tools.visualvm.api.caching.Cache;
import com.sun.tools.visualvm.api.caching.Entry;
import com.sun.tools.visualvm.api.caching.EntryFactory;
import com.sun.tools.visualvm.api.caching.Persistor;
import java.lang.ref.Reference;
import java.util.HashMap;
import java.util.Map;
/**
* Default class implementation - should be used as a base for caching functionality
* @author Jaroslav Bachorik
*/
final class CacheImpl<K, V> extends Cache<K,V> {
final private Map<Reference<K>, Entry<V>> objectCache = new HashMap<Reference<K>, Entry<V>>();
private long update_interval = 60480000; // 7 days in milliseconds
private Persistor<K, V> persistor = Persistor.DEFAULT;
private KeyFactory<K> keyFactory = KeyFactory.DEFAULT;
private EntryFactory<K,V> resolver = EntryFactory.DEFAULT;
CacheImpl() {};
CacheImpl(EntryFactory<K,V> resolver) {
this.resolver = resolver;
}
CacheImpl(Persistor<K,V> persistor) {
this.persistor = persistor;
}
CacheImpl(KeyFactory<K> keyFactory) {
this.keyFactory = keyFactory;
}
CacheImpl(EntryFactory<K,V> resolver, Persistor<K,V> persistor) {
this.resolver = resolver;
this.persistor = persistor;
}
CacheImpl(EntryFactory<K,V> resolver, KeyFactory<K> keyFactory) {
this.resolver = resolver;
this.keyFactory = keyFactory;
}
CacheImpl(KeyFactory<K> keyFactory, Persistor<K,V> persistor) {
this.persistor = persistor;
this.keyFactory = keyFactory;
}
CacheImpl(EntryFactory<K,V> resolver, KeyFactory<K> keyFactory, Persistor<K,V> persistor) {
this.resolver = resolver;
this.persistor = persistor;
this.keyFactory = keyFactory;
}
/**
* Retrieves an object from the cache by the given key
* <p>
* If there is no cached version then a registered instance of {@linkplain EntryFactory}
* is used to invoke its {@linkplain EntryFactory#createEntry(java.lang.Object)} method.<br/>
* Also, a {@linkplain Persistor} instance is used to retrieve and store the cached value in
* a dedicated storage.
* </p>
* @param key The key identifying the object to be retrieved
* @return Returns the cached object or NULL
*/
@Override
final public V retrieveObject(K key) {
Reference<K> softKey = keyFactory.createKey(key);
synchronized(objectCache) {
Entry<V> entry = objectCache.get(softKey);
if (entry == null) {
entry = persistor.retrieve(key);
}
if (entry == null) {
entry = cacheMiss(key);
if (entry != null && entry.getContent() != null) {
persistor.store(key, entry);
objectCache.put(softKey, entry);
}
} else {
long timestamp = System.currentTimeMillis();
if ((timestamp - entry.getUpdateTimeStamp()) > update_interval) {
Entry<V> newEntry = cacheMiss(key);
if (newEntry != null && newEntry.getContent() != null) {
persistor.store(key, entry);
objectCache.put(softKey, newEntry);
entry = newEntry;
}
}
}
return entry != null ? entry.getContent() : null;
}
}
@Override
final public V invalidateObject(K key) {
Reference<K> softKey = keyFactory.createKey(key);
synchronized(objectCache) {
Entry<V> entry = objectCache.remove(softKey);
return entry != null ? entry.getContent() : null;
}
}
/**
* Property getter
* @return Returns TTL interval in milliseconds
*/
@Override
final public long getTTL() {
return update_interval;
}
/**
* Property setter
* @param ttl TTL interval in milliseconds
*/
@Override
final public void setTTL(long ttl) {
this.update_interval = ttl;
}
/**
* This method is called in case of cache-miss
* It can return NULL if it's not possible to resolve the missing instance
* @param key The key of the missing object
* @return Returns the resolved object or NULL
*/
private Entry<V> cacheMiss(K key) {
return resolver.createEntry(key);
}
}