Source code: raining/server/FileInfo.java
1 package raining.server;
2 import raining.core.*;
3 import raining.protocol.http.*;
4 import raining.util.*;
5 import java.util.*;
6 import java.io.*;
7 import java.nio.ByteBuffer;
8 /** Maintains cacheable information of a file such as content, when last
9 * updated in cache, and methods to update the cache, and tell the cache
10 * if it needs updating.
11 * TODO: should store as ByteBuffers
12 * also i can keep header and content separate, and use a gathered
13 * write. First i need to change that in NioSocket. <br>
14 * TODO: server_root needs to be set, i think.
15 * RK modified on 20031116 14:44:39: header is now part of content.
16 *
17 * Taken from Bulka's code (Java Performance and Scalability)
18 * and modified.
19 * This needs to be redone. The server sample wasn't written in an OO
20 * manner.
21 * I need to look into caching the header. Currently headers are managed by
22 * HTTPResponse implementations.
23 * We need to have a CachedObject interface with a needsRefreshing and
24 * update() contract.
25 */
26 public class FileInfo {
27 /** HTTP header.
28 * ignored for the present. header is now part of content.
29 */
30 private byte[] headers;
31 /** file content along with header.
32 */
33 //protected byte[] content;
34 protected ByteBuffer content;
35 /** when was the content last updated.
36 */
37 protected long lastUpdate;
38 /** when was the file updated - required for if-modified-since -
39 * this is a cached time.
40 */
41 protected long lastModified;
42 /** unused currently
43 */
44 private byte[] logSecondHalf;
45 /** the url or filename associated with this object. No longer
46 * required to be stored since the File object is being stored. But
47 * maybe can be kept for some time.
48 */
49 protected String url = null;
50 /** file object, stored here since its required to check whether the
51 * file has been modified or not since it was last read up.
52 */
53 protected File targ;
54
55 /** constructor taking a header but no content.
56 * why would you use this ?
57 * @param h byte[] h header
58 * @param lu long lu time updated in cache
59 * @param u url of file
60 */
61 public FileInfo (byte[] h, long lu, String u) {
62 this.url = u;
63 this.targ = urlToFile (url);
64 this.lastModified = this.targ.lastModified();
65 headers = h;
66 content = null;
67 lastUpdate = lu;
68 logSecondHalf = null;
69 }
70 /** constructor taking header and content and url.
71 * Header and content are concatenated and stored as content.
72 * This approach exists if you
73 * wish to keep sending fresh headers with current/lazy date.
74 * however, i wish to keep static headers. need to ascertain whether
75 * this breaks the rfc.
76 * @param h header
77 * @param c content
78 * @param u url
79 */
80 public FileInfo (byte[] h, byte[] c, String u) {
81 this.url = u;
82 this.targ = urlToFile (url);
83 this.lastModified = this.targ.lastModified();
84 headers = h;
85 byte [] tmpcontent;
86 if (h == null)
87 tmpcontent = c;
88 else
89 tmpcontent = join (h, c);
90 this.content = ByteBuffer.wrap(tmpcontent);
91
92 this.lastUpdate = System.currentTimeMillis();
93 this.logSecondHalf = null;
94 }
95 /** constructor that takes only filename.
96 * This allows us to create
97 * the OK header since that needs file date also.
98 */
99 public FileInfo (String u) {
100 this.url = u;
101 this.targ = urlToFile (url);
102 this.lastModified = this.targ.lastModified();
103 //this.content = ByteBuffer.wrap(getBytes(url));
104 lastUpdate = System.currentTimeMillis();
105 logSecondHalf = null;
106 }
107 /** static method to get a file info object.
108 * @param url String url name of file
109 * @return FileInfo file info object
110 */
111 public static FileInfo getFileInfo (String url) throws java.io.IOException {
112
113 byte[] tmp = getBytes(url);
114 FileInfo fi = new FileInfo( url );
115 Response resp = new HttpOKResponse( null, tmp, fi.lastModified );
116 fi.setContent(resp.getBytes());
117 return fi;
118 }
119 /** updates the cache whenever the file is updated.
120 * this also recreates the header RK modified on 20031116 14:44:11
121 */
122 public void update() throws java.io.IOException {
123 byte[] tmp = getBytes(this.url);
124 Response resp = new HttpOKResponse( null, tmp, this.lastModified );
125 this.content = ByteBuffer.wrap(resp.getBytes());
126 this.lastUpdate = System.currentTimeMillis();
127 }
128 // a file can be deleted after caching
129 /** read up the files contents into a byte array.
130 * why am i adding the CRLF at start. doesnt the header have that at
131 * end already. RK 20031116 15:12:54
132 * @param url files url
133 * @return byte array file content.
134 */
135 public static byte[] getBytes (String url) throws java.io.IOException{
136 File targ = urlToFile (url);
137 byte b[] = null;
138 InputStream is = null;
139 try {
140 is = new FileInputStream(targ.getAbsolutePath());
141 int n;
142 b = new byte[2+(int)targ.length()];
143 byte [] buf = new byte[2048];
144 b[0] = (byte) '\r';
145 b[1] = (byte) '\n';
146 int blen = 2;
147 while ((n = is.read(buf)) > 0) {
148 //ps.write(buf, 0, n);
149 // Assuming everything fits in a single buffer...
150 System.arraycopy(buf,0,b,blen,n);
151 blen += n;
152 }
153 } catch (FileNotFoundException exc) {
154 //System.err.println( "throwing exc!"+ exc.toString());
155
156 throw exc;
157 } catch (IOException exc) { System.err.println( P+" L 135 EXC:"+ exc.toString()); exc.printStackTrace();
158 } finally {
159 try {
160 is.close();
161 } catch (Exception ignoredexc) { }
162 }
163 return b;
164 }
165
166
167 public void setHeaders(byte[] h) {
168 headers = h;
169 }
170 public void setContent(byte[] c) {
171 content = ByteBuffer.wrap(c);
172 }
173 public void setContent(ByteBuffer c) {
174 content = c;
175 }
176 public void setLastUpdate(long t) {
177 lastUpdate = t;
178 }
179 public void setLogSecondHalf(byte[] s) {
180 logSecondHalf = s;
181 }
182 public byte[] getHeaders() {
183 return headers;
184 }
185 /** returns content as a ByteByffer, after duplicating the buffer.
186 * Thus, multiple threads may use the same at a time.
187 */
188 public ByteBuffer getContent() {
189 return content.duplicate();
190 }
191 public long getLastUpdate() {
192 return lastUpdate;
193 }
194 public byte[] getLogSecondHalf() {
195 return logSecondHalf;
196 }
197 public static File urlToFile(String url) {
198 String fname = url.replace('/', File.separatorChar);
199 if (fname.startsWith(File.separator)) {
200 fname = fname.substring(1);
201 }
202 File targ = new File(server_root, fname);
203 if (targ.isDirectory()) {
204 File ind = new File(targ, "index.html");
205 if (ind.exists()) {
206 targ = ind;
207 }
208 }
209 return targ;
210 }
211 public static String server_root = null;
212
213 /*
214 public void needsRefreshing (boolean b){
215 needsRefreshing = b;
216 }
217 */
218 /** returns true if the files actual lastModified is greater than
219 * the cache update time.
220 * This invokes an expensive system call to find out file update
221 * time.
222 */
223 public boolean needsRefreshing (){
224 return targ.lastModified()>lastUpdate;
225 }
226
227 /** joins 2 byte arrays and returns a new array.
228 * written for the ctor that takes header and content as separate
229 * arrays.
230 */
231 public static byte[] join (byte[] a, byte b[]){
232
233 assert a != null : "Received a null byte array" ;
234 byte[] n = new byte[ a.length + b.length ];
235 System.arraycopy(a, 0, n, 0, a.length);
236 System.arraycopy(b, 0, n, a.length, b.length);
237 return n;
238
239 }
240 /** returns true if the file has been modified since the
241 * time requested by the user (in if-Modified-Since field of
242 * header).
243 */
244 public boolean ifModifiedSince (long requestedTime){
245 return lastModified > requestedTime;
246 }
247
248 public static void main (String args[]){
249
250 if (args.length==0){
251 System.out.println( "pls give a file name");
252 System.exit(-1);
253 }
254
255 try {
256 FileInfo fi = FileInfo.getFileInfo(args[0]);
257 System.out.println( fi.getContent());
258 //System.out.println( new String(fi.getContent()));
259 System.out.println( " needs:" + fi.needsRefreshing());
260 try {
261 Thread.sleep(15000);
262 } catch (Exception exc) { System.err.println( " L EXC:"+ exc.toString()); exc.printStackTrace(); }
263 System.out.println( " needs:" + fi.needsRefreshing());
264 } catch (Exception exc) { System.err.println( " L EXC:"+ exc.toString()); exc.printStackTrace(); }
265 }
266
267
268
269 private static final String P = "FileInfo";
270
271 /*
272 // RFC 822 and RFC 1123
273 SimpleDateFormat fo =
274 new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
275 fo.setTimeZone(TimeZone.getTimeZone("GMT"));
276 requests.setIfNotSet("If-Modified-Since", fo.format(date));
277 */
278
279 }