001: /* *****************************************************************************
002: * CompilationManager.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.cm;
011:
012: import java.io.InputStream;
013: import java.io.File;
014: import java.io.FileOutputStream;
015: import java.io.FilterOutputStream;
016: import java.io.FileInputStream;
017: import java.io.PrintWriter;
018: import java.io.FileNotFoundException;
019: import java.io.IOException;
020: import java.io.Serializable;
021: import java.util.*;
022: import org.openlaszlo.compiler.*;
023: import org.openlaszlo.server.LPS;
024: import org.openlaszlo.server.Configuration;
025: import org.openlaszlo.utils.FileUtils;
026: import org.openlaszlo.utils.LZHttpUtils;
027: import org.openlaszlo.cache.Cache;
028: import org.openlaszlo.compiler.CompilationEnvironment;
029: import org.apache.log4j.*;
030:
031: /** A <code>CompilationManager</code> is responsible for maintaining
032: * the correspondence between source and object files. It's
033: * responsible for dependency analysis, caching, and selective recompilation.
034: *
035: * The main entry point to the Compilationmanager code is getObjectStream
036: *
037: * A CompilationManager is constructed with a source directory, where it
038: * looks for source files, and a cache directory, where it places
039: * compiled object files and cached (dependency) information. It can be
040: * instructed to always recompile files, never recompile them so long
041: * as they exist, or use dependency information that it creates during
042: * the course of a file compilation to determine whether a file is out
043: * of date. See the documentation for getProperty() for a description
044: * of how to select between these behaviors.
045: *
046: * The compilation manager currently uses itself to represent the
047: * cache, and methods that access the cache are synchronized. This
048: * would have to change to support multiple readers.
049: *
050: * Methods that trigger recompilation are synchronized: it's safe for
051: * multiple threads to contain references to a compilation manager if
052: * they are only using it to compile. Accessor methods should only be
053: * called single-threaded. Two compilation managers shouldn't be
054: * pointed at the same cache directory.
055: */
056: public class CompilationManager extends Cache {
057: /** Logger. */
058: private static Logger mLogger = Logger
059: .getLogger(CompilationManager.class);
060:
061: /** See the constructor. */
062: protected File mSourceDirectory;
063: /** See the constructor. */
064: protected File mCacheDirectory;
065: /** Cache for compiled media */
066: protected CompilerMediaCache mMediaCache;
067: /** See getProperties. */
068: protected Properties mProperties = null;
069:
070: protected File mLPSJarFile = null;
071:
072: private int[] lfcsizes = null;
073:
074: public static final String RECOMPILE = "lzrecompile";
075:
076: /**
077: * Creates a new <code>CompilationManager</code> instance.
078: *
079: * @param sourceDirectory a <code>File</code> naming a directory,
080: * that is used as a base for resolving relative names that are
081: * passed to getObjectData.
082: *
083: * @param cacheDirectory a <code>File</code> naming a directory.
084: * The <code>CompilationManager</code> places object files and
085: * dependency-tracking information here, to avoid unnecessary
086: * subsequent recompilation.
087: *
088: */
089: public CompilationManager(File sourceDirectory,
090: File cacheDirectory, Properties props) throws IOException {
091: super ("cache", cacheDirectory, props);
092: this .mSourceDirectory = sourceDirectory;
093: this .mCacheDirectory = cacheDirectory;
094: try {
095: cacheDirectory.mkdirs();
096: } catch (SecurityException se) {
097: }
098: if (!cacheDirectory.exists()) {
099: throw new FileNotFoundException(
100: /* (non-Javadoc)
101: * @i18n.test
102: * @org-mes=p[0] + " does not exist"
103: */
104: org.openlaszlo.i18n.LaszloMessages.getMessage(
105: CompilationManager.class.getName(), "051018-102",
106: new Object[] { cacheDirectory.getAbsolutePath() }));
107: }
108: if (!cacheDirectory.canRead()) {
109: throw new IOException(
110: /* (non-Javadoc)
111: * @i18n.test
112: * @org-mes="can't read " + p[0]
113: */
114: org.openlaszlo.i18n.LaszloMessages.getMessage(
115: CompilationManager.class.getName(), "051018-112",
116: new Object[] { cacheDirectory.getAbsolutePath() }));
117: }
118: String p = cacheDirectory.getAbsolutePath() + File.separator
119: + "media";
120: this .mMediaCache = new CompilerMediaCache(new File(p), props);
121: this .mProperties = props;
122:
123: String jd = LPS.getProperty("compMgr.lps.jar.dependency",
124: "true");
125: if ("true".equals(jd)) {
126: mLPSJarFile = LPS.getLPSJarFile();
127: }
128: }
129:
130: /**
131: * Returns the media cache for the compilation manager
132: */
133: public CompilerMediaCache getCompilerMediaCache() {
134: return mMediaCache;
135: }
136:
137: /** Sets the source directory. This is the directory within which
138: * source files will be searched for.
139: * @param sourceDirectory a File
140: */
141: public void setSourceDirectory(File sourceDirectory) {
142: this .mSourceDirectory = sourceDirectory;
143: }
144:
145: /** Clear the cache.
146: * @return true if full removal of cache was successful, otherwise false.
147: */
148: public boolean clearCacheDirectory() {
149: return mMediaCache.clearCache() && clearCache();
150: }
151:
152: /** Miscellaneous properties.
153: *
154: * <dl>
155: * <dt>recompile=always
156: * <dd>Always recompile object files, regardless of whether source
157: * files have changed.
158: * <dt>recompile=never
159: * <dd>Never recompile object files.
160: * <dt>recompile=check (default)
161: * <dd>Recompile an object file if a source file that it depends
162: * on has changed.
163: * </dl>
164: *
165: * Additionally, if <code>getProperties().getProperty("compiler."
166: * + <var>key</var>) == <var>value</var></code>, then files are
167: * compiled with a compiler such that
168: * <code>compiler.getProperty(<var>key</var>) ==
169: * <var>value</var></code>.
170: * @return a Properties
171: */
172: public Properties getProperties() {
173: return mProperties;
174: }
175:
176: public void setProperty(String key, String value) {
177: mProperties.setProperty(key, value);
178: }
179:
180: protected void afterCacheRead(Object metaData) {
181: CachedInfo ci = (CachedInfo) metaData;
182: DependencyTracker dt = ci.getDependencyTracker();
183:
184: // update webapp path of cache files
185: dt.updateWebappPath();
186:
187: // set application options in configuration
188: Canvas canvas = ci.getCanvas();
189: LPS.configuration.setApplicationOptions(canvas.getFilePath(),
190: canvas.getSecurityOptions());
191: }
192:
193: /**
194: * Returns a File containing the compiled form of the file
195: * named by pathname, suitable play on the client.
196: *
197: * @param pathname a <code>String</code> value. If
198: * <var>pathname</var> is relative, it is resolved relative to the
199: * CompilationManager's <var>sourceDirectory</var>.
200: * @param props params for dependency tracker and compiler
201: * @return the compiled File object.
202: * @exception CompilationError if an error occurs.
203: */
204: public synchronized File getObjectFile(String pathname,
205: Properties props) throws CompilationError, IOException {
206: mLogger.debug(
207: /* (non-Javadoc)
208: * @i18n.test
209: * @org-mes="getObjectFile for " + p[0]
210: */
211: org.openlaszlo.i18n.LaszloMessages.getMessage(
212: CompilationManager.class.getName(), "051018-208",
213: new Object[] { pathname.toString() }));
214: return getItem(pathname, props).getFile();
215: }
216:
217: /**
218: * Returns an InputStream containing the compiled form of the file
219: * named by pathname, suitable play on the client.
220: *
221: * @param pathname a <code>String</code> value. If
222: * <var>pathname</var> is relative, it is resolved relative to the
223: * CompilationManager's <var>sourceDirectory</var>.
224: * @param props params for dependency tracker and compiler
225: * @return the compiled File object.
226: * @exception CompilationError if an error occurs.
227: */
228: public synchronized InputStream getObjectStream(String pathname,
229: Properties props) throws CompilationError, IOException {
230: return getItem(pathname, props).getStream();
231: }
232:
233: /**
234: * Returns an InputStream containing the script form of the file
235: * named by pathname, suitable play on the client.
236: *
237: * @param pathname a <code>String</code> value. If
238: * <var>pathname</var> is relative, it is resolved relative to the
239: * CompilationManager's <var>sourceDirectory</var>.
240: * @param props params for dependency tracker and compiler
241: * @return the compiled File object.
242: * @exception CompilationError if an error occurs.
243: */
244: public synchronized InputStream getScriptStream(String pathname,
245: Properties props) throws CompilationError, IOException {
246: return getItem(pathname, props).getStream();
247: }
248:
249: /**
250: * @return the canvas for this app
251: */
252: public synchronized Canvas getCanvas(String pathname)
253: throws CompilationError, IOException {
254: return getCanvas(pathname, new Properties());
255: }
256:
257: /**
258: * Return the canvas associated with the given LZX file
259: *
260: * @return the canvas
261: * @param pathname path to the LZX file
262: * @param props props for dependency tracker and compiler
263: * @throws CompilationError if there is a compilation error
264: * in the file
265: */
266: public synchronized Canvas getCanvas(String pathname,
267: Properties props) throws CompilationError, IOException {
268:
269: mLogger.debug(
270: /* (non-Javadoc)
271: * @i18n.test
272: * @org-mes="getCanvas for " + p[0]
273: */
274: org.openlaszlo.i18n.LaszloMessages.getMessage(
275: CompilationManager.class.getName(), "051018-469",
276: new Object[] { pathname.toString() }));
277: CachedInfo info = (CachedInfo) getItem(pathname, props)
278: .getMetaData();
279: return info.getCanvas();
280: }
281:
282: /**
283: * @return an array with the size and gzipped size of the InputStream in
284: * bytes
285: */
286: private int[] getLFCSizes(InputStream in) throws IOException {
287: java.io.ByteArrayOutputStream outbuf = new java.io.ByteArrayOutputStream();
288: java.util.zip.GZIPOutputStream out = new java.util.zip.GZIPOutputStream(
289: outbuf);
290: int size = FileUtils.sendToStream(in, out);
291: in.close();
292: out.finish();
293: int gzsize = outbuf.size();
294: out.close();
295: int[] result = { size, gzsize };
296: return result;
297: }
298:
299: /**
300: * @return a String containing XML info about this app
301: */
302: public synchronized String getInfoXML(String pathname,
303: Properties props) throws CompilationError, IOException {
304:
305: if (pathname == null) {
306: return "";
307: }
308:
309: Item item = getItem(pathname, props);
310: String enc = props.getProperty(LZHttpUtils.CONTENT_ENCODING);
311:
312: boolean isDebug = "true".equals(props.getProperty("debug"));
313: boolean isProfile = "true".equals(props.getProperty("profile"));
314: boolean isBacktrace = "true".equals(props
315: .getProperty("backtrace"));
316: String runtime = props
317: .getProperty(CompilationEnvironment.RUNTIME_PROPERTY);
318:
319: String lfc = LPS.getLFCname(runtime, isDebug, isProfile,
320: isBacktrace);
321: String path = LPS.getLFCDirectory();
322:
323: File lfcfile = new File(path, lfc);
324:
325: // TODO: update to cache correct size for debug, profiled LFC
326: if (lfcsizes == null)
327: lfcsizes = getLFCSizes(new FileInputStream(lfcfile));
328: int lfcsize = lfcsizes[0];
329: int gzlfcsize = lfcsizes[1];
330:
331: int[] sizes = getLFCSizes(getObjectStream(pathname, props));
332: int size = sizes[0];
333: int gzsize = sizes[1];
334:
335: boolean debugExists = isDebug;
336: boolean nondebugExists = !isDebug;
337: boolean debugUptodate = false;
338: boolean nondebugUptodate = false;
339: Properties alt = null;
340:
341: if (isDebug) {
342: alt = (Properties) props.clone();
343: alt.setProperty("debug", "false");
344: nondebugExists = (getItem(computeKey(pathname, alt)) != null);
345: } else {
346: alt = (Properties) props.clone();
347: alt.setProperty("debug", "true");
348: debugExists = (getItem(computeKey(pathname, alt)) != null);
349: }
350:
351: if (debugExists) {
352: Properties p;
353: if (!isDebug) {
354: item = getItem(computeKey(pathname, alt));
355: p = (Properties) alt.clone();
356: } else {
357: p = (Properties) props.clone();
358: }
359: CachedInfo info = (CachedInfo) item.getMetaData();
360: if (info != null) {
361: DependencyTracker tracker = info.getDependencyTracker();
362: debugUptodate = tracker.isUpToDate(p);
363: }
364: }
365:
366: if (nondebugExists) {
367: Properties p;
368: if (isDebug) {
369: item = getItem(computeKey(pathname, alt));
370: p = (Properties) alt.clone();
371: } else {
372: item = getItem(computeKey(pathname, props));
373: p = (Properties) props.clone();
374: }
375: CachedInfo info = (CachedInfo) item.getMetaData();
376: if (info != null) {
377: DependencyTracker tracker = info.getDependencyTracker();
378: nondebugUptodate = tracker.isUpToDate(p);
379: }
380: }
381:
382: return "<info size=\"" + size + "\" debug=\"" + isDebug
383: + "\" encoding=\"" + enc + "\" debug-exists=\""
384: + debugExists + "\" debug-up-to-date=\""
385: + debugUptodate + "\" nondebug-exists=\""
386: + nondebugExists + "\" nondebug-up-to-date=\""
387: + nondebugUptodate + "\" runtime=\"" + runtime
388: + "\" gzsize=\"" + gzsize + "\" lfcsize=\"" + lfcsize
389: + "\" gzlfcsize=\"" + gzlfcsize + "\" />";
390: }
391:
392: /**
393: * Return the last modified time associated with the given LZX file
394: *
395: * @return the last modified time in utc
396: * @param pathname path to the LZX file
397: * @param props params for dependency tracker and compiler
398: * @throws CompilationError if there is a compilation error
399: * in the file
400: */
401: public synchronized long getLastModified(String pathname,
402: Properties props) throws CompilationError, IOException {
403: mLogger.debug(
404: /* (non-Javadoc)
405: * @i18n.test
406: * @org-mes="getLastModified for " + p[0]
407: */
408: org.openlaszlo.i18n.LaszloMessages.getMessage(
409: CompilationManager.class.getName(), "051018-590",
410: new Object[] { pathname.toString() }));
411: // This is the last modified time from the item in the cache
412: File file = new File(getItem(pathname, props).getPathName());
413: return file.lastModified();
414: }
415:
416: /**
417: * Get the cached item. Recompile or convert encoding as needed.
418: *
419: * @param pathname path to the LZX file
420: * @param props params for compiler
421: */
422: public synchronized Item getItem(String pathname, Properties props)
423: throws IOException {
424:
425: Serializable key = computeKey(pathname, props);
426: String enc = props.getProperty(LZHttpUtils.CONTENT_ENCODING);
427: boolean lockit = false;
428: Item item = findItem(key, enc, lockit);
429: // Set up the properties, for dependency checking
430: Properties compilationProperties = (Properties) props.clone();
431: if (!isItemUpToDate(item, pathname, compilationProperties)) {
432:
433: Properties props2 = (Properties) props.clone();
434: if (enc == null || enc.equals("")) {
435: props2
436: .setProperty(LZHttpUtils.CONTENT_ENCODING,
437: "gzip");
438: } else {
439: props2.setProperty(LZHttpUtils.CONTENT_ENCODING, "");
440: }
441: Serializable key2 = computeKey(pathname, props2);
442: Item item2 = getItem(key2);
443: if (item2 != null
444: && isItemUpToDate(item2, pathname, props2)) {
445: convertItemEncoding(item2, item, pathname, enc,
446: compilationProperties);
447: } else {
448: compileItem(item, pathname, compilationProperties);
449: }
450: }
451: updateCache(item);
452: return item;
453: }
454:
455: /**
456: * Take source item, un-encode it, and then re-encode and store in dest item
457: * with the specified encoding
458: * @param src - source item
459: * @param dest - dest item
460: * @param pathname - pathname for destination item
461: * @param enc - encoding for dest
462: * @param props - properties for compile
463: *
464: * For now, hardcoded to only support gzip.
465: */
466: public synchronized void convertItemEncoding(Item src, Item dest,
467: String pathname, String enc, Properties props) {
468:
469: File srcFile = src.getFile();
470: File destFile = new File(dest.getPathName());
471: CachedInfo srcInfo = (CachedInfo) src.getMetaData();
472: try {
473:
474: dest.markDirty();
475:
476: File tempFile = null;
477: FileInputStream tempFileStream = null;
478: try {
479: tempFile = File.createTempFile("lzc-", null);
480: mLogger.debug(
481: /* (non-Javadoc)
482: * @i18n.test
483: * @org-mes="Temporary file is " + p[0]
484: */
485: org.openlaszlo.i18n.LaszloMessages.getMessage(
486: CompilationManager.class.getName(),
487: "051018-663", new Object[] { tempFile
488: .getAbsolutePath() }));
489:
490: mLogger.debug(
491: /* (non-Javadoc)
492: * @i18n.test
493: * @org-mes="Re-encoding from " + p[0] + " to " + p[1]
494: */
495: org.openlaszlo.i18n.LaszloMessages.getMessage(
496: CompilationManager.class.getName(),
497: "051018-672",
498: new Object[] { srcFile.toString(),
499: tempFile.toString() }));
500:
501: if (enc != null && enc.equals("gzip")) {
502: // Encode from uncompressed to gzip
503: FileUtils.encode(srcFile, tempFile, enc);
504: } else {
505: // Decode from gzip to uncompressed.
506: FileUtils.decode(srcFile, tempFile, "gzip");
507: }
508:
509: tempFileStream = new FileInputStream(tempFile);
510:
511: dest.update(tempFileStream);
512:
513: DependencyTracker dependencyTracker = new DependencyTracker(
514: props);
515: dependencyTracker.copyFiles(srcInfo
516: .getDependencyTracker(), srcFile);
517: dependencyTracker.addFile(destFile);
518:
519: // FIXME: [2003-07-21 bloch] the factoring of cm.CacheInfo and cache.CacheInfo
520: // has never been right. Someday, if there are more subclasses of Cache that
521: // use the [gs]etMetaData methods, then this should really be fixed. (bug #1778).
522: CachedInfo info = new CachedInfo(dependencyTracker,
523: srcInfo.getCanvas(), enc);
524: dest.getInfo().setLastModified(destFile.lastModified());
525: dest.update(info);
526: dest.markClean();
527:
528: } finally {
529: if (tempFileStream != null) {
530: FileUtils.close(tempFileStream);
531: }
532: if (tempFile != null) {
533: tempFile.delete();
534: }
535: // Cleanup now
536: mLogger.debug(
537: /* (non-Javadoc)
538: * @i18n.test
539: * @org-mes="starting gc"
540: */
541: org.openlaszlo.i18n.LaszloMessages.getMessage(
542: CompilationManager.class.getName(),
543: "051018-713"));
544: System.gc();
545: mLogger.debug(
546: /* (non-Javadoc)
547: * @i18n.test
548: * @org-mes="finished gc"
549: */
550: org.openlaszlo.i18n.LaszloMessages.getMessage(
551: CompilationManager.class.getName(),
552: "051018-722"));
553: }
554:
555: } catch (IOException ioe) {
556: CompilationError e = new CompilationError(ioe);
557: e.initPathname(pathname);
558: throw e;
559: }
560: }
561:
562: /**
563: * @return true if the item is up to date
564: *
565: * @param pathname a <code>String</code> value. If
566: * <var>pathname</var> is relative, it is resolved relative to the
567: * CompilationManager's <var>sourceDirectory</var>.
568: * @param props properties that affect the compile.
569: * @return the bytes of the object file
570: * @exception CompilationError if an error occurs
571: *
572: * The PROPS parameter may contain
573: * <ul>
574: * <li>"Content-Encoding" => the encoding of the output (ignore if null)
575: * </ul>
576: * NOTE: can remove the recompile property from props
577: */
578: public boolean isItemUpToDate(Item item, String pathname,
579: Properties props) throws CompilationError {
580:
581: File sourceFile = new File(mSourceDirectory, pathname);
582: File objectFile = new File(item.getPathName());
583:
584: boolean recompile = props.getProperty(RECOMPILE) != null;
585: props.remove(RECOMPILE);
586:
587: boolean upToDate = false;
588:
589: try {
590: String recompileSwitch = mProperties.getProperty(
591: "recompile", "check").intern();
592:
593: if (pathname.endsWith(".lzo")) {
594: return true; // we cannot recompile a kranked file automatically
595: } else if (recompile) {
596: mLogger.info(
597: /* (non-Javadoc)
598: * @i18n.test
599: * @org-mes="forcing a recompile"
600: */
601: org.openlaszlo.i18n.LaszloMessages.getMessage(
602: CompilationManager.class.getName(),
603: "051018-774"));
604: return false;
605: } else if (recompileSwitch == "always") {
606: return false;
607: } else if (recompileSwitch == "never") {
608: return objectFile.exists();
609: } else if (recompileSwitch == "check") {
610: CachedInfo info = (CachedInfo) item.getMetaData();
611: DependencyTracker tracker = null;
612: if (info != null) {
613: tracker = info.getDependencyTracker();
614: }
615: // Set up the properties, for dependency checking
616: props = (Properties) props.clone();
617: return (tracker != null) && tracker.isUpToDate(props);
618: } else {
619: throw new IllegalArgumentException(
620: /* (non-Javadoc)
621: * @i18n.test
622: * @org-mes="invalid value for 'recompile' property"
623: */
624: org.openlaszlo.i18n.LaszloMessages.getMessage(
625: CompilationManager.class.getName(),
626: "051018-797"));
627: }
628: } catch (Exception ex) {
629: CompilationError e = new CompilationError(ex);
630: e.initPathname(sourceFile.getPath());
631: throw e;
632: }
633: }
634:
635: /**
636: * Compiles the file named by pathname and leaves the result
637: * in the cached item. If the file can't be compiled and
638: * debugCompilationErrors is true, returns an HTML document
639: * describing the error; otherwise, passes the exception.
640: *
641: * @param pathname a <code>String</code> value. If
642: * <var>pathname</var> is relative, it is resolved relative to the
643: * CompilationManager's <var>sourceDirectory</var>.
644: * @param props properties that affect the compile.
645: * @return the bytes of the object file
646: * @exception CompilationError if an error occurs
647: *
648: * The PROPS parameter may contain
649: * <ul>
650: * <li>"Content-Encoding" => the encoding of the output (ignore if null)
651: * </ul>
652: */
653: public synchronized void compileItem(Item item, String pathname,
654: Properties compilationProperties) throws CompilationError {
655: File sourceFile = new File(mSourceDirectory, pathname);
656: File objectFile = new File(item.getPathName());
657:
658: try {
659:
660: item.markDirty();
661:
662: org.openlaszlo.compiler.Compiler compiler = new org.openlaszlo.compiler.Compiler();
663: // For each property compiler.name=value, set a
664: // compiler property name=value (that is, without
665: // the "compiler." prefix)
666: for (java.util.Enumeration e = getProperties()
667: .propertyNames(); e.hasMoreElements();) {
668: String key = (String) e.nextElement();
669: if (key.startsWith("compiler.")) {
670: compiler.setProperty(key.substring("compiler."
671: .length()), getProperties()
672: .getProperty(key));
673: }
674: }
675: DependencyTracker dependencyTracker = new DependencyTracker(
676: compilationProperties);
677: TrackingFileResolver resolver = new TrackingFileResolver(
678: compiler.getFileResolver(), dependencyTracker);
679: dependencyTracker.addFile(sourceFile);
680: if (mLPSJarFile != null) {
681: // TODO [bshine 10.19.06] mLPSJarFile doesn't seem to
682: // have the correct value. It doesn't point to a real
683: // jar.
684: dependencyTracker.addFile(mLPSJarFile);
685: }
686: compiler.setFileResolver(resolver);
687: compiler.setMediaCache(mMediaCache);
688:
689: Canvas canvas = null;
690:
691: File tempFile = null;
692: FileInputStream tempFileStream = null;
693: try {
694: tempFile = File.createTempFile("lzc-", null);
695: mLogger.debug(
696: /* (non-Javadoc)
697: * @i18n.test
698: * @org-mes="Temporary file is " + p[0]
699: */
700: org.openlaszlo.i18n.LaszloMessages.getMessage(
701: CompilationManager.class.getName(),
702: "051018-663", new Object[] { tempFile
703: .getAbsolutePath() }));
704:
705: mLogger.debug(
706: /* (non-Javadoc)
707: * @i18n.test
708: * @org-mes="Compiling " + p[0] + " to " + p[1]
709: */
710: org.openlaszlo.i18n.LaszloMessages.getMessage(
711: CompilationManager.class.getName(),
712: "051018-883", new Object[] {
713: sourceFile.toString(),
714: tempFile.toString() }));
715: canvas = compiler.compile(sourceFile, tempFile,
716: compilationProperties);
717:
718: mLogger.debug(
719: /* (non-Javadoc)
720: * @i18n.test
721: * @org-mes="Canvas size is " + p[0] + " by " + p[1]
722: */
723: org.openlaszlo.i18n.LaszloMessages.getMessage(
724: CompilationManager.class.getName(),
725: "051018-894", new Object[] {
726: new Integer(canvas.getWidth()),
727: new Integer(canvas.getHeight()) }));
728:
729: tempFileStream = new FileInputStream(tempFile);
730:
731: item.update(tempFileStream);
732: dependencyTracker.addFile(objectFile);
733: String encoding = compilationProperties
734: .getProperty(LZHttpUtils.CONTENT_ENCODING);
735: // FIXME: [2003-07-21 bloch] the factoring of cm.CacheInfo and cache.CacheInfo
736: // has never been right. Someday, if there are more subclasses of Cache that
737: // use the [gs]etMetaData methods, then this should really be fixed. (bug #1778).
738: CachedInfo info = new CachedInfo(dependencyTracker,
739: canvas, encoding);
740: item.getInfo().setLastModified(
741: objectFile.lastModified());
742: item.update(info);
743: item.markClean();
744:
745: // Add security options for this application after compilation
746: LPS.configuration.setApplicationOptions(FileUtils
747: .relativePath(pathname, LPS.HOME()), canvas
748: .getSecurityOptions());
749:
750: } finally {
751: if (tempFileStream != null) {
752: FileUtils.close(tempFileStream);
753: }
754: if (tempFile != null) {
755: tempFile.delete();
756: }
757:
758: // Cleanup now
759: mLogger.debug(
760: /* (non-Javadoc)
761: * @i18n.test
762: * @org-mes="starting gc"
763: */
764: org.openlaszlo.i18n.LaszloMessages.getMessage(
765: CompilationManager.class.getName(),
766: "051018-713"));
767: System.gc();
768: mLogger.debug(
769: /* (non-Javadoc)
770: * @i18n.test
771: * @org-mes="finished gc"
772: */
773: org.openlaszlo.i18n.LaszloMessages.getMessage(
774: CompilationManager.class.getName(),
775: "051018-722"));
776: }
777: } catch (IOException ioe) {
778: CompilationError e = new CompilationError(ioe);
779: e.initPathname(sourceFile.getPath());
780: throw e;
781: }
782: }
783:
784: /**
785: * @return a cache key for the given compile
786: */
787: static Serializable computeKey(String pathname, Properties props) {
788:
789: TreeSet sorted = new TreeSet();
790: for (java.util.Enumeration e = props.propertyNames(); e
791: .hasMoreElements();) {
792: String key = (String) e.nextElement();
793: // Omit the recompile property
794: if (key.equalsIgnoreCase(RECOMPILE))
795: continue;
796:
797: String value = props.getProperty(key);
798: StringBuffer buf = new StringBuffer();
799: buf.append(key);
800: buf.append(' ');
801: buf.append(value);
802:
803: sorted.add(buf.toString());
804: }
805:
806: StringBuffer buf = new StringBuffer(FileUtils.relativePath(
807: pathname, LPS.HOME()));
808:
809: buf.append(' ');
810: for (java.util.Iterator e = sorted.iterator(); e.hasNext();) {
811: String str = (String) e.next();
812: buf.append(str);
813: }
814:
815: mLogger.debug(
816: /* (non-Javadoc)
817: * @i18n.test
818: * @org-mes="computeKey: " + p[0]
819: */
820: org.openlaszlo.i18n.LaszloMessages.getMessage(
821: CompilationManager.class.getName(), "051018-984",
822: new Object[] { buf.toString() }));
823: return buf.toString();
824: }
825:
826: /** Given the pathname of a file, return a file name
827: * for an "source" version of this file that lives in the cache.
828: * @param srcName path to file in source directory
829: * @param webappPath real path for web application
830: * @return a File
831: */
832: public File getCacheSourcePath(String srcName, String webappPath) {
833: // todo: relative to src directory
834: File src = new File(srcName);
835: if (src.getParentFile() != null) {
836: String baseParent = src.getParentFile().getAbsolutePath();
837: if (baseParent != null
838: && baseParent.startsWith(mCacheDirectory
839: .getAbsolutePath())) {
840: return new File(srcName);
841: }
842: }
843: // FIXME: [2003-01-21 pkang] webapp real path should match first part of
844: // source name. CompilationManager and LZServlet will need overhaul to
845: // support reading directly from war file.
846: if (webappPath == null || !srcName.startsWith(webappPath))
847: throw new RuntimeException(
848: /* (non-Javadoc)
849: * @i18n.test
850: * @org-mes=p[0] + " doesn't begin with " + p[1]
851: */
852: org.openlaszlo.i18n.LaszloMessages.getMessage(
853: CompilationManager.class.getName(), "051018-1015",
854: new Object[] { srcName, webappPath }));
855: String fixedName = srcName.substring(webappPath.length());
856: return new File(mCacheDirectory, fixedName);
857: }
858: }
859:
860: /** A FileResolver that tracks dependencies.
861: *
862: * @author Oliver Steele
863: */
864: class TrackingFileResolver implements FileResolver {
865: private final FileResolver mBaseResolver;
866: private final DependencyTracker mDependencies;
867:
868: TrackingFileResolver(FileResolver baseResolver,
869: DependencyTracker tracker) {
870: this .mBaseResolver = baseResolver;
871: this .mDependencies = tracker;
872: }
873:
874: public Set getBinaryIncludes() {
875: return mBaseResolver.getBinaryIncludes();
876: }
877:
878: /**
879: * Implement the FileResolver interface.
880: *
881: * @param pathname a <code>String</code> value
882: * @param base a <code>String</code> value
883: * @param asLibrary a <code>boolean</code> value
884: * @return a <code>File</code> value
885: * @exception FileNotFoundException if an error occurs
886: */
887: public File resolve(String pathname, String base, boolean asLibrary)
888: throws FileNotFoundException {
889: File file = mBaseResolver.resolve(pathname, base, asLibrary);
890: mDependencies.addFile(file);
891: return file;
892: }
893:
894: public File resolve(CompilationEnvironment env, String pathname,
895: String base, boolean asLibrary)
896: throws FileNotFoundException {
897: File file = mBaseResolver.resolve(env, pathname, base,
898: asLibrary);
899: mDependencies.addFile(file);
900: return file;
901: }
902:
903: /** For debugging. */
904: /*void writeResults() {
905: System.out.println("depends on");
906: for (java.util.Iterator e = mDependencies.iterator();
907: e.hasNext(); ) {
908: FileInfo fi = (FileInfo) e.next();
909: System.out.println(fi.mPathname + " -> " + fi.mChecksum);
910: }
911: }*/
912: }
|