KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > shell > GWTShellServlet


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.shell;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20 import com.google.gwt.dev.cfg.ModuleDef;
21 import com.google.gwt.dev.cfg.ModuleDefLoader;
22 import com.google.gwt.dev.util.HttpHeaders;
23 import com.google.gwt.dev.util.SelectionScriptGenerator;
24 import com.google.gwt.dev.util.log.ServletContextTreeLogger;
25
26 import java.io.File JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.OutputStream JavaDoc;
30 import java.io.PrintWriter JavaDoc;
31 import java.net.MalformedURLException JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.net.URLConnection JavaDoc;
34 import java.util.Date JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.Iterator JavaDoc;
37 import java.util.Map JavaDoc;
38
39 import javax.servlet.ServletContext JavaDoc;
40 import javax.servlet.ServletException JavaDoc;
41 import javax.servlet.http.HttpServlet JavaDoc;
42 import javax.servlet.http.HttpServletRequest JavaDoc;
43 import javax.servlet.http.HttpServletResponse JavaDoc;
44
45 /**
46  * Built-in servlet for convenient access to the public path of a specified
47  * module.
48  */

49 public class GWTShellServlet extends HttpServlet JavaDoc {
50
51   private static class RequestParts {
52     public final String JavaDoc moduleName;
53
54     public final String JavaDoc partialPath;
55
56     public RequestParts(HttpServletRequest JavaDoc request)
57         throws UnableToCompleteException {
58       String JavaDoc pathInfo = request.getPathInfo();
59       if (pathInfo != null) {
60         int slash = pathInfo.indexOf('/', 1);
61         if (slash != -1) {
62           moduleName = pathInfo.substring(1, slash);
63           partialPath = pathInfo.substring(slash + 1);
64           return;
65         } else {
66           moduleName = pathInfo.substring(1);
67           partialPath = null;
68           return;
69         }
70       }
71       throw new UnableToCompleteException();
72     }
73   }
74
75   /**
76    * This the default cache time in seconds for files that aren't either
77    * *.cache.*, *.nocache.*.
78    */

79   private static final int DEFAULT_CACHE_SECONDS = 5;
80
81   private static final String JavaDoc XHTML_MIME_TYPE = "application/xhtml+xml";
82
83   private final Map JavaDoc loadedModulesByName = new HashMap JavaDoc();
84
85   private final Map JavaDoc loadedServletsByClassName = new HashMap JavaDoc();
86
87   private final Map JavaDoc mimeTypes = new HashMap JavaDoc();
88
89   private final Map JavaDoc modulesByServletPath = new HashMap JavaDoc();
90
91   private int nextRequestId;
92
93   private File JavaDoc outDir;
94
95   private final Object JavaDoc requestIdLock = new Object JavaDoc();
96
97   private TreeLogger topLogger;
98
99   public GWTShellServlet() {
100     initMimeTypes();
101   }
102
103   protected void doGet(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
104       throws ServletException JavaDoc, IOException JavaDoc {
105     processFileRequest(request, response);
106   }
107
108   protected void doPost(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
109       throws ServletException JavaDoc, IOException JavaDoc {
110     processFileRequest(request, response);
111   }
112
113   protected void processFileRequest(HttpServletRequest JavaDoc request,
114       HttpServletResponse JavaDoc response) throws IOException JavaDoc {
115
116     String JavaDoc pathInfo = request.getPathInfo();
117     if (pathInfo.length() == 0 || pathInfo.equals("/")) {
118       response.setContentType("text/html");
119       PrintWriter JavaDoc writer = response.getWriter();
120       writer.println("<html><body><basefont face='arial'>");
121       writer.println("To launch an application, specify a URL of the form <code>/<i>module</i>/<i>file.html</i></code>");
122       writer.println("</body></html>");
123       return;
124     }
125
126     TreeLogger logger = getLogger();
127
128     // Parse the request assuming it is module/resource.
129
//
130
RequestParts parts;
131     try {
132       parts = new RequestParts(request);
133     } catch (UnableToCompleteException e) {
134       sendErrorResponse(response, HttpServletResponse.SC_NOT_FOUND,
135           "Don't know what to do with this URL: '" + pathInfo + "'");
136       return;
137     }
138
139     String JavaDoc partialPath = parts.partialPath;
140     String JavaDoc moduleName = parts.moduleName;
141
142     if (partialPath == null) {
143       // Redir back to the same URL but ending with a slash.
144
//
145
response.sendRedirect(moduleName + "/");
146       return;
147     } else if (partialPath.length() > 0) {
148       // Both the module name and a resource.
149
//
150
doGetPublicFile(request, response, logger, partialPath, moduleName);
151       return;
152     } else {
153       // Was just the module name, ending with a slash.
154
//
155
doGetModule(request, response, logger, parts);
156       return;
157     }
158   }
159
160   protected void service(HttpServletRequest JavaDoc request,
161       HttpServletResponse JavaDoc response) throws ServletException JavaDoc, IOException JavaDoc {
162
163     TreeLogger logger = getLogger();
164     int id = allocateRequestId();
165     if (logger.isLoggable(TreeLogger.TRACE)) {
166       StringBuffer JavaDoc url = request.getRequestURL();
167
168       // Branch the logger in case we decide to log more below.
169
logger = logger.branch(TreeLogger.TRACE, "Request " + id + ": " + url,
170           null);
171     }
172
173     String JavaDoc servletClassName = null;
174
175     try {
176       // Attempt to split the URL into module/path, which we'll use to see
177
// if we can map the request to a module's servlet.
178
RequestParts parts = new RequestParts(request);
179
180       // See if the request references a module we know.
181
// Note that we do *not* actually try to load the module here, because
182
// we're only looking for servlet invocations, which can only happen
183
// when we have *already* loaded the destination module to serve up the
184
// client code in the first place.
185
ModuleDef moduleDef = (ModuleDef) loadedModulesByName.get(parts.moduleName);
186       if (moduleDef != null) {
187         // Okay, we know this module. Do we know this servlet path?
188
// It is right to prepend the slash because (1) ModuleDefSchema requires
189
// every servlet path to begin with a slash and (2) RequestParts always
190
// rips off the leading slash.
191
String JavaDoc servletPath = "/" + parts.partialPath;
192         servletClassName = moduleDef.findServletForPath(servletPath);
193
194         // Fall-through below, where we check servletClassName.
195
} else {
196         // Fall-through below, where we check servletClassName.
197
}
198     } catch (UnableToCompleteException e) {
199       // Do nothing, since it was speculative anyway.
200
}
201
202     // BEGIN BACKWARD COMPATIBILITY
203
if (servletClassName == null) {
204       // Try to map a bare path that isn't preceded by the module name.
205
// This is no longer the recommended practice, so we warn.
206
String JavaDoc path = request.getPathInfo();
207       ModuleDef moduleDef = (ModuleDef) modulesByServletPath.get(path);
208       if (moduleDef != null) {
209         // See if there is a servlet we can delegate to for the given url.
210
servletClassName = moduleDef.findServletForPath(path);
211
212         if (servletClassName != null) {
213           TreeLogger branch = logger.branch(TreeLogger.WARN,
214               "Use of deprecated hosted mode servlet path mapping", null);
215           branch.log(
216               TreeLogger.WARN,
217               "The client code is invoking the servlet with a URL that is not module-relative: "
218                   + path, null);
219           branch.log(
220               TreeLogger.WARN,
221               "Prepend GWT.getModuleBaseURL() to the URL in client code to create a module-relative URL: /"
222                   + moduleDef.getName() + path, null);
223           branch.log(
224               TreeLogger.WARN,
225               "Using module-relative URLs ensures correct URL-independent behavior in external servlet containers",
226               null);
227         }
228
229         // Fall-through below, where we check servletClassName.
230
} else {
231         // Fall-through below, where we check servletClassName.
232
}
233     }
234     // END BACKWARD COMPATIBILITY
235

236     // Load/get the servlet if we found one.
237
if (servletClassName != null) {
238       HttpServlet JavaDoc delegatee = tryGetOrLoadServlet(logger, servletClassName);
239       if (delegatee == null) {
240         logger.log(TreeLogger.ERROR, "Unable to dispatch request", null);
241         sendErrorResponse(response,
242             HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
243             "Unable to find/load mapped servlet class '" + servletClassName
244                 + "'");
245         return;
246       }
247
248       // Delegate everything to the downstream servlet and we're done.
249
delegatee.service(request, response);
250     } else {
251       // Use normal default processing on this request, since we couldn't
252
// recognize it as anything special.
253
super.service(request, response);
254     }
255   }
256
257   private int allocateRequestId() {
258     synchronized (requestIdLock) {
259       return nextRequestId++;
260     }
261   }
262
263   /**
264    * Handle auto-generated resources.
265    *
266    * @return <code>true</code> if a resource was generated
267    */

268   private boolean autoGenerateResources(HttpServletRequest JavaDoc request,
269       HttpServletResponse JavaDoc response, TreeLogger logger, String JavaDoc partialPath,
270       String JavaDoc moduleName) throws IOException JavaDoc {
271
272     // If the request is of the form ".../moduleName.nocache.js" or
273
// ".../moduleName-xs.nocache.js" then generate the selection script for
274
// them.
275
boolean nocacheHtml = partialPath.equals(moduleName + ".nocache.js");
276     boolean nocacheScript = !nocacheHtml
277         && partialPath.equals(moduleName + "-xs.nocache.js");
278     if (nocacheHtml || nocacheScript) {
279       // If the '?compiled' request property is specified, don't auto-generate.
280
if (request.getParameter("compiled") == null) {
281         // Generate the .js file.
282
try {
283           String JavaDoc js = genSelectionScript(logger, moduleName, nocacheScript);
284           setResponseCacheHeaders(response, 0); // do not cache selection script
285
response.setStatus(HttpServletResponse.SC_OK);
286           response.setContentType("text/javascript");
287           response.getWriter().println(js);
288           return true;
289         } catch (UnableToCompleteException e) {
290           // The error will have already been logged. Continue, since this could
291
// actually be a request for a static file that happens to have an
292
// unfortunately confusing name.
293
}
294       }
295     }
296
297     return false;
298   }
299
300   private void doGetModule(HttpServletRequest JavaDoc request,
301       HttpServletResponse JavaDoc response, TreeLogger logger, RequestParts parts)
302       throws IOException JavaDoc {
303
304     if ("favicon.ico".equalsIgnoreCase(parts.moduleName)) {
305       sendErrorResponse(response, HttpServletResponse.SC_NOT_FOUND,
306           "Icon not available");
307       return;
308     }
309
310     // Generate a generic empty host page.
311
//
312
String JavaDoc msg = "The development shell servlet received a request to generate a host page for module '"
313         + parts.moduleName + "' ";
314
315     logger = logger.branch(TreeLogger.TRACE, msg, null);
316
317     try {
318       // Try to load the module just to make sure it'll work.
319
getModuleDef(logger, parts.moduleName);
320     } catch (UnableToCompleteException e) {
321       sendErrorResponse(response, HttpServletResponse.SC_NOT_FOUND,
322           "Unable to find/load module '" + parts.moduleName
323               + "' (see server log for details)");
324       return;
325     }
326
327     response.setContentType("text/html");
328     PrintWriter JavaDoc writer = response.getWriter();
329     writer.println("<html><head>");
330     writer.print("<script language='javascript' SRC='");
331     writer.print(parts.moduleName);
332     writer.println(".nocache.js'></script>");
333
334     // Create a property for each query param.
335
//
336
Map JavaDoc params = request.getParameterMap();
337     for (Iterator JavaDoc iter = params.entrySet().iterator(); iter.hasNext();) {
338       Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
339       String JavaDoc[] values = (String JavaDoc[]) entry.getValue();
340       if (values.length > 0) {
341         writer.print("<meta name='gwt:property' content='");
342         writer.print(entry.getKey());
343         writer.print("=");
344         writer.print(values[values.length - 1]);
345         writer.println("'>");
346       }
347     }
348
349     writer.println("</head><body>");
350     writer.println("<iframe SRC=\"javascript:''\" id='__gwt_historyFrame' "
351         + "style='width:0;height:0;border:0'></iframe>");
352     writer.println("</body></html>");
353
354     // Done.
355
}
356
357   /**
358    * Fetch a file and return it as the HTTP response, setting the cache-related
359    * headers according to the name of the file (see
360    * {@link #getCacheTime(String)}). This function honors If-Modified-Since to
361    * minimize the impact of limiting caching of files for development.
362    *
363    * @param request the HTTP request
364    * @param response the HTTP response
365    * @param logger a TreeLogger to use for debug output
366    * @param partialPath the path within the module
367    * @param moduleName the name of the module
368    * @throws IOException
369    */

370   private void doGetPublicFile(HttpServletRequest JavaDoc request,
371       HttpServletResponse JavaDoc response, TreeLogger logger, String JavaDoc partialPath,
372       String JavaDoc moduleName) throws IOException JavaDoc {
373
374     // Create a logger branch for this request.
375
String JavaDoc msg = "The development shell servlet received a request for '"
376         + partialPath + "' in module '" + moduleName + "' ";
377     logger = logger.branch(TreeLogger.TRACE, msg, null);
378
379     // Handle auto-generation of resources.
380
if (autoGenerateResources(request, response, logger, partialPath,
381         moduleName)) {
382       return;
383     }
384
385     URL JavaDoc foundResource;
386     try {
387       // Look for the requested file on the public path.
388
//
389
ModuleDef moduleDef = getModuleDef(logger, moduleName);
390       foundResource = moduleDef.findPublicFile(partialPath);
391
392       if (foundResource == null) {
393         // Look in the place where we write compiled output.
394
File JavaDoc moduleDir = new File JavaDoc(getOutputDir(), moduleName);
395         File JavaDoc requestedFile = new File JavaDoc(moduleDir, partialPath);
396         if (requestedFile.exists()) {
397           try {
398             foundResource = requestedFile.toURL();
399           } catch (MalformedURLException JavaDoc e) {
400             // ignore since it was speculative anyway
401
}
402         }
403
404         if (foundResource == null) {
405           msg = "Resource not found: " + partialPath;
406           logger.log(TreeLogger.WARN, msg, null);
407           throw new UnableToCompleteException();
408         }
409       }
410     } catch (UnableToCompleteException e) {
411       sendErrorResponse(response, HttpServletResponse.SC_NOT_FOUND,
412           "Cannot find resource '" + partialPath
413               + "' in the public path of module '" + moduleName + "'");
414       return;
415     }
416
417     // Get the MIME type.
418
String JavaDoc path = foundResource.toExternalForm();
419     String JavaDoc mimeType = null;
420     try {
421       mimeType = getServletContext().getMimeType(path);
422     } catch (UnsupportedOperationException JavaDoc e) {
423       // Certain minimalist servlet containers throw this.
424
// Fall through to guess the type.
425
}
426
427     if (mimeType == null) {
428       mimeType = guessMimeType(path);
429       msg = "Guessed MIME type '" + mimeType + "'";
430       logger.log(TreeLogger.TRACE, msg, null);
431     }
432
433     maybeIssueXhtmlWarning(logger, mimeType, partialPath);
434
435     long cacheSeconds = getCacheTime(path);
436
437     InputStream JavaDoc is = null;
438     try {
439       // Check for up-to-datedness.
440
URLConnection JavaDoc conn = foundResource.openConnection();
441       long lastModified = conn.getLastModified();
442       if (isNotModified(request, lastModified)) {
443         response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
444         setResponseCacheHeaders(response, cacheSeconds);
445         return;
446       }
447
448       // Set up headers to really send it.
449
response.setStatus(HttpServletResponse.SC_OK);
450       long now = new Date JavaDoc().getTime();
451       response.setHeader(HttpHeaders.DATE,
452           HttpHeaders.toInternetDateFormat(now));
453       response.setContentType(mimeType);
454       String JavaDoc lastModifiedStr = HttpHeaders.toInternetDateFormat(lastModified);
455       response.setHeader(HttpHeaders.LAST_MODIFIED, lastModifiedStr);
456
457       // Expiration header. Either immediately stale (requiring an
458
// "If-Modified-Since") or infinitely cacheable (not requiring even a
459
// freshness check).
460
setResponseCacheHeaders(response, cacheSeconds);
461
462       // Content length.
463
int contentLength = conn.getContentLength();
464       if (contentLength >= 0) {
465         response.setHeader(HttpHeaders.CONTENT_LENGTH,
466             Integer.toString(contentLength));
467       }
468
469       // Send the bytes.
470
is = foundResource.openStream();
471       streamOut(is, response.getOutputStream(), 1024 * 8);
472     } finally {
473       if (is != null) {
474         try {
475           is.close();
476         } catch (IOException JavaDoc swallowed) {
477           // Nothing we can do now.
478
//
479
}
480       }
481     }
482   }
483
484   /**
485    * Generates a module.js file on the fly. Note that the nocache file that is
486    * generated that can only be used for hosted mode. It cannot produce a web
487    * mode version, since this servlet doesn't know strong names, since by
488    * definition of "hosted mode" JavaScript hasn't been compiled at this point.
489    */

490   private String JavaDoc genSelectionScript(TreeLogger logger, String JavaDoc moduleName,
491       boolean asScript) throws UnableToCompleteException {
492     String JavaDoc msg = asScript ? "Generating a script selection script for module "
493         : "Generating an html selection script for module ";
494     msg += moduleName;
495     logger.log(TreeLogger.TRACE, msg, null);
496
497     ModuleDef moduleDef = getModuleDef(logger, moduleName);
498     SelectionScriptGenerator gen = new SelectionScriptGenerator(moduleDef);
499     return gen.generateSelectionScript(false, asScript);
500   }
501
502   /**
503    * Get the length of time a given file should be cacheable. If the path
504    * contains *.nocache.*, it is never cacheable; if it contains *.cache.*, it
505    * is infinitely cacheable; anything else gets a default time.
506    *
507    * @return cache time in seconds, or 0 if the file is not cacheable at all
508    */

509   private long getCacheTime(String JavaDoc path) {
510     int lastDot = path.lastIndexOf('.');
511     if (lastDot >= 0) {
512       String JavaDoc prefix = path.substring(0, lastDot);
513       if (prefix.endsWith(".cache")) {
514         // RFC2616 says to never give a cache time of more than a year
515
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
516
return HttpHeaders.SEC_YR;
517       } else if (prefix.endsWith(".nocache")) {
518         return 0;
519       }
520     }
521     return DEFAULT_CACHE_SECONDS;
522   }
523
524   private synchronized TreeLogger getLogger() {
525     if (topLogger == null) {
526       ServletContext JavaDoc servletContext = getServletContext();
527       final String JavaDoc attr = "com.google.gwt.dev.shell.logger";
528       topLogger = (TreeLogger) servletContext.getAttribute(attr);
529       if (topLogger == null) {
530         // No shell available, so wrap the regular servlet context logger.
531
//
532
topLogger = new ServletContextTreeLogger(servletContext);
533       }
534     }
535     return topLogger;
536   }
537
538   /**
539    * We don't actually log this on purpose since the client does anyway.
540    */

541   private ModuleDef getModuleDef(TreeLogger logger, String JavaDoc moduleName)
542       throws UnableToCompleteException {
543     synchronized (loadedModulesByName) {
544       ModuleDef moduleDef = (ModuleDef) loadedModulesByName.get(moduleName);
545       if (moduleDef == null) {
546         moduleDef = ModuleDefLoader.loadFromClassPath(logger, moduleName);
547         loadedModulesByName.put(moduleName, moduleDef);
548
549         // BEGIN BACKWARD COMPATIBILITY
550
// The following map of servlet path to module is included only
551
// for backward-compatibility. We are going to remove this functionality
552
// when we go out of beta. The new behavior is that the client should
553
// specify the module name as part of the URL and construct it using
554
// getModuleBaseURL().
555
String JavaDoc[] servletPaths = moduleDef.getServletPaths();
556         for (int i = 0; i < servletPaths.length; i++) {
557           ModuleDef oldDef = (ModuleDef) modulesByServletPath.put(
558               servletPaths[i], moduleDef);
559           if (oldDef != null) {
560             logger.log(TreeLogger.WARN, "Undefined behavior: Servlet path "
561                 + servletPaths[i] + " conflicts in modules "
562                 + moduleDef.getName() + " and " + oldDef.getName(), null);
563           }
564         }
565         // END BACKWARD COMPATIBILITY
566
}
567       return moduleDef;
568     }
569   }
570
571   private synchronized File JavaDoc getOutputDir() {
572     if (outDir == null) {
573       ServletContext JavaDoc servletContext = getServletContext();
574       final String JavaDoc attr = "com.google.gwt.dev.shell.outdir";
575       outDir = (File JavaDoc) servletContext.getAttribute(attr);
576       assert (outDir != null);
577     }
578     return outDir;
579   }
580
581   private String JavaDoc guessMimeType(String JavaDoc fullPath) {
582     int dot = fullPath.lastIndexOf('.');
583     if (dot != -1) {
584       String JavaDoc ext = fullPath.substring(dot + 1);
585       String JavaDoc mimeType = (String JavaDoc) mimeTypes.get(ext);
586       if (mimeType != null) {
587         return mimeType;
588       }
589
590       // Otherwise, fall through.
591
//
592
}
593
594     // Last resort.
595
//
596
return "application/octet-stream";
597   }
598
599   private void initMimeTypes() {
600     mimeTypes.put("abs", "audio/x-mpeg");
601     mimeTypes.put("ai", "application/postscript");
602     mimeTypes.put("aif", "audio/x-aiff");
603     mimeTypes.put("aifc", "audio/x-aiff");
604     mimeTypes.put("aiff", "audio/x-aiff");
605     mimeTypes.put("aim", "application/x-aim");
606     mimeTypes.put("art", "image/x-jg");
607     mimeTypes.put("asf", "video/x-ms-asf");
608     mimeTypes.put("asx", "video/x-ms-asf");
609     mimeTypes.put("au", "audio/basic");
610     mimeTypes.put("avi", "video/x-msvideo");
611     mimeTypes.put("avx", "video/x-rad-screenplay");
612     mimeTypes.put("bcpio", "application/x-bcpio");
613     mimeTypes.put("bin", "application/octet-stream");
614     mimeTypes.put("bmp", "image/bmp");
615     mimeTypes.put("body", "text/html");
616     mimeTypes.put("cdf", "application/x-cdf");
617     mimeTypes.put("cer", "application/x-x509-ca-cert");
618     mimeTypes.put("class", "application/java");
619     mimeTypes.put("cpio", "application/x-cpio");
620     mimeTypes.put("csh", "application/x-csh");
621     mimeTypes.put("css", "text/css");
622     mimeTypes.put("dib", "image/bmp");
623     mimeTypes.put("doc", "application/msword");
624     mimeTypes.put("dtd", "text/plain");
625     mimeTypes.put("dv", "video/x-dv");
626     mimeTypes.put("dvi", "application/x-dvi");
627     mimeTypes.put("eps", "application/postscript");
628     mimeTypes.put("etx", "text/x-setext");
629     mimeTypes.put("exe", "application/octet-stream");
630     mimeTypes.put("gif", "image/gif");
631     mimeTypes.put("gtar", "application/x-gtar");
632     mimeTypes.put("gz", "application/x-gzip");
633     mimeTypes.put("hdf", "application/x-hdf");
634     mimeTypes.put("hqx", "application/mac-binhex40");
635     mimeTypes.put("htc", "text/x-component");
636     mimeTypes.put("htm", "text/html");
637     mimeTypes.put("html", "text/html");
638     mimeTypes.put("hqx", "application/mac-binhex40");
639     mimeTypes.put("ief", "image/ief");
640     mimeTypes.put("jad", "text/vnd.sun.j2me.app-descriptor");
641     mimeTypes.put("jar", "application/java-archive");
642     mimeTypes.put("java", "text/plain");
643     mimeTypes.put("jnlp", "application/x-java-jnlp-file");
644     mimeTypes.put("jpe", "image/jpeg");
645     mimeTypes.put("jpeg", "image/jpeg");
646     mimeTypes.put("jpg", "image/jpeg");
647     mimeTypes.put("js", "text/javascript");
648     mimeTypes.put("jsf", "text/plain");
649     mimeTypes.put("jspf", "text/plain");
650     mimeTypes.put("kar", "audio/x-midi");
651     mimeTypes.put("latex", "application/x-latex");
652     mimeTypes.put("m3u", "audio/x-mpegurl");
653     mimeTypes.put("mac", "image/x-macpaint");
654     mimeTypes.put("man", "application/x-troff-man");
655     mimeTypes.put("me", "application/x-troff-me");
656     mimeTypes.put("mid", "audio/x-midi");
657     mimeTypes.put("midi", "audio/x-midi");
658     mimeTypes.put("mif", "application/x-mif");
659     mimeTypes.put("mov", "video/quicktime");
660     mimeTypes.put("movie", "video/x-sgi-movie");
661     mimeTypes.put("mp1", "audio/x-mpeg");
662     mimeTypes.put("mp2", "audio/x-mpeg");
663     mimeTypes.put("mp3", "audio/x-mpeg");
664     mimeTypes.put("mpa", "audio/x-mpeg");
665     mimeTypes.put("mpe", "video/mpeg");
666     mimeTypes.put("mpeg", "video/mpeg");
667     mimeTypes.put("mpega", "audio/x-mpeg");
668     mimeTypes.put("mpg", "video/mpeg");
669     mimeTypes.put("mpv2", "video/mpeg2");
670     mimeTypes.put("ms", "application/x-wais-source");
671     mimeTypes.put("nc", "application/x-netcdf");
672     mimeTypes.put("oda", "application/oda");
673     mimeTypes.put("pbm", "image/x-portable-bitmap");
674     mimeTypes.put("pct", "image/pict");
675     mimeTypes.put("pdf", "application/pdf");
676     mimeTypes.put("pgm", "image/x-portable-graymap");
677     mimeTypes.put("pic", "image/pict");
678     mimeTypes.put("pict", "image/pict");
679     mimeTypes.put("pls", "audio/x-scpls");
680     mimeTypes.put("png", "image/png");
681     mimeTypes.put("pnm", "image/x-portable-anymap");
682     mimeTypes.put("pnt", "image/x-macpaint");
683     mimeTypes.put("ppm", "image/x-portable-pixmap");
684     mimeTypes.put("ppt", "application/powerpoint");
685     mimeTypes.put("ps", "application/postscript");
686     mimeTypes.put("psd", "image/x-photoshop");
687     mimeTypes.put("qt", "video/quicktime");
688     mimeTypes.put("qti", "image/x-quicktime");
689     mimeTypes.put("qtif", "image/x-quicktime");
690     mimeTypes.put("ras", "image/x-cmu-raster");
691     mimeTypes.put("rgb", "image/x-rgb");
692     mimeTypes.put("rm", "application/vnd.rn-realmedia");
693     mimeTypes.put("roff", "application/x-troff");
694     mimeTypes.put("rtf", "application/rtf");
695     mimeTypes.put("rtx", "text/richtext");
696     mimeTypes.put("sh", "application/x-sh");
697     mimeTypes.put("shar", "application/x-shar");
698     mimeTypes.put("smf", "audio/x-midi");
699     mimeTypes.put("sit", "application/x-stuffit");
700     mimeTypes.put("snd", "audio/basic");
701     mimeTypes.put("src", "application/x-wais-source");
702     mimeTypes.put("sv4cpio", "application/x-sv4cpio");
703     mimeTypes.put("sv4crc", "application/x-sv4crc");
704     mimeTypes.put("swf", "application/x-shockwave-flash");
705     mimeTypes.put("t", "application/x-troff");
706     mimeTypes.put("tar", "application/x-tar");
707     mimeTypes.put("tcl", "application/x-tcl");
708     mimeTypes.put("tex", "application/x-tex");
709     mimeTypes.put("texi", "application/x-texinfo");
710     mimeTypes.put("texinfo", "application/x-texinfo");
711     mimeTypes.put("tif", "image/tiff");
712     mimeTypes.put("tiff", "image/tiff");
713     mimeTypes.put("tr", "application/x-troff");
714     mimeTypes.put("tsv", "text/tab-separated-values");
715     mimeTypes.put("txt", "text/plain");
716     mimeTypes.put("ulw", "audio/basic");
717     mimeTypes.put("ustar", "application/x-ustar");
718     mimeTypes.put("xbm", "image/x-xbitmap");
719     mimeTypes.put("xht", "application/xhtml+xml");
720     mimeTypes.put("xhtml", "application/xhtml+xml");
721     mimeTypes.put("xml", "text/xml");
722     mimeTypes.put("xpm", "image/x-xpixmap");
723     mimeTypes.put("xsl", "text/xml");
724     mimeTypes.put("xwd", "image/x-xwindowdump");
725     mimeTypes.put("wav", "audio/x-wav");
726     mimeTypes.put("svg", "image/svg+xml");
727     mimeTypes.put("svgz", "image/svg+xml");
728     mimeTypes.put("vsd", "application/x-visio");
729     mimeTypes.put("wbmp", "image/vnd.wap.wbmp");
730     mimeTypes.put("wml", "text/vnd.wap.wml");
731     mimeTypes.put("wmlc", "application/vnd.wap.wmlc");
732     mimeTypes.put("wmls", "text/vnd.wap.wmlscript");
733     mimeTypes.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
734     mimeTypes.put("wrl", "x-world/x-vrml");
735     mimeTypes.put("Z", "application/x-compress");
736     mimeTypes.put("z", "application/x-compress");
737     mimeTypes.put("zip", "application/zip");
738   }
739
740   /**
741    * Checks to see whether or not a client's file is out of date relative to the
742    * original.
743    */

744   private boolean isNotModified(HttpServletRequest JavaDoc request, long ageOfServerCopy) {
745     // The age of the server copy *must* have the milliseconds truncated.
746
// Since milliseconds isn't part of the GMT format, failure to truncate
747
// will leave the file in a state where it appears constantly out of date
748
// and yet it can never get in sync because the Last-Modified date keeps
749
// truncating off the milliseconds part on its way out.
750
//
751
ageOfServerCopy -= (ageOfServerCopy % 1000);
752
753     long ageOfClientCopy = 0;
754     String JavaDoc ifModifiedSince = request.getHeader("If-Modified-Since");
755     if (ifModifiedSince != null) {
756       // Rip off any additional stuff at the end, such as "; length="
757
// (IE does add this).
758
//
759
int lastSemi = ifModifiedSince.lastIndexOf(';');
760       if (lastSemi != -1) {
761         ifModifiedSince = ifModifiedSince.substring(0, lastSemi);
762       }
763       ageOfClientCopy = HttpHeaders.fromInternetDateFormat(ifModifiedSince);
764     }
765
766     if (ageOfClientCopy >= ageOfServerCopy) {
767       // The client already has a good copy.
768
//
769
return true;
770     } else {
771       // The client needs a fresh copy of the requested file.
772
//
773
return false;
774     }
775   }
776
777   private void maybeIssueXhtmlWarning(TreeLogger logger, String JavaDoc mimeType,
778       String JavaDoc path) {
779     if (!XHTML_MIME_TYPE.equals(mimeType)) {
780       return;
781     }
782
783     String JavaDoc msg = "File was returned with content-type of \"" + mimeType
784         + "\". GWT requires browser features that are not available to "
785         + "documents with this content-type.";
786
787     int ix = path.lastIndexOf('.');
788     if (ix >= 0 && ix < path.length()) {
789       String JavaDoc base = path.substring(0, ix);
790       msg += " Consider renaming \"" + path + "\" to \"" + base + ".html\".";
791     }
792
793     logger.log(TreeLogger.WARN, msg, null);
794   }
795
796   private void sendErrorResponse(HttpServletResponse JavaDoc response, int statusCode,
797       String JavaDoc msg) throws IOException JavaDoc {
798     response.setContentType("text/html");
799     response.getWriter().println(msg);
800     response.setStatus(statusCode);
801   }
802
803   /**
804    * Sets the Cache-control and Expires headers in the response based on the
805    * supplied cache time.
806    *
807    * Expires is used in addition to Cache-control for older clients or proxies
808    * which may not properly understand Cache-control.
809    *
810    * @param response the HttpServletResponse to update
811    * @param cacheTime non-negative number of seconds to cache the response; 0
812    * means specifically do not allow caching at all.
813    * @throws IllegalArgumentException if cacheTime is negative
814    */

815   private void setResponseCacheHeaders(HttpServletResponse JavaDoc response,
816       long cacheTime) {
817     long expires;
818     if (cacheTime < 0) {
819       throw new IllegalArgumentException JavaDoc("cacheTime of " + cacheTime
820           + " is negative");
821     }
822     if (cacheTime > 0) {
823       // Expire the specified seconds in the future.
824
expires = new Date JavaDoc().getTime() + cacheTime * HttpHeaders.MS_SEC;
825     } else {
826       // Prevent caching by using a time in the past for cache expiration.
827
// Use January 2, 1970 00:00:00, to account for timezone changes
828
// in case a browser tries to convert to a local timezone first
829
// 0=Jan 1, so add 1 day's worth of milliseconds to get Jan 2
830
expires = HttpHeaders.SEC_DAY * HttpHeaders.MS_SEC;
831     }
832     response.setHeader(HttpHeaders.CACHE_CONTROL,
833         HttpHeaders.CACHE_CONTROL_MAXAGE + cacheTime);
834     String JavaDoc expiresString = HttpHeaders.toInternetDateFormat(expires);
835     response.setHeader(HttpHeaders.EXPIRES, expiresString);
836   }
837
838   private void streamOut(InputStream JavaDoc in, OutputStream JavaDoc out, int bufferSize)
839       throws IOException JavaDoc {
840     assert (bufferSize >= 0);
841
842     byte[] buffer = new byte[bufferSize];
843     int bytesRead = 0;
844     while (true) {
845       bytesRead = in.read(buffer);
846       if (bytesRead >= 0) {
847         // Copy the bytes out.
848
out.write(buffer, 0, bytesRead);
849       } else {
850         // End of input stream.
851
return;
852       }
853     }
854   }
855
856   private HttpServlet JavaDoc tryGetOrLoadServlet(TreeLogger logger, String JavaDoc className) {
857     synchronized (loadedServletsByClassName) {
858       HttpServlet JavaDoc servlet = (HttpServlet JavaDoc) loadedServletsByClassName.get(className);
859       if (servlet != null) {
860         // Found it.
861
//
862
return servlet;
863       }
864
865       // Try to load and instantiate it.
866
//
867
Throwable JavaDoc caught = null;
868       try {
869         Class JavaDoc servletClass = Class.forName(className);
870         Object JavaDoc newInstance = servletClass.newInstance();
871         if (!(newInstance instanceof HttpServlet JavaDoc)) {
872           logger.log(TreeLogger.ERROR,
873               "Not compatible with HttpServlet: " + className
874                   + " (does your service extend RemoteServiceServlet?)", null);
875           return null;
876         }
877
878         // Success. Hang onto the instance so we can reuse it.
879
//
880
servlet = (HttpServlet JavaDoc) newInstance;
881
882         servlet.init(getServletConfig());
883
884         loadedServletsByClassName.put(className, servlet);
885         return servlet;
886       } catch (ClassNotFoundException JavaDoc e) {
887         caught = e;
888       } catch (InstantiationException JavaDoc e) {
889         caught = e;
890       } catch (IllegalAccessException JavaDoc e) {
891         caught = e;
892       } catch (ServletException JavaDoc e) {
893         caught = e;
894       }
895       String JavaDoc msg = "Unable to instantiate '" + className + "'";
896       logger.log(TreeLogger.ERROR, msg, caught);
897       return null;
898     }
899   }
900 }
901
Popular Tags