1 // Copyright 2009 Google Inc. All rights reserved.
3 package com
.google
.appengine
.tools
.development
;
5 import com
.google
.apphosting
.utils
.config
.AppEngineWebXml
;
7 import org
.mortbay
.jetty
.handler
.ContextHandler
;
8 import org
.mortbay
.jetty
.servlet
.Dispatcher
;
9 import org
.mortbay
.resource
.Resource
;
10 import org
.mortbay
.util
.URIUtil
;
12 import java
.io
.IOException
;
13 import java
.net
.MalformedURLException
;
14 import java
.util
.logging
.Level
;
15 import java
.util
.logging
.Logger
;
17 import javax
.servlet
.Filter
;
18 import javax
.servlet
.FilterChain
;
19 import javax
.servlet
.FilterConfig
;
20 import javax
.servlet
.RequestDispatcher
;
21 import javax
.servlet
.ServletException
;
22 import javax
.servlet
.ServletRequest
;
23 import javax
.servlet
.ServletResponse
;
24 import javax
.servlet
.http
.HttpServletRequest
;
25 import javax
.servlet
.http
.HttpServletRequestWrapper
;
26 import javax
.servlet
.http
.HttpServletResponse
;
29 * {@code StaticFileFilter} is a {@link Filter} that replicates the
30 * static file serving logic that is present in the PFE and AppServer.
31 * This logic was originally implemented in {@link
32 * LocalResourceFileServlet} but static file serving needs to take
33 * precedence over all other servlets and filters.
36 public class StaticFileFilter
implements Filter
{
37 private static final Logger logger
=
38 Logger
.getLogger(StaticFileFilter
.class.getName());
40 private StaticFileUtils staticFileUtils
;
41 private AppEngineWebXml appEngineWebXml
;
42 private Resource resourceBase
;
43 private String
[] welcomeFiles
;
44 private String resourceRoot
;
45 private ContextHandler
.SContext servletContext
;
48 public void init(FilterConfig filterConfig
) throws ServletException
{
49 servletContext
= (ContextHandler
.SContext
) filterConfig
.getServletContext();
50 staticFileUtils
= new StaticFileUtils(servletContext
);
52 welcomeFiles
= servletContext
.getContextHandler().getWelcomeFiles();
54 appEngineWebXml
= (AppEngineWebXml
) servletContext
.getAttribute(
55 "com.google.appengine.tools.development.appEngineWebXml");
56 resourceRoot
= appEngineWebXml
.getPublicRoot();
59 resourceBase
= servletContext
.getContextHandler().getResource(URIUtil
.SLASH
+ resourceRoot
);
60 } catch (MalformedURLException ex
) {
61 logger
.log(Level
.WARNING
, "Could not initialize:", ex
);
62 throw new ServletException(ex
);
67 public void doFilter(ServletRequest request
, ServletResponse response
, FilterChain chain
)
68 throws ServletException
, IOException
{
69 Boolean forwarded
= (Boolean
) request
.getAttribute(Dispatcher
.__FORWARD_JETTY
);
70 if (forwarded
== null) {
71 forwarded
= Boolean
.FALSE
;
74 Boolean included
= (Boolean
) request
.getAttribute(Dispatcher
.__INCLUDE_JETTY
);
75 if (included
== null) {
76 included
= Boolean
.FALSE
;
79 if (forwarded
|| included
) {
80 chain
.doFilter(request
, response
);
84 HttpServletRequest httpRequest
= (HttpServletRequest
) request
;
85 HttpServletResponse httpResponse
= (HttpServletResponse
) response
;
86 String servletPath
= httpRequest
.getServletPath();
87 String pathInfo
= httpRequest
.getPathInfo();
88 String pathInContext
= URIUtil
.addPaths(servletPath
, pathInfo
);
90 if (maybeServeWelcomeFile(pathInContext
, httpRequest
, httpResponse
)) {
94 Resource resource
= null;
96 resource
= getResource(pathInContext
);
98 if (resource
!= null && resource
.exists() && !resource
.isDirectory()) {
99 if (appEngineWebXml
.includesStatic(resourceRoot
+ pathInContext
)) {
100 if (staticFileUtils
.passConditionalHeaders(httpRequest
, httpResponse
, resource
)) {
101 staticFileUtils
.sendData(httpRequest
, httpResponse
, false, resource
);
107 if (resource
!= null) {
111 chain
.doFilter(request
, response
);
115 * Get Resource to serve.
116 * @param pathInContext The path to find a resource for.
117 * @return The resource to serve.
119 private Resource
getResource(String pathInContext
) {
121 if (resourceBase
!= null) {
122 return resourceBase
.addPath(pathInContext
);
124 } catch (IOException ex
) {
125 logger
.log(Level
.WARNING
, "Could not find: " + pathInContext
, ex
);
131 * Finds a matching welcome file for the supplied path and, if
132 * found, serves it to the user. This will be the first entry in
133 * the list of configured {@link #welcomeFiles welcome files} that
134 * exists within the directory referenced by the path.
138 * @return true if a welcome file was served, false otherwise
139 * @throws IOException
140 * @throws MalformedURLException
142 private boolean maybeServeWelcomeFile(String path
,
143 HttpServletRequest request
,
144 HttpServletResponse response
)
145 throws IOException
, ServletException
{
146 if (welcomeFiles
== null) {
150 if (!path
.endsWith(URIUtil
.SLASH
)) {
151 path
+= URIUtil
.SLASH
;
154 for (String welcomeName
: welcomeFiles
) {
155 final String welcomePath
= path
+ welcomeName
;
157 Resource welcomeFile
= getResource(path
+ welcomeName
);
158 if (welcomeFile
!= null && welcomeFile
.exists()) {
159 if (appEngineWebXml
.includesStatic(resourceRoot
+ welcomePath
)) {
160 RequestDispatcher dispatcher
= servletContext
.getNamedDispatcher("_ah_default");
161 request
= new HttpServletRequestWrapper(request
) {
163 public String
getServletPath() {
168 public String
getPathInfo() {
172 return staticFileUtils
.serveWelcomeFileAsForward(dispatcher
, false, request
, response
);
181 public void destroy() {