package com.googlecode.objectify.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Transaction;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.Query;
/**
* Extends the ObjectifyImpl to add a session cache. Note that it only needs
* to override a few key methods.
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
public class SessionCachingObjectifyImpl extends ObjectifyImpl
{
/** Value which gets put in the cache for negative results */
protected static final Object NEGATIVE_RESULT = new Object();
/** The cache is a simple hashmap */
protected Map<Key<?>, Object> cache = new HashMap<Key<?>, Object>();
/**
* Protected constructor creates a wrapper on the datastore with
* the specified txn.
*
* @param txn can be null to not use transactions.
*/
public SessionCachingObjectifyImpl(ObjectifyFactory fact, DatastoreService ds, Transaction txn)
{
super(fact, ds, txn);
}
/* (non-Javadoc)
* @see com.google.code.objectify.Objectify#get(java.lang.Iterable)
*/
@Override
@SuppressWarnings("unchecked")
public <T> Map<Key<T>, T> get(Iterable<? extends Key<? extends T>> keys)
{
List<Key<? extends T>> needFetching = new ArrayList<Key<? extends T>>();
for (Key<? extends T> key: keys)
if (!this.cache.containsKey(key))
needFetching.add(key);
Map<Key<T>, T> fetched = super.get(needFetching);
Map<Key<T>, T> result = new LinkedHashMap<Key<T>, T>();
for (Key<? extends T> key: keys)
{
T t = (T)this.cache.get(key);
if (t != null)
{
if (t != NEGATIVE_RESULT)
result.put((Key<T>)key, t);
}
else
{
t = fetched.get(key);
if (t != null)
{
result.put((Key<T>)key, t);
this.cache.put(key, t);
}
else
{
this.cache.put(key, NEGATIVE_RESULT);
}
}
}
return result;
}
/* (non-Javadoc)
* @see com.googlecode.objectify.Objectify#find(com.google.appengine.api.datastore.Key)
*/
@Override
@SuppressWarnings("unchecked")
public <T> T find(Key<? extends T> key)
{
T t = (T)this.cache.get(key);
if (t != null)
{
if (t == NEGATIVE_RESULT)
return null;
else
return t;
}
else
{
t = super.find(key);
if (t == null)
this.cache.put(key, NEGATIVE_RESULT);
else
this.cache.put(key, t);
return t;
}
}
/* (non-Javadoc)
* @see com.google.code.objectify.Objectify#put(java.lang.Object)
*/
@Override
public <T> Key<T> put(T obj)
{
Key<T> key = super.put(obj);
this.cache.put(key, obj);
return key;
}
/* (non-Javadoc)
* @see com.google.code.objectify.Objectify#put(java.lang.Iterable)
*/
@Override
public <T> Map<Key<T>, T> put(Iterable<? extends T> objs)
{
Map<Key<T>, T> result = super.put(objs);
this.cache.putAll(result);
return result;
}
/* (non-Javadoc)
* @see com.googlecode.objectify.Objectify#delete(java.lang.Object)
*/
@Override
public void delete(Object keyOrEntity)
{
super.delete(keyOrEntity);
Key<?> key = this.getFactory().getKey(keyOrEntity);
this.cache.put(key, NEGATIVE_RESULT);
}
/* (non-Javadoc)
* @see com.google.code.objectify.Objectify#delete(java.lang.Iterable)
*/
@Override
public void delete(Iterable<?> keysOrEntities)
{
super.delete(keysOrEntities);
for (Object obj: keysOrEntities)
this.cache.put(this.getFactory().getKey(obj), NEGATIVE_RESULT);
}
/* (non-Javadoc)
* @see com.googlecode.objectify.Objectify#query()
*/
@Override
public <T> Query<T> query()
{
return new SessionCachingQueryImpl<T>(this.factory, this, this.cache);
}
/* (non-Javadoc)
* @see com.googlecode.objectify.Objectify#query(java.lang.Class)
*/
@Override
public <T> Query<T> query(Class<T> clazz)
{
return new SessionCachingQueryImpl<T>(this.factory, this, this.cache, clazz);
}
}