/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.function.blacklist; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.util.ArgumentChecker; /** * Manages a set of {@link FunctionBlacklistRule}s with expiry times. */ public class FunctionBlacklistRuleSet implements Set<FunctionBlacklistRule> { private static final Logger s_logger = LoggerFactory.getLogger(FunctionBlacklistRuleSet.class); private static final int MAX_TTL = 86400; private final Map<FunctionBlacklistRule, Long> _rules = new ConcurrentHashMap<FunctionBlacklistRule, Long>(); private volatile int _minimumTTL; private final Cleaner _cleaner; private static class Cleaner implements Runnable { private final ScheduledExecutorService _executor; private final WeakReference<FunctionBlacklistRuleSet> _ref; private Future<?> _future; public Cleaner(final FunctionBlacklistRuleSet ref, final ScheduledExecutorService executor, final int minimumTTL) { s_logger.debug("Creating cleaner for {} at {}s", ref, minimumTTL); _executor = executor; _ref = new WeakReference<FunctionBlacklistRuleSet>(ref); _future = executor.scheduleWithFixedDelay(this, minimumTTL, minimumTTL, TimeUnit.SECONDS); } public void reschedule(final int time) { // Reference must be valid because this gets called from the referent s_logger.debug("Rescheduling cleanup of {} for {}s", _ref.get(), time); _future.cancel(false); _future = _executor.scheduleWithFixedDelay(this, time, time, TimeUnit.SECONDS); } @Override public void run() { final FunctionBlacklistRuleSet ref = _ref.get(); if (ref == null) { _future.cancel(false); } else { ref.clean(); } } } public FunctionBlacklistRuleSet(final ScheduledExecutorService executor) { this(executor, MAX_TTL); } public FunctionBlacklistRuleSet(final ScheduledExecutorService executor, final int minimumTTL) { _minimumTTL = minimumTTL; _cleaner = new Cleaner(this, executor, minimumTTL); } @Override public int size() { return _rules.size(); } @Override public boolean isEmpty() { return _rules.isEmpty(); } @Override public boolean contains(Object o) { return _rules.keySet().contains(o); } @Override public Iterator<FunctionBlacklistRule> iterator() { final Iterator<FunctionBlacklistRule> itr = _rules.keySet().iterator(); return new Iterator<FunctionBlacklistRule>() { private FunctionBlacklistRule _last; @Override public boolean hasNext() { return itr.hasNext(); } @Override public FunctionBlacklistRule next() { _last = itr.next(); return _last; } @Override public void remove() { itr.remove(); onRemove(_last); } }; } @Override public Object[] toArray() { return _rules.keySet().toArray(); } @Override public <T> T[] toArray(T[] a) { return _rules.keySet().toArray(a); } @Override public boolean add(final FunctionBlacklistRule e) { add(e, _minimumTTL); return true; } @Override public boolean remove(Object o) { if (!_rules.keySet().remove(o)) { return false; } onRemove((FunctionBlacklistRule) o); return true; } @Override public boolean containsAll(Collection<?> c) { return _rules.keySet().containsAll(c); } @Override public boolean addAll(Collection<? extends FunctionBlacklistRule> c) { for (FunctionBlacklistRule rule : c) { add(rule, _minimumTTL); } return true; } @Override public boolean retainAll(Collection<?> c) { boolean changed = false; final Iterator<FunctionBlacklistRule> itr = iterator(); while (itr.hasNext()) { if (!c.contains(itr.next())) { itr.remove(); changed = true; } } return changed; } @Override public boolean removeAll(Collection<?> c) { boolean changed = false; for (Object o : c) { changed |= remove(o); } return changed; } @Override public void clear() { final Iterator<FunctionBlacklistRule> itr = iterator(); while (itr.hasNext()) { itr.next(); itr.remove(); } } public void add(final FunctionBlacklistRule rule, final int timeToLive) { ArgumentChecker.notNegativeOrZero(timeToLive, "timeToLive"); final long expiry = System.nanoTime() + (long) timeToLive * 1000000000L; if (_rules.put(rule, expiry) == null) { onAdd(rule); } if (timeToLive < _minimumTTL) { synchronized (this) { if (timeToLive < _minimumTTL) { _cleaner.reschedule(timeToLive); _minimumTTL = timeToLive; } } } } protected void clean() { s_logger.debug("Cleaning ruleset {}", this); final long time = System.nanoTime(); _minimumTTL = MAX_TTL + 1; final Iterator<Map.Entry<FunctionBlacklistRule, Long>> itr = _rules.entrySet().iterator(); long lowestTTL = (long) MAX_TTL * 1000000000L; while (itr.hasNext()) { final Map.Entry<FunctionBlacklistRule, Long> entry = itr.next(); final long ttl = entry.getValue() - time; if (ttl <= 0) { itr.remove(); onRemove(entry.getKey()); } else { if (ttl < lowestTTL) { lowestTTL = ttl; } } } final int ttl = (int) ((lowestTTL + 999999999L) / 1000000000L); if (ttl < _minimumTTL) { synchronized (this) { if (ttl < _minimumTTL) { _cleaner.reschedule(ttl); } } } } /** * Called when a rule has been added to the set. This is provided so that a subclass may perform specific actions. This is called after the rule has been added. * * @param rule the rule that was added to the set */ protected void onAdd(final FunctionBlacklistRule rule) { s_logger.debug("Added rule {}", rule); } /** * Called when a rule added to the set is removed, perhaps because it's reached its expiry. This is provided so that a subclass may perform specific actions. This is called after the rule has been * removed. * * @param rule the rule that was removed from the set */ protected void onRemove(final FunctionBlacklistRule rule) { s_logger.debug("Removed rule {}", rule); } }