Clean up the nresource patch & older code cache stuff.
Two important changes: 1. Don't change the name of the code cache dir as that would leave a stale code cache around on old installations that were using the code cache. 2. Don't create a native libs cache dir if there are no native resources. Everything else is mostly cosmetic and organizational.
This commit is contained in:
+24
-27
@@ -1,8 +1,11 @@
|
||||
package com.threerings.getdown.classpath.cache;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
|
||||
/**
|
||||
@@ -44,38 +47,32 @@ public class GarbageCollector
|
||||
|
||||
/**
|
||||
* Collect and delete garbage in the native cache. It tries to find a jar file with a matching
|
||||
* lastmodified file, and deletes the entire directory accordingly.
|
||||
* last modified file, and deletes the entire directory accordingly.
|
||||
*/
|
||||
public static void collectNative (File cacheDir, final long retentionPeriodMillis) {
|
||||
FileUtil.walkDirectChildren(cacheDir, new FileUtil.Visitor() {
|
||||
@Override
|
||||
public void visit(File file) {
|
||||
if (file.isDirectory()) {
|
||||
// Get all the native jars in the directory (there should only be one though)
|
||||
File[] nativejars = file.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.getAbsolutePath().endsWith(".jar");
|
||||
}
|
||||
});
|
||||
for (File nativejar : nativejars) {
|
||||
if (nativejar==null || !nativejar.exists()) {
|
||||
public static void collectNative (File cacheDir, final long retentionPeriodMillis)
|
||||
{
|
||||
File[] subdirs = cacheDir.listFiles();
|
||||
if (subdirs != null) {
|
||||
for (File dir : subdirs) {
|
||||
if (dir.isDirectory()) {
|
||||
// Get all the native jars in the directory (there should only be one)
|
||||
for (File file : dir.listFiles()) {
|
||||
if (!file.getName().endsWith(".jar")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File cachedFile = getCachedFile(nativejar);
|
||||
File lastAccessedFile = getLastAccessedFile(nativejar);
|
||||
|
||||
if (!cachedFile.exists() || !lastAccessedFile.exists() || shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
|
||||
FileUtil.deleteDirHarder(file);
|
||||
File cachedFile = getCachedFile(file);
|
||||
File lastAccessedFile = getLastAccessedFile(file);
|
||||
if (!cachedFile.exists() || !lastAccessedFile.exists() ||
|
||||
shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
|
||||
FileUtil.deleteDirHarder(dir);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// @TODO There shouldn't be any loose files in native/ but if there are then what? Delete them?
|
||||
// file.delete();
|
||||
// @TODO There shouldn't be any loose files in native/ but if there are then
|
||||
// what? Delete them? file.delete();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldDelete (File lastAccessedFile, long retentionMillis)
|
||||
+14
-28
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath.cache;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -14,7 +19,6 @@ public class ResourceCache
|
||||
public ResourceCache (File _cacheDir) throws IOException
|
||||
{
|
||||
this._cacheDir = _cacheDir;
|
||||
|
||||
createDirectoryIfNecessary(_cacheDir);
|
||||
}
|
||||
|
||||
@@ -26,34 +30,16 @@ public class ResourceCache
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the given file under it's {@code digest}. Same as calling {@link #cacheFile(File, String, boolean)}
|
||||
* with {@code false}
|
||||
* @return the cached file
|
||||
* Caches the given file under its {@code digest}.
|
||||
* @param fileToCache file to cache.
|
||||
* @param cacheSubdir the subdirectory of the cache directory in which to store the cached
|
||||
* file. Usually either {@code digest} or a prefix of {@code digest}.
|
||||
* @param digest a crypto digest of the cached files contents.
|
||||
* @return the cached file.
|
||||
*/
|
||||
public File cacheFile (File fileToCache, String digest) throws IOException
|
||||
public File cacheFile (File fileToCache, String cacheSubdir, String digest) throws IOException
|
||||
{
|
||||
return cacheFile(fileToCache, digest, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the given file under it's {@code digest}.
|
||||
* @param fileToCache file to cache
|
||||
* @param digest used to determine the name of the directory to store file in
|
||||
* @param useFullHashName if true, the name of the cache directory will not be truncated.
|
||||
* This is useful if you need to avoid the possibility of two files
|
||||
* sharing a directory.
|
||||
* @return the cached file
|
||||
*/
|
||||
public File cacheFile (File fileToCache, String digest, boolean useFullHashName) throws IOException
|
||||
{
|
||||
File cacheLocation;
|
||||
|
||||
if (useFullHashName) {
|
||||
cacheLocation = new File(_cacheDir, digest);
|
||||
} else {
|
||||
cacheLocation = new File(_cacheDir, digest.substring(0, 2));
|
||||
|
||||
}
|
||||
File cacheLocation = new File(_cacheDir, cacheSubdir);
|
||||
createDirectoryIfNecessary(cacheLocation);
|
||||
|
||||
File cachedFile = new File(cacheLocation, digest + getFileSuffix(fileToCache));
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.classpath;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.threerings.getdown.classpath.cache.GarbageCollector;
|
||||
import com.threerings.getdown.classpath.cache.ResourceCache;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
|
||||
public class ClassPaths
|
||||
{
|
||||
|
||||
/**
|
||||
* Builds either a default or cached classpath based on {@code app}'s configuration.
|
||||
*/
|
||||
public static ClassPath buildClassPath (Application app) throws IOException
|
||||
{
|
||||
return app.useCodeCache() ? buildCachedClassPath(app) : buildDefaultClassPath(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ClassPath} instance for {@code app} using the code resources in place in
|
||||
* the app directory.
|
||||
*/
|
||||
public static ClassPath buildDefaultClassPath (Application app)
|
||||
{
|
||||
LinkedHashSet<File> classPathEntries = new LinkedHashSet<File>();
|
||||
for (Resource resource: app.getActiveCodeResources()) {
|
||||
classPathEntries.add(resource.getFinalTarget());
|
||||
}
|
||||
return new ClassPath(classPathEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ClassPath} instance for {@code app} by first copying the code resources into
|
||||
* a cache directory and then referencing them from there. This avoids problems with
|
||||
* overwriting in-use classpath elements when the application is later updated. This also
|
||||
* "garbage collects" expired caches if necessary.
|
||||
*/
|
||||
public static ClassPath buildCachedClassPath (Application app) throws IOException
|
||||
{
|
||||
File codeCacheDir = new File(app.getAppDir(), Application.CACHE_DIR + "/code");
|
||||
|
||||
// a negative value of code_cache_retention_days allows to clean up the cache forcefully
|
||||
if (app.getCodeCacheRetentionDays() != 0) {
|
||||
runGarbageCollection(app, codeCacheDir);
|
||||
}
|
||||
|
||||
ResourceCache cache = new ResourceCache(codeCacheDir);
|
||||
LinkedHashSet<File> classPathEntries = new LinkedHashSet<>();
|
||||
for (Resource resource: app.getActiveCodeResources()) {
|
||||
File entry = cache.cacheFile(resource.getFinalTarget(), app.getDigest(resource));
|
||||
classPathEntries.add(entry);
|
||||
}
|
||||
|
||||
return new ClassPath(classPathEntries);
|
||||
}
|
||||
|
||||
private static void runGarbageCollection (Application app, File cacheDir)
|
||||
{
|
||||
long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
|
||||
GarbageCollector.collect(cacheDir, retainMillis);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.threerings.getdown.classpath;
|
||||
|
||||
import com.threerings.getdown.classpath.cache.GarbageCollector;
|
||||
import com.threerings.getdown.classpath.cache.ResourceCache;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.JarFile;
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
/**
|
||||
* Similar to {@link ClassPath}, except used to represent directories containing native libraries instead.
|
||||
*/
|
||||
public class NativeLibPath extends ClassPath {
|
||||
|
||||
public NativeLibPath(LinkedHashSet<File> entries) {
|
||||
super(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link NativeLibPath} instance by first caching all native jars (indicated by
|
||||
* nresource=[native jar]), unpacking them, and referencing the locations of each of the unpacked files.
|
||||
* Also performs garbage collection similar to {@link ClassPaths#buildCachedClassPath}
|
||||
*
|
||||
* @param app used to determine native jars and related information
|
||||
* @param addCurrentLibraryPath if true, it adds the locations referenced by
|
||||
* {@code System.getProperty("java.library.path")} as well
|
||||
*/
|
||||
public static NativeLibPath buildLibsPath(Application app, boolean addCurrentLibraryPath) throws IOException {
|
||||
List<Resource> resources = app.getNativeJars();
|
||||
String[] curPaths = System.getProperty("java.library.path").split(File.pathSeparator);
|
||||
LinkedHashSet<File> nativedirs = new LinkedHashSet<>();
|
||||
|
||||
File nativeCacheDir = new File(app.getAppDir(), Application.CACHE_DIR + "/native");
|
||||
ResourceCache cache = new ResourceCache(nativeCacheDir);
|
||||
|
||||
// negative value forces total garbage collection, 0 avoids garbage collection at all
|
||||
if (app.getCodeCacheRetentionDays() != 0) {
|
||||
runGarbageCollection(app, nativeCacheDir);
|
||||
}
|
||||
|
||||
for (Resource resource : resources) {
|
||||
// Use untruncated directory names because in the off chance that two native jars share a directory AND
|
||||
// contain files with the same names, we'll get overwriting issues when unpacking
|
||||
File cachedFile = cache.cacheFile(resource.getFinalTarget(), app.getDigest(resource), true);
|
||||
|
||||
if (!getUnpackedIndicator(cachedFile).exists()) {
|
||||
try {
|
||||
FileUtil.unpackJar(new JarFile(cachedFile), cachedFile.getParentFile(), false);
|
||||
getUnpackedIndicator(cachedFile).createNewFile();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Failed to unpack native jar", "File", cachedFile.getAbsolutePath(), ioe);
|
||||
// Keep going and unpack the other jars...
|
||||
}
|
||||
}
|
||||
|
||||
nativedirs.add(cachedFile.getParentFile());
|
||||
}
|
||||
|
||||
if (addCurrentLibraryPath) {
|
||||
for (String path : curPaths) {
|
||||
nativedirs.add(new File(path));
|
||||
}
|
||||
}
|
||||
|
||||
return new NativeLibPath(nativedirs);
|
||||
}
|
||||
|
||||
private static void runGarbageCollection(Application app, File nativeCacheDir) {
|
||||
long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
|
||||
GarbageCollector.collectNative(nativeCacheDir, retainMillis);
|
||||
}
|
||||
|
||||
private static File getUnpackedIndicator(File cachedFile) {
|
||||
return new File(cachedFile.getParent(), cachedFile.getName() + ".unpacked");
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,6 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.threerings.getdown.classpath.ClassPaths;
|
||||
import com.threerings.getdown.classpath.ClassPath;
|
||||
import com.threerings.getdown.classpath.NativeLibPath;
|
||||
import com.threerings.getdown.util.*;
|
||||
// avoid ambiguity with java.util.Base64 which we can't use as it's 1.8+
|
||||
import com.threerings.getdown.util.Base64;
|
||||
@@ -55,9 +52,6 @@ public class Application
|
||||
/** A special classname that means 'use -jar code.jar' instead of a classname. */
|
||||
public static final String MANIFEST_CLASS = "manifest";
|
||||
|
||||
/** Name of directory to store cached files in. */
|
||||
public static final String CACHE_DIR = ".cache";
|
||||
|
||||
/** Used to communicate information about the UI displayed when updating the application. */
|
||||
public static final class UpdateInterface
|
||||
{
|
||||
@@ -379,19 +373,17 @@ public class Application
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all jar resources indicated to contain native library files (.dll, .so etc.)
|
||||
* Returns all resources indicated to contain native library files (.dll, .so, etc.).
|
||||
*/
|
||||
public List<Resource> getNativeJars() {
|
||||
|
||||
List<Resource> nativeJars = new ArrayList<>();
|
||||
|
||||
public List<Resource> getNativeResources ()
|
||||
{
|
||||
List<Resource> natives = new ArrayList<>();
|
||||
for (Resource resource: _resources) {
|
||||
if (resource.isNativeJar()) {
|
||||
nativeJars.add(resource);
|
||||
if (resource.isNative()) {
|
||||
natives.add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return nativeJars;
|
||||
return natives;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,7 +694,10 @@ public class Application
|
||||
parseResources(config, auxgroup + ".ucode", Resource.UNPACK, codes);
|
||||
ArrayList<Resource> rsrcs = new ArrayList<>();
|
||||
parseResources(config, auxgroup + ".resource", Resource.NORMAL, rsrcs);
|
||||
parseResources(config, auxgroup + ".xresource", Resource.EXEC, rsrcs);
|
||||
parseResources(config, auxgroup + ".uresource", Resource.UNPACK, rsrcs);
|
||||
parseResources(config, auxgroup + ".presource", Resource.PRELOAD, rsrcs);
|
||||
parseResources(config, auxgroup + ".nresource", Resource.NATIVE, rsrcs);
|
||||
_auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
|
||||
}
|
||||
|
||||
@@ -934,12 +929,7 @@ public class Application
|
||||
boolean dashJarMode = MANIFEST_CLASS.equals(_class);
|
||||
|
||||
// add the -classpath arguments if we're not in -jar mode
|
||||
ClassPath classPath = ClassPaths.buildClassPath(this);
|
||||
|
||||
// get the -Djava.library.path value to pass
|
||||
// @TODO optional getdown.txt parameter to set addCurrentLibraryPath to true or false?
|
||||
NativeLibPath nativeLibPaths = NativeLibPath.buildLibsPath(this, true);
|
||||
|
||||
ClassPath classPath = PathBuilder.buildClassPath(this);
|
||||
if (!dashJarMode) {
|
||||
args.add("-classpath");
|
||||
args.add(classPath.asArgumentString());
|
||||
@@ -963,8 +953,12 @@ public class Application
|
||||
// add the marker indicating the app is running in getdown
|
||||
args.add("-D" + Properties.GETDOWN + "=true");
|
||||
|
||||
// set the native library path
|
||||
args.add("-Djava.library.path=" + nativeLibPaths.asArgumentString());
|
||||
// set the native library path if we have native resources
|
||||
// @TODO optional getdown.txt parameter to set addCurrentLibraryPath to true or false?
|
||||
ClassPath javaLibPath = PathBuilder.buildLibsPath(this, true);
|
||||
if (javaLibPath != null) {
|
||||
args.add("-Djava.library.path=" + javaLibPath.asArgumentString());
|
||||
}
|
||||
|
||||
// pass along any pass-through arguments
|
||||
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
|
||||
@@ -1044,7 +1038,7 @@ public class Application
|
||||
*/
|
||||
public void invokeDirect () throws IOException
|
||||
{
|
||||
ClassPath classPath = ClassPaths.buildClassPath(this);
|
||||
ClassPath classPath = PathBuilder.buildClassPath(this);
|
||||
URL[] jarUrls = classPath.asUrls();
|
||||
|
||||
// create custom class loader
|
||||
|
||||
+9
-4
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -9,9 +14,9 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the class path and it's elements of the application to be launched. The class path can
|
||||
* either be represented as an {@link #asArgumentString() argument string} for the java command line
|
||||
* or as an {@link #asUrls() array of URLs} to be used by a {@link URLClassLoader}.
|
||||
* Represents the class path and it's elements of the application to be launched. The class path
|
||||
* can either be represented as an {@link #asArgumentString() argument string} for the java command
|
||||
* line or as an {@link #asUrls() array of URLs} to be used by a {@link URLClassLoader}.
|
||||
*/
|
||||
public class ClassPath
|
||||
{
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import com.threerings.getdown.cache.GarbageCollector;
|
||||
import com.threerings.getdown.cache.ResourceCache;
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
public class PathBuilder
|
||||
{
|
||||
/** Name of directory to store cached code files in. */
|
||||
public static final String CODE_CACHE_DIR = ".cache";
|
||||
|
||||
/** Name of directory to store cached native resources in. */
|
||||
public static final String NATIVE_CACHE_DIR = ".ncache";
|
||||
|
||||
/**
|
||||
* Builds either a default or cached classpath based on {@code app}'s configuration.
|
||||
*/
|
||||
public static ClassPath buildClassPath (Application app) throws IOException
|
||||
{
|
||||
return app.useCodeCache() ? buildCachedClassPath(app) : buildDefaultClassPath(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ClassPath} instance for {@code app} using the code resources in place in
|
||||
* the app directory.
|
||||
*/
|
||||
public static ClassPath buildDefaultClassPath (Application app)
|
||||
{
|
||||
LinkedHashSet<File> classPathEntries = new LinkedHashSet<File>();
|
||||
for (Resource resource: app.getActiveCodeResources()) {
|
||||
classPathEntries.add(resource.getFinalTarget());
|
||||
}
|
||||
return new ClassPath(classPathEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ClassPath} instance for {@code app} by first copying the code resources into
|
||||
* a cache directory and then referencing them from there. This avoids problems with
|
||||
* overwriting in-use classpath elements when the application is later updated. This also
|
||||
* "garbage collects" expired caches if necessary.
|
||||
*/
|
||||
public static ClassPath buildCachedClassPath (Application app) throws IOException
|
||||
{
|
||||
File codeCacheDir = new File(app.getAppDir(), CODE_CACHE_DIR);
|
||||
|
||||
// a negative value of code_cache_retention_days allows to clean up the cache forcefully
|
||||
long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
|
||||
if (retainMillis != 0L) {
|
||||
GarbageCollector.collect(codeCacheDir, retainMillis);
|
||||
}
|
||||
|
||||
ResourceCache cache = new ResourceCache(codeCacheDir);
|
||||
LinkedHashSet<File> classPathEntries = new LinkedHashSet<>();
|
||||
for (Resource resource: app.getActiveCodeResources()) {
|
||||
String digest = app.getDigest(resource);
|
||||
File entry = cache.cacheFile(resource.getFinalTarget(), digest.substring(0, 2), digest);
|
||||
classPathEntries.add(entry);
|
||||
}
|
||||
|
||||
return new ClassPath(classPathEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ClassPath} instance by first caching all native jars (indicated by
|
||||
* nresource=[native jar]), unpacking them, and referencing the locations of each of the
|
||||
* unpacked files. Also performs garbage collection similar to {@link #buildCachedClassPath}
|
||||
*
|
||||
* @param app used to determine native jars and related information.
|
||||
* @param addCurrentLibraryPath if true, it adds the locations referenced by
|
||||
* {@code System.getProperty("java.library.path")} as well.
|
||||
* @return a classpath instance if at least one native resource was found and unpacked,
|
||||
* {@code null} if no native resources were used by the application.
|
||||
*/
|
||||
public static ClassPath buildLibsPath (Application app,
|
||||
boolean addCurrentLibraryPath) throws IOException {
|
||||
List<Resource> resources = app.getNativeResources();
|
||||
if (resources.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LinkedHashSet<File> nativedirs = new LinkedHashSet<>();
|
||||
File nativeCacheDir = new File(app.getAppDir(), NATIVE_CACHE_DIR);
|
||||
ResourceCache cache = new ResourceCache(nativeCacheDir);
|
||||
|
||||
// negative value forces total garbage collection, 0 avoids garbage collection at all
|
||||
long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
|
||||
if (retainMillis != 0L) {
|
||||
GarbageCollector.collectNative(nativeCacheDir, retainMillis);
|
||||
}
|
||||
|
||||
for (Resource resource : resources) {
|
||||
// Use untruncated cache subdirectory names to avoid overwriting issues when unpacking,
|
||||
// in the off chance that two native jars share a directory AND contain files with the
|
||||
// same names
|
||||
String digest = app.getDigest(resource);
|
||||
File cachedFile = cache.cacheFile(resource.getFinalTarget(), digest, digest);
|
||||
File cachedParent = cachedFile.getParentFile();
|
||||
File unpackedIndicator = new File(cachedParent, cachedFile.getName() + ".unpacked");
|
||||
|
||||
if (!unpackedIndicator.exists()) {
|
||||
try {
|
||||
FileUtil.unpackJar(new JarFile(cachedFile), cachedParent, false);
|
||||
unpackedIndicator.createNewFile();
|
||||
} catch (IOException ioe) {
|
||||
log.warning("Failed to unpack native jar",
|
||||
"file", cachedFile.getAbsolutePath(), ioe);
|
||||
// Keep going and unpack the other jars...
|
||||
}
|
||||
}
|
||||
|
||||
nativedirs.add(cachedFile.getParentFile());
|
||||
}
|
||||
|
||||
if (addCurrentLibraryPath) {
|
||||
for (String path : System.getProperty("java.library.path").split(File.pathSeparator)) {
|
||||
nativedirs.add(new File(path));
|
||||
}
|
||||
}
|
||||
|
||||
return new ClassPath(nativedirs);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class Resource implements Comparable<Resource>
|
||||
public static final EnumSet<Attr> UNPACK = EnumSet.of(Attr.UNPACK);
|
||||
public static final EnumSet<Attr> EXEC = EnumSet.of(Attr.EXEC);
|
||||
public static final EnumSet<Attr> PRELOAD = EnumSet.of(Attr.PRELOAD);
|
||||
public static final EnumSet<Attr> NATIVE = EnumSet.of(Attr.NATIVE);
|
||||
public static final EnumSet<Attr> NATIVE = EnumSet.of(Attr.NATIVE);
|
||||
|
||||
/**
|
||||
* Computes the MD5 hash of the supplied file.
|
||||
@@ -218,12 +218,11 @@ public class Resource implements Comparable<Resource>
|
||||
/**
|
||||
* Returns true if this resource is a native lib jar.
|
||||
*/
|
||||
public boolean isNativeJar ()
|
||||
public boolean isNative ()
|
||||
{
|
||||
return _attrs.contains(Attr.NATIVE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the MD5 hash of this resource's underlying file.
|
||||
* <em>Note:</em> This is both CPU and I/O intensive.
|
||||
|
||||
@@ -79,20 +79,17 @@ public class FileUtil
|
||||
|
||||
/**
|
||||
* Force deletes {@code file} and all of its children recursively using {@link #deleteHarder}.
|
||||
* Note that some children may still be deleted even if {@code false} is returned. Also, since {@link #deleteHarder}
|
||||
* is used, the {@code file} could be deleted once the jvm exits even if {@code false} is returned.
|
||||
* Note that some children may still be deleted even if {@code false} is returned. Also, since
|
||||
* {@link #deleteHarder} is used, the {@code file} could be deleted once the jvm exits even if
|
||||
* {@code false} is returned.
|
||||
*
|
||||
* @param file file to delete
|
||||
* @param file file to delete.
|
||||
* @return true iff {@code file} was successfully deleted.
|
||||
*/
|
||||
public static boolean deleteDirHarder(File file) {
|
||||
public static boolean deleteDirHarder (File file) {
|
||||
if (file.isDirectory()) {
|
||||
for (File child : file.listFiles()) {
|
||||
if (child.isDirectory()) {
|
||||
deleteDirHarder(child);
|
||||
} else {
|
||||
deleteHarder(child);
|
||||
}
|
||||
deleteDirHarder(child);
|
||||
}
|
||||
}
|
||||
return deleteHarder(file);
|
||||
@@ -211,7 +208,7 @@ public class FileUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link #walkTree} and {@link #walkDirectChildren}.
|
||||
* Used by {@link #walkTree}.
|
||||
*/
|
||||
public interface Visitor
|
||||
{
|
||||
@@ -239,21 +236,4 @@ public class FileUtil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks all direct sub-files of {@code root}, calling {@code visitor} on each file.
|
||||
*/
|
||||
public static void walkDirectChildren (final File root, Visitor visitor) {
|
||||
File[] children = root.listFiles();
|
||||
|
||||
if (children == null) return;
|
||||
Deque<File> stack = new ArrayDeque<>(Arrays.asList(children));
|
||||
while (!stack.isEmpty()) {
|
||||
File currentFile = stack.pop();
|
||||
if (currentFile.exists()) {
|
||||
visitor.visit(currentFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath.cache;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
+7
-2
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath.cache;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -26,7 +31,7 @@ public class ResourceCacheTest
|
||||
|
||||
private File cacheFile() throws IOException
|
||||
{
|
||||
return _cache.cacheFile(_fileToCache, "abc123");
|
||||
return _cache.cacheFile(_fileToCache, "abc123", "abc123");
|
||||
}
|
||||
|
||||
@Test public void shouldTrackFileUsage () throws IOException
|
||||
+6
-1
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
+11
-9
@@ -1,4 +1,9 @@
|
||||
package com.threerings.getdown.classpath;
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -14,11 +19,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ClassPathsTest
|
||||
public class PathBuilderTest
|
||||
{
|
||||
@Before public void setupFilesAndResources () throws IOException
|
||||
{
|
||||
@@ -33,7 +35,7 @@ public class ClassPathsTest
|
||||
|
||||
@Test public void shouldBuildDefaultClassPath () throws IOException
|
||||
{
|
||||
ClassPath classPath = ClassPaths.buildDefaultClassPath(_application);
|
||||
ClassPath classPath = PathBuilder.buildDefaultClassPath(_application);
|
||||
String expectedClassPath = _firstJarFile.getAbsolutePath() + File.pathSeparator +
|
||||
_secondJarFile.getAbsolutePath();
|
||||
assertEquals(expectedClassPath, classPath.asArgumentString());
|
||||
@@ -46,15 +48,15 @@ public class ClassPathsTest
|
||||
when(_application.getCodeCacheRetentionDays()).thenReturn(1);
|
||||
|
||||
Path firstCachedJarFile = _appdir.getRoot().toPath().
|
||||
resolve(Application.CACHE_DIR + "/code").resolve("fi").resolve("first.jar");
|
||||
resolve(PathBuilder.CODE_CACHE_DIR).resolve("fi").resolve("first.jar");
|
||||
|
||||
Path secondCachedJarFile = _appdir.getRoot().toPath().
|
||||
resolve(Application.CACHE_DIR + "/code").resolve("se").resolve("second.jar");
|
||||
resolve(PathBuilder.CODE_CACHE_DIR).resolve("se").resolve("second.jar");
|
||||
|
||||
String expectedClassPath = firstCachedJarFile.toAbsolutePath() + File.pathSeparator +
|
||||
secondCachedJarFile.toAbsolutePath();
|
||||
|
||||
ClassPath classPath = ClassPaths.buildCachedClassPath(_application);
|
||||
ClassPath classPath = PathBuilder.buildCachedClassPath(_application);
|
||||
assertEquals(expectedClassPath, classPath.asArgumentString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user