diff options
Diffstat (limited to 'netx')
-rw-r--r-- | netx/net/sourceforge/jnlp/cache/CacheEntry.java | 20 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java | 245 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/cache/CacheUtil.java | 200 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/cache/ResourceTracker.java | 115 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java | 4 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java | 1 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/util/FileUtils.java | 42 | ||||
-rw-r--r-- | netx/net/sourceforge/jnlp/util/XDesktopEntry.java | 2 |
8 files changed, 573 insertions, 56 deletions
diff --git a/netx/net/sourceforge/jnlp/cache/CacheEntry.java b/netx/net/sourceforge/jnlp/cache/CacheEntry.java index affb5fb..8d70da6 100644 --- a/netx/net/sourceforge/jnlp/cache/CacheEntry.java +++ b/netx/net/sourceforge/jnlp/cache/CacheEntry.java @@ -162,4 +162,24 @@ public class CacheEntry { properties.store(); } + /** + * Mark this entry for deletion at shutdown. + */ + public void markForDelete() { // once marked it should not be unmarked. + properties.setProperty("delete", Boolean.toString(true)); + } + + /** + * Lock cache item. + */ + protected void lock() { + CacheUtil.lockFile(properties); + } + + /** + * Unlock cache item. + */ + protected void unlock() { + CacheUtil.unlockFile(properties); + } } diff --git a/netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java b/netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java new file mode 100644 index 0000000..2a91456 --- /dev/null +++ b/netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java @@ -0,0 +1,245 @@ +/* CacheLRUWrapper -- Handle LRU for cache files. + Copyright (C) 2011 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + */ +package net.sourceforge.jnlp.cache; + +import java.io.File; +import java.io.IOException; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; + +import net.sourceforge.jnlp.config.DeploymentConfiguration; +import net.sourceforge.jnlp.runtime.JNLPRuntime; +import net.sourceforge.jnlp.util.FileUtils; +import net.sourceforge.jnlp.util.PropertiesFile; + +/** + * This class helps maintain the ordering of most recently use items across + * multiple jvm instances. + * + * @author Andrew Su ([email protected], [email protected]) + * + */ +enum CacheLRUWrapper { + INSTANCE; + + private int lockCount = 0; + + /* lock for the file RecentlyUsed */ + private FileLock fl = null; + + /* location of cache directory */ + private final String cacheDir = new File(JNLPRuntime.getConfiguration() + .getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR)).getPath(); + + /* + * back-end of how LRU is implemented This file is to keep track of the most + * recently used items. The items are to be kept with key = (current time + * accessed) followed by folder of item. value = path to file. + */ + private PropertiesFile cacheOrder = new PropertiesFile( + new File(cacheDir + File.separator + "recently_used")); + + /** + * Returns an instance of the policy. + * + * @param propertiesFile + * @return + */ + public static CacheLRUWrapper getInstance() { + return INSTANCE; + } + + /** + * Update map for keeping track of recently used items. + */ + public synchronized void load() { + cacheOrder.load(); + } + + /** + * Write file to disk. + */ + public synchronized void store() { + cacheOrder.store(); + } + + /** + * This adds a new entry to file. + * + * @param key key we want path to be associated with. + * @param path path to cache item. + * @return true if we successfully added to map, false otherwise. + */ + public synchronized boolean addEntry(String key, String path) { + if (cacheOrder.containsKey(key)) return false; + cacheOrder.setProperty(key, path); + return true; + } + + /** + * This removed an entry from our map. + * + * @param key key we want to remove. + * @return true if we successfully removed key from map, false otherwise. + */ + public synchronized boolean removeEntry(String key) { + if (!cacheOrder.containsKey(key)) return false; + cacheOrder.remove(key); + return true; + } + + private String getIdForCacheFolder(String folder) { + int len = cacheDir.length(); + int index = folder.indexOf(File.separatorChar, len + 1); + return folder.substring(len + 1, index); + } + + /** + * This updates the given key to reflect it was recently accessed. + * + * @param oldKey Key we wish to update. + * @return true if we successfully updated value, false otherwise. + */ + public synchronized boolean updateEntry(String oldKey) { + if (!cacheOrder.containsKey(oldKey)) return false; + String value = cacheOrder.getProperty(oldKey); + String folder = getIdForCacheFolder(value); + + cacheOrder.remove(oldKey); + cacheOrder.setProperty(Long.toString(System.currentTimeMillis()) + "," + folder, value); + return true; + } + + /** + * Return a copy of the keys available. + * + * @return List of Strings sorted by ascending order. + */ + public synchronized List<Entry<String, String>> getLRUSortedEntries() { + ArrayList<Entry<String, String>> entries = new ArrayList(cacheOrder.entrySet()); + // sort by keys in descending order. + Collections.sort(entries, new Comparator<Entry<String, String>>() { + @Override + public int compare(Entry<String, String> e1, Entry<String, String> e2) { + try { + Long t1 = Long.parseLong(e1.getKey().split(",")[0]); + Long t2 = Long.parseLong(e2.getKey().split(",")[0]); + + int c = t1.compareTo(t2); + return c < 0 ? 1 : (c > 0 ? -1 : 0); + } catch (NumberFormatException e) { + // Perhaps an error is too harsh. Maybe just somehow turn + // caching off if this is the case. + throw new InternalError("Corrupt LRU file entries"); + } + } + }); + return entries; + } + + /** + * Lock the file to have exclusive access. + */ + public synchronized void lock() { + try { + File f = cacheOrder.getStoreFile(); + if (!f.exists()) { + FileUtils.createParentDir(f); + FileUtils.createRestrictedFile(f, true); + } + fl = FileUtils.getFileLock(f.getPath(), false, true); + } catch (OverlappingFileLockException e) { // if overlap we just increase the count. + } catch (Exception e) { // We didn't get a lock.. + e.printStackTrace(); + } + if (fl != null) lockCount++; + } + + /** + * Unlock the file. + */ + public synchronized void unlock() { + if (fl != null) { + lockCount--; + try { + if (lockCount == 0) { + fl.release(); + fl.channel().close(); + fl = null; + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * Return the value of given key. + * + * @param key + * @return value of given key, null otherwise. + */ + public synchronized String getValue(String key) { + return cacheOrder.getProperty(key); + } + + /** + * Test if we the key provided is in use. + * + * @param key key to be tested. + * @return true if the key is in use. + */ + public synchronized boolean contains(String key) { + return cacheOrder.contains(key); + } + + /** + * Generate a key given the path to file. May or may not generate the same + * key given same path. + * + * @param path Path to generate a key with. + * @return String representing the a key. + */ + public String generateKey(String path) { + return System.currentTimeMillis() + "," + getIdForCacheFolder(path); + } +} diff --git a/netx/net/sourceforge/jnlp/cache/CacheUtil.java b/netx/net/sourceforge/jnlp/cache/CacheUtil.java index 4143738..120573f 100644 --- a/netx/net/sourceforge/jnlp/cache/CacheUtil.java +++ b/netx/net/sourceforge/jnlp/cache/CacheUtil.java @@ -21,7 +21,14 @@ import static net.sourceforge.jnlp.runtime.Translator.R; import java.io.*; import java.net.*; import java.nio.channels.FileChannel; -import java.util.*; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; import java.security.*; import javax.jnlp.*; @@ -29,6 +36,7 @@ import net.sourceforge.jnlp.*; import net.sourceforge.jnlp.config.DeploymentConfiguration; import net.sourceforge.jnlp.runtime.*; import net.sourceforge.jnlp.util.FileUtils; +import net.sourceforge.jnlp.util.PropertiesFile; /** * Provides static methods to interact with the cache, download @@ -39,6 +47,11 @@ import net.sourceforge.jnlp.util.FileUtils; */ public class CacheUtil { + private static final String cacheDir = new File(JNLPRuntime.getConfiguration() + .getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR)).getPath(); // Do this with file to standardize it. + private static final CacheLRUWrapper lruHandler = CacheLRUWrapper.getInstance(); + private static final HashMap<String, FileLock> propertiesLockPool = new HashMap<String, FileLock>(); + /** * Compares a URL using string compare of its protocol, host, * port, path, query, and anchor. This method avoids the host @@ -138,8 +151,7 @@ public class CacheUtil { return; } - File cacheDir = new File(JNLPRuntime.getConfiguration() - .getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR)); + File cacheDir = new File(CacheUtil.cacheDir); if (!(cacheDir.isDirectory())) { return; } @@ -284,18 +296,95 @@ public class CacheUtil { if (!isCacheable(source, version)) throw new IllegalArgumentException(R("CNotCacheable", source)); - try { - String cacheDir = JNLPRuntime.getConfiguration() - .getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR); - File localFile = urlToPath(source, cacheDir); - FileUtils.createParentDir(localFile); + File cacheFile = null; + synchronized (lruHandler) { + lruHandler.lock(); - return localFile; - } catch (Exception ex) { - if (JNLPRuntime.isDebug()) - ex.printStackTrace(); + // We need to reload the cacheOrder file each time + // since another plugin/javaws instance may have updated it. + lruHandler.load(); + cacheFile = getCacheFileIfExist(urlToPath(source, "")); + if (cacheFile == null) { // We did not find a copy of it. + cacheFile = makeNewCacheFile(source, version); + } + lruHandler.unlock(); + } + return cacheFile; + } + + /** + * This will return a File pointing to the location of cache item. + * + * @param urlPath Path of cache item within cache directory. + * @return File if we have searched before, null otherwise. + */ + private static File getCacheFileIfExist(File urlPath) { + synchronized (lruHandler) { + File cacheFile = null; + List<Entry<String, String>> entries = lruHandler.getLRUSortedEntries(); + // Start searching from the most recent to least recent. + for (Entry<String, String> e : entries) { + final String key = e.getKey(); + final String path = e.getValue(); + + if (path != null) { + if (pathToURLPath(path).equals(urlPath.getPath())) { // Match found. + cacheFile = new File(path); + lruHandler.updateEntry(key); + break; // Stop searching since we got newest one already. + } + } + } + return cacheFile; + } + } + + /** + * Get the path to file minus the cache directory and indexed folder. + */ + private static String pathToURLPath(String path) { + int len = cacheDir.length(); + int index = path.indexOf(File.separatorChar, len + 1); + return path.substring(index); + } + + /** + * This will create a new entry for the cache item. It is however not + * initialized but any future calls to getCacheFile with the source and + * version given to here, will cause it to return this item. + * + * @param source the source URL + * @param version the version id of the local file + * @return the file location in the cache. + */ + public static File makeNewCacheFile(URL source, Version version) { + synchronized (lruHandler) { + lruHandler.lock(); + lruHandler.load(); + + File cacheFile = null; + for (long i = 0; i < Long.MAX_VALUE; i++) { + String path = cacheDir + File.separator + i; + File cDir = new File(path); + if (!cDir.exists()) { + // We can use this directory. + try { + cacheFile = urlToPath(source, path); + FileUtils.createParentDir(cacheFile); + File pf = new File(cacheFile.getPath() + ".info"); + FileUtils.createRestrictedFile(pf, true); // Create the info file for marking later. + lruHandler.addEntry(lruHandler.generateKey(cacheFile.getPath()), cacheFile.getPath()); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + break; + } + } - return null; + lruHandler.store(); + lruHandler.unlock(); + return cacheFile; } } @@ -436,4 +525,89 @@ public class CacheUtil { } } + /** + * This will remove all old cache items. + */ + public static void cleanCache() { + if (okToClearCache()) { + // First we want to figure out which stuff we need to delete. + HashSet<String> keep = new HashSet<String>(); + lruHandler.load(); + + for (Entry<String, String> e : lruHandler.getLRUSortedEntries()) { + // Check if the item is contained in cacheOrder. + final String key = e.getKey(); + final String value = e.getValue(); + + if (value != null) { + PropertiesFile pf = new PropertiesFile(new File(value + ".info")); + boolean delete = Boolean.parseBoolean(pf.getProperty("delete")); + + // This will get me the root directory specific to this cache item. + String rStr = value.substring(cacheDir.length()); + rStr = cacheDir + rStr.substring(0, rStr.indexOf(File.separatorChar, 1)); + + if (delete || keep.contains(rStr)) { + lruHandler.removeEntry(key); + } else { + keep.add(value.substring(rStr.length())); + keep.add(rStr); // We can just use the same map, since these two things are disjoint with each other. + } + } else { + lruHandler.removeEntry(key); + } + } + lruHandler.store(); + + removeUntrackedDirectories(keep); + } + } + + private static void removeUntrackedDirectories(Set<String> keep) { + File temp = new File(cacheDir); + // Remove all folder not listed in keep. + for (File f : temp.listFiles()) { + if (f.isDirectory() && !keep.contains(f.getPath())) { + try { + FileUtils.recursiveDelete(f, f); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Lock the property file and add it to our pool of locks. + * + * @param properties Property file to lock. + */ + public static void lockFile(PropertiesFile properties) { + String storeFilePath = properties.getStoreFile().getPath(); + try { + propertiesLockPool.put(storeFilePath, FileUtils.getFileLock(storeFilePath, false, true)); + } catch (OverlappingFileLockException e) { + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + /** + * Unlock the property file and remove it from our pool. Nothing happens if + * it wasn't locked. + * + * @param properties Property file to unlock. + */ + public static void unlockFile(PropertiesFile properties) { + File storeFile = properties.getStoreFile(); + FileLock fl = propertiesLockPool.get(storeFile.getPath()); + try { + if (fl == null) return; + fl.release(); + fl.channel().close(); + propertiesLockPool.remove(storeFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/netx/net/sourceforge/jnlp/cache/ResourceTracker.java b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java index c42cc99..61bebce 100644 --- a/netx/net/sourceforge/jnlp/cache/ResourceTracker.java +++ b/netx/net/sourceforge/jnlp/cache/ResourceTracker.java @@ -629,6 +629,9 @@ public class ResourceTracker { private void downloadResource(Resource resource) { resource.fireDownloadEvent(); // fire DOWNLOADING + CacheEntry origEntry = new CacheEntry(resource.location, resource.downloadVersion); // This is where the jar file will be. + origEntry.lock(); + try { // create out second in case in does not exist URL realLocation = resource.getDownloadLocation(); @@ -666,57 +669,71 @@ public class ResourceTracker { downloadLocation = new URL(downloadLocation.toString() + ".gz"); } - InputStream in = new BufferedInputStream(con.getInputStream()); - OutputStream out = CacheUtil.getOutputStream(downloadLocation, resource.downloadVersion); - byte buf[] = new byte[1024]; - int rlen; + File downloadLocationFile = CacheUtil.getCacheFile(downloadLocation, resource.downloadVersion); + CacheEntry downloadEntry = new CacheEntry(downloadLocation, resource.downloadVersion); + File finalFile = CacheUtil.getCacheFile(resource.location, resource.downloadVersion); // This is where extracted version will be, or downloaded file if not compressed. - while (-1 != (rlen = in.read(buf))) { - resource.transferred += rlen; - out.write(buf, 0, rlen); - } + if (!finalFile.exists()) { + // Make sure we don't re-download the file. however it will wait as if it was downloading. + // (This is fine because file is not ready yet anyways) + byte buf[] = new byte[1024]; + int rlen; - in.close(); - out.close(); + InputStream in = new BufferedInputStream(con.getInputStream()); + OutputStream out = CacheUtil.getOutputStream(downloadLocation, resource.downloadVersion); - // explicitly close the URLConnection. - if (con instanceof HttpURLConnection) - ((HttpURLConnection) con).disconnect(); + while (-1 != (rlen = in.read(buf))) { + resource.transferred += rlen; + out.write(buf, 0, rlen); + } - /* - * If the file was compressed, uncompress it. - */ + in.close(); + out.close(); - if (packgz) { - GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream( - CacheUtil.getCacheFile(downloadLocation, resource.downloadVersion))); - InputStream inputStream = new BufferedInputStream(gzInputStream); + // explicitly close the URLConnection. + if (con instanceof HttpURLConnection) + ((HttpURLConnection) con).disconnect(); - JarOutputStream outputStream = new JarOutputStream(new FileOutputStream( - CacheUtil.getCacheFile(resource.location, resource.downloadVersion))); + /* + * If the file was compressed, uncompress it. + */ + if (packgz) { + GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream(CacheUtil + .getCacheFile(downloadLocation, resource.downloadVersion))); + InputStream inputStream = new BufferedInputStream(gzInputStream); - Unpacker unpacker = Pack200.newUnpacker(); - unpacker.unpack(inputStream, outputStream); + JarOutputStream outputStream = new JarOutputStream(new FileOutputStream(CacheUtil + .getCacheFile(resource.location, resource.downloadVersion))); - outputStream.close(); - inputStream.close(); - gzInputStream.close(); - } else if (gzip) { - GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream(CacheUtil - .getCacheFile(downloadLocation, resource.downloadVersion))); - InputStream inputStream = new BufferedInputStream(gzInputStream); + Unpacker unpacker = Pack200.newUnpacker(); + unpacker.unpack(inputStream, outputStream); - BufferedOutputStream outputStream = new BufferedOutputStream( - new FileOutputStream(CacheUtil.getCacheFile(resource.location, - resource.downloadVersion))); + outputStream.close(); + inputStream.close(); + gzInputStream.close(); + } else if (gzip) { + GZIPInputStream gzInputStream = new GZIPInputStream(new FileInputStream(CacheUtil + .getCacheFile(downloadLocation, resource.downloadVersion))); + InputStream inputStream = new BufferedInputStream(gzInputStream); - while (-1 != (rlen = inputStream.read(buf))) { - outputStream.write(buf, 0, rlen); + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(CacheUtil + .getCacheFile(resource.location, resource.downloadVersion))); + + while (-1 != (rlen = inputStream.read(buf))) { + outputStream.write(buf, 0, rlen); + } + + outputStream.close(); + inputStream.close(); + gzInputStream.close(); } + } else { + resource.transferred = downloadLocationFile.length(); + } - outputStream.close(); - inputStream.close(); - gzInputStream.close(); + if (!downloadLocationFile.getPath().equals(finalFile.getPath())) { + downloadEntry.markForDelete(); + downloadEntry.store(); } resource.changeStatus(DOWNLOADING, DOWNLOADED); @@ -733,6 +750,8 @@ public class ResourceTracker { lock.notifyAll(); // wake up wait's to check for completion } resource.fireDownloadEvent(); // fire ERROR + } finally { + origEntry.unlock(); } } @@ -743,6 +762,9 @@ public class ResourceTracker { private void initializeResource(Resource resource) { resource.fireDownloadEvent(); // fire CONNECTING + CacheEntry entry = new CacheEntry(resource.location, resource.requestVersion); + entry.lock(); + try { File localFile = CacheUtil.getCacheFile(resource.location, resource.downloadVersion); @@ -754,6 +776,18 @@ public class ResourceTracker { int size = connection.getContentLength(); boolean current = CacheUtil.isCurrent(resource.location, resource.requestVersion, connection) && resource.getUpdatePolicy() != UpdatePolicy.FORCE; + if (!current) { + if (entry.isCached()) { + entry.markForDelete(); + entry.store(); + // Old entry will still exist. (but removed at cleanup) + localFile = CacheUtil.makeNewCacheFile(resource.location, resource.downloadVersion); + CacheEntry newEntry = new CacheEntry(resource.location, resource.requestVersion); + newEntry.lock(); + entry.unlock(); + entry = newEntry; + } + } synchronized (resource) { resource.localFile = localFile; @@ -767,7 +801,6 @@ public class ResourceTracker { } // update cache entry - CacheEntry entry = new CacheEntry(resource.location, resource.requestVersion); if (!current) entry.initialize(connection); @@ -791,6 +824,8 @@ public class ResourceTracker { lock.notifyAll(); // wake up wait's to check for completion } resource.fireDownloadEvent(); // fire ERROR + } finally { + entry.unlock(); } } diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java index 13de695..20a9262 100644 --- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java +++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java @@ -166,11 +166,11 @@ public class JNLPClassLoader extends URLClassLoader { // initialize extensions initializeExtensions(); + initializeResources(); + // initialize permissions initializePermissions(); - initializeResources(); - setSecurity(); installShutdownHooks(); diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java index 75b9ec5..69dcf48 100644 --- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java +++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java @@ -701,6 +701,7 @@ public class JNLPRuntime { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { markNetxStopped(); + CacheUtil.cleanCache(); } }); } diff --git a/netx/net/sourceforge/jnlp/util/FileUtils.java b/netx/net/sourceforge/jnlp/util/FileUtils.java index a682fa2..80a303a 100644 --- a/netx/net/sourceforge/jnlp/util/FileUtils.java +++ b/netx/net/sourceforge/jnlp/util/FileUtils.java @@ -19,7 +19,11 @@ package net.sourceforge.jnlp.util; import static net.sourceforge.jnlp.runtime.Translator.R; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; import net.sourceforge.jnlp.runtime.JNLPRuntime; @@ -292,4 +296,42 @@ public final class FileUtils { } + /** + * This will return a lock to the file specified. + * + * @param path File path to file we want to lock. + * @param shared Specify if the lock will be a shared lock. + * @param allowBlock Specify if we should block when we can not get the + * lock. Getting a shared lock will always block. + * @return FileLock if we were successful in getting a lock, otherwise null. + * @throws FileNotFoundException If the file does not exist. + */ + public static FileLock getFileLock(String path, boolean shared, boolean allowBlock) throws FileNotFoundException { + RandomAccessFile rafFile = new RandomAccessFile(path, "rw"); + FileChannel fc = rafFile.getChannel(); + FileLock lock = null; + try { + if (!shared) { + if (allowBlock) { + lock = fc.lock(0, Long.MAX_VALUE, false); + } else { + lock = fc.tryLock(0, Long.MAX_VALUE, false); + } + } else { // We want shared lock. This will block regardless if allowBlock is true or not. + // Test to see if we can get a shared lock. + lock = fc.lock(0, 1, true); // Block if a non exclusive lock is being held. + if (!lock.isShared()) { // This lock is an exclusive lock. Use alternate solution. + FileLock tempLock = null; + for (long pos = 1; tempLock == null && pos < Long.MAX_VALUE - 1; pos++) { + tempLock = fc.tryLock(pos, 1, false); + } + lock.release(); + lock = tempLock; // Get the unique exclusive lock. + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return lock; + } } diff --git a/netx/net/sourceforge/jnlp/util/XDesktopEntry.java b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java index 6e3a7e1..b06222b 100644 --- a/netx/net/sourceforge/jnlp/util/XDesktopEntry.java +++ b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java @@ -74,7 +74,7 @@ public class XDesktopEntry { String cacheDir = JNLPRuntime.getConfiguration() .getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR); - File cacheFile = CacheUtil.urlToPath(file.getSourceLocation(), cacheDir); + File cacheFile = CacheUtil.getCacheFile(file.getSourceLocation(), null); String fileContents = "[Desktop Entry]\n"; fileContents += "Version=1.0\n"; |