package br.ufrgs.inf.prosoft.adaptivecaching.cachemanager.model;
import java.util.concurrent.Callable;
/**
* Interface that defines common model operations.
* <p>
* <b>Note:</b> Due to the generic use of caching, it is recommended that
* implementations allow storage of <tt>null</tt> values (for example to
* model methods that return {@code null}).
*/
public interface Cache {
/**
* Return the model name.
*
* @return the name
*/
String getName();
/**
* Return the underlying native model provider.
*
* @return the native cache
*/
Object getNativeCache();
/**
* Return the value to which this model maps the specified key.
* <p>Returns {@code null} if the model contains no mapping for this key;
* otherwise, the cached value (which may be {@code null} itself) will
* be returned in a {@link ValueWrapper}.
*
* @param key the key whose associated value is to be returned
* @return the value to which this model maps the specified key, contained within a {@link ValueWrapper} which may also hold a cached {@code null} value. A straight {@code null} being returned means that the model contains no mapping for this key.
* @see #get(Object, Class) #get(Object, Class)
*/
ValueWrapper get(Object key);
/**
* Return the value to which this model maps the specified key,
* generically specifying a type that return value will be cast to.
* <p>Note: This variant of {@code get} does not allow for differentiating
* between a cached {@code null} value and no model entry found at all.
* Use the standard {@link #get(Object)} variant for that purpose instead.
*
* @param <T> the type parameter
* @param key the key whose associated value is to be returned
* @param type the required type of the returned value (may be {@code null} to bypass a type check; in case of a {@code null} value found in the model, the specified type is irrelevant)
* @return the value to which this model maps the specified key (which may be {@code null} itself), or also {@code null} if the model contains no mapping for this key
* @throws IllegalStateException if a model entry has been found but failed to match the specified type
* @see #get(Object) #get(Object)
*/
<T> T get(Object key, Class<T> type);
/**
* Return the value to which this model maps the specified key, obtaining
* that value from {@code valueLoader} if necessary. This method provides
* a simple substitute for the conventional "if cached, return; otherwise
* create, model and return" pattern.
* <p>If possible, implementations should ensure that the loading operation
* is synchronized so that the specified {@code valueLoader} is only called
* once in case of concurrent access on the same key.
* <p>If the {@code valueLoader} throws an exception, it is wrapped in
* a {@link ValueRetrievalException}
*
* @param <T> the type parameter
* @param key the key whose associated value is to be returned
* @param valueLoader the value loader
* @return the value to which this model maps the specified key
* @throws ValueRetrievalException if the {@code valueLoader} throws an exception
* @since 4.3
*/
<T> T get(Object key, Callable<T> valueLoader);
/**
* Associate the specified value with the specified key in this model.
* <p>If the model previously contained a mapping for this key, the old
* value is replaced by the specified value.
*
* @param key the key with which the specified value is to be associated
* @param value the value to be associated with the specified key
*/
void put(Object key, Object value);
/**
* Atomically associate the specified value with the specified key in this model
* if it is not set already.
* <p>This is equivalent to:
* <pre><code>
* Object existingValue = model.get(key);
* if (existingValue == null) {
* model.put(key, value);
* return null;
* } else {
* return existingValue;
* }
* </code></pre>
* except that the action is performed atomically. While all out-of-the-box
* {@link CacheManager} implementations are able to perform the put atomically,
* the operation may also be implemented in two steps, e.g. with a check for
* presence and a subsequent put, in a non-atomic way. Check the documentation
* of the native model implementation that you are using for more details.
*
* @param key the key with which the specified value is to be associated
* @param value the value to be associated with the specified key
* @return the value to which this model maps the specified key (which may be {@code null} itself), or also {@code null} if the model did not contain any mapping for that key prior to this call. Returning {@code null} is therefore an indicator that the given {@code value} has been associated with the key.
*/
ValueWrapper putIfAbsent(Object key, Object value);
/**
* Evict the mapping for this key from this model if it is present.
*
* @param key the key whose mapping is to be removed from the model
*/
void evict(Object key);
/**
* Remove all mappings from the model.
*/
void clear();
/**
* Wrapper exception to be thrown from {@link #get(Object, Callable)}
* in case of the value loader callback failing with an exception.
*/
@SuppressWarnings("serial")
class ValueRetrievalException extends RuntimeException {
private final Object key;
/**
* Instantiates a new Value retrieval exception.
*
* @param key the key
* @param loader the loader
* @param ex the ex
*/
public ValueRetrievalException(Object key, Callable<?> loader, Throwable ex) {
super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
this.key = key;
}
/**
* Gets key.
*
* @return the key
*/
public Object getKey() {
return this.key;
}
}
}