KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > servlets > responders > ResponderCompile


1 /******************************************************************************
2  * ResponderCompile.java
3  * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.servlets.responders;
11
12 import java.io.*;
13 import java.net.URL JavaDoc;
14 import java.util.Hashtable JavaDoc;
15 import java.util.Properties JavaDoc;
16 import javax.servlet.ServletConfig JavaDoc;
17 import javax.servlet.ServletOutputStream JavaDoc;
18 import javax.servlet.ServletException JavaDoc;
19 import javax.servlet.http.HttpUtils JavaDoc;
20 import javax.servlet.http.HttpSession JavaDoc;
21 import javax.servlet.http.HttpServletRequest JavaDoc;
22 import javax.servlet.http.HttpServletResponse JavaDoc;
23 import org.openlaszlo.cm.CompilationManager;
24 import org.openlaszlo.compiler.Canvas;
25 import org.openlaszlo.compiler.CompilationError;
26 import org.openlaszlo.compiler.CompilationEnvironment;
27 import org.openlaszlo.sc.ScriptCompiler;
28 import org.openlaszlo.utils.ContentEncoding;
29 import org.openlaszlo.utils.FileUtils;
30 import org.openlaszlo.utils.LZHttpUtils;
31 import org.openlaszlo.utils.LZGetMethod;
32 import org.openlaszlo.utils.StringUtils;
33 import org.openlaszlo.server.LPS;
34 import org.openlaszlo.servlets.LZBindingListener;
35 import org.apache.commons.httpclient.HttpClient;
36 import org.apache.commons.httpclient.methods.GetMethod;
37
38 import org.apache.log4j.Logger;
39
40
41
42 public abstract class ResponderCompile extends Responder
43 {
44     protected static CompilationManager mCompMgr = null;
45     protected static ScriptCompiler mScriptCache = null;
46
47     private static boolean mIsInitialized = false;
48     private static boolean mAllowRequestSOURCE = true;
49     private static boolean mAllowRequestKRANK = true;
50     private static boolean mAllowRecompile = true;
51     private static boolean mCheckModifiedSince = true;
52     private static String JavaDoc mAdminPassword = null;
53     private static Logger mLogger = Logger.getLogger(ResponderCompile.class);
54
55     /** @param filename path of the actual file to be compiled -- happens when
56      * we're compiling JSPs. */

57     abstract protected void respondImpl(String JavaDoc fileName, HttpServletRequest JavaDoc req,
58                                         HttpServletResponse JavaDoc res)
59         throws IOException;
60
61
62     synchronized public void init(String JavaDoc reqName, ServletConfig JavaDoc config, Properties JavaDoc prop)
63         throws ServletException JavaDoc, IOException
64     {
65         super.init(reqName, config, prop);
66
67         // All compilation classes share cache directory and compilation manager.
68
if (! mIsInitialized) {
69
70             mAllowRequestSOURCE =
71                 prop.getProperty("allowRequestSOURCE", "true").intern() == "true";
72             mAllowRequestKRANK =
73                 prop.getProperty("allowRequestKRANK", "true").intern() == "true";
74             mCheckModifiedSince =
75                 prop.getProperty("checkModifiedSince", "true").intern() == "true";
76             mAllowRecompile =
77                 prop.getProperty("allowRecompile", "true").intern() == "true";
78
79             mAdminPassword = prop.getProperty("adminPassword", null);
80
81             // Initialize the Compilation Cache
82
String JavaDoc cacheDir = config.getInitParameter("lps.cache.directory");
83             if (cacheDir == null) {
84                 cacheDir = prop.getProperty("cache.directory");
85             }
86             if (cacheDir == null) {
87                 cacheDir = LPS.getWorkDirectory() + File.separator + "cache";
88             }
89
90             File cache = checkDirectory(cacheDir);
91             mLogger.info("application cache is at " + cacheDir);
92
93             if (mCompMgr == null) {
94                 mCompMgr = new CompilationManager(null, cache, prop);
95             }
96
97             if (mScriptCache == null) {
98                 String JavaDoc scacheDir = config.getInitParameter("lps.scache.directory");
99                 if (scacheDir == null) {
100                     scacheDir = prop.getProperty("scache.directory");
101                 }
102                 if (scacheDir == null) {
103                     scacheDir = LPS.getWorkDirectory() + File.separator + "scache";
104                 }
105                 File scache = checkDirectory(scacheDir);
106                 mScriptCache = ScriptCompiler.initScriptCompilerCache(scache, prop);
107             }
108
109             String JavaDoc cmOption = prop.getProperty("compMgrDependencyOption");
110             if (cmOption!=null) {
111                 mLogger.debug("Setting cm option to \"" + cmOption + "\"");
112                 mCompMgr.setProperty("recompile", cmOption);
113             }
114         }
115     }
116
117
118     /**
119      * This method should be called by subclasses in respondImpl(req, res);
120      *
121      */

122     protected final void respondImpl(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res)
123         throws IOException
124     {
125         String JavaDoc fileName = LZHttpUtils.getRealPath(mContext, req);
126
127         String JavaDoc lzt = req.getParameter("lzt");
128         String JavaDoc krank = req.getParameter("krank");
129         boolean kranking = ("true".equals(krank) && "swf".equals(lzt));
130
131         if (fileName.endsWith(".lzo") && kranking) {
132             fileName = fileName.substring(0, fileName.length()-1) + 'x';
133         }
134
135         // FIXME: [2003-01-14 bloch] this needs to be rethought out for real!!
136
// Is this the right logic for deciding when to do preprocessing?
137
File file = new File(fileName);
138         if ( ! file.canRead() ) {
139
140             File base = new File(FileUtils.getBase(fileName));
141
142             if (base.canRead() && base.isFile()) {
143                 String JavaDoc tempFileName = doPreProcessing(req, fileName);
144                 if (tempFileName != null) {
145                     fileName = tempFileName;
146                 }
147             }
148         }
149
150         if (! new File(fileName).exists()) {
151             boolean isOpt = fileName.endsWith(".lzo");
152             boolean lzogz = new File(fileName+".gz").exists();
153             if (!(isOpt && lzogz)) {
154                 boolean hasFb = req.getParameter("fb") != null;
155                 if (!(isOpt && hasFb)) {
156                     res.sendError(HttpServletResponse.SC_NOT_FOUND, req.getRequestURI() + " not found");
157                     mLogger.info(req.getRequestURI() + " not found");
158                     return;
159                 } else {
160                     fileName = fileName.substring(0, fileName.length()-1) + 'x';
161                 }
162             }
163         }
164
165         try {
166             /* If it's a krank instrumented file then we always
167              * fall through and call the code to deliver the
168              * .swf to the client, regardless of the object
169              * file modification time, because that code path
170              * is where we launch the serialization
171              * port-listener thread.
172              */

173             if (mCheckModifiedSince && !kranking) {
174                 long lastModified = getLastModified(fileName, req);
175                 // Round to the nearest second.
176
lastModified = ((lastModified + 500L)/1000L) * 1000L;
177                 if (notModified(lastModified, req, res)) {
178                     return;
179                 }
180             }
181
182             respondImpl(fileName, req, res);
183
184             // Check that global config knows about app security options
185
String JavaDoc path = req.getServletPath();
186             if (LPS.configuration.getApplicationOptions(path) == null) {
187                 // Filename could still be lzo here.
188
if (fileName.endsWith(".lzo")) {
189                     fileName = fileName.substring(0, fileName.length()-1) + 'x';
190                 }
191                 Canvas canvas = getCanvas(fileName, req);
192                 LPS.configuration.setApplicationOptions(path, canvas.getSecurityOptions());
193             }
194
195         } catch (CompilationError e) {
196             handleCompilationError(e, req, res);
197         }
198     }
199
200     protected void handleCompilationError(CompilationError e,
201                                           HttpServletRequest JavaDoc req,
202                                           HttpServletResponse JavaDoc res)
203         throws IOException
204     {
205         throw e;
206     }
207
208     /**
209      * @return File name for temporary LZX file that is the
210      * result of this http pre-processing; null for a bad request
211      * @param req request
212      * @param fileName file name associated with request
213      */

214     private String JavaDoc doPreProcessing(HttpServletRequest JavaDoc req, String JavaDoc fileName)
215         throws IOException
216     {
217         // Do an http request for this and see what we get back.
218
//
219
StringBuffer JavaDoc s = HttpUtils.getRequestURL(req);
220         int len = s.length();
221         // Remove the .lzx from the end of the URL
222
if (len <= 4) {
223             return null;
224         }
225         s.delete(len-4, len);
226
227         // FIXME [2002-12-15 bloch] does any/all of this need to be synchronized on session?
228

229         // First get the temporary file name for this session
230
HttpSession JavaDoc session = req.getSession();
231         String JavaDoc sid = session.getId();
232         String JavaDoc tempFileName = getTempFileName(fileName, sid);
233         File tempFile = new File(tempFileName);
234
235         tempFile.deleteOnExit();
236
237         // Now pre-process the request and copy the data to
238
// the temporary file that is unique to this session
239

240         // FIXME: query string processing
241

242         String JavaDoc surl = s.toString();
243
244         URL JavaDoc url = new URL JavaDoc(surl);
245         mLogger.debug("Preprocessing request at " + surl);
246         GetMethod getRequest = new LZGetMethod();
247         getRequest.setPath(url.getPath());
248         //getRequest.setQueryString(url.getQuery());
249
getRequest.setQueryString(req.getQueryString());
250
251         // Copy headers to request
252
LZHttpUtils.proxyRequestHeaders(req, getRequest);
253         // Mention the last modified time, if the file exists
254
if (tempFile.exists()) {
255             long lastModified = tempFile.lastModified();
256             getRequest.addRequestHeader("If-Modified-Since",
257                         LZHttpUtils.getDateString(lastModified));
258         } else {
259             // Otherwise, create a listener that will clean up the tempfile
260
// Note: web server administrators must make sure that their servers are killed
261
// gracefully or temporary files will not be handled by the LZBindingListener.
262
// Add a binding listener for this session that
263
// will remove our temporary files
264
LZBindingListener listener = (LZBindingListener)session.getAttribute("tmpl");
265             if (listener == null) {
266                 listener = new LZBindingListener(tempFileName);
267                 session.setAttribute("tmpl", listener);
268             } else {
269                 listener.addTempFile(tempFileName);
270             }
271         }
272
273         HttpClient htc = new HttpClient();
274         htc.startSession(url);
275
276         int rc = htc.executeMethod(getRequest);
277         mLogger.debug("Response Status: " + rc);
278         if (rc >= 400) {
279             // respondWithError(req, res, "HTTP Status code: " + rc + " for url " + surl, rc);
280
return null;
281         } if (rc != HttpServletResponse.SC_NOT_MODIFIED) {
282             FileOutputStream output = new FileOutputStream(tempFile);
283             try {
284                 // FIXME:[2002-12-17 bloch] verify that the response body is XML
285
FileUtils.sendToStream(getRequest.getResponseBodyAsStream(), output);
286                 // TODO: [2002-12-15 bloch] What to do with response headers?
287
} catch (FileUtils.StreamWritingException e) {
288                 mLogger.warn("StreamWritingException while sending error: " + e.getMessage());
289             } finally {
290                 FileUtils.close(output);
291             }
292         }
293
294         return tempFileName;
295     }
296
297     /**
298      * @return Name of temporary cached version of this file
299      * for this session.
300      */

301     private String JavaDoc getTempFileName(String JavaDoc fileName, String JavaDoc sid)
302     {
303         String JavaDoc webappPath = LZHttpUtils.getRealPath(mContext, "/");
304         File source = mCompMgr.getCacheSourcePath(fileName, webappPath);
305         File cacheDir = source.getParentFile();
306         if (cacheDir != null) {
307             cacheDir.mkdirs();
308         }
309         String JavaDoc sourcePath = source.getAbsolutePath();
310         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(sourcePath);
311         int index = sourcePath.lastIndexOf(File.separator);
312         buf.insert(index + 1, "lzf-" + sid + "-");
313         return buf.toString();
314     }
315
316     /**
317      * Process if-modified-since req header and stick last-modified
318      * response header there.
319      */

320     private boolean notModified(long lastModified, HttpServletRequest JavaDoc req,
321                                 HttpServletResponse JavaDoc res)
322         throws IOException
323     {
324         if (lastModified != 0) {
325
326             String JavaDoc lms = LZHttpUtils.getDateString(lastModified);
327
328             mLogger.debug("Last-Modified: " + lms);
329
330             // Check last-modified and if-modified-since dates
331
String JavaDoc ims = req.getHeader(LZHttpUtils.IF_MODIFIED_SINCE);
332             long ifModifiedSince = LZHttpUtils.getDate(ims);
333
334             if (ifModifiedSince != -1) {
335
336                 mLogger.debug("If-Modified-Since: " + ims);
337
338                 mLogger.debug("modsince " + ifModifiedSince
339                             + " lastmod " + lastModified);
340                 if (lastModified <= ifModifiedSince) {
341                     res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
342                     mLogger.info("Responding with NOT_MODIFIED");
343                     return true;
344                 }
345             }
346
347             res.setHeader(LZHttpUtils.LAST_MODIFIED, lms);
348         }
349
350         return false;
351     }
352
353     /**
354      * Return the last modified time for this source file (and its dependencies)
355      *
356      * @param fileName name of file to compile
357      * @return the canvas
358      */

359     protected long getLastModified(String JavaDoc fileName, HttpServletRequest JavaDoc req)
360         throws CompilationError, IOException
361     {
362         // If it's a .lzo file (and lzt=swf), just return the file last modified
363
// date from the filesystem, or zero if the file does not
364
// exist, since the compilation manager cannot generate a
365
// kranked (.lzo) file file automatically under any
366
// circumstances.
367
if (fileName.endsWith(".lzo") && "swf".equals(req.getParameter("lzt"))) {
368             File kranked = new File(fileName);
369             if (kranked.exists()) {
370                 return kranked.lastModified();
371             } else {
372                 return 0;
373             }
374         }
375
376         // Pass the .lzx filename into compilation manager, so it can find/update the canvas
377
// for this app.
378
if (fileName.endsWith(".lzo")) {
379             fileName = fileName.substring(0, fileName.length() - 1) + "x";
380         }
381
382         Properties JavaDoc props = initCMgrProperties(req);
383         return mCompMgr.getLastModified(fileName, props);
384     }
385
386     /**
387      * Sets up various parameters for the CompilationManager, using
388      * data from the HttpServletRequest
389      *
390      * @param req
391      *
392      *
393      * Looks for these properties:
394      * <br>
395      * <ul>
396      * <li> "debug"
397      * <li> "logdebug"
398      * <li> "profile"
399      * <li> "krank"
400      * <li> "sourcelocators"
401      * <li> "remotedebug"
402      * <li> "lzr" (swf version := swf5 | swf6)
403      * <li> "lzproxied" true|false
404      * <ul>
405      * also grabs the request URL.
406      */

407     static protected Properties JavaDoc initCMgrProperties(HttpServletRequest JavaDoc req) {
408
409         Properties JavaDoc props = new Properties JavaDoc();
410
411         // Look for "runtime=..." flag
412
String JavaDoc swfversion = req.getParameter("lzr");
413         if (swfversion == null) {
414             swfversion = LPS.getProperty("compiler.runtime.default", "swf6");
415         }
416         props.setProperty(CompilationEnvironment.SWFVERSION_PROPERTY, swfversion);
417
418         // This is an integer which specifies the remote debug TCP port, if any
419
props.setProperty(CompilationEnvironment.REMOTEDEBUG_PROPERTY, "");
420         String JavaDoc remotedebug = req.getParameter(CompilationEnvironment.REMOTEDEBUG_PROPERTY);
421         if (remotedebug != null) {
422             props.setProperty(CompilationEnvironment.REMOTEDEBUG_PROPERTY, remotedebug);
423         }
424
425         // TODO: [2003-04-11 pkang] the right way to do this is to have a
426
// separate property to see if this allows debug.
427
if (mAllowRequestSOURCE) {
428             // Look for "logdebug=true" flag
429
props.setProperty("logdebug", "false");
430             String JavaDoc logdebug = req.getParameter("logdebug");
431             if (logdebug != null) {
432                 props.setProperty("logdebug", logdebug);
433             }
434
435             // Look for "debug=true" flag
436
props.setProperty("debug", "false");
437             String JavaDoc debug = req.getParameter("debug");
438             if (debug != null) {
439                 props.setProperty("debug", debug);
440             }
441
442             // Look for "sourcelocators=true" flag
443
props.setProperty("sourcelocators", "false");
444             String JavaDoc sl = req.getParameter("sourcelocators");
445             if (sl != null) {
446                 props.setProperty("sourcelocators", sl);
447             }
448
449             // Look for "profile=true" flag
450
props.setProperty("profile", "false");
451             String JavaDoc profile = req.getParameter("profile");
452             if (profile != null) {
453                 props.setProperty("profile", profile);
454             }
455         }
456
457         // Set the 'lzproxied' default = false
458
String JavaDoc proxied = req.getParameter("lzproxied");
459         if (proxied != null) {
460             props.setProperty("lzproxied", proxied);
461         }
462
463         if (mAllowRecompile) {
464             String JavaDoc recompile = req.getParameter(CompilationManager.RECOMPILE);
465             if (recompile != null) {
466                 // Check for passwd if required.
467
String JavaDoc pwd = req.getParameter("pwd");
468                 if ( mAdminPassword == null ||
469                         (pwd != null && pwd.equals(mAdminPassword)) ) {
470                     props.setProperty(CompilationManager.RECOMPILE, "true");
471                 } else {
472                     mLogger.warn("lzrecompile attempted but not allowed");
473                 }
474             }
475         }
476
477         if (mAllowRequestKRANK) {
478             // Look for "krank=true" flag. We only set this if
479
// lzt=swf, i.e., this is a request for an actual
480
// instrumented object (.swf) file. This tells the
481
// compilation manager to actually launch a krank
482
// listener.
483
props.setProperty("krank", "false");
484             String JavaDoc lzt = req.getParameter("lzt");
485             String JavaDoc krank = req.getParameter("krank");
486             if (krank != null && "swf".equals(lzt)) {
487                 props.setProperty("krank", krank);
488             }
489         }
490
491
492         String JavaDoc encoding = ContentEncoding.chooseEncoding(req);
493         // Only try to compress SWF5, SWF6 and later have built in
494
// compression.
495

496         if ("swf5".equals(props.getProperty(CompilationEnvironment.SWFVERSION_PROPERTY))
497              && (encoding != null)) {
498             props.setProperty(LZHttpUtils.CONTENT_ENCODING, encoding);
499         }
500
501         return props;
502     }
503
504
505     /**
506      * Return the canvas for the given LZX file.
507      *
508      * @param fileName pathname of file
509      * @return the canvas
510      */

511     protected Canvas getCanvas(String JavaDoc fileName, HttpServletRequest JavaDoc req)
512         throws CompilationError, IOException
513     {
514         Properties JavaDoc props = initCMgrProperties(req);
515         return mCompMgr.getCanvas(fileName, props);
516     }
517
518     public static CompilationManager getCompilationManager()
519     {
520         return mCompMgr;
521     }
522 }
523
Popular Tags