package com.delect.motiver.server.cache; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.delect.motiver.server.jdo.UserOpenid; import com.delect.motiver.server.jdo.nutrition.FoodName; import com.delect.motiver.server.jdo.nutrition.MealJDO; import com.delect.motiver.server.jdo.nutrition.TimeJDO; import com.delect.motiver.server.service.MyServiceImpl; import com.google.appengine.api.memcache.Expiration; import com.google.appengine.api.memcache.MemcacheService; import com.google.appengine.api.memcache.MemcacheServiceFactory; import com.google.appengine.api.memcache.MemcacheService.IdentifiableValue; import com.prodeagle.java.counters.Counter; public class NutritionCache { private final static boolean CACHE_ON = true; private final static String PREFIX_FOOD_NAME = "nca_en1_"; private final static String PREFIX_FOOD_NAMES = "nca_en2_"; private final static String PREFIX_FOOD_NAME_COUNT = "nca_fn_c"; private final static String PREFIX_TIMES = "nca_fn_t"; private final static String PREFIX_TIME = "nca_t"; private final static String PREFIX_MEAL = "nca_m"; private final static int CACHE_EXPIRE_SECONDS = 604800; private SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd"); /** * Logger for this class */ private static final Logger logger = Logger.getLogger(NutritionCache.class.getName()); private static final Expiration DEFAULT_EXPIRATION = Expiration.byDeltaSeconds(CACHE_EXPIRE_SECONDS); MemcacheService cache = MemcacheServiceFactory.getMemcacheService(); private static NutritionCache nutritionCache; @SuppressWarnings("unchecked") public static NutritionCache getInstance() { if(nutritionCache == null) { nutritionCache = new NutritionCache(); } return nutritionCache; } public List<TimeJDO> getTimes(String uid, Date date) { if(cache == null || !CACHE_ON) { return null; } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_TIMES); builder.append("_"); builder.append(uid); builder.append("_"); builder.append(fmt.format(date)); builder.append("_"); Object obj = cache.get(builder.toString()); List<TimeJDO> times = null; if(obj instanceof Map) { //prodeagle counter Counter.increment("Cache.Times"); times = new ArrayList<TimeJDO>(); Map<Long, TimeJDO> map = (Map<Long, TimeJDO>)obj; Collection<TimeJDO> c = map.values(); Iterator<TimeJDO> itr = c.iterator(); while(itr.hasNext()) { times.add(itr.next()); } } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded times ("+uid+", "+date+"): "+times); } return times; } public void setTimes(String uid, Date date, List<TimeJDO> list) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Adding times ("+uid+", "+date+"): "+list); } Map<Long, TimeJDO> map = null; if(list != null) { map = new HashMap<Long, TimeJDO>(); for(TimeJDO t : list) { map.put(t.getId(), t); } } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_TIMES); builder.append("_"); builder.append(uid); builder.append("_"); builder.append(fmt.format(date)); builder.append("_"); cache.put(builder.toString(), map, DEFAULT_EXPIRATION); } public TimeJDO getTime(Long timeId) { if(cache == null || !CACHE_ON) { return null; } //time StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_TIME); builder.append(timeId); Object obj = cache.get(builder.toString()); TimeJDO t = null; if(obj != null && obj instanceof TimeJDO) { //prodeagle counter Counter.increment("Cache.Time"); t = (TimeJDO)obj; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded single time ("+timeId+"): "+t); } return t; } public MealJDO getMeal(Long mealId) { if(cache == null || !CACHE_ON) { return null; } //meal StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_MEAL); builder.append(mealId); Object obj = cache.get(builder.toString()); MealJDO t = null; if(obj != null && obj instanceof MealJDO) { //prodeagle counter Counter.increment("Cache.Meal"); t = (MealJDO)obj; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded single meal ("+mealId+"): "+t); } return t; } public void addMeal(MealJDO meal) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Saving single meal: "+meal); } //meal StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_MEAL); builder.append(meal.getId()); String s = builder.toString(); boolean ok = false; while(!ok) { IdentifiableValue oldValue = cache.getIdentifiable(s); if(oldValue != null) ok = cache.putIfUntouched(s, oldValue, meal, DEFAULT_EXPIRATION); else { cache.put(s, meal, DEFAULT_EXPIRATION); ok = true; } } } public void removeMeal(Long mealId) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Removing single meal: "+mealId); } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_MEAL); builder.append(mealId); cache.delete(builder.toString()); } @SuppressWarnings("unchecked") public Map<Long, String> getFoodNames(String locale) { if(cache == null || !CACHE_ON) { return null; } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAMES); builder.append(locale); builder.append("_"); Object obj = cache.get(builder.toString()); Map<Long, String> names = null; if(obj instanceof Map) { //prodeagle counter Counter.increment("Cache.FoodNames"); names = (Map<Long, String>)obj; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded food names: "+((names != null)? names.size() : 0)); } return names; } @SuppressWarnings("unchecked") public void updateFoodNames(String locale, FoodName name) { if(cache == null || !CACHE_ON) { return; } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAMES); builder.append(locale); builder.append("_"); String s = builder.toString(); //get old value boolean ok = false; while(!ok) { IdentifiableValue oldValue = cache.getIdentifiable(s); if(oldValue != null) { Map<Long, String> names = (Map<Long, String>) oldValue.getValue(); names.put(name.getId(), name.getName()); ok = cache.putIfUntouched(s, oldValue, names, DEFAULT_EXPIRATION); } else ok = true; } } public void setFoodNames(String locale, Map<Long, String> map) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Saving food names: "+map.size()); } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAMES); builder.append(locale); builder.append("_"); String s = builder.toString(); boolean ok = false; while(!ok) { IdentifiableValue oldValue = cache.getIdentifiable(s); if(oldValue != null) ok = cache.putIfUntouched(s, oldValue, map, DEFAULT_EXPIRATION); else { cache.put(s, map, DEFAULT_EXPIRATION); ok = true; } } } public int getFoodNameCount(UserOpenid user, Long id) { if(cache == null || !CACHE_ON) { return -1; } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAME_COUNT); builder.append(id); builder.append("_"); builder.append(user.getUid()); Object obj = cache.get(builder.toString()); int c = (obj != null)? (Integer)obj : -1; if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded food name count ("+user.getUid()+", "+id+"): "+c); } return c; } public void setFoodNameCount(UserOpenid user, Long id, int count) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Saving food name count ("+user.getUid()+", "+id+", "+count+")"); } StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAME_COUNT); builder.append(id); builder.append("_"); builder.append(user.getUid()); cache.put(builder.toString(), count, DEFAULT_EXPIRATION); } public FoodName getFoodName(Long key) { if(cache == null || !CACHE_ON) { return null; } //workout StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAME); builder.append(key); Object obj = cache.get(builder.toString()); FoodName t = null; if(obj != null && obj instanceof FoodName) { //prodeagle counter Counter.increment("Cache.FoodName"); t = (FoodName)obj; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Loaded food name ("+key+"): "+t); } return t; } public void addFoodName(FoodName jdo) { if(cache == null || !CACHE_ON) { return; } if(logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Saving single food name: "+jdo); } //food name StringBuilder builder = MyServiceImpl.getStringBuilder(); builder.append(PREFIX_FOOD_NAME); builder.append(jdo.getId()); String s = builder.toString(); boolean ok = false; while(!ok) { IdentifiableValue oldValue = cache.getIdentifiable(s); if(oldValue != null) ok = cache.putIfUntouched(s, oldValue, jdo, DEFAULT_EXPIRATION); else { cache.put(s, jdo, DEFAULT_EXPIRATION); ok = true; } } //add to "search" cache updateFoodNames(jdo.getLocale(), jdo); } }