001: /*
002: * PnutsServlet.java
003: *
004: * Copyright (c) 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.servlet;
010:
011: import javax.servlet.http.HttpServlet;
012: import javax.servlet.http.HttpServletRequest;
013: import javax.servlet.http.HttpServletResponse;
014: import javax.servlet.ServletException;
015: import javax.servlet.ServletConfig;
016: import javax.servlet.ServletContext;
017: import java.io.File;
018: import java.io.FileInputStream;
019: import java.io.FileNotFoundException;
020: import java.io.Reader;
021: import java.io.InputStreamReader;
022: import java.io.IOException;
023: import java.util.Hashtable;
024: import java.util.StringTokenizer;
025: import java.util.TimeZone;
026: import java.util.Locale;
027: import java.net.URL;
028: import java.net.URLClassLoader;
029: import java.net.MalformedURLException;
030: import java.util.HashSet;
031: import java.util.Set;
032: import pnuts.lang.Pnuts;
033: import pnuts.lang.PnutsImpl;
034: import pnuts.lang.Context;
035: import pnuts.lang.Runtime;
036: import pnuts.lang.Package;
037: import pnuts.compiler.Compiler;
038: import pnuts.compiler.CompilerPnutsImpl;
039:
040: /**
041: * Servlet for Pnuts scripting
042: *
043: * <pre>
044: * Initparams:
045: * 'module' the module to be use()'d
046: * 'compile' scripts are compiled if true
047: * </pre>
048: */
049: public class PnutsServlet extends HttpServlet {
050:
051: public final static String SYMBOL_THIS = "this".intern();
052: public final static String SYMBOL_REQUEST = "request".intern();
053: public final static String SYMBOL_RESPONSE = "response".intern();
054: public final static String SERVLET_REQUEST = "request".intern();
055: public final static String SERVLET_RESPONSE = "response".intern();
056: public final static String SERVLET_BUFFER = "pnuts.servlet.buffer"
057: .intern();
058: public final static String EXECUTE_LATEST_SCRIPT = "pnuts.servlet.latest.script"
059: .intern();
060: public final static String SERVLET_WRITER = "pnuts.servlet.writer"
061: .intern();
062: public final static String SERVLET_MULTIPART_PARAM = "pnuts.servlet.multipart.param"
063: .intern();
064: public final static String SERVLET_PARAM = "pnuts.servlet.parameters"
065: .intern();
066: public final static String SERVLET_COOKIE = "pnuts.servlet.cookies"
067: .intern();
068: public final static String SERVLET_FILE = "pnuts.servlet.file"
069: .intern();
070: public final static String SERVLET_BASEDIR = "pnuts.servlet.dir"
071: .intern();
072: public final static String REQUEST_SCOPE = "pnuts.servlet.request.scope"
073: .intern();
074: public final static String SERVLET_COMPILER = "pnuts.servlet.compiler"
075: .intern();
076: private final static String LOCALE_KEY = "pnuts$lib$locale"
077: .intern();
078: private final static String TIMEZONE_KEY = "pnuts$lib$timezone"
079: .intern();
080:
081: boolean debug;
082: boolean buffering;
083: boolean checkUpdate = true;
084: boolean compile = true;
085: Compiler compiler;
086: File scriptFile;
087: String encoding;
088: boolean isolation;
089: URL baseURL;
090: String errorPage;
091:
092: private PnutsServletContext pnutsServletContext;
093: private Hashtable contexts = new Hashtable();
094: private Context context = new Context();
095: private Package pkg;
096:
097: public PnutsServlet() {
098: }
099:
100: public void init() throws ServletException {
101: ServletConfig conf = getServletConfig();
102: debug = Boolean.valueOf(conf.getInitParameter("debug"))
103: .booleanValue();
104: String module = conf.getInitParameter("module");
105: String script = conf.getInitParameter("script");
106: ServletContext servletContext = getServletContext();
107: if (script != null) {
108: String path = servletContext.getRealPath(script);
109: File f = new File(path);
110: if (f.exists()) {
111: this .scriptFile = f;
112: }
113: }
114: String _compile = conf.getInitParameter("compile");
115: boolean compile = this .compile = Boolean.valueOf(_compile)
116: .booleanValue();
117: if (compile) {
118: this .compiler = new Compiler(null, false, true);
119: context.set(SERVLET_COMPILER, compiler);
120: }
121: String locale = conf.getInitParameter("locale");
122: String timezone = conf.getInitParameter("timezone");
123:
124: PnutsImpl pnutsImpl;
125: if (compile) {
126: pnutsImpl = new CompilerPnutsImpl(true, true);
127: } else {
128: pnutsImpl = PnutsImpl.getDefault();
129: }
130: context.setImplementation(pnutsImpl);
131: String _buffering = conf.getInitParameter("buffering");
132: if (_buffering != null) {
133: buffering = Boolean.valueOf(_buffering).booleanValue();
134: } else {
135: buffering = true;
136: }
137: String check = conf.getInitParameter("execute-latest-script");
138: if (check != null) {
139: checkUpdate = Boolean.valueOf(check).booleanValue();
140: }
141: if (checkUpdate) {
142: context.set(EXECUTE_LATEST_SCRIPT, Boolean.TRUE);
143: }
144: encoding = conf.getInitParameter("encoding"); // script encoding
145: if (encoding != null) {
146: context.setScriptEncoding(encoding);
147: }
148: String _isolation = conf.getInitParameter("isolation");
149: if (_isolation != null) {
150: isolation = Boolean.valueOf(_isolation).booleanValue();
151: } else {
152: isolation = true;
153: }
154:
155: this .errorPage = conf.getInitParameter("error-page");
156:
157: if (locale != null) {
158: StringTokenizer st = new StringTokenizer(locale, "_");
159: int n = st.countTokens();
160: Locale loc;
161: switch (n) {
162: case 0:
163: loc = new Locale("");
164: break;
165: case 1:
166: loc = new Locale(st.nextToken(), "", "");
167: break;
168: case 2:
169: loc = new Locale(st.nextToken(), st.nextToken(), "");
170: break;
171: default:
172: loc = new Locale(st.nextToken(), st.nextToken(), st
173: .nextToken());
174: break;
175: }
176: context.set(LOCALE_KEY, loc);
177: }
178:
179: if (timezone != null) {
180: context.set(TIMEZONE_KEY, TimeZone.getTimeZone(timezone));
181: }
182:
183: this .pkg = new Package("root", null);
184: context.setCurrentPackage(pkg);
185:
186: if (module != null) {
187: StringTokenizer st = new StringTokenizer(module, ",", false);
188: while (st.hasMoreTokens()) {
189: String mod = st.nextToken().trim();
190: context.usePackage(mod);
191: }
192: }
193: context.set(SYMBOL_THIS, this );
194: context.set(SERVLET_BASEDIR, new File(servletContext
195: .getRealPath("/")));
196:
197: String initialScript = conf.getInitParameter("initialScript");
198: try {
199: this .baseURL = new File(servletContext.getRealPath("/"))
200: .toURL();
201: if (initialScript != null) {
202: String path = servletContext.getRealPath(initialScript);
203: File f = new File(path);
204: if (f.exists()) {
205: File dir = f.getParentFile();
206: context.set(SERVLET_FILE, f);
207: pkg.set(SYMBOL_THIS, this , context);
208: ClassLoader ccl = getContextClassLoader();
209: URL[] urls = new URL[] { dir.toURL(), baseURL };
210: ClassLoader loader = new URLClassLoader(urls, ccl);
211: ClassLoader pcl = Pnuts.createClassLoader(context,
212: loader);
213: context.setClassLoader(pcl);
214: Pnuts.require(initialScript, context);
215: } else {
216: System.err.println(path + " is not found");
217: }
218: }
219: if (this .scriptFile != null) {
220: this .pnutsServletContext = createPnutsServletContext(scriptFile);
221: }
222: } catch (FileNotFoundException e1) {
223: e1.printStackTrace();
224: } catch (MalformedURLException e2) {
225: e2.printStackTrace();
226: }
227: }
228:
229: protected void doGet(HttpServletRequest request,
230: HttpServletResponse response) throws ServletException,
231: IOException {
232: do_service(request, response);
233: }
234:
235: protected void doPost(HttpServletRequest request,
236: HttpServletResponse response) throws ServletException,
237: IOException {
238: do_service(request, response);
239: }
240:
241: protected void doPut(HttpServletRequest request,
242: HttpServletResponse response) throws ServletException,
243: IOException {
244: do_service(request, response);
245: }
246:
247: protected void doDelete(HttpServletRequest request,
248: HttpServletResponse response) throws ServletException,
249: IOException {
250: do_service(request, response);
251: }
252:
253: /*
254: * Get the ClassLoader by which ServletContext finds resources and classes.
255: * There is no standard way to get this. We use Thread.getContextClassLoader()
256: * here, since Tomcat 3.3a, 4.0 and Resin2.0.5 use it in J2SE environment.
257: */
258: protected ClassLoader getContextClassLoader() {
259: return Thread.currentThread().getContextClassLoader();
260: }
261:
262: /**
263: * The following context-variables are defined
264: *
265: * <dl>
266: * <dt>pnuts.servlet.request
267: * <dt>pnuts.servlet.response
268: * <dt>pnuts.servlet.file
269: * </dl>
270: */
271: void do_service(HttpServletRequest request,
272: HttpServletResponse response) throws ServletException,
273: IOException {
274: Context c = null;
275: Package p = null;
276: Pnuts script = null;
277: long time = 0L;
278: try {
279: File file = scriptFile;
280: if (file == null) {
281: String path = "";
282: String servletPath = request.getServletPath();
283: if (servletPath != null) {
284: path = servletPath;
285: }
286: if (path == null) {
287: path = request.getPathInfo();
288: }
289: String real_path = getServletContext()
290: .getRealPath(path);
291:
292: if (real_path != null) {
293: file = new File(real_path);
294: }
295: }
296: if (file == null || !file.exists()) {
297: if (debug) {
298: throw new FileNotFoundException();
299: } else {
300: response
301: .sendError(HttpServletResponse.SC_NOT_FOUND);
302: return;
303: }
304: }
305:
306: PnutsServletContext ctx = this .pnutsServletContext;
307: if (ctx == null) {
308: synchronized (contexts) {
309: ctx = (PnutsServletContext) contexts.get(file);
310: if (ctx == null) {
311: ctx = createPnutsServletContext(file);
312: c = ctx.context;
313: p = ctx.basePackage;
314: contexts.put(file, ctx);
315: if (debug) {
316: c.setVerbose(true);
317: }
318: } else {
319: c = ctx.context;
320: p = ctx.basePackage;
321: script = ctx.script;
322: time = ctx.time;
323: }
324: }
325: } else {
326: c = ctx.context;
327: p = ctx.basePackage;
328: script = ctx.script;
329: time = ctx.time;
330: }
331: c = (Context) c.clone();
332: Thread.currentThread().setContextClassLoader(
333: c.getClassLoader());
334:
335: boolean buffering = this .buffering;
336: ResponseWriter rw = new ResponseWriter(response, c,
337: buffering);
338: c.setWriter(rw);
339: p.set(SYMBOL_THIS, this , c);
340:
341: Package pkg = new Package(null, p);
342: pkg.set(SYMBOL_RESPONSE, response, c);
343: pkg.set(SYMBOL_REQUEST, request, c);
344: c.set(SERVLET_RESPONSE, response);
345: c.set(SERVLET_REQUEST, request);
346: c.set(REQUEST_SCOPE, pkg);
347:
348: c.setCurrentPackage(pkg);
349:
350: if (script == null) {
351: script = readScript(file, encoding, ctx);
352: } else if (checkUpdate) {
353: if (ctx.needToUpdate()) {
354: script = readScript(file, encoding, ctx);
355: }
356: }
357: script.run(c);
358:
359: if (buffering) {
360: rw.flushBuffer();
361: }
362:
363: } catch (Throwable e) {
364: if (debug) {
365: e.printStackTrace();
366: } else {
367: System.err.println(e);
368: }
369: if (errorPage != null) {
370: response.sendRedirect(errorPage);
371: } else {
372: response.sendError(
373: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
374: "");
375: }
376: }
377: }
378:
379: PnutsServletContext createPnutsServletContext(File file)
380: throws MalformedURLException {
381: ClassLoader ccl = getContextClassLoader();
382: File dir = file.getParentFile();
383: URL[] urls = new URL[] { dir.toURL(), baseURL };
384: ClassLoader loader = new URLClassLoader(urls, ccl);
385: ClassLoader pcl = Pnuts.createClassLoader(context, loader);
386: Context c;
387: if (isolation) {
388: c = new Context(context);
389: } else {
390: c = (Context) context.clone();
391: }
392: c.setClassLoader(pcl);
393: c.set(SERVLET_FILE, file);
394: c.set(SYMBOL_THIS, this );
395: Package p;
396: if (isolation) {
397: p = (Package) pkg.clone();
398: } else {
399: p = pkg;
400: }
401: PnutsServletContext ctx = new PnutsServletContext(c, p);
402: if (debug) {
403: c.setVerbose(true);
404: }
405: return ctx;
406: }
407:
408: protected Pnuts parseFile(File file, String encoding,
409: PnutsServletContext psc) throws IOException {
410: URL scriptURL = file.toURL();
411: Reader reader = null;
412: if (encoding != null) {
413: reader = new InputStreamReader(new FileInputStream(file),
414: encoding);
415: } else {
416: reader = Runtime.getScriptReader(new FileInputStream(file),
417: context);
418: }
419: try {
420: Pnuts result = Pnuts.parse(reader, scriptURL, psc.context);
421: Set scriptURLs = new HashSet();
422: scriptURLs.add(scriptURL);
423: psc.scriptURLs = scriptURLs;
424: return result;
425: } finally {
426: if (reader != null) {
427: reader.close();
428: }
429: }
430: }
431:
432: protected Pnuts readScript(File file, String encoding,
433: PnutsServletContext psc) throws IOException {
434: if (debug) {
435: System.out.println("readScript " + file);
436: }
437:
438: Pnuts expr = parseFile(file, encoding, psc);
439: if (compile) {
440: try {
441: expr = compiler.compile(expr, psc.context);
442: } catch (ClassFormatError e) {
443: if (debug) {
444: System.out.println("compile failed (interpreting)");
445: }
446: }
447: }
448: psc.script = expr;
449: psc.time = System.currentTimeMillis();
450: return expr;
451: }
452:
453: public String getServletInfo() {
454: return "Pnuts Servlet for servlet scripting. See http://pnuts.org/ for more information";
455: }
456: }
|