package com.octo.android.robospice.persistence; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import roboguice.util.temp.Ln; import com.octo.android.robospice.persistence.exception.CacheCreationException; import com.octo.android.robospice.persistence.exception.CacheLoadingException; import com.octo.android.robospice.persistence.exception.CacheSavingException; /** * An entity responsible for loading/saving data from/to cache. It implements a * Chain of Responsibility pattern, delegating loading and saving operations to * {@link ObjectPersister} or {@link ObjectPersisterFactory} elements. The chain * of responsibility is ordered. This means that the order used to register * elements matters. All elements in the chain of responsibility are questioned * in order. The first element that can handle a given class for persistence * will be used to persist data of this class. * @author sni */ /* * Note to maintainers : concurrency must be taken care of as persister can be * created by factories at any time. Thx to Henri Tremblay from EasyMock for * peer review and concurrency checks. */ @SuppressWarnings("deprecation") public class CacheManager implements ICacheManager { /** The Chain of Responsibility list of all {@link Persister}. */ private Collection<Persister> listPersister = new ArrayList<Persister>(); private Map<ObjectPersisterFactory, List<ObjectPersister<?>>> mapFactoryToPersister = new HashMap<ObjectPersisterFactory, List<ObjectPersister<?>>>(); /** {@inheritDoc} */ @Override public void addPersister(Persister persister) { listPersister.add(persister); if (persister instanceof ObjectPersisterFactory) { // will lead the list to be copied whenever we add a persister to it // but there won't be any overhead while iterating through the list. mapFactoryToPersister.put((ObjectPersisterFactory) persister, new CopyOnWriteArrayList<ObjectPersister<?>>()); } else if (!(persister instanceof ObjectPersister)) { throw new RuntimeException(getClass().getSimpleName() + " only supports " + ObjectPersister.class.getSimpleName() + " or " + ObjectPersisterFactory.class.getSimpleName() + " instances."); } } /** {@inheritDoc} */ @Override public void removePersister(Persister persister) { listPersister.remove(persister); if (persister instanceof ObjectPersisterFactory) { mapFactoryToPersister.remove(persister); } } /** * {@inheritDoc} * @throws CacheCreationException */ @Override public <T> T loadDataFromCache(Class<T> clazz, Object cacheKey, long maxTimeInCacheBeforeExpiry) throws CacheLoadingException, CacheCreationException { return getObjectPersister(clazz).loadDataFromCache(cacheKey, maxTimeInCacheBeforeExpiry); } @Override @SuppressWarnings("unchecked") /** {@inheritDoc}*/ public <T> T saveDataToCacheAndReturnData(T data, Object cacheKey) throws CacheSavingException, CacheCreationException { // http://stackoverflow.com/questions/4460580/java-generics-why-someobject-getclass-doesnt-return-class-extends-t ObjectPersister<T> classCacheManager = getObjectPersister((Class<T>) data.getClass()); return classCacheManager.saveDataToCacheAndReturnData(data, cacheKey); } /** * {@inheritDoc} * @throws CacheCreationException */ @Override public boolean isDataInCache(Class<?> clazz, Object cacheKey, long maxTimeInCacheBeforeExpiry) throws CacheCreationException { return getObjectPersister(clazz).isDataInCache(cacheKey, maxTimeInCacheBeforeExpiry); } /** * {@inheritDoc} * @throws CacheLoadingException * @throws CacheCreationException */ @Override public Date getDateOfDataInCache(Class<?> clazz, Object cacheKey) throws CacheLoadingException, CacheCreationException { return new Date(getObjectPersister(clazz).getCreationDateInCache(cacheKey)); } /** * {@inheritDoc} */ @Override public boolean removeDataFromCache(Class<?> clazz, Object cacheKey) { try { return getObjectPersister(clazz).removeDataFromCache(cacheKey); } catch (CacheCreationException e) { Ln.e(e); return false; } } /** * {@inheritDoc} */ @Override public void removeAllDataFromCache(Class<?> clazz) { try { getObjectPersister(clazz).removeAllDataFromCache(); } catch (CacheCreationException e) { Ln.e(e); } } /** * {@inheritDoc} */ @Override public <T> List<Object> getAllCacheKeys(final Class<T> clazz) { try { return getObjectPersister(clazz).getAllCacheKeys(); } catch (CacheCreationException e) { Ln.e(e); return Collections.emptyList(); } } /** * {@inheritDoc} * @throws CacheCreationException */ @Override public <T> List<T> loadAllDataFromCache(final Class<T> clazz) throws CacheLoadingException, CacheCreationException { return getObjectPersister(clazz).loadAllDataFromCache(); } /** {@inheritDoc} */ @Override public void removeAllDataFromCache() { for (Persister persister : this.listPersister) { if (persister instanceof CacheCleaner) { ((CacheCleaner) persister).removeAllDataFromCache(); } if (persister instanceof ObjectPersisterFactory) { ObjectPersisterFactory factory = (ObjectPersisterFactory) persister; List<ObjectPersister<?>> listPersisterForFactory = mapFactoryToPersister.get(factory); for (ObjectPersister<?> objectPersister : listPersisterForFactory) { objectPersister.removeAllDataFromCache(); } } } } @SuppressWarnings("unchecked") protected <T> ObjectPersister<T> getObjectPersister(Class<T> clazz) throws CacheCreationException { for (Persister persister : this.listPersister) { if (persister.canHandleClass(clazz)) { if (persister instanceof ObjectPersister) { return (ObjectPersister<T>) persister; } if (persister instanceof ObjectPersisterFactory) { ObjectPersisterFactory factory = (ObjectPersisterFactory) persister; if (factory.canHandleClass(clazz)) { List<ObjectPersister<?>> listPersisterForFactory = mapFactoryToPersister.get(factory); for (ObjectPersister<?> objectPersister : listPersisterForFactory) { if (objectPersister.canHandleClass(clazz)) { return (ObjectPersister<T>) objectPersister; } } ObjectPersister<T> newPersister = factory.createObjectPersister(clazz); newPersister.setAsyncSaveEnabled(factory.isAsyncSaveEnabled()); listPersisterForFactory.add(newPersister); return newPersister; } } } } throw new RuntimeException("Class " + clazz.getName() + " is not handled by any registered ObjectPersister. Please add a Persister for this class inside the CacheManager of your SpiceService."); } }