View Javadoc

1   /*
2    * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
3    * 
4    * Redistribution and use of this software and associated documentation
5    * ("Software"), with or without modification, are permitted provided that the
6    * following conditions are met: 1. Redistributions of source code must retain
7    * copyright statements and notices. Redistributions must also contain a copy
8    * of this document. 2. Redistributions in binary form must reproduce the above
9    * copyright notice, this list of conditions and the following disclaimer in
10   * the documentation and/or other materials provided with the distribution. 3.
11   * The name "groovy" must not be used to endorse or promote products derived
12   * from this Software without prior written permission of The Codehaus. For
13   * written permission, please contact info@codehaus.org. 4. Products derived
14   * from this Software may not be called "groovy" nor may "groovy" appear in
15   * their names without prior written permission of The Codehaus. "groovy" is a
16   * registered trademark of The Codehaus. 5. Due credit should be given to The
17   * Codehaus - http://groovy.codehaus.org/
18   * 
19   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
20   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29   * DAMAGE.
30   *  
31   */
32  
33  /*
34   * 
35   * The Seasar Software License, Version 1.1
36   *
37   * Copyright (c) 2003-2004 The Seasar Project. All rights reserved.
38   *
39   * Redistribution and use in source and binary forms, with or 
40   * without modification, are permitted provided that the following 
41   * conditions are met:
42   *
43   * 1. Redistributions of source code must retain the above 
44   *    copyright notice, this list of conditions and the following 
45   *    disclaimer. 
46   *
47   * 2. Redistributions in binary form must reproduce the above 
48   *    copyright notice, this list of conditions and the following 
49   *    disclaimer in the documentation and/or other materials provided 
50   *    with the distribution.
51   *
52   * 3. The end-user documentation included with the redistribution,
53   *    if any, must include the following acknowledgement:  
54   *    "This product includes software developed by the 
55   *    Seasar Project (http://www.seasar.org/)."
56   *    Alternately, this acknowledgement may appear in the software
57   *    itself, if and wherever such third-party acknowledgements 
58   *    normally appear.
59   *
60   * 4. Neither the name "The Seasar Project" nor the names of its
61   *    contributors may be used to endorse or promote products derived 
62   *    from this software without specific prior written permission of 
63   *    the Seasar Project.
64   *
65   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR 
66   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
67   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
68   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE SEASAR PROJECT 
69   * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
70   * INCIDENTAL,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
71   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
72   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
73   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
74   * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING 
75   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
76   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
77   */
78  
79  package org.seasar.groovy.servlet;
80  
81  import groovy.lang.Binding;
82  import groovy.lang.Closure;
83  import groovy.lang.MetaClass;
84  import groovy.servlet.GroovyServlet;
85  import groovy.servlet.ServletCategory;
86  import groovy.util.GroovyScriptEngine;
87  import groovy.util.ResourceConnector;
88  import groovy.util.ResourceException;
89  import groovy.util.ScriptException;
90  
91  import java.io.IOException;
92  import java.net.URL;
93  import java.net.URLConnection;
94  import java.util.ArrayList;
95  import java.util.Collections;
96  import java.util.Enumeration;
97  import java.util.HashMap;
98  import java.util.List;
99  import java.util.Map;
100 
101 import javax.servlet.ServletConfig;
102 import javax.servlet.ServletContext;
103 import javax.servlet.ServletException;
104 import javax.servlet.ServletRequest;
105 import javax.servlet.ServletResponse;
106 import javax.servlet.http.HttpServlet;
107 import javax.servlet.http.HttpServletRequest;
108 import javax.servlet.http.HttpServletResponse;
109 
110 import org.codehaus.groovy.runtime.GroovyCategorySupport;
111 import org.seasar.framework.container.S2Container;
112 import org.seasar.framework.container.factory.SingletonS2ContainerFactory;
113 import org.seasar.framework.util.StringUtil;
114 
115 /***
116  * 
117  * @author takai
118  * @version $Revision: 1.2 $
119  */
120 public class S2GroovyServlet extends HttpServlet implements ResourceConnector {
121 
122 	private static class ServletCacheEntry {
123 
124 		private Map dependencies = new HashMap();
125 
126 		private long lastModified;
127 
128 		private Class servletScriptClass;
129 	}
130 
131 	public static final String CONFIG_PATH_KEY = "configPath";
132 
133 	private static GroovyScriptEngine gse;
134 
135 	private static ClassLoader parent;
136 
137 	private static Map servletCache = Collections
138 			.synchronizedMap(new HashMap());
139 
140 	public static S2Container getContainer() {
141 		return SingletonS2ContainerFactory.getContainer();
142 	}
143 
144 	private ServletContext sc;
145 
146 	public void destroy() {
147 		SingletonS2ContainerFactory.destroy();
148 	}
149 
150 	public URLConnection getResourceConnection(String name)
151 			throws ResourceException {
152 		try {
153 			URL url = sc.getResource("/" + name);
154 			if (url == null) {
155 				url = sc.getResource("/WEB-INF/groovy/" + name);
156 				if (url == null) {
157 					throw new ResourceException("Resource " + name
158 							+ " not found");
159 				}
160 			}
161 			return url.openConnection();
162 		} catch (IOException ioe) {
163 			throw new ResourceException("Problem reading resource " + name);
164 		}
165 	}
166 
167 	public ServletContext getServletContext() {
168 		return sc;
169 	}
170 
171 	public void init() {
172 		initGroovy();
173 		initS2();
174 	}
175 
176 	/***
177 	 * @param config
178 	 */
179 	protected void initGroovy() {
180 		final ServletConfig config = getServletConfig();
181 
182 		// Use reflection, some containers don't load classes properly
183 		MetaClass.setUseReflection(true);
184 
185 		// Get the servlet context
186 		sc = config.getServletContext();
187 		sc.log("Groovy servlet initialized");
188 
189 		// Ensure that we use the correct classloader so that we can find
190 		// classes in an application server.
191 		parent = Thread.currentThread().getContextClassLoader();
192 		if (parent == null)
193 			parent = GroovyServlet.class.getClassLoader();
194 
195 		// Set up the scripting engine
196 		gse = new GroovyScriptEngine(this);
197 	}
198 
199 	protected void initS2() {
200 		final ServletConfig config = getServletConfig();
201 		String configPath = null;
202 
203 		if (config != null) {
204 			configPath = config.getInitParameter(CONFIG_PATH_KEY);
205 		}
206 		if (!StringUtil.isEmpty(configPath)) {
207 			SingletonS2ContainerFactory.setConfigPath(configPath);
208 		}
209 		SingletonS2ContainerFactory.setServletContext(getServletContext());
210 		SingletonS2ContainerFactory.init();
211 	}
212 
213 	public void service(ServletRequest request, ServletResponse response)
214 			throws ServletException, IOException {
215 
216 		// Convert the generic servlet request and response to their Http
217 		// versions
218 		final HttpServletRequest httpRequest = (HttpServletRequest) request;
219 		final HttpServletResponse httpResponse = (HttpServletResponse) response;
220 
221 		// S2Container
222 		final S2Container container = getContainer();
223 		container.setRequest(httpRequest);
224 
225 		// Get the name of the Groovy script
226 		int contextLength = httpRequest.getContextPath().length();
227 		final String scriptFilename = httpRequest.getRequestURI().substring(
228 				contextLength).substring(1);
229 
230 		// Set up the script context
231 		final Binding binding = new Binding();
232 		binding.setVariable("request", httpRequest);
233 		binding.setVariable("response", httpResponse);
234 		binding.setVariable("application", sc);
235 		binding.setVariable("session", httpRequest.getSession(true));
236 		binding.setVariable("out", httpResponse.getWriter());
237 
238 		binding.setVariable("container", container);
239 
240 		// Form parameters. If there are multiple its passed as a list.
241 		for (Enumeration paramEnum = request.getParameterNames(); paramEnum
242 				.hasMoreElements();) {
243 			String key = (String) paramEnum.nextElement();
244 			if (binding.getVariable(key) == null) {
245 				String[] values = request.getParameterValues(key);
246 				if (values.length == 1) {
247 					binding.setVariable(key, values[0]);
248 				} else {
249 					binding.setVariable(key, values);
250 				}
251 			}
252 		}
253 
254 		// Set it to HTML by default
255 		response.setContentType("text/html");
256 
257 		// Run the script
258 		try {
259 			Closure closure = new Closure(gse) {
260 				public Object call() {
261 					try {
262 						return ((GroovyScriptEngine) getDelegate()).run(
263 								scriptFilename, binding);
264 					} catch (ResourceException e) {
265 						throw new RuntimeException(e);
266 					} catch (ScriptException e) {
267 						throw new RuntimeException(e);
268 					}
269 				}
270 			};
271 
272 			List categories = new ArrayList();
273 			categories.add(ServletCategory.class);
274 			categories.add(S2GroovyServletCategory.class);
275 			GroovyCategorySupport.use(categories, closure);
276 
277 		} catch (RuntimeException re) {
278 			Throwable e = re.getCause();
279 			if (e instanceof ResourceException) {
280 				httpResponse.sendError(404);
281 			} else {
282 				if (e != null) {
283 					sc.log("An error occurred processing the request", e);
284 				} else {
285 					sc.log("An error occurred processing the request", re);
286 				}
287 				httpResponse.sendError(500);
288 			}
289 		}
290 	}
291 }