net.anotheria.anosite.photoserver.service.storage.StorageServiceCache Maven / Gradle / Ivy
package net.anotheria.anosite.photoserver.service.storage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.anotheria.anoprise.cache.Cache;
import net.anotheria.anoprise.cache.CacheProducerWrapper;
import net.anotheria.anoprise.cache.Caches;
import net.anotheria.anosite.photoserver.shared.ApprovalStatus;
import net.anotheria.moskito.core.logging.DefaultStatsLogger;
import net.anotheria.moskito.core.logging.IntervalStatsLogger;
import net.anotheria.moskito.core.logging.SL4JLogOutput;
import net.anotheria.moskito.core.stats.DefaultIntervals;
import net.anotheria.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* StorageService cache.
*
* Cache for StorageService. Contains 3 caches . - albumsCache - maps id of album to id of album owner (user); - photosCache - maps id of photo to id of photo
* owner (user); - userAlbums - maps id of User to internal UserMediaData object which holds all required properties, methods, etc..
*
*
*
* NOTE : each cache method will return cloned result!
*
* @author h3ll
*/
public final class StorageServiceCache {
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(StorageServiceCache.class);
/**
* Cache start size constant.
*/
private static final int CACHE_START_SIZE = 1000;
/**
* Cache max size constant.
*/
private static final int CACHE_MAX_SIZE = 5000;
/**
* Photo cache. Photo.id to userId.
*/
private Cache photosCache;
/**
* Albums cache. Album id to userId
*/
private Cache albumsCache;
/**
* User data cache - UserId to userMediaData.
*/
private Cache userDataCache;
/**
* Lock.
*/
private static Object lock = new Object();
/**
* Constructor.
*/
protected StorageServiceCache() {
photosCache = createCache("ano-site-photoserver-storageservice-photo-cache");
albumsCache = createCache("ano-site-photoserver-storageservice-albums-cache");
userDataCache = createCache("ano-site-photoserver-storageservice-userdata-cache");
}
/**
* Generic cache creation with attaching moskito statistic and loggers.
*
* @param
* - key
* @param
* - value
* @param configFileName
* configuration file name
* @return create cache instance
*/
private Cache createCache(String configFileName) {
Cache cache;
if (StringUtils.isEmpty(configFileName)) {
LOG.warn("Illegal fileName - relying on defaults.");
cache = Caches.createHardwiredCache(configFileName, CACHE_START_SIZE, CACHE_MAX_SIZE);
} else
try {
cache = Caches.createConfigurableHardwiredCache(configFileName);
} catch (IllegalArgumentException e) {
LOG.warn("Can't find cache configuration for " + configFileName + ", falling back to min cache.");
cache = Caches.createHardwiredCache(configFileName, CACHE_START_SIZE, CACHE_MAX_SIZE);
}
CacheProducerWrapper cacheWrapper = new CacheProducerWrapper(cache, configFileName, "cache", "default");
new DefaultStatsLogger(cacheWrapper, new SL4JLogOutput(LoggerFactory.getLogger("MoskitoDefault")));
new IntervalStatsLogger(cacheWrapper, DefaultIntervals.FIVE_MINUTES, new SL4JLogOutput(LoggerFactory.getLogger("Moskito5m")));
new IntervalStatsLogger(cacheWrapper, DefaultIntervals.FIFTEEN_MINUTES, new SL4JLogOutput(LoggerFactory.getLogger("Moskito15m")));
new IntervalStatsLogger(cacheWrapper, DefaultIntervals.ONE_HOUR, new SL4JLogOutput(LoggerFactory.getLogger("Moskito1h")));
new IntervalStatsLogger(cacheWrapper, DefaultIntervals.ONE_DAY, new SL4JLogOutput(LoggerFactory.getLogger("Moskito1d")));
return cache;
}
// ###### Album related methods!!! -- START
/**
* Returns cached {@link AlbumBO} , with selected id.
*
* @param albumId
* id of album
* @return {@link AlbumBO}
*/
protected AlbumBO getCachedAlbumById(final long albumId) {
String albumOwnerId = albumsCache.get(albumId);
if (StringUtils.isEmpty(albumOwnerId))
// nothing is cached
return null;
return getCachedAlbum(albumOwnerId, albumId);
}
/**
* Return album with selected id if such was cached.
*
* @param userId
* id of album owner
* @param albumId
* id of album
* @return AlbumBO
*/
private AlbumBO getCachedAlbum(final String userId, final long albumId) {
UserMediaData data = userDataCache.get(userId);
if (data == null)
return null;
return data.getAlbumById(albumId);
}
/**
* Put/update {@link AlbumBO} in cache.
*
* @param toCache
* {@link AlbumBO}
*/
protected void updateItem(final AlbumBO toCache) {
if (toCache == null)
return;
albumsCache.put(toCache.getId(), toCache.getUserId());
updateUserData(toCache);
}
/**
* Update/Add {@link AlbumBO} to cached user data. If no data exists - it will be created.
*
* @param toCache
* {@link AlbumBO} item to cache
*/
private void updateUserData(final AlbumBO toCache) {
final String userId = toCache.getUserId();
if (StringUtils.isEmpty(userId))
return;
UserMediaData data = getUserMediaData(userId);
data.addDataItem(toCache);
}
/**
* Remove {@link AlbumBO} from all caches!
*
* @param toRemove
* {@link AlbumBO}
*/
protected void removeItem(final AlbumBO toRemove) {
if (toRemove == null)
return;
albumsCache.remove(toRemove.getId());
removeItemFromCachedUserData(toRemove);
}
/**
* Remove {@link AlbumBO} from cached UserData if it's present. If album is default - it's reference also will be removed!
*
* @param toRemove
* {@link AlbumBO}
*/
private void removeItemFromCachedUserData(final AlbumBO toRemove) {
final String userId = toRemove.getUserId();
if (StringUtils.isEmpty(userId))
return;
UserMediaData data = userDataCache.get(userId);
if (data == null)
return;
data.removeItem(toRemove);
}
/**
* Returns default album {@link AlbumBO} for selected user, if such was cached.
*
* @param userId
* id of user
* @return default {@link AlbumBO}
*/
protected AlbumBO getDefaultAlbum(String userId) {
if (StringUtils.isEmpty(userId))
return null;
UserMediaData data = userDataCache.get(userId);
if (data == null)
return null;
return data.getDefaultAlbum();
}
/**
* Return all user albums - if such data was cached.
*
* @param userId
* id of user
* @return {@link java.util.List} albums collection
*/
protected List getAllAlbums(String userId) {
if (StringUtils.isEmpty(userId))
return null;
UserMediaData data = userDataCache.get(userId);
if (data == null)
return null;
// call to all albums!
return data.getCachedAlbums();
}
/**
* Cache all user albums!
*
* @param userId
* id of user
* @param allAlbums
* all user albums collection
*/
protected void cacheAlbums(String userId, List allAlbums) {
if (StringUtils.isEmpty(userId))
return;
if (allAlbums == null || allAlbums.isEmpty())
return;
UserMediaData data = getUserMediaData(userId);
data.addAllAlbums(allAlbums);
// updating Albums cache!!!
for (AlbumBO album : allAlbums)
albumsCache.put(album.getId(), album.getUserId());
}
// ###### Album related methods!!! -- END
// ###### PHOTO related methods!!! -- START
/**
* Returns cached {@link PhotoBO} , with selected id.
*
* @param photoId
* id of photo
* @return {@link PhotoBO}
*/
protected PhotoBO getPhotoById(final long photoId) {
String photoOwnerId = photosCache.get(photoId);
if (StringUtils.isEmpty(photoOwnerId))
// nothing is cached
return null;
return getCachedPhoto(photoOwnerId, photoId);
}
/**
* Return photo with selected id if such was cached.
*
* @param userId
* id of album owner
* @param photoId
* id of photo
* @return PhotoBO
*/
private PhotoBO getCachedPhoto(final String userId, final long photoId) {
if (StringUtils.isEmpty(userId))
return null;
if (photoId <= 0)
return null;
UserMediaData data = userDataCache.get(userId);
if (data == null)
return null;
return data.getPhotoById(photoId);
}
/**
* /** Put/update {@link AlbumBO} in cache.
*
* @param toCache
* {@link AlbumBO}
*/
protected void updateItem(PhotoBO toCache) {
if (toCache == null)
return;
photosCache.put(toCache.getId(), toCache.getUserId());
updateUserData(toCache);
}
/**
* Update/Add {@link AlbumBO} to cached user data. If no data exists - it will be created.
*
* @param toCache
* {@link AlbumBO} item to cache
*/
protected void updateUserData(final PhotoBO toCache) {
final String userId = toCache.getUserId();
if (StringUtils.isEmpty(userId))
return;
UserMediaData data = getUserMediaData(userId);
data.addDataItem(toCache);
}
/**
* Remove {@link AlbumBO} from all caches!
*
* @param toRemove
* {@link AlbumBO}
*/
protected void removeItem(final PhotoBO toRemove) {
if (toRemove == null)
return;
photosCache.remove(toRemove.getId());
removeItemFromCachedUserData(toRemove);
}
/**
* Remove {@link PhotoBO} from cached UserData if it's present.
*
* @param toRemove
* {@link PhotoBO}
*/
private void removeItemFromCachedUserData(final PhotoBO toRemove) {
final String userId = toRemove.getUserId();
if (StringUtils.isEmpty(userId))
return;
UserMediaData data = userDataCache.get(userId);
if (data == null)
return;
data.removeItem(toRemove);
}
/**
* Update {@link ApprovalStatus} for selected photos, in case when those photos are cached.
*
* @param statuses
* Photo id to ApprovalStatus collection
*/
protected void updatePhotoApprovalsStatuses(final Map statuses) {
for (Long photoId : statuses.keySet()) {
ApprovalStatus status = statuses.get(photoId);
if (photoId != null && status != null) {
String ownerId = photosCache.get(photoId);
// only if user exists
if (!StringUtils.isEmpty(ownerId))
updatePhotoApprovalsStatuses(ownerId, photoId, status);
}
}
}
/**
* Update PhotoApproval status in cache.
*
* @param userId
* id of user
* @param photoId
* id of photo
* @param status
* {@link ApprovalStatus} status to set
*/
private void updatePhotoApprovalsStatuses(String userId, long photoId, ApprovalStatus status) {
if (StringUtils.isEmpty(userId)) {
LOG.debug("Illegal incoming parameters userId[" + userId + "]");
return;
}
UserMediaData data = userDataCache.get(userId);
if (data == null)
return;
if (photoId <= 0) {
LOG.debug("Illegal incoming parameters photoId[" + userId + "]");
return;
}
PhotoBO photo = data.getPhotoById(photoId);
// if status is current - leaving !
if (photo.getApprovalStatus() == status)
return;
// remove photo!
data.removeItem(photo);
// status update;
photo.setApprovalStatus(status);
data.addDataItem(photo);
}
/**
* Return {@link PhotoBO} with selected id's.
*
* @param userId
* id of the user
* @param albumId
* id's of photos
* @return List with selected id's
*/
protected List getAllAlbumPhotos(final String userId, final long albumId) {
if (StringUtils.isEmpty(userId)) {
LOG.debug("Illegal incoming parameters userId[" + userId + "]");
return null;
}
if (albumId <= 0) {
LOG.warn("Illegal incoming parameters albumId[" + albumId + "]");
return null;
}
UserMediaData data = userDataCache.get(userId);
if (data == null)
return null;
return data.getAllPhotosFromAlbum(albumId);
}
/**
* Add to cache all photos from selected album! NOTE - if all this photos are already in cache - they will be replaced!
*
* @param userId
* id of user
* @param albumId
* id of album
* @param allPhotos
* all user photos collection
*/
protected void addAlbumPhotosToCache(final String userId, final long albumId, final List allPhotos) {
if (StringUtils.isEmpty(userId)) {
LOG.debug("Illegal incoming parameters userId[" + userId + "]");
return;
}
if (albumId <= 0) {
LOG.debug("Illegal incoming parameters albumId[" + albumId + "]");
return;
}
if (allPhotos == null || allPhotos.isEmpty()) {
LOG.debug("Illegal incoming parameters photosCollection[" + allPhotos + "]");
return;
}
UserMediaData data = getUserMediaData(userId);
data.putPhotosPhotosToCache(albumId, allPhotos);
// updating Albums cache!!!
for (PhotoBO photo : allPhotos)
photosCache.put(photo.getId(), photo.getUserId());
}
// ###### PHoTO related methods!!! -- END
private UserMediaData getUserMediaData(final String userId) {
if (StringUtils.isEmpty(userId))
return null;
UserMediaData data = userDataCache.get(userId);
synchronized (lock) {
data = userDataCache.get(userId);
if (data == null) {
data = new UserMediaData(userId);
userDataCache.put(userId, data);
}
}
return data;
}
/**
* User media data class. Internal bean for User Albums, Photos, and few other properties hold.
*/
private static class UserMediaData {
/**
* Id of the user.
*/
private String userId;
/**
* Default user album.
*/
private Long defaultAlbumId;
/**
* User albums.
*/
private Map userAlbums;
/**
* User photos.
*/
private Map userPhotos;
/**
* True - if all albums for some user were loaded from storage.
*/
private boolean isAllUserAlbumsLoaded;
/**
* Contains album id to Boolean mapping. If value for some album is TRUE- this means that all pictures where load to cache!
*/
private Map readAllAlbumPhotosPermission = new ConcurrentHashMap();
/**
* Constructor.
*
* @param userId
* id of the user
*/
public UserMediaData(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
/**
* Add album to User cached data. Album well be cloned inside.
*
* @param album
* {@link AlbumBO}
*/
protected void addDataItem(AlbumBO album) {
if (userAlbums == null)
userAlbums = new ConcurrentHashMap();
userAlbums.put(album.getId(), album.clone());
if (album.isDefault())
defaultAlbumId = album.getId();
}
/**
* Add photo to User cached data. Photo will be cloned inside.
*
* @param photo
* {@link PhotoBO}
*/
protected void addDataItem(PhotoBO photo) {
if (userPhotos == null)
userPhotos = new ConcurrentHashMap();
userPhotos.put(photo.getId(), photo.clone());
}
/**
* Return album with selected id - if such exists. If result exists - it will be cloned before return.
*
* @param albumId
* id of album
* @return {@link AlbumBO}
*/
protected AlbumBO getAlbumById(long albumId) {
if (userAlbums == null)
return null;
AlbumBO result = userAlbums.get(albumId);
// clone before return
if (result != null)
return result.clone();
return null;
}
/**
* Return photo with selected id - if such exists. If result exists - it will be cloned before return.
*
* @param photoId
* id of photo
* @return {@link PhotoBO}
*/
protected PhotoBO getPhotoById(long photoId) {
if (userPhotos == null)
return null;
PhotoBO result = userPhotos.get(photoId);
// clone before return
if (result != null)
return result.clone();
return null;
}
/**
* Remove {@link AlbumBO} from Albums. If current Album is default, defaultAlbumId will be reset.
*
* @param toRemove
* {@link AlbumBO}
*/
protected void removeItem(AlbumBO toRemove) {
// removing if default
if (toRemove.isDefault())
defaultAlbumId = null;
if (userAlbums == null)
return;
userAlbums.remove(toRemove.getId());
// remove read all photos permission
readAllAlbumPhotosPermission.remove(toRemove.getId());
}
/**
* Remove {@link PhotoBO} from cached photos.
*
* @param toRemove
* {@link PhotoBO}
*/
protected void removeItem(PhotoBO toRemove) {
if (userPhotos == null)
return;
userPhotos.remove(toRemove.getId());
}
/**
* Returns default user album {@link AlbumBO}.
*
* @return {@link AlbumBO}
*/
protected AlbumBO getDefaultAlbum() {
if (defaultAlbumId == null)
return null;
return getAlbumById(defaultAlbumId);
}
/**
* Return all cached user albums "all data is cloned!" - if all was cached! In case if "isAllUserAlbumsLoaded" is false - null will be returned. NOTE :
* Collection of cloned elements will be returned.
*
* @return List
*/
protected List getCachedAlbums() {
if (userAlbums == null || !isAllUserAlbumsLoaded)
return null;
List result = new ArrayList();
for (AlbumBO cachedAlbum : userAlbums.values())
result.add(cachedAlbum.clone());
return result;
}
/**
* Return {@link java.util.List} all photos for selected album. If readAllAlbumPhotosPermission does not contain selected AlbumId, or
* permissions - is false, then null will be returned. NUll will be also returned in case when UserPhotos were not yet initialized!
*
* @param albumId
* id of album
* @return {@link java.util.List}
*/
protected List getAllPhotosFromAlbum(final long albumId) {
boolean isLoaded = readAllAlbumPhotosPermission.get(albumId) != null && readAllAlbumPhotosPermission.get(albumId);
// if photos for selected album were not loaded to cache -- returning null!
if (!isLoaded || userPhotos == null)
return null;
List albumPhotos = new ArrayList();
List allPhotos = new ArrayList(userPhotos.values());
for (PhotoBO photo : allPhotos)
if (albumId == photo.getAlbumId())
albumPhotos.add(photo.clone());
return albumPhotos;
}
/**
* Add all user albums to cache.
*
* @param allAlbums
* all user albums collection
*/
protected void addAllAlbums(final List allAlbums) {
for (AlbumBO album : allAlbums)
addDataItem(album);
isAllUserAlbumsLoaded = true;
}
/**
* Put to cache all photos from some selected album.
*
* @param albumId
* id of album
* @param albumPhotos
* all photos form album
*/
protected void putPhotosPhotosToCache(long albumId, final List albumPhotos) {
for (PhotoBO photo : albumPhotos)
addDataItem(photo);
readAllAlbumPhotosPermission.put(albumId, true);
}
@Override
public boolean equals(Object o) {
return this == o || (o instanceof UserMediaData) && UserMediaData.class.cast(o).getUserId().equals(getUserId());
}
@Override
public int hashCode() {
return userId != null ? userId.hashCode() : 0;
}
@Override
public String toString() {
return "UserMediaData{" + "userId=" + userId + ", defaultAlbumId=" + defaultAlbumId + ", userAlbums=" + userAlbums + ", userPhotos=" + userPhotos
+ ", isAllUserAlbumsLoaded=" + isAllUserAlbumsLoaded + ", readAllAlbumPhotosPermission=" + readAllAlbumPhotosPermission + '}';
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy