From f321d56283005f071a1917e7dd377de074692a8c Mon Sep 17 00:00:00 2001 From: Benjamin Erb Date: Thu, 23 Dec 2010 00:00:49 +0100 Subject: [PATCH 1/2] * added caching skeleton --- .../ioexception/www/server/cache/Cache.java | 7 +++ .../www/server/cache/impl/LRUCache.java | 54 +++++++++++++++++++ .../www/server/impl/CachingHttpWorker.java | 23 ++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/de/ioexception/www/server/cache/Cache.java create mode 100644 src/de/ioexception/www/server/cache/impl/LRUCache.java create mode 100644 src/de/ioexception/www/server/impl/CachingHttpWorker.java diff --git a/src/de/ioexception/www/server/cache/Cache.java b/src/de/ioexception/www/server/cache/Cache.java new file mode 100644 index 0000000..94cbc02 --- /dev/null +++ b/src/de/ioexception/www/server/cache/Cache.java @@ -0,0 +1,7 @@ +package de.ioexception.www.server.cache; + +public interface Cache +{ + public V put(K key, V value); + public V get(K key); +} diff --git a/src/de/ioexception/www/server/cache/impl/LRUCache.java b/src/de/ioexception/www/server/cache/impl/LRUCache.java new file mode 100644 index 0000000..371394b --- /dev/null +++ b/src/de/ioexception/www/server/cache/impl/LRUCache.java @@ -0,0 +1,54 @@ +package de.ioexception.www.server.cache.impl; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import de.ioexception.www.server.cache.Cache; + +/** + * A thread-safe LRU cache implementation based on internal LinkedHashMap. + * + * @author Benjamin Erb + * + * @param + * Entry Key Type + * @param + * Entry Value Type + */ +public class LRUCache implements Cache +{ + public static final int DEFAULT_MAX_SIZE = 1000; + + private final Map internalMap; + + public LRUCache() + { + this(DEFAULT_MAX_SIZE); + } + + public LRUCache(final int maxSize) + { + this.internalMap = (Map) Collections.synchronizedMap(new LinkedHashMap(maxSize + 1, .75F, true) + { + private static final long serialVersionUID = 5369285290965670135L; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > maxSize; + } + }); + } + + public V put(K key, V value) + { + return internalMap.put(key, value); + } + + public V get(K key) + { + return internalMap.get(key); + } + +} \ No newline at end of file diff --git a/src/de/ioexception/www/server/impl/CachingHttpWorker.java b/src/de/ioexception/www/server/impl/CachingHttpWorker.java new file mode 100644 index 0000000..6e0b7a9 --- /dev/null +++ b/src/de/ioexception/www/server/impl/CachingHttpWorker.java @@ -0,0 +1,23 @@ +package de.ioexception.www.server.impl; + +import java.net.Socket; + +import de.ioexception.www.http.HttpRequest; +import de.ioexception.www.http.HttpResponse; + +public class CachingHttpWorker extends BasicHttpWorker +{ + + public CachingHttpWorker(Socket socket, BasicHttpServer server) + { + super(socket, server); + } + + @Override + protected HttpResponse handleRequest(HttpRequest request) + { + // TODO: add caching support + return super.handleRequest(request); + } + +} From 7a6af4e4248b88a67fc44a5e2851a11d6eb15923 Mon Sep 17 00:00:00 2001 From: Benjamin Erb Date: Thu, 23 Dec 2010 17:41:52 +0100 Subject: [PATCH 2/2] added simple LRU-based in memory cache --- .../www/server/cache/EntityCacheEntry.java | 8 ++++ .../cache/impl/EntityCacheEntryImpl.java | 37 +++++++++++++++ .../www/server/impl/BasicHttpServer.java | 8 +++- .../www/server/impl/CachingHttpWorker.java | 47 +++++++++++++++++-- 4 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 src/de/ioexception/www/server/cache/EntityCacheEntry.java create mode 100644 src/de/ioexception/www/server/cache/impl/EntityCacheEntryImpl.java diff --git a/src/de/ioexception/www/server/cache/EntityCacheEntry.java b/src/de/ioexception/www/server/cache/EntityCacheEntry.java new file mode 100644 index 0000000..f04a2d7 --- /dev/null +++ b/src/de/ioexception/www/server/cache/EntityCacheEntry.java @@ -0,0 +1,8 @@ +package de.ioexception.www.server.cache; + +public interface EntityCacheEntry +{ + byte[] getEntity(); + String getETag(); + String getContentType(); +} diff --git a/src/de/ioexception/www/server/cache/impl/EntityCacheEntryImpl.java b/src/de/ioexception/www/server/cache/impl/EntityCacheEntryImpl.java new file mode 100644 index 0000000..d895d39 --- /dev/null +++ b/src/de/ioexception/www/server/cache/impl/EntityCacheEntryImpl.java @@ -0,0 +1,37 @@ +package de.ioexception.www.server.cache.impl; + +import de.ioexception.www.server.cache.EntityCacheEntry; + +public class EntityCacheEntryImpl implements EntityCacheEntry +{ + private final byte[] entity; + private final String eTag; + private final String contentType; + + public EntityCacheEntryImpl(byte[] entity, String eTag, String contentType) + { + super(); + this.entity = entity; + this.eTag = eTag; + this.contentType = contentType; + } + + @Override + public byte[] getEntity() + { + return entity; + } + + @Override + public String getETag() + { + return eTag; + } + + @Override + public String getContentType() + { + return contentType; + } + +} diff --git a/src/de/ioexception/www/server/impl/BasicHttpServer.java b/src/de/ioexception/www/server/impl/BasicHttpServer.java index ddca550..8ccf297 100644 --- a/src/de/ioexception/www/server/impl/BasicHttpServer.java +++ b/src/de/ioexception/www/server/impl/BasicHttpServer.java @@ -8,6 +8,9 @@ import java.util.concurrent.Executors; import de.ioexception.www.server.HttpServer; +import de.ioexception.www.server.cache.Cache; +import de.ioexception.www.server.cache.EntityCacheEntry; +import de.ioexception.www.server.cache.impl.LRUCache; /** * A simple HTTP server implementation. @@ -27,6 +30,8 @@ public class BasicHttpServer implements HttpServer private final ExecutorService workerPool; private final ExecutorService dispatcherService; private final ServerSocket serverSocket; + + private final Cache cache = new LRUCache(100); /** @@ -62,7 +67,8 @@ public BasicHttpServer(int port) @Override public void dispatchRequest(Socket socket) { - workerPool.submit(new BasicHttpWorker(socket, this)); +// workerPool.submit(new BasicHttpWorker(socket, this)); + workerPool.submit(new CachingHttpWorker(socket, this,cache)); } @Override diff --git a/src/de/ioexception/www/server/impl/CachingHttpWorker.java b/src/de/ioexception/www/server/impl/CachingHttpWorker.java index 6e0b7a9..b045fae 100644 --- a/src/de/ioexception/www/server/impl/CachingHttpWorker.java +++ b/src/de/ioexception/www/server/impl/CachingHttpWorker.java @@ -1,23 +1,62 @@ package de.ioexception.www.server.impl; import java.net.Socket; +import java.util.HashMap; +import de.ioexception.www.Http; import de.ioexception.www.http.HttpRequest; import de.ioexception.www.http.HttpResponse; +import de.ioexception.www.http.HttpStatusCode; +import de.ioexception.www.http.impl.BasicHttpResponse; +import de.ioexception.www.server.cache.Cache; +import de.ioexception.www.server.cache.EntityCacheEntry; +import de.ioexception.www.server.cache.impl.EntityCacheEntryImpl; public class CachingHttpWorker extends BasicHttpWorker { + private final Cache cache; - public CachingHttpWorker(Socket socket, BasicHttpServer server) + public CachingHttpWorker(Socket socket, BasicHttpServer server, Cache cache) { super(socket, server); + this.cache = cache; } - + @Override protected HttpResponse handleRequest(HttpRequest request) { - // TODO: add caching support - return super.handleRequest(request); + EntityCacheEntry cacheEntry = cache.get(request.getRequestUri()); + + if(null == cacheEntry) + { + HttpResponse response = super.handleRequest(request); + if(response.getStatusCode().equals(HttpStatusCode.OK) && response.getEntity() != null && response.getEntity().length > 0) + { + EntityCacheEntry entry = new EntityCacheEntryImpl(response.getEntity(), response.getHeaders().get(Http.ETAG), response.getHeaders().get(Http.CONTENT_TYPE)); + cache.put(request.getRequestUri(), entry); + } + return response; + } + else + { + //TODO check for conditional request + BasicHttpResponse response = new BasicHttpResponse(); + response.setHeaders(new HashMap()); + response.getHeaders().put(Http.SERVER, server.getServerSignature()); + response.setVersion(request.getHttpVersion()); + response.getHeaders().put(Http.CONTENT_LENGTH, ""+cacheEntry.getEntity().length); + if(null != cacheEntry.getETag()) + { + response.getHeaders().put(Http.ETAG, cacheEntry.getETag()); + } + if(null != cacheEntry.getContentType()) + { + response.getHeaders().put(Http.CONTENT_TYPE, cacheEntry.getContentType()); + } + response.setEntity(cacheEntry.getEntity()); + response.setStatusCode(HttpStatusCode.OK); + return response; + } } }