/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.vipr.client.core.util;
import static com.emc.vipr.client.core.util.ResourceUtils.id;
import static com.emc.vipr.client.core.util.ResourceUtils.refIds;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.emc.storageos.model.DataObjectRestRep;
import com.emc.storageos.model.RelatedResourceRep;
import com.emc.vipr.client.core.Resources;
import com.emc.vipr.client.core.filters.ResourceFilter;
import com.emc.vipr.client.exceptions.ViPRHttpException;
/**
* Wrapper around resources that can cache the results. This can be quite useful when looking up related resources from
* another type.
*
* @param <T>
* the resource type.
*/
public class CachedResources<T extends DataObjectRestRep> {
private static final int NOT_FOUND = 404;
private final Resources<T> resources;
private Map<URI, T> cache = new HashMap<URI, T>();
public CachedResources(Resources<T> resources) {
this.resources = resources;
}
/**
* Gets a resource by ID, first checking if it is already in the cache. This will handle an HTTP 404 error by
* caching and returning a null value.
*
* @param id
* the resource ID.
* @return the resource.
*/
public T get(URI id) {
if (id == null) {
return null;
}
if (cache.containsKey(id)) {
return cache.get(id);
}
try {
T value = cache(resources.get(id));
return value;
} catch (ViPRHttpException e) {
if (e.getHttpCode() == NOT_FOUND) {
cache.put(id, null);
return null;
}
throw e;
}
}
/**
* Gets a list resource by reference, first checking if it is already in the cache.
*
* @param ref
* the resource reference.
* @return the resource.
*/
public T get(RelatedResourceRep ref) {
return get(id(ref));
}
/**
* Gets a list of resources by reference, checking the cache for each. This is a convenience method for
* <code>getByRefs(refs, null)</code>.
*
* @param refs
* the resource references.
* @return the list of resources.
*
* @see #getByRefs(Collection, ResourceFilter)
*/
public List<T> getByRefs(Collection<? extends RelatedResourceRep> refs) {
return getByRefs(refs, null);
}
/**
* Gets a list of resources by reference, checking the cache for each. If a filter is provided the values are
* filtered as they are retrieved.
*
* @param refs
* the resource references.
* @param filter
* the filter to apply (may be null).
* @return the list of resources.
*/
public List<T> getByRefs(Collection<? extends RelatedResourceRep> refs, ResourceFilter<T> filter) {
return getByIds(refIds(refs), filter);
}
/**
* Gets a list of resources by IDs, checking the cache for each. This is a convenience method for <code>getByIds(ids, null)</code>.
*
* @param ids
* the resource IDs.
* @return the resources.
*
* @see #getByIds(Collection, ResourceFilter)
*/
public List<T> getByIds(Collection<URI> ids) {
return getByIds(ids, null);
}
/**
* Gets a list of resources by IDs, checking the cache for each. If a filter is provided, the resources are filtered
* as they are retrieved.
*
* @param ids
* the resource IDs.
* @param filter
* the filter to apply (may be null).
* @return the resources.
*/
public List<T> getByIds(Collection<URI> ids, ResourceFilter<T> filter) {
List<T> values = new ArrayList<T>();
Set<URI> fetchIds = new HashSet<URI>();
for (URI id : ids) {
// Skip IDs that are rejected by the filter
if ((filter != null) && !filter.acceptId(id)) {
continue;
}
// Bypass values that are already cached
if (isCached(id)) {
T value = getCached(id);
if (value != null) {
values.add(value);
}
}
else {
fetchIds.add(id);
}
}
// Fetch any missing values and store them into the cache
if (!fetchIds.isEmpty()) {
List<T> results = resources.getByIds(fetchIds, filter);
for (T result : results) {
values.add(cache(result));
}
}
return values;
}
/**
* Determines if the cache contains the given resource by ID
*
* @param id
* resource ID.
* @return true if the cache contains the resource.
*/
public boolean isCached(URI id) {
return cache.containsKey(id);
}
/**
* Gets the resource from the cache only.
*
* @param id
* the resource ID.
* @return the cached resource.
*/
public T getCached(URI id) {
return cache.get(id);
}
/**
* Adds the resource to the cache.
*
* @param value
* the resource to cache.
* @return the resource.
*/
protected T cache(T value) {
if (value != null) {
cache.put(value.getId(), value);
}
return value;
}
/**
* Clears the cached values.
*/
public void clear() {
cache.clear();
}
/**
* Removes a resource from the cache.
*
* @param id
* the resource ID.
* @return the previously cached value.
*/
public T remove(URI id) {
return cache.remove(id);
}
/**
* Removes a resource from the cache.
*
* @param ref
* the resource reference.
* @return the previously cached value.
*/
public T remove(RelatedResourceRep ref) {
return remove(id(ref));
}
}