/* ********************************************************************** ** ** Copyright notice ** ** ** ** (c) 2005-2009 RSSOwl Development Team ** ** http://www.rssowl.org/ ** ** ** ** All rights reserved ** ** ** ** This program and the accompanying materials are made available under ** ** the terms of the Eclipse Public License v1.0 which accompanies this ** ** distribution, and is available at: ** ** http://www.rssowl.org/legal/epl-v10.html ** ** ** ** A copy is found in the file epl-v10.html and important notices to the ** ** license from the team is found in the textfile LICENSE.txt distributed ** ** in this package. ** ** ** ** This copyright notice MUST APPEAR in all copies of the file! ** ** ** ** Contributors: ** ** RSSOwl Development Team - initial API and implementation ** ** ** ** ********************************************************************** */ package org.rssowl.core.internal.persist.dao; import org.rssowl.core.internal.persist.service.DBManager; import org.rssowl.core.internal.persist.service.DatabaseEvent; import org.rssowl.core.internal.persist.service.DatabaseListener; import org.rssowl.core.persist.IEntity; import org.rssowl.core.persist.dao.IEntityDAO; import org.rssowl.core.persist.event.EntityListener; import org.rssowl.core.persist.event.ModelEvent; import org.rssowl.core.persist.event.runnable.EventType; import org.rssowl.core.persist.service.PersistenceException; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Cache that stores all entities of type T in memory. In the future, we could * improve it to store only entities that have been requested. We could also add * eviction strategies at some point. * * @param <D> * @param <T> * @param <L> * @param <E> */ public abstract class CachingDAO<D extends AbstractEntityDAO<T, L, E>, T extends IEntity, L extends EntityListener<E, T>, E extends ModelEvent> implements IEntityDAO<T, L, E> { protected static final boolean USE_LEGACY_CACHE_ACTIVATION = true; private final D fDAO; private final ConcurrentMap<Long, T> fCache; public CachingDAO(D dao) { fDAO = dao; fDAO.addEntityListener(createEntityListener()); fCache = new ConcurrentHashMap<Long, T>(16, 0.75f, 1); /* Update the Cache based on Database Events */ DBManager.getDefault().addEntityStoreListener(new DatabaseListener() { public void databaseClosed(DatabaseEvent event) { onDatabaseClosed(event); } public void databaseOpened(DatabaseEvent event) { onDatabaseOpened(event); } }); } protected void putAll(Set<E> events) { for (E event : events) { fCache.put(event.getEntity().getId(), fDAO.getEntityClass().cast(event.getEntity())); } } protected void removeAll(Set<E> events) { for (E event : events) { fCache.remove(event.getEntity().getId()); } } protected void onDatabaseClosed(@SuppressWarnings("unused") DatabaseEvent event) { fCache.clear(); } protected void onDatabaseOpened(@SuppressWarnings("unused") DatabaseEvent event) { /* Ensure we start with a fresh cache */ fCache.clear(); /* Add all from DAO */ for (T entity : fDAO.loadAll()) { fCache.put(entity.getId(), entity); } } /** * @return the listener to properly handle updates to the cache. */ protected abstract L createEntityListener(); /** * @return the DAO this cache is wrapping around. */ protected final D getDAO() { return fDAO; } /** * @return the cache implementation (Map). */ protected final ConcurrentMap<Long, T> getCache() { return fCache; } /* * @see org.rssowl.core.persist.dao.IEntityDAO#exists(long) */ public final boolean exists(long id) throws PersistenceException { return fCache.containsKey(id); } /* * @see org.rssowl.core.persist.dao.IEntityDAO#load(long) */ public final T load(long id) throws PersistenceException { return fCache.get(id); } /* * @see org.rssowl.core.persist.dao.IPersistableDAO#loadAll() */ public final Collection<T> loadAll() throws PersistenceException { return Collections.unmodifiableCollection(fCache.values()); } /* * @see org.rssowl.core.persist.dao.IEntityDAO#fireEvents(java.util.Set, * org.rssowl.core.persist.event.runnable.EventType) */ public final void fireEvents(Set<E> events, EventType eventType) { fDAO.fireEvents(events, eventType); } /* * @see org.rssowl.core.persist.dao.IPersistableDAO#countAll() */ public final long countAll() throws PersistenceException { return fCache.size(); } /* * @see * org.rssowl.core.persist.dao.IEntityDAO#addEntityListener(org.rssowl.core * .persist.event.EntityListener) */ public final void addEntityListener(L listener) { fDAO.addEntityListener(listener); } /* * @see * org.rssowl.core.persist.dao.IEntityDAO#removeEntityListener(org.rssowl. * core.persist.event.EntityListener) */ public final void removeEntityListener(L listener) { fDAO.removeEntityListener(listener); } /* * @see * org.rssowl.core.persist.dao.IPersistableDAO#delete(org.rssowl.core.persist * .IPersistable) */ public final void delete(T persistable) throws PersistenceException { fDAO.delete(persistable); } /* * @see * org.rssowl.core.persist.dao.IPersistableDAO#deleteAll(java.util.Collection) */ public final void deleteAll(Collection<T> persistables) throws PersistenceException { fDAO.deleteAll(persistables); } /* * @see org.rssowl.core.persist.dao.IPersistableDAO#getEntityClass() */ public final Class<? extends T> getEntityClass() { return fDAO.getEntityClass(); } /* * @see * org.rssowl.core.persist.dao.IPersistableDAO#save(org.rssowl.core.persist * .IPersistable) */ public final T save(T persistable) throws PersistenceException { return fDAO.save(persistable); } /* * @see * org.rssowl.core.persist.dao.IPersistableDAO#saveAll(java.util.Collection) */ public final void saveAll(Collection<T> persistables) throws PersistenceException { fDAO.saveAll(persistables); } }