KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > servlet > FileHandler


1 /*
2  * Copyright 2005 Joe Walker
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.directwebremoting.servlet;
17
18 import java.io.BufferedReader JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.InputStreamReader JavaDoc;
22 import java.io.PrintWriter JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import javax.servlet.http.HttpServletRequest JavaDoc;
27 import javax.servlet.http.HttpServletResponse JavaDoc;
28
29 import org.directwebremoting.extend.DwrConstants;
30 import org.directwebremoting.extend.Handler;
31 import org.directwebremoting.util.IdGenerator;
32 import org.directwebremoting.util.JavascriptUtil;
33 import org.directwebremoting.util.LocalUtil;
34 import org.directwebremoting.util.Logger;
35 import org.directwebremoting.util.MimeConstants;
36
37 /**
38  * Basically a file servlet component that does some <b>very limitted</b>
39  * EL type processing on the file. See the source for the cheat.
40  * @author Joe Walker [joe at getahead dot ltd dot uk]
41  */

42 public class FileHandler implements Handler
43 {
44     /**
45      * Create a new FileHandler
46      * @param filePath The filePath to search for, process and output
47      * @param mimeType The mime type to use for this output file
48      * @param dynamic Should the script be recalculated each time?
49      */

50     public FileHandler(String JavaDoc filePath, String JavaDoc mimeType, boolean dynamic)
51     {
52         this.filePath = filePath;
53         this.mimeType = mimeType;
54         this.dynamic = dynamic;
55     }
56
57     /**
58      * Create a new FileHandler
59      */

60     public FileHandler()
61     {
62     }
63
64     /* (non-Javadoc)
65      * @see org.directwebremoting.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
66      */

67     public void handle(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response) throws IOException JavaDoc
68     {
69         if (dynamic)
70         {
71             response.setHeader("pragma", "public");
72             response.setHeader("Expires", "0");
73             response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
74         }
75
76         if (!dynamic && isUpToDate(request))
77         {
78             response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
79             return;
80         }
81
82         String JavaDoc output;
83
84         synchronized (scriptCache)
85         {
86             output = (String JavaDoc) scriptCache.get(filePath);
87             if (output == null)
88             {
89                 StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
90
91                 String JavaDoc resource = DwrConstants.PACKAGE + filePath;
92                 InputStream JavaDoc raw = getClass().getResourceAsStream(resource);
93                 if (raw == null)
94                 {
95                     throw new IOException JavaDoc("Failed to find resource: " + resource);
96                 }
97
98                 BufferedReader JavaDoc in = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(raw));
99                 while (true)
100                 {
101                     String JavaDoc line = in.readLine();
102                     if (line == null)
103                     {
104                         break;
105                     }
106
107                     if (dynamic)
108                     {
109                         if (line.indexOf(PARAM_SCRIPT_COOKIENAME) != -1)
110                         {
111                             line = LocalUtil.replace(line, PARAM_SCRIPT_COOKIENAME, sessionCookieName);
112                         }
113
114                         if (line.indexOf(PARAM_SCRIPT_SESSIONID) != -1)
115                         {
116                             line = LocalUtil.replace(line, PARAM_SCRIPT_SESSIONID, generator.generateId(pageIdLength));
117                         }
118
119                         if (line.indexOf(PARAM_SCRIPT_ALLOWGET) != -1)
120                         {
121                             line = LocalUtil.replace(line, PARAM_SCRIPT_ALLOWGET, String.valueOf(allowGetForSafariButMakeForgeryEasier));
122                         }
123
124                         if (line.indexOf(PARAM_SCRIPT_TAG_PROTECTION) != -1)
125                         {
126                             line = LocalUtil.replace(line, PARAM_SCRIPT_TAG_PROTECTION, scriptTagProtection);
127                         }
128
129                         if (line.indexOf(PARAM_DEFAULT_PATH) != -1)
130                         {
131                             String JavaDoc path = request.getContextPath() + request.getServletPath();
132                             if (overridePath != null)
133                             {
134                                 path = overridePath;
135                             }
136                             line = LocalUtil.replace(line, PARAM_DEFAULT_PATH, path);
137                         }
138                     }
139
140                     buffer.append(line);
141                     buffer.append('\n');
142                 }
143
144                 output = buffer.toString();
145
146                 if (mimeType.equals(MimeConstants.MIME_JS) && scriptCompressed)
147                 {
148                     output = JavascriptUtil.compress(output, compressionLevel);
149                 }
150
151                 if (!dynamic)
152                 {
153                     scriptCache.put(filePath, output);
154                 }
155             }
156         }
157
158         response.setContentType(mimeType);
159         response.setDateHeader(HttpConstants.HEADER_LAST_MODIFIED, servletContainerStartTime);
160         response.setHeader(HttpConstants.HEADER_ETAG, etag);
161
162         PrintWriter JavaDoc out = response.getWriter();
163         out.println(output);
164     }
165
166     /**
167      * Do we need to send the conent for this file
168      * @param req The HTTP request
169      * @return true iff the ETags and If-Modified-Since headers say we have not changed
170      */

171     private boolean isUpToDate(HttpServletRequest JavaDoc req)
172     {
173         if (ignoreLastModified)
174         {
175             return false;
176         }
177
178         long modifiedSince = -1;
179         try
180         {
181             // HACK: Webfear appears to get confused sometimes
182
modifiedSince = req.getDateHeader(HttpConstants.HEADER_IF_MODIFIED);
183         }
184         catch (RuntimeException JavaDoc ex)
185         {
186             // TODO: Check for "length" and re-parse
187
// Normally clients send If-Modified-Since in rfc-compliant form
188
// ("If-Modified-Since: Tue, 13 Mar 2007 13:11:09 GMT") some proxies
189
// or browsers add length to this header so it comes like
190
// ("If-Modified-Since: Tue, 13 Mar 2007 13:11:09 GMT; length=35946")
191
// Servlet spec says container can throw IllegalArgumentException
192
// if header value can not be parsed as http-date.
193
// We might want to check for "; length=" and then do our own parsing
194
// See: http://getahead.org/bugs/browse/DWR-20
195
// And: http://www-1.ibm.com/support/docview.wss?uid=swg1PK20062
196
}
197
198         if (modifiedSince != -1)
199         {
200             // Browsers are only accurate to the second
201
modifiedSince -= modifiedSince % 1000;
202         }
203         String JavaDoc givenEtag = req.getHeader(HttpConstants.HEADER_IF_NONE);
204
205         // Deal with missing etags
206
if (givenEtag == null)
207         {
208             // There is no ETag, just go with If-Modified-Since
209
if (modifiedSince > servletContainerStartTime)
210             {
211                 if (log.isDebugEnabled())
212                 {
213                     log.debug("Sending 304 for " + filePath + " If-Modified-Since=" + modifiedSince + ", Last-Modified=" + servletContainerStartTime);
214                 }
215                 return true;
216             }
217
218             // There are no modified setttings, carry on
219
return false;
220         }
221
222         // Deal with missing If-Modified-Since
223
if (modifiedSince == -1)
224         {
225             if (!etag.equals(givenEtag))
226             {
227                 // There is an ETag, but no If-Modified-Since
228
if (log.isDebugEnabled())
229                 {
230                     log.debug("Sending 304 for " + filePath + " Old ETag=" + givenEtag + ", New ETag=" + etag);
231                 }
232                 return true;
233             }
234
235             // There are no modified setttings, carry on
236
return false;
237         }
238
239         // Do both values indicate that we are in-date?
240
if (etag.equals(givenEtag) && modifiedSince <= servletContainerStartTime)
241         {
242             if (log.isDebugEnabled())
243             {
244                 log.debug("Sending 304 for " + filePath);
245             }
246             return true;
247         }
248
249         return false;
250     }
251
252     /**
253      * @param allowGetForSafariButMakeForgeryEasier Do we reduce security to help Safari
254      */

255     public void setAllowGetForSafariButMakeForgeryEasier(boolean allowGetForSafariButMakeForgeryEasier)
256     {
257         this.allowGetForSafariButMakeForgeryEasier = allowGetForSafariButMakeForgeryEasier;
258     }
259
260     /**
261      * @param ignoreLastModified The ignoreLastModified to set.
262      */

263     public void setIgnoreLastModified(boolean ignoreLastModified)
264     {
265         this.ignoreLastModified = ignoreLastModified;
266     }
267
268     /**
269      * Alter the session cookie name from the default JSESSIONID.
270      * @param sessionCookieName the sessionCookieName to set
271      */

272     public void setSessionCookieName(String JavaDoc sessionCookieName)
273     {
274         this.sessionCookieName = sessionCookieName;
275     }
276
277     /**
278      * To what level do we compress scripts?
279      * @param scriptCompressed The scriptCompressed to set.
280      */

281     public void setScriptCompressed(boolean scriptCompressed)
282     {
283         this.scriptCompressed = scriptCompressed;
284     }
285
286     /**
287      * @param compressionLevel The compressionLevel to set.
288      */

289     public void setCompressionLevel(int compressionLevel)
290     {
291         this.compressionLevel = compressionLevel;
292     }
293
294     /**
295      * @param filePath the filePath to set
296      */

297     public void setFilePath(String JavaDoc filePath)
298     {
299         this.filePath = filePath;
300     }
301
302     /**
303      * Are we expected to do the minor EL type processing?
304      * @param dynamic the dynamic to set
305      */

306     public void setDynamic(boolean dynamic)
307     {
308         this.dynamic = dynamic;
309     }
310
311     /**
312      * The mime type to send the output under
313      * @param mimeType the mimeType to set
314      */

315     public void setMimeType(String JavaDoc mimeType)
316     {
317         this.mimeType = mimeType;
318     }
319
320     /**
321      * What is the string we use for script tag hack protection
322      * @param scriptTagProtection the scriptTagProtection to set
323      */

324     public void setScriptTagProtection(String JavaDoc scriptTagProtection)
325     {
326         this.scriptTagProtection = scriptTagProtection;
327     }
328
329     /**
330      * If we need to override the default path
331      * @param overridePath The new override path
332      */

333     public void setOverridePath(String JavaDoc overridePath)
334     {
335         this.overridePath = overridePath;
336     }
337
338     /**
339      * If we need to override the default path
340      */

341     private String JavaDoc overridePath = null;
342
343     /**
344      * By default we disable GET, but this hinders old Safaris
345      */

346     private boolean allowGetForSafariButMakeForgeryEasier = false;
347
348     /**
349      * What is the string we use for script tag hack protection
350      */

351     private String JavaDoc scriptTagProtection = DwrConstants.SCRIPT_TAG_PROTECTION;
352
353     /**
354      * Do we ignore all the Last-Modified/ETags blathering?
355      */

356     protected boolean ignoreLastModified = false;
357
358     /**
359      * The session cookie name
360      */

361     protected String JavaDoc sessionCookieName = "JSESSIONID";
362
363     /**
364      * How much do we compression javascript by?
365      */

366     protected int compressionLevel = JavascriptUtil.LEVEL_DEBUGGABLE;
367
368     /**
369      * Do we retain comments and unneeded spaces in Javascript code?
370      */

371     protected boolean scriptCompressed = false;
372
373     /**
374      * The method by which we get new page ids
375      */

376     protected IdGenerator generator = new IdGenerator();
377
378     /**
379      * The page id length
380      */

381     protected int pageIdLength = 16;
382
383     /**
384      * We cache the script output for speed
385      */

386     protected final Map JavaDoc scriptCache = new HashMap JavaDoc();
387
388     /**
389      * The file filePath and resource filePath (minus org.directwebremoting) to read from
390      */

391     private String JavaDoc filePath;
392
393     /**
394      * The mime type to send the output under
395      */

396     private String JavaDoc mimeType;
397
398     /**
399      * Are we expected to do the minor EL type processing?
400      */

401     private boolean dynamic;
402
403     /**
404      * The time on the script files
405      */

406     private static final long servletContainerStartTime;
407
408     /**
409      * The etag (=time for us) on the script files
410      */

411     private static final String JavaDoc etag;
412
413     /**
414      * Initialize the container start time
415      */

416     static
417     {
418         // Browsers are only accurate to the second
419
long now = System.currentTimeMillis();
420         servletContainerStartTime = now - (now % 1000);
421
422         etag = "\"" + servletContainerStartTime + '\"';
423     }
424
425     /**
426      * Does engine.js do GETs for Safari
427      */

428     protected static final String JavaDoc PARAM_SCRIPT_ALLOWGET = "${allowGetForSafariButMakeForgeryEasier}";
429
430     /**
431      * The page id parameter that goes in engine.js
432      */

433     protected static final String JavaDoc PARAM_SCRIPT_SESSIONID = "${scriptSessionId}";
434
435     /**
436      * Under what cookie name is the session id stored?
437      */

438     protected static final String JavaDoc PARAM_SCRIPT_COOKIENAME = "${sessionCookieName}";
439
440     /**
441      * What is the replacement field we use to tell engine.js what we are using
442      * for script tag hack protection
443      */

444     protected static final String JavaDoc PARAM_SCRIPT_TAG_PROTECTION = "${scriptTagProtection}";
445
446     /**
447      * What is the replacement field we use to tell engine.js what we are using
448      * for script tag hack protection
449      */

450     protected static final String JavaDoc PARAM_DEFAULT_PATH = "${defaultPath}";
451
452     /**
453      * The log stream
454      */

455     private static final Logger log = Logger.getLogger(FileHandler.class);
456 }
457
Popular Tags