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 java.util.Set;
 
 
 public abstract class AbstractKeyValueStore<K, V> implements KeyValueStore<K, V>
 {
 	protected final KeyMapper<K, V> keyMapper;

creates a new key-value store manager (this doesn't indicate creation of a brand new storage unit (e.g. table), only a creation of this class, which manages a newly-created/pre-existing storage)

Parameters:
keyMapper this optional parameter is suitable in situations where the key of an entry can be inferred from its value directly (as opposed to when the key and value are stored separately). when provided, several convenience methods become applicable
 
 	protected AbstractKeyValueStore(KeyMapper<K, V> keyMapper)
 	{
 		this. = keyMapper;
 	}
 
 	protected void initializeCache()
 	{
 		 = ( != null);
 	}
 
 	/* *********************************************************************************
 	 * 
 	 * GETTERS
 	 * 
 	 * many of the non-abstract methods here offer somewhat of a naive implementation.
 	 * subclasses are welcome to override with their own efficient implementation.
 	 * 
 	 * *********************************************************************************
 	 */

Given a key, retrieves its paired value directly from the storage
 
 	protected abstract V dbGet(K key);

Retrieves the entries indicated by the given keys directly from the storage

See iteratorFor(java.util.Collection,boolean) for the restrictions on the resulting iterator

 
 	protected abstract Iterator<KeyValue<K, V>> dbOrderedIterator(Collection<K> keys);

Returns ALL entries, bypassing cache entirely (read and write)
 
 	public abstract Iterator<KeyValue<K, V>> iterator();
 
 	// ///////////////////////////////////////////////////////////////////////////////////////
 
 	public V get(K key)
 	{
 		return get(keytrue);
 	}
 
 	public V get(K keyboolean cacheResult)
 	{
 		if (!)
			return dbGet(key);
value = .get(key);
		if (value == null)
		{
			value = dbGet(key);
			if (cacheResult && value != null)
				.put(keyvalue);
		}
		return value;
	}

Executes iteratorFor(java.util.Collection,boolean) with postponeCaching set to false
	public Iterator<KeyValue<K, V>> iteratorFor(final Collection<K> keys)
	{
		return iteratorFor(keysfalse);
	}

Returns an iterator for a list of entries whose keys were passed to the method

IMPORTANT: Unlike simple 'SELECT WHERE KEY IN (..)', the list returned here is guaranteed to be at the exact same order of the keys passed. Moreover, if a certain key is passed more than once, so will its corresponding entry. Clearly, if a key doesn't exist in the store, it will be skipped (i.e. the returning list will never contain nulls).

Parameters:
keys ordered collection of keys to lookup (this is not necessarily a set, as keys can appear more than once in the collection)
postponeCaching indicates whether cache entries individually as each is read from the backend storage, or cache them all in a single call at the end of the iteration (use only if you'll certainly iterate through the entire result)
	public Iterator<KeyValue<K, V>> iteratorFor(final Collection<K> keysfinal boolean postponeCaching)
	{
		if (keys.isEmpty())
			return Collections.emptyIterator();
			return dbOrderedIterator(keys);
		// we intend to use cached results of at least some of the keys passed. the general goal is to find out which keys are missing,
		// retrieve them separately from the storage, and then merge into a single list the cached and retrieved entries
		final Map<K, V> cachedValues = .get(keys);
		final Iterator<KeyValue<K, V>> dbIter;
		if (cachedValues.size() == keys.size())
			dbIter = Collections.emptyIterator(); // i.e. all keys were found in cache
		else
		{
			final Collection<K> uncachedKeys;
			if (cachedValues.size() == 0) // i.e. no key was found on cache
				uncachedKeys = keys;
			else
			{
				uncachedKeys = new ArrayList<>();
				for (K key : keys)
					if (!cachedValues.containsKey(key))
						uncachedKeys.add(key);
			}
			if (uncachedKeys.isEmpty())
				dbIter = Collections.emptyIterator(); // possible only when duplicate keys were passed as input
			else
				dbIter = dbOrderedIterator(uncachedKeys);
		}
		return new Iterator<KeyValue<K, V>>()
		{
			private Iterator<K> keysIter = keys.iterator();
			private KeyValue<K, V> dbNext = dbIter.hasNext() ? dbIter.next() : null;
			private KeyValue<K, V> nextItem = null;
			private Map<K, V> toCache = new HashMap<>();
			public boolean hasNext()
			{
				if ( != null)
					return true;
				while (.hasNext())
				{
key = .next();
					// try from db
					if ( != null && .getKey().equals(key))
					{
						// cache it first (or mark for postponed caching)
value = .getValue();
						if (!postponeCaching)
							.put(keyvalue);
						else
							.put(keyvalue);
						// take value and move db-pointer to next entry
						 = dbIter.hasNext() ? dbIter.next() : null;
						return true;
					}
					// try from cache
value = cachedValues.get(key);
					if (value != null)
					{
						 = KvsUtil.keyValueOf(keyvalue);
						return true;
					}
				}
				if (dbIter.hasNext())
					throw new RuntimeException("Internal error in cache-based iteration");
				if (postponeCaching &&  != null)
				{
					 = null;
				}
				return false// i.e. nextVal is null
			}
			public KeyValue<K, V> next()
			{
				if (!hasNext())
				KeyValue<K, V> next = ;
				 = null;
				return next;
			}
			public void remove()
			{
			}
		};
	}
	public Iterator<K> keyIterator()
	{
		final Iterator<KeyValue<K, V>> iter = iterator();
		return new Iterator<K>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public K next()
			{
				return iter.next().getKey();
			}
			public void remove()
			{
			}
		};
	}
	public Iterator<V> valueIterator()
	{
		final Iterator<KeyValue<K, V>> iter = iterator();
		return new Iterator<V>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public V next()
			{
				return iter.next().getValue();
			}
			public void remove()
			{
			}
		};
	}
	public Iterator<V> valueIteratorFor(Collection<K> keys)
	{
		return valueIteratorFor(keysfalse);
	}
	public Iterator<V> valueIteratorFor(Collection<K> keysboolean postponeCaching)
	{
		if (keys.isEmpty())
			return Collections.emptyIterator();
		final Iterator<KeyValue<K, V>> iter = iteratorFor(keyspostponeCaching);
		return new Iterator<V>()
		{
			public boolean hasNext()
			{
				return iter.hasNext();
			}
			public V next()
			{
				return iter.next().getValue();
			}
			public void remove()
			{
			}
		};
	}
	public Set<K> keySet()
	{
		return KvsUtil.iterateInto(keyIterator(), new HashSet<K>());
	}
	public List<V> values()
	{
		return KvsUtil.iterateInto(valueIterator(), new ArrayList<V>());
	}
	public List<V> valuesFor(Collection<K> keys)
	{
		if (keys.isEmpty())
			return Collections.emptyList();
		return KvsUtil.iterateInto(valueIteratorFor(keystrue), new ArrayList<V>());
	}
	public boolean containsKey(K key)
	{
		// NOTE: the implementation here avoid serialization, but does caching. you'll probably want to override..
		return iteratorFor(Collections.singletonList(key)).hasNext();
	}
	/* *********************************************************************************
	 * 
	 * SETTERS (PROTOCOL)
	 * 
	 * *********************************************************************************
	 */

an interface for managing a modification process of an existing entry. there are two types of such modification: both methods are invoked under the concurrency protection a lock provided with KeyValueStore.getModificationLock(java.lang.Object).
	protected interface Modifier<K, V>
	{
Returns an instance that can be safely modified by the caller. During this modification, calls to getValue() will return the unchanged value. If the instance was indeed modified by the caller, and no exception occurred in the process, the method dbPut(java.lang.Object) will be invoked.

NOTE: this method is called only on a locked entry

Makes the changes to an entry permanent. After this method finishes, calls to KeyValueStore.get(java.lang.Object,boolean) will return the updated value.

NOTE: this method is called only on a locked entry

		void dbPut(V value);
	}
	protected static enum ModificationType
	{
		UPDATE, PUT;
	}

required to return a (short-lived) instance of AbstractKeyValueStore.Modifier corresponding to a given key, or a null if the passed key can't be updated (because it doesn't exist, or for another reason). The returned instance is used for a one-time modification.
	protected abstract Modifier<K, V> getModifier(K keyModificationType purpose);

expected to return a global lock for a specific key (global means that it blocks other machines as well, not just the current instance). It is a feature of this framework that only one updater is allowed for an entry at each given moment, so whenever an updater thread starts the (non-atomic) process of updating the entry, all other attempts should be blocked.
	protected abstract Lock getModificationLock(K key);
	/* *********************************************************************************
	 * 
	 * SETTERS
	 * 
	 * *********************************************************************************
	 */
	protected abstract void dbInsert(K key, V value);

inserts a new entry, whose key doesn't already exist in storage. it's a faster and more resource-efficient way to insert entries compared to put(java.lang.Object,java.lang.Object) as it doesn't use any locking. do not use if you're not completely sure whether the key already exists. the behavior of the store in such case is undetermined and implementation-dependent.
	public void add(K key, V value)
	{
		fireEvent(.keyvalue);
		dbInsert(keyvalue);
			.put(keyvalue);
	}

inserts or updates an entry. if you're sure that the entry is new (i.e. its key doesn't already exist), use the more efficient add(java.lang.Object,java.lang.Object) instead
	public void put(K key, V value)
	{
		Lock lock = getModificationLock(key);
		lock.lock();
		try
		{
			Modifier<K, V> modifier = getModifier(key.);
			if (modifier == null)
				add(keyvalue);
			else
			{
				fireEvent(.keyvalue);
				modifier.dbPut(value);
					.put(keyvalue);
			}
		}
		finally
		{
			lock.unlock();
		}
	}

insert and entry only if the key is not yet taken

Returns:
the newly added value, or null if nothing was inserted
	public V putIfAbsent(K keyValueGenerator<K, V> generator)
	{
		Lock lock = getModificationLock(key);
		lock.lock();
		try
		{
			if (containsKey(key))
				return null;
value = generator.generate(key);
			add(keyvalue);
			return value;
		}
		finally
		{
			lock.unlock();
		}
	}
	public V update(K keyUpdater<V> updater)
	{
		Lock lock = getModificationLock(key);
		lock.lock();
		try
		{
			Modifier<K, V> modifier = getModifier(key.);
			if (modifier == null)
			{
				updater.entryNotFound();
				return null;
			}
value = modifier.getModifiableValue();
			if (value == null)
			{
				updater.entryNotFound();
				return null;
			}
			updater.changed = updater.update(value);
			if (updater.changed)
			{
				modifier.dbPut(value);
					.put(keyvalue);
			}
			updater.postPersist(value);
			return value;
		}
		finally
		{
			lock.unlock();
		}
	}
	public int update(Collection<K> keysUpdater<V> updater)
	{
		int count = 0;
		for (K key : keys)
		{
			update(keyupdater);
			if (updater.changed)
				count++;
			if (updater.stopped)
				break;
		}
		return count;
	}
	/* *********************************************************************************
	 * 
	 * SETTERS (CONVENIENCE)
	 * 
	 * *********************************************************************************
	 */

convenience method to update all entries
	public int updateAll(Updater<V> updater)
	{
		return update(keySet(), updater);
	}

convenience method applicable when keyMapper is provided

	public V updateValue(V valueUpdater<V> updater)
	{
		return update(.getKeyOf(value), updater);
	}

convenience method applicable when keyMapper is provided

	public void addValue(V value)
	{
		add(.getKeyOf(value), value);
	}

convenience method applicable when keyMapper is provided

	public void putValue(V value)
	{
		put(.getKeyOf(value), value);
	}
	/* *********************************************************************************
	 * 
	 * DELETERS
	 * 
	 * *********************************************************************************
	 */
	protected abstract boolean dbDelete(K key);
	protected abstract int dbDeleteAll();
	public boolean delete(K key)
	{
		return dbDelete(key);
	}
	public int deleteAll()
	{
		return dbDeleteAll();
	}
	/* *********************************************************************************
	 * 
	 * INDEXES
	 * 
	 * *********************************************************************************
	 */
	public abstract <F> Index<K, V, F> createIndex(String indexNameIndexMapper<V, F> mapFunc);
	/* *********************************************************************************
	 * 
	 * CACHE
	 * 
	 * *********************************************************************************
	 */
	protected Cache<K, V> cache;
	protected boolean usingCache;
	protected static interface Cache<K, V>
	{
get(K key);
		Map<K, V> get(Collection<K> keys);
		void put(K key, V value);
		void put(Map<K, V> values);
		void delete(K key);
		void deleteAll();
	}

overridden by subclasses that wish to support a caching mechanism
	protected Cache<K, V> createCache()
	{
		return null;
	}
	public void clearCache()
	{
	}
	/* *********************************************************************************
	 * 
	 * EVENTS
	 * 
	 * *********************************************************************************
	 */
	public void addListener(EventType typeEventHandler<K, V> handler)
	{
		.put(typehandler);
	}
	protected void fireEvent(EventType type, K key, V value)
	{
		Set<EventHandler<K, V>> events = .get(type);
		if (events != null)
			for (EventHandler<K, V> event : events)
				event.handle(typekeyvalue);
	}
New to GrepCode? Check out our FAQ X