aboutsummaryrefslogtreecommitdiffstats
path: root/netx/net/sourceforge
diff options
context:
space:
mode:
authorAndrew Su <[email protected]>2011-04-18 17:38:31 -0400
committerAndrew Su <[email protected]>2011-04-18 17:38:31 -0400
commitd2ab6fbc6ac11d8d4684f2f855a5324548227917 (patch)
tree56a36a6682480f03a927791db26b63f6a310e9c8 /netx/net/sourceforge
parent3614db44151bb47e747226e5bd49d30bb9620e96 (diff)
Changed cache to prevent jar overwriting when update happens.
Diffstat (limited to 'netx/net/sourceforge')
-rw-r--r--netx/net/sourceforge/jnlp/cache/CacheEntry.java20
-rw-r--r--netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java245
-rw-r--r--netx/net/sourceforge/jnlp/cache/CacheUtil.java200
-rw-r--r--netx/net/sourceforge/jnlp/cache/ResourceTracker.java115
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java4
-rw-r--r--netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java1
-rw-r--r--netx/net/sourceforge/jnlp/util/FileUtils.java42
-rw-r--r--netx/net/sourceforge/jnlp/util/XDesktopEntry.java2
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";