Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2014 Zach Melamed
   * 
   * Latest version available online at https://github.com/zach-m/tectonica-commons
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.tectonica.kvs;
 
 import java.util.List;
 import java.util.Map;
 
 
 import  com.google.appengine.api.NamespaceManager;
 import  com.google.appengine.api.datastore.Blob;
 import  com.google.appengine.api.datastore.DatastoreService;
 import  com.google.appengine.api.datastore.DatastoreServiceFactory;
 import  com.google.appengine.api.datastore.Entity;
 import  com.google.appengine.api.datastore.EntityNotFoundException;
 import  com.google.appengine.api.datastore.Key;
 import  com.google.appengine.api.datastore.KeyFactory;
 import  com.google.appengine.api.datastore.Query;
 import  com.google.appengine.api.datastore.Query.Filter;
 import  com.google.appengine.api.datastore.Query.FilterOperator;
 import  com.google.appengine.api.datastore.Query.FilterPredicate;
 import  com.google.appengine.api.memcache.MemcacheService;
 import  com.google.appengine.api.memcache.MemcacheServiceFactory;
 
 public class GaeKeyValueStore<V extends Serializableextends AbstractKeyValueStore<String, V>
 {
 	private final DatastoreService ds;
 
 	private final Config<V> config;
 	private final String kind;
 	private final Key ancestor// dummy parent for all entities to guarantee Datastore consistency
 	private final List<GaeIndexImpl<?>> indexes;
 
 	public static class Config<V>
 	{
 		private final Class<V> valueClass;
 		private Serializer<V> serializer;
 		private String namespace;
 		private boolean usingNamespace = false;
 
 		public static <V> Config<V> create(Class<V> valueClass)
 		{
 			return new Config<V>(valueClass);
 		}
 
 		private Config(Class<V> valueClass)
 		{
 			if (valueClass == null)
 				throw new NullPointerException("valueClass");
 			this. = valueClass;
 		}
 
 		public Config<V> withSerializer(Serializer<V> serializer)
 		{
 			this. = serializer;
 			return this;
 		}
 
 		public Config<V> withNamespace(String namespace)
 		{
 			this. = namespace;
 			this. = true;
 			return this;
 		}
 	}
 
 	public GaeKeyValueStore(Config<V> configKeyMapper<String, V> keyMapper)
 	{
 		super(keyMapper);
 		if (config == null)
 			throw new NullPointerException("config");
 		if (config.serializer == null)
 			config.serializer = new JavaSerializer<V>();
 
		this. = config;
		this. = config.valueClass.getSimpleName();
		this. = new ArrayList<>();
		applyNamespace(); // needed before creation of key and initialization of cache
		this. = DatastoreServiceFactory.getDatastoreService();
		this. = KeyFactory.createKey();
		// create cache now that all values (specifically 'kind') are initialized
	}
	protected void initializeCache()
	{
		// prevent execution of the parent's attempt to create cache, we'll call it manually when we're ready
	}
	protected Cache<String, V> createCache()
	{
		// we're returning a memcached-based wrapper here, so the 'kind' property needs to be set
		if (. instanceof JavaSerializer)
			return new JavaSerializeCache();
		return new CustomSerializeCache();
	}
	private Key keyOf(String key)
	{
		return KeyFactory.createKey(key);
	}
	private void applyNamespace()
	{
			NamespaceManager.set(.);
	}

GETTERS /
	protected V dbGet(String key)
	{
		try
		{
			return entityToValue(.get(keyOf(key)));
		}
		catch (EntityNotFoundException e)
		{
			return null;
		}
	}
	{
		return entryIteratorOfQuery(newQuery()); // query without filters = all
	}
	{
		return keyIteratorOfQuery(newQuery().setKeysOnly());
	}
	public Iterator<V> valueIterator()
	{
	}
	{
		if (keys.size() > 30)
			throw new RuntimeException("GAE doesn't support more than 30 at the time, need to break it");
		List<Key> gaeKeys = new ArrayList<>(keys.size());
		for (String key : keys)
			gaeKeys.add(keyOf(key));
		// we define a filter based on the IN operator, which returns values in the order of listing.
		// see: https://cloud.google.com/appengine/docs/java/datastore/queries#Java_Query_structure
		Filter filter = new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.IN, gaeKeys);
		return entryIteratorOfQuery(newQuery().setFilter(filter));
	}
	public boolean containsKey(String key)
	{
		Filter filter = new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.EQUAL, keyOf(key));
		return .prepare(newQuery().setFilter(filter).setKeysOnly()).asIterator().hasNext();
	}

SETTERS (UTILS) /
	protected Modifier<String, V> getModifier(final String keyModificationType purpose)
	{
		// insert, replace and update are all treated the same in GAE: all simply do save()
		return new Modifier<String, V>()
		{
			public V getModifiableValue()
			{
				// we use here same calls as if for read-only value, because in both cases a new instance is deserialized
				// NOTE: if we ever switch to a different implementation, with local objects, this wouldn't work
				return get(keyfalse);
			}
			public void dbPut(V value)
			{
				save(keyvalue);
			}
		};
	}
	{
		return GaeMemcacheLock.getLock( + ":" + keytrue);
	}

SETTERS /
	protected void dbInsert(String key, V value)
	{
		save(keyvalue);
	}

DELETERS /
	protected boolean dbDelete(String key)
	{
		.delete(keyOf(key));
		return true// we don't really know if the key previously existed
	}
	protected int dbDeleteAll()
	{
		int removed = 0;
		for (Entity entity : .prepare(newQuery().setKeysOnly()).asIterable())
		{
			.delete(entity.getKey());
			removed++;
		}
		return removed// an estimate. we have to assume that all keys existed before delete
	}

INDEXES /
	public <F> Index<String, V, F> createIndex(String indexNameIndexMapper<V, F> mapFunc)
	{
		GaeIndexImpl<F> index = new GaeIndexImpl<>(mapFuncindexName);
		.add(index);
		return index;
	}

GAE implementation of an index - simply exposes the Datastore property filters

Author(s):
Zach Melamed
	private class GaeIndexImpl<F> extends Index<String, V, F>
	{
		public GaeIndexImpl(IndexMapper<V, F> mapFuncString name)
		{
			super(mapFuncname);
		}
		public Iterator<KeyValue<String, V>> iteratorOf(F f)
		{
		}
		{
			return keyIteratorOfQuery(newIndexQuery(f).setKeysOnly());
		}
		public Iterator<V> valueIteratorOf(F f)
		{
		}
		private Query newIndexQuery(F f)
		{
			Filter filter = new FilterPredicate(propertyName(), FilterOperator.EQUAL, f);
			return newQuery().setFilter(filter);
		}
		private String propertyName()
		{
		}
		private F getIndexedFieldOf(V value)
		{
			return .getIndexedFieldOf(value);
		}
	}

DATASTORE UTILS /
	private static final String COL_NAME_ENTRY_VALUE = "value";
	private static final String COL_NAME_INDEX_PREFIX = "_i_";
	private static final String BOGUS_ANCESTOR_KEY_NAME = " ";
	private V entityToValue(Entity entity)
	{
		Blob blob = (Blob) entity.getProperty();
		return ..bytesToObj(blob.getBytes(), .);
	}
	private Entity entryToEntity(String key, V value)
	{
		Entity entity = new Entity(key);
		entity.setUnindexedProperty(new Blob(..objToBytes(value)));
		for (GaeIndexImpl<?> index : )
		{
			Object field = (value == null) ? null : index.getIndexedFieldOf(value);
			entity.setProperty(index.propertyName(), field);
		}
		return entity;
	}
	private void save(String key, V value)
	{
		.put(entryToEntity(keyvalue));
	}
	private Query newQuery()
	{
		return new Query().setAncestor();
	}
	private Iterator<KeyValue<String, V>> entryIteratorOfQuery(Query q)
	{
		final Iterator<Entity> iter = .prepare(q).asIterator();
		return new Iterator<KeyValue<String, V>>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public KeyValue<String, V> next()
			{
				final Entity entity = iter.next();
				return new KeyValue<String, V>()
				{
					public String getKey()
					{
						return entity.getKey().getName();
					}
					public V getValue()
					{
						return entityToValue(entity);
					}
				};
			}
			public void remove()
			{
			}
		};
	}
	private Iterator<StringkeyIteratorOfQuery(Query q)
	{
		final Iterator<Entity> iter = .prepare(q).asIterator();
		return new Iterator<String>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public String next()
			{
				return iter.next().getKey().getName();
			}
			public void remove()
			{
			}
		};
	}
	private Iterator<V> valueIteratorOfQuery(Query q)
	{
		final Iterator<Entity> iter = .prepare(q).asIterator();
		return new Iterator<V>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public V next()
			{
				return entityToValue(iter.next());
			}
			public void remove()
			{
			}
		};
	}

SERIALIZATION /
	public static interface Serializer<V>
	{
bytesToObj(byte[] bytesClass<V> clz); // NOTE: if bytes is null, return null
		byte[] objToBytes(V obj); // NOTE: if obj is null, return null
	}
	private static final class JavaSerializer<V> implements Serializer<V>
	{
		public V bytesToObj(byte[] bytesClass<V> clz)
		{
			return SerializeUtil.bytesToObj(bytesclz);
		}
		public byte[] objToBytes(V obj)
		{
			return SerializeUtil.objToBytes(obj);
		}
	}
	public static class KryoSerializer<V> implements Serializer<V>
	{
		public V bytesToObj(byte[] bytesClass<V> clz)
		{
			return KryoUtil.bytesToObj(bytesclz);
		}
		public byte[] objToBytes(V obj)
		{
			return KryoUtil.objToBytes(obj);
		}
	}
	public static class FstSerializer<V> implements Serializer<V>
	{
		public FstSerializer()
		{
			 = FSTConfiguration.createDefaultConfiguration();
			.setShareReferences(false); // no circular references
		}
		@SuppressWarnings("unchecked")
		public V bytesToObj(byte[] bytesClass<V> clz)
		{
			return (bytes == null) ? null : (V) .asObject(bytes);
		}
		public byte[] objToBytes(V obj)
		{
			return (obj == null) ? null : .asByteArray(obj);
		}
	}

CACHE IMPLEMENTATION /
	private abstract class MemcachedBasedCache implements Cache<String, V>
	{
		protected final MemcacheService mc;
		{
			String gaeNS = NamespaceManager.get();
			String mcNS = (gaeNS == null) ?  : gaeNS + "-" + ;
			 = MemcacheServiceFactory.getMemcacheService(mcNS);
		}
	}
	private class JavaSerializeCache extends MemcachedBasedCache
	{
		@SuppressWarnings("unchecked")
		public V get(String key)
		{
			return (V) .get(key);
		}
		@SuppressWarnings("unchecked")
		public Map<String, V> get(Collection<Stringkeys)
		{
			return (Map<String, V>) (Map<String, ?>) .getAll(keys);
		}
		public void put(String key, V value)
		{
			.put(keyvalue);
		}
		public void put(Map<String, V> values)
		{
			.putAll(values);
		}
		public void delete(String key)
		{
			.delete(key);
		}
		public void deleteAll()
		{
			.clearAll();
		}
	};
	private class CustomSerializeCache extends MemcachedBasedCache
	{
		public V get(String key)
		{
			byte[] bytes = (byte[]) .get(key);
		}
		@SuppressWarnings("unchecked")
		public Map<String, V> get(Collection<Stringkeys)
		{
			Map<StringObjectvalues = .getAll(keys);
			Iterator<Entry<StringObject>> iter = values.entrySet().iterator();
			while (iter.hasNext())
			{
				Entry<StringObjectentry = iter.next();
				byte[] bytes = (byte[]) entry.getValue();
			}
			return (Map<String, V>) (Map<String, ?>) values;
		}
		public void put(String key, V value)
		{
			.put(key..objToBytes(value));
		}
		@SuppressWarnings("unchecked")
		public void put(Map<String, V> values)
		{
			// NOTE: we make a huge assumption here, that we can modify the passed map. by doing so we rely on "inside information" that
			// this is harmless given how 'iteratorFor' is implemented
			Iterator<Entry<StringObject>> iter = ((Map<StringObject>) (Map<String, ?>) values).entrySet().iterator();
			while (iter.hasNext())
			{
				Entry<StringObjectentry = iter.next();
				Object value = entry.getValue();
			}
			.putAll(values);
		}
		public void delete(String key)
		{
			.delete(key);
		}
		public void deleteAll()
		{
			.clearAll();
		}
	};
New to GrepCode? Check out our FAQ X