[tor-commits] [onionoo/master] Create node index in background thread.
karsten at torproject.org
karsten at torproject.org
Sat Apr 19 11:03:53 UTC 2014
commit bb1d0569bb8dd618b44281f7e90e3f7d9e783e00
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Sat Apr 19 10:21:20 2014 +0200
Create node index in background thread.
---
etc/web.xml.template | 14 +-
src/org/torproject/onionoo/ApplicationFactory.java | 11 +
src/org/torproject/onionoo/NodeIndexer.java | 438 ++++++++++++++++++++
src/org/torproject/onionoo/RequestHandler.java | 321 ++------------
src/org/torproject/onionoo/ResourceServlet.java | 38 +-
src/org/torproject/onionoo/ResponseBuilder.java | 16 +-
.../torproject/onionoo/ResourceServletTest.java | 23 +-
7 files changed, 526 insertions(+), 335 deletions(-)
diff --git a/etc/web.xml.template b/etc/web.xml.template
index f69314f..988d47a 100644
--- a/etc/web.xml.template
+++ b/etc/web.xml.template
@@ -12,10 +12,6 @@
org.torproject.onionoo.ResourceServlet
</servlet-class>
<init-param>
- <param-name>outDir</param-name>
- <param-value>/srv/onionoo/out/</param-value>
- </init-param>
- <init-param>
<param-name>maintenance</param-name>
<param-value>0</param-value>
</init-param>
@@ -45,5 +41,15 @@
<url-pattern>/uptime</url-pattern>
</servlet-mapping>
+ <context-param>
+ <param-name>outDir</param-name>
+ <param-value>/srv/onionoo.torproject.org/onionoo/out/</param-value>
+ </context-param>
+
+ <listener>
+ <listener-class>
+ org.torproject.onionoo.NodeIndexer
+ </listener-class>
+ </listener>
</web-app>
diff --git a/src/org/torproject/onionoo/ApplicationFactory.java b/src/org/torproject/onionoo/ApplicationFactory.java
index 98952df..44f2c17 100644
--- a/src/org/torproject/onionoo/ApplicationFactory.java
+++ b/src/org/torproject/onionoo/ApplicationFactory.java
@@ -37,4 +37,15 @@ public class ApplicationFactory {
}
return documentStoreInstance;
}
+
+ private static NodeIndexer nodeIndexerInstance;
+ public static void setNodeIndexer(NodeIndexer nodeIndexer) {
+ nodeIndexerInstance = nodeIndexer;
+ }
+ public static NodeIndexer getNodeIndexer() {
+ if (nodeIndexerInstance == null) {
+ nodeIndexerInstance = new NodeIndexer();
+ }
+ return nodeIndexerInstance;
+ }
}
diff --git a/src/org/torproject/onionoo/NodeIndexer.java b/src/org/torproject/onionoo/NodeIndexer.java
new file mode 100644
index 0000000..87ecf9f
--- /dev/null
+++ b/src/org/torproject/onionoo/NodeIndexer.java
@@ -0,0 +1,438 @@
+package org.torproject.onionoo;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+class NodeIndex {
+
+ private String relaysPublishedString;
+ public void setRelaysPublishedString(String relaysPublishedString) {
+ this.relaysPublishedString = relaysPublishedString;
+ }
+ public String getRelaysPublishedString() {
+ return relaysPublishedString;
+ }
+
+ private String bridgesPublishedString;
+ public void setBridgesPublishedString(String bridgesPublishedString) {
+ this.bridgesPublishedString = bridgesPublishedString;
+ }
+ public String getBridgesPublishedString() {
+ return bridgesPublishedString;
+ }
+
+ private List<String> relaysByConsensusWeight;
+ public void setRelaysByConsensusWeight(
+ List<String> relaysByConsensusWeight) {
+ this.relaysByConsensusWeight = relaysByConsensusWeight;
+ }
+ public List<String> getRelaysByConsensusWeight() {
+ return relaysByConsensusWeight;
+ }
+
+ private Map<String, String> relayFingerprintSummaryLines;
+ public void setRelayFingerprintSummaryLines(
+ Map<String, String> relayFingerprintSummaryLines) {
+ this.relayFingerprintSummaryLines = relayFingerprintSummaryLines;
+ }
+ public Map<String, String> getRelayFingerprintSummaryLines() {
+ return this.relayFingerprintSummaryLines;
+ }
+
+ private Map<String, String> bridgeFingerprintSummaryLines;
+ public void setBridgeFingerprintSummaryLines(
+ Map<String, String> bridgeFingerprintSummaryLines) {
+ this.bridgeFingerprintSummaryLines = bridgeFingerprintSummaryLines;
+ }
+ public Map<String, String> getBridgeFingerprintSummaryLines() {
+ return this.bridgeFingerprintSummaryLines;
+ }
+
+ private Map<String, Set<String>> relaysByCountryCode = null;
+ public void setRelaysByCountryCode(
+ Map<String, Set<String>> relaysByCountryCode) {
+ this.relaysByCountryCode = relaysByCountryCode;
+ }
+ public Map<String, Set<String>> getRelaysByCountryCode() {
+ return relaysByCountryCode;
+ }
+
+ private Map<String, Set<String>> relaysByASNumber = null;
+ public void setRelaysByASNumber(
+ Map<String, Set<String>> relaysByASNumber) {
+ this.relaysByASNumber = relaysByASNumber;
+ }
+ public Map<String, Set<String>> getRelaysByASNumber() {
+ return relaysByASNumber;
+ }
+
+ private Map<String, Set<String>> relaysByFlag = null;
+ public void setRelaysByFlag(Map<String, Set<String>> relaysByFlag) {
+ this.relaysByFlag = relaysByFlag;
+ }
+ public Map<String, Set<String>> getRelaysByFlag() {
+ return relaysByFlag;
+ }
+
+ private Map<String, Set<String>> bridgesByFlag = null;
+ public void setBridgesByFlag(Map<String, Set<String>> bridgesByFlag) {
+ this.bridgesByFlag = bridgesByFlag;
+ }
+ public Map<String, Set<String>> getBridgesByFlag() {
+ return bridgesByFlag;
+ }
+
+ private Map<String, Set<String>> relaysByContact = null;
+ public void setRelaysByContact(
+ Map<String, Set<String>> relaysByContact) {
+ this.relaysByContact = relaysByContact;
+ }
+ public Map<String, Set<String>> getRelaysByContact() {
+ return relaysByContact;
+ }
+
+ private SortedMap<Integer, Set<String>> relaysByFirstSeenDays;
+ public void setRelaysByFirstSeenDays(
+ SortedMap<Integer, Set<String>> relaysByFirstSeenDays) {
+ this.relaysByFirstSeenDays = relaysByFirstSeenDays;
+ }
+ public SortedMap<Integer, Set<String>> getRelaysByFirstSeenDays() {
+ return relaysByFirstSeenDays;
+ }
+
+ private SortedMap<Integer, Set<String>> bridgesByFirstSeenDays;
+ public void setBridgesByFirstSeenDays(
+ SortedMap<Integer, Set<String>> bridgesByFirstSeenDays) {
+ this.bridgesByFirstSeenDays = bridgesByFirstSeenDays;
+ }
+ public SortedMap<Integer, Set<String>> getBridgesByFirstSeenDays() {
+ return bridgesByFirstSeenDays;
+ }
+
+ private SortedMap<Integer, Set<String>> relaysByLastSeenDays;
+ public void setRelaysByLastSeenDays(
+ SortedMap<Integer, Set<String>> relaysByLastSeenDays) {
+ this.relaysByLastSeenDays = relaysByLastSeenDays;
+ }
+ public SortedMap<Integer, Set<String>> getRelaysByLastSeenDays() {
+ return relaysByLastSeenDays;
+ }
+
+ private SortedMap<Integer, Set<String>> bridgesByLastSeenDays;
+ public void setBridgesByLastSeenDays(
+ SortedMap<Integer, Set<String>> bridgesByLastSeenDays) {
+ this.bridgesByLastSeenDays = bridgesByLastSeenDays;
+ }
+ public SortedMap<Integer, Set<String>> getBridgesByLastSeenDays() {
+ return bridgesByLastSeenDays;
+ }
+}
+
+public class NodeIndexer implements ServletContextListener, Runnable {
+
+ public void contextInitialized(ServletContextEvent contextEvent) {
+ ServletContext servletContext = contextEvent.getServletContext();
+ File outDir = new File(servletContext.getInitParameter("outDir"));
+ DocumentStore documentStore = ApplicationFactory.getDocumentStore();
+ documentStore.setOutDir(outDir);
+ /* The servlet container created us, and we need to avoid that
+ * ApplicationFactory creates another instance of us. */
+ ApplicationFactory.setNodeIndexer(this);
+ this.startIndexing();
+ }
+
+ public void contextDestroyed(ServletContextEvent contextEvent) {
+ this.stopIndexing();
+ }
+
+ private long lastIndexed = -1L;
+
+ private NodeIndex latestNodeIndex = null;
+
+ private Thread nodeIndexerThread = null;
+
+ public synchronized long getLastIndexed(long timeoutMillis) {
+ if (this.lastIndexed == 0L && this.nodeIndexerThread != null &&
+ timeoutMillis > 0L) {
+ try {
+ this.wait(timeoutMillis);
+ } catch (InterruptedException e) {
+ }
+ }
+ return this.lastIndexed;
+ }
+
+ public synchronized NodeIndex getLatestNodeIndex(long timeoutMillis) {
+ if (this.latestNodeIndex == null && this.nodeIndexerThread != null &&
+ timeoutMillis > 0L) {
+ try {
+ this.wait(timeoutMillis);
+ } catch (InterruptedException e) {
+ }
+ }
+ return this.latestNodeIndex;
+ }
+
+ public synchronized void startIndexing() {
+ if (this.nodeIndexerThread == null) {
+ this.nodeIndexerThread = new Thread(this);
+ this.nodeIndexerThread.setDaemon(true);
+ this.nodeIndexerThread.start();
+ }
+ }
+
+ public void run() {
+ while (this.nodeIndexerThread != null) {
+ this.indexNodeStatuses();
+ try {
+ Thread.sleep(DateTimeHelper.ONE_MINUTE);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public synchronized void stopIndexing() {
+ Thread indexerThread = this.nodeIndexerThread;
+ this.nodeIndexerThread = null;
+ indexerThread.interrupt();
+ }
+
+ private void indexNodeStatuses() {
+ long updateStatusMillis = -1L;
+ DocumentStore documentStore = ApplicationFactory.getDocumentStore();
+ UpdateStatus updateStatus = documentStore.retrieve(UpdateStatus.class,
+ false);
+ if (updateStatus != null &&
+ updateStatus.getDocumentString() != null) {
+ String updateString = updateStatus.getDocumentString();
+ try {
+ updateStatusMillis = Long.parseLong(updateString.trim());
+ } catch (NumberFormatException e) {
+ /* Handle below. */
+ }
+ }
+ synchronized (this) {
+ if (updateStatusMillis <= this.lastIndexed) {
+ return;
+ }
+ }
+ List<String> newRelaysByConsensusWeight = new ArrayList<String>();
+ Map<String, String>
+ newRelayFingerprintSummaryLines = new HashMap<String, String>(),
+ newBridgeFingerprintSummaryLines = new HashMap<String, String>();
+ Map<String, Set<String>>
+ newRelaysByCountryCode = new HashMap<String, Set<String>>(),
+ newRelaysByASNumber = new HashMap<String, Set<String>>(),
+ newRelaysByFlag = new HashMap<String, Set<String>>(),
+ newBridgesByFlag = new HashMap<String, Set<String>>(),
+ newRelaysByContact = new HashMap<String, Set<String>>();
+ SortedMap<Integer, Set<String>>
+ newRelaysByFirstSeenDays = new TreeMap<Integer, Set<String>>(),
+ newBridgesByFirstSeenDays = new TreeMap<Integer, Set<String>>(),
+ newRelaysByLastSeenDays = new TreeMap<Integer, Set<String>>(),
+ newBridgesByLastSeenDays = new TreeMap<Integer, Set<String>>();
+ Set<NodeStatus> currentRelays = new HashSet<NodeStatus>(),
+ currentBridges = new HashSet<NodeStatus>();
+ SortedSet<String> fingerprints = documentStore.list(NodeStatus.class,
+ false);
+ long relaysLastValidAfterMillis = 0L, bridgesLastPublishedMillis = 0L;
+ for (String fingerprint : fingerprints) {
+ NodeStatus node = documentStore.retrieve(NodeStatus.class, true,
+ fingerprint);
+ if (node.isRelay()) {
+ relaysLastValidAfterMillis = Math.max(
+ relaysLastValidAfterMillis, node.getLastSeenMillis());
+ currentRelays.add(node);
+ } else {
+ bridgesLastPublishedMillis = Math.max(
+ bridgesLastPublishedMillis, node.getLastSeenMillis());
+ currentBridges.add(node);
+ }
+ }
+ Time time = ApplicationFactory.getTime();
+ List<String> orderRelaysByConsensusWeight = new ArrayList<String>();
+ for (NodeStatus entry : currentRelays) {
+ String fingerprint = entry.getFingerprint().toUpperCase();
+ String hashedFingerprint = entry.getHashedFingerprint().
+ toUpperCase();
+ entry.setRunning(entry.getLastSeenMillis() ==
+ relaysLastValidAfterMillis);
+ String line = formatRelaySummaryLine(entry);
+ newRelayFingerprintSummaryLines.put(fingerprint, line);
+ newRelayFingerprintSummaryLines.put(hashedFingerprint, line);
+ long consensusWeight = entry.getConsensusWeight();
+ orderRelaysByConsensusWeight.add(String.format("%020d %s",
+ consensusWeight, fingerprint));
+ orderRelaysByConsensusWeight.add(String.format("%020d %s",
+ consensusWeight, hashedFingerprint));
+ if (entry.getCountryCode() != null) {
+ String countryCode = entry.getCountryCode();
+ if (!newRelaysByCountryCode.containsKey(countryCode)) {
+ newRelaysByCountryCode.put(countryCode,
+ new HashSet<String>());
+ }
+ newRelaysByCountryCode.get(countryCode).add(fingerprint);
+ newRelaysByCountryCode.get(countryCode).add(hashedFingerprint);
+ }
+ if (entry.getASNumber() != null) {
+ String aSNumber = entry.getASNumber();
+ if (!newRelaysByASNumber.containsKey(aSNumber)) {
+ newRelaysByASNumber.put(aSNumber, new HashSet<String>());
+ }
+ newRelaysByASNumber.get(aSNumber).add(fingerprint);
+ newRelaysByASNumber.get(aSNumber).add(hashedFingerprint);
+ }
+ for (String flag : entry.getRelayFlags()) {
+ String flagLowerCase = flag.toLowerCase();
+ if (!newRelaysByFlag.containsKey(flagLowerCase)) {
+ newRelaysByFlag.put(flagLowerCase, new HashSet<String>());
+ }
+ newRelaysByFlag.get(flagLowerCase).add(fingerprint);
+ newRelaysByFlag.get(flagLowerCase).add(hashedFingerprint);
+ }
+ int daysSinceFirstSeen = (int) ((time.currentTimeMillis()
+ - entry.getFirstSeenMillis()) / DateTimeHelper.ONE_DAY);
+ if (!newRelaysByFirstSeenDays.containsKey(daysSinceFirstSeen)) {
+ newRelaysByFirstSeenDays.put(daysSinceFirstSeen,
+ new HashSet<String>());
+ }
+ newRelaysByFirstSeenDays.get(daysSinceFirstSeen).add(fingerprint);
+ newRelaysByFirstSeenDays.get(daysSinceFirstSeen).add(
+ hashedFingerprint);
+ int daysSinceLastSeen = (int) ((time.currentTimeMillis()
+ - entry.getLastSeenMillis()) / DateTimeHelper.ONE_DAY);
+ if (!newRelaysByLastSeenDays.containsKey(daysSinceLastSeen)) {
+ newRelaysByLastSeenDays.put(daysSinceLastSeen,
+ new HashSet<String>());
+ }
+ newRelaysByLastSeenDays.get(daysSinceLastSeen).add(fingerprint);
+ newRelaysByLastSeenDays.get(daysSinceLastSeen).add(
+ hashedFingerprint);
+ String contact = entry.getContact();
+ if (!newRelaysByContact.containsKey(contact)) {
+ newRelaysByContact.put(contact, new HashSet<String>());
+ }
+ newRelaysByContact.get(contact).add(fingerprint);
+ newRelaysByContact.get(contact).add(hashedFingerprint);
+ }
+ Collections.sort(orderRelaysByConsensusWeight);
+ newRelaysByConsensusWeight = new ArrayList<String>();
+ for (String relay : orderRelaysByConsensusWeight) {
+ newRelaysByConsensusWeight.add(relay.split(" ")[1]);
+ }
+ for (NodeStatus entry : currentBridges) {
+ String hashedFingerprint = entry.getFingerprint().toUpperCase();
+ String hashedHashedFingerprint = entry.getHashedFingerprint().
+ toUpperCase();
+ entry.setRunning(entry.getRelayFlags().contains("Running") &&
+ entry.getLastSeenMillis() == bridgesLastPublishedMillis);
+ String line = formatBridgeSummaryLine(entry);
+ newBridgeFingerprintSummaryLines.put(hashedFingerprint, line);
+ newBridgeFingerprintSummaryLines.put(hashedHashedFingerprint,
+ line);
+ for (String flag : entry.getRelayFlags()) {
+ String flagLowerCase = flag.toLowerCase();
+ if (!newBridgesByFlag.containsKey(flagLowerCase)) {
+ newBridgesByFlag.put(flagLowerCase, new HashSet<String>());
+ }
+ newBridgesByFlag.get(flagLowerCase).add(hashedFingerprint);
+ newBridgesByFlag.get(flagLowerCase).add(
+ hashedHashedFingerprint);
+ }
+ int daysSinceFirstSeen = (int) ((time.currentTimeMillis()
+ - entry.getFirstSeenMillis()) / DateTimeHelper.ONE_DAY);
+ if (!newBridgesByFirstSeenDays.containsKey(daysSinceFirstSeen)) {
+ newBridgesByFirstSeenDays.put(daysSinceFirstSeen,
+ new HashSet<String>());
+ }
+ newBridgesByFirstSeenDays.get(daysSinceFirstSeen).add(
+ hashedFingerprint);
+ newBridgesByFirstSeenDays.get(daysSinceFirstSeen).add(
+ hashedHashedFingerprint);
+ int daysSinceLastSeen = (int) ((time.currentTimeMillis()
+ - entry.getLastSeenMillis()) / DateTimeHelper.ONE_DAY);
+ if (!newBridgesByLastSeenDays.containsKey(daysSinceLastSeen)) {
+ newBridgesByLastSeenDays.put(daysSinceLastSeen,
+ new HashSet<String>());
+ }
+ newBridgesByLastSeenDays.get(daysSinceLastSeen).add(
+ hashedFingerprint);
+ newBridgesByLastSeenDays.get(daysSinceLastSeen).add(
+ hashedHashedFingerprint);
+ }
+ NodeIndex newNodeIndex = new NodeIndex();
+ newNodeIndex.setRelaysByConsensusWeight(newRelaysByConsensusWeight);
+ newNodeIndex.setRelayFingerprintSummaryLines(
+ newRelayFingerprintSummaryLines);
+ newNodeIndex.setBridgeFingerprintSummaryLines(
+ newBridgeFingerprintSummaryLines);
+ newNodeIndex.setRelaysByCountryCode(newRelaysByCountryCode);
+ newNodeIndex.setRelaysByASNumber(newRelaysByASNumber);
+ newNodeIndex.setRelaysByFlag(newRelaysByFlag);
+ newNodeIndex.setBridgesByFlag(newBridgesByFlag);
+ newNodeIndex.setRelaysByContact(newRelaysByContact);
+ newNodeIndex.setRelaysByFirstSeenDays(newRelaysByFirstSeenDays);
+ newNodeIndex.setRelaysByLastSeenDays(newRelaysByLastSeenDays);
+ newNodeIndex.setBridgesByFirstSeenDays(newBridgesByFirstSeenDays);
+ newNodeIndex.setBridgesByLastSeenDays(newBridgesByLastSeenDays);
+ newNodeIndex.setRelaysPublishedString(DateTimeHelper.format(
+ relaysLastValidAfterMillis));
+ newNodeIndex.setBridgesPublishedString(DateTimeHelper.format(
+ bridgesLastPublishedMillis));
+ synchronized (this) {
+ this.lastIndexed = updateStatusMillis;
+ this.latestNodeIndex = newNodeIndex;
+ this.notifyAll();
+ }
+ }
+
+ private String formatRelaySummaryLine(NodeStatus entry) {
+ String nickname = !entry.getNickname().equals("Unnamed") ?
+ entry.getNickname() : null;
+ String fingerprint = entry.getFingerprint();
+ String running = entry.getRunning() ? "true" : "false";
+ List<String> addresses = new ArrayList<String>();
+ addresses.add(entry.getAddress());
+ for (String orAddress : entry.getOrAddresses()) {
+ addresses.add(orAddress);
+ }
+ for (String exitAddress : entry.getExitAddresses()) {
+ if (!addresses.contains(exitAddress)) {
+ addresses.add(exitAddress);
+ }
+ }
+ StringBuilder addressesBuilder = new StringBuilder();
+ int written = 0;
+ for (String address : addresses) {
+ addressesBuilder.append((written++ > 0 ? "," : "") + "\""
+ + address.toLowerCase() + "\"");
+ }
+ return String.format("{%s\"f\":\"%s\",\"a\":[%s],\"r\":%s}",
+ (nickname == null ? "" : "\"n\":\"" + nickname + "\","),
+ fingerprint, addressesBuilder.toString(), running);
+ }
+
+ private String formatBridgeSummaryLine(NodeStatus entry) {
+ String nickname = !entry.getNickname().equals("Unnamed") ?
+ entry.getNickname() : null;
+ String hashedFingerprint = entry.getFingerprint();
+ String running = entry.getRunning() ? "true" : "false";
+ return String.format("{%s\"h\":\"%s\",\"r\":%s}",
+ (nickname == null ? "" : "\"n\":\"" + nickname + "\","),
+ hashedFingerprint, running);
+ }
+}
+
diff --git a/src/org/torproject/onionoo/RequestHandler.java b/src/org/torproject/onionoo/RequestHandler.java
index 89871ae..f0e58da 100644
--- a/src/org/torproject/onionoo/RequestHandler.java
+++ b/src/org/torproject/onionoo/RequestHandler.java
@@ -10,278 +10,13 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
public class RequestHandler {
- private static long summaryFileLastModified = -1L;
- private static DocumentStore documentStore;
- private static Time time;
- private static boolean successfullyReadSummaryFile = false;
- private static String relaysPublishedString, bridgesPublishedString;
- private static List<String> relaysByConsensusWeight = null;
- private static Map<String, String> relayFingerprintSummaryLines = null,
- bridgeFingerprintSummaryLines = null;
- private static Map<String, Set<String>> relaysByCountryCode = null,
- relaysByASNumber = null, relaysByFlag = null, bridgesByFlag = null,
- relaysByContact = null;
- private static SortedMap<Integer, Set<String>>
- relaysByFirstSeenDays = null, bridgesByFirstSeenDays = null,
- relaysByLastSeenDays = null, bridgesByLastSeenDays = null;
- private static final long SUMMARY_MAX_AGE = DateTimeHelper.SIX_HOURS;
-
- public static void initialize() {
- documentStore = ApplicationFactory.getDocumentStore();
- time = ApplicationFactory.getTime();
- readSummaryFile();
- }
-
- public static boolean update() {
- readSummaryFile();
- return successfullyReadSummaryFile;
- }
-
- private static void readSummaryFile() {
- long newSummaryFileLastModified = -1L;
- UpdateStatus updateStatus = documentStore.retrieve(UpdateStatus.class,
- false);
- if (updateStatus != null &&
- updateStatus.getDocumentString() != null) {
- String updateString = updateStatus.getDocumentString();
- try {
- newSummaryFileLastModified = Long.parseLong(updateString.trim());
- } catch (NumberFormatException e) {
- /* Handle below. */
- }
- }
- if (newSummaryFileLastModified < 0L) {
- // TODO Does this actually solve anything? Should we instead
- // switch to a variant of the maintenance mode and re-check when
- // the next requests comes in that happens x seconds after this one?
- successfullyReadSummaryFile = false;
- return;
- }
- if (newSummaryFileLastModified + SUMMARY_MAX_AGE
- < time.currentTimeMillis()) {
- // TODO Does this actually solve anything? Should we instead
- // switch to a variant of the maintenance mode and re-check when
- // the next requests comes in that happens x seconds after this one?
- successfullyReadSummaryFile = false;
- return;
- }
- if (newSummaryFileLastModified > summaryFileLastModified) {
- List<String> newRelaysByConsensusWeight = new ArrayList<String>();
- Map<String, String>
- newRelayFingerprintSummaryLines = new HashMap<String, String>(),
- newBridgeFingerprintSummaryLines =
- new HashMap<String, String>();
- Map<String, Set<String>>
- newRelaysByCountryCode = new HashMap<String, Set<String>>(),
- newRelaysByASNumber = new HashMap<String, Set<String>>(),
- newRelaysByFlag = new HashMap<String, Set<String>>(),
- newBridgesByFlag = new HashMap<String, Set<String>>(),
- newRelaysByContact = new HashMap<String, Set<String>>();
- SortedMap<Integer, Set<String>>
- newRelaysByFirstSeenDays = new TreeMap<Integer, Set<String>>(),
- newBridgesByFirstSeenDays = new TreeMap<Integer, Set<String>>(),
- newRelaysByLastSeenDays = new TreeMap<Integer, Set<String>>(),
- newBridgesByLastSeenDays = new TreeMap<Integer, Set<String>>();
- long relaysLastValidAfterMillis = -1L,
- bridgesLastPublishedMillis = -1L;
- String newRelaysPublishedString, newBridgesPublishedString;
- Set<NodeStatus> currentRelays = new HashSet<NodeStatus>(),
- currentBridges = new HashSet<NodeStatus>();
- SortedSet<String> fingerprints = documentStore.list(
- NodeStatus.class, false);
- // TODO We should be able to learn if something goes wrong when
- // reading the summary file, rather than silently having an empty
- // list of fingerprints.
- for (String fingerprint : fingerprints) {
- NodeStatus node = documentStore.retrieve(NodeStatus.class, true,
- fingerprint);
- if (node.isRelay()) {
- relaysLastValidAfterMillis = Math.max(
- relaysLastValidAfterMillis, node.getLastSeenMillis());
- currentRelays.add(node);
- } else {
- bridgesLastPublishedMillis = Math.max(
- bridgesLastPublishedMillis, node.getLastSeenMillis());
- currentBridges.add(node);
- }
- }
- newRelaysPublishedString = DateTimeHelper.format(
- relaysLastValidAfterMillis);
- newBridgesPublishedString = DateTimeHelper.format(
- bridgesLastPublishedMillis);
- List<String> orderRelaysByConsensusWeight = new ArrayList<String>();
- for (NodeStatus entry : currentRelays) {
- String fingerprint = entry.getFingerprint().toUpperCase();
- String hashedFingerprint = entry.getHashedFingerprint().
- toUpperCase();
- entry.setRunning(entry.getLastSeenMillis() ==
- relaysLastValidAfterMillis);
- String line = formatRelaySummaryLine(entry);
- newRelayFingerprintSummaryLines.put(fingerprint, line);
- newRelayFingerprintSummaryLines.put(hashedFingerprint, line);
- long consensusWeight = entry.getConsensusWeight();
- orderRelaysByConsensusWeight.add(String.format("%020d %s",
- consensusWeight, fingerprint));
- orderRelaysByConsensusWeight.add(String.format("%020d %s",
- consensusWeight, hashedFingerprint));
- if (entry.getCountryCode() != null) {
- String countryCode = entry.getCountryCode();
- if (!newRelaysByCountryCode.containsKey(countryCode)) {
- newRelaysByCountryCode.put(countryCode,
- new HashSet<String>());
- }
- newRelaysByCountryCode.get(countryCode).add(fingerprint);
- newRelaysByCountryCode.get(countryCode).add(hashedFingerprint);
- }
- if (entry.getASNumber() != null) {
- String aSNumber = entry.getASNumber();
- if (!newRelaysByASNumber.containsKey(aSNumber)) {
- newRelaysByASNumber.put(aSNumber, new HashSet<String>());
- }
- newRelaysByASNumber.get(aSNumber).add(fingerprint);
- newRelaysByASNumber.get(aSNumber).add(hashedFingerprint);
- }
- for (String flag : entry.getRelayFlags()) {
- String flagLowerCase = flag.toLowerCase();
- if (!newRelaysByFlag.containsKey(flagLowerCase)) {
- newRelaysByFlag.put(flagLowerCase, new HashSet<String>());
- }
- newRelaysByFlag.get(flagLowerCase).add(fingerprint);
- newRelaysByFlag.get(flagLowerCase).add(hashedFingerprint);
- }
- int daysSinceFirstSeen = (int) ((newSummaryFileLastModified
- - entry.getFirstSeenMillis()) / 86400000L);
- if (!newRelaysByFirstSeenDays.containsKey(daysSinceFirstSeen)) {
- newRelaysByFirstSeenDays.put(daysSinceFirstSeen,
- new HashSet<String>());
- }
- newRelaysByFirstSeenDays.get(daysSinceFirstSeen).add(fingerprint);
- newRelaysByFirstSeenDays.get(daysSinceFirstSeen).add(
- hashedFingerprint);
- int daysSinceLastSeen = (int) ((newSummaryFileLastModified
- - entry.getLastSeenMillis()) / 86400000L);
- if (!newRelaysByLastSeenDays.containsKey(daysSinceLastSeen)) {
- newRelaysByLastSeenDays.put(daysSinceLastSeen,
- new HashSet<String>());
- }
- newRelaysByLastSeenDays.get(daysSinceLastSeen).add(fingerprint);
- newRelaysByLastSeenDays.get(daysSinceLastSeen).add(
- hashedFingerprint);
- String contact = entry.getContact();
- if (!newRelaysByContact.containsKey(contact)) {
- newRelaysByContact.put(contact, new HashSet<String>());
- }
- newRelaysByContact.get(contact).add(fingerprint);
- newRelaysByContact.get(contact).add(hashedFingerprint);
- }
- Collections.sort(orderRelaysByConsensusWeight);
- newRelaysByConsensusWeight = new ArrayList<String>();
- for (String relay : orderRelaysByConsensusWeight) {
- newRelaysByConsensusWeight.add(relay.split(" ")[1]);
- }
- for (NodeStatus entry : currentBridges) {
- String hashedFingerprint = entry.getFingerprint().toUpperCase();
- String hashedHashedFingerprint = entry.getHashedFingerprint().
- toUpperCase();
- entry.setRunning(entry.getRelayFlags().contains("Running") &&
- entry.getLastSeenMillis() == bridgesLastPublishedMillis);
- String line = formatBridgeSummaryLine(entry);
- newBridgeFingerprintSummaryLines.put(hashedFingerprint, line);
- newBridgeFingerprintSummaryLines.put(hashedHashedFingerprint,
- line);
- for (String flag : entry.getRelayFlags()) {
- String flagLowerCase = flag.toLowerCase();
- if (!newBridgesByFlag.containsKey(flagLowerCase)) {
- newBridgesByFlag.put(flagLowerCase, new HashSet<String>());
- }
- newBridgesByFlag.get(flagLowerCase).add(hashedFingerprint);
- newBridgesByFlag.get(flagLowerCase).add(
- hashedHashedFingerprint);
- }
- int daysSinceFirstSeen = (int) ((newSummaryFileLastModified
- - entry.getFirstSeenMillis()) / 86400000L);
- if (!newBridgesByFirstSeenDays.containsKey(daysSinceFirstSeen)) {
- newBridgesByFirstSeenDays.put(daysSinceFirstSeen,
- new HashSet<String>());
- }
- newBridgesByFirstSeenDays.get(daysSinceFirstSeen).add(
- hashedFingerprint);
- newBridgesByFirstSeenDays.get(daysSinceFirstSeen).add(
- hashedHashedFingerprint);
- int daysSinceLastSeen = (int) ((newSummaryFileLastModified
- - entry.getLastSeenMillis()) / 86400000L);
- if (!newBridgesByLastSeenDays.containsKey(daysSinceLastSeen)) {
- newBridgesByLastSeenDays.put(daysSinceLastSeen,
- new HashSet<String>());
- }
- newBridgesByLastSeenDays.get(daysSinceLastSeen).add(
- hashedFingerprint);
- newBridgesByLastSeenDays.get(daysSinceLastSeen).add(
- hashedHashedFingerprint);
- }
- relaysByConsensusWeight = newRelaysByConsensusWeight;
- relayFingerprintSummaryLines = newRelayFingerprintSummaryLines;
- bridgeFingerprintSummaryLines = newBridgeFingerprintSummaryLines;
- relaysByCountryCode = newRelaysByCountryCode;
- relaysByASNumber = newRelaysByASNumber;
- relaysByFlag = newRelaysByFlag;
- bridgesByFlag = newBridgesByFlag;
- relaysByContact = newRelaysByContact;
- relaysByFirstSeenDays = newRelaysByFirstSeenDays;
- relaysByLastSeenDays = newRelaysByLastSeenDays;
- bridgesByFirstSeenDays = newBridgesByFirstSeenDays;
- bridgesByLastSeenDays = newBridgesByLastSeenDays;
- relaysPublishedString = newRelaysPublishedString;
- bridgesPublishedString = newBridgesPublishedString;
- }
- summaryFileLastModified = newSummaryFileLastModified;
- successfullyReadSummaryFile = true;
- }
-
- private static String formatRelaySummaryLine(NodeStatus entry) {
- String nickname = !entry.getNickname().equals("Unnamed") ?
- entry.getNickname() : null;
- String fingerprint = entry.getFingerprint();
- String running = entry.getRunning() ? "true" : "false";
- List<String> addresses = new ArrayList<String>();
- addresses.add(entry.getAddress());
- for (String orAddress : entry.getOrAddresses()) {
- addresses.add(orAddress);
- }
- for (String exitAddress : entry.getExitAddresses()) {
- if (!addresses.contains(exitAddress)) {
- addresses.add(exitAddress);
- }
- }
- StringBuilder addressesBuilder = new StringBuilder();
- int written = 0;
- for (String address : addresses) {
- addressesBuilder.append((written++ > 0 ? "," : "") + "\""
- + address.toLowerCase() + "\"");
- }
- return String.format("{%s\"f\":\"%s\",\"a\":[%s],\"r\":%s}",
- (nickname == null ? "" : "\"n\":\"" + nickname + "\","),
- fingerprint, addressesBuilder.toString(), running);
- }
-
- private static String formatBridgeSummaryLine(NodeStatus entry) {
- String nickname = !entry.getNickname().equals("Unnamed") ?
- entry.getNickname() : null;
- String hashedFingerprint = entry.getFingerprint();
- String running = entry.getRunning() ? "true" : "false";
- return String.format("{%s\"h\":\"%s\",\"r\":%s}",
- (nickname == null ? "" : "\"n\":\"" + nickname + "\","),
- hashedFingerprint, running);
- }
+ private NodeIndex nodeIndex;
- public static long getLastModified() {
- readSummaryFile();
- return summaryFileLastModified;
+ public RequestHandler(NodeIndex nodeIndex) {
+ this.nodeIndex = nodeIndex;
}
private String resourceType;
@@ -368,8 +103,10 @@ public class RequestHandler {
new HashMap<String, String>();
public void handleRequest() {
- this.filteredRelays.putAll(relayFingerprintSummaryLines);
- this.filteredBridges.putAll(bridgeFingerprintSummaryLines);
+ this.filteredRelays.putAll(
+ this.nodeIndex.getRelayFingerprintSummaryLines());
+ this.filteredBridges.putAll(
+ this.nodeIndex.getBridgeFingerprintSummaryLines());
this.filterByResourceType();
this.filterByType();
this.filterByRunning();
@@ -531,11 +268,12 @@ public class RequestHandler {
return;
}
String countryCode = this.country.toLowerCase();
- if (!relaysByCountryCode.containsKey(countryCode)) {
+ if (!this.nodeIndex.getRelaysByCountryCode().containsKey(
+ countryCode)) {
this.filteredRelays.clear();
} else {
Set<String> relaysWithCountryCode =
- relaysByCountryCode.get(countryCode);
+ this.nodeIndex.getRelaysByCountryCode().get(countryCode);
Set<String> removeRelays = new HashSet<String>();
for (Map.Entry<String, String> e : this.filteredRelays.entrySet()) {
String fingerprint = e.getKey();
@@ -558,11 +296,11 @@ public class RequestHandler {
if (!aSNumber.startsWith("AS")) {
aSNumber = "AS" + aSNumber;
}
- if (!relaysByASNumber.containsKey(aSNumber)) {
+ if (!this.nodeIndex.getRelaysByASNumber().containsKey(aSNumber)) {
this.filteredRelays.clear();
} else {
Set<String> relaysWithASNumber =
- relaysByASNumber.get(aSNumber);
+ this.nodeIndex.getRelaysByASNumber().get(aSNumber);
Set<String> removeRelays = new HashSet<String>();
for (Map.Entry<String, String> e : this.filteredRelays.entrySet()) {
String fingerprint = e.getKey();
@@ -582,10 +320,11 @@ public class RequestHandler {
return;
}
String flag = this.flag.toLowerCase();
- if (!relaysByFlag.containsKey(flag)) {
+ if (!this.nodeIndex.getRelaysByFlag().containsKey(flag)) {
this.filteredRelays.clear();
} else {
- Set<String> relaysWithFlag = relaysByFlag.get(flag);
+ Set<String> relaysWithFlag = this.nodeIndex.getRelaysByFlag().get(
+ flag);
Set<String> removeRelays = new HashSet<String>();
for (Map.Entry<String, String> e : this.filteredRelays.entrySet()) {
String fingerprint = e.getKey();
@@ -597,10 +336,11 @@ public class RequestHandler {
this.filteredRelays.remove(fingerprint);
}
}
- if (!bridgesByFlag.containsKey(flag)) {
+ if (!this.nodeIndex.getBridgesByFlag().containsKey(flag)) {
this.filteredBridges.clear();
} else {
- Set<String> bridgesWithFlag = bridgesByFlag.get(flag);
+ Set<String> bridgesWithFlag = this.nodeIndex.getBridgesByFlag().get(
+ flag);
Set<String> removeBridges = new HashSet<String>();
for (Map.Entry<String, String> e :
this.filteredBridges.entrySet()) {
@@ -619,20 +359,20 @@ public class RequestHandler {
if (this.firstSeenDays == null) {
return;
}
- filterNodesByDays(this.filteredRelays, relaysByFirstSeenDays,
- this.firstSeenDays);
- filterNodesByDays(this.filteredBridges, bridgesByFirstSeenDays,
- this.firstSeenDays);
+ filterNodesByDays(this.filteredRelays,
+ this.nodeIndex.getRelaysByFirstSeenDays(), this.firstSeenDays);
+ filterNodesByDays(this.filteredBridges,
+ this.nodeIndex.getBridgesByFirstSeenDays(), this.firstSeenDays);
}
private void filterNodesByLastSeenDays() {
if (this.lastSeenDays == null) {
return;
}
- filterNodesByDays(this.filteredRelays, relaysByLastSeenDays,
- this.lastSeenDays);
- filterNodesByDays(this.filteredBridges, bridgesByLastSeenDays,
- this.lastSeenDays);
+ filterNodesByDays(this.filteredRelays,
+ this.nodeIndex.getRelaysByLastSeenDays(), this.lastSeenDays);
+ filterNodesByDays(this.filteredBridges,
+ this.nodeIndex.getBridgesByLastSeenDays(), this.lastSeenDays);
}
private void filterNodesByDays(Map<String, String> filteredNodes,
@@ -657,7 +397,8 @@ public class RequestHandler {
return;
}
Set<String> removeRelays = new HashSet<String>();
- for (Map.Entry<String, Set<String>> e : relaysByContact.entrySet()) {
+ for (Map.Entry<String, Set<String>> e :
+ this.nodeIndex.getRelaysByContact().entrySet()) {
String contact = e.getKey();
for (String contactPart : this.contact) {
if (contact == null ||
@@ -676,7 +417,7 @@ public class RequestHandler {
private void order() {
if (this.order != null && this.order.length == 1) {
List<String> orderBy = new ArrayList<String>(
- relaysByConsensusWeight);
+ this.nodeIndex.getRelaysByConsensusWeight());
if (this.order[0].startsWith("-")) {
Collections.reverse(orderBy);
}
@@ -747,10 +488,10 @@ public class RequestHandler {
}
public String getRelaysPublishedString() {
- return relaysPublishedString;
+ return this.nodeIndex.getRelaysPublishedString();
}
public String getBridgesPublishedString() {
- return bridgesPublishedString;
+ return this.nodeIndex.getBridgesPublishedString();
}
}
diff --git a/src/org/torproject/onionoo/ResourceServlet.java b/src/org/torproject/onionoo/ResourceServlet.java
index dcfefc5..f2f3005 100644
--- a/src/org/torproject/onionoo/ResourceServlet.java
+++ b/src/org/torproject/onionoo/ResourceServlet.java
@@ -2,7 +2,6 @@
* See LICENSE for licensing information */
package org.torproject.onionoo;
-import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -27,30 +26,17 @@ public class ResourceServlet extends HttpServlet {
/* Called by servlet container, not by test class. */
public void init(ServletConfig config) throws ServletException {
super.init(config);
- boolean maintenanceMode =
- config.getInitParameter("maintenance") != null
- && config.getInitParameter("maintenance").equals("1");
- File outDir = new File(config.getInitParameter("outDir"));
- DocumentStore documentStore = ApplicationFactory.getDocumentStore();
- documentStore.setOutDir(outDir);
- this.init(maintenanceMode);
- }
-
- /* Called (indirectly) by servlet container and (directly) by test
- * class. */
- protected void init(boolean maintenanceMode) {
- this.maintenanceMode = maintenanceMode;
- if (!maintenanceMode) {
- RequestHandler.initialize();
- ResponseBuilder.initialize();
- }
+ this.maintenanceMode =
+ config.getInitParameter("maintenance") != null &&
+ config.getInitParameter("maintenance").equals("1");
}
public long getLastModified(HttpServletRequest request) {
if (this.maintenanceMode) {
return super.getLastModified(request);
} else {
- return RequestHandler.getLastModified();
+ return ApplicationFactory.getNodeIndexer().getLastIndexed(
+ DateTimeHelper.TEN_SECONDS);
}
}
@@ -109,7 +95,16 @@ public class ResourceServlet extends HttpServlet {
return;
}
- if (!RequestHandler.update()) {
+ if (ApplicationFactory.getNodeIndexer().getLastIndexed(
+ DateTimeHelper.TEN_SECONDS) + DateTimeHelper.SIX_HOURS
+ < ApplicationFactory.getTime().currentTimeMillis()) {
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ NodeIndex nodeIndex = ApplicationFactory.getNodeIndexer().
+ getLatestNodeIndex(DateTimeHelper.TEN_SECONDS);
+ if (nodeIndex == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -135,7 +130,8 @@ public class ResourceServlet extends HttpServlet {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
- RequestHandler rh = new RequestHandler();
+
+ RequestHandler rh = new RequestHandler(nodeIndex);
rh.setResourceType(resourceType);
/* Extract parameters either from the old-style URI or from request
diff --git a/src/org/torproject/onionoo/ResponseBuilder.java b/src/org/torproject/onionoo/ResponseBuilder.java
index a841d29..d14ee5b 100644
--- a/src/org/torproject/onionoo/ResponseBuilder.java
+++ b/src/org/torproject/onionoo/ResponseBuilder.java
@@ -9,10 +9,10 @@ import java.util.Scanner;
public class ResponseBuilder {
- private static DocumentStore documentStore;
+ private DocumentStore documentStore;
- public static void initialize() {
- documentStore = ApplicationFactory.getDocumentStore();
+ public ResponseBuilder() {
+ this.documentStore = ApplicationFactory.getDocumentStore();
}
private String resourceType;
@@ -114,7 +114,7 @@ public class ResponseBuilder {
return "";
}
fingerprint = fingerprint.substring(0, 40);
- DetailsDocument detailsDocument = documentStore.retrieve(
+ DetailsDocument detailsDocument = this.documentStore.retrieve(
DetailsDocument.class, false, fingerprint);
if (detailsDocument != null &&
detailsDocument.getDocumentString() != null) {
@@ -184,7 +184,7 @@ public class ResponseBuilder {
return "";
}
fingerprint = fingerprint.substring(0, 40);
- BandwidthDocument bandwidthDocument = documentStore.retrieve(
+ BandwidthDocument bandwidthDocument = this.documentStore.retrieve(
BandwidthDocument.class, false, fingerprint);
if (bandwidthDocument != null &&
bandwidthDocument.getDocumentString() != null) {
@@ -208,7 +208,7 @@ public class ResponseBuilder {
return "";
}
fingerprint = fingerprint.substring(0, 40);
- WeightsDocument weightsDocument = documentStore.retrieve(
+ WeightsDocument weightsDocument = this.documentStore.retrieve(
WeightsDocument.class, false, fingerprint);
if (weightsDocument != null &&
weightsDocument.getDocumentString() != null) {
@@ -231,7 +231,7 @@ public class ResponseBuilder {
return "";
}
fingerprint = fingerprint.substring(0, 40);
- ClientsDocument clientsDocument = documentStore.retrieve(
+ ClientsDocument clientsDocument = this.documentStore.retrieve(
ClientsDocument.class, false, fingerprint);
if (clientsDocument != null &&
clientsDocument.getDocumentString() != null) {
@@ -257,7 +257,7 @@ public class ResponseBuilder {
return "";
}
fingerprint = fingerprint.substring(0, 40);
- UptimeDocument uptimeDocument = documentStore.retrieve(
+ UptimeDocument uptimeDocument = this.documentStore.retrieve(
UptimeDocument.class, false, fingerprint);
if (uptimeDocument != null &&
uptimeDocument.getDocumentString() != null) {
diff --git a/test/org/torproject/onionoo/ResourceServletTest.java b/test/org/torproject/onionoo/ResourceServletTest.java
index cbe787c..f5395ed 100644
--- a/test/org/torproject/onionoo/ResourceServletTest.java
+++ b/test/org/torproject/onionoo/ResourceServletTest.java
@@ -32,12 +32,8 @@ public class ResourceServletTest {
private SortedMap<String, String> relays, bridges;
- // 2013-04-24 12:22:22
- private static long lastModified = 1366806142000L;
-
- private long currentTimeMillis = 1366806142000L;
-
- private boolean maintenanceMode = false;
+ private long currentTimeMillis = DateTimeHelper.parse(
+ "2013-04-24 12:22:22");
private class TestingHttpServletRequestWrapper
extends HttpServletRequestWrapper {
@@ -149,6 +145,7 @@ public class ResourceServletTest {
try {
this.createDummyTime();
this.createDummyDocumentStore();
+ this.createNodeIndexer();
this.makeRequest(requestURI, parameterMap);
this.parseResponse();
} catch (IOException e) {
@@ -162,13 +159,10 @@ public class ResourceServletTest {
}
private void createDummyDocumentStore() {
- /* TODO Incrementing static lastModified is necessary for
- * ResponseBuilder to read state from the newly created DocumentStore.
- * Otherwise, ResponseBuilder would use data from the previous test
- * run. This is bad design and should be fixed. */
DummyDocumentStore documentStore = new DummyDocumentStore();
UpdateStatus updateStatus = new UpdateStatus();
- updateStatus.setDocumentString(String.valueOf(lastModified++));
+ updateStatus.setDocumentString(String.valueOf(
+ this.currentTimeMillis));
documentStore.addDocument(updateStatus, null);
for (Map.Entry<String, String> e : relays.entrySet()) {
documentStore.addDocument(NodeStatus.fromString(e.getValue()),
@@ -181,10 +175,15 @@ public class ResourceServletTest {
ApplicationFactory.setDocumentStore(documentStore);
}
+ private void createNodeIndexer() {
+ NodeIndexer newNodeIndexer = new NodeIndexer();
+ newNodeIndexer.startIndexing();
+ ApplicationFactory.setNodeIndexer(newNodeIndexer);
+ }
+
private void makeRequest(String requestURI,
Map<String, String[]> parameterMap) throws IOException {
ResourceServlet rs = new ResourceServlet();
- rs.init(this.maintenanceMode);
this.request = new TestingHttpServletRequestWrapper(requestURI,
parameterMap);
this.response = new TestingHttpServletResponseWrapper();
More information about the tor-commits
mailing list