[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] [metrics-web/master 3/3] Make all graph types dynamic.
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Wed, 6 Oct 2010 11:05:20 +0200
Subject: Make all graph types dynamic.
Commit: 5b12b9c499700f267a6a67c3f2383f4711928412
Adapt R code that was run in a cronjob to accept parameters and work with
Rserve.
Replace various graph image servlets with a single servlet that
understands all parameters and handles all graph types.
---
rserve/graphs.R | 353 ++++++++++++++++++++
rserve/linegraphs.R | 101 ------
rserve/rserve-init.R | 2 +-
.../torproject/ernie/web/GraphImageServlet.java | 306 +++++++++++++++++
.../ernie/web/NetworkSizeImageServlet.java | 99 ------
.../ernie/web/RelayBandwidthImageServlet.java | 99 ------
.../ernie/web/RelayPlatformsImageServlet.java | 99 ------
.../ernie/web/RelayVersionsImageServlet.java | 99 ------
war/WEB-INF/web.xml | 50 ++-
9 files changed, 691 insertions(+), 517 deletions(-)
create mode 100644 rserve/graphs.R
delete mode 100644 rserve/linegraphs.R
create mode 100644 src/org/torproject/ernie/web/GraphImageServlet.java
delete mode 100644 src/org/torproject/ernie/web/NetworkSizeImageServlet.java
delete mode 100644 src/org/torproject/ernie/web/RelayBandwidthImageServlet.java
delete mode 100644 src/org/torproject/ernie/web/RelayPlatformsImageServlet.java
delete mode 100644 src/org/torproject/ernie/web/RelayVersionsImageServlet.java
diff --git a/rserve/graphs.R b/rserve/graphs.R
new file mode 100644
index 0000000..13c44c3
--- /dev/null
+++ b/rserve/graphs.R
@@ -0,0 +1,353 @@
+plot_networksize <- function(start, end, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, avg_running AS relays FROM network_size ",
+ "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ relays <- fetch(rs, n = -1)
+ q <- paste("SELECT date, avg_running AS bridges ",
+ "FROM bridge_network_size WHERE date >= '", start,
+ "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ bridges <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, as.Date(relays$date, origin = "1970-01-01"))
+ if (length(missing) > 0)
+ relays <- rbind(relays,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ relays = NA))
+ missing <- setdiff(dates, bridges$date)
+ if (length(missing) > 0)
+ bridges <- rbind(bridges,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ bridges = NA))
+ relays <- melt(relays, id = "date")
+ bridges <- melt(bridges, id = "date")
+ networksize <- rbind(relays, bridges)
+ ggplot(networksize, aes(x = as.Date(date, "%Y-%m-%d"), y = value,
+ colour = variable)) + geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(networksize$value,
+ na.rm = TRUE))) +
+ scale_colour_hue("", breaks = c("relays", "bridges"),
+ labels = c("Relays", "Bridges")) +
+ opts(title = "Number of relays\n")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_versions <- function(start, end, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, version, relays FROM relay_versions ",
+ "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ versions <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+# TODO improve colors?
+ colours <- data.frame(version = c("0.1.0", "0.1.1", "0.1.2", "0.2.0",
+ "0.2.1", "0.2.2", "0.2.3"), colour = c("#B4674D", "#C0448F",
+ "#1F75FE", "#FF7F49", "#1CAC78", "#5D76CB", "#FF496C"),
+ stringsAsFactors = FALSE)
+ colours <- colours[colours$version %in% unique(versions$version),
+ "colour"]
+ ggplot(versions, aes(x = as.Date(date, "%Y-%m-%d"), y = relays,
+ colour = version)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "",
+ limits = c(0, max(versions$relays, na.rm = TRUE))) +
+ scale_colour_manual(name = "Tor version", values = colours) +
+ opts(title = "Relay versions\n")
+ ggsave(filename = path, width = 8,height = 5,dpi = 72)
+}
+
+plot_platforms <- function(start, end, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user=dbuser, password=dbpassword, dbname=db)
+ q <- paste("SELECT date, avg_linux, avg_darwin, avg_bsd, avg_windows, ",
+ "avg_other FROM relay_platforms WHERE date >= '", start,
+ "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ platforms <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ platforms <- melt(platforms, id = "date")
+ ggplot(platforms, aes(x = as.Date(date, "%Y-%m-%d"), y = value,
+ colour = variable)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "",
+ limits = c(0, max(platforms$value, na.rm = TRUE))) +
+ scale_colour_brewer(name = "Platform", breaks = c("avg_linux",
+ "avg_darwin", "avg_bsd", "avg_windows", "avg_other"),
+ labels = c("Linux", "Darwin", "FreeBSD", "Windows", "Other")) +
+ opts(title = "Relay platforms\n")
+ ggsave(filename = path,width = 8,height = 5,dpi = 72)
+}
+
+plot_bandwidth <- function(start, end, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, bwadvertised FROM total_bandwidth ",
+ "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ bw_desc <- fetch(rs, n = -1)
+ q <- paste("SELECT date, read, written FROM total_bwhist ",
+ "WHERE date >= '", start, "' AND date <= '", end, "' ",
+ "AND date < current_date - 1", sep = "")
+ rs <- dbSendQuery(con, q)
+ bw_hist <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ bandwidth <- rbind(data.frame(date = bw_desc$date,
+ value = bw_desc$bwadvertised, variable = "bwadv"),
+ data.frame(date = bw_hist$date, value = (bw_hist$read +
+ bw_hist$written) / (2 * 86400), variable = "bwhist"))
+ ggplot(bandwidth, aes(x = as.Date(date, "%Y-%m-%d"), y = value / 2^20,
+ colour = variable)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name="Bandwidth (MiB/s)",
+ limits = c(0, max(bandwidth$value, na.rm = TRUE) / 2^20)) +
+ scale_colour_hue(name = "", breaks = c("bwadv", "bwhist"),
+ labels = c("Advertised bandwidth", "Bandwidth history")) +
+ opts(title = "Total relay bandwidth", legend.position = "top")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_relayflags <- function(start, end, flags, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ columns <- paste("avg_", tolower(flags), sep = "", collapse = ", ")
+ q <- paste("SELECT date, ", columns, " FROM network_size ",
+ "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ networksize <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ networksize <- melt(networksize, id = "date")
+# TODO improve colors?
+ colours <- data.frame(flag = c("Running", "Exit", "Guard", "Fast",
+ "Stable"), colour = c("black", "green", "orange", "red", "blue"),
+ stringsAsFactors = FALSE)
+ colours <- colours[colours$flag %in% flags, "colour"]
+ ggplot(networksize, aes(x = as.Date(date, "%Y-%m-%d"), y = value,
+ colour = variable)) + geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(networksize$value,
+ na.rm = TRUE))) +
+ scale_colour_manual(name = "Relay flags",
+ breaks = paste("avg_", tolower(flags), sep = ""), labels = flags,
+ values = colours) +
+ opts(title = "Number of relays with relay flags assigned\n")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_new_users <- function(start, end, country, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, 6 * requests AS users FROM dirreq_stats ",
+ "WHERE (source = '68333D0761BCF397A587A0C0B963E4A9E99EC4D3' ",
+ "OR source = 'F2044413DAC2E02E3D6BCF4735A19BCA1DE97281') ",
+ "AND date >= '", start, "' AND date <= '", end, "' AND country = '",
+ country, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ newusers <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, newusers$date)
+ if (length(missing) > 0)
+ newusers <- rbind(newusers,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ users = NA))
+ peoples <- data.frame(country = c("au", "bh", "br", "ca", "cn", "cu",
+ "de", "et", "fr", "gb", "ir", "it", "jp", "kr", "mm", "pl", "ru",
+ "sa", "se", "sy", "tn", "tm", "us", "uz", "vn", "ye"),
+ people = c("Australian", "Bahraini", "Brazilian", "Canadian",
+ "Chinese", "Cuban", "German", "Ethiopian", "French", "U.K.",
+ "Iranian", "Italian", "Japanese", "South Korean", "Burmese", "Polish",
+ "Russian", "Saudi", "Swedish", "Syrian", "Tunisian", "Turkmen",
+ "U.S.", "Uzbek", "Vietnamese", "Yemeni"), stringsAsFactors = FALSE)
+ title <- ifelse(country == "zy",
+ "Total new or returning, directly connecting Tor users (all data)\n",
+ paste("New or returning, directly connecting",
+ peoples[peoples$country == country, "people"], "Tor users\n"))
+ ggplot(newusers, aes(x = as.Date(date, "%Y-%m-%d"), y = users)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(newusers$users,
+ na.rm = TRUE))) +
+ opts(title = title)
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_direct_users <- function(start, end, country, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, 10 * requests / share AS users ",
+ "FROM dirreq_stats WHERE share >= 1 ",
+ "AND source = '8522EB98C91496E80EC238E732594D1509158E77' ",
+ "AND date >= '", start, "' AND date <= '", end, "' AND country = '",
+ country, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ directusers <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, directusers$date)
+ if (length(missing) > 0)
+ directusers <- rbind(directusers,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ users = NA))
+ peoples <- data.frame(country = c("au", "bh", "br", "ca", "cn", "cu",
+ "de", "et", "fr", "gb", "ir", "it", "jp", "kr", "mm", "pl", "ru",
+ "sa", "se", "sy", "tn", "tm", "us", "uz", "vn", "ye"),
+ people = c("Australian", "Bahraini", "Brazilian", "Canadian",
+ "Chinese", "Cuban", "German", "Ethiopian", "French", "U.K.",
+ "Iranian", "Italian", "Japanese", "South Korean", "Burmese", "Polish",
+ "Russian", "Saudi", "Swedish", "Syrian", "Tunisian", "Turkmen",
+ "U.S.", "Uzbek", "Vietnamese", "Yemeni"), stringsAsFactors = FALSE)
+ title <- ifelse(country == "zy",
+ "Total recurring, directly connecting Tor users (all data)\n",
+ paste("Recurring, directly connecting",
+ peoples[peoples$country == country, "people"], "Tor users\n"))
+ ggplot(directusers, aes(x = as.Date(date, "%Y-%m-%d"), y = users)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(directusers$users,
+ na.rm = TRUE))) +
+ opts(title = title)
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_bridge_users <- function(start, end, country, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, users FROM bridge_stats ",
+ "WHERE date >= '", start, "' AND date <= '", end, "' ",
+ "AND date < (SELECT MAX(date) FROM bridge_stats) ",
+ "AND country = '", country, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ bridgeusers <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, bridgeusers$date)
+ if (length(missing) > 0)
+ bridgeusers <- rbind(bridgeusers,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ users = NA))
+ peoples <- data.frame(country = c("au", "bh", "br", "ca", "cn", "cu",
+ "de", "et", "fr", "gb", "ir", "it", "jp", "kr", "mm", "pl", "ru",
+ "sa", "se", "sy", "tn", "tm", "us", "uz", "vn", "ye"),
+ people = c("Australian", "Bahraini", "Brazilian", "Canadian",
+ "Chinese", "Cuban", "German", "Ethiopian", "French", "U.K.",
+ "Iranian", "Italian", "Japanese", "South Korean", "Burmese", "Polish",
+ "Russian", "Saudi", "Swedish", "Syrian", "Tunisian", "Turkmen",
+ "U.S.", "Uzbek", "Vietnamese", "Yemeni"), stringsAsFactors = FALSE)
+ title <- ifelse(country == "zy",
+ "Total users via bridges (all data)\n",
+ paste(peoples[peoples$country == country, "people"],
+ "users via bridges\n"))
+ ggplot(bridgeusers, aes(x = as.Date(date, "%Y-%m-%d"), y = users)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(bridgeusers$users,
+ na.rm = TRUE))) +
+ opts(title = title)
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_gettor <- function(start, end, bundle, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ condition <- ifelse(bundle == "all", "<> 'none'",
+ paste("LIKE 'tor-%browser-bundle_", tolower(bundle), "'", sep = ""))
+ q <- paste("SELECT date, SUM(downloads) AS downloads ",
+ "FROM gettor_stats WHERE bundle ", condition, " AND date >= '",
+ start, "' AND date <= '", end, "' GROUP BY date", sep = "")
+ rs <- dbSendQuery(con, q)
+ downloads <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, downloads$date)
+ if (length(missing) > 0)
+ downloads <- rbind(downloads,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ users = NA))
+ title <- ifelse(bundle == "all",
+ "Total packages requested from GetTor per day\n",
+ paste("Tor Browser Bundles (", bundle,
+ ") requested from GetTor per day\n", sep = ""))
+ ggplot(downloads, aes(x = as.Date(date, "%Y-%m-%d"), y = downloads)) +
+ geom_line(size = 1) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, max(downloads$downloads,
+ na.rm = TRUE))) +
+ opts(title = title)
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_torperf <- function(start, end, source, filesize, path) {
+ drv <- dbDriver("PostgreSQL")
+ con <- dbConnect(drv, user = dbuser, password = dbpassword, dbname = db)
+ q <- paste("SELECT date, q1, md, q3 FROM torperf_stats ",
+ "WHERE source = '", paste(source, filesize, sep = "-"),
+ "' AND date >= '", start, "' AND date <= '", end, "'", sep = "")
+ rs <- dbSendQuery(con, q)
+ torperf <- fetch(rs, n = -1)
+ dbDisconnect(con)
+ dbUnloadDriver(drv)
+ dates <- seq(from = as.Date(start, "%Y-%m-%d"),
+ to = as.Date(end, "%Y-%m-%d"), by="1 day")
+ missing <- setdiff(dates, torperf$date)
+ if (length(missing) > 0)
+ torperf <- rbind(torperf,
+ data.frame(date = as.Date(missing, origin = "1970-01-01"),
+ q1 = NA, md = NA, q3 = NA))
+ colours <- data.frame(source = c("siv", "moria", "torperf"),
+ colour = c("#0000EE", "#EE0000", "#00CD00"),
+ stringsAsFactors = FALSE)
+ colour <- colours[colours$source == source, "colour"]
+ filesizes <- data.frame(filesizes = c("5mb", "1mb", "50kb"),
+ label = c("5 MiB", "1 MiB", "50 KiB"), stringsAsFactors = FALSE)
+ filesizeStr <- filesizes[filesizes$filesize == filesize, "label"]
+ maxY <- max(torperf$q3, na.rm = TRUE)
+ ggplot(torperf, aes(x = as.Date(date, "%Y-%m-%d"), y = md/1e3,
+ fill = "line")) +
+ geom_line(colour = colour, size = 0.75) +
+ geom_ribbon(data = torperf, aes(x = date, ymin = q1/1e3,
+ ymax = q3/1e3, fill = "ribbon")) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = "")) +
+ scale_y_continuous(name = "", limits = c(0, maxY) / 1e3) +
+ coord_cartesian(ylim = c(0, 0.8 * maxY / 1e3)) +
+ scale_fill_manual(name = paste("Measured times on",
+ source, "per day"),
+ breaks = c("line", "ribbon"),
+ labels = c("Median", "1st to 3rd quartile"),
+ values = paste(colour, c("", "66"), sep = "")) +
+ opts(title = paste("Time in seconds to complete", filesizeStr,
+ "request"), legend.position = "top")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
diff --git a/rserve/linegraphs.R b/rserve/linegraphs.R
deleted file mode 100644
index 3e40298..0000000
--- a/rserve/linegraphs.R
+++ /dev/null
@@ -1,101 +0,0 @@
-plot_networksize_line <- function(start, end, path) {
- drv <- dbDriver("PostgreSQL")
- con <- dbConnect(drv, user=dbuser, password=dbpassword, dbname=db)
- q <- paste("SELECT date, avg_running, avg_exit, avg_guard ",
- "FROM network_size WHERE date >= '", start, "' AND date <= '", end,
- "'", sep = "")
- rs <- dbSendQuery(con, q)
- networksize <- fetch(rs,n=-1)
- networksize <- melt(networksize, id="date")
- ggplot(networksize, aes(x = as.Date(date, "%Y-%m-%d"), y = value,
- colour = variable)) + geom_line(size=1) +
- scale_x_date(name = paste("\nThe Tor Project - ",
- "https://metrics.torproject.org/", sep = "")) +
- scale_y_continuous(name="", limits = c(0, max(networksize$value,
- na.rm = TRUE))) +
- scale_colour_hue("",breaks=c("avg_running","avg_exit","avg_guard"),
- labels=c("Total","Exit","Guard")) +
- opts(title = "Number of relays\n")
- ggsave(filename=path, width=8, height=5, dpi=72)
- dbDisconnect(con)
- dbUnloadDriver(drv)
-}
-plot_versions_line <- function(start, end, path) {
- drv <- dbDriver("PostgreSQL")
- con <- dbConnect(drv, user=dbuser, password=dbpassword, dbname=db)
- q <- paste("SELECT date, version, relays FROM relay_versions ",
- "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
- rs <- dbSendQuery(con, q)
- v <- fetch(rs,n=-1)
- colours <- data.frame(version = c("0.1.0", "0.1.1", "0.1.2", "0.2.0",
- "0.2.1", "0.2.2", "0.2.3"), colour = c("#B4674D", "#C0448F",
- "#1F75FE", "#FF7F49", "#1CAC78", "#5D76CB", "#FF496C"),
- stringsAsFactors = FALSE)
- colours <- colours[colours$version %in% unique(v$version), "colour"]
- ggplot(v, aes(x = as.Date(date, "%Y-%m-%d"), y = relays,
- colour = version)) +
- geom_line(size=1) +
- scale_x_date(name = paste("\nThe Tor Project - ",
- "https://metrics.torproject.org/", sep = "")) +
- scale_y_continuous(name= "",
- limits = c(0, max(v$relays, na.rm = TRUE))) +
- scale_colour_manual(name = "Tor version", values = colours) +
- opts(title = "Relay versions\n")
- ggsave(filename=path, width=8,height=5,dpi=72)
- dbDisconnect(con)
- dbUnloadDriver(drv)
-}
-plot_platforms_line <- function(start, end, path) {
- drv <- dbDriver("PostgreSQL")
- con <- dbConnect(drv, user=dbuser, password=dbpassword, dbname=db)
- q <- paste("SELECT date, avg_linux, avg_darwin, avg_bsd, avg_windows, ",
- "avg_other FROM relay_platforms WHERE date >= '", start,
- "' AND date <= '", end, "'", sep = "")
- rs <- dbSendQuery(con, q)
- p <- fetch(rs,n=-1)
- p <- melt(p, id="date")
- ggplot(p, aes(x=as.Date(date, "%Y-%m-%d"), y=value, colour=variable)) +
- geom_line(size=1) +
- scale_x_date(name = paste("\nThe Tor Project - ",
- "https://metrics.torproject.org/", sep = "")) +
- scale_y_continuous(name="",
- limits=c(0,max(p$value, na.rm=TRUE))) +
- scale_colour_brewer(name="Platform", breaks=c("avg_linux",
- "avg_darwin", "avg_bsd", "avg_windows", "avg_other"),
- labels=c("Linux", "Darwin", "FreeBSD", "Windows", "Other")) +
- opts(title = "Relay platforms\n")
- ggsave(filename=path,width=8,height=5,dpi=72)
- dbDisconnect(con)
- dbUnloadDriver(drv)
-}
-plot_bandwidth_line <- function(start, end, path) {
- drv <- dbDriver("PostgreSQL")
- con <- dbConnect(drv, user=dbuser, password=dbpassword, dbname=db)
- q1 <- paste("SELECT date, bwadvertised FROM total_bandwidth ",
- "WHERE date >= '", start, "' AND date <= '", end, "'", sep = "")
- rs1 <- dbSendQuery(con, q1)
- bw_desc <- fetch(rs1, n = -1)
- q2 <- paste("SELECT date, read, written FROM total_bwhist ",
- "WHERE date >= '", start, "' AND date <= '", end, "' ",
- "AND date < current_date - 1", sep = "")
- rs2 <- dbSendQuery(con, q2)
- bw_hist <- fetch(rs2, n = -1)
- bandwidth <- rbind(data.frame(date = bw_desc$date,
- value = bw_desc$bwadvertised, variable = "bwadv"),
- data.frame(date = bw_hist$date, value = (bw_hist$read +
- bw_hist$written) / (2 * 86400), variable = "bwhist"))
- ggplot(bandwidth, aes(x = as.Date(date, "%Y-%m-%d"), y = value / 2^20,
- colour = variable)) +
- geom_line(size=1) +
- scale_x_date(name = paste("\nThe Tor Project - ",
- "https://metrics.torproject.org/", sep = "")) +
- scale_y_continuous(name="Bandwidth (MiB/s)",
- limits = c(0, max(bandwidth$value, na.rm = TRUE) / 2^20)) +
- scale_colour_hue(name = "", breaks = c("bwadv", "bwhist"),
- labels = c("Advertised bandwidth", "Bandwidth history")) +
- opts(title = "Total relay bandwidth", legend.position = "top")
- ggsave(filename = path, width = 8, height = 5, dpi = 72)
- dbDisconnect(con)
- dbUnloadDriver(drv)
-}
-
diff --git a/rserve/rserve-init.R b/rserve/rserve-init.R
index d0d2eaf..79e9a75 100644
--- a/rserve/rserve-init.R
+++ b/rserve/rserve-init.R
@@ -13,4 +13,4 @@ db = "tordir"
dbuser = "ernie"
dbpassword= ""
-source('linegraphs.R')
+source('graphs.R')
diff --git a/src/org/torproject/ernie/web/GraphImageServlet.java b/src/org/torproject/ernie/web/GraphImageServlet.java
new file mode 100644
index 0000000..304b103
--- /dev/null
+++ b/src/org/torproject/ernie/web/GraphImageServlet.java
@@ -0,0 +1,306 @@
+package org.torproject.ernie.web;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+/**
+ * Servlet that reads an HTTP request for a graph image, asks the
+ * GraphController to generate this graph if it's not in the cache, and
+ * returns the image bytes to the client.
+ */
+public class GraphImageServlet extends HttpServlet {
+
+ private GraphController graphController;
+
+ private SimpleDateFormat dateFormat;
+
+ /* Available graphs with corresponding parameter lists. */
+ private Map<String, String> availableGraphs;
+
+ /* Known parameters and parameter values. */
+ private Map<String, String> knownParameterValues;
+
+ public GraphImageServlet() {
+ this.graphController = GraphController.getInstance();
+ this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ this.availableGraphs = new HashMap<String, String>();
+ this.availableGraphs.put("networksize", "start,end,filename");
+ this.availableGraphs.put("relayflags", "start,end,flag,filename");
+ this.availableGraphs.put("versions", "start,end,filename");
+ this.availableGraphs.put("platforms", "start,end,filename");
+ this.availableGraphs.put("bandwidth", "start,end,filename");
+ this.availableGraphs.put("new-users", "start,end,country,filename");
+ this.availableGraphs.put("direct-users",
+ "start,end,country,filename");
+ this.availableGraphs.put("bridge-users",
+ "start,end,country,filename");
+ this.availableGraphs.put("gettor", "start,end,bundle,filename");
+ this.availableGraphs.put("torperf",
+ "start,end,source,filesize,filename");
+
+ this.knownParameterValues = new HashMap<String, String>();
+ this.knownParameterValues.put("flag",
+ "Running,Exit,Guard,Fast,Stable");
+ this.knownParameterValues.put("country", "au,bh,br,ca,cn,cu,de,et,"
+ + "fr,gb,ir,it,jp,kr,mm,pl,ru,sa,se,sy,tn,tm,us,uz,vn,ye");
+ this.knownParameterValues.put("bundle", "en,zh_cn,fa");
+ this.knownParameterValues.put("source", "siv,moria,torperf");
+ this.knownParameterValues.put("filesize", "50kb,1mb,5mb");
+ }
+
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws IOException,
+ ServletException {
+
+ /* Find out which graph type was requested and make sure we know this
+ * graph type. */
+ String requestedGraph = request.getRequestURI();
+ if (requestedGraph == null || requestedGraph.length() < 6) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ requestedGraph = requestedGraph.substring(1,
+ requestedGraph.length() - 4);
+ if (!this.availableGraphs.containsKey(requestedGraph)) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+
+ /* Find out which other parameters are supported by this graph type
+ * and parse them if they are given. */
+ Set<String> supportedGraphParameters = new HashSet<String>(Arrays.
+ asList(this.availableGraphs.get(requestedGraph).split(",")));
+ Map<String, String[]> recognizedGraphParameters =
+ new HashMap<String, String[]>();
+
+ /* Parse start and end dates if supported by the graph type. If
+ * neither start nor end date are given, set the default date range to
+ * the past 90 days. */
+ if (supportedGraphParameters.contains("start") ||
+ supportedGraphParameters.contains("end")) {
+ String startParameter = request.getParameter("start");
+ String endParameter = request.getParameter("end");
+ if (startParameter == null && endParameter == null) {
+ /* If no start and end parameters are given, set default date
+ * range to the past 90 days. */
+ long now = System.currentTimeMillis();
+ startParameter = dateFormat.format(now
+ - 90L * 24L * 60L * 60L * 1000L);
+ endParameter = dateFormat.format(now);
+ } else if (startParameter != null && endParameter != null) {
+ long startTimestamp = -1L, endTimestamp = -1L;
+ try {
+ startTimestamp = dateFormat.parse(startParameter).getTime();
+ endTimestamp = dateFormat.parse(endParameter).getTime();
+ } catch (ParseException e) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ /* The parameters are dates. Good. Does end not precede start? */
+ if (startTimestamp > endTimestamp) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ /* And while we're at it, make sure both parameters lie in this
+ * century. */
+ if (!startParameter.startsWith("20") ||
+ !endParameter.startsWith("20")) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ /* Looks like sane parameters. Re-format them to get a canonical
+ * version, not something like 2010-1-1, 2010-01-1, etc. */
+ startParameter = dateFormat.format(startTimestamp);
+ endParameter = dateFormat.format(endTimestamp);
+ } else {
+ /* Either none or both of start and end need to be set. */
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ recognizedGraphParameters.put("start",
+ new String[] { startParameter });
+ recognizedGraphParameters.put("end", new String[] { endParameter });
+ }
+
+ /* Parse relay flags if supported by the graph type. If no relay flags
+ * are passed or none of them have been recognized, use the set of all
+ * known flags as default. */
+ if (supportedGraphParameters.contains("flag")) {
+ String[] flagParameters = request.getParameterValues("flag");
+ List<String> knownFlags = Arrays.asList(
+ this.knownParameterValues.get("flag").split(","));
+ if (flagParameters != null) {
+ for (String flag : flagParameters) {
+ if (flag == null || flag.length() == 0 ||
+ !knownFlags.contains(flag)) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ }
+ } else {
+ flagParameters = this.knownParameterValues.get("flag").split(",");
+ }
+ recognizedGraphParameters.put("flag", flagParameters);
+ }
+
+ /* Parse country codes if supported by the graph type. If no countries
+ * are passed, use country code "zy" (all countries) as default. */
+ if (supportedGraphParameters.contains("country")) {
+ String[] countryParameters = request.getParameterValues("country");
+ List<String> knownCountries = Arrays.asList(
+ this.knownParameterValues.get("country").split(","));
+ if (countryParameters != null) {
+ for (String country : countryParameters) {
+ if (country == null || country.length() == 0 ||
+ !knownCountries.contains(country)) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ }
+ } else {
+ countryParameters = new String[] { "zy" };
+ }
+ recognizedGraphParameters.put("country", countryParameters);
+ }
+
+ /* Parse GetTor bundle if supported by the graph type. Only a single
+ * bundle can be passed. If no bundle is passed, use "all" as
+ * default. */
+ if (supportedGraphParameters.contains("bundle")) {
+ String[] bundleParameter = request.getParameterValues("bundle");
+ List<String> knownBundles = Arrays.asList(
+ this.knownParameterValues.get("bundle").split(","));
+ if (bundleParameter != null) {
+ if (bundleParameter.length != 1) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ if (bundleParameter[0].length() == 0 ||
+ !knownBundles.contains(bundleParameter[0])) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ } else {
+ bundleParameter = new String[] { "all" };
+ }
+ recognizedGraphParameters.put("bundle", bundleParameter);
+ }
+
+ /* Parse torperf data source if supported by the graph type. Only a
+ * single source can be passed. If no source is passed, use "torperf"
+ * as default. */
+ if (supportedGraphParameters.contains("source")) {
+ String[] sourceParameter = request.getParameterValues("source");
+ List<String> knownSources = Arrays.asList(
+ this.knownParameterValues.get("source").split(","));
+ if (sourceParameter != null) {
+ if (sourceParameter.length != 1) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ if (sourceParameter[0].length() == 0 ||
+ !knownSources.contains(sourceParameter[0])) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ } else {
+ sourceParameter = new String[] { "torperf" };
+ }
+ recognizedGraphParameters.put("source", sourceParameter);
+ }
+
+ /* Parse torperf file size if supported by the graph type. Only a
+ * single file size can be passed. If no file size is passed, use
+ * "50kb" as default. */
+ if (supportedGraphParameters.contains("filesize")) {
+ String[] filesizeParameter = request.getParameterValues("filesize");
+ List<String> knownFilesizes = Arrays.asList(
+ this.knownParameterValues.get("filesize").split(","));
+ if (filesizeParameter != null) {
+ if (filesizeParameter.length != 1) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ if (filesizeParameter[0].length() == 0 ||
+ !knownFilesizes.contains(filesizeParameter[0])) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ } else {
+ filesizeParameter = new String[] { "50kb" };
+ }
+ recognizedGraphParameters.put("filesize", filesizeParameter);
+ }
+
+ /* Prepare filename and R query string. */
+ StringBuilder rQueryBuilder = new StringBuilder("plot_"
+ + requestedGraph.replaceAll("-", "_") + "("),
+ imageFilenameBuilder = new StringBuilder(requestedGraph);
+ List<String> requiredGraphParameters = Arrays.asList(
+ this.availableGraphs.get(requestedGraph).split(","));
+ for (String graphParameter : requiredGraphParameters) {
+ if (graphParameter.equals("filename")) {
+ break;
+ }
+ if (!recognizedGraphParameters.containsKey(graphParameter)) {
+ /* We should have parsed this parameter above! */
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "Missing parameter: " + graphParameter);
+ return;
+ }
+ String[] parameterValues = recognizedGraphParameters.get(
+ graphParameter);
+ if (parameterValues.length == 0) {
+ /* We should not have added a zero-length array here! */
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "Missing parameter: " + graphParameter);
+ return;
+ }
+ for (String param : parameterValues) {
+ imageFilenameBuilder.append("-" + param);
+ }
+ if (parameterValues.length < 2) {
+ rQueryBuilder.append("'" + parameterValues[0] + "', ");
+ } else {
+ rQueryBuilder.append("c(");
+ for (int i = 0; i < parameterValues.length - 1; i++) {
+ rQueryBuilder.append("'" + parameterValues[i] + "', ");
+ }
+ rQueryBuilder.append("'" + parameterValues[
+ parameterValues.length - 1] + "'), ");
+ }
+ }
+ imageFilenameBuilder.append(".png");
+ String imageFilename = imageFilenameBuilder.toString();
+ rQueryBuilder.append("'%s')");
+ String rQuery = rQueryBuilder.toString();
+
+ /* Request graph from graph controller, which either returns it from
+ * its cache or asks Rserve to generate it. */
+ byte[] graphBytes = graphController.generateGraph(rQuery,
+ imageFilename);
+
+ /* Make sure that we have a graph to return. */
+ if (graphBytes == null) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ /* Write graph bytes to response. */
+ BufferedOutputStream output = null;
+ response.setContentType("image/png");
+ response.setHeader("Content-Length",
+ String.valueOf(graphBytes.length));
+ response.setHeader("Content-Disposition",
+ "inline; filename=\"" + imageFilename + "\"");
+ output = new BufferedOutputStream(response.getOutputStream(), 1024);
+ output.write(graphBytes, 0, graphBytes.length);
+ output.close();
+ }
+}
+
diff --git a/src/org/torproject/ernie/web/NetworkSizeImageServlet.java b/src/org/torproject/ernie/web/NetworkSizeImageServlet.java
deleted file mode 100644
index c31da86..0000000
--- a/src/org/torproject/ernie/web/NetworkSizeImageServlet.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.io.*;
-import java.text.*;
-import java.util.*;
-import javax.servlet.*;
-import javax.servlet.http.*;
-
-/* TODO This class shares a lot of code with the other *ImageServlet
- * classes. We should at some point try harder to reuse code. But let's
- * wait until we know what parameters besides start and end time will be
- * shared between these classes. We'll likely want to add more parameters
- * that reduce the code that can be re-used between servlets. */
-
-public class NetworkSizeImageServlet extends HttpServlet {
-
- private GraphController graphController;
-
- private SimpleDateFormat dateFormat;
-
- public NetworkSizeImageServlet() {
- this.graphController = GraphController.getInstance();
- this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws IOException,
- ServletException {
-
- /* Check parameters. */
- String startParameter = request.getParameter("start");
- String endParameter = request.getParameter("end");
- if (startParameter == null && endParameter == null) {
- /* If no parameters are given, set default date range to the past 30
- * days. */
- long now = System.currentTimeMillis();
- startParameter = dateFormat.format(now
- - 30L * 24L * 60L * 60L * 1000L);
- endParameter = dateFormat.format(now);
- } else if (startParameter != null && endParameter != null) {
- long startTimestamp = -1L, endTimestamp = -1L;
- try {
- startTimestamp = dateFormat.parse(startParameter).getTime();
- endTimestamp = dateFormat.parse(endParameter).getTime();
- } catch (ParseException e) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* The parameters are dates. Good. Does end not precede start? */
- if (startTimestamp > endTimestamp) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* And while we're at it, make sure both parameters lie in this
- * century. */
- if (!startParameter.startsWith("20") ||
- !endParameter.startsWith("20")) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* Looks like sane parameters. Re-format them to get a canonical
- * version, not something like 2010-1-1, 2010-01-1, etc. */
- startParameter = dateFormat.format(startTimestamp);
- endParameter = dateFormat.format(endTimestamp);
- } else {
- /* Either none or both of start and end need to be set. */
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- /* Request graph from graph controller, which either returns it from
- * its cache or asks Rserve to generate it. */
- String imageFilename = "networksize-" + startParameter + "-"
- + endParameter + ".png";
- String rQuery = "plot_networksize_line('" + startParameter + "', '"
- + endParameter + "', '%s')";
- byte[] graphBytes = graphController.generateGraph(rQuery,
- imageFilename);
-
- /* Make sure that we have a graph to return. */
- if (graphBytes == null) {
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- /* Write graph bytes to response. */
- BufferedOutputStream output = null;
- response.setContentType("image/png");
- response.setHeader("Content-Length",
- String.valueOf(graphBytes.length));
- response.setHeader("Content-Disposition",
- "inline; filename=\"" + imageFilename + "\"");
- output = new BufferedOutputStream(response.getOutputStream(), 1024);
- output.write(graphBytes, 0, graphBytes.length);
- output.close();
- }
-}
-
diff --git a/src/org/torproject/ernie/web/RelayBandwidthImageServlet.java b/src/org/torproject/ernie/web/RelayBandwidthImageServlet.java
deleted file mode 100644
index 83475a4..0000000
--- a/src/org/torproject/ernie/web/RelayBandwidthImageServlet.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.io.*;
-import java.text.*;
-import java.util.*;
-import javax.servlet.*;
-import javax.servlet.http.*;
-
-/* TODO This class shares a lot of code with the other *ImageServlet
- * classes. We should at some point try harder to reuse code. But let's
- * wait until we know what parameters besides start and end time will be
- * shared between these classes. We'll likely want to add more parameters
- * that reduce the code that can be re-used between servlets. */
-
-public class RelayBandwidthImageServlet extends HttpServlet {
-
- private GraphController graphController;
-
- private SimpleDateFormat dateFormat;
-
- public RelayBandwidthImageServlet() {
- this.graphController = GraphController.getInstance();
- this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws IOException,
- ServletException {
-
- /* Check parameters. */
- String startParameter = request.getParameter("start");
- String endParameter = request.getParameter("end");
- if (startParameter == null && endParameter == null) {
- /* If no parameters are given, set default date range to the past 30
- * days. */
- long now = System.currentTimeMillis();
- startParameter = dateFormat.format(now
- - 30L * 24L * 60L * 60L * 1000L);
- endParameter = dateFormat.format(now);
- } else if (startParameter != null && endParameter != null) {
- long startTimestamp = -1L, endTimestamp = -1L;
- try {
- startTimestamp = dateFormat.parse(startParameter).getTime();
- endTimestamp = dateFormat.parse(endParameter).getTime();
- } catch (ParseException e) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* The parameters are dates. Good. Does end not precede start? */
- if (startTimestamp > endTimestamp) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* And while we're at it, make sure both parameters lie in this
- * century. */
- if (!startParameter.startsWith("20") ||
- !endParameter.startsWith("20")) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* Looks like sane parameters. Re-format them to get a canonical
- * version, not something like 2010-1-1, 2010-01-1, etc. */
- startParameter = dateFormat.format(startTimestamp);
- endParameter = dateFormat.format(endTimestamp);
- } else {
- /* Either none or both of start and end need to be set. */
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- /* Request graph from graph controller, which either returns it from
- * its cache or asks Rserve to generate it. */
- String imageFilename = "bandwidth-" + startParameter + "-"
- + endParameter + ".png";
- String rQuery = "plot_bandwidth_line('" + startParameter + "', '"
- + endParameter + "', '%s')";
- byte[] graphBytes = graphController.generateGraph(rQuery,
- imageFilename);
-
- /* Make sure that we have a graph to return. */
- if (graphBytes == null) {
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- /* Write graph bytes to response. */
- BufferedOutputStream output = null;
- response.setContentType("image/png");
- response.setHeader("Content-Length",
- String.valueOf(graphBytes.length));
- response.setHeader("Content-Disposition",
- "inline; filename=\"" + imageFilename + "\"");
- output = new BufferedOutputStream(response.getOutputStream(), 1024);
- output.write(graphBytes, 0, graphBytes.length);
- output.close();
- }
-}
-
diff --git a/src/org/torproject/ernie/web/RelayPlatformsImageServlet.java b/src/org/torproject/ernie/web/RelayPlatformsImageServlet.java
deleted file mode 100644
index ee3efec..0000000
--- a/src/org/torproject/ernie/web/RelayPlatformsImageServlet.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.io.*;
-import java.text.*;
-import java.util.*;
-import javax.servlet.*;
-import javax.servlet.http.*;
-
-/* TODO This class shares a lot of code with the other *ImageServlet
- * classes. We should at some point try harder to reuse code. But let's
- * wait until we know what parameters besides start and end time will be
- * shared between these classes. We'll likely want to add more parameters
- * that reduce the code that can be re-used between servlets. */
-
-public class RelayPlatformsImageServlet extends HttpServlet {
-
- private GraphController graphController;
-
- private SimpleDateFormat dateFormat;
-
- public RelayPlatformsImageServlet() {
- this.graphController = GraphController.getInstance();
- this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws IOException,
- ServletException {
-
- /* Check parameters. */
- String startParameter = request.getParameter("start");
- String endParameter = request.getParameter("end");
- if (startParameter == null && endParameter == null) {
- /* If no parameters are given, set default date range to the past 30
- * days. */
- long now = System.currentTimeMillis();
- startParameter = dateFormat.format(now
- - 30L * 24L * 60L * 60L * 1000L);
- endParameter = dateFormat.format(now);
- } else if (startParameter != null && endParameter != null) {
- long startTimestamp = -1L, endTimestamp = -1L;
- try {
- startTimestamp = dateFormat.parse(startParameter).getTime();
- endTimestamp = dateFormat.parse(endParameter).getTime();
- } catch (ParseException e) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* The parameters are dates. Good. Does end not precede start? */
- if (startTimestamp > endTimestamp) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* And while we're at it, make sure both parameters lie in this
- * century. */
- if (!startParameter.startsWith("20") ||
- !endParameter.startsWith("20")) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* Looks like sane parameters. Re-format them to get a canonical
- * version, not something like 2010-1-1, 2010-01-1, etc. */
- startParameter = dateFormat.format(startTimestamp);
- endParameter = dateFormat.format(endTimestamp);
- } else {
- /* Either none or both of start and end need to be set. */
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- /* Request graph from graph controller, which either returns it from
- * its cache or asks Rserve to generate it. */
- String imageFilename = "platforms-" + startParameter + "-"
- + endParameter + ".png";
- String rQuery = "plot_platforms_line('" + startParameter + "', '"
- + endParameter + "', '%s')";
- byte[] graphBytes = graphController.generateGraph(rQuery,
- imageFilename);
-
- /* Make sure that we have a graph to return. */
- if (graphBytes == null) {
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- /* Write graph bytes to response. */
- BufferedOutputStream output = null;
- response.setContentType("image/png");
- response.setHeader("Content-Length",
- String.valueOf(graphBytes.length));
- response.setHeader("Content-Disposition",
- "inline; filename=\"" + imageFilename + "\"");
- output = new BufferedOutputStream(response.getOutputStream(), 1024);
- output.write(graphBytes, 0, graphBytes.length);
- output.close();
- }
-}
-
diff --git a/src/org/torproject/ernie/web/RelayVersionsImageServlet.java b/src/org/torproject/ernie/web/RelayVersionsImageServlet.java
deleted file mode 100644
index 36c2684..0000000
--- a/src/org/torproject/ernie/web/RelayVersionsImageServlet.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.io.*;
-import java.text.*;
-import java.util.*;
-import javax.servlet.*;
-import javax.servlet.http.*;
-
-/* TODO This class shares a lot of code with the other *ImageServlet
- * classes. We should at some point try harder to reuse code. But let's
- * wait until we know what parameters besides start and end time will be
- * shared between these classes. We'll likely want to add more parameters
- * that reduce the code that can be re-used between servlets. */
-
-public class RelayVersionsImageServlet extends HttpServlet {
-
- private GraphController graphController;
-
- private SimpleDateFormat dateFormat;
-
- public RelayVersionsImageServlet() {
- this.graphController = GraphController.getInstance();
- this.dateFormat = new SimpleDateFormat("yyyy-MM-dd");
- this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- public void doGet(HttpServletRequest request,
- HttpServletResponse response) throws IOException,
- ServletException {
-
- /* Check parameters. */
- String startParameter = request.getParameter("start");
- String endParameter = request.getParameter("end");
- if (startParameter == null && endParameter == null) {
- /* If no parameters are given, set default date range to the past 30
- * days. */
- long now = System.currentTimeMillis();
- startParameter = dateFormat.format(now
- - 30L * 24L * 60L * 60L * 1000L);
- endParameter = dateFormat.format(now);
- } else if (startParameter != null && endParameter != null) {
- long startTimestamp = -1L, endTimestamp = -1L;
- try {
- startTimestamp = dateFormat.parse(startParameter).getTime();
- endTimestamp = dateFormat.parse(endParameter).getTime();
- } catch (ParseException e) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* The parameters are dates. Good. Does end not precede start? */
- if (startTimestamp > endTimestamp) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* And while we're at it, make sure both parameters lie in this
- * century. */
- if (!startParameter.startsWith("20") ||
- !endParameter.startsWith("20")) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
- /* Looks like sane parameters. Re-format them to get a canonical
- * version, not something like 2010-1-1, 2010-01-1, etc. */
- startParameter = dateFormat.format(startTimestamp);
- endParameter = dateFormat.format(endTimestamp);
- } else {
- /* Either none or both of start and end need to be set. */
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- /* Request graph from graph controller, which either returns it from
- * its cache or asks Rserve to generate it. */
- String imageFilename = "versions-" + startParameter + "-"
- + endParameter + ".png";
- String rQuery = "plot_versions_line('" + startParameter + "', '"
- + endParameter + "', '%s')";
- byte[] graphBytes = graphController.generateGraph(rQuery,
- imageFilename);
-
- /* Make sure that we have a graph to return. */
- if (graphBytes == null) {
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- /* Write graph bytes to response. */
- BufferedOutputStream output = null;
- response.setContentType("image/png");
- response.setHeader("Content-Length",
- String.valueOf(graphBytes.length));
- response.setHeader("Content-Disposition",
- "inline; filename=\"" + imageFilename + "\"");
- output = new BufferedOutputStream(response.getOutputStream(), 1024);
- output.write(graphBytes, 0, graphBytes.length);
- output.close();
- }
-}
-
diff --git a/war/WEB-INF/web.xml b/war/WEB-INF/web.xml
index 29d3579..ed0ce53 100644
--- a/war/WEB-INF/web.xml
+++ b/war/WEB-INF/web.xml
@@ -38,37 +38,49 @@
<url-pattern>/relay-search.html</url-pattern>
</servlet-mapping>
<servlet>
- <servlet-name>NetworkSizeImageServlet</servlet-name>
- <servlet-class>org.torproject.ernie.web.NetworkSizeImageServlet</servlet-class>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <servlet-class>org.torproject.ernie.web.GraphImageServlet</servlet-class>
</servlet>
<servlet-mapping>
- <servlet-name>NetworkSizeImageServlet</servlet-name>
+ <servlet-name>GraphImageServlet</servlet-name>
<url-pattern>/networksize.png</url-pattern>
</servlet-mapping>
- <servlet>
- <servlet-name>RelayPlatformsImageServlet</servlet-name>
- <servlet-class>org.torproject.ernie.web.RelayPlatformsImageServlet</servlet-class>
- </servlet>
<servlet-mapping>
- <servlet-name>RelayPlatformsImageServlet</servlet-name>
- <url-pattern>/platforms.png</url-pattern>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/relayflags.png</url-pattern>
</servlet-mapping>
- <servlet>
- <servlet-name>RelayVersionsImageServlet</servlet-name>
- <servlet-class>org.torproject.ernie.web.RelayVersionsImageServlet</servlet-class>
- </servlet>
<servlet-mapping>
- <servlet-name>RelayVersionsImageServlet</servlet-name>
+ <servlet-name>GraphImageServlet</servlet-name>
<url-pattern>/versions.png</url-pattern>
</servlet-mapping>
- <servlet>
- <servlet-name>RelayBandwidthImageServlet</servlet-name>
- <servlet-class>org.torproject.ernie.web.RelayBandwidthImageServlet</servlet-class>
- </servlet>
<servlet-mapping>
- <servlet-name>RelayBandwidthImageServlet</servlet-name>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/platforms.png</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
<url-pattern>/bandwidth.png</url-pattern>
</servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/new-users.png</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/direct-users.png</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/bridge-users.png</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/gettor.png</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>GraphImageServlet</servlet-name>
+ <url-pattern>/torperf.png</url-pattern>
+ </servlet-mapping>
<servlet>
<servlet-name>ExoneraTor</servlet-name>
<servlet-class>org.torproject.ernie.web.ExoneraTorServlet</servlet-class>
--
1.7.1