Who is logged in?

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com

As part of the chat software I am building, I need to know who is logged in. I’ve built a small Clojure app that will take requests from Javascript and store user info in maps inside an atom called “registry”. As yet there is no security check, but I will eventually add a check of the PHP session info stored in /var/lib/php5. Here is the core of the app, everything is working except for the PHP session check:

(ns who-is-allowed-to-vote.core
(:import (java.util Date))
(:require clojure.string clojure.java.io who-is-logged-in.memory_display)
(:use [net.cgrand.moustache :only [app delegate]]
[ring.adapter.jetty :only [run-jetty]]))

(def registry (atom {}))

(defn add-to-logged-in-registry [this-users-params]
“We assume some user is looking at a site such as wpquestions.com and the Javascript on that site is sending an Ajax request to this app, every 10 seconds, with a map of information about the user, which we need to store in the registry.”
(let [right-now (. (Date.) getTime)
new-user-entry (conj this-users-params { :updated right-now })]
(swap! registry (fn [map-of-user-maps]
(assoc (assoc map-of-user-maps (get new-user-entry “username”) {}) (get new-user-entry “username”) new-user-entry)))))

(defn is-current? [this-users-map]
“If we have not received an Ajax request from a user during the last 15 seconds then we can assume they have left the site. We need to remove them from registry.”
(let [most-recent-ping (:updated this-users-map)
allowed-cutoff (- (. (Date.) getTime) 15000)]
(if (> most-recent-ping allowed-cutoff)

(defn is-not-current? [this-users-map]
“Needed? Opposite of is-current? Used in early versions.”
(if (not (is-current? this-users-map))

(defn remove-old-registrants []
“The registry should only show people who have pinged this app during the last 15 seconds. We rebuild the registry with only those users whose maps return true from is-current?”
(def updated-registry {})
(swap! registry (fn [map-of-all-user-maps]
(into {}
(for [[username-as-key each-user-map] map-of-all-user-maps :when (is-current? each-user-map)]
(assoc updated-registry (get each-user-map “username”) each-user-map)))))))

(defn current-users [request]
“The default action of this app. Add new users to the registry, and delete the ones that are more than 15 seconds old”
(let [this-users-params (:params request)]
(add-to-logged-in-registry this-users-params)
(response (apply str (doall @registry)))))

(defn show-stats-regarding-resources-used-by-this-app [request]
(response (apply str “Memory in use (percentage/used/max-heap): ” (who-is-logged-in.memory_display/memory-usage) ” CPU usage (how-many-cpu’s/load-average): ” (who-is-logged-in.memory_display/cpu-load-usage) ” free memory in jvm: ” (conj [] (who-is-logged-in.memory_display/free-memory-in-jvm)))))

(def my-app-handler
[""] (delegate current-users)
["show-resources"] (delegate show-stats-regarding-resources-used-by-this-app)))

(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(println “App ‘Who is logged in?’ is starting”)
(println “port: ” port)
(run-jetty #’my-app-handler {:port port
:join? false})))