mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-19 21:16:30 +02:00
Rewrite *everything* to use Okio
This commit is contained in:
parent
81c1ebaa15
commit
442fb93ca8
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -4,5 +4,6 @@
|
|||||||
"**/.project": true,
|
"**/.project": true,
|
||||||
"**/.settings": true,
|
"**/.settings": true,
|
||||||
"**/.factorypath": true
|
"**/.factorypath": true
|
||||||
}
|
},
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ dependencies {
|
|||||||
// TODO: Implement tests
|
// TODO: Implement tests
|
||||||
//testImplementation 'junit:junit:4.12'
|
//testImplementation 'junit:junit:4.12'
|
||||||
implementation 'com.google.code.gson:gson:2.8.1'
|
implementation 'com.google.code.gson:gson:2.8.1'
|
||||||
|
implementation 'com.squareup.okio:okio:2.2.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -29,10 +29,14 @@ import com.moandjiezana.toml.Toml;
|
|||||||
import link.infra.packwiz.installer.metadata.IndexFile;
|
import link.infra.packwiz.installer.metadata.IndexFile;
|
||||||
import link.infra.packwiz.installer.metadata.ManifestFile;
|
import link.infra.packwiz.installer.metadata.ManifestFile;
|
||||||
import link.infra.packwiz.installer.metadata.PackFile;
|
import link.infra.packwiz.installer.metadata.PackFile;
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
||||||
import link.infra.packwiz.installer.request.HandlerManager;
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
import link.infra.packwiz.installer.ui.IUserInterface;
|
import link.infra.packwiz.installer.ui.IUserInterface;
|
||||||
import link.infra.packwiz.installer.ui.InstallProgress;
|
import link.infra.packwiz.installer.ui.InstallProgress;
|
||||||
|
import okio.Buffer;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public class UpdateManager {
|
public class UpdateManager {
|
||||||
|
|
||||||
@ -118,13 +122,10 @@ public class UpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui.submitProgress(new InstallProgress("Loading pack file..."));
|
ui.submitProgress(new InstallProgress("Loading pack file..."));
|
||||||
Hash.HashInputStream packFileStream;
|
GeneralHashingSource packFileSource;
|
||||||
try {
|
try {
|
||||||
InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI);
|
Source src = HandlerManager.getFileSource(opts.downloadURI);
|
||||||
if (stream == null) {
|
packFileSource = HashUtils.getHasher("sha256").getHashingSource(src);
|
||||||
throw new Exception("Pack file URI is invalid, is it supported?");
|
|
||||||
}
|
|
||||||
packFileStream = new Hash.HashInputStream(stream, "sha256");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: still launch the game if updating doesn't work?
|
// TODO: still launch the game if updating doesn't work?
|
||||||
// TODO: ask user if they want to launch the game, exit(1) if they don't
|
// TODO: ask user if they want to launch the game, exit(1) if they don't
|
||||||
@ -133,14 +134,13 @@ public class UpdateManager {
|
|||||||
}
|
}
|
||||||
PackFile pf;
|
PackFile pf;
|
||||||
try {
|
try {
|
||||||
pf = new Toml().read(packFileStream).to(PackFile.class);
|
pf = new Toml().read(Okio.buffer(packFileSource).inputStream()).to(PackFile.class);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
ui.handleExceptionAndExit(e);
|
ui.handleExceptionAndExit(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash packFileHash = packFileStream.get();
|
if (packFileSource.hashIsEqual(manifest.packFileHash)) {
|
||||||
if (packFileHash.equals(manifest.packFileHash)) {
|
|
||||||
System.out.println("Hash already up to date!");
|
System.out.println("Hash already up to date!");
|
||||||
// WOOO it's already up to date
|
// WOOO it's already up to date
|
||||||
// todo: --force?
|
// todo: --force?
|
||||||
@ -148,11 +148,15 @@ public class UpdateManager {
|
|||||||
|
|
||||||
System.out.println(pf.name);
|
System.out.println(pf.name);
|
||||||
|
|
||||||
processIndex(HandlerManager.getNewLoc(opts.downloadURI, pf.index.file),
|
try {
|
||||||
new Hash(pf.index.hash, pf.index.hashFormat), manifest);
|
processIndex(HandlerManager.getNewLoc(opts.downloadURI, pf.index.file),
|
||||||
|
HashUtils.getHash(pf.index.hash, pf.index.hashFormat), manifest);
|
||||||
|
} catch (Exception e1) {
|
||||||
|
ui.handleExceptionAndExit(e1);
|
||||||
|
}
|
||||||
|
|
||||||
// When successfully updated
|
// When successfully updated
|
||||||
manifest.packFileHash = packFileHash;
|
manifest.packFileHash = packFileSource.getHash();
|
||||||
// update other hashes
|
// update other hashes
|
||||||
// TODO: don't do this on failure?
|
// TODO: don't do this on failure?
|
||||||
try (Writer writer = new FileWriter(Paths.get(opts.packFolder, opts.manifestFile).toString())) {
|
try (Writer writer = new FileWriter(Paths.get(opts.packFolder, opts.manifestFile).toString())) {
|
||||||
@ -168,14 +172,11 @@ public class UpdateManager {
|
|||||||
// TODO: implement
|
// TODO: implement
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processIndex(URI indexUri, Hash indexHash, ManifestFile manifest) {
|
protected void processIndex(URI indexUri, Object indexHash, ManifestFile manifest) {
|
||||||
Hash.HashInputStream indexFileStream;
|
GeneralHashingSource indexFileSource;
|
||||||
try {
|
try {
|
||||||
InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI);
|
Source src = HandlerManager.getFileSource(opts.downloadURI);
|
||||||
if (stream == null) {
|
indexFileSource = HashUtils.getHasher("sha256").getHashingSource(src);
|
||||||
throw new Exception("Index file URI is invalid, is it supported?");
|
|
||||||
}
|
|
||||||
indexFileStream = new Hash.HashInputStream(stream, indexHash);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: still launch the game if updating doesn't work?
|
// TODO: still launch the game if updating doesn't work?
|
||||||
// TODO: ask user if they want to launch the game, exit(1) if they don't
|
// TODO: ask user if they want to launch the game, exit(1) if they don't
|
||||||
@ -184,13 +185,13 @@ public class UpdateManager {
|
|||||||
}
|
}
|
||||||
IndexFile indexFile;
|
IndexFile indexFile;
|
||||||
try {
|
try {
|
||||||
indexFile = new Toml().read(indexFileStream).to(IndexFile.class);
|
indexFile = new Toml().read(Okio.buffer(indexFileSource).inputStream()).to(IndexFile.class);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
ui.handleExceptionAndExit(e);
|
ui.handleExceptionAndExit(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!indexFileStream.hashIsEqual()) {
|
if (!indexFileSource.hashIsEqual(indexHash)) {
|
||||||
System.out.println("Hash problems!!!!!!!");
|
System.out.println("Hash problems!!!!!!!");
|
||||||
// TODO: throw exception
|
// TODO: throw exception
|
||||||
}
|
}
|
||||||
@ -199,7 +200,13 @@ public class UpdateManager {
|
|||||||
ConcurrentLinkedQueue<Exception> exceptionQueue = new ConcurrentLinkedQueue<Exception>();
|
ConcurrentLinkedQueue<Exception> exceptionQueue = new ConcurrentLinkedQueue<Exception>();
|
||||||
List<IndexFile.File> newFiles = indexFile.files.stream().filter(f -> {
|
List<IndexFile.File> newFiles = indexFile.files.stream().filter(f -> {
|
||||||
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
|
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
|
||||||
Hash newHash = new Hash(f.hash, f.hashFormat);
|
Object newHash;
|
||||||
|
try {
|
||||||
|
newHash = HashUtils.getHash(f.hashFormat, f.hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
exceptionQueue.add(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return cachedFile == null || newHash.equals(cachedFile.hash);
|
return cachedFile == null || newHash.equals(cachedFile.hash);
|
||||||
}).parallel().map(f -> {
|
}).parallel().map(f -> {
|
||||||
try {
|
try {
|
||||||
@ -248,15 +255,22 @@ public class UpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream stream = f.getInputStream(indexUri);
|
Source src = f.getSource(indexUri);
|
||||||
if (stream == null) {
|
GeneralHashingSource fileSource = HashUtils.getHasher(f.hashFormat).getHashingSource(src);
|
||||||
throw new Exception("Failed to open download stream");
|
Buffer data = new Buffer();
|
||||||
|
Okio.buffer(fileSource).readAll(data);
|
||||||
|
|
||||||
|
Object hash;
|
||||||
|
if (f.linkedFile != null) {
|
||||||
|
hash = f.linkedFile.getHash();
|
||||||
|
} else {
|
||||||
|
hash = f.getHash();
|
||||||
|
}
|
||||||
|
if (fileSource.hashIsEqual(hash)) {
|
||||||
|
Files.copy(data.inputStream(), Paths.get(opts.packFolder, f.getDestURI().toString()), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
} else {
|
||||||
|
dc.err = new Exception("Hash invalid!");
|
||||||
}
|
}
|
||||||
Hash.HashInputStream fileStream = new Hash.HashInputStream(stream, f.getHash());
|
|
||||||
// UGHHHHHH if you're reading this point in history
|
|
||||||
// this is the point where i change EVERYTHING to okio because it's 1000000000000 times nicer for this
|
|
||||||
byte[] data = fileStream.readAllBytes();
|
|
||||||
Files.copy(fileStream, Paths.get(opts.packFolder, f.getDestURI().toString()), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
|
|
||||||
return dc;
|
return dc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -8,8 +7,11 @@ import java.util.List;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.moandjiezana.toml.Toml;
|
import com.moandjiezana.toml.Toml;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
||||||
import link.infra.packwiz.installer.request.HandlerManager;
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public class IndexFile {
|
public class IndexFile {
|
||||||
@SerializedName("hash-format")
|
@SerializedName("hash-format")
|
||||||
@ -38,43 +40,40 @@ public class IndexFile {
|
|||||||
if (hashFormat == null || hashFormat.length() == 0) {
|
if (hashFormat == null || hashFormat.length() == 0) {
|
||||||
hashFormat = parentIndexFile.hashFormat;
|
hashFormat = parentIndexFile.hashFormat;
|
||||||
}
|
}
|
||||||
Hash fileHash = new Hash(hash, hashFormat);
|
Object fileHash = HashUtils.getHash(hashFormat, hash);
|
||||||
linkedFileURI = HandlerManager.getNewLoc(indexUri, file);
|
linkedFileURI = HandlerManager.getNewLoc(indexUri, file);
|
||||||
InputStream stream = HandlerManager.getFileInputStream(linkedFileURI);
|
Source src = HandlerManager.getFileSource(linkedFileURI);
|
||||||
if (stream == null) {
|
GeneralHashingSource fileStream = HashUtils.getHasher(hashFormat).getHashingSource(src);
|
||||||
throw new Exception("Index file URI is invalid, is it supported?");
|
|
||||||
}
|
|
||||||
Hash.HashInputStream fileStream = new Hash.HashInputStream(stream, fileHash);
|
|
||||||
|
|
||||||
linkedFile = new Toml().read(fileStream).to(ModFile.class);
|
linkedFile = new Toml().read(Okio.buffer(fileStream).inputStream()).to(ModFile.class);
|
||||||
if (!fileStream.hashIsEqual()) {
|
if (!fileStream.hashIsEqual(fileHash)) {
|
||||||
throw new Exception("Invalid mod file hash");
|
throw new Exception("Invalid mod file hash");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream(URI indexUri) throws Exception {
|
public Source getSource(URI indexUri) throws Exception {
|
||||||
if (metafile) {
|
if (metafile) {
|
||||||
if (linkedFile == null) {
|
if (linkedFile == null) {
|
||||||
throw new Exception("Linked file doesn't exist!");
|
throw new Exception("Linked file doesn't exist!");
|
||||||
}
|
}
|
||||||
return linkedFile.getInputStream(linkedFileURI);
|
return linkedFile.getSource(linkedFileURI);
|
||||||
} else {
|
} else {
|
||||||
URI newLoc = HandlerManager.getNewLoc(indexUri, file);
|
URI newLoc = HandlerManager.getNewLoc(indexUri, file);
|
||||||
if (newLoc == null) {
|
if (newLoc == null) {
|
||||||
throw new Exception("Index file URI is invalid");
|
throw new Exception("Index file URI is invalid");
|
||||||
}
|
}
|
||||||
return HandlerManager.getFileInputStream(newLoc);
|
return HandlerManager.getFileSource(newLoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hash getHash() throws Exception {
|
public Object getHash() throws Exception {
|
||||||
if (hash == null) {
|
if (hash == null) {
|
||||||
throw new Exception("Index file doesn't have a hash");
|
throw new Exception("Index file doesn't have a hash");
|
||||||
}
|
}
|
||||||
if (hashFormat == null) {
|
if (hashFormat == null) {
|
||||||
throw new Exception("Index file doesn't have a hash format");
|
throw new Exception("Index file doesn't have a hash format");
|
||||||
}
|
}
|
||||||
return new Hash(hash, hashFormat);
|
return HashUtils.getHash(hashFormat, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -3,18 +3,16 @@ package link.infra.packwiz.installer.metadata;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
|
||||||
|
|
||||||
public class ManifestFile {
|
public class ManifestFile {
|
||||||
|
|
||||||
public Hash packFileHash = null;
|
public Object packFileHash = null;
|
||||||
public Hash indexFileHash = null;
|
public Object indexFileHash = null;
|
||||||
public Map<URI, File> cachedFiles;
|
public Map<URI, File> cachedFiles;
|
||||||
|
|
||||||
public static class File {
|
public static class File {
|
||||||
public Hash hash = null;
|
public Object hash = null;
|
||||||
public boolean isOptional = false;
|
public boolean isOptional = false;
|
||||||
public boolean optionValue = true;
|
public boolean optionValue = true;
|
||||||
public Hash linkedFileHash = null;
|
public Object linkedFileHash = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,14 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.UpdateManager.Options.Side;
|
import link.infra.packwiz.installer.UpdateManager.Options.Side;
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
||||||
import link.infra.packwiz.installer.request.HandlerManager;
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public class ModFile {
|
public class ModFile {
|
||||||
public String name;
|
public String name;
|
||||||
@ -33,7 +33,7 @@ public class ModFile {
|
|||||||
public boolean defaultValue;
|
public boolean defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream(URI baseLoc) throws Exception {
|
public Source getSource(URI baseLoc) throws Exception {
|
||||||
if (download == null) {
|
if (download == null) {
|
||||||
throw new Exception("Metadata file doesn't have download");
|
throw new Exception("Metadata file doesn't have download");
|
||||||
}
|
}
|
||||||
@ -45,10 +45,10 @@ public class ModFile {
|
|||||||
throw new Exception("Metadata file URI is invalid");
|
throw new Exception("Metadata file URI is invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
return HandlerManager.getFileInputStream(newLoc);
|
return HandlerManager.getFileSource(newLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hash getHash() throws Exception {
|
public Object getHash() throws Exception {
|
||||||
if (download == null) {
|
if (download == null) {
|
||||||
throw new Exception("Metadata file doesn't have download");
|
throw new Exception("Metadata file doesn't have download");
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ public class ModFile {
|
|||||||
if (download.hashFormat == null) {
|
if (download.hashFormat == null) {
|
||||||
throw new Exception("Metadata file doesn't have a hash format");
|
throw new Exception("Metadata file doesn't have a hash format");
|
||||||
}
|
}
|
||||||
return new Hash(download.hash, download.hashFormat);
|
return HashUtils.getHash(download.hash, download.hashFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOptional() {
|
public boolean isOptional() {
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package link.infra.packwiz.installer.metadata.hash;
|
||||||
|
|
||||||
|
import okio.ForwardingSource;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
|
public abstract class GeneralHashingSource extends ForwardingSource {
|
||||||
|
|
||||||
|
public GeneralHashingSource(Source delegate) {
|
||||||
|
super(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Object getHash();
|
||||||
|
|
||||||
|
public boolean hashIsEqual(Object compareTo) {
|
||||||
|
return compareTo.equals(getHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.metadata.hash;
|
|
||||||
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class Hash {
|
|
||||||
public final String value;
|
|
||||||
public final String type;
|
|
||||||
|
|
||||||
public Hash(String value, String type) {
|
|
||||||
this.value = value;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Map<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
hashTypeConversion.put("sha256", new HasherMessageDigest("SHA-256"));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IHasher getHasher() {
|
|
||||||
return hashTypeConversion.get(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IHasher getHasher(String type) {
|
|
||||||
return hashTypeConversion.get(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == null || !(obj instanceof Hash)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Hash hash = (Hash)obj;
|
|
||||||
return type.equals(hash.type) && getHasher().equalValues(value, hash.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HashInputStream extends FilterInputStream {
|
|
||||||
|
|
||||||
private IHasher md;
|
|
||||||
private Hash output;
|
|
||||||
private final String hashType;
|
|
||||||
private Hash compare = null;
|
|
||||||
|
|
||||||
public HashInputStream(InputStream in, String hashType) throws NoSuchAlgorithmException {
|
|
||||||
super(in);
|
|
||||||
this.hashType = hashType;
|
|
||||||
md = hashTypeConversion.get(hashType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashInputStream(InputStream in, Hash compare) throws NoSuchAlgorithmException {
|
|
||||||
this(in, compare.type);
|
|
||||||
this.compare = compare;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
int value = super.read();
|
|
||||||
if (value == -1) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
md.update((byte) value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
int bytesRead = super.read(b, off, len);
|
|
||||||
if (bytesRead > 0) {
|
|
||||||
md.update(b, off, len);
|
|
||||||
}
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() throws IOException {
|
|
||||||
throw new IOException("HashInputStream doesn't support reset()");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean markSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mark(int readlimit) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hash get() {
|
|
||||||
if (output == null) {
|
|
||||||
String value = md.get();
|
|
||||||
if (value != null) {
|
|
||||||
output = new Hash(value, hashType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hashIsEqual() {
|
|
||||||
if (output == null) {
|
|
||||||
get();
|
|
||||||
}
|
|
||||||
return !output.equals(compare);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +1,27 @@
|
|||||||
package link.infra.packwiz.installer.metadata.hash;
|
package link.infra.packwiz.installer.metadata.hash;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class HashUtils {
|
public class HashUtils {
|
||||||
private HashUtils() {}
|
private static final Map<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
|
||||||
|
static {
|
||||||
// Why did Java remove this in 1.9????!
|
hashTypeConversion.put("sha256", new HasherHashingSource("sha256"));
|
||||||
public static byte[] parseHexBinary(String s) {
|
|
||||||
final int len = s.length();
|
|
||||||
|
|
||||||
// "111" is not a valid hex encoding.
|
|
||||||
if( len%2 != 0 )
|
|
||||||
throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);
|
|
||||||
|
|
||||||
byte[] out = new byte[len/2];
|
|
||||||
|
|
||||||
for( int i=0; i<len; i+=2 ) {
|
|
||||||
int h = hexToBin(s.charAt(i ));
|
|
||||||
int l = hexToBin(s.charAt(i+1));
|
|
||||||
if( h==-1 || l==-1 )
|
|
||||||
throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);
|
|
||||||
|
|
||||||
out[i/2] = (byte)(h*16+l);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int hexToBin( char ch ) {
|
public static IHasher getHasher(String type) throws Exception {
|
||||||
if( '0'<=ch && ch<='9' ) return ch-'0';
|
IHasher hasher = hashTypeConversion.get(type);
|
||||||
if( 'A'<=ch && ch<='F' ) return ch-'A'+10;
|
if (hasher == null) {
|
||||||
if( 'a'<=ch && ch<='f' ) return ch-'a'+10;
|
throw new Exception("Hash type not supported!");
|
||||||
return -1;
|
}
|
||||||
|
return hasher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final char[] hexCode = "0123456789abcdef".toCharArray();
|
public static Object getHash(String type, String value) throws Exception {
|
||||||
|
if (hashTypeConversion.containsKey(type)) {
|
||||||
public static String printHexBinary(byte[] data) {
|
return hashTypeConversion.get(type).getHash(value);
|
||||||
StringBuilder r = new StringBuilder(data.length*2);
|
|
||||||
for ( byte b : data) {
|
|
||||||
r.append(hexCode[(b >> 4) & 0xF]);
|
|
||||||
r.append(hexCode[(b & 0xF)]);
|
|
||||||
}
|
}
|
||||||
return r.toString();
|
throw new Exception("Hash type not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package link.infra.packwiz.installer.metadata.hash;
|
||||||
|
|
||||||
|
import okio.HashingSource;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
|
public class HasherHashingSource implements IHasher {
|
||||||
|
String type;
|
||||||
|
|
||||||
|
public HasherHashingSource(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// i love naming things
|
||||||
|
private class HashingSourceGeneralHashingSource extends GeneralHashingSource {
|
||||||
|
HashingSource delegateHashing;
|
||||||
|
HashingSourceHash value;
|
||||||
|
|
||||||
|
public HashingSourceGeneralHashingSource(HashingSource delegate) {
|
||||||
|
super(delegate);
|
||||||
|
delegateHashing = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getHash() {
|
||||||
|
if (value == null) {
|
||||||
|
value = new HashingSourceHash(delegateHashing.hash().hex());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// this some funky inner class stuff
|
||||||
|
// each of these classes is specific to the instance of the HasherHashingSource
|
||||||
|
// therefore HashingSourceHashes from different parent instances will be not instanceof each other
|
||||||
|
private class HashingSourceHash {
|
||||||
|
String value;
|
||||||
|
private HashingSourceHash(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof HashingSourceHash)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HashingSourceHash objHash = (HashingSourceHash) obj;
|
||||||
|
if (value != null) {
|
||||||
|
return value.equals(objHash.value);
|
||||||
|
} else {
|
||||||
|
return objHash.value == null ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeneralHashingSource getHashingSource(Source delegate) {
|
||||||
|
switch (type) {
|
||||||
|
case "sha256":
|
||||||
|
return new HashingSourceGeneralHashingSource(HashingSource.sha256(delegate));
|
||||||
|
// TODO: support other hash types
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Invalid hash type provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getHash(String value) {
|
||||||
|
return new HashingSourceHash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package link.infra.packwiz.installer.metadata.hash;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class HasherMessageDigest implements IHasher {
|
|
||||||
MessageDigest md;
|
|
||||||
|
|
||||||
public HasherMessageDigest(String hashType) throws NoSuchAlgorithmException {
|
|
||||||
md = MessageDigest.getInstance(hashType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] data) {
|
|
||||||
md.update(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] data, int offset, int length) {
|
|
||||||
md.update(data, offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte data) {
|
|
||||||
md.update(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return HashUtils.printHexBinary(md.digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce case insensitivity
|
|
||||||
@Override
|
|
||||||
public boolean equalValues(String a, String b) {
|
|
||||||
if (a == null) {
|
|
||||||
if (b == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return a.equalsIgnoreCase(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +1,8 @@
|
|||||||
package link.infra.packwiz.installer.metadata.hash;
|
package link.infra.packwiz.installer.metadata.hash;
|
||||||
|
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public interface IHasher {
|
public interface IHasher {
|
||||||
public void update(byte[] data);
|
public GeneralHashingSource getHashingSource(Source delegate);
|
||||||
public void update(byte[] data, int offset, int length);
|
public Object getHash(String value);
|
||||||
public void update(byte data);
|
|
||||||
public String get();
|
|
||||||
public default boolean equalValues(String a, String b) {
|
|
||||||
if (a == null) {
|
|
||||||
if (b == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return a.equals(b);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
package link.infra.packwiz.installer.request;
|
package link.infra.packwiz.installer.request;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.request.handlers.RequestHandlerGithub;
|
import link.infra.packwiz.installer.request.handlers.RequestHandlerGithub;
|
||||||
import link.infra.packwiz.installer.request.handlers.RequestHandlerHTTP;
|
import link.infra.packwiz.installer.request.handlers.RequestHandlerHTTP;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public abstract class HandlerManager {
|
public abstract class HandlerManager {
|
||||||
|
|
||||||
@ -35,14 +35,14 @@ public abstract class HandlerManager {
|
|||||||
// Zip handler discards once read, requesting multiple times on other handlers would cause multiple downloads
|
// Zip handler discards once read, requesting multiple times on other handlers would cause multiple downloads
|
||||||
// Caching system? Copy from already downloaded files?
|
// Caching system? Copy from already downloaded files?
|
||||||
|
|
||||||
public static InputStream getFileInputStream(URI loc) throws Exception {
|
public static Source getFileSource(URI loc) throws Exception {
|
||||||
for (IRequestHandler handler : handlers) {
|
for (IRequestHandler handler : handlers) {
|
||||||
if (handler.matchesHandler(loc)) {
|
if (handler.matchesHandler(loc)) {
|
||||||
InputStream stream = handler.getFileInputStream(loc);
|
Source src = handler.getFileSource(loc);
|
||||||
if (stream == null) {
|
if (src == null) {
|
||||||
throw new Exception("Couldn't find URI: " + loc.toString());
|
throw new Exception("Couldn't find URI: " + loc.toString());
|
||||||
} else {
|
} else {
|
||||||
return stream;
|
return src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,25 +50,6 @@ public abstract class HandlerManager {
|
|||||||
throw new Exception("No handler available for URI: " + loc.toString());
|
throw new Exception("No handler available for URI: " + loc.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// To enqueue stuff:
|
|
||||||
// private ExecutorService threadPool = Executors.newFixedThreadPool(10);
|
|
||||||
// CompletionService<InputStream> completionService = new ExecutorCompletionService<InputStream>(threadPool);
|
|
||||||
//
|
|
||||||
// public Future<InputStream> enqueue(URI loc) {
|
|
||||||
// for (IRequestHandler handler : handlers) {
|
|
||||||
// if (handler.matchesHandler(loc)) {
|
|
||||||
// return completionService.submit(new Callable<InputStream>() {
|
|
||||||
// public InputStream call() {
|
|
||||||
// return handler.getFileInputStream(loc);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // TODO: throw error??
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// Use completionService.take() to get (waits until available) a Future<InputStream>, where you can call .get() and handle exceptions etc
|
|
||||||
|
|
||||||
// github toml resolution
|
// github toml resolution
|
||||||
// e.g. https://github.com/comp500/Demagnetize -> demagnetize.toml
|
// e.g. https://github.com/comp500/Demagnetize -> demagnetize.toml
|
||||||
// https://github.com/comp500/Demagnetize/blob/master/demagnetize.toml
|
// https://github.com/comp500/Demagnetize/blob/master/demagnetize.toml
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package link.infra.packwiz.installer.request;
|
package link.infra.packwiz.installer.request;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IRequestHandler handles requests for locations specified in modpack metadata.
|
* IRequestHandler handles requests for locations specified in modpack metadata.
|
||||||
*/
|
*/
|
||||||
@ -15,12 +16,12 @@ public interface IRequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the InputStream for a location. Must be threadsafe.
|
* Gets the Source for a location. Must be threadsafe.
|
||||||
* It is assumed that each location is read only once for the duration of an IRequestHandler.
|
* It is assumed that each location is read only once for the duration of an IRequestHandler.
|
||||||
* @param loc The location to be read
|
* @param loc The location to be read
|
||||||
* @return The InputStream containing the data of the file
|
* @return The Source containing the data of the file
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public InputStream getFileInputStream(URI loc) throws Exception;
|
public Source getFileSource(URI loc) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package link.infra.packwiz.installer.request.handlers;
|
package link.infra.packwiz.installer.request.handlers;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.request.IRequestHandler;
|
import link.infra.packwiz.installer.request.IRequestHandler;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public class RequestHandlerHTTP implements IRequestHandler {
|
public class RequestHandlerHTTP implements IRequestHandler {
|
||||||
|
|
||||||
@ -15,14 +16,14 @@ public class RequestHandlerHTTP implements IRequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getFileInputStream(URI loc) throws Exception {
|
public Source getFileSource(URI loc) throws Exception {
|
||||||
URLConnection conn = loc.toURL().openConnection();
|
URLConnection conn = loc.toURL().openConnection();
|
||||||
// TODO: when do we send specific headers??? should there be a way to signal this?
|
// TODO: when do we send specific headers??? should there be a way to signal this?
|
||||||
// github *sometimes* requires it, sometimes not!
|
// github *sometimes* requires it, sometimes not!
|
||||||
//conn.addRequestProperty("Accept", "application/octet-stream");
|
//conn.addRequestProperty("Accept", "application/octet-stream");
|
||||||
// 30 second read timeout
|
// 30 second read timeout
|
||||||
conn.setReadTimeout(30 * 1000);
|
conn.setReadTimeout(30 * 1000);
|
||||||
return conn.getInputStream();
|
return Okio.source(conn.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package link.infra.packwiz.installer.request.handlers;
|
package link.infra.packwiz.installer.request.handlers;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -14,6 +11,11 @@ import java.util.function.Predicate;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
import okio.Buffer;
|
||||||
|
import okio.BufferedSource;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Source;
|
||||||
|
|
||||||
public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
||||||
|
|
||||||
protected final boolean modeHasFolder;
|
protected final boolean modeHasFolder;
|
||||||
@ -33,31 +35,33 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
private class ZipReader {
|
private class ZipReader {
|
||||||
|
|
||||||
private final ZipInputStream zis;
|
private final ZipInputStream zis;
|
||||||
private final Map<URI, byte[]> readFiles = new HashMap<URI, byte[]>();
|
private final Map<URI, Buffer> readFiles = new HashMap<URI, Buffer>();
|
||||||
// Write lock implies access to ZipInputStream - only 1 thread must read at a time!
|
// Write lock implies access to ZipInputStream - only 1 thread must read at a time!
|
||||||
final ReentrantLock filesLock = new ReentrantLock();
|
final ReentrantLock filesLock = new ReentrantLock();
|
||||||
private ZipEntry entry;
|
private ZipEntry entry;
|
||||||
|
|
||||||
public ZipReader(InputStream zip) {
|
private final BufferedSource zipSource;
|
||||||
zis = new ZipInputStream(zip);
|
|
||||||
|
public ZipReader(Source zip) {
|
||||||
|
zis = new ZipInputStream(Okio.buffer(zip).inputStream());
|
||||||
|
zipSource = Okio.buffer(Okio.source(zis));
|
||||||
}
|
}
|
||||||
|
|
||||||
// File lock must be obtained before calling this function
|
// File lock must be obtained before calling this function
|
||||||
private byte[] readCurrFile() throws IOException {
|
private Buffer readCurrFile() throws IOException {
|
||||||
byte[] bytes = new byte[(int) entry.getSize()];
|
Buffer fileBuffer = new Buffer();
|
||||||
DataInputStream dis = new DataInputStream(zis);
|
zipSource.readFully(fileBuffer, entry.getSize());
|
||||||
dis.readFully(bytes);
|
return fileBuffer;
|
||||||
return bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// File lock must be obtained before calling this function
|
// File lock must be obtained before calling this function
|
||||||
private byte[] findFile(URI loc) throws IOException, URISyntaxException {
|
private Buffer findFile(URI loc) throws IOException, URISyntaxException {
|
||||||
while (true) {
|
while (true) {
|
||||||
entry = zis.getNextEntry();
|
entry = zis.getNextEntry();
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
byte[] data = readCurrFile();
|
Buffer data = readCurrFile();
|
||||||
URI fileLoc = new URI(removeFolder(entry.getName()));
|
URI fileLoc = new URI(removeFolder(entry.getName()));
|
||||||
if (loc.equals(fileLoc)) {
|
if (loc.equals(fileLoc)) {
|
||||||
return data;
|
return data;
|
||||||
@ -67,19 +71,19 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getFileInputStream(URI loc) throws Exception {
|
public Source getFileSource(URI loc) throws Exception {
|
||||||
filesLock.lock();
|
filesLock.lock();
|
||||||
// Assume files are only read once, allow GC by removing
|
// Assume files are only read once, allow GC by removing
|
||||||
byte[] file = readFiles.remove(loc);
|
Buffer file = readFiles.remove(loc);
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
filesLock.unlock();
|
filesLock.unlock();
|
||||||
return new ByteArrayInputStream(file);
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = findFile(loc);
|
file = findFile(loc);
|
||||||
filesLock.unlock();
|
filesLock.unlock();
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
return new ByteArrayInputStream(file);
|
return file;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -99,7 +103,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
filesLock.unlock();
|
filesLock.unlock();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
byte[] data = readCurrFile();
|
Buffer data = readCurrFile();
|
||||||
URI fileLoc = new URI(removeFolder(entry.getName()));
|
URI fileLoc = new URI(removeFolder(entry.getName()));
|
||||||
readFiles.put(fileLoc, data);
|
readFiles.put(fileLoc, data);
|
||||||
if (matches.test(fileLoc)) {
|
if (matches.test(fileLoc)) {
|
||||||
@ -122,7 +126,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
public abstract boolean matchesHandler(URI loc);
|
public abstract boolean matchesHandler(URI loc);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getFileInputStream(URI loc) throws Exception {
|
public Source getFileSource(URI loc) throws Exception {
|
||||||
URI zipUri = getZipUri(loc);
|
URI zipUri = getZipUri(loc);
|
||||||
cacheLock.readLock().lock();
|
cacheLock.readLock().lock();
|
||||||
ZipReader zr = cache.get(zipUri);
|
ZipReader zr = cache.get(zipUri);
|
||||||
@ -132,18 +136,18 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
// Recheck, because unlocking read lock allows another thread to modify it
|
// Recheck, because unlocking read lock allows another thread to modify it
|
||||||
zr = cache.get(zipUri);
|
zr = cache.get(zipUri);
|
||||||
if (zr == null) {
|
if (zr == null) {
|
||||||
InputStream str = super.getFileInputStream(zipUri);
|
Source src = super.getFileSource(zipUri);
|
||||||
if (str == null) {
|
if (src == null) {
|
||||||
cacheLock.writeLock().unlock();
|
cacheLock.writeLock().unlock();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
zr = new ZipReader(str);
|
zr = new ZipReader(src);
|
||||||
cache.put(zipUri, zr);
|
cache.put(zipUri, zr);
|
||||||
}
|
}
|
||||||
cacheLock.writeLock().unlock();
|
cacheLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return zr.getFileInputStream(getLocationInZip(loc));
|
return zr.getFileSource(getLocationInZip(loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected URI findInZip(URI loc, Predicate<URI> matches) throws Exception {
|
protected URI findInZip(URI loc, Predicate<URI> matches) throws Exception {
|
||||||
@ -156,7 +160,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
|
|||||||
// Recheck, because unlocking read lock allows another thread to modify it
|
// Recheck, because unlocking read lock allows another thread to modify it
|
||||||
zr = cache.get(zipUri);
|
zr = cache.get(zipUri);
|
||||||
if (zr == null) {
|
if (zr == null) {
|
||||||
zr = new ZipReader(super.getFileInputStream(zipUri));
|
zr = new ZipReader(super.getFileSource(zipUri));
|
||||||
cache.put(zipUri, zr);
|
cache.put(zipUri, zr);
|
||||||
}
|
}
|
||||||
cacheLock.writeLock().unlock();
|
cacheLock.writeLock().unlock();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user