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  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 should be registered to *.groovy in the web.xml.
63   * 
64   * <servlet><servlet-name>Groovy</servlet-name><servlet-class>
65   * groovy.servlet.GroovyServlet</servlet-class></servlet>
66   * 
67   * <servlet-mapping><servlet-name>Groovy</servlet-name><url-pattern>
68   * *.groovy</url-pattern></servlet-mapping>
69   * 
70   * @author Sam Pullara
71   */
72  public class GroovyServlet extends HttpServlet implements ResourceConnector {
73  
74  	private ServletContext sc;
75  
76  	private static Map servletCache = Collections.synchronizedMap(new HashMap());
77  	private static ClassLoader parent;
78  	private static GroovyScriptEngine gse;
79  	
80  	public ServletContext getServletContext() {
81  		return sc;
82  	}
83  	
84  	private static class ServletCacheEntry {
85  		private Class servletScriptClass;
86  		private long lastModified;
87  		private Map dependencies = new HashMap();
88  	}
89  
90  	public void init(ServletConfig config) {
91  	    // Use reflection, some containers don't load classes properly
92  	    MetaClass.setUseReflection(true);
93  	    
94  		// Get the servlet context
95  		sc = config.getServletContext();
96  		sc.log("Groovy servlet initialized");
97  
98  		// Ensure that we use the correct classloader so that we can find
99  		// classes in an application server.
100 		parent = Thread.currentThread().getContextClassLoader();
101 		if (parent == null)
102 			parent = GroovyServlet.class.getClassLoader();
103 		
104 		// Set up the scripting engine
105 		gse = new GroovyScriptEngine(this);
106 	}
107 
108 	public URLConnection getResourceConnection(String name) throws ResourceException {
109 		try {
110 			URL url = sc.getResource("/" + name);
111 			if (url == null) {
112 				url = sc.getResource("/WEB-INF/groovy/" + name);
113 				if (url == null) {
114 					throw new ResourceException("Resource " + name + " not found");
115 				}
116 			}
117 			return url.openConnection();
118 		} catch (IOException ioe) {
119 			throw new ResourceException("Problem reading resource " + name);
120 		}
121 	}	
122 	
123 	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
124 
125 		// Convert the generic servlet request and response to their Http
126 		// versions
127 		final HttpServletRequest httpRequest = (HttpServletRequest) request;
128 		final HttpServletResponse httpResponse = (HttpServletResponse) response;
129 
130 		// Get the name of the Groovy script
131 		int contextLength = httpRequest.getContextPath().length();
132 		final String scriptFilename = httpRequest.getRequestURI().substring(contextLength).substring(1);
133 
134 		// Set up the script context
135 		final Binding binding = new Binding();
136 		binding.setVariable("request", httpRequest);
137 		binding.setVariable("response", httpResponse);
138 		binding.setVariable("application", sc);
139 		binding.setVariable("session", httpRequest.getSession(true));
140 		binding.setVariable("out", httpResponse.getWriter());
141 
142 		// Form parameters. If there are multiple its passed as a list.
143 		for (Enumeration paramEnum = request.getParameterNames(); paramEnum.hasMoreElements();) {
144 			String key = (String) paramEnum.nextElement();
145 			if (binding.getVariable(key) == null) {
146 				String[] values = request.getParameterValues(key);
147 				if (values.length == 1) {
148 					binding.setVariable(key, values[0]);
149 				} else {
150 					binding.setVariable(key, values);
151 				}
152 			}
153 		}
154 
155 		// Set it to HTML by default
156 		response.setContentType("text/html");
157 		
158 		// Run the script
159 		try {
160             Closure closure = new Closure(gse) {
161                 public Object call() {
162                     try {
163                         return ((GroovyScriptEngine)getDelegate()).run(scriptFilename, binding);
164                     } catch (ResourceException e) {
165                         throw new RuntimeException(e);
166                     } catch (ScriptException e) {
167                         throw new RuntimeException(e);
168                     }
169                 }
170             };
171             GroovyCategorySupport.use(ServletCategory.class, closure);
172         } catch (RuntimeException re) {
173             Throwable e = re.getCause();
174             if (e instanceof ResourceException) {
175                 httpResponse.sendError(404);
176             } else {
177                 if (e != null) {
178                     sc.log("An error occurred processing the request", e);
179                 } else {
180                     sc.log("An error occurred processing the request", re);
181                 }
182     			httpResponse.sendError(500);
183             }
184         }
185 	}
186 
187 }