/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.smartitengineering.user.service.impl.cache;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.smartitengineering.dao.common.cache.CacheServiceProvider;
import com.smartitengineering.dao.common.cache.Lock;
import com.smartitengineering.dao.common.cache.Mutex;
import com.smartitengineering.dao.common.cache.impl.CacheAPIFactory;
import com.smartitengineering.user.domain.User;
import com.smartitengineering.user.filter.UserFilter;
import com.smartitengineering.user.service.UserService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author imyousuf
*/
public class UserServiceCacheImpl implements UserService {
@Inject
@Named("primaryService")
private UserService primaryService;
@Inject
private CacheServiceProvider<Long, User> cacheProvider;
@Inject
private CacheServiceProvider<String, Long> nameCacheProvider;
private transient final Logger logger = LoggerFactory.getLogger(getClass());
private final Mutex<Long> mutex = CacheAPIFactory.<Long>getMutex();
@Override
public void save(User user) {
//Simply delegate
primaryService.save(user);
}
@Override
public void update(User user) {
//First update then delete if update successful!
try {
primaryService.update(user);
expireFromCache(user);
}
catch (RuntimeException exception) {
logger.info("Could not update thus invalidate cache!", exception);
throw exception;
}
}
@Override
public void delete(User user) {
//First update then delete if update successful!
try {
primaryService.delete(user);
expireFromCache(user);
}
catch (RuntimeException exception) {
logger.info("Could not update thus invalidate cache!", exception);
throw exception;
}
}
@Override
public User getById(Long id) {
//Check cache first
User user = cacheProvider.retrieveFromCache(id);
if (user != null) {
return user;
}
else {
try {
Lock<Long> lock = mutex.acquire(id);
user = cacheProvider.retrieveFromCache(id);
if (user != null) {
return user;
}
user = primaryService.getById(id);
if (user != null) {
putToCache(user);
}
mutex.release(lock);
}
catch (Exception ex) {
logger.warn("Could not do cache lookup!", ex);
}
return user;
}
}
@Override
public Set<User> getUsersByIds(Long... userId) {
return getUsersByIds(Arrays.asList(userId));
}
@Override
public Set<User> getUsersByIds(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptySet();
}
Map<Long, User> results = new HashMap<Long, User>(ids.size());
List<Long> missedIds = new ArrayList<Long>(ids);
results.putAll(cacheProvider.retrieveFromCache(missedIds));
for (Long id : results.keySet()) {
missedIds.remove(id);
}
Map<Long, Lock<Long>> locks = new HashMap<Long, Lock<Long>>(missedIds.size());
for (Long missedId : missedIds) {
boolean attained = false;
while (!attained) {
try {
locks.put(missedId, mutex.acquire(missedId));
attained = true;
}
catch (Exception ex) {
logger.warn("Could not acquire lock for user!");
}
}
}
results.putAll(cacheProvider.retrieveFromCache(missedIds));
for (Long id : results.keySet()) {
if (missedIds.remove(id)) {
mutex.release(locks.get(id));
}
}
Set<User> fromSource = primaryService.getUsersByIds(missedIds);
for (User user : fromSource) {
putToCache(user);
results.put(user.getId(), user);
}
for (Long id : missedIds) {
mutex.release(locks.get(id));
}
LinkedHashSet<User> resultSet = new LinkedHashSet<User>(results.size());
for (Long id : ids) {
User user = results.get(id);
if (user != null) {
resultSet.add(user);
}
}
return resultSet;
}
@Override
public User getUserByUsername(String usernameWithOrganizationName) {
String username;
String organizationName;
StringTokenizer tokenizer = new StringTokenizer(usernameWithOrganizationName, "@");
if (tokenizer.hasMoreTokens()) {
username = tokenizer.nextToken();
}
else {
username = "";
}
if (tokenizer.hasMoreTokens()) {
organizationName = tokenizer.nextToken();
}
else {
organizationName = "";
}
User user = getUserByOrganizationAndUserName(organizationName, username);
return user;
}
@Override
public User getUserByOrganizationAndUserName(String organizationShortName, String userName) {
Long id = nameCacheProvider.retrieveFromCache(getNameCacheKey(userName, organizationShortName));
if (id != null) {
User user = getById(id);
if (user != null) {
return user;
}
}
final User userByOrganizationAndUserName =
primaryService.getUserByOrganizationAndUserName(organizationShortName, userName);
if (userByOrganizationAndUserName != null) {
try {
Lock<Long> lock = mutex.acquire(userByOrganizationAndUserName.getId());
putToCache(userByOrganizationAndUserName);
mutex.release(lock);
}
catch (Exception ex) {
logger.warn("Could not do cache lookup!", ex);
}
}
return userByOrganizationAndUserName;
}
@Override
public Collection<User> search(UserFilter filter) {
return primaryService.search(filter);
}
@Override
public Collection<User> getAllUser() {
return primaryService.getAllUser();
}
@Override
public Collection<User> getUsers(String userNameLike, String userName, boolean isSmallerThan, int count) {
return primaryService.getUsers(userNameLike, userName, isSmallerThan, count);
}
@Override
public Collection<User> getUserByOrganization(String organizationName) {
return primaryService.getUserByOrganization(organizationName);
}
@Override
public Collection<User> getUserByOrganization(String organizationName, String userName, boolean isSmallerThan,
int count) {
return primaryService.getUserByOrganization(organizationName, userName, isSmallerThan, count);
}
@Override
public void validateUser(User user) {
primaryService.validateUser(user);
}
private String getNameCacheKey(User user) {
final String username = user.getUsername();
final String uniqueShortName = user.getOrganization().getUniqueShortName();
return getNameCacheKey(username, uniqueShortName);
}
protected String getNameCacheKey(final String username, final String uniqueShortName) {
return new StringBuilder("user:").append(username).append(':').append(uniqueShortName).toString();
}
private void putToCache(User user) {
cacheProvider.putToCache(user.getId(), user);
nameCacheProvider.putToCache(getNameCacheKey(user), user.getId());
}
private void expireFromCache(User user) {
if (cacheProvider.containsKey(user.getId())) {
User oldUser = getById(user.getId());
if (oldUser != null) {
nameCacheProvider.expireFromCache(getNameCacheKey(oldUser));
}
cacheProvider.expireFromCache(oldUser.getId());
}
}
}