Added support for marking resources as executable.
This commit is contained in:
@@ -269,7 +269,7 @@ public class Application
|
|||||||
public Resource getConfigResource ()
|
public Resource getConfigResource ()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return createResource(CONFIG_FILE, false);
|
return createResource(CONFIG_FILE, Resource.NORMAL);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Invalid appbase '" + _vappbase + "'.", e);
|
throw new RuntimeException("Invalid appbase '" + _vappbase + "'.", e);
|
||||||
}
|
}
|
||||||
@@ -395,7 +395,7 @@ public class Application
|
|||||||
String pfile = "patch" + infix + _version + ".dat";
|
String pfile = "patch" + infix + _version + ".dat";
|
||||||
try {
|
try {
|
||||||
URL remote = new URL(createVAppBase(_targetVersion), encodePath(pfile));
|
URL remote = new URL(createVAppBase(_targetVersion), encodePath(pfile));
|
||||||
return new Resource(pfile, remote, getLocalPath(pfile), false);
|
return new Resource(pfile, remote, getLocalPath(pfile), Resource.NORMAL);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warning("Failed to create patch resource path",
|
log.warning("Failed to create patch resource path",
|
||||||
"pfile", pfile, "appbase", _appbase, "tvers", _targetVersion, "error", e);
|
"pfile", pfile, "appbase", _appbase, "tvers", _targetVersion, "error", e);
|
||||||
@@ -417,7 +417,8 @@ public class Application
|
|||||||
String vmfile = LaunchUtil.LOCAL_JAVA_DIR + ".jar";
|
String vmfile = LaunchUtil.LOCAL_JAVA_DIR + ".jar";
|
||||||
try {
|
try {
|
||||||
URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation));
|
URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation));
|
||||||
return new Resource(vmfile, remote, getLocalPath(vmfile), true);
|
return new Resource(vmfile, remote, getLocalPath(vmfile),
|
||||||
|
EnumSet.of(Resource.Attr.UNPACK));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase,
|
log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase,
|
||||||
"tvers", _targetVersion, "javaloc", _javaLocation, "error", e);
|
"tvers", _targetVersion, "javaloc", _javaLocation, "error", e);
|
||||||
@@ -434,7 +435,7 @@ public class Application
|
|||||||
String file = "full";
|
String file = "full";
|
||||||
try {
|
try {
|
||||||
URL remote = new URL(createVAppBase(_targetVersion), encodePath(file));
|
URL remote = new URL(createVAppBase(_targetVersion), encodePath(file));
|
||||||
return new Resource(file, remote, getLocalPath(file), false);
|
return new Resource(file, remote, getLocalPath(file), Resource.NORMAL);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warning("Failed to create full resource path",
|
log.warning("Failed to create full resource path",
|
||||||
"file", file, "appbase", _appbase, "tvers", _targetVersion, "error", e);
|
"file", file, "appbase", _appbase, "tvers", _targetVersion, "error", e);
|
||||||
@@ -656,21 +657,22 @@ public class Application
|
|||||||
ConfigUtil.getMultiValue(cdata, "ucode") == null) {
|
ConfigUtil.getMultiValue(cdata, "ucode") == null) {
|
||||||
throw new IOException("m.missing_code");
|
throw new IOException("m.missing_code");
|
||||||
}
|
}
|
||||||
parseResources(cdata, "code", false, _codes);
|
parseResources(cdata, "code", Resource.NORMAL, _codes);
|
||||||
parseResources(cdata, "ucode", true, _codes);
|
parseResources(cdata, "ucode", Resource.UNPACK, _codes);
|
||||||
|
|
||||||
// parse our non-code resources
|
// parse our non-code resources
|
||||||
parseResources(cdata, "resource", false, _resources);
|
parseResources(cdata, "resource", Resource.NORMAL, _resources);
|
||||||
parseResources(cdata, "uresource", true, _resources);
|
parseResources(cdata, "uresource", Resource.UNPACK, _resources);
|
||||||
|
parseResources(cdata, "xresource", Resource.EXEC, _resources);
|
||||||
|
|
||||||
// parse our auxiliary resource groups
|
// parse our auxiliary resource groups
|
||||||
for (String auxgroup : parseList(cdata, "auxgroups")) {
|
for (String auxgroup : parseList(cdata, "auxgroups")) {
|
||||||
ArrayList<Resource> codes = new ArrayList<>();
|
ArrayList<Resource> codes = new ArrayList<>();
|
||||||
parseResources(cdata, auxgroup + ".code", false, codes);
|
parseResources(cdata, auxgroup + ".code", Resource.NORMAL, codes);
|
||||||
parseResources(cdata, auxgroup + ".ucode", true, codes);
|
parseResources(cdata, auxgroup + ".ucode", Resource.UNPACK, codes);
|
||||||
ArrayList<Resource> rsrcs = new ArrayList<>();
|
ArrayList<Resource> rsrcs = new ArrayList<>();
|
||||||
parseResources(cdata, auxgroup + ".resource", false, rsrcs);
|
parseResources(cdata, auxgroup + ".resource", Resource.NORMAL, rsrcs);
|
||||||
parseResources(cdata, auxgroup + ".uresource", true, rsrcs);
|
parseResources(cdata, auxgroup + ".uresource", Resource.UNPACK, rsrcs);
|
||||||
_auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
|
_auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1377,7 @@ public class Application
|
|||||||
toInstall.add(rsrc);
|
toInstall.add(rsrc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// unpack this resource if appropriate
|
rsrc.applyAttrs();
|
||||||
rsrc.unpackIfNeeded();
|
|
||||||
unpacked.add(rsrc);
|
unpacked.add(rsrc);
|
||||||
rsrc.markAsValid();
|
rsrc.markAsValid();
|
||||||
return;
|
return;
|
||||||
@@ -1672,15 +1673,15 @@ public class Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Helper function for creating {@link Resource} instances. */
|
/** Helper function for creating {@link Resource} instances. */
|
||||||
protected Resource createResource (String path, boolean unpack)
|
protected Resource createResource (String path, EnumSet<Resource.Attr> attrs)
|
||||||
throws MalformedURLException
|
throws MalformedURLException
|
||||||
{
|
{
|
||||||
return new Resource(path, getRemoteURL(path), getLocalPath(path), unpack);
|
return new Resource(path, getRemoteURL(path), getLocalPath(path), attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to parse resources with the specified name. */
|
/** Used to parse resources with the specified name. */
|
||||||
protected void parseResources (Map<String,Object> cdata, String name, boolean unpack,
|
protected void parseResources (Map<String,Object> cdata, String name,
|
||||||
List<Resource> list)
|
EnumSet<Resource.Attr> attrs, List<Resource> list)
|
||||||
{
|
{
|
||||||
String[] rsrcs = ConfigUtil.getMultiValue(cdata, name);
|
String[] rsrcs = ConfigUtil.getMultiValue(cdata, name);
|
||||||
if (rsrcs == null) {
|
if (rsrcs == null) {
|
||||||
@@ -1688,7 +1689,7 @@ public class Application
|
|||||||
}
|
}
|
||||||
for (String rsrc : rsrcs) {
|
for (String rsrc : rsrcs) {
|
||||||
try {
|
try {
|
||||||
list.add(createResource(rsrc, unpack));
|
list.add(createResource(rsrc, attrs));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warning("Invalid resource '" + rsrc + "'. " + e);
|
log.warning("Invalid resource '" + rsrc + "'. " + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.net.URL;
|
|||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
@@ -27,6 +28,18 @@ import static com.threerings.getdown.Log.log;
|
|||||||
*/
|
*/
|
||||||
public class Resource implements Comparable<Resource>
|
public class Resource implements Comparable<Resource>
|
||||||
{
|
{
|
||||||
|
/** Defines special attributes for resources. */
|
||||||
|
public static enum Attr {
|
||||||
|
/** Indicates that the resource should be unpacked. */
|
||||||
|
UNPACK,
|
||||||
|
/** Indicates that the resource should be marked executable. */
|
||||||
|
EXEC
|
||||||
|
};
|
||||||
|
|
||||||
|
public static EnumSet<Attr> NORMAL = EnumSet.noneOf(Attr.class);
|
||||||
|
public static EnumSet<Attr> UNPACK = EnumSet.of(Attr.UNPACK);
|
||||||
|
public static EnumSet<Attr> EXEC = EnumSet.of(Attr.EXEC);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the MD5 hash of the supplied file.
|
* Computes the MD5 hash of the supplied file.
|
||||||
* @param version the version of the digest protocol to use.
|
* @param version the version of the digest protocol to use.
|
||||||
@@ -115,7 +128,7 @@ public class Resource implements Comparable<Resource>
|
|||||||
/**
|
/**
|
||||||
* Creates a resource with the supplied remote URL and local path.
|
* Creates a resource with the supplied remote URL and local path.
|
||||||
*/
|
*/
|
||||||
public Resource (String path, URL remote, File local, boolean unpack)
|
public Resource (String path, URL remote, File local, EnumSet<Attr> attrs)
|
||||||
{
|
{
|
||||||
_path = path;
|
_path = path;
|
||||||
_remote = remote;
|
_remote = remote;
|
||||||
@@ -124,12 +137,13 @@ public class Resource implements Comparable<Resource>
|
|||||||
String lpath = _local.getPath();
|
String lpath = _local.getPath();
|
||||||
_marker = new File(lpath + "v");
|
_marker = new File(lpath + "v");
|
||||||
|
|
||||||
_unpack = unpack;
|
_attrs = attrs;
|
||||||
_isJar = isJar(lpath);
|
_isJar = isJar(lpath);
|
||||||
_isPacked200Jar = isPacked200Jar(lpath);
|
_isPacked200Jar = isPacked200Jar(lpath);
|
||||||
if (_unpack && _isJar) {
|
boolean unpack = attrs.contains(Attr.UNPACK);
|
||||||
|
if (unpack && _isJar) {
|
||||||
_unpacked = _local.getParentFile();
|
_unpacked = _local.getParentFile();
|
||||||
} else if(_unpack && _isPacked200Jar) {
|
} else if(unpack && _isPacked200Jar) {
|
||||||
String dotJar = ".jar", lname = _local.getName();
|
String dotJar = ".jar", lname = _local.getName();
|
||||||
String uname = lname.substring(0, lname.lastIndexOf(dotJar) + dotJar.length());
|
String uname = lname.substring(0, lname.lastIndexOf(dotJar) + dotJar.length());
|
||||||
_unpacked = new File(_local.getParent(), uname);
|
_unpacked = new File(_local.getParent(), uname);
|
||||||
@@ -189,7 +203,7 @@ public class Resource implements Comparable<Resource>
|
|||||||
*/
|
*/
|
||||||
public boolean shouldUnpack ()
|
public boolean shouldUnpack ()
|
||||||
{
|
{
|
||||||
return _unpack && !SysProps.noUnpack();
|
return _attrs.contains(Attr.UNPACK) && !SysProps.noUnpack();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,8 +268,7 @@ public class Resource implements Comparable<Resource>
|
|||||||
if (!FileUtil.renameTo(source, dest)) {
|
if (!FileUtil.renameTo(source, dest)) {
|
||||||
throw new IOException("Failed to rename " + source + " to " + dest);
|
throw new IOException("Failed to rename " + source + " to " + dest);
|
||||||
}
|
}
|
||||||
// unpack the resource, now that it's installed, and mark it as valid
|
applyAttrs();
|
||||||
unpackIfNeeded();
|
|
||||||
markAsValid();
|
markAsValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,12 +289,16 @@ public class Resource implements Comparable<Resource>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpacks this resource if needed.
|
* Applies this resources special attributes: unpacks this resource if needed, marks it as
|
||||||
|
* executable if needed.
|
||||||
*/
|
*/
|
||||||
public void unpackIfNeeded () throws IOException {
|
public void applyAttrs () throws IOException {
|
||||||
if (shouldUnpack()) {
|
if (shouldUnpack()) {
|
||||||
unpack();
|
unpack();
|
||||||
}
|
}
|
||||||
|
if (_attrs.contains(Attr.EXEC)) {
|
||||||
|
FileUtil.makeExecutable(_local);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,7 +358,8 @@ public class Resource implements Comparable<Resource>
|
|||||||
protected String _path;
|
protected String _path;
|
||||||
protected URL _remote;
|
protected URL _remote;
|
||||||
protected File _local, _localNew, _marker, _unpacked;
|
protected File _local, _localNew, _marker, _unpacked;
|
||||||
protected boolean _unpack, _isJar, _isPacked200Jar;
|
protected EnumSet<Attr> _attrs;
|
||||||
|
protected boolean _isJar, _isPacked200Jar;
|
||||||
|
|
||||||
/** Used to sort the entries in a jar file. */
|
/** Used to sort the entries in a jar file. */
|
||||||
protected static final Comparator<JarEntry> ENTRY_COMP = new Comparator<JarEntry>() {
|
protected static final Comparator<JarEntry> ENTRY_COMP = new Comparator<JarEntry>() {
|
||||||
|
|||||||
@@ -633,9 +633,9 @@ public abstract class Getdown extends Thread
|
|||||||
|
|
||||||
// these only run on non-Windows platforms, so we use Unix file separators
|
// these only run on non-Windows platforms, so we use Unix file separators
|
||||||
String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/";
|
String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/";
|
||||||
makeExecutable(localJavaDir + "bin/java");
|
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "bin/java"));
|
||||||
makeExecutable(localJavaDir + "lib/jspawnhelper");
|
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/jspawnhelper"));
|
||||||
makeExecutable(localJavaDir + "lib/amd64/jspawnhelper");
|
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/amd64/jspawnhelper"));
|
||||||
|
|
||||||
// lastly regenerate the .jsa dump file that helps Java to start up faster
|
// lastly regenerate the .jsa dump file that helps Java to start up faster
|
||||||
String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath(""));
|
String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath(""));
|
||||||
@@ -649,23 +649,6 @@ public abstract class Getdown extends Thread
|
|||||||
reportTrackingEvent("jvm_complete", -1);
|
reportTrackingEvent("jvm_complete", -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void makeExecutable (String path) {
|
|
||||||
// Java doesn't know anything about file permissions (and by extension then,
|
|
||||||
// neither does Jar), so on Unix we have to hackily do so via chmod
|
|
||||||
if (!RunAnywhere.isWindows()) {
|
|
||||||
File target = _app.getLocalPath(path);
|
|
||||||
String cmd = "chmod a+rx " + target;
|
|
||||||
try {
|
|
||||||
if (target.exists()) {
|
|
||||||
log.info("Running: " + cmd);
|
|
||||||
Runtime.getRuntime().exec(cmd);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("Failed to mark VM binary as executable", "cmd", cmd, "error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called if the application is determined to be of an old version.
|
* Called if the application is determined to be of an old version.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class GetdownApp
|
|||||||
if (icons.isEmpty()) {
|
if (icons.isEmpty()) {
|
||||||
log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
|
log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
|
||||||
} else {
|
} else {
|
||||||
SwingUtil.setFrameIcons(_frame, icons);
|
_frame.setIconImages(icons);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,6 +206,21 @@ public class FileUtil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks {@code file} as executable, if it exists. Catches and logs any errors that occur.
|
||||||
|
*/
|
||||||
|
public static void makeExecutable (File file) {
|
||||||
|
try {
|
||||||
|
if (file.exists()) {
|
||||||
|
if (!file.setExecutable(true, false)) {
|
||||||
|
log.warning("Failed to mark as executable", "file", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warning("Failed to mark as executable", "file", file, "error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by {@link #walkTree}.
|
* Used by {@link #walkTree}.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user