File.createTempFile() sometimes hangs on the build system while trying to get an exclusive lock on a temp file.  This patch uses a custom method to create a temp file.

Patch by: jlabanca
Review by: fabbott



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6023 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java
index 01fe650..fa4b01b 100644
--- a/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java
+++ b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/LatestTimeJar.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 Google Inc.
- *
+ * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- *
+ * 
  * http://www.apache.org/licenses/LICENSE-2.0
- *
+ * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -28,12 +28,13 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.Random;
 import java.util.TreeMap;
 
 /**
  * A variation on Jar which handles duplicate entries by only archiving the most
  * recent of any given path. This is done by keeping a map of paths (as shown in
- * the jar file) against {@link #EntryInfo} objects identifying the input source
+ * the jar file) against {@link EntryInfo} objects identifying the input source
  * and its timestamp. Most of the actual archiving is deferred until archive
  * finalization, when we've decided on the actual de-duplicated set of entries.
  */
@@ -54,7 +55,7 @@
 
     /**
      * Called to actually add the entry to a given zip stream.
-     *
+     * 
      * @param out
      * @param path
      * @throws IOException
@@ -101,7 +102,7 @@
     public FileEntryInfo(InputStream in, long lastModified, File fromArchive,
         int mode) throws IOException {
       super(lastModified, mode);
-      tmpFile = File.createTempFile("gwtjar", "");
+      tmpFile = createTempFile("gwtjar", "");
       tmpFile.deleteOnExit();
       OutputStream fos = new FileOutputStream(tmpFile);
       int readLen = in.read(buffer);
@@ -125,6 +126,54 @@
     }
   }
 
+  /**
+   * Used to generate temporary file names.
+   */
+  private static long counter = -1;
+
+  /**
+   * Creates a temporary file.
+   * 
+   * @param prefix the file prefix
+   * @param suffix the file suffix
+   * @return the new file
+   * @throws IOException if the file cannot be created
+   */
+  private static File createTempFile(String prefix, String suffix)
+      throws IOException {
+    if (suffix == null) {
+      suffix = ".tmp";
+    }
+
+    // Get the temp file directory.
+    File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+    tmpDir.mkdirs();
+
+    // Generate a random name.
+    if (counter == -1) {
+      counter = new Random().nextLong();
+    }
+    boolean created = false;
+    File tmpFile;
+    do {
+      counter++;
+      tmpFile = new File(tmpDir, prefix + Long.toString(counter) + suffix);
+      if (!tmpFile.exists()) {
+        created = tmpFile.createNewFile();
+        if (!created) {
+          // If we fail the create the temp file, it must have been created by
+          // another thread between lines 161 and 162.  We re-seed to avoid
+          // further race conditions.
+          counter = new Random().nextLong();
+        }
+      }
+    } while (!created);
+
+    // Create the file.
+    tmpFile.createNewFile();
+    return tmpFile;
+  }
+
   private byte buffer[] = new byte[16 * 1024];
   private Map<String, EntryInfo> paths = new TreeMap<String, EntryInfo>();
 
@@ -188,10 +237,10 @@
   /**
    * Checks whether an entry should be replaced, by touch dates and duplicates
    * setting.
-   *
+   * 
    * @param path the path of an entry being considered
    * @param touchTime the lastModified of the candiate replacement
-   * @return
+   * @return true if the file should be replaced
    */
   private boolean shouldReplace(String path, long touchTime) {
     EntryInfo oldInfo = paths.get(path);