| 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); |
| } |
| } |
| |
| } |