package codeine.jsons.peer_status; import codeine.db.IStatusDatabaseConnector; import codeine.db.mysql.connectors.StatusDatabaseConnectorListProvider; import codeine.executer.PeriodicExecuter; import codeine.executer.Task; import codeine.utils.StringUtils; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Maps; import org.apache.log4j.Logger; import javax.inject.Inject; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; public class PeersProjectsStatusInWebServer implements PeersProjectsStatus { private static final Logger log = Logger.getLogger(PeersProjectsStatusInWebServer.class); public static final long SLEEP_TIME = TimeUnit.SECONDS.toMillis(55); public static long WORKER_SLEEP_TIME = TimeUnit.SECONDS.toMillis(30); private StatusDatabaseConnectorListProvider statusDatabaseConnectorListProvider; private Map<String, PeerStatusJsonV2> peer_to_projects = Maps.newHashMap(); private Cache<String, PeerStatusJsonV2> cache = CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.MINUTES).build(); private Map<String, PeriodicExecuter> connectorsMap = Maps.newHashMap(); @Inject public PeersProjectsStatusInWebServer(StatusDatabaseConnectorListProvider statusDatabaseConnectorListProvider) { super(); this.statusDatabaseConnectorListProvider = statusDatabaseConnectorListProvider; if (!StringUtils.isEmpty(System.getProperty("REFRESH_SECONDS"))) { try { WORKER_SLEEP_TIME = TimeUnit.SECONDS.toMillis(Long.parseLong(System.getProperty("REFRESH_SECONDS"), 10)); } catch(Exception e) { log.error("Failed to parse REFRESH_SECONDS property", e); } } log.info("refresh rate is " + WORKER_SLEEP_TIME + " milliseconds"); } @Override public void run() { log.debug("getting data from directory"); HashMap<String, PeriodicExecuter> connectorsToRemove = Maps.newHashMap(connectorsMap); for (IStatusDatabaseConnector connector : statusDatabaseConnectorListProvider.get()) { connectorsToRemove.remove(connector.server()); if (!connectorsMap.containsKey(connector.server())) { log.info("start fetching data from " + connector.server()); PeersProjectsStatusInWebServerConnectorWorker w = new PeersProjectsStatusInWebServerConnectorWorker(connector); PeriodicExecuter e = new PeriodicExecuter(WORKER_SLEEP_TIME, w, "PeersStatusWorker-" + connector.server()); e.runInThread(); connectorsMap.put(connector.server(), e); } } for (Entry<String, PeriodicExecuter> e : connectorsToRemove.entrySet()) { log.info("stopping " + e.getKey()); e.getValue().stopWhenPossible(); } peer_to_projects = getCacheAsMap(); } private Map<String, PeerStatusJsonV2> getCacheAsMap() { Map<String, PeerStatusJsonV2> $ = Maps.newHashMap(); synchronized (cache) { $.putAll(cache.asMap()); } return $; } private void mergeUpdateMap(Map<String, PeerStatusJsonV2> peersStatus) { synchronized (cache) { for (Entry<String, PeerStatusJsonV2> e : peersStatus.entrySet()) { if (!cache.asMap().containsKey(e.getKey())) { cache.put(e.getKey(), e.getValue()); } else { // more than one/already in cache log.debug("peer appears in more than one database " + e.getKey()); if (isNewer(e.getValue(), cache.asMap().get(e.getKey()))) { cache.put(e.getKey(), e.getValue()); } } } } } private boolean isNewer(PeerStatusJsonV2 newOne, PeerStatusJsonV2 oldOne) { if (newOne.update_time_from_peer() == 0) { log.debug("peer new do not have update time " + newOne); } if (oldOne.update_time_from_peer() == 0) { log.debug("peer old do not have update time " + oldOne); } if (newOne.update_time_from_peer() > 0 || oldOne.update_time_from_peer() > 0) { return newOne.update_time_from_peer() > oldOne.update_time_from_peer(); } return newOne.update_time() > oldOne.update_time(); } @Override public Map<String, PeerStatusJsonV2> peer_to_projects() { return peer_to_projects; } public class PeersProjectsStatusInWebServerConnectorWorker implements Task { private IStatusDatabaseConnector connector; public PeersProjectsStatusInWebServerConnectorWorker(IStatusDatabaseConnector connector) { this.connector = connector; } @Override public void run() { Map<String, PeerStatusJsonV2> peersStatus = connector.getPeersStatus(); log.info("got peer status from connector " + connector.server() + " with size " + peersStatus.size()); mergeUpdateMap(peersStatus); } @Override public String toString() { return "PeersProjectsStatusInWebServerConnectorWorker [connector=" + connector + "]"; } } }