diff --git a/src/com/wentch/redkale/net/http/BasedHttpServlet.java b/src/com/wentch/redkale/net/http/BasedHttpServlet.java index 7779fe0e5..0646ae127 100644 --- a/src/com/wentch/redkale/net/http/BasedHttpServlet.java +++ b/src/com/wentch/redkale/net/http/BasedHttpServlet.java @@ -11,7 +11,9 @@ import com.wentch.redkale.net.Context; import com.wentch.redkale.util.AnyValue; import java.io.IOException; import java.lang.reflect.Method; +import java.nio.*; import java.util.*; +import java.util.concurrent.*; import jdk.internal.org.objectweb.asm.*; import static jdk.internal.org.objectweb.asm.Opcodes.*; @@ -33,7 +35,19 @@ public abstract class BasedHttpServlet extends HttpServlet { for (Map.Entry en : actions) { if (request.getRequestURI().startsWith(en.getKey())) { Entry entry = en.getValue(); - if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) entry.servlet.execute(request, response); + if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) { + if (entry.cachetimeout > 0) {//有缓存设置 + CacheEntry ce = entry.cache.get(request.getRequestURI()); + if (ce != null && ce.time + entry.cachetimeout > System.currentTimeMillis()) { //缓存有效 + response.setStatus(ce.status); + response.setContentType(ce.contentType); + response.finish(ce.getBuffers()); + return; + } + response.setInterceptor(entry.cacheInterceptor); + } + entry.servlet.execute(request, response); + } return; } } @@ -180,12 +194,28 @@ public abstract class BasedHttpServlet extends HttpServlet { this.method = method; this.servlet = servlet; this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null; + HttpCacheable hc = method.getAnnotation(HttpCacheable.class); + this.cachetimeout = hc == null ? 0 : hc.timeout() * 1000; + this.cache = cachetimeout > 0 ? new ConcurrentHashMap() : null; + this.cacheInterceptor = cachetimeout > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> { + int status = response.getStatus(); + if (status != 200) return null; + CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers); + cache.put(response.getRequest().getRequestURI(), ce); + return ce.getBuffers(); + } : null; } public boolean isNeedCheck() { return this.moduleid != 0 || this.actionid != 0; } + public final HttpResponse.Interceptor cacheInterceptor; + + public final ConcurrentHashMap cache; + + public final int cachetimeout; + public final boolean ignore; public final int moduleid; @@ -198,4 +228,33 @@ public abstract class BasedHttpServlet extends HttpServlet { public final HttpServlet servlet; } + + private static final class CacheEntry { + + public final long time = System.currentTimeMillis(); + + private final ByteBuffer[] buffers; + + private final int status; + + private final String contentType; + + public CacheEntry(int status, String contentType, ByteBuffer[] bufs) { + this.status = status; + this.contentType = contentType; + final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length]; + for (int i = 0; i < newBuffers.length; i++) { + newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer(); + } + this.buffers = newBuffers; + } + + public ByteBuffer[] getBuffers() { + final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length]; + for (int i = 0; i < newBuffers.length; i++) { + newBuffers[i] = buffers[i].duplicate(); + } + return newBuffers; + } + } } diff --git a/src/com/wentch/redkale/net/http/HttpCacheable.java b/src/com/wentch/redkale/net/http/HttpCacheable.java new file mode 100644 index 000000000..6c2010381 --- /dev/null +++ b/src/com/wentch/redkale/net/http/HttpCacheable.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.net.http; + +import java.lang.annotation.*; + +/** + * + * @author zhangjx + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface HttpCacheable { + + /** + * 超时的秒数 + * + * @return + */ + int timeout() default 15; +} diff --git a/src/com/wentch/redkale/net/http/HttpResponse.java b/src/com/wentch/redkale/net/http/HttpResponse.java index b40163940..2e897dccf 100644 --- a/src/com/wentch/redkale/net/http/HttpResponse.java +++ b/src/com/wentch/redkale/net/http/HttpResponse.java @@ -26,6 +26,15 @@ import java.util.concurrent.atomic.*; */ public class HttpResponse extends Response { + /** + * HttpResponse.finish 方法内调用 + * + */ + public static interface Interceptor { + + public ByteBuffer[] invoke(final HttpResponse response, final ByteBuffer[] buffers); + } + private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\n\r\n".getBytes()).asReadOnlyBuffer(); private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer(); @@ -98,6 +107,9 @@ public class HttpResponse extends Response { private boolean headsended = false; + private Interceptor interceptor; + //------------------------------------------------ + private final DefaultAnyValue header = new DefaultAnyValue(); private final String[][] defaultAddHeaders; @@ -130,6 +142,7 @@ public class HttpResponse extends Response { this.cookies = null; this.headsended = false; this.header.clear(); + this.interceptor = null; return super.recycle(); } @@ -142,6 +155,10 @@ public class HttpResponse extends Response { return httpCodes.get(status); } + protected HttpRequest getRequest() { + return request; + } + protected String getHttpCode(int status, String defValue) { String v = httpCodes.get(status); return v == null ? defValue : v; @@ -186,6 +203,9 @@ public class HttpResponse extends Response { return; } if (context.getCharset() == null) { + if (interceptor != null) { + interceptor.invoke(this, new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(obj))}); + } final char[] chars = Utility.charArray(obj); this.contentLength = Utility.encodeUTF8Length(chars); final ByteBuffer headbuf = createHeader(); @@ -198,6 +218,10 @@ public class HttpResponse extends Response { } } else { ByteBuffer buffer = context.getCharset().encode(obj); + if (interceptor != null) { + ByteBuffer[] bufs = interceptor.invoke(this, new ByteBuffer[]{buffer}); + if (bufs != null) buffer = bufs[0]; + } this.contentLength = buffer.remaining(); final ByteBuffer headbuf = createHeader(); headbuf.flip(); @@ -247,6 +271,10 @@ public class HttpResponse extends Response { @Override public void finish(boolean kill, ByteBuffer... buffers) { + if (interceptor != null) { + ByteBuffer[] bufs = interceptor.invoke(this, buffers); + if (bufs != null) buffers = bufs; + } if (kill) refuseAlive(); if (!this.headsended) { long len = 0; @@ -422,6 +450,10 @@ public class HttpResponse extends Response { this.headsended = true; } + protected DefaultAnyValue duplicateHeader() { + return this.header.duplicate(); + } + public void setHeader(String name, Object value) { this.header.setValue(name, String.valueOf(value)); } @@ -454,6 +486,14 @@ public class HttpResponse extends Response { this.contentLength = contentLength; } + public Interceptor getInterceptor() { + return interceptor; + } + + public void setInterceptor(Interceptor interceptor) { + this.interceptor = interceptor; + } + protected final class TransferFileHandler implements CompletionHandler { private final AsynchronousFileChannel filechannel;