// Copyright (C) 2001-2003 Jon A. Maxwell (JAM) // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package net.sourceforge.jnlp.cache; import static net.sourceforge.jnlp.runtime.Translator.R; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.security.Permission; 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 javax.jnlp.DownloadServiceListener; import net.sourceforge.jnlp.Version; import net.sourceforge.jnlp.config.DeploymentConfiguration; import net.sourceforge.jnlp.runtime.ApplicationInstance; import net.sourceforge.jnlp.runtime.JNLPRuntime; import net.sourceforge.jnlp.util.FileUtils; import net.sourceforge.jnlp.util.PropertiesFile; import net.sourceforge.jnlp.util.UrlUtils; /** * Provides static methods to interact with the cache, download * indicator, and other utility methods.
*
* @author Jon A. Maxwell (JAM) - initial author
* @version $Revision: 1.17 $
*/
public class CacheUtil {
private static final String setCacheDir = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_USER_CACHE_DIR);
private static final String cacheDir = new File(setCacheDir != null ? setCacheDir : System.getProperty("java.io.tmpdir")).getPath(); // Do this with file to standardize it.
private static final CacheLRUWrapper lruHandler = CacheLRUWrapper.getInstance();
private static final HashMap
*
* @param location location of the resource
* @param version the version, or null
* @return either the location in the cache or the original location
*/
public static URL getCachedResource(URL location, Version version, UpdatePolicy policy) {
ResourceTracker rt = new ResourceTracker();
rt.addResource(location, version, null, policy);
try {
File f = rt.getCacheFile(location);
// TODO: Should be toURI().toURL()
return f.toURL();
} catch (MalformedURLException ex) {
return location;
}
}
/**
* Compare strings that can be null.
*/
private static boolean compare(String s1, String s2, boolean ignore) {
if (s1 == s2)
return true;
if (s1 == null || s2 == null)
return false;
if (ignore)
return s1.equalsIgnoreCase(s2);
else
return s1.equals(s2);
}
/**
* Returns the Permission object necessary to access the
* resource, or null if no permission is needed.
*/
public static Permission getReadPermission(URL location, Version version) {
if (CacheUtil.isCacheable(location, version)) {
File file = CacheUtil.getCacheFile(location, version);
return new FilePermission(file.getPath(), "read");
} else {
try {
// this is what URLClassLoader does
return location.openConnection().getPermission();
} catch (java.io.IOException ioe) {
// should try to figure out the permission
if (JNLPRuntime.isDebug())
ioe.printStackTrace();
}
}
return null;
}
/**
* Clears the cache by deleting all the Netx cache files
*
* Note: Because of how our caching system works, deleting jars of another javaws
* process is using them can be quite disasterous. Hence why Launcher creates lock files
* and we check for those by calling {@link #okToClearCache()}
*/
public static boolean clearCache() {
if (!okToClearCache()) {
System.err.println(R("CCannotClearCache"));
return false;
}
File cacheDir = new File(CacheUtil.cacheDir);
if (!(cacheDir.isDirectory())) {
return false;
}
if (JNLPRuntime.isDebug()) {
System.err.println("Clearing cache directory: " + cacheDir);
}
try {
cacheDir = cacheDir.getCanonicalFile();
FileUtils.recursiveDelete(cacheDir, cacheDir);
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
/**
* Returns a boolean indicating if it ok to clear the netx application cache at this point
* @return true if the cache can be cleared at this time without problems
*/
private static boolean okToClearCache() {
File otherJavawsRunning = new File(JNLPRuntime.getConfiguration()
.getProperty(DeploymentConfiguration.KEY_USER_NETX_RUNNING_FILE));
try {
if (otherJavawsRunning.isFile()) {
FileOutputStream fis = new FileOutputStream(otherJavawsRunning);
FileChannel channel = fis.getChannel();
if (channel.tryLock() == null) {
if (JNLPRuntime.isDebug()) {
System.out.println("Other instances of netx are running");
}
return false;
}
if (JNLPRuntime.isDebug()) {
System.out.println("No other instances of netx are running");
}
return true;
} else {
if (JNLPRuntime.isDebug()) {
System.out.println("No instance file found");
}
return true;
}
} catch (IOException e) {
return false;
}
}
/**
* Returns whether there is a version of the URL contents in the
* cache and it is up to date. This method may not return
* immediately.
*
* @param source the source URL
* @param version the versions to check for
* @param connection a connection to the URL, or null
* @return whether the cache contains the version
* @throws IllegalArgumentException if the source is not cacheable
*/
public static boolean isCurrent(URL source, Version version, URLConnection connection) {
if (!isCacheable(source, version))
throw new IllegalArgumentException(R("CNotCacheable", source));
try {
if (connection == null)
connection = source.openConnection();
connection.connect();
CacheEntry entry = new CacheEntry(source, version); // could pool this
boolean result = entry.isCurrent(connection);
if (JNLPRuntime.isDebug())
System.out.println("isCurrent: " + source + " = " + result);
return result;
} catch (Exception ex) {
if (JNLPRuntime.isDebug())
ex.printStackTrace();
return isCached(source, version); // if can't connect return whether already in cache
}
}
/**
* Returns true if the cache has a local copy of the contents of
* the URL matching the specified version string.
*
* @param source the source URL
* @param version the versions to check for
* @return true if the source is in the cache
* @throws IllegalArgumentException if the source is not cacheable
*/
public static boolean isCached(URL source, Version version) {
if (!isCacheable(source, version))
throw new IllegalArgumentException(R("CNotCacheable", source));
CacheEntry entry = new CacheEntry(source, version); // could pool this
boolean result = entry.isCached();
if (JNLPRuntime.isDebug())
System.out.println("isCached: " + source + " = " + result);
return result;
}
/**
* Returns whether the resource can be cached as a local file;
* if not, then URLConnection.openStream can be used to obtain
* the contents.
*/
public static boolean isCacheable(URL source, Version version) {
if (source == null)
return false;
if (source.getProtocol().equals("file"))
return false;
if (source.getProtocol().equals("jar"))
return false;
return true;
}
/**
* Returns the file for the locally cached contents of the
* source. This method returns the file location only and does
* not download the resource. The latest version of the
* resource that matches the specified version will be returned.
*
* @param source the source URL
* @param version the version id of the local file
* @return the file location in the cache, or null if no versions cached
* @throws IllegalArgumentException if the source is not cacheable
*/
public static File getCacheFile(URL source, Version version) {
// ensure that version is an version id not version string
if (!isCacheable(source, version))
throw new IllegalArgumentException(R("CNotCacheable", source));
File cacheFile = null;
synchronized (lruHandler) {
lruHandler.lock();
// 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);
} else
lruHandler.store();
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