All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.anthavio.cache.ConfiguredCacheLoader Maven / Gradle / Ivy

The newest version!
package net.anthavio.cache;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author martin.vanek
 *
 */
public class ConfiguredCacheLoader extends CacheEntryLoader {

	/**
	 * What to log on entry load exception
	 */
	public static enum LogErrorAs {
		NOTHING, MESSAGE, STACKTRACE;
	}

	/**
	 * What to return on missing entry load error
	 */
	public static enum MissingReturn {
		EXCEPTION, //throw CacheLoaderException 
		NULL; // return null
	}

	/**
	 * What to return on expired entry load error
	 */
	public static enum ExpiredReturn {
		EXCEPTION, //throw CacheLoaderException 
		EXPIRED, // return expired entry
		NULL; // return null
	}

	/**
	 * What to do with returned value
	 */
	public static enum CacheReturned {
		DONT, // do not cache 
		EXPIRED, // cache as soft expired entry
		REQUEST; // cache as fresh entry from request TTLs
	}

	/**
	 * Recipe what to do on exception when loading missing cache value (cached value NOT available) 
	 * 
	 * @author martin.vanek
	 *
	 */
	public static class MissingFailedRecipe {

		/**
		 * Preconfigured: dont log & throw exception
		 */
		public static final MissingFailedRecipe SYNC_STRICT = new MissingFailedRecipe(LogErrorAs.NOTHING,
				MissingReturn.EXCEPTION, CacheReturned.DONT);

		/**
		 * Preconfigured: log stacktrace & return null & dont cache
		 */
		public static final MissingFailedRecipe ASYN_STRICT = new MissingFailedRecipe(LogErrorAs.STACKTRACE,
				MissingReturn.NULL, CacheReturned.DONT);

		/**
		 * Preconfigured: log mesage & return null & dont cache
		 */
		public static final MissingFailedRecipe SYNC_NULL = new MissingFailedRecipe(LogErrorAs.MESSAGE, MissingReturn.NULL,
				CacheReturned.DONT);

		private final LogErrorAs logAs;

		private final MissingReturn returnAs;

		private final CacheReturned cacheAs;

		public MissingFailedRecipe(LogErrorAs logAs, MissingReturn returnAs, CacheReturned cacheAs) {
			if (logAs == null) {
				throw new IllegalArgumentException("Null LogErrorAs");
			}
			this.logAs = logAs;

			if (returnAs == null) {
				throw new IllegalArgumentException("Null MissingReturn");
			}
			this.returnAs = returnAs;

			if (cacheAs == null) {
				throw new IllegalArgumentException("Null CacheReturned");
			}
			this.cacheAs = cacheAs;
		}

		public LogErrorAs getLogAs() {
			return logAs;
		}

		public MissingReturn getReturnAs() {
			return returnAs;
		}

		public CacheReturned getCacheAs() {
			return cacheAs;
		}

	}

	/**
	 * Recipe what to do on exception when loading soft expired cache value (cached value IS available)
	 * 
	 * @author martin.vanek
	 *
	 */
	public static class ExpiredFailedRecipe {

		public static final ExpiredFailedRecipe SYNC_STRICT = new ExpiredFailedRecipe(LogErrorAs.NOTHING,
				ExpiredReturn.EXCEPTION, CacheReturned.DONT);

		public static final ExpiredFailedRecipe SYNC_RETURN = new ExpiredFailedRecipe(LogErrorAs.MESSAGE,
				ExpiredReturn.EXPIRED, CacheReturned.DONT);

		public static final ExpiredFailedRecipe ASYN_STRICT = new ExpiredFailedRecipe(LogErrorAs.STACKTRACE,
				ExpiredReturn.EXPIRED, CacheReturned.DONT);

		private final LogErrorAs logErrorAs;

		private final ExpiredReturn returnAs;

		private final CacheReturned cacheAs;

		public ExpiredFailedRecipe(LogErrorAs logOn, ExpiredReturn returnOn, CacheReturned cacheOn) {
			this.logErrorAs = logOn;
			this.returnAs = returnOn;
			this.cacheAs = cacheOn;
		}

		public LogErrorAs getLogErrorAs() {
			return logErrorAs;
		}

		public ExpiredReturn getReturnAs() {
			return returnAs;
		}

		public CacheReturned getCacheAs() {
			return cacheAs;
		}

	}

	/**
	 * @author martin.vanek
	 *
	 * @param 
	 */
	public static interface SimpleLoader {

		public V load() throws Exception;
	}

	private final Logger logger;

	private final SimpleLoader loader;

	protected final MissingFailedRecipe msettings;

	protected final ExpiredFailedRecipe esettings;

	/**
	 * Create with default exception settings 
	 */
	public ConfiguredCacheLoader(SimpleLoader loader) {
		this(loader, MissingFailedRecipe.SYNC_STRICT, ExpiredFailedRecipe.SYNC_STRICT);
	}

	/**
	 * Create with custom exception settings
	 * 
	 */
	public ConfiguredCacheLoader(SimpleLoader loader, MissingFailedRecipe missSettings, ExpiredFailedRecipe expSettings) {
		this.loader = loader;
		this.logger = LoggerFactory.getLogger(loader.getClass());
		this.msettings = missSettings;
		this.esettings = expSettings;
	}

	/**
	 * Executed on cache miss - Expired value does not exist
	 * 
	 * @param request - original request
	 * @param asyn - flag indicating asynchronous execution of this method
	 * @return result to be returned and possibly cached
	 * @throws Exception
	 */
	protected V loadMissing(CacheLoadRequest request, boolean asyn) throws Exception {
		return loader.load();
	}

	/**
	 * Executed on cache expired hit - Expired value exists
	 * 
	 * @param request - original request
	 * @param asyn - flag indicating asynchronous execution of this method
	 * @param expiredEntry - previous expired cache entry with value
	 * @return result to be returned and possibly cached
	 * @throws Exception
	 */
	protected V loadExpired(CacheLoadRequest request, boolean asyn, CacheEntry expiredEntry) throws Exception {
		return loader.load();
	}

	@Override
	protected CacheEntryLoadResult load(CacheLoadRequest request, boolean async, CacheEntry expiredEntry) {
		try {
			//return load();
			V value;
			if (expiredEntry == null) {
				value = loadMissing(request, async);
			} else {
				value = loadExpired(request, async, expiredEntry);
			}
			return new CacheEntryLoadResult(true, value, request.getHardTtl(), request.getSoftTtl());
		} catch (Exception x) {
			if (expiredEntry == null) {
				//MissingErrorSettings msettings = async ? asynMissing : syncMissing;
				logException(x, request, async);
				return getMissingResult(x, msettings, request);
			} else {
				//ExpiredErrorSettings esettings = async ? asynExpired : syncExpired;
				logException(x, request, async);
				return getExpiredResult(x, esettings, request, expiredEntry);
			}
		}
	}

	protected void logException(Exception exception, CacheLoadRequest request, boolean async) {
		switch (msettings.logAs) {
		case NOTHING:
			break;
		case MESSAGE:
			logger.warn("Load failed " + request + " " + exception);
			break;
		case STACKTRACE:
			logger.warn("Load failed " + request, exception);
			break;
		default:
			throw new IllegalStateException("Unsupported switch value " + msettings.logAs);
		}

	}

	/**
	 * @param exception
	 * @param settings
	 * @param request
	 * @return
	 */
	protected CacheEntryLoadResult getMissingResult(Exception exception, MissingFailedRecipe settings,
			CacheLoadRequest request) {
		V value;
		switch (settings.returnAs) {
		case EXCEPTION:
			throw new CacheLoaderException(exception, request);
		case NULL:
			value = null;
			break;
		default:
			throw new IllegalStateException("Unsupported switch value " + settings.returnAs);
		}

		CacheEntryLoadResult entry;
		switch (settings.cacheAs) {
		case DONT:
			entry = new CacheEntryLoadResult(false, value, request.getHardTtl(), request.getSoftTtl());
			break;
		case EXPIRED:
			entry = new CacheEntryLoadResult(true, value, request.getHardTtl(), -1);
			break;
		case REQUEST:
			entry = new CacheEntryLoadResult(true, value, request.getHardTtl(), request.getSoftTtl());
			break;
		default:
			throw new IllegalStateException("Unsupported switch value " + settings.cacheAs);
		}
		return entry;
	}

	/**
	 * @param exception
	 * @param settings
	 * @param request
	 * @param expiredEntry - previous expired cache entry
	 * @return
	 */
	protected CacheEntryLoadResult getExpiredResult(Exception exception, ExpiredFailedRecipe settings,
			CacheLoadRequest request, CacheEntry expiredEntry) {
		V value;
		switch (settings.returnAs) {
		case EXCEPTION:
			throw new CacheLoaderException(exception, request);
		case EXPIRED:
			value = expiredEntry.getValue();
			break;
		case NULL:
			value = null;
			break;
		default:
			throw new IllegalStateException("Unsupported switch value " + settings.returnAs);
		}

		CacheEntryLoadResult entry;
		switch (settings.cacheAs) {
		case DONT:
			entry = new CacheEntryLoadResult(false, value, expiredEntry.getEvictTtl(), expiredEntry.getStaleTtl());
			entry.setStoredAt(expiredEntry.getStoredAt());
			break;
		case EXPIRED:
			entry = new CacheEntryLoadResult(true, value, expiredEntry.getEvictTtl(), -1);
			break;
		case REQUEST:
			entry = new CacheEntryLoadResult(true, value, request.getHardTtl(), request.getSoftTtl());
			break;
		default:
			throw new IllegalStateException("Unsupported switch value " + settings.cacheAs);
		}
		return entry;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy