0
|
1 package cachingfilter;
|
|
2
|
|
3 import java.io.File;
|
|
4 import java.io.FileFilter;
|
|
5 import java.io.FilenameFilter;
|
|
6 import java.io.IOException;
|
|
7 import java.io.ObjectInputStream;
|
|
8 import java.io.BufferedInputStream;
|
|
9 import java.io.FileInputStream;
|
|
10 import java.io.BufferedOutputStream;
|
|
11 import java.io.FileOutputStream;
|
|
12 import java.io.ObjectOutputStream;
|
|
13 import java.util.Set;
|
|
14 import java.util.HashSet;
|
|
15 import java.util.concurrent.CopyOnWriteArraySet;
|
|
16 import java.util.concurrent.Executors;
|
|
17 import java.util.concurrent.ScheduledExecutorService;
|
|
18 import java.util.concurrent.TimeUnit;
|
|
19 import javax.servlet.Filter;
|
|
20 import javax.servlet.FilterChain;
|
|
21 import javax.servlet.FilterConfig;
|
|
22 import javax.servlet.ServletException;
|
|
23 import javax.servlet.ServletRequest;
|
|
24 import javax.servlet.ServletResponse;
|
|
25 import javax.servlet.http.HttpServletRequest;
|
|
26 import javax.servlet.http.HttpServletResponse;
|
|
27 import org.slf4j.Logger;
|
|
28 import org.slf4j.LoggerFactory;
|
|
29
|
|
30
|
|
31 public final class CachingFilter implements Filter {
|
|
32 private static final Logger logger = LoggerFactory.getLogger(CachingFilter.class);
|
|
33
|
|
34 static final Locker<String> locker = new Locker<String>();
|
|
35
|
|
36 // private static final long sweepFreqSeconds = 60*60*24;
|
|
37 // private static final long expiryPeriodMillis = 1000L*60*60*24*10;
|
|
38 private static final long sweepFreqSeconds = 60*60;
|
|
39 private static final long expiryPeriodMillis = 1000L*60*60*24;
|
|
40
|
|
41 static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
|
42
|
|
43 private File dir;
|
|
44 private boolean hasDelayedDelete;
|
|
45 private final Set<String> encodings = new HashSet<String>();
|
|
46
|
|
47 public void init(FilterConfig filterConfig)
|
|
48 throws ServletException
|
|
49 {
|
|
50 String dirS = filterConfig.getInitParameter("dir");
|
|
51 if( dirS == null )
|
|
52 throw new ServletException("'dir' init parameter must be set");
|
|
53 dir = new File(dirS);
|
|
54 dir.mkdirs();
|
|
55
|
|
56 String acceptEncoding = filterConfig.getInitParameter("acceptEncoding");
|
|
57 if( acceptEncoding == null )
|
|
58 throw new ServletException("'acceptEncoding' init parameter must be set");
|
|
59 for( String encoding : acceptEncoding.split(",") ) {
|
|
60 encoding = encoding.trim();
|
|
61 if( encoding.length() > 0 )
|
|
62 encodings.add(encoding);
|
|
63 }
|
|
64
|
|
65 String hasDelayedDeleteS = filterConfig.getInitParameter("hasDelayedDelete");
|
|
66 if( hasDelayedDeleteS == null )
|
|
67 throw new ServletException("'hasDelayedDelete' init parameter must be set");
|
|
68 hasDelayedDelete = Boolean.parseBoolean(hasDelayedDeleteS);
|
|
69
|
|
70 Runnable sweeper = new Runnable() {
|
|
71 public void run() {
|
|
72 try {
|
|
73 logger.error("starting sweep");
|
|
74 final int[] total = new int[1];
|
|
75 final long expired = System.currentTimeMillis() - expiryPeriodMillis;
|
|
76 File[] files = dir.listFiles(new FileFilter(){
|
|
77 public boolean accept(File file) {
|
|
78 total[0]++;
|
|
79 return file.lastModified() < expired && file.getName().charAt(0) != '~';
|
|
80 }
|
|
81 });
|
|
82 int n = 0;
|
|
83 for( File file : files ) {
|
|
84 String name = file.getName();
|
|
85 locker.lock(name);
|
|
86 try {
|
|
87 if( newCachedPage(name).delete() )
|
|
88 n++;
|
|
89 } finally {
|
|
90 locker.unlock(name);
|
|
91 }
|
|
92 }
|
|
93 logger.error("finished sweep, deleted "+n+" pages out of "+files.length+" candidates, "+total[0]+" total");
|
|
94 } catch(RuntimeException e) {
|
|
95 logger.error("exception in sweeper",e);
|
|
96 throw e;
|
|
97 } catch(Error e) {
|
|
98 logger.error("error in sweeper",e);
|
|
99 System.exit(-1);
|
|
100 }
|
|
101 }
|
|
102 };
|
|
103 executor.scheduleWithFixedDelay( sweeper, sweepFreqSeconds, sweepFreqSeconds, TimeUnit.SECONDS );
|
|
104 }
|
|
105
|
|
106 public void destroy() {}
|
|
107
|
|
108 public static void shutdown() {}
|
|
109
|
|
110 CachedPage newCachedPage(String name) {
|
|
111 if( name.length() == 0 )
|
|
112 throw new RuntimeException("empty filenames not allowed");
|
|
113 if( name.charAt(0) == '~' )
|
|
114 throw new RuntimeException("invalid filename: "+name);
|
|
115 return hasDelayedDelete ? new CachedFile(dir,name) : new CachedDir(dir,name);
|
|
116 }
|
|
117
|
|
118 public void doFilter(
|
|
119 ServletRequest req,
|
|
120 ServletResponse res,
|
|
121 FilterChain chain
|
|
122 )
|
|
123 throws IOException, ServletException
|
|
124 {
|
|
125 HttpServletRequest request = (HttpServletRequest)req;
|
|
126 HttpServletResponse response = (HttpServletResponse)res;
|
|
127 if( !"GET".equals( request.getMethod() ) ) {
|
|
128 chain.doFilter(request,response);
|
|
129 return;
|
|
130 }
|
|
131 CachingRequestWrapper wrappedRequest = new CachingRequestWrapper(request);
|
|
132 CachingResponseWrapper wrappedResponse = new CachingResponseWrapper(this,wrappedRequest,response);
|
|
133 boolean isDone = false;
|
|
134 try {
|
|
135 chain.doFilter(wrappedRequest,wrappedResponse);
|
|
136 isDone = true;
|
|
137 } finally {
|
|
138 wrappedResponse.finish(isDone);
|
|
139 }
|
|
140
|
|
141 }
|
|
142
|
|
143 public Set<String> getEncodings() {
|
|
144 return encodings;
|
|
145 }
|
|
146
|
|
147 }
|