1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package groovy.servlet;
33
34 import groovy.lang.Binding;
35 import groovy.lang.MetaClass;
36 import groovy.lang.Closure;
37 import groovy.util.GroovyScriptEngine;
38 import groovy.util.ResourceConnector;
39 import groovy.util.ResourceException;
40 import groovy.util.ScriptException;
41
42 import java.io.IOException;
43 import java.net.URL;
44 import java.net.URLConnection;
45 import java.util.Collections;
46 import java.util.Enumeration;
47 import java.util.HashMap;
48 import java.util.Map;
49
50 import javax.servlet.ServletConfig;
51 import javax.servlet.ServletContext;
52 import javax.servlet.ServletException;
53 import javax.servlet.ServletRequest;
54 import javax.servlet.ServletResponse;
55 import javax.servlet.http.HttpServlet;
56 import javax.servlet.http.HttpServletRequest;
57 import javax.servlet.http.HttpServletResponse;
58
59 import org.codehaus.groovy.runtime.GroovyCategorySupport;
60
61 /***
62 * This servlet will run Groovy scripts as Groovlets. Groovlets are scripts
63 * with these objects implicit in their scope:
64 *
65 * <ul>
66 * <li>request - the HttpServletRequest</li>
67 * <li>response - the HttpServletResponse</li>
68 * <li>application - the ServletContext associated with the servlet</li>
69 * <li>session - the HttpSession associated with the HttpServletRequest</li>
70 * <li>out - the PrintWriter associated with the ServletRequest</li>
71 * </ul>
72 *
73 * <p>Your script sources can be placed either in your web application's normal
74 * web root (allows for subdirectories) or in /WEB-INF/groovy/* (also allows
75 * subdirectories).
76 *
77 * <p>To make your web application more groovy, you must add the GroovyServlet
78 * to your application's web.xml configuration using any mapping you like, so
79 * long as it follows the pattern *.* (more on this below). Here is the
80 * web.xml entry:
81 *
82 * <pre>
83 * <servlet>
84 * <servlet-name>Groovy</servlet-name>
85 * <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
86 * </servlet>
87 *
88 * <servlet-mapping>
89 * <servlet-name>Groovy</servlet-name>
90 * <url-pattern>*.groovy</url-pattern>
91 * </servlet-mapping>
92 * </pre>
93 *
94 * <p>The URL pattern does not require the "*.groovy" mapping. You can, for
95 * example, make it more Struts-like but groovy by making your mapping "*.gdo".
96 *
97 * <p>Whatever your mapping, the GroovyServlet will check to see if your
98 * URL ends with ".groovy" and, if it does not, it will strip your mapping
99 * and append ".groovy".
100 *
101 * <p>NOTE! The GroovyServlet only handles mappings of the *.* type, not the
102 * path-like type of /groovy/*</p>
103 *
104 * @author Sam Pullara
105 * @author Mark Turansky (markturansky at hotmail.com)
106 * @author Guillaume Laforge
107 */
108 public class GroovyServlet extends HttpServlet implements ResourceConnector {
109
110
111
112 /***
113 * A constant for ".groovy" which gets appended to script paths as needed.
114 */
115 public static final String GROOVY_EXTENSION = ".groovy";
116
117 /***
118 * The context in which this servlet is executing
119 */
120 private ServletContext sc;
121
122 /***
123 * The classloader associated with this servlet
124 */
125 private static ClassLoader parent;
126
127 /***
128 * The script engine executing the Groovy scripts for this servlet
129 */
130 private static GroovyScriptEngine gse;
131
132
133
134 /***
135 * Returns the ServletContext for this servlet
136 */
137 public ServletContext getServletContext() {
138 return sc;
139 }
140
141 /***
142 * Initialize the GroovyServlet.
143 */
144 public void init(ServletConfig config) {
145
146
147 MetaClass.setUseReflection(true);
148
149
150 sc = config.getServletContext();
151 sc.log("Groovy servlet initialized");
152
153
154
155 parent = Thread.currentThread().getContextClassLoader();
156 if (parent == null)
157 parent = GroovyServlet.class.getClassLoader();
158
159
160 gse = new GroovyScriptEngine(this);
161 }
162
163 /***
164 * Interface method for ResourceContainer. This is used by the GroovyScriptEngine.
165 */
166 public URLConnection getResourceConnection(String name) throws ResourceException {
167 try {
168 URL url = sc.getResource("/" + name);
169 if (url == null) {
170 url = sc.getResource("/WEB-INF/groovy/" + name);
171 if (url == null) {
172 throw new ResourceException("Resource " + name + " not found");
173 }
174 }
175 return url.openConnection();
176 } catch (IOException ioe) {
177 throw new ResourceException("Problem reading resource " + name);
178 }
179 }
180
181 /***
182 * Handle web requests to the GroovyServlet
183 */
184 public void service(ServletRequest request, ServletResponse response)
185 throws ServletException, IOException {
186
187
188 final HttpServletRequest httpRequest = (HttpServletRequest) request;
189 final HttpServletResponse httpResponse = (HttpServletResponse) response;
190
191
192 final String scriptFilename = getGroovyScriptPath(httpRequest);
193
194
195 response.setContentType("text/html");
196
197
198 final Binding binding = new ServletBinding((HttpServletRequest) request, response, sc);
199
200
201 try {
202 Closure closure = new Closure(gse) {
203 public Object call() {
204 try {
205 return ((GroovyScriptEngine)getDelegate()).run(scriptFilename, binding);
206 } catch (ResourceException e) {
207 throw new RuntimeException(e);
208 } catch (ScriptException e) {
209 throw new RuntimeException(e);
210 }
211 }
212 };
213 GroovyCategorySupport.use(ServletCategory.class, closure);
214 } catch (RuntimeException re) {
215
216 StringBuffer error = new StringBuffer("GroovyServlet Error: ");
217 error.append(" script: '");
218 error.append(scriptFilename);
219 error.append("': ");
220
221 Throwable e = re.getCause();
222 if (e instanceof ResourceException) {
223 error.append(" Script not found, sending 404.");
224 sc.log(error.toString());
225 System.out.println(error.toString());
226 httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
227 } else {
228
229
230 if (re.getMessage() != null)
231 error.append(re.getMessage());
232
233 if (e != null) {
234 sc.log("An error occurred processing the request", e);
235 } else {
236 sc.log("An error occurred processing the request", re);
237 }
238 sc.log(error.toString());
239 System.out.println(error.toString());
240
241 httpResponse.sendError(
242 HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
243 }
244 }
245 }
246
247
248
249 /***
250 * From the HttpServletRequest, parse the groovy script path using the URL,
251 * adding the extension ".groovy" as needed.
252 */
253 private String getGroovyScriptPath(HttpServletRequest request) {
254
255
256 int contextLength = request.getContextPath().length();
257 String scriptFilename = request.getRequestURI()
258 .substring(contextLength).substring(1);
259
260
261
262 if (scriptFilename.endsWith(GROOVY_EXTENSION))
263 return scriptFilename;
264
265
266 int lastDot = scriptFilename.lastIndexOf(".");
267 scriptFilename = scriptFilename.substring(0, lastDot)
268 + GROOVY_EXTENSION;
269 return scriptFilename;
270
271 }
272 }