
com.threerings.getdown.data.PathBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of getdown-core Show documentation
Show all versions of getdown-core Show documentation
Core Getdown functionality
The newest version!
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2018 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.Set;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipFile;
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 classPathEntries = new LinkedHashSet();
for (Resource resource : app.getActiveCodeResources()) {
classPathEntries.add(resource.getFinalTarget());
}
addClassPathDirectories(app, classPathEntries);
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 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);
}
addClassPathDirectories(app, classPathEntries);
return new ClassPath(classPathEntries);
}
private static void addClassPathDirectories (Application app, Set classPathEntries) {
for (String cpdir : app.getClassPathDirectories()) {
File cpfile = new File(cpdir);
if (!cpfile.isAbsolute()) {
cpfile = new File(app.getAppDir(), cpdir);
}
classPathEntries.add(cpfile);
}
}
/**
* 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 resources = app.getNativeResources();
if (resources.isEmpty()) {
return null;
}
LinkedHashSet 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 ZipFile(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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy