Skip to content

Commit

Permalink
Fixed possible thread blocking issue
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkmc committed Aug 6, 2010
1 parent 385b4f7 commit 1eefa96
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 19 deletions.
80 changes: 61 additions & 19 deletions app/press/Compressor.java
Expand Up @@ -278,11 +278,29 @@ protected static VirtualFile getCompressedFile(FileCompressor compressor,
}
}

//
// We create a temp file to which the output will be written to first,
// and then rename it to the target file name (because compression can
// take a while)
// If the temp file is already being written by another thread, this
// method will block until it is complete and then return null
//
File tmp = getTmpOutputFile(file);
if (tmp == null) {
PressLogger.trace("Compressed file was generated by another thread");
} else {
writeCompressedFile(compressor, componentFiles, file, tmp);
}

return file;
}

private static void writeCompressedFile(FileCompressor compressor,
List<FileInfo> componentFiles, VirtualFile file, File tmp) {
Writer out = null;
try {
// Compress the component files and write the output to a temporary
// file
File tmp = createTempFile(file);
// Compress the component files and write the output to the
// temporary file
out = new BufferedWriter(new FileWriter(tmp));

// Add the last modified dates of each component file to the start
Expand All @@ -300,7 +318,7 @@ protected static VirtualFile getCompressedFile(FileCompressor compressor,
.getRealFile().getName(), (timeAfter - timeStart));

// Once the compressed output has been written to the temporary
// file, move it to the cache directory.
// file, rename it to overwrite the original file.
String msg = "Output written to temporary file\n%s\n";
msg += "Moving from tmp path to final path:\n%s";
String tmpPath = tmp.getAbsolutePath();
Expand All @@ -326,8 +344,46 @@ protected static VirtualFile getCompressedFile(FileCompressor compressor,
}
}
}
}

return file;
private static File getTmpOutputFile(VirtualFile file) {
String origPath = file.getRealFile().getAbsolutePath();
File tmp = new File(origPath + ".tmp");

// If the temp file already exists
if (tmp.exists()) {
long tmpLastModified = tmp.lastModified();
long now = System.currentTimeMillis();

// If the temp file is older than the destination file, or if it is
// older than the allowed compression time, it must be a remnant of
// a previous server crash so we can overwrite it
if (tmpLastModified < file.lastModified()) {
return tmp;
}
if (now - tmpLastModified > PluginConfig.maxCompressionTimeMillis) {
return tmp;
}

// Otherwise it must be currently being written by another thread,
// so wait for it to finish
while (tmp.exists()) {
if (System.currentTimeMillis() - now > PluginConfig.maxCompressionTimeMillis) {
throw new PressException("Timeout waiting for compressed file to be generated");
}

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}

// Return null to indicate that the file was already generated by
// another thread
return null;
}

return tmp;
}

public static List<File> clearCache(String compressedDir, String extension) {
Expand Down Expand Up @@ -554,20 +610,6 @@ private static VirtualFile getVirtualFile(String filePath) {
return VirtualFile.open(Play.getFile(filePath));
}

/**
* Creates a temporary version of a file that will later be copied over the
* original
*/
private static File createTempFile(VirtualFile original) {
String origPath = original.getRealFile().getAbsolutePath();
File tmpFile = new File(origPath + ".tmp");
for (int i = 1; tmpFile.exists(); i++) {
tmpFile = new File(origPath + ".tmp");
}

return tmpFile;
}

protected static class FileInfo {
String fileName;
boolean compress;
Expand Down
7 changes: 7 additions & 0 deletions app/press/PluginConfig.java
Expand Up @@ -25,6 +25,10 @@ public static class DefaultConfig {
// less than a second)
public static final String compressionKeyStorageTime = "2mn";

// The maximum amount of time in milli-seconds allowed for compression
// to occur before a timeout exception is thrown.
public static final int maxCompressionTimeMillis = 60000;

public static class js {
// The directory where source javascript files are read from
public static final String srcDir = "/public/javascripts/";
Expand Down Expand Up @@ -56,6 +60,7 @@ public static class css {
public static CachingStrategy cache;
public static boolean cacheClearEnabled;
public static String compressionKeyStorageTime;
public static int maxCompressionTimeMillis;

public static class js {
public static String srcDir = DefaultConfig.js.srcDir;
Expand Down Expand Up @@ -90,6 +95,8 @@ public static void readConfig() {
enabled = ConfigHelper.getBoolean("press.enabled", DefaultConfig.enabled);
compressionKeyStorageTime = ConfigHelper.getString("press.key.lifetime",
DefaultConfig.compressionKeyStorageTime);
maxCompressionTimeMillis = ConfigHelper.getInt("press.compression.maxTimeMillis",
DefaultConfig.maxCompressionTimeMillis);

css.srcDir = ConfigHelper.getString("press.css.sourceDir", DefaultConfig.css.srcDir);
css.compressedDir = ConfigHelper.getString("press.css.outputDir",
Expand Down
6 changes: 6 additions & 0 deletions documentation/manual/home.textile
Expand Up @@ -148,6 +148,12 @@ When the **#{press.compressed-script}** or **#{press.compressed-stylesheet}** ta
**press.key.lifetime=2mn**


h3. __press.compression.maxTimeMillis__

The maximum amount of time in milli-seconds that compression is allowed to take before a timeout exception is thrown.
**press.compression.maxTimeMillis=60000**


h3. __press.js.sourceDir__

The source directory for javascript files, relative to the application root
Expand Down

0 comments on commit 1eefa96

Please sign in to comment.