diff src/nabble/view/web/user/OnlineStatus.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children 18cf4872fd7f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/view/web/user/OnlineStatus.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,173 @@
+package nabble.view.web.user;
+
+import fschmidt.util.servlet.ServletUtils;
+import nabble.model.Executors;
+import nabble.model.ModelHome;
+import nabble.model.Person;
+import nabble.model.Site;
+import nabble.model.User;
+import nabble.view.lib.Jtp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+public class OnlineStatus {
+
+	private static final Logger logger = LoggerFactory.getLogger(OnlineStatus.class);
+
+	private static final long TIMEOUT = 3 * 60 * 1000L; // 3 minutes
+	private static final String INVISIBLE = "invisible:";
+
+	/** Maps a site ID to a map (Person-Source-ID > last time) */
+	private static final Map<Long, Map<String, Long>> sitesMap = new HashMap<Long, Map<String, Long>>();
+
+	public static void setOnline(HttpServletRequest request, Person visitor, Site site) {
+		setOnline(request,visitor.getSearchId(),site);
+	}
+
+	private static void setOnline(HttpServletRequest request, String sourceId, Site site) {
+		synchronized(sitesMap) {
+			Map<String, Long> map = sitesMap.get(site.getId());
+			if (map == null) {
+				map = new HashMap<String, Long>();
+				sitesMap.put(site.getId(), map);
+				logger.debug("New map for site ID = " + site.getId());
+			}
+			boolean isVisible = ServletUtils.getCookieValue(request, "visible") == null || !Jtp.isInteger(sourceId);
+			map.put(isVisible? sourceId : INVISIBLE+sourceId, System.currentTimeMillis());
+		}
+	}
+
+	public static boolean isOnline(Person visitor, Site site) {
+		return isOnline(visitor.getSearchId(),site);
+	}
+
+	public static boolean isOnline(String sourceId, Site site) {
+		synchronized(sitesMap) {
+			Map<String, Long> map = sitesMap.get(site.getId());
+			return map != null && map.get(sourceId) != null;
+		}
+	}
+
+	private static void runGC() {
+		final long start = System.currentTimeMillis();
+		synchronized(sitesMap) {
+			for( Iterator<Map<String, Long>> iter = sitesMap.values().iterator(); iter.hasNext(); ) {
+				Map<String, Long> map = iter.next();
+				for( Iterator<Long> subIter = map.values().iterator(); subIter.hasNext(); ) {
+					long time = subIter.next();
+					if (start - time > TIMEOUT) {
+						subIter.remove();
+						logger.debug("Removed user");
+					}
+				}
+				if( map.isEmpty() ) {
+					iter.remove();
+					logger.debug("Removed map");
+				}
+			}
+		}
+		logger.info("OnlineStatus GC took " + (System.currentTimeMillis()-start) + " ms");
+	}
+
+	public static String getOnlineStats() {
+		int count = 0;
+		int sites;
+		float ratio = 0f;
+		synchronized(sitesMap) {
+			Collection<Map<String, Long>> entries = sitesMap.values();
+			for (Map<String, Long> entry : entries) {
+				count += entry.keySet().size();
+			}
+			sites =  sitesMap.size();
+			if (sites > 0)
+				ratio = count / (float) sitesMap.size();
+		}
+		return "Users = " + count + " | Sites = " + sites + " | Ratio = " + String.format("%.2f", ratio);
+	}
+
+	public static Map<String, Integer> getOnlineSites() {
+		Map<String, Integer> sites = new HashMap<String, Integer>();
+
+		synchronized(sitesMap) {
+			for (Map.Entry<Long, Map<String, Long>> entry : sitesMap.entrySet()) {
+				String url = ModelHome.getSite(entry.getKey()).getBaseUrl();
+				sites.put(url, entry.getValue().size());
+			}
+		}
+		return sites;
+	}
+
+	public static List<User> getOnlineUsers(Site site, boolean includeInvisibleUsers) {
+		String[] visitorIDs = getVisitorIds(site);
+		List<User> users = new ArrayList<User>();
+		for (String id : visitorIDs) {
+			long userId = 0;
+			if (Jtp.isInteger(id)) {
+				userId = Long.valueOf(id);
+			} else if (includeInvisibleUsers && isInvisible(id)) {
+				userId = Long.valueOf(id.substring(INVISIBLE.length()));
+			}
+			if (userId > 0) {
+				User u = site.getUser(userId);
+				if (u != null)
+					users.add(u);
+			}
+		}
+		return users;
+	}
+
+	public static int getOnlineAnonymousUsersCount(Site site) {
+		String[] visitorIDs = getVisitorIds(site);
+		int c = 0;
+		for (String id : visitorIDs)
+			if (!Jtp.isInteger(id) && !isInvisible(id)) {
+				c++;
+		}
+		return c;
+	}
+
+	public static int getOnlineInvisibleUsersCount(Site site) {
+		String[] visitorIDs = getVisitorIds(site);
+		int c = 0;
+		for (String id : visitorIDs) {
+			if (isInvisible(id))
+				c++;
+		}
+		return c;
+	}
+
+	private static String[] getVisitorIds(Site site) {
+		synchronized(sitesMap) {
+			Map<String, Long> map = sitesMap.get(site.getId());
+			if (map == null)
+				return new String[0];
+			Set<String> set = map.keySet();
+			String[] visitorIds = new String[set.size()];
+			set.toArray(visitorIds);
+			return visitorIds;
+		}
+	}
+
+	private static boolean isInvisible(String id) {
+		return id.startsWith(INVISIBLE);
+	}
+
+	static {
+		Executors.scheduleWithFixedDelay(new Runnable(){
+			public void run(){
+				runGC();
+			}
+		}, 2*60, 2*60, TimeUnit.SECONDS); // Every 2 minutes
+	}
+
+}