summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/net/yacy/htroot/NetworkPicture.java94
-rw-r--r--source/net/yacy/peers/graphics/NetworkGraph.java86
-rw-r--r--source/net/yacy/search/Switchboard.java3
-rw-r--r--test/java/net/yacy/peers/graphics/NetworkGraphTest.java56
4 files changed, 159 insertions, 80 deletions
diff --git a/source/net/yacy/htroot/NetworkPicture.java b/source/net/yacy/htroot/NetworkPicture.java
index 0ebfb7f91..ea89744f7 100644
--- a/source/net/yacy/htroot/NetworkPicture.java
+++ b/source/net/yacy/htroot/NetworkPicture.java
@@ -37,13 +37,17 @@ import net.yacy.search.SwitchboardConstants;
import net.yacy.server.serverObjects;
import net.yacy.server.serverSwitch;
+
/** draw a picture of the yacy network */
public class NetworkPicture {
- private static final ConcurrentLog log = new ConcurrentLog("NetworkPicture");
- private static final Semaphore sync = new Semaphore(1, true);
- private static long lastAccessSeconds = 0;
+ private static final ConcurrentLog log = new ConcurrentLog("NetworkPicture");
+ private static final Semaphore sync = new Semaphore(1, true);
+ private static final int NETWORK_PICTURE_CACHE_MAX_SIZE = 20;
+ private static final long NETWORK_PICTURE_CACHE_MAX_AGE_MILLIS = 10000;
+ public static final NetworkGraph.Cache cache = new NetworkGraph.Cache(NETWORK_PICTURE_CACHE_MAX_AGE_MILLIS, NETWORK_PICTURE_CACHE_MAX_SIZE);
+
public static EncodedImage respond(
final RequestHeader header,
final serverObjects post,
@@ -51,35 +55,8 @@ public class NetworkPicture {
final Switchboard sb = (Switchboard) env;
final boolean authorized = sb.adminAuthenticated(header) >= 2;
- final long timeSeconds = System.currentTimeMillis() / 1000;
- if (NetworkGraph.buffer != null && timeSeconds - lastAccessSeconds < 2) {
- if (log.isFine()) log.fine("cache hit (1); authorized = "
- + authorized
- + ", timeSeconds - lastAccessSeconds = "
- + (timeSeconds - lastAccessSeconds));
- return NetworkGraph.buffer;
- }
-
- boolean lockAcquired = sync.tryAcquire();
- if (!lockAcquired) {
- if (NetworkGraph.buffer != null) {
- return NetworkGraph.buffer;
- }
- sync.acquireUninterruptibly();
- lockAcquired = true;
- }
- try {
- final long refreshCheckSeconds = System.currentTimeMillis() / 1000;
- if (NetworkGraph.buffer != null && refreshCheckSeconds - lastAccessSeconds < 2) {
- if (log.isFine()) log.fine("cache hit (2); authorized = "
- + authorized
- + ", timeSeconds - lastAccessSeconds = "
- + (refreshCheckSeconds - lastAccessSeconds));
- return NetworkGraph.buffer;
- }
-
- int width = 1280; // 640x480 = VGA, 768x576 = SD/4:3, 1024x576 =SD/16:9 1280x720 = HD/16:9, 1920x1080 = FULL HD/16:9
- int height = 720;
+ int width = 1280; // 640x480 = VGA, 768x576 = SD/4:3, 1024x576 =SD/16:9 1280x720 = HD/16:9, 1920x1080 = FULL HD/16:9
+ int height = 720;
int passiveLimit = 1440; // minutes; 1440 = 1 day; 720 = 12 hours; 1440 = 24 hours, 10080 = 1 week;
int potentialLimit = 1440;
int maxCount = 9000;
@@ -111,11 +88,34 @@ public class NetworkPicture {
if ( potentialLimit > 1000000 ) {
potentialLimit = 1000000;
}
- if ( maxCount > 10000 ) {
- maxCount = 10000;
- }
-
- NetworkGraph.buffer =
+ if ( maxCount > 10000 ) {
+ maxCount = 10000;
+ }
+
+ EncodedImage cachedPicture = cache.getFresh(width, height, coronaangle);
+ if (cachedPicture != null) {
+ if (log.isFine()) log.fine("cache hit (1); authorized = " + authorized + ", width = " + width + ", height = " + height);
+ return cachedPicture;
+ }
+
+ boolean lockAcquired = sync.tryAcquire();
+ if (!lockAcquired) {
+ /* A stale picture is preferable to waiting, but only for a matching variant. */
+ cachedPicture = cache.getCached(width, height, coronaangle);
+ if (cachedPicture != null) {
+ return cachedPicture;
+ }
+ sync.acquireUninterruptibly();
+ lockAcquired = true;
+ }
+ try {
+ cachedPicture = cache.getFresh(width, height, coronaangle);
+ if (cachedPicture != null) {
+ if (log.isFine()) log.fine("cache hit (2); authorized = " + authorized + ", width = " + width + ", height = " + height);
+ return cachedPicture;
+ }
+
+ final EncodedImage networkPicture =
new EncodedImage(NetworkGraph.getNetworkPicture(
sb.peers,
width,
@@ -130,24 +130,8 @@ public class NetworkPicture {
Long.parseLong(bgcolor, 16),
cyc), "png", false);
- /*
- NetworkGraph.buffer =
- new EncodedImage(NetworkSkylineGraph.getNetworkSkylinePicture(
- sb.peers,
- width,
- height,
- passiveLimit,
- potentialLimit,
- maxCount,
- coronaangle,
- communicationTimeout,
- env.getConfig(SwitchboardConstants.NETWORK_NAME, "unspecified"),
- env.getConfig("network.unit.description", "unspecified"),
- Long.parseLong(bgcolor, 16),
- cyc), "png", false);
- */
- lastAccessSeconds = System.currentTimeMillis() / 1000;
- return NetworkGraph.buffer;
+ cache.put(width, height, coronaangle, networkPicture);
+ return networkPicture;
} finally {
if (lockAcquired) {
sync.release();
diff --git a/source/net/yacy/peers/graphics/NetworkGraph.java b/source/net/yacy/peers/graphics/NetworkGraph.java
index cd501dccd..1b14b0379 100644
--- a/source/net/yacy/peers/graphics/NetworkGraph.java
+++ b/source/net/yacy/peers/graphics/NetworkGraph.java
@@ -30,9 +30,12 @@ import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
-import java.util.Date;
-import java.util.Iterator;
+import java.util.AbstractMap;
+import java.util.Date;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import net.yacy.cora.document.encoding.ASCII;
import net.yacy.cora.document.encoding.UTF8;
@@ -50,12 +53,11 @@ import net.yacy.search.query.SearchEventCache;
import net.yacy.visualization.PrintTool;
import net.yacy.visualization.RasterPlotter;
-public class NetworkGraph {
+public class NetworkGraph {
- private final static double DOUBLE_LONG_MAX_VALUE = Long.MAX_VALUE;
- public static EncodedImage buffer = null;
-
+ private static final double DOUBLE_LONG_MAX_VALUE = Long.MAX_VALUE;
+
private static int shortestName = 10;
private static int longestName = 30;
@@ -81,13 +83,62 @@ public class NetworkGraph {
private static final long COL_NORMAL_TEXT = 0x000000;
private static final long COL_LOAD_BG = 0xF7F7F7;
- /** Private constructor to avoid instantiation of utility class. */
- private NetworkGraph() { }
- public static void clearcache() {
- buffer = null;
+ public static class Cache {
+
+ private final Map<String, Map.Entry<Long, EncodedImage>> cache;
+ private final long maxAge;
+ private final int maxSize;
+
+ public Cache(long maxAge, int maxSize) {
+ this.maxAge = maxAge;
+ this.maxSize = maxSize;
+ this.cache = new ConcurrentHashMap<>();
+ }
+
+ private String cacheKey(final int width, final int height, final int coronaangle) {
+ return "key-" + width + "-" + height + "-" + coronaangle;
+ }
+
+ public EncodedImage getCached(final int width, final int height, final int coronaangle) {
+ final Map.Entry<Long, EncodedImage> cached = cache.get(cacheKey(width, height, coronaangle));
+ return cached == null ? null : cached.getValue();
+ }
+
+ public EncodedImage getFresh(final int width, final int height, final int coronaangle) {
+ final String key = cacheKey(width, height, coronaangle);
+ final Map.Entry<Long, EncodedImage> cached = this.cache.get(key);
+ if (cached == null) return null;
+ if (System.currentTimeMillis() - cached.getKey() >= this.maxAge) return null;
+ return cached.getValue();
+ }
+
+ public synchronized void put(final int width, final int height, final int coronaangle, final EncodedImage image) {
+ final String key = cacheKey(width, height, coronaangle);
+ if (!this.cache.containsKey(key) && this.cache.size() >= this.maxSize) {
+ String oldestKey = null;
+ long oldestCreationTime = Long.MAX_VALUE;
+ for (final Map.Entry<String, Map.Entry<Long, EncodedImage>> entry: this.cache.entrySet()) {
+ if (entry.getValue().getKey() < oldestCreationTime) {
+ oldestKey = entry.getKey();
+ oldestCreationTime = entry.getValue().getKey();
+ }
+ }
+ if (oldestKey != null) {
+ this.cache.remove(oldestKey);
+ }
+ }
+ this.cache.put(key, new AbstractMap.SimpleEntry<Long, EncodedImage>(System.currentTimeMillis(), image));
+ }
+
+ public void clear() {
+ this.cache.clear();
+ }
}
+ /** Private constructor to avoid instantiation of utility class. */
+ private NetworkGraph() { }
+
public static class CircleThreadPiece {
private final String pieceName;
private final Color color;
@@ -148,19 +199,6 @@ public class NetworkGraph {
eventPicture.arcLine(cx, cy, cr - 20, cr, angle, true, null, null, -1, -1, -1, false);
}
- // draw in the secondary search peers
- /*
- if (secondarySearches != null) {
- for (final Thread secondarySearche : secondarySearches) {
- if (secondarySearche == null) continue;
- eventPicture.setColor((secondarySearche.isAlive()) ? RasterPlotter.RED : RasterPlotter.GREEN);
- angle = cyc + (360.0d * ((FlatWordPartitionScheme.std.dhtPosition(UTF8.getBytes(secondarySearche.target().hash), null)) / DOUBLE_LONG_MAX_VALUE));
- eventPicture.arcLine(cx, cy, cr - 10, cr, angle - 1.0, true, null, null, -1, -1, -1, false);
- eventPicture.arcLine(cx, cy, cr - 10, cr, angle + 1.0, true, null, null, -1, -1, -1, false);
- }
- }
- */
-
// draw in the search target
final Iterator<byte[]> i = event.query.getQueryGoal().getIncludeHashes().iterator();
eventPicture.setColor(RasterPlotter.GREY);
@@ -447,4 +485,4 @@ public class NetworkGraph {
g.drawChars(caption.toCharArray(), 0, caption.length(), x+LEGEND_BOX_SIZE+5,y);
}
-} \ No newline at end of file
+}
diff --git a/source/net/yacy/search/Switchboard.java b/source/net/yacy/search/Switchboard.java
index 1d0b8d42a..fb19d2a99 100644
--- a/source/net/yacy/search/Switchboard.java
+++ b/source/net/yacy/search/Switchboard.java
@@ -182,6 +182,7 @@ import net.yacy.document.parser.pdfParser;
import net.yacy.document.parser.html.Evaluation;
import net.yacy.gui.Audio;
import net.yacy.gui.Tray;
+import net.yacy.htroot.NetworkPicture;
import net.yacy.http.YaCyHttpServer;
import net.yacy.kelondro.blob.ArrayStack;
import net.yacy.kelondro.blob.BEncodedHeap;
@@ -2445,7 +2446,7 @@ public final class Switchboard extends serverSwitch {
// clear graphics caches
CircleTool.clearcache();
- NetworkGraph.clearcache();
+ NetworkPicture.cache.clear();;
}
public int schedulerJobSize() {
diff --git a/test/java/net/yacy/peers/graphics/NetworkGraphTest.java b/test/java/net/yacy/peers/graphics/NetworkGraphTest.java
new file mode 100644
index 000000000..ca3d2f24d
--- /dev/null
+++ b/test/java/net/yacy/peers/graphics/NetworkGraphTest.java
@@ -0,0 +1,56 @@
+// NetworkGraphTest.java
+// This is a part of YaCy, a peer-to-peer based web search engine
+
+package net.yacy.peers.graphics;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Unit tests for the network picture cache in {@link NetworkGraph}. */
+public class NetworkGraphTest {
+
+ @Before
+ @After
+ public void clearCache() {
+ NetworkGraph.clearcache();
+ }
+
+ @Test
+ public void testNetworkPictureCacheIsPartitionedByImageSize() {
+ final EncodedImage small = new EncodedImage(new byte[] {1}, "png", false);
+ final EncodedImage large = new EncodedImage(new byte[] {2}, "png", false);
+
+ NetworkGraph.cacheNetworkPicture(825, 450, 0, small);
+ NetworkGraph.cacheNetworkPicture(1280, 900, 0, large);
+
+ assertSame(small, NetworkGraph.getCachedNetworkPicture(825, 450, 0));
+ assertSame(large, NetworkGraph.getCachedNetworkPicture(1280, 900, 0));
+ assertNull(NetworkGraph.getCachedNetworkPicture(825, 900, 0));
+ }
+
+ @Test
+ public void testNetworkPictureCacheIsPartitionedByCoronaAngle() {
+ final EncodedImage firstPhase = new EncodedImage(new byte[] {1}, "png", false);
+ final EncodedImage secondPhase = new EncodedImage(new byte[] {2}, "png", false);
+
+ NetworkGraph.cacheNetworkPicture(1280, 900, 0, firstPhase);
+ NetworkGraph.cacheNetworkPicture(1280, 900, 60, secondPhase);
+
+ assertSame(firstPhase, NetworkGraph.getCachedNetworkPicture(1280, 900, 0));
+ assertSame(secondPhase, NetworkGraph.getCachedNetworkPicture(1280, 900, 60));
+ assertNull(NetworkGraph.getCachedNetworkPicture(1280, 900, 120));
+ }
+
+ @Test
+ public void testExpiredNetworkPictureRemainsAvailableForBusyFallback() {
+ final EncodedImage image = new EncodedImage(new byte[] {1}, "png", false);
+ NetworkGraph.cacheNetworkPicture(825, 450, 0, image);
+
+ assertNull(NetworkGraph.getCachedNetworkPicture(825, 450, 0, 0));
+ assertSame(image, NetworkGraph.getCachedNetworkPicture(825, 450, 0));
+ }
+}