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

net.anthavio.httl.cache.CachingExtractor Maven / Gradle / Ivy

The newest version!
package net.anthavio.httl.cache;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

import net.anthavio.cache.CacheBase;
import net.anthavio.cache.CacheEntry;
import net.anthavio.cache.CacheEntryLoader;
import net.anthavio.cache.CacheLoadRequest;
import net.anthavio.cache.CachingSettings;
import net.anthavio.cache.ConfiguredCacheLoader;
import net.anthavio.cache.ConfiguredCacheLoader.SimpleLoader;
import net.anthavio.cache.LoadingSettings;
import net.anthavio.httl.HttlRequest;
import net.anthavio.httl.HttlResponseExtractor.ExtractedResponse;
import net.anthavio.httl.HttlSender;
import net.anthavio.httl.cache.Builders.ExtractingRequestBuilder;

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

/**
 * Sender wrapper that caches Responses as they are recieved from remote server. 
 * Server response bodies are cached as string or byte array and extraction performed every call.
 * 
 * TODO reintroduce CachingExtractor - Note: If you want to cache extraction product, use CachingExtractor
 * 
 * Note: For automatic updates use CacheBase#schedule
 * 
 * @author martin.vanek
 *
 */
public class CachingExtractor /*implements SenderOperations, ExtractionOperations*/{

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private final HttlSender sender;

	private final CacheBase cache;

	public CachingExtractor(HttlSender sender, CacheBase cache) {
		if (sender == null) {
			throw new IllegalArgumentException("sender is null");
		}
		this.sender = sender;

		if (cache == null) {
			throw new IllegalArgumentException("cache is null");
		}
		this.cache = cache;

	}

	/**
	 * @return underlying sender
	 */
	public HttlSender getSender() {
		return sender;
	}

	/**
	 * @return underlying cache
	 */
	public CacheBase getCache() {
		return cache;
	}

	/**
	 * FIXME - this is silly method name
	 * Start fluent builder
	 */
	public ExtractingRequestBuilder from(HttlRequest request) {
		return new ExtractingRequestBuilder(this, request);
	}

	/**
	 * Custom cache key from request (if exist) takes precedence
	 * Otherwise key derived from request URL is used
	 
	protected String getCacheKey(CachingSenderRequest request) {
		String cacheKey = request.getUserKey();
		if (cacheKey == null) {
			cacheKey = keyProvider.provideKey(request.getSenderRequest());
		}
		return cacheKey;
	}
	*/
	public static class SimpleHttpSenderExtractor implements SimpleLoader {

		private final HttlSender sender;

		private final CachingExtractorRequest request;

		public SimpleHttpSenderExtractor(HttlSender sender, CachingExtractorRequest request) {
			if (sender == null) {
				throw new IllegalArgumentException("Null sender");
			}
			this.sender = sender;

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

		}

		@Override
		public T load() throws Exception {
			ExtractedResponse bodyResponse;
			if (request.getExtractor() != null) {
				bodyResponse = sender.extract(request.getSenderRequest(), request.getExtractor());
			} else {
				bodyResponse = sender.extract(request.getSenderRequest(), request.getResultType());
			}
			return bodyResponse.getBody();
		}

	}

	/**
	 * Turns Sender Request into Cache Request
	 */
	public  CacheLoadRequest convert(CachingExtractorRequest request) {
		//String cacheKey = getCacheKey(request);
		CachingSettings caching = new CachingSettings(request.getSenderRequest(), request.getEvictTtl(),
				request.getExpiryTtl(), TimeUnit.SECONDS);

		SimpleHttpSenderExtractor simple = new SimpleHttpSenderExtractor(sender, request);
		CacheEntryLoader loader = new ConfiguredCacheLoader(simple,
				request.getMissingRecipe(), request.getExpiredRecipe());
		LoadingSettings loading = new LoadingSettings(loader, request.isMissingLoadAsync(),
				request.isExpiredLoadAsync());
		return new CacheLoadRequest(caching, loading);
	}

	/**
	 * Static caching based on specified TTL
	 * 
	 * XXX Having complexity of entry loading and caching on Cache side imply
	 * simplifies this class implementation
	 * causes two cache queries for expired and missing entries 
	 */
	public  CacheEntry extract(CachingExtractorRequest request) {
		//String cacheKey = getCacheKey(request);
		CacheEntry entry = (CacheEntry) cache.get(request.getSenderRequest());
		if (entry != null) {
			//entry.getValue().setRequest(request.getSenderRequest());
			if (!entry.isStale()) {
				return (CacheEntry) entry; //fresh hit
			} else {
				CacheLoadRequest cacheRequest = convert(request);
				return (CacheEntry) cache.load((CacheLoadRequest) cacheRequest,
						(CacheEntry) entry);
				//return (CacheEntry) cache.get(lrequest);
			}
		} else { //cache miss - we have nothing
			CacheLoadRequest cacheRequest = convert(request);
			return (CacheEntry) cache.load((CacheLoadRequest) cacheRequest, null);
		}
	}

	/**
	 * Static caching based on specified TTL
	 
	public  ExtractedBodyResponse extract(CachingSenderRequest request, Class resultType) {
		SenderResponse response = execute(request).getValue();
		try {
			T extracted = sender.extract(response, resultType);
			return new ExtractedBodyResponse(response, extracted);
		} finally {
			Cutils.close(response);
		}
	}
	*/
	/**
	 * Static caching based on specified TTL
	 
	public  ExtractedBodyResponse extract(CachingSenderRequest request, ResponseBodyExtractor extractor) {
		SenderResponse response = execute(request).getValue();
		try {
			T extracted = extractor.extract(response);
			return new ExtractedBodyResponse(response, extracted);

		} catch (IOException iox) {
			throw new SenderException(iox);
		} finally {
			Cutils.close(response);
		}
	}
	*/
	/**
	 * Caching based on HTTP response headers
	 * 
	 * Extracted response version. Response is extracted, closed and result is returned to caller
	 
	public  ExtractedBodyResponse extract(SenderRequest request, ResponseBodyExtractor extractor) {
		if (extractor == null) {
			throw new IllegalArgumentException("Extractor is null");
		}
		SenderResponse response = execute(request);
		try {
			if (response.getHttpStatusCode() >= 300) {
				throw new SenderHttpStatusException(response);
			}
			T extracted = extractor.extract(response);
			return new ExtractedBodyResponse(response, extracted);

		} catch (IOException iox) {
			throw new SenderException(iox);
		} finally {
			Cutils.close(response);
		}
	}
	*/
	/**
	 * Caching based on HTTP response headers
	 * 
	 * Extracted response version. Response is extracted, closed and result is returned to caller
	 
	public  ExtractedBodyResponse extract(SenderRequest request, Class resultType) {
		if (resultType == null) {
			throw new IllegalArgumentException("resultType is null");
		}
		SenderResponse response = execute(request);
		try {
			T extracted = sender.extract(response, resultType);
			return new ExtractedBodyResponse(response, extracted);
		} finally {
			Cutils.close(response);
		}
	}
	*/
	/**
	 * Caching based on HTTP headers values (ETag, Last-Modified, Cache-Control, Expires).
	 * 
	 * Caller is responsible for closing Response.
	 
	public SenderResponse execute(SenderRequest request) {
		if (request == null) {
			throw new IllegalArgumentException("request is null");
		}
		String cacheKey = sender.getCacheKey(request);
		CacheEntry entry = cache.get(cacheKey);
		if (entry != null) {
			entry.getValue().setRequest(request);
			if (!entry.isExpired()) {
				return entry.getValue(); //cache hit and not soft expired - hurray
			} else {
				//soft expired - verify freshness
				String etag = entry.getValue().getFirstHeader("ETag");
				if (etag != null) { //ETag
					request.setHeader("If-None-Match", etag); //XXX this modifies request so hashCode will change as well
				}
				String lastModified = entry.getValue().getFirstHeader("Last-Modified");
				if (lastModified != null) { //Last-Modified
					request.setHeader("If-Modified-Since", lastModified);
				}
			}
		} else if (request.getFirstHeader("If-None-Match") != null) {
			throw new IllegalStateException("Cannot use request ETag without holding cached response");
		}
		SenderResponse response = sender.execute(request);

		if (response.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
			//only happen when we sent Etag => we have CacheEntry
			//entry.setServerDate(response.getFirstHeader("Date"));
			Cutils.close(response);
			return entry.getValue();
		}

		if (response.getHttpStatusCode() < 300) {
			CacheEntry entryNew = HttpHeaderUtil.buildCacheEntry(request, response);
			if (entryNew != null) {
				cache.set(cacheKey, entryNew);
				return entryNew.getValue();
			} else {
				logger.info("Response http headers do not allow caching");
				return response;
			}

		} else {
			//Other then HTTP 200 OK responses are NOT cached
			return response;
		}
	}
	*/
	@Override
	public String toString() {
		return "CachingSender [url=" + sender.getConfig().getUrl() + "]";
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy