aQute.libg.shacache.ShaCache Maven / Gradle / Ivy
The newest version!
package aQute.libg.shacache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Pattern;
import aQute.lib.io.IO;
import aQute.libg.cryptography.SHA1;
/**
* Provide a standardized cache based on the SHA-1 of a file.
*/
public class ShaCache {
static Pattern SHA_P = Pattern.compile("[A-F0-9]{40,40}", Pattern.CASE_INSENSITIVE);
private final File root;
/**
* Create a SHA-1 cache on a directory.
*
* @param root the directory
*/
public ShaCache(File root) {
this.root = root;
try {
IO.mkdirs(this.root);
} catch (IOException e) {
throw new IllegalArgumentException("Cannot create shacache root directory " + root, e);
}
}
/**
* Return a stream that is associated with a SHA. If the SHA is not in the
* local cache, the given sources parameter can specify a way to get the
* content.
*
* @param sha the sha
* @param sources objects that can retrieve the original data
* @return the stream or null if not found.
*/
public InputStream getStream(String sha, ShaSource... sources) throws Exception {
//
// Must be a valid SHA otherwise could be used to traverse the file
// system
//
if (!SHA_P.matcher(sha)
.matches())
throw new IllegalArgumentException("Not a SHA");
//
// Get the file
//
File f = new File(root, sha);
if (!f.isFile()) {
//
// Not found, try the sources
//
for (ShaSource s : sources) {
try {
InputStream in = s.get(sha);
if (in == null)
continue;
//
// If the source is a fast source we should
// not cache it
//
if (s.isFast())
return in;
//
// Create a unique temporary file
// and copy it.
//
File tmp = IO.createTempFile(root, sha.toLowerCase(), ".shacache");
IO.copy(in, tmp);
String digest = SHA1.digest(tmp)
.asHex();
if (digest.equalsIgnoreCase(sha)) {
//
// Atomic rename. So even if it is downloaded multiple
// times we end up with one copy and the SHA makes it
// unique with the content.
//
IO.rename(tmp, f);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//
// Check if we succeeded
//
if (!f.isFile())
return null;
return IO.stream(f);
}
/**
* Small variation on the cache that returns a file instead of a stream
*
* @param sha the SHA-1
* @param sources the inputs
* @return a file or null
*/
public File getFile(String sha, ShaSource... sources) throws Exception {
//
// Must be a valid SHA otherwise could be used to traverse the file
// system
//
if (!SHA_P.matcher(sha)
.matches())
throw new IllegalArgumentException("Not a SHA");
//
// See if we already got it
//
File f = new File(root, sha);
if (f.isFile())
return f;
for (ShaSource s : sources) {
try {
InputStream in = s.get(sha);
if (in != null) {
File tmp = IO.createTempFile(root, sha.toLowerCase(), ".shacache");
IO.copy(in, tmp);
String digest = SHA1.digest(tmp)
.asHex();
if (digest.equalsIgnoreCase(sha)) {
IO.rename(tmp, f);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (!f.isFile())
return null;
return f;
}
/**
* Clean the cache
*
* @throws Exception
*/
public void purge() throws Exception {
IO.deleteWithException(root);
IO.mkdirs(root);
}
/**
* Get the root to the cache
*/
public File getRoot() {
return root;
}
}