blob: f23ba822b6443bef0e26ef27d81f5ef5b1730c8c [file] [log] [blame]
package aQute.bnd.maven.support;
import java.io.*;
import java.net.*;
import java.security.*;
import java.util.*;
import java.util.concurrent.*;
import aQute.lib.hex.*;
import aQute.lib.io.*;
import aQute.libg.filelock.*;
/**
* An entry (a group/artifact) in the maven cache in the .m2/repository
* directory. It provides methods to get the pom and the artifact.
*
*/
public class MavenEntry implements Closeable {
final Maven maven;
final File root;
final File dir;
final String path;
final DirectoryLock lock;
final Map<URI, CachedPom> poms = new HashMap<URI, CachedPom>();
final File pomFile;
final File artifactFile;
final String pomPath;
final File propertiesFile;
Properties properties;
private boolean propertiesChanged;
FutureTask<File> artifact;
private String artifactPath;
/**
* Constructor.
*
* @param maven
* @param path
*/
MavenEntry(Maven maven, String path) {
this.root = maven.repository;
this.maven = maven;
this.path = path;
this.pomPath = path + ".pom";
this.artifactPath = path + ".jar";
this.dir = IO.getFile(maven.repository, path).getParentFile();
this.dir.mkdirs();
this.pomFile = new File(maven.repository, pomPath);
this.artifactFile = new File(maven.repository, artifactPath);
this.propertiesFile = new File(dir, "bnd.properties");
this.lock = new DirectoryLock(dir, 5 * 60000); // 5 mins
}
/**
* This is the method to get the POM for a cached entry.
*
* @param urls
* The allowed URLs
* @return a CachedPom for this maven entry
*
* @throws Exception
* If something goes haywire
*/
public CachedPom getPom(URI[] urls) throws Exception {
// First check if we have the pom cached in memory
synchronized (this) {
// Try to find it in the in-memory cache
for (URI url : urls) {
CachedPom pom = poms.get(url);
if (pom != null)
return pom;
}
}
// Ok, we need to see if it exists on disk
// lock.lock();
try {
if (isValid()) {
// Check if one of our repos had the correct file.
for (URI url : urls) {
String valid = getProperty(url.toASCIIString());
if (valid != null)
return createPom(url);
}
// we have the info, but have to verify that it
// exists in one of our repos but we do not have
// to download it as our cache is already ok.
for (URI url : urls) {
if (verify(url, pomPath)) {
return createPom(url);
}
}
// It does not exist in out repo
// so we have to fail even though we do have
// the file.
} else {
dir.mkdirs();
// We really do not have the file
// so we have to find out who has it.
for (final URI url : urls) {
if (download(url, pomPath)) {
if (verify(url, pomPath)) {
artifact = new FutureTask<File>(new Callable<File>() {
public File call() throws Exception {
if (download(url, artifactPath)) {
verify(url, artifactPath);
}
return artifactFile;
}
});
maven.executor.execute(artifact);
return createPom(url);
}
}
}
}
return null;
} finally {
saveProperties();
// lock.release();
}
}
/**
* Download a resource from the given repo.
*
* @param url
* The base url for the repo
* @param path
* The path part
* @return
* @throws MalformedURLException
*/
private boolean download(URI repo, String path) throws MalformedURLException {
try {
URL url = toURL(repo, path);
System.out.println("Downloading " + repo + " path " + path + " url " + url);
File file = new File(root, path);
IO.copy(url.openStream(), file);
System.out.println("Downloaded " + url);
return true;
} catch (Exception e) {
System.err.println("debug: " + e);
return false;
}
}
/**
* Converts a repo + path to a URL..
*
* @param base
* The base repo
* @param path
* The path in the directory + url
* @return a URL that points to the file in the repo
*
* @throws MalformedURLException
*/
URL toURL(URI base, String path) throws MalformedURLException {
StringBuilder r = new StringBuilder();
r.append(base.toString());
if (r.charAt(r.length() - 1) != '/')
r.append('/');
r.append(path);
return new URL(r.toString());
}
/**
* Check if this is a valid cache directory, might probably need some more
* stuff.
*
* @return true if valid
*/
private boolean isValid() {
return pomFile.isFile() && pomFile.length() > 100 && artifactFile.isFile()
&& artifactFile.length() > 100;
}
/**
* We maintain a set of bnd properties in the cache directory.
*
* @param key
* The key for the property
* @param value
* The value for the property
*/
private void setProperty(String key, String value) {
Properties properties = getProperties();
properties.setProperty(key, value);
propertiesChanged = true;
}
/**
* Answer the properties, loading if needed.
*/
protected Properties getProperties() {
if (properties == null) {
properties = new Properties();
File props = new File(dir, "bnd.properties");
if (props.exists()) {
try {
FileInputStream in = new FileInputStream(props);
properties.load(in);
} catch (Exception e) {
// we ignore for now, will handle it on safe
}
}
}
return properties;
}
/**
* Answer a property.
*
* @param key
* The key
* @return The value
*/
private String getProperty(String key) {
Properties properties = getProperties();
return properties.getProperty(key);
}
private void saveProperties() throws IOException {
if (propertiesChanged) {
FileOutputStream fout = new FileOutputStream(propertiesFile);
try {
properties.store(fout, "");
} finally {
properties = null;
propertiesChanged = false;
fout.close();
}
}
}
/**
* Help function to create the POM and record its source.
*
* @param url
* the repo from which it was constructed
* @return the new pom
* @throws Exception
*/
private CachedPom createPom(URI url) throws Exception {
CachedPom pom = new CachedPom(this, url);
pom.parse();
poms.put(url, pom);
setProperty(url.toASCIIString(), "true");
return pom;
}
/**
* Verify that the repo has a checksum file for the given path and that this
* checksum matchs.
*
* @param repo
* The repo
* @param path
* The file id
* @return true if there is a digest and it matches one of the algorithms
* @throws Exception
*/
boolean verify(URI repo, String path) throws Exception {
for (String algorithm : Maven.ALGORITHMS) {
if (verify(repo, path, algorithm))
return true;
}
return false;
}
/**
* Verify the path against its digest for the given algorithm.
*
* @param repo
* @param path
* @param algorithm
* @return
* @throws Exception
*/
private boolean verify(URI repo, String path, String algorithm) throws Exception {
String digestPath = path + "." + algorithm;
File actualFile = new File(root, path);
if (download(repo, digestPath)) {
File digestFile = new File(root, digestPath);
final MessageDigest md = MessageDigest.getInstance(algorithm);
IO.copy(actualFile, new OutputStream() {
@Override public void write(int c) throws IOException {
md.update((byte) c);
}
@Override public void write(byte[] buffer, int offset, int length) {
md.update(buffer, offset, length);
}
});
byte[] digest = md.digest();
String source = IO.collect(digestFile).toUpperCase();
String hex = Hex.toHexString(digest).toUpperCase();
if (source.startsWith(hex)) {
System.out.println("Verified ok " + actualFile + " digest " + algorithm);
return true;
}
}
System.out.println("Failed to verify " + actualFile + " for digest " + algorithm);
return false;
}
public File getArtifact() throws Exception {
if (artifact == null )
return artifactFile;
return artifact.get();
}
public File getPomFile() {
return pomFile;
}
public void close() throws IOException {
}
public void remove() {
if (dir.getParentFile() != null) {
IO.delete(dir);
}
}
}