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

org.dihedron.patterns.cache.Cache Maven / Gradle / Ivy

/**
 * Copyright (c) 2012-2014, Andrea Funto'. All rights reserved. See LICENSE for details.
 */ 

package org.dihedron.patterns.cache;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;

import org.dihedron.core.License;
import org.dihedron.core.regex.Regex;
import org.dihedron.core.streams.Streams;
import org.dihedron.core.strings.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Andrea Funto'
 */
@License
public class Cache implements Iterable{
	
	/** 
	 * The logger. 
	 */
	private static final Logger logger = LoggerFactory.getLogger(Cache.class);

	/** 
	 * The underlying storage engine. 
	 */
	private Storage storage;
	
	/**
	 * Constructor.
	 * 
	 * @param storage
	 *   the storage engine to be used for persistence.
	 */
	public Cache(Storage storage) {
		logger.debug("created cache");
		this.storage = storage;
	}
	
	/**
	 * Retrieves the underlying storage engine.
	 * 
	 * @return
	 *   the storage engine.
	 */
	@Deprecated
	public Storage getStorageEngine() {
		return storage;
	}
	
	/**
	 * Checks whether the cache is empty.
	 * 
	 * @return
	 *   whether the cache is empty.
	 */
	public boolean isEmpty() {
		logger.trace("checking if cache is empty");
		return storage.isEmpty();
	}
	
	/**
	 * Returns the number of elements in the cache.
	 * 
	 * @return
	 *   the number of elements in the cache.
	 */
	public long size() {
		return storage.size();
	}
	
	/**
	 * Empties the cache.
	 * 
	 * @return
	 *   the cache itself, for method chaining.
	 */
	public Cache clear(){
		logger.debug("clearing the cache");
		storage.clear();
		return this;
	}
	
	/**
	 * Deletes all resources that match the given resource name criteria.
	 * 
	 * @param regex
	 *   the resource name pattern.
	 * @return
	 *   the cache itself, for method chaining.
	 */
	public Cache delete(Regex regex) {
		logger.debug("deleting all files named according to /{}/", regex);
		storage.delete(regex);
		return this;
	}
	
	/**
	 * Deletes all resource that match the given resource name criteria.
	 * 
	 * @param resource
	 *   the resource name.
	 * @param caseInsensitive
	 *   whether the resource name comparison should be
	 *   case insensitive.
	 * @return
	 *   the cache itself, for method chaining.
	 */
	public Cache delete(String resource, boolean caseInsensitive) {
		logger.debug("deleting all files named according to '{}' (case insensitive)", resource);
		storage.delete(resource, caseInsensitive);
		return this;
	}
	
	/**
	 * Copies data from one resource to another, possibly replacing the destination 
	 * resource if one exists.
	 * 
	 * @param source
	 *   the name of the source resource.
	 * @param destination
	 *   the name of the destination resource
	 * @return
	 *   the cache itself, for method chaining.
	 */
	public Cache copyAs(String source, String destination) throws CacheException {
		if(!Strings.areValid(source, destination)) {
			logger.error("invalid input parameters for copy from '{}' to '{}'", source, destination);
			throw new CacheException("invalid input parameters (source: '" + source + "', destination: '" + destination + "')");
		}
		try (InputStream input = storage.retrieve(source); OutputStream output = storage.store(destination)) {
			long copied = Streams.copy(input, output);
			logger.trace("copied {} bytes from '{}' to '{}'", copied, source, destination);
		} catch (IOException e) {
			logger.error("error copying from '" + source + "' to '" + destination + "'", e);
			throw new CacheException("error copying from '" + source + "' to '" + destination + "'", e);
		}
		return this;
	}
	
	/**
	 * Returns the iterator on the cache items.
	 * 
	 * @see Iterable#iterator()
	 */
	@Override
	public Iterator iterator() {		
		return storage.iterator();
	}
	
	/**
	 * Returns the iterator on the cache items; only items whose name matches 
	 * the given regular expression will be returned.
	 * 
	 * @param regex
	 *   an optional regular expression; if {@code null} all items will be 
	 *   returned.
	 * @return
	 *   an iterator that allows looping over the resource names.
	 */	
	public Iterator iterator(Regex regex) {		
		return storage.iterator(regex);
	}	
	
	/**
	 * Checks whether the cache contains the given resource.
	 * 
	 * @param resource
	 *   the resource whose existence in the cache is to be checked.
	 * @return
	 *   true if the resource exists in the cache, false
	 *    otherwise.
	 */
	public boolean contains(String resource) {
		boolean result = storage.contains(resource);
		logger.debug("resource '{}' {} in cache", resource, (result ? "is" : "is not"));
		return result; 
	}
	
	/**
	 * Retrieves a resource from the cache if it is in there; if
	 * the autoRefresh flag is on, the cache will check
	 * if the resource has not been refreshed during this session yet
	 * and (if so) will retrieve it again.
	 * 
	 * @param resource
	 *   the name of the resource.
	 * @param handlers
	 *   an optional set of cache miss handler, which will be requested to 
	 *   retrieve the resource, if missing; the handlers will be called in the 
	 *   order specified here, and as soon as one returns a valid stream the 
	 *   lookup stops.
	 * @return
	 *   the resource as an input stream if it can be retrieved, {@code null}
	 *   otherwise.
	 * @throws CacheException 
	 */
	public InputStream get(String resource, CacheMissHandler ... handlers) throws CacheException {
		InputStream stream = storage.retrieve(resource);		
		if(stream == null) {
			logger.trace("cache miss for resource '{}'...", resource);
			if(handlers != null) {				
				lookup:
				for(CacheMissHandler handler : handlers) {
					logger.trace("... attempting retrieval of '{}' using handler of class '{}'", resource, handler.getClass().getSimpleName());
					try (InputStream input = handler.getAsStream(); OutputStream output = storage.store(resource)) {
						if(input != null) {
							long copied = Streams.copy(input,  output);
							logger.trace("... stored {} bytes for resource '{}'", copied, resource);
							break lookup;
						} else {
							logger.trace("... resource '{}' not found", resource);
							continue lookup;
						}
					} catch (IOException e) {
						logger.warn("I/O error trying to retrieve resource '" + resource + "' with handler of class '" + handler.getClass().getSimpleName() +"'", e);
					}					
				}
			}
			logger.trace("retrieving resource from storage");
			stream = storage.retrieve(resource);			
		}
		return stream;
	}
	
	/**
	 * Tells the cache to store under the given resource name the contents 
	 * that will be written to the output stream; the method creates a new 
	 * resource entry and opens an output stream to it, then returns the
	 * stream to the caller so this can copy its data into it. It is up to 
	 * the caller to close the steam once all data have been written to it.
	 * This mechanism actually by-passes the cache and the miss handlers and
	 * provides direct access to the underlying storage engine, thus providing
	 * a highly efficient way of storing data into the cache. 
	 * 
	 * @param resource
	 *   the name of the new resource, to which the returned output stream 
	 *   will point.
	 * @return
	 *   an output stream ; the caller will write its data into it, and then 
	 *   will flush and close it one it's done writing data.
	 * @throws CacheException
	 */
	public OutputStream put(String resource) throws CacheException {
		return storage.store(resource);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy