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

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

The newest version!
package net.anthavio.cache;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

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

/**
 * 
 * @author martin.vanek
 *
 */
public class Scheduler {

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

	private ReentrantLock reloadLock = new ReentrantLock(true); //Lock for the refreshing Map

	private Map> reloading = new HashMap>();

	private ExecutorService executor;

	private Map> scheduled = new HashMap>();

	private long schedulerInterval = 1; //SECONDS

	private SchedulerThread scheduler;

	private CacheBase cache;

	public Scheduler(CacheBase cache, ExecutorService executor) {
		if (cache == null) {
			throw new IllegalArgumentException("Null cache");
		}
		this.cache = cache;

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

	public void close() {
		logger.info("Scheduler close " + cache.getName());
		if (scheduler != null) {
			scheduler.keepGoing = false;
			scheduler.interrupt();
		}
	}

	/**
	 * @return scheduler thread sleep time  
	 */
	public long getSchedulerInterval() {
		return schedulerInterval;
	}

	public void setSchedulerInterval(long interval, TimeUnit unit) {
		this.schedulerInterval = unit.toSeconds(interval);
		if (this.schedulerInterval < 1) {
			throw new IllegalArgumentException("Scheduler interval " + schedulerInterval + " must be >= 1 second");
		}
	}

	/**
	 * @return underylying ExecutorService
	 */
	public ExecutorService getExecutor() {
		return executor;
	}

	/**
	 * @return requests scheduled to be refreshed automaticaly
	 */
	public Map> getScheduled() {
		return scheduled;
	}

	/**
	 * @return 
	 */
	public CacheLoadRequest getScheduled(String userKey) {
		return scheduled.get(userKey);
	}

	/**
	 * Register new CacheLoadRequest (or replace existing)
	 */
	public void schedule(CacheLoadRequest request) {
		if (this.executor == null) {
			throw new IllegalStateException("Executor for asynchronous loading is not configured");
		}
		K userKey = request.getUserKey();
		synchronized (scheduled) {
			if (scheduled.get(userKey) != null) {
				scheduled.put(userKey, new ScheduledRequest(request.getCaching(), request.getLoading()));
				if (logger.isDebugEnabled()) {
					logger.debug("ScheduledRequest replaced: " + request);
				}
			} else {
				scheduled.put(userKey, new ScheduledRequest(request.getCaching(), request.getLoading()));
				if (logger.isDebugEnabled()) {
					logger.debug("ScheduledRequest created: " + request);
				}

				//delayed create & start of the scheduler thread 
				synchronized (this) {
					if (scheduler == null) {
						scheduler = new SchedulerThread(schedulerInterval, TimeUnit.SECONDS);
						scheduler.start();
					}
				}
			}
		}
	}

	public void startReload(CacheLoadRequest request, CacheEntry expiredEntry) {
		if (this.executor == null) {
			throw new IllegalStateException("Executor for asynchronous loading is not configured");
		}
		K userKey = request.getUserKey();
		reloadLock.lock();
		try {
			if (reloading.containsKey(userKey)) {
				logger.debug("Request load already running: " + request);
			} else {
				logger.debug("Request load starting: " + request);
				try {
					reloading.put(userKey, request);
					executor.execute(new ReloaderRunnable(request, expiredEntry)); //throws Exception...
				} catch (RejectedExecutionException rx) {
					logger.warn("Request load start rejected " + rx.getMessage());
					reloading.remove(userKey);
				} catch (Exception x) {
					logger.error("Request load start failed: " + request, x);
					reloading.remove(userKey);
				}
			}
		} finally {
			reloadLock.unlock();
		}
	}

	/**
	 * Runnable for {@link LoadMode#ASYNC}
	 * 
	 */
	private class ReloaderRunnable implements Runnable {

		private final CacheLoadRequest request;

		private final CacheEntry softExpiredEntry;

		public ReloaderRunnable(CacheLoadRequest request, CacheEntry softExpiredEntry) {
			if (request == null) {
				throw new IllegalArgumentException("request is null");
			}
			this.request = request;
			this.softExpiredEntry = softExpiredEntry;
		}

		@Override
		public void run() {
			try {
				cache.load(true, request, softExpiredEntry);
				if (logger.isDebugEnabled()) {
					logger.debug("Request load completed: " + request);
				}
			} catch (Exception x) {
				logger.warn("Request load failed: " + request, x);
			} finally {
				reloading.remove(request.getUserKey());
			}
		}
	}

	private class SchedulerThread extends Thread {

		private final long interval;

		private boolean keepGoing = true;

		public SchedulerThread(long interval, TimeUnit unit) {
			this.interval = unit.toMillis(interval);
			if (this.interval < 1000) {
				throw new IllegalArgumentException("Interval " + this.interval + " must be >= 1000 millis");
			}
			setDaemon(true);
			setName("scheduler-" + cache.getName());
		}

		@Override
		public void run() {
			logger.info(getName() + " started");
			while (keepGoing) {
				try {
					check();
				} catch (Exception x) {
					logger.warn(getName() + " check iteration failed", x);
				}
				try {
					Thread.sleep(interval);
				} catch (InterruptedException ix) {
					logger.info(getName() + " interrupted");
					if (!keepGoing) {
						break;
					}
				}
			}
			logger.info(getName() + " stopped");
		}

		private void check() {
			long now = System.currentTimeMillis();
			if (logger.isTraceEnabled()) {
				logger.trace(getName() + " check");
			}
			for (Entry> entry : scheduled.entrySet()) {
				try {
					ScheduledRequest request = entry.getValue();
					if (request.getSoftExpire() <= now) {
						startReload(request, null);
					}
				} catch (Exception x) {
					logger.warn("Exception during refresh of " + entry.getValue(), x);
				}
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy