Using Google Guava For Cache With Timeout – Java

If you need a list to keep your objects for a while and then automatically delete/expire them, you can use caches from Google Guava.

The Guava project contains several of Google’s core libraries that we rely on in our Java-based projects: collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, and so forth.

The latest release is 18.0, released August 25, 2014.

First, let’s write a class based on LoadingCache.

package com;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

public abstract class TimeoutCache {

	public TimeoutCache(int expireInSeconds) {
		init(expireInSeconds);
	}

	private LoadingCache<String, Object> cache = null;

	private void init(int expireInseconds) {
		RemovalListener<String, Object> removalListener = new RemovalListener<String, Object>() {

			public void onRemoval(RemovalNotification<String, Object> removal) {
				if (removal.getCause() == RemovalCause.EXPIRED) {
					processAfterExpire(removal.getKey(), removal.getValue());
				} else if (removal.getCause() == RemovalCause.REPLACED) {
					// TODO if necessary
				} else {
					// TODO if necessary
				}

			}
		};

		// TODO what about expireAfterAccess()? try!
		cache = CacheBuilder.newBuilder().expireAfterWrite(expireInseconds, TimeUnit.SECONDS)
				.removalListener(removalListener).build(new CacheLoader<String, Object>() {

					public Object load(String key) {
						// TODO what about getIfPresent? try!
						return getUnchecked(key);
					}
				});

	}

	private Object getUnchecked(String key) {
		return cache.getUnchecked(key);
	}

	public void add(String key, Object o) {
		cache.put(key, o);
	}

	public void delete(String key) {
		cache.invalidate(key);
	}

	public long getSize() {
		return cache.size();
	}

	public void cleanup() {
		cache.cleanUp();
	}

	public ConcurrentMap<String, Object> getAsMap() {
		return cache.asMap();
	}

	public LoadingCache<String, Object> getCache() {
		return cache;
	}

	public abstract void processAfterExpire(String key, Object obj);

}


This abstract class gives us freedom to create different types of caches with different behavior to decide what is going to happen to expired items.
Now let’s create a class that we can use as timeout list.

package com;

import java.util.Date;

public class TimeoutList extends TimeoutCache {

	public TimeoutList(int expireInSeconds) {
		super(expireInSeconds);
	}

	@Override
	public void processAfterExpire(String key, Object obj) {
		// timeout alert at right time?
		// YES if only you guarantee high-throughput!
		// otherwise NO, timeout alert when cleanup is called!
		// check it:
		// https://code.google.com/p/guava-libraries/wiki/CachesExplained#When_Does_Cleanup_Happen?
		System.out.println("TIMEOUT OCCURED! Key: " + key + ", Date: " + new Date().toString());

	}

}


To test our code, we are going to add an item to our list in each second and hope to see items are expired according to their addition time.

package com;

import java.util.Date;
import java.util.concurrent.ExecutionException;

public class Main {
	private static int EXPIRE_IN_SECONDS = 5; // Timeout after 5 seconds
	private static TimeoutList timeoutList = new TimeoutList(EXPIRE_IN_SECONDS);

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		//add an item each second
		for (int i = 0; i < 5; i++) {
			String key = "myKey_" + i;
			String object = "myObj_" + i;
			timeoutList.add(key, object);
			// TODO what about adding with same key over and over again? try!

			getStats();

			// wait for a second
			Thread.sleep(1000);
		}

		//see how items expire
		for (int i = 0; i < 5; i++) {
			timeoutList.cleanup();
			// TODO what about no cleanup? try!
			// check it:
			// https://code.google.com/p/guava-libraries/wiki/CachesExplained#When_Does_Cleanup_Happen?

			getStats();

			// wait for a second
			Thread.sleep(1000);
			// TODO what about 2 seconds? try!
		}

	}

	private static void getStats() throws ExecutionException {
		System.out.println("-----------------" 
		+ "\n Date: " + new Date().toString() 
		+ "\n List: " + timeoutList.getAsMap() 
		+ "\n Size: " + timeoutList.getSize() 
		+ "\n -----------------");
	}

}


Any it looks like this:

—————–
Date: Sat Aug 29 20:49:03 EEST 2015
List: {myKey_0=myObj_0}
Size: 1
—————–
—————–
Date: Sat Aug 29 20:49:04 EEST 2015
List: {myKey_0=myObj_0, myKey_1=myObj_1}
Size: 2
—————–
—————–
Date: Sat Aug 29 20:49:05 EEST 2015
List: {myKey_0=myObj_0, myKey_2=myObj_2, myKey_1=myObj_1}
Size: 3
—————–
—————–
Date: Sat Aug 29 20:49:06 EEST 2015
List: {myKey_3=myObj_3, myKey_0=myObj_0, myKey_2=myObj_2, myKey_1=myObj_1}
Size: 4
—————–
—————–
Date: Sat Aug 29 20:49:07 EEST 2015
List: {myKey_4=myObj_4, myKey_3=myObj_3, myKey_0=myObj_0, myKey_2=myObj_2, myKey_1=myObj_1}
Size: 5
—————–
TIMEOUT OCCURED! Key: myKey_0, Date: Sat Aug 29 20:49:08 EEST 2015
—————–
Date: Sat Aug 29 20:49:08 EEST 2015
List: {myKey_4=myObj_4, myKey_3=myObj_3, myKey_2=myObj_2, myKey_1=myObj_1}
Size: 4
—————–
TIMEOUT OCCURED! Key: myKey_1, Date: Sat Aug 29 20:49:09 EEST 2015
—————–
Date: Sat Aug 29 20:49:09 EEST 2015
List: {myKey_4=myObj_4, myKey_3=myObj_3, myKey_2=myObj_2}
Size: 3
—————–
TIMEOUT OCCURED! Key: myKey_2, Date: Sat Aug 29 20:49:10 EEST 2015
—————–
Date: Sat Aug 29 20:49:10 EEST 2015
List: {myKey_4=myObj_4, myKey_3=myObj_3}
Size: 2
—————–
TIMEOUT OCCURED! Key: myKey_3, Date: Sat Aug 29 20:49:11 EEST 2015
—————–
Date: Sat Aug 29 20:49:11 EEST 2015
List: {myKey_4=myObj_4}
Size: 1
—————–
TIMEOUT OCCURED! Key: myKey_4, Date: Sat Aug 29 20:49:12 EEST 2015
—————–
Date: Sat Aug 29 20:49:12 EEST 2015
List: {}
Size: 0
—————–


Any criticism is welcome!

Advertisements
Tagged

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: