commit 7b05cc2bc7e94424ffdbccceaa6d2102c0e2aeae Author: Karsten Loesing karsten.loesing@gmx.net Date: Sun Apr 20 14:51:44 2014 +0200
Use Gson to format JSON uptime documents. --- src/org/torproject/onionoo/GraphHistory.java | 56 +++++ src/org/torproject/onionoo/ResponseBuilder.java | 4 +- src/org/torproject/onionoo/UptimeDocument.java | 14 ++ .../torproject/onionoo/UptimeDocumentWriter.java | 67 +++--- .../onionoo/UptimeDocumentWriterTest.java | 253 ++++++++++++++++++++ 5 files changed, 355 insertions(+), 39 deletions(-)
diff --git a/src/org/torproject/onionoo/GraphHistory.java b/src/org/torproject/onionoo/GraphHistory.java new file mode 100644 index 0000000..f03be58 --- /dev/null +++ b/src/org/torproject/onionoo/GraphHistory.java @@ -0,0 +1,56 @@ +/* Copyright 2014 The Tor Project + * See LICENSE for licensing information */ +package org.torproject.onionoo; + +import java.util.List; + +public class GraphHistory { + + private String first; + public void setFirst(String first) { + this.first = first; + } + public String getFirst() { + return this.first; + } + + private String last; + public void setLast(String last) { + this.last = last; + } + public String getLast() { + return this.last; + } + + private Integer interval; + public void setInterval(Integer interval) { + this.interval = interval; + } + public Integer getInterval() { + return this.interval; + } + + private Double factor; + public void setFactor(Double factor) { + this.factor = factor; + } + public Double getFactor() { + return this.factor; + } + + private Integer count; + public void setCount(Integer count) { + this.count = count; + } + public Integer getCount() { + return this.count; + } + + private List<Integer> values; + public void setValues(List<Integer> values) { + this.values = values; + } + public List<Integer> getValues() { + return this.values; + } +} diff --git a/src/org/torproject/onionoo/ResponseBuilder.java b/src/org/torproject/onionoo/ResponseBuilder.java index c4f3f07..dbd8cc7 100644 --- a/src/org/torproject/onionoo/ResponseBuilder.java +++ b/src/org/torproject/onionoo/ResponseBuilder.java @@ -236,9 +236,7 @@ public class ResponseBuilder { UptimeDocument.class, false, fingerprint); if (uptimeDocument != null && uptimeDocument.getDocumentString() != null) { - String uptimeLines = uptimeDocument.getDocumentString(); - uptimeLines = uptimeLines.substring(0, uptimeLines.length() - 1); - return uptimeLines; + return uptimeDocument.getDocumentString(); } else { return "{"fingerprint":"" + fingerprint.toUpperCase() + ""}"; } diff --git a/src/org/torproject/onionoo/UptimeDocument.java b/src/org/torproject/onionoo/UptimeDocument.java index f71cb87..b2df03b 100644 --- a/src/org/torproject/onionoo/UptimeDocument.java +++ b/src/org/torproject/onionoo/UptimeDocument.java @@ -2,7 +2,21 @@ * See LICENSE for licensing information */ package org.torproject.onionoo;
+import java.util.Map; + class UptimeDocument extends Document {
+ private String fingerprint; + public void setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + } + + private Map<String, GraphHistory> uptime; + public void setUptime(Map<String, GraphHistory> uptime) { + this.uptime = uptime; + } + public Map<String, GraphHistory> getUptime() { + return this.uptime; + } }
diff --git a/src/org/torproject/onionoo/UptimeDocumentWriter.java b/src/org/torproject/onionoo/UptimeDocumentWriter.java index 14c800d..8293f77 100644 --- a/src/org/torproject/onionoo/UptimeDocumentWriter.java +++ b/src/org/torproject/onionoo/UptimeDocumentWriter.java @@ -3,8 +3,9 @@ package org.torproject.onionoo;
import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; +import java.util.Map; import java.util.SortedSet; import java.util.TreeSet;
@@ -70,9 +71,8 @@ public class UptimeDocumentWriter implements FingerprintListener, SortedSet<UptimeHistory> history = relay ? uptimeStatus.getRelayHistory() : uptimeStatus.getBridgeHistory(); - UptimeDocument uptimeDocument = new UptimeDocument(); - uptimeDocument.setDocumentString(this.formatHistoryString(relay, - fingerprint, history, knownStatuses)); + UptimeDocument uptimeDocument = this.compileUptimeDocument(relay, + fingerprint, history, knownStatuses); this.documentStore.store(uptimeDocument, fingerprint); this.writtenDocuments++; } @@ -99,31 +99,29 @@ public class UptimeDocumentWriter implements FingerprintListener, DateTimeHelper.TWO_DAYS, DateTimeHelper.TEN_DAYS };
- private String formatHistoryString(boolean relay, String fingerprint, - SortedSet<UptimeHistory> history, + private UptimeDocument compileUptimeDocument(boolean relay, + String fingerprint, SortedSet<UptimeHistory> history, SortedSet<UptimeHistory> knownStatuses) { - StringBuilder sb = new StringBuilder(); - sb.append("{"fingerprint":"" + fingerprint + """); - sb.append(",\n"uptime":{"); - int graphIntervalsWritten = 0; + UptimeDocument uptimeDocument = new UptimeDocument(); + uptimeDocument.setFingerprint(fingerprint); + Map<String, GraphHistory> uptime = + new LinkedHashMap<String, GraphHistory>(); for (int graphIntervalIndex = 0; graphIntervalIndex < this.graphIntervals.length; graphIntervalIndex++) { - String timeline = this.formatTimeline(graphIntervalIndex, relay, - history, knownStatuses); - if (timeline != null) { - sb.append((graphIntervalsWritten++ > 0 ? "," : "") + "\n" - + timeline); + String graphName = this.graphNames[graphIntervalIndex]; + GraphHistory graphHistory = this.compileUptimeHistory( + graphIntervalIndex, relay, history, knownStatuses); + if (graphHistory != null) { + uptime.put(graphName, graphHistory); } } - sb.append("}"); - sb.append("\n}\n"); - return sb.toString(); + uptimeDocument.setUptime(uptime); + return uptimeDocument; }
- private String formatTimeline(int graphIntervalIndex, boolean relay, - SortedSet<UptimeHistory> history, + private GraphHistory compileUptimeHistory(int graphIntervalIndex, + boolean relay, SortedSet<UptimeHistory> history, SortedSet<UptimeHistory> knownStatuses) { - String graphName = this.graphNames[graphIntervalIndex]; long graphInterval = this.graphIntervals[graphIntervalIndex]; long dataPointInterval = this.dataPointIntervals[graphIntervalIndex]; @@ -253,18 +251,17 @@ public class UptimeDocumentWriter implements FingerprintListener, } long lastDataPointMillis = firstDataPointMillis + (lastNonNullIndex - firstNonNullIndex) * dataPointInterval; - double factor = 1.0 / 999.0; int count = lastNonNullIndex - firstNonNullIndex + 1; - StringBuilder sb = new StringBuilder(); - sb.append(""" + graphName + "":{" - + ""first":"" + DateTimeHelper.format(firstDataPointMillis) - + "","last":"" + DateTimeHelper.format(lastDataPointMillis) - + "","interval":" + String.valueOf(dataPointInterval - / DateTimeHelper.ONE_SECOND) - + ","factor":" + String.format(Locale.US, "%.9f", factor) - + ","count":" + String.valueOf(count) + ","values":["); - int dataPointsWritten = 0, previousNonNullIndex = -2; + GraphHistory graphHistory = new GraphHistory(); + graphHistory.setFirst(DateTimeHelper.format(firstDataPointMillis)); + graphHistory.setLast(DateTimeHelper.format(lastDataPointMillis)); + graphHistory.setInterval((int) (dataPointInterval + / DateTimeHelper.ONE_SECOND)); + graphHistory.setFactor(1.0 / 999.0); + graphHistory.setCount(count); + int previousNonNullIndex = -2; boolean foundTwoAdjacentDataPoints = false; + List<Integer> values = new ArrayList<Integer>(); for (int dataPointIndex = firstNonNullIndex; dataPointIndex <= lastNonNullIndex; dataPointIndex++) { double dataPoint = dataPoints.get(dataPointIndex); @@ -274,13 +271,11 @@ public class UptimeDocumentWriter implements FingerprintListener, } previousNonNullIndex = dataPointIndex; } - sb.append((dataPointsWritten++ > 0 ? "," : "") - + (dataPoint < -0.5 ? "null" : - String.valueOf((long) (dataPoint * 999.0)))); + values.add(dataPoint < -0.5 ? null : ((int) (dataPoint * 999.0))); } - sb.append("]}"); + graphHistory.setValues(values); if (foundTwoAdjacentDataPoints) { - return sb.toString(); + return graphHistory; } else { return null; } diff --git a/test/org/torproject/onionoo/UptimeDocumentWriterTest.java b/test/org/torproject/onionoo/UptimeDocumentWriterTest.java new file mode 100644 index 0000000..5065e4d --- /dev/null +++ b/test/org/torproject/onionoo/UptimeDocumentWriterTest.java @@ -0,0 +1,253 @@ +/* Copyright 2014 The Tor Project + * See LICENSE for licensing information */ +package org.torproject.onionoo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class UptimeDocumentWriterTest { + + private static final long TEST_TIME = DateTimeHelper.parse( + "2014-03-23 12:00:00"); + + private DummyTime dummyTime; + + @Before + public void createDummyTime() { + this.dummyTime = new DummyTime(TEST_TIME); + ApplicationFactory.setTime(this.dummyTime); + } + + private DummyDescriptorSource descriptorSource; + + @Before + public void createDummyDescriptorSource() { + this.descriptorSource = new DummyDescriptorSource(); + ApplicationFactory.setDescriptorSource(this.descriptorSource); + } + + private DummyDocumentStore documentStore; + + @Before + public void createDummyDocumentStore() { + this.documentStore = new DummyDocumentStore(); + ApplicationFactory.setDocumentStore(this.documentStore); + } + + @Test + public void testNoStatuses() { + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + writer.writeDocuments(); + assertEquals("Without providing any data, nothing should be written " + + "to disk.", 0, + this.documentStore.getPerformedStoreOperations()); + } + + private static final String ALL_RELAYS_FINGERPRINT = null; + + private static final String GABELMOO_FINGERPRINT = + "F2044413DAC2E02E3D6BCF4735A19BCA1DE97281"; + + private void addStatusOneWeekSample(String allRelaysUptime, + String gabelmooUptime) { + UptimeStatus status = new UptimeStatus(); + status.fromDocumentString(allRelaysUptime); + this.documentStore.addDocument(status, ALL_RELAYS_FINGERPRINT); + status = new UptimeStatus(); + status.fromDocumentString(gabelmooUptime); + this.documentStore.addDocument(status, GABELMOO_FINGERPRINT); + this.descriptorSource.addFingerprint(DescriptorType.RELAY_CONSENSUSES, + GABELMOO_FINGERPRINT); + } + + private void assertOneWeekGraph(UptimeDocument document, int graphs, + String first, String last, int count, List<Integer> values) { + this.assertGraph(document, graphs, "1_week", first, last, + (int) (DateTimeHelper.ONE_HOUR / DateTimeHelper.ONE_SECOND), + count, values); + } + + private void assertOneMonthGraph(UptimeDocument document, int graphs, + String first, String last, int count, List<Integer> values) { + this.assertGraph(document, graphs, "1_month", first, last, + (int) (DateTimeHelper.FOUR_HOURS / DateTimeHelper.ONE_SECOND), + count, values); + } + + private void assertGraph(UptimeDocument document, int graphs, + String graphName, String first, String last, int interval, + int count, List<Integer> values) { + assertEquals("Should contain exactly " + graphs + " graphs.", graphs, + document.getUptime().size()); + assertTrue("Should contain a graph for " + graphName + ".", + document.getUptime().containsKey(graphName)); + GraphHistory history = document.getUptime().get(graphName); + assertEquals("First data point should be " + first + ".", first, + history.getFirst()); + assertEquals("Last data point should be " + last + ".", last, + history.getLast()); + assertEquals("Interval should be " + interval + " seconds.", interval, + (int) history.getInterval()); + assertEquals("Factor should be 1.0 / 999.0.", 1.0 / 999.0, + (double) history.getFactor(), 0.01); + assertEquals("There should be one data point per hour.", count, + (int) history.getCount()); + assertEquals("Count should be the same as the number of values.", + count, history.getValues().size()); + if (values == null) { + for (int value : history.getValues()) { + assertEquals("All values should be 999.", 999, value); + } + } else { + assertEquals("Values are not as expected.", values, + history.getValues()); + } + } + + @Test + public void testOneHourUptime() { + this.addStatusOneWeekSample("r 2014-03-23-11 1\n", + "r 2014-03-23-11 1\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + assertEquals("Should not contain any graph.", 0, + document.getUptime().size()); + } + + @Test + public void testTwoHoursUptime() { + this.addStatusOneWeekSample("r 2014-03-23-10 2\n", + "r 2014-03-23-10 2\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneWeekGraph(document, 1, "2014-03-23 10:30:00", + "2014-03-23 11:30:00", 2, null); + } + + @Test + public void testTwoHoursUptimeSeparatedByNull() { + this.addStatusOneWeekSample("r 2014-03-23-09 1\nr 2014-03-23-11 1\n", + "r 2014-03-23-09 1\nr 2014-03-23-11 1\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + assertEquals("Should not contain any graph.", 0, + document.getUptime().size()); + } + + @Test + public void testTwoHoursUptimeSeparatedByZero() { + this.addStatusOneWeekSample("r 2014-03-23-09 3\n", + "r 2014-03-23-09 1\nr 2014-03-23-11 1\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneWeekGraph(document, 1, "2014-03-23 09:30:00", + "2014-03-23 11:30:00", 3, + Arrays.asList(new Integer[] { 999, 0, 999 })); + } + + @Test + public void testTwoHoursUptimeThenDowntime() { + this.addStatusOneWeekSample("r 2014-03-23-09 3\n", + "r 2014-03-23-09 2\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneWeekGraph(document, 1, "2014-03-23 09:30:00", + "2014-03-23 11:30:00", 3, + Arrays.asList(new Integer[] { 999, 999, 0 })); + } + + @Test + public void testOneWeekUptime() { + this.addStatusOneWeekSample("r 2014-03-16-12 168\n", + "r 2014-03-16-12 168\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneWeekGraph(document, 1, "2014-03-16 12:30:00", + "2014-03-23 11:30:00", 168, null); + } + + @Test + public void testOneWeekOneHourUptime() { + this.addStatusOneWeekSample("r 2014-03-16-11 169\n", + "r 2014-03-16-11 169\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneWeekGraph(document, 2, "2014-03-16 12:30:00", + "2014-03-23 11:30:00", 168, null); + this.assertOneMonthGraph(document, 2, "2014-03-16 10:00:00", + "2014-03-23 10:00:00", 43, null); + } + + @Test + public void testOneMonthPartialIntervalOnline() { + this.addStatusOneWeekSample("r 2014-03-16-08 8\n", + "r 2014-03-16-11 5\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneMonthGraph(document, 2, "2014-03-16 10:00:00", + "2014-03-16 14:00:00", 2, null); + } + + @Test + public void testOneMonthPartialIntervalOnOff() { + this.addStatusOneWeekSample("r 2014-03-16-08 8\n", + "r 2014-03-16-10 1\nr 2014-03-16-12 1\n"); + UptimeDocumentWriter writer = new UptimeDocumentWriter(); + ApplicationFactory.getDescriptorSource().readDescriptors(); + writer.writeDocuments(); + assertEquals("Should write exactly one document.", 1, + this.documentStore.getPerformedStoreOperations()); + UptimeDocument document = this.documentStore.getDocument( + UptimeDocument.class, GABELMOO_FINGERPRINT); + this.assertOneMonthGraph(document, 2, "2014-03-16 10:00:00", + "2014-03-16 14:00:00", 2, + Arrays.asList(new Integer[] { 499, 249 })); + } +} +