diff options
-rw-r--r-- | src/java/com/jogamp/common/util/IOUtil.java | 53 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempFileCache.java | 288 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempJarCache.java | 4 |
3 files changed, 211 insertions, 134 deletions
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java index 1f0b2ed..1e81c8e 100644 --- a/src/java/com/jogamp/common/util/IOUtil.java +++ b/src/java/com/jogamp/common/util/IOUtil.java @@ -532,4 +532,57 @@ public class IOUtil { } return tmpRoot; } + + /** + * This methods finds [and creates] a temporary directory: + * <pre> + * for(tempBaseDir = tempRootDir + tmpDirPrefix + _ + [000000-999999]) { + * if(tempBaseDir.isDirectory()) { + * if(tempBaseDir.canWrite()) { + * return tempBaseDir; + * } + * } else { + * tempBaseDir.mkdir(); + * return tempBaseDir; + * } + * } + * </pre> + * The <code>tempRootDir</code> is retrieved by {@link #getTempRoot()}. + * <p> + * The iteration through [000000-999999] ensures that the code is multi-user save. + * </p> + * @param tmpDirPrefix + * @return a temporary directory, writable by this user + * @throws IOException + * @throws SecurityException + */ + public static File getTempDir(String tmpDirPrefix) + throws IOException, SecurityException + { + final File tempRoot = IOUtil.getTempRoot(); + + for(int i = 0; i<=999999; i++) { + final String tmpDirSuffix = String.format("_%06d", i); // 6 digits for iteration + final File tmpBaseDir = new File(tempRoot, tmpDirPrefix+tmpDirSuffix); + if (tmpBaseDir.isDirectory()) { + // existing directory + if(tmpBaseDir.canWrite()) { + // can write - OK + return tmpBaseDir; + } + // not writable, hence used by another user - continue + } else { + // non existing directory, create and validate it + tmpBaseDir.mkdir(); + if (!tmpBaseDir.isDirectory()) { + throw new IOException("Cannot create temp base directory " + tmpBaseDir); + } + if(!tmpBaseDir.canWrite()) { + throw new IOException("Cannot write to created temp base directory " + tmpBaseDir); + } + return tmpBaseDir; // created and writable - OK + } + } + throw new IOException("Could not create temp directory @ "+tempRoot.getAbsolutePath()+tmpDirPrefix+"_*"); + } } diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index 9c2556d..4210a24 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -41,26 +41,27 @@ import jogamp.common.Debug; public class TempFileCache { private static final boolean DEBUG = Debug.debug("TempFileCache"); - // Lifecycle: For all JVMs, ClassLoader and times. + // Flag indicating that we got a fatal error in the static initializer. + private static boolean staticInitError = false; + private static final String tmpDirPrefix = "jogamp.tmp.cache"; + // Lifecycle: For one user's JVMs, ClassLoader and time. + private static final File tmpBaseDir; + // Get the value of the tmproot system property - // Lifecycle: For all JVMs and ClassLoader + // Lifecycle: For one user's concurrently running JVMs and ClassLoader /* package */ static final String tmpRootPropName = "jnlp.jogamp.tmp.cache.root"; - // Flag indicating that we got a fatal error in the static initializer. - private static boolean staticInitError = false; - - private static File tmpBaseDir; - // String representing the name of the temp root directory relative to the // tmpBaseDir. Its value is "jlnNNNNN", which is the unique filename created // by File.createTempFile() without the ".tmp" extension. // - // Lifecycle: For all JVMs and ClassLoader + // Lifecycle: For one user's concurrently running JVMs and ClassLoader // private static String tmpRootPropValue; + // Lifecycle: For one user's concurrently running JVMs and ClassLoader private static File tmpRootDir; // Flag indicating that we got a fatal error in the initializer. @@ -69,15 +70,39 @@ public class TempFileCache { private File individualTmpDir; static { - // Create / initialize the temp root directory, starting the Reaper - // thread to reclaim old installations if necessary. If we get an - // exception, set an error code. - try { - initTmpRoot(); - } catch (Exception ex) { - ex.printStackTrace(); - staticInitError = true; - } + // Global Lock ! + synchronized (System.out) { + // Create / initialize the temp root directory, starting the Reaper + // thread to reclaim old installations if necessary. If we get an + // exception, set an error code. + File _tmpBaseDir = null; + try { + _tmpBaseDir = IOUtil.getTempDir(tmpDirPrefix); // Retrieve the tmpbase directory. + } catch (Exception ex) { + System.err.println("Warning: Catched Exception while retrieving temp base directory:"); + ex.printStackTrace(); + staticInitError = true; + } + tmpBaseDir = _tmpBaseDir; + + if (DEBUG) { + System.err.println("TempFileCache: Static Initialization ---------------------------------------------- OK: "+(!staticInitError)); + System.err.println("TempFileCache: Thread: "+Thread.currentThread().getName()+", CL 0x"+Integer.toHexString(TempFileCache.class.getClassLoader().hashCode())+", tempBaseDir "+tmpBaseDir.getAbsolutePath()); + } + + if(!staticInitError) { + try { + initTmpRoot(); + } catch (Exception ex) { + System.err.println("Warning: Catched Exception due to initializing TmpRoot:"); + ex.printStackTrace(); + staticInitError = true; + } + } + if (DEBUG) { + System.err.println("------------------------------------------------------------------ OK: "+(!staticInitError)); + } + } } /** @@ -96,8 +121,9 @@ public class TempFileCache { * 1. Synchronize on a global lock. Note that for this purpose we will * use System.out in the absence of a true global lock facility. * We are careful not to hold this lock too long. + * The global lock is claimed in the static initializer block, calling this method! * - * 2. Check for the existence of the "jnlp.applet.launcher.tmproot" + * 2. Check for the existence of the "jnlp.jogamp.tmp.cache.root" * system property. * * a. If set, then some other thread in a different ClassLoader has @@ -125,7 +151,7 @@ public class TempFileCache { * by that active process. This guarantee lets us avoid reaping an * active process' files. * - * 4. Set the "jnlp.applet.launcher.tmproot" system property. + * 4. Set the "jnlp.jogamp.tmp.cache.root" system property. * * 5. Add a shutdown hook to cleanup jlnNNNN.lck and jlnNNNN.tmp. We * don't actually expect that this shutdown hook will ever be called, @@ -137,117 +163,101 @@ public class TempFileCache { * 6. Start the Reaper thread to cleanup old installations. */ private static void initTmpRoot() throws IOException { - if (DEBUG) { - System.err.println("TempFileCache: Static Initialization ----------------------------------------------"); - System.err.println("TempFileCache: Thread: "+Thread.currentThread().getName()+", CL 0x"+Integer.toHexString(TempFileCache.class.getClassLoader().hashCode())); - } + tmpRootPropValue = System.getProperty(tmpRootPropName); - synchronized (System.out) { - // Get the name of the tmpbase directory. - tmpBaseDir = new File(IOUtil.getTempRoot(), tmpDirPrefix); - - tmpRootPropValue = System.getProperty(tmpRootPropName); - - if (tmpRootPropValue == null) { - // Create the tmpbase directory if it doesn't already exist - tmpBaseDir.mkdir(); - if (!tmpBaseDir.isDirectory()) { - throw new IOException("Cannot create directory " + tmpBaseDir); - } - - // Create ${tmpbase}/jlnNNNN.tmp then lock the file - File tmpFile = File.createTempFile("jln", ".tmp", tmpBaseDir); - if (DEBUG) { - System.err.println("TempFileCache: tmpFile = " + tmpFile.getAbsolutePath()); - } - final FileOutputStream tmpOut = new FileOutputStream(tmpFile); - final FileChannel tmpChannel = tmpOut.getChannel(); - final FileLock tmpLock = tmpChannel.lock(); - - // Strip off the ".tmp" to get the name of the tmprootdir - String tmpFileName = tmpFile.getAbsolutePath(); - String tmpRootName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".tmp")); - - // create ${tmpbase}/jlnNNNN.lck then lock the file - String lckFileName = tmpRootName + ".lck"; - File lckFile = new File(lckFileName); - if (DEBUG) { - System.err.println("TempFileCache: lckFile = " + lckFile.getAbsolutePath()); - } - lckFile.createNewFile(); - final FileOutputStream lckOut = new FileOutputStream(lckFile); - final FileChannel lckChannel = lckOut.getChannel(); - final FileLock lckLock = lckChannel.lock(); - - // Create tmprootdir - tmpRootDir = new File(tmpRootName); - if (DEBUG) { - System.err.println("TempFileCache: tmpRootDir = " + tmpRootDir.getAbsolutePath()); - } - if (!tmpRootDir.mkdir()) { - throw new IOException("Cannot create " + tmpRootDir); - } + if (tmpRootPropValue == null) { + // Create ${tmpbase}/jlnNNNN.tmp then lock the file + File tmpFile = File.createTempFile("jln", ".tmp", tmpBaseDir); + if (DEBUG) { + System.err.println("TempFileCache: tmpFile = " + tmpFile.getAbsolutePath()); + } + final FileOutputStream tmpOut = new FileOutputStream(tmpFile); + final FileChannel tmpChannel = tmpOut.getChannel(); + final FileLock tmpLock = tmpChannel.lock(); + + // Strip off the ".tmp" to get the name of the tmprootdir + String tmpFileName = tmpFile.getAbsolutePath(); + String tmpRootName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".tmp")); + + // create ${tmpbase}/jlnNNNN.lck then lock the file + String lckFileName = tmpRootName + ".lck"; + File lckFile = new File(lckFileName); + if (DEBUG) { + System.err.println("TempFileCache: lckFile = " + lckFile.getAbsolutePath()); + } + lckFile.createNewFile(); + final FileOutputStream lckOut = new FileOutputStream(lckFile); + final FileChannel lckChannel = lckOut.getChannel(); + final FileLock lckLock = lckChannel.lock(); + + // Create tmprootdir + tmpRootDir = new File(tmpRootName); + if (DEBUG) { + System.err.println("TempFileCache: tmpRootDir = " + tmpRootDir.getAbsolutePath()); + } + if (!tmpRootDir.mkdir()) { + throw new IOException("Cannot create " + tmpRootDir); + } - // Add shutdown hook to cleanup the OutputStream, FileChannel, - // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. - // We do this so that the locks never get garbage-collected. - Runtime.getRuntime().addShutdownHook(new Thread() { - /* @Override */ - public void run() { - // NOTE: we don't really expect that this code will ever - // be called. If it does, we will close the output - // stream, which will in turn close the channel. - // We will then release the lock. - try { - tmpOut.close(); - tmpLock.release(); - lckOut.close(); - lckLock.release(); - } catch (IOException ex) { - // Do nothing - } + // Add shutdown hook to cleanup the OutputStream, FileChannel, + // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. + // We do this so that the locks never get garbage-collected. + Runtime.getRuntime().addShutdownHook(new Thread() { + /* @Override */ + public void run() { + // NOTE: we don't really expect that this code will ever + // be called. If it does, we will close the output + // stream, which will in turn close the channel. + // We will then release the lock. + try { + tmpOut.close(); + tmpLock.release(); + lckOut.close(); + lckLock.release(); + } catch (IOException ex) { + // Do nothing } - }); - - // Set the system property... - tmpRootPropValue = tmpRootName.substring(tmpRootName.lastIndexOf(File.separator) + 1); - System.setProperty(tmpRootPropName, tmpRootPropValue); - if (DEBUG) { - System.err.println("TempFileCache: Setting " + tmpRootPropName + "=" + tmpRootPropValue); } + }); - // Start a new Reaper thread to do stuff... - Thread reaperThread = new Thread() { - /* @Override */ - public void run() { - deleteOldTempDirs(); - } - }; - reaperThread.setName("TempFileCache-Reaper"); - reaperThread.start(); - } else { - // Make sure that the property is not set to an illegal value - if (tmpRootPropValue.indexOf('/') >= 0 || - tmpRootPropValue.indexOf(File.separatorChar) >= 0) { - throw new IOException("Illegal value of: " + tmpRootPropName); - } + // Set the system property... + tmpRootPropValue = tmpRootName.substring(tmpRootName.lastIndexOf(File.separator) + 1); + System.setProperty(tmpRootPropName, tmpRootPropValue); + if (DEBUG) { + System.err.println("TempFileCache: Setting " + tmpRootPropName + "=" + tmpRootPropValue); + } - // Set tmpRootDir = ${tmpbase}/${jnlp.applet.launcher.tmproot} - if (DEBUG) { - System.err.println("TempFileCache: Using existing value of: " + - tmpRootPropName + "=" + tmpRootPropValue); - } - tmpRootDir = new File(tmpBaseDir, tmpRootPropValue); - if (DEBUG) { - System.err.println("TempFileCache: tmpRootDir = " + tmpRootDir.getAbsolutePath()); - } - if (!tmpRootDir.isDirectory()) { - throw new IOException("Cannot access " + tmpRootDir); + // Start a new Reaper thread to do stuff... + Thread reaperThread = new Thread() { + /* @Override */ + public void run() { + deleteOldTempDirs(); } + }; + reaperThread.setName("TempFileCache-Reaper"); + reaperThread.start(); + } else { + // Make sure that the property is not set to an illegal value + if (tmpRootPropValue.indexOf('/') >= 0 || + tmpRootPropValue.indexOf(File.separatorChar) >= 0) { + throw new IOException("Illegal value of: " + tmpRootPropName); + } + + // Set tmpRootDir = ${tmpbase}/${jnlp.applet.launcher.tmproot} + if (DEBUG) { + System.err.println("TempFileCache: Using existing value of: " + + tmpRootPropName + "=" + tmpRootPropValue); + } + tmpRootDir = new File(tmpBaseDir, tmpRootPropValue); + if (DEBUG) { + System.err.println("TempFileCache: tmpRootDir = " + tmpRootDir.getAbsolutePath()); + } + if (!tmpRootDir.isDirectory()) { + throw new IOException("Temp root directory does not exist: " + tmpRootDir.getAbsolutePath()); + } + if (!tmpRootDir.canWrite()) { + throw new IOException("Temp root directory is not writable: " + tmpRootDir.getAbsolutePath()); } - } - if (DEBUG) { - System.err.println("------------------------------------------------------------------ (static ok: "+(!staticInitError)+")"); } } @@ -412,13 +422,16 @@ public class TempFileCache { public boolean isValid() { return !staticInitError && !initError; } /** - * Base temp directory used by TempFileCache. - * Lifecycle: For all JVMs, ClassLoader and times. + * Base temp directory used by TempFileCache. + * + * <p> + * Lifecycle: For one user's JVMs, ClassLoader and time. + * </p> * * This is set to: - * - * ${java.io.tmpdir}/<tmpDirPrefix> - * + * <pre> + * ${java.io.tmpdir}/tmpDirPrefix + * </pre> * * @return */ @@ -428,14 +441,22 @@ public class TempFileCache { * Root temp directory for this JVM instance. Used to store individual * directories. * - * Lifecycle: For all JVMs and ClassLoader + * <p> + * Lifecycle: For one user's concurrently running JVMs and ClassLoader + * </p> * - * <tmpBaseDir>/<tmpRootPropValue> + * <pre> + * tmpBaseDir/tmpRootPropValue + * </pre> * - * Use Case: Per ClassLoader files, eg. native libraries. + * <p> + * Use Case: Per ClassLoader files, eg. native libraries. + * </p> * + * <p> * Old temp directories are cleaned up the next time a JVM is launched that * uses TempFileCache. + * </p> * * * @return @@ -446,9 +467,13 @@ public class TempFileCache { * Temporary directory for individual files (eg. native libraries of one ClassLoader instance). * The directory name is: * + * <p> * Lifecycle: Within each JVM .. use case dependent, ie. per ClassLoader + * </p> * - * <tmpRootDir>/jlnMMMMM + * <pre> + * tmpRootDir/jlnMMMMM + * </pre> * * where jlnMMMMM is the unique filename created by File.createTempFile() * without the ".tmp" extension. @@ -467,7 +492,6 @@ public class TempFileCache { * We avoid deleteOnExit, because it doesn't work reliably. */ private void createTmpDir() throws IOException { - File tmpFile = File.createTempFile("jln", ".tmp", tmpRootDir); String tmpFileName = tmpFile.getAbsolutePath(); String tmpDirName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".tmp")); diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index 71a12e7..2286d6c 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -99,10 +99,10 @@ public class TempJarCache { /** * - * @return true if this class has been initialized, ie. used, otherwise not. + * @return true if this class has been properly initialized, ie. is in use, otherwise false. */ public static boolean isInitialized() { - return isInit; + return isInit && !staticInitError; } /* package */ static void checkInitialized() { |