Search code examples
javacachingfactory-pattern

Java: multiple self refreshing cache object


I would like create a cache object with a factory, for a multiple tables. Each cache has their own life time which are different to one another. For example, lifetime for City object is about a year, and lifetime for facility list object is about 1 day.

This below is my cache class

public abstract class CacheAbstract<E, I, C> {

private Long timestamp;
private Class<E> entityClass;
private Class<I> idClass;
private Class<C> cacheClass;
protected Integer TTL = 60; // In minutes. defaults to 60 mins

private Map<I, C> cache;
private Timer timer;

public CacheAbstract(Class<E> entityClass, Class<I> idClass, Class<C> cacheClass) {
    this.entityClass = entityClass;
    this.idClass = idClass;
    this.cacheClass = cacheClass;
}

public Boolean isExpired() {
    return (this.timestamp + TTL * 60000) < new Date().getTime();
}

private void verifyCache() {
    if (this.cache == null || isExpired()) {
        this.load();
    }
}

private void onTimer() {
    this.timer = new Timer();
    timer.schedule(new TimerTask() {

        @Override
        public void run() {
            throw new UnsupportedOperationException("Not supported yet."); //What should I do here? I can't call this class' load(), can I?;
        }
    }, this.TTL * 60000);
}


public void load() {
    // Load all data here
    this.timestamp = new Date().getTime();
}

public C get(I id) {
    this.verifyCache();
    return this.cache.get(id);
}

public List<C> getAll() {
    this.verifyCache();
    List<C> list = new ArrayList<>(this.cache.values());
    return list;
}

protected abstract C transform(E entity);

protected abstract I getID(E entity);

public abstract String getName();
}

this class is not a singleton, and will be instantiated by a factory., let's say, CacheFactory

public final class CacheFactory {

private Map<String, CacheAbstract> caches;

private CacheFactory() {
}

public void registerCache(CacheAbstract cache) {
    if (caches == null) {
        caches = new HashMap<>();
    }
    if (caches.containsKey(cache.getName())) {
        this.caches.get(cache.getName()).load();
    } else {
        this.caches.put(cache.getName(), cache);
    }
}

public CacheAbstract getCache(String name) throws ExceptionInvalidCacheName {
    if (caches.containsKey(name)) {
        return caches.get(name);
    } else {
        throw new ExceptionInvalidCacheName("KOLOM SERVER [cache]: Cache '" + name + "' not registered");
    }
}

public Boolean isExist(String name) {
    return caches.containsKey(name);
}

public List<String> getNames() {
    return new ArrayList<>(caches.keySet());
}

public static CacheFactory getInstance() {
    return CacheFactoryHolder.INSTANCE;
}

private static class CacheFactoryHolder {

    private static final CacheFactory INSTANCE = new CacheFactory();
}
}

Now, how I can implement algorithm to let CacheAbstract to refresh itself when needed?


Solution

  • Yes, you can simply call load():

    private void onTimer() {
        timer = new Timer();
        timer.schedule(new TimerTask() {
    
            @Override
            public void run() {
                 load(); // Call the containing class method
            }
        }, TTL * 60000);
    }
    

    because every anonymous class is actually also an inner class, so it has a reference to the containing class that declares it.

    But, you would only use a Timer if you really wanted active expiration. You already have lazy expiration due to the call to verifyCache() in every call to get(). Maybe that's enough for your needs now.