KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > cm > CompilationManager


1 /* *****************************************************************************
2  * CompilationManager.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.cm;
11 import java.io.InputStream JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.FileOutputStream JavaDoc;
14 import java.io.FilterOutputStream JavaDoc;
15 import java.io.FileInputStream JavaDoc;
16 import java.io.PrintWriter JavaDoc;
17 import java.io.FileNotFoundException JavaDoc;
18 import java.io.IOException JavaDoc;
19 import java.io.Serializable JavaDoc;
20 import java.util.*;
21 import org.openlaszlo.compiler.*;
22 import org.openlaszlo.server.LPS;
23 import org.openlaszlo.server.Configuration;
24 import org.openlaszlo.servlets.KrankListener;
25 import org.openlaszlo.utils.FileUtils;
26 import org.openlaszlo.utils.LZHttpUtils;
27 import org.openlaszlo.cache.Cache;
28 import org.apache.log4j.*;
29
30 /** A <code>CompilationManager</code> is responsible for maintaining
31  * the correspondence between source and object files. It's
32  * responsible for dependency analysis, caching, and selective recompilation.
33  *
34  * The main entry point to the Compilationmanager code is getObjectStream
35  *
36  * A CompilationManager is constructed with a source directory, where it
37  * looks for source files, and a cache directory, where it places
38  * compiled object files and cached (dependency) information. It can be
39  * instructed to always recompile files, never recompile them so long
40  * as they exist, or use dependency information that it creates during
41  * the course of a file compilation to determine whether a file is out
42  * of date. See the documentation for getProperty() for a description
43  * of how to select between these behaviors.
44  *
45  * The compilation manager currently uses itself to represent the
46  * cache, and methods that access the cache are synchronized. This
47  * would have to change to support multiple readers.
48  *
49  * Methods that trigger recompilation are synchronized: it's safe for
50  * multiple threads to contain references to a compilation manager if
51  * they are only using it to compile. Accessor methods should only be
52  * called single-threaded. Two compilation managers shouldn't be
53  * pointed at the same cache directory.
54  */

55 public class CompilationManager extends Cache {
56     /** Logger. */
57     private static Logger mLogger = Logger.getLogger(CompilationManager.class);
58
59     /** See the constructor. */
60     protected File JavaDoc mSourceDirectory;
61     /** See the constructor. */
62     protected File JavaDoc mCacheDirectory;
63     /** Cache for compiled media */
64     protected CompilerMediaCache mMediaCache;
65     /** See getProperties. */
66     protected Properties mProperties = null;
67
68     public static KrankListener sKrankListener = null;
69
70     protected File JavaDoc mLPSJarFile = null;
71
72     public static final String JavaDoc RECOMPILE = "lzrecompile";
73
74     /**
75      * Creates a new <code>CompilationManager</code> instance.
76      *
77      * @param sourceDirectory a <code>File</code> naming a directory,
78      * that is used as a base for resolving relative names that are
79      * passed to getObjectData.
80      *
81      * @param cacheDirectory a <code>File</code> naming a directory.
82      * The <code>CompilationManager</code> places object files and
83      * dependency-tracking information here, to avoid unnecessary
84      * subsequent recompilation.
85      *
86      */

87     public CompilationManager(File JavaDoc sourceDirectory, File JavaDoc cacheDirectory, Properties props)
88         throws IOException JavaDoc {
89         super("cache", cacheDirectory, props);
90         this.mSourceDirectory = sourceDirectory;
91         this.mCacheDirectory = cacheDirectory;
92         try {
93             cacheDirectory.mkdirs();
94         } catch (SecurityException JavaDoc se) {
95         }
96         if (!cacheDirectory.exists()) {
97             throw new FileNotFoundException JavaDoc(cacheDirectory.getAbsolutePath() + " does not exist");
98         }
99         if (!cacheDirectory.canRead()) {
100             throw new IOException JavaDoc("can't read " + cacheDirectory.getAbsolutePath());
101         }
102         String JavaDoc p = cacheDirectory.getAbsolutePath() + File.separator + "media";
103         this.mMediaCache = new CompilerMediaCache(new File JavaDoc(p), props);
104         this.mProperties = props;
105
106         String JavaDoc jd = LPS.getProperty("compMgr.lps.jar.dependency", "true");
107         if ("true".equals(jd)) {
108             mLPSJarFile = LPS.getLPSJarFile();
109         }
110     }
111
112     /**
113      * Returns the media cache for the compilation manager
114      */

115     public CompilerMediaCache getCompilerMediaCache() {
116         return mMediaCache;
117     }
118
119     /** Sets the source directory. This is the directory within which
120      * source files will be searched for.
121      * @param sourceDirectory a File
122      */

123     public void setSourceDirectory(File JavaDoc sourceDirectory) {
124         this.mSourceDirectory = sourceDirectory;
125     }
126
127     /** Clear the cache.
128      * @return true if full removal of cache was successful, otherwise false.
129      */

130     public boolean clearCacheDirectory() {
131         return mMediaCache.clearCache() && clearCache();
132     }
133     
134     /** Miscellaneous properties.
135      *
136      * <dl>
137      * <dt>recompile=always
138      * <dd>Always recompile object files, regardless of whether source
139      * files have changed.
140      * <dt>recompile=never
141      * <dd>Never recompile object files.
142      * <dt>recompile=check (default)
143      * <dd>Recompile an object file if a source file that it depends
144      * on has changed.
145      * </dl>
146      *
147      * Additionally, if <code>getProperties().getProperty("compiler."
148      * + <var>key</var>) == <var>value</var></code>, then files are
149      * compiled with a compiler such that
150      * <code>compiler.getProperty(<var>key</var>) ==
151      * <var>value</var></code>.
152      * @return a Properties
153      */

154     public Properties getProperties() {
155         return mProperties;
156     }
157     
158     public void setProperty(String JavaDoc key, String JavaDoc value) {
159         mProperties.setProperty(key, value);
160     }
161
162     protected void afterCacheRead(Object JavaDoc metaData) {
163         CachedInfo ci = (CachedInfo)metaData;
164         DependencyTracker dt = ci.getDependencyTracker();
165
166         // update webapp path of cache files
167
dt.updateWebappPath();
168
169         // set application options in configuration
170
Canvas canvas = ci.getCanvas();
171         LPS.configuration.setApplicationOptions
172             (canvas.getFilePath(), canvas.getSecurityOptions());
173     }
174
175     /**
176      * Returns a File containing the compiled form of the file
177      * named by pathname, suitable play on the client.
178      *
179      * @param pathname a <code>String</code> value. If
180      * <var>pathname</var> is relative, it is resolved relative to the
181      * CompilationManager's <var>sourceDirectory</var>.
182      * @param props params for dependency tracker and compiler
183      * @return the compiled File object.
184      * @exception CompilationError if an error occurs.
185      */

186     public synchronized File JavaDoc getObjectFile(String JavaDoc pathname, Properties props)
187         throws CompilationError, IOException JavaDoc
188     {
189         mLogger.debug("getObjectFile for " + pathname.toString());
190         return getItem(pathname, props).getFile();
191     }
192
193     /**
194        Abort the krank thread if it is running.
195      */

196     public synchronized static void abortKrankProcess () {
197         KrankListener kt = sKrankListener;
198         mLogger.info("called abortKrankProcess()");
199         // try and halt any previous listener that is sitting around hogging port 4444
200
if (kt != null) {
201             kt.setState(KrankListener.ABORTED);
202             try {
203                 // [todo: hqm 10-1-2003 for some reason
204
// thread.interrupt() seems to have no effect on the
205
// running krank thread. Thread.stop() stops it
206
// alright, but is deprecated by Sun
207
kt.closeSocket();
208                 kt.interrupt();
209             } catch (Exception JavaDoc e) {
210                 mLogger.debug("abortKrankProcess exception while killing thread: "+e.getMessage());
211             }
212         }
213
214     }
215
216     /**
217      * Returns an InputStream containing the compiled form of the file
218      * named by pathname, suitable play on the client.
219      *
220      * If the krank flag is set, run a krank workflow thread including
221      * starting the krank listener on port 4444
222      *
223      * @param pathname a <code>String</code> value. If
224      * <var>pathname</var> is relative, it is resolved relative to the
225      * CompilationManager's <var>sourceDirectory</var>.
226      * @param props params for dependency tracker and compiler
227      * @return the compiled File object.
228      * @exception CompilationError if an error occurs.
229      */

230     public synchronized InputStream JavaDoc getObjectStream(String JavaDoc pathname, Properties props)
231         throws CompilationError, IOException JavaDoc
232     {
233         // +++ We really want the actual original source file path , from the HttpRequest
234
// is that what we are getting? check the log for this message:
235
boolean kranking = "true".equals(props.get("krank"));
236         if (kranking) {
237             startKrankWorkflow(pathname, props);
238         }
239         return getItem(pathname, props).getStream();
240     }
241
242     /**
243      * Start a thread which does the krank workflow; listen on TCP port for xml serializtion,
244      * and when that completes, compile the kranked swf file and leave it in the source directory
245      * with a .lzo suffix.
246      * @param pathname path to source file
247      * @param props params for dependency tracker and compiler
248      */

249     protected void startKrankWorkflow (String JavaDoc pathname, Properties props) {
250         File JavaDoc srcfile = new File JavaDoc(pathname);
251         // Get just the prefix of the file basename: /bar/baz/foo.lzx => foo
252
String JavaDoc fname = new File JavaDoc(pathname).getName();
253         String JavaDoc prefix;
254         int idx = fname.indexOf(".");
255         if (idx != -1) {
256             prefix = fname.substring(0, idx);
257         } else {
258             prefix = fname;
259         }
260
261         // Where is the source directory?
262
mLogger.debug("getObjectStream pathname= " + pathname.toString());
263         mLogger.debug("krank prefix= "+prefix);
264
265         // Java requires variables that are referenced from inside
266
// a closure (the KrankListener runner thread below) to be
267
// declared final. Why?
268

269         File JavaDoc xmlFile = new File JavaDoc(srcfile.getParent(), prefix + "obj__.xml");
270         File JavaDoc krankedSWFfilecopy = new File JavaDoc(srcfile.getParent(), prefix + "__.swf");
271
272         String JavaDoc myfileName = pathname;
273         Properties myprops = (Properties) props.clone();
274         mLogger.debug("myprops = "+myprops.toString());
275         String JavaDoc myprefix = prefix;
276
277         // returns path with no suffix, like "c:/lps-krank/test/simple"
278
File JavaDoc basepath = new File JavaDoc(srcfile.getParent(), myprefix);
279         File JavaDoc targetSWF = new File JavaDoc(basepath.getAbsolutePath()+".lzo");
280         File JavaDoc targetSWFgz = new File JavaDoc(basepath.getAbsolutePath()+".lzo.gz");
281
282         try {
283             targetSWF.delete();
284         } catch (SecurityException JavaDoc e) {
285             mLogger.error("Could not delete old .lzo file at" + targetSWF.getAbsolutePath());
286             throw new CompilationError("Could not delete old .lzo file at" + targetSWF.getAbsolutePath());
287         }
288
289         try {
290             krankedSWFfilecopy.delete();
291         } catch (SecurityException JavaDoc e) {
292             mLogger.error("Could not delete old swf file at" + krankedSWFfilecopy.getAbsolutePath());
293             throw new CompilationError("Could not delete old swf file at" + krankedSWFfilecopy.getAbsolutePath());
294         }
295
296
297         try {
298             targetSWFgz.delete();
299         } catch (SecurityException JavaDoc e) {
300             mLogger.error("Could not delete old gzipped .lzo file at" + targetSWFgz.getAbsolutePath());
301             throw new CompilationError("Could not delete gzipped .lzo file at" + targetSWFgz.getAbsolutePath());
302         }
303
304         try {
305             xmlFile.delete();
306         } catch (SecurityException JavaDoc e) {
307             mLogger.error("Could not delete old xml data file at" + xmlFile.getAbsolutePath());
308             throw new CompilationError("Could not delete old xml data file at" + xmlFile.getAbsolutePath());
309         }
310
311         mLogger.debug("Krank xml data file is " + xmlFile.getAbsolutePath());
312
313         // kill any old listener hanging around;
314
abortKrankProcess();
315
316         // Remove gzip encoding property, so we can get our
317
// hands on an unzipped swf to give to the kranker
318
myprops.remove("Content-Encoding");
319         File JavaDoc krankedSWF;
320         try {
321             krankedSWF = getObjectFile(myfileName, myprops);
322         } catch (IOException JavaDoc e) {
323             throw new CompilationError("IOException could not locate kranked swf file for "+myfileName+" "+ myprops+": "+e.getMessage());
324         }
325
326         sKrankListener = new KrankListener(
327             prefix,
328             xmlFile,
329             krankedSWF,
330             krankedSWFfilecopy,
331             basepath,
332             targetSWF,
333             targetSWFgz,
334             myprops);
335
336         sKrankListener.start();
337     }
338
339     /**
340      * @return the canvas for this app
341      */

342     public synchronized Canvas getCanvas(String JavaDoc pathname)
343         throws CompilationError, IOException JavaDoc {
344         return getCanvas(pathname, new Properties());
345     }
346
347     /**
348      * Return the canvas associated with the given LZX file
349      *
350      * @return the canvas
351      * @param pathname path to the LZX file
352      * @param props props for dependency tracker and compiler
353      * @throws CompilationError if there is a compilation error
354      * in the file
355      */

356     public synchronized Canvas getCanvas(String JavaDoc pathname, Properties props)
357         throws CompilationError, IOException JavaDoc {
358
359         mLogger.debug("getCanvas for " + pathname.toString());
360         CachedInfo info = (CachedInfo)getItem(pathname, props).getMetaData();
361         return info.getCanvas();
362     }
363
364     /**
365      * @return a String containing XML info about this app
366      */

367     public synchronized String JavaDoc getInfoXML(String JavaDoc pathname, Properties props)
368         throws CompilationError, IOException JavaDoc {
369
370         if (pathname == null) {
371              return "";
372         }
373
374         Properties altProps = (Properties)props.clone();
375         Item item = getItem(pathname, props);
376         String JavaDoc enc = props.getProperty(LZHttpUtils.CONTENT_ENCODING);
377         long size = item.getSize();
378         if (enc == null) {
379             enc = "";
380         }
381
382         boolean isDebug = "true".equals(props.getProperty("debug"));
383         boolean debugExists = isDebug;
384         boolean nondebugExists = !isDebug;
385         boolean debugUptodate = false;
386         boolean nondebugUptodate = false;
387         Properties alt = null;
388
389         if (isDebug) {
390             alt = (Properties)props.clone();
391             alt.setProperty("debug", "false");
392             nondebugExists = (getItem(computeKey(pathname, alt)) != null);
393         } else {
394             alt = (Properties)props.clone();
395             alt.setProperty("debug", "true");
396             debugExists = (getItem(computeKey(pathname, alt)) != null);
397         }
398
399         if (debugExists) {
400             Properties p;
401             if (!isDebug) {
402                 item = getItem(computeKey(pathname, alt));
403                 p = (Properties)alt.clone();
404             } else {
405                 p = (Properties)props.clone();
406             }
407             CachedInfo info = (CachedInfo)item.getMetaData();
408             if (info != null) {
409                 DependencyTracker tracker = info.getDependencyTracker();
410                 debugUptodate = tracker.isUpToDate(p);
411             }
412         }
413
414         if (nondebugExists) {
415             Properties p;
416             if (isDebug) {
417                 item = getItem(computeKey(pathname, alt));
418                 p = (Properties)alt.clone();
419             } else {
420                 item = getItem(computeKey(pathname, props));
421                 p = (Properties)props.clone();
422             }
423             CachedInfo info = (CachedInfo)item.getMetaData();
424             if (info != null) {
425                 DependencyTracker tracker = info.getDependencyTracker();
426                 nondebugUptodate = tracker.isUpToDate(p);
427             }
428         }
429
430         long gzsize = 0;
431         long ungzsize = 0;
432
433         // Only swf5 has alternate compressed file version,
434
// swf6 and greater has internal file compression.
435
String JavaDoc version = props.getProperty(CompilationEnvironment.SWFVERSION_PROPERTY);
436         if ("swf5".equals(version)) {
437             if (enc.equals("gzip")) {
438                 altProps.setProperty(LZHttpUtils.CONTENT_ENCODING, "");
439                 ungzsize = getItem(pathname, altProps).getSize();
440                 gzsize = size;
441             } else {
442                 altProps.setProperty(LZHttpUtils.CONTENT_ENCODING, "gzip");
443                 gzsize = getItem(pathname, altProps).getSize();
444                 ungzsize = size;
445             }
446         } else {
447             ungzsize = size;
448         }
449
450         return "<info size=\"" + ungzsize +
451                "\" gz-size=\"" + gzsize +
452                "\" debug=\"" + isDebug +
453                "\" encoding=\"" + enc +
454                "\" debug-exists=\"" + debugExists +
455                "\" debug-up-to-date=\"" + debugUptodate +
456                "\" nondebug-exists=\"" + nondebugExists +
457                "\" nondebug-up-to-date=\"" + nondebugUptodate +
458                "\" runtime=\"" + version +
459                "\" />";
460     }
461
462     /**
463      * Return the last modified time associated with the given LZX file
464      *
465      * @return the last modified time in utc
466      * @param pathname path to the LZX file
467      * @param props params for dependency tracker and compiler
468      * @throws CompilationError if there is a compilation error
469      * in the file
470      */

471     public synchronized long getLastModified(String JavaDoc pathname, Properties props)
472         throws CompilationError, IOException JavaDoc {
473         mLogger.debug("getLastModified for " + pathname.toString());
474         // This is the last modified time from the item in the cache
475
File JavaDoc file = new File JavaDoc(getItem(pathname, props).getPathName());
476         return file.lastModified();
477     }
478     
479     /**
480      * Get the cached item. Recompile or convert encoding as needed.
481      *
482      * @param pathname path to the LZX file
483      * @param props params for compiler
484      */

485     public synchronized Item getItem(String JavaDoc pathname, Properties props)
486         throws IOException JavaDoc {
487
488         Serializable JavaDoc key = computeKey(pathname, props);
489         String JavaDoc enc = props.getProperty(LZHttpUtils.CONTENT_ENCODING);
490         boolean lockit = false;
491         Item item = findItem(key, enc, lockit);
492         // Set up the properties, for dependency checking
493
Properties compilationProperties = (Properties) props.clone();
494         if (!isItemUpToDate(item, pathname, compilationProperties)) {
495             
496             Properties props2 = (Properties) props.clone();
497             if (enc == null || enc.equals("")) {
498                 props2.setProperty(LZHttpUtils.CONTENT_ENCODING, "gzip");
499             } else {
500                 props2.setProperty(LZHttpUtils.CONTENT_ENCODING, "");
501             }
502             Serializable JavaDoc key2 = computeKey(pathname, props2);
503             Item item2 = getItem(key2);
504             if (item2 != null && isItemUpToDate(item2, pathname, props2)) {
505                 convertItemEncoding(item2, item, pathname, enc, compilationProperties);
506             } else {
507                 compileItem(item, pathname, compilationProperties);
508             }
509         }
510         updateCache(item);
511         return item;
512     }
513
514     /**
515      * Take source item, un-encode it, and then re-encode and store in dest item
516      * with the specified encoding
517      * @param src - source item
518      * @param dest - dest item
519      * @param pathname - pathname for destination item
520      * @param enc - encoding for dest
521      * @param props - properties for compile
522      *
523      * For now, hardcoded to only support gzip.
524      */

525     public synchronized void convertItemEncoding(Item src, Item dest,
526         String JavaDoc pathname, String JavaDoc enc, Properties props) {
527
528         File JavaDoc srcFile = src.getFile();
529         File JavaDoc destFile = new File JavaDoc(dest.getPathName());
530         CachedInfo srcInfo = (CachedInfo)src.getMetaData();
531         try {
532
533             dest.markDirty();
534
535             File JavaDoc tempFile = null;
536             FileInputStream JavaDoc tempFileStream = null;
537             try {
538                 tempFile = File.createTempFile("lzc-", null);
539                 mLogger.debug("Temporary file is " + tempFile.getAbsolutePath());
540
541                 mLogger.debug("Re-encoding from " + srcFile.toString() + " to "
542                                                   + tempFile.toString());
543
544                 if (enc != null && enc.equals("gzip")) {
545                     // Encode from uncompressed to gzip
546
FileUtils.encode(srcFile, tempFile, enc);
547                 } else {
548                     // Decode from gzip to uncompressed.
549
FileUtils.decode(srcFile, tempFile, "gzip");
550                 }
551
552                 tempFileStream = new FileInputStream JavaDoc(tempFile);
553
554                 dest.update(tempFileStream);
555
556                 DependencyTracker dependencyTracker = new DependencyTracker(props);
557                 dependencyTracker.copyFiles(srcInfo.getDependencyTracker(), srcFile);
558                 dependencyTracker.addFile(destFile);
559     
560                 // FIXME: [2003-07-21 bloch] the factoring of cm.CacheInfo and cache.CacheInfo
561
// has never been right. Someday, if there are more subclasses of Cache that
562
// use the [gs]etMetaData methods, then this should really be fixed. (bug #1778).
563
CachedInfo info = new CachedInfo(dependencyTracker, srcInfo.getCanvas(), enc);
564                 dest.getInfo().setLastModified(destFile.lastModified());
565                 dest.update(info);
566                 dest.markClean();
567
568             } finally {
569                 if (tempFileStream != null) {
570                     FileUtils.close(tempFileStream);
571                 }
572                 if (tempFile != null) {
573                     tempFile.delete();
574                 }
575
576                 // Cleanup now
577
mLogger.debug("starting gc");
578                 System.gc();
579                 mLogger.debug("finished gc");
580             }
581
582
583         } catch (IOException JavaDoc ioe) {
584             CompilationError e = new CompilationError(ioe);
585             e.initPathname(pathname);
586             throw e;
587         }
588     }
589
590     /**
591      * @return true if the item is up to date
592      *
593      * @param pathname a <code>String</code> value. If
594      * <var>pathname</var> is relative, it is resolved relative to the
595      * CompilationManager's <var>sourceDirectory</var>.
596      * @param props properties that affect the compile.
597      * @return the bytes of the object file
598      * @exception CompilationError if an error occurs
599      *
600      * The PROPS parameter may contain
601      * <ul>
602      * <li>"Content-Encoding" =&gt; the encoding of the output (ignore if null)
603      * </ul>
604      * NOTE: can remove the recompile property from props
605      */

606     public boolean isItemUpToDate(Item item, String JavaDoc pathname, Properties props)
607         throws CompilationError {
608
609         File JavaDoc sourceFile = new File JavaDoc(mSourceDirectory, pathname);
610         File JavaDoc objectFile = new File JavaDoc(item.getPathName());
611
612         boolean recompile = props.getProperty(RECOMPILE) != null;
613         props.remove(RECOMPILE);
614
615         boolean upToDate = false;
616
617         try {
618             String JavaDoc recompileSwitch =
619                 mProperties.getProperty("recompile", "check").intern();
620
621             if (pathname.endsWith(".lzo")) {
622                 return true; // we cannot recompile a kranked file automatically
623
} else if (recompile) {
624                 mLogger.info("forcing a recompile");
625                 return false;
626             } else if (recompileSwitch == "always") {
627                 return false;
628             } else if (recompileSwitch == "never") {
629                 return objectFile.exists();
630             } else if (recompileSwitch == "check") {
631                 CachedInfo info = (CachedInfo)item.getMetaData();
632                 DependencyTracker tracker = null;
633                 if (info != null) {
634                     tracker = info.getDependencyTracker();
635                 }
636                 // Set up the properties, for dependency checking
637
props = (Properties) props.clone();
638                 return (tracker != null) && tracker.isUpToDate(props);
639             } else {
640                 throw new IllegalArgumentException JavaDoc(
641                     "invalid value for 'recompile' property");
642             }
643         } catch (Exception JavaDoc ex) {
644             CompilationError e = new CompilationError(ex);
645             e.initPathname(sourceFile.getPath());
646             throw e;
647         }
648     }
649
650     /**
651      * Compiles the file named by pathname and leaves the result
652      * in the cached item. If the file can't be compiled and
653      * debugCompilationErrors is true, returns an HTML document
654      * describing the error; otherwise, passes the exception.
655      *
656      * @param pathname a <code>String</code> value. If
657      * <var>pathname</var> is relative, it is resolved relative to the
658      * CompilationManager's <var>sourceDirectory</var>.
659      * @param props properties that affect the compile.
660      * @return the bytes of the object file
661      * @exception CompilationError if an error occurs
662      *
663      * The PROPS parameter may contain
664      * <ul>
665      * <li>"Content-Encoding" =&gt; the encoding of the output (ignore if null)
666      * </ul>
667      */

668     public synchronized void compileItem(Item item, String JavaDoc pathname,
669             Properties compilationProperties)
670         throws CompilationError
671     {
672         File JavaDoc sourceFile = new File JavaDoc(mSourceDirectory, pathname);
673         File JavaDoc objectFile = new File JavaDoc(item.getPathName());
674
675         try {
676
677             item.markDirty();
678
679             org.openlaszlo.compiler.Compiler compiler =
680                 new org.openlaszlo.compiler.Compiler();
681             // For each property compiler.name=value, set a
682
// compiler property name=value (that is, without
683
// the "compiler." prefix)
684
for (java.util.Enumeration JavaDoc e = getProperties().propertyNames();
685                  e.hasMoreElements(); ) {
686                 String JavaDoc key = (String JavaDoc) e.nextElement();
687                 if (key.startsWith("compiler.")) {
688                     compiler.setProperty(
689                         key.substring("compiler.".length()),
690                         getProperties().getProperty(key));
691                 }
692             }
693             DependencyTracker dependencyTracker =
694                 new DependencyTracker(compilationProperties);
695             TrackingFileResolver resolver =
696                 new TrackingFileResolver(compiler.getFileResolver(),
697                                          dependencyTracker);
698             dependencyTracker.addFile(sourceFile);
699             if (mLPSJarFile != null) {
700                 dependencyTracker.addFile(mLPSJarFile);
701             }
702             compiler.setFileResolver(resolver);
703             compiler.setMediaCache(mMediaCache);
704
705             Canvas canvas = null;
706
707             File JavaDoc tempFile = null;
708             FileInputStream JavaDoc tempFileStream = null;
709             try {
710                 tempFile = File.createTempFile("lzc-", null);
711                 mLogger.debug("Temporary file is " + tempFile.getAbsolutePath());
712
713                 mLogger.debug("Compiling " + sourceFile.toString() + " to "
714                                            + tempFile.toString());
715                 canvas = compiler.compile(sourceFile, tempFile,
716                                           compilationProperties);
717
718                 mLogger.debug("Canvas size is " + canvas.getWidth() + " by "
719                                                 + canvas.getHeight());
720
721                 tempFileStream = new FileInputStream JavaDoc(tempFile);
722
723                 item.update(tempFileStream);
724                 dependencyTracker.addFile(objectFile);
725                 String JavaDoc encoding = compilationProperties.getProperty(LZHttpUtils.CONTENT_ENCODING);
726                 // FIXME: [2003-07-21 bloch] the factoring of cm.CacheInfo and cache.CacheInfo
727
// has never been right. Someday, if there are more subclasses of Cache that
728
// use the [gs]etMetaData methods, then this should really be fixed. (bug #1778).
729
CachedInfo info = new CachedInfo(dependencyTracker, canvas, encoding);
730                 item.getInfo().setLastModified(objectFile.lastModified());
731                 item.update(info);
732                 item.markClean();
733
734                 // Add security options for this application after compilation
735
LPS.configuration.setApplicationOptions(FileUtils.relativePath(pathname, LPS.HOME()),
736                                                         canvas.getSecurityOptions());
737
738             } finally {
739                 if (tempFileStream != null) {
740                     FileUtils.close(tempFileStream);
741                 }
742                 if (tempFile != null) {
743                     tempFile.delete();
744                 }
745
746                 // Cleanup now
747
mLogger.debug("starting gc");
748                 System.gc();
749                 mLogger.debug("finished gc");
750             }
751         } catch (IOException JavaDoc ioe) {
752             CompilationError e = new CompilationError(ioe);
753             e.initPathname(sourceFile.getPath());
754             throw e;
755         }
756     }
757
758     /**
759      * @return a cache key for the given compile
760      */

761     static Serializable JavaDoc computeKey(String JavaDoc pathname, Properties props) {
762
763         TreeSet sorted = new TreeSet ();
764         for (java.util.Enumeration JavaDoc e = props.propertyNames();
765              e.hasMoreElements(); ) {
766             String JavaDoc key = (String JavaDoc) e.nextElement();
767             // Omit the recompile property
768
if (key.equalsIgnoreCase(RECOMPILE))
769                 continue;
770
771             String JavaDoc value = props.getProperty(key);
772             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
773             buf.append(key);
774             buf.append(' ');
775             buf.append(value);
776
777             sorted.add(buf.toString());
778         }
779
780         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(FileUtils.relativePath(pathname, LPS.HOME()));
781
782         buf.append(' ');
783         for (java.util.Iterator JavaDoc e = sorted.iterator(); e.hasNext(); ) {
784             String JavaDoc str = (String JavaDoc) e.next();
785             buf.append(str);
786         }
787
788         mLogger.debug("computeKey: " + buf.toString());
789         return buf.toString();
790     }
791
792     /** Given the pathname of a file, return a file name
793      * for an "source" version of this file that lives in the cache.
794      * @param srcName path to file in source directory
795      * @param webappPath real path for web application
796      * @return a File
797      */

798     public File JavaDoc getCacheSourcePath (String JavaDoc srcName, String JavaDoc webappPath) {
799         // todo: relative to src directory
800
File JavaDoc src = new File JavaDoc(srcName);
801         if (src.getParentFile() != null) {
802             String JavaDoc baseParent = src.getParentFile().getAbsolutePath();
803             if (baseParent != null &&
804                 baseParent.startsWith(mCacheDirectory.getAbsolutePath())) {
805                 return new File JavaDoc(srcName);
806             }
807         }
808         // FIXME: [2003-01-21 pkang] webapp real path should match first part of
809
// source name. CompilationManager and LZServlet will need overhaul to
810
// support reading directly from war file.
811
if (webappPath == null || ! srcName.startsWith(webappPath))
812             throw new RuntimeException JavaDoc(srcName + " doesn't begin with " +
813                     webappPath);
814         String JavaDoc fixedName = srcName.substring(webappPath.length());
815         return new File JavaDoc(mCacheDirectory, fixedName);
816     }
817 }
818
819
820 /** A FileResolver that tracks dependencies.
821  *
822  * @author Oliver Steele
823  */

824 class TrackingFileResolver implements FileResolver {
825     private final FileResolver mBaseResolver;
826     private final DependencyTracker mDependencies;
827
828     TrackingFileResolver(FileResolver baseResolver,
829                          DependencyTracker tracker) {
830         this.mBaseResolver = baseResolver;
831         this.mDependencies = tracker;
832     }
833     
834     /**
835      * Implement the FileResolver interface.
836      *
837      * @param pathname a <code>String</code> value
838      * @param base a <code>String</code> value
839      * @return a <code>File</code> value
840      * @exception FileNotFoundException if an error occurs
841      */

842     public File JavaDoc resolve(String JavaDoc pathname, String JavaDoc base)
843         throws FileNotFoundException JavaDoc
844     {
845         File JavaDoc file = mBaseResolver.resolve(pathname, base);
846         mDependencies.addFile(file);
847         return file;
848     }
849
850     /** For debugging. */
851     /*void writeResults() {
852         System.out.println("depends on");
853         for (java.util.Iterator e = mDependencies.iterator();
854              e.hasNext(); ) {
855             FileInfo fi = (FileInfo) e.next();
856             System.out.println(fi.mPathname + " -> " + fi.mChecksum);
857         }
858         }*/

859 }
860
Popular Tags