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 servletContext.log(error.toString());
161 System.err.println(error.toString());
162 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
163 return;
164 }
165 /*
166 * Resource not found.
167 */
168 if (e instanceof ResourceException) {
169 error.append(" Script not found, sending 404.");
170 servletContext.log(error.toString());
171 System.err.println(error.toString());
172 response.sendError(HttpServletResponse.SC_NOT_FOUND);
173 return;
174 }
175 /*
176 * Other internal error. Perhaps syntax?!
177 */
178 servletContext.log("An error occurred processing the request", e);
179 servletContext.log(error.toString());
180 System.err.println(error.toString());
181 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
182 } finally {
183 /*
184 * Finally, flush the response buffer.
185 */
186 response.flushBuffer();
187 // servletContext.log("Flushed response buffer.");
188 }
189 }
190
191 }