001    /*
002     * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
003     *
004     * Redistribution and use of this software and associated documentation
005     * ("Software"), with or without modification, are permitted provided that the
006     * following conditions are met: 1. Redistributions of source code must retain
007     * copyright statements and notices. Redistributions must also contain a copy
008     * of this document. 2. Redistributions in binary form must reproduce the above
009     * copyright notice, this list of conditions and the following disclaimer in
010     * the documentation and/or other materials provided with the distribution. 3.
011     * The name "groovy" must not be used to endorse or promote products derived
012     * from this Software without prior written permission of The Codehaus. For
013     * written permission, please contact info@codehaus.org. 4. Products derived
014     * from this Software may not be called "groovy" nor may "groovy" appear in
015     * their names without prior written permission of The Codehaus. "groovy" is a
016     * registered trademark of The Codehaus. 5. Due credit should be given to The
017     * Codehaus - http://groovy.codehaus.org/
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
020     * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022     * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
023     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
024     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
025     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
026     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
027     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
028     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
029     * DAMAGE.
030     *
031     */
032    package groovy.servlet;
033    
034    import groovy.lang.Binding;
035    import groovy.lang.Closure;
036    import groovy.util.GroovyScriptEngine;
037    import groovy.util.ResourceException;
038    import groovy.util.ScriptException;
039    
040    import java.io.IOException;
041    
042    import javax.servlet.ServletConfig;
043    import javax.servlet.ServletException;
044    import javax.servlet.http.HttpServletRequest;
045    import javax.servlet.http.HttpServletResponse;
046    
047    import org.codehaus.groovy.runtime.GroovyCategorySupport;
048    
049    /**
050     * This servlet will run Groovy scripts as Groovlets.  Groovlets are scripts
051     * with these objects implicit in their scope:
052     *
053     * <ul>
054     *      <li>request - the HttpServletRequest</li>
055     *  <li>response - the HttpServletResponse</li>
056     *  <li>application - the ServletContext associated with the servlet</li>
057     *  <li>session - the HttpSession associated with the HttpServletRequest</li>
058     *  <li>out - the PrintWriter associated with the ServletRequest</li>
059     * </ul>
060     *
061     * <p>Your script sources can be placed either in your web application's normal
062     * web root (allows for subdirectories) or in /WEB-INF/groovy/* (also allows
063     * subdirectories).
064     *
065     * <p>To make your web application more groovy, you must add the GroovyServlet
066     * to your application's web.xml configuration using any mapping you like, so
067     * long as it follows the pattern *.* (more on this below).  Here is the
068     * web.xml entry:
069     *
070     * <pre>
071     *    <servlet>
072     *      <servlet-name>Groovy</servlet-name>
073     *      <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
074     *    </servlet>
075     *
076     *    <servlet-mapping>
077     *      <servlet-name>Groovy</servlet-name>
078     *      <url-pattern>*.groovy</url-pattern>
079     *      <url-pattern>*.gdo</url-pattern>
080     *    </servlet-mapping>
081     * </pre>
082     *
083     * <p>The URL pattern does not require the "*.groovy" mapping.  You can, for
084     * example, make it more Struts-like but groovy by making your mapping "*.gdo".
085     *
086     * @author Sam Pullara
087     * @author Mark Turansky (markturansky at hotmail.com)
088     * @author Guillaume Laforge
089     * @author Christian Stein
090     * 
091     * @see groovy.servlet.ServletBinding
092     */
093    public class GroovyServlet extends AbstractHttpServlet {
094    
095        /**
096         * The script engine executing the Groovy scripts for this servlet
097         */
098        private static GroovyScriptEngine gse;
099    
100        /**
101         * Initialize the GroovyServlet.
102         *
103         * @throws ServletException
104         *  if this method encountered difficulties
105         */
106        public void init(ServletConfig config) throws ServletException {
107            super.init(config);
108    
109            // Set up the scripting engine
110            gse = new GroovyScriptEngine(this);
111    
112            servletContext.log("Groovy servlet initialized on " + gse + ".");
113        }
114    
115        /**
116         * Handle web requests to the GroovyServlet
117         */
118        public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
119    
120            // Get the script path from the request - include aware (GROOVY-815)
121            final String scriptUri = getScriptUri(request);
122    
123            // Set it to HTML by default
124            response.setContentType("text/html");
125    
126            // Set up the script context
127            final Binding binding = new ServletBinding(request, response, servletContext);
128    
129            // Run the script
130            try {
131                Closure closure = new Closure(gse) {
132    
133                    public Object call() {
134                        try {
135                            return ((GroovyScriptEngine) getDelegate()).run(scriptUri, binding);
136                        } catch (ResourceException e) {
137                            throw new RuntimeException(e);
138                        } catch (ScriptException e) {
139                            throw new RuntimeException(e);
140                        }
141                    }
142    
143                };
144                GroovyCategorySupport.use(ServletCategory.class, closure);
145                /*
146                 * Set reponse code 200.
147                 */
148                response.setStatus(HttpServletResponse.SC_OK);
149            } catch (RuntimeException runtimeException) {
150                StringBuffer error = new StringBuffer("GroovyServlet Error: ");
151                error.append(" script: '");
152                error.append(scriptUri);
153                error.append("': ");
154                Throwable e = runtimeException.getCause();
155                /*
156                 * Null cause?!
157                 */
158                if (e == null) {
159                    error.append(" Script processing failed.");
160                    error.append(runtimeException.getMessage());
161                    error.append(runtimeException.getStackTrace()[0].toString());
162                    servletContext.log(error.toString());
163                    System.err.println(error.toString());
164                    runtimeException.printStackTrace(System.err);
165                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error.toString());
166                    return;
167                }
168                /*
169                 * Resource not found.
170                 */
171                if (e instanceof ResourceException) {
172                    error.append(" Script not found, sending 404.");
173                    servletContext.log(error.toString());
174                    System.err.println(error.toString());
175                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
176                    return;
177                }
178                /*
179                 * Other internal error. Perhaps syntax?! 
180                 */
181                servletContext.log("An error occurred processing the request", runtimeException);
182                error.append(e.getMessage());
183                error.append(e.getStackTrace()[0].toString());
184                servletContext.log(e.toString());
185                System.err.println(e.toString());
186                runtimeException.printStackTrace(System.err);
187                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
188            } finally {
189                /*
190                 * Finally, flush the response buffer.
191                 */
192                response.flushBuffer();
193                // servletContext.log("Flushed response buffer.");
194            }
195        }
196    
197    }