001    /*
002     * $Id: AbstractHttpServlet.java,v 1.4 2005/06/05 08:16:08 cstein Exp $
003     * 
004     * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005     * 
006     * Redistribution and use of this software and associated documentation
007     * ("Software"), with or without modification, are permitted provided that the
008     * following conditions are met:
009     * 
010     * 1. Redistributions of source code must retain copyright statements and
011     * notices. Redistributions must also contain a copy of this document.
012     * 
013     * 2. Redistributions in binary form must reproduce the above copyright notice,
014     * this list of conditions and the following disclaimer in the documentation
015     * and/or other materials provided with the distribution.
016     * 
017     * 3. The name "groovy" must not be used to endorse or promote products derived
018     * from this Software without prior written permission of The Codehaus. For
019     * written permission, please contact info@codehaus.org.
020     * 
021     * 4. Products derived from this Software may not be called "groovy" nor may
022     * "groovy" appear in their names without prior written permission of The
023     * Codehaus. "groovy" is a registered trademark of The Codehaus.
024     * 
025     * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
026     * 
027     * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
028     * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
029     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
030     * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
031     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
032     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
033     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
034     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
035     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
036     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037     *  
038     */
039    package groovy.servlet;
040    
041    import groovy.lang.MetaClass;
042    import groovy.util.ResourceConnector;
043    import groovy.util.ResourceException;
044    
045    import java.io.File;
046    import java.io.IOException;
047    import java.net.URL;
048    import java.net.URLConnection;
049    
050    import javax.servlet.ServletConfig;
051    import javax.servlet.ServletContext;
052    import javax.servlet.ServletException;
053    import javax.servlet.http.HttpServlet;
054    import javax.servlet.http.HttpServletRequest;
055    
056    /**
057     * A common ground dealing with the http servlet API wrinkles.
058     *
059     * @author Christian Stein
060     */
061    public abstract class AbstractHttpServlet extends HttpServlet implements
062        ResourceConnector {
063    
064      /**
065       * Content type of the HTTP response.
066       */
067      public static final String CONTENT_TYPE_TEXT_HTML = "text/html";
068    
069      /**
070       * Servlet API include key name: path_info
071       */
072      public static final String INC_PATH_INFO = "javax.servlet.include.path_info";
073      
074      /* Not used, yet. See comments in getScriptUri(HttpServletRequest request)!
075       * Servlet API include key name: request_uri
076       */
077      // public static final String INC_REQUEST_URI = "javax.servlet.include.request_uri";
078      
079      /**
080       * Servlet API include key name: servlet_path
081       */
082      public static final String INC_SERVLET_PATH = "javax.servlet.include.servlet_path";
083    
084      /**
085       * Debug flag logging the class the class loader of the request.
086       */
087      private boolean logRequestClassAndLoaderOnce;
088      
089      /**
090       * Servlet (or the web application) context.
091       */
092      protected ServletContext servletContext;
093      
094      /**
095       * Initializes all fields.
096       * 
097       */
098      public AbstractHttpServlet() {
099        this.logRequestClassAndLoaderOnce = true;
100        this.servletContext = null;
101      }
102    
103      /**
104       * Interface method for ResourceContainer. This is used by the GroovyScriptEngine.
105       */
106      public URLConnection getResourceConnection(String name)
107          throws ResourceException {
108        try {
109          URL url = servletContext.getResource("/" + name);
110          if (url == null) {
111            url = servletContext.getResource("/WEB-INF/groovy/" + name);
112            if (url == null) {
113              throw new ResourceException("Resource " + name + " not found");
114            }
115          }
116          return url.openConnection();
117        }
118        catch (IOException ioe) {
119          throw new ResourceException("Problem reading resource " + name);
120        }
121      }
122    
123      /**
124       * Returns the include-aware uri of the script or template file.
125       * 
126       * @param request
127       *  the http request to analyze
128       * @return the include-aware uri either parsed from request attributes or
129       *  hints provided by the servlet container
130       */
131      protected String getScriptUri(HttpServletRequest request) {
132        // 
133        if (logRequestClassAndLoaderOnce) {
134          log("Logging request class and its class loader:");
135          log(" c = request.getClass() :\"" + request.getClass()+ "\"");
136          log(" l = c.getClassLoader() :\"" + request.getClass().getClassLoader()+ "\"");
137          log(" l.getClass()           :\"" + request.getClass().getClassLoader().getClass()+ "\"");
138          logRequestClassAndLoaderOnce = false;
139        }
140    
141        //
142        // NOTE: This piece of code is heavily inspired by Apaches Jasper2!
143        // 
144        // http://cvs.apache.org/viewcvs.cgi/jakarta-tomcat-jasper/jasper2/ \
145        //        src/share/org/apache/jasper/servlet/JspServlet.java?view=markup
146        //
147        // Why doesn't it use request.getRequestURI() or INC_REQUEST_URI?
148        //
149    
150        String uri = null;
151        String info = null;
152    
153        //
154        // Check to see if the requested script/template source file has been the
155        // target of a RequestDispatcher.include().
156        //
157        uri = (String) request.getAttribute(INC_SERVLET_PATH);
158        if (uri != null) {
159          //
160          // Requested script/template file has been target of 
161          // RequestDispatcher.include(). Its path is assembled from the relevant
162          // javax.servlet.include.* request attributes and returned!
163          //
164          info = (String) request.getAttribute(INC_PATH_INFO);
165          if (info != null) {
166            uri += info;
167          }
168          return uri;
169        }
170    
171        //
172        // Requested script/template file has not been the target of a 
173        // RequestDispatcher.include(). Reconstruct its path from the request's
174        // getServletPath() and getPathInfo() results.
175        //
176        uri = request.getServletPath();
177        info = request.getPathInfo();
178        if (info != null) {
179          uri += info;
180        }
181    
182        return uri;
183      }
184    
185      /**
186       * Parses the http request for the real script or template source file.
187       * 
188       * @param request
189       *  the http request to analyze
190       * @param context
191       *  the context of this servlet used to get the real path string
192       * @return a file object using an absolute file path name
193       */
194      protected File getScriptUriAsFile(HttpServletRequest request) {
195        String uri = getScriptUri(request);
196        String real = servletContext.getRealPath(uri);
197        File file = new File(real).getAbsoluteFile();
198    
199        // log("\tInclude-aware URI: " + uri);
200        // log("\tContext real path: " + real); // context.getRealPath(uri)
201        // log("\t             File: " + file);
202        // log("\t      File exists? " + file.exists());
203        // log("\t    File can read? " + file.canRead());
204        // log("\t      ServletPath: " + request.getServletPath());
205        // log("\t         PathInfo: " + request.getPathInfo()); 
206        // log("\t       RequestURI: " + request.getRequestURI());
207        // log("\t      QueryString: " + request.getQueryString());
208    
209        // //log("\t  Request Params: ");
210        // //Enumeration e = request.getParameterNames();
211        // //while (e.hasMoreElements()) {
212        // //  String name = (String) e.nextElement();
213        // //  log("\t\t " + name + " = " + request.getParameter(name));
214        // //}   
215    
216        return file;
217      }
218    
219      /**
220       * Overrides the generic init method.
221       * 
222       * Enables a fix, that tells Groovy to use (slower) reflection than compiling
223       * metaclass proxies. This is needed due to some container implementation hide
224       * their classes from the servlet by using different class loaders. See
225       * {@link http://jira.codehaus.org/browse/GROOVY-861} for details.
226       * 
227       * @param config
228       *  the servlet coniguration provided by the container
229       * @throws ServletException if init() method defined in super class 
230       *  javax.servlet.GenericServlet throws it
231       */
232      public void init(ServletConfig config) throws ServletException {
233        super.init(config);
234        this.servletContext = config.getServletContext();
235    
236        // FIXME http://jira.codehaus.org/browse/GROOVY-861
237        MetaClass.setUseReflection(true);
238        String value = config.getInitParameter("logRequestClassAndLoaderOnce");
239        if (value != null) {
240          this.logRequestClassAndLoaderOnce = Boolean.valueOf(value).booleanValue();
241        }
242    
243      }
244    
245    }