blob: f23ba822b6443bef0e26ef27d81f5ef5b1730c8c [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.bnd.maven.support;
2
3import java.io.*;
4import java.net.*;
5import java.security.*;
6import java.util.*;
7import java.util.concurrent.*;
8
9import aQute.lib.hex.*;
10import aQute.lib.io.*;
11import aQute.libg.filelock.*;
12
13/**
14 * An entry (a group/artifact) in the maven cache in the .m2/repository
15 * directory. It provides methods to get the pom and the artifact.
16 *
17 */
18public class MavenEntry implements Closeable {
19 final Maven maven;
20 final File root;
21 final File dir;
22 final String path;
23 final DirectoryLock lock;
24 final Map<URI, CachedPom> poms = new HashMap<URI, CachedPom>();
25 final File pomFile;
26 final File artifactFile;
27 final String pomPath;
28 final File propertiesFile;
29 Properties properties;
30 private boolean propertiesChanged;
31 FutureTask<File> artifact;
32 private String artifactPath;
33
34 /**
35 * Constructor.
36 *
37 * @param maven
38 * @param path
39 */
40 MavenEntry(Maven maven, String path) {
41 this.root = maven.repository;
42 this.maven = maven;
43 this.path = path;
44 this.pomPath = path + ".pom";
45 this.artifactPath = path + ".jar";
46 this.dir = IO.getFile(maven.repository, path).getParentFile();
47 this.dir.mkdirs();
48 this.pomFile = new File(maven.repository, pomPath);
49 this.artifactFile = new File(maven.repository, artifactPath);
50 this.propertiesFile = new File(dir, "bnd.properties");
51 this.lock = new DirectoryLock(dir, 5 * 60000); // 5 mins
52 }
53
54 /**
55 * This is the method to get the POM for a cached entry.
56 *
57 * @param urls
58 * The allowed URLs
59 * @return a CachedPom for this maven entry
60 *
61 * @throws Exception
62 * If something goes haywire
63 */
64 public CachedPom getPom(URI[] urls) throws Exception {
65
66 // First check if we have the pom cached in memory
67 synchronized (this) {
68 // Try to find it in the in-memory cache
69 for (URI url : urls) {
70 CachedPom pom = poms.get(url);
71 if (pom != null)
72 return pom;
73 }
74 }
75
76 // Ok, we need to see if it exists on disk
77
78 // lock.lock();
79 try {
80
81 if (isValid()) {
82 // Check if one of our repos had the correct file.
83 for (URI url : urls) {
84 String valid = getProperty(url.toASCIIString());
85 if (valid != null)
86 return createPom(url);
87 }
88
89 // we have the info, but have to verify that it
90 // exists in one of our repos but we do not have
91 // to download it as our cache is already ok.
92 for (URI url : urls) {
93 if (verify(url, pomPath)) {
94 return createPom(url);
95 }
96 }
97
98 // It does not exist in out repo
99 // so we have to fail even though we do have
100 // the file.
101
102 } else {
103 dir.mkdirs();
104 // We really do not have the file
105 // so we have to find out who has it.
106 for (final URI url : urls) {
107
108 if (download(url, pomPath)) {
109 if (verify(url, pomPath)) {
110 artifact = new FutureTask<File>(new Callable<File>() {
111
112 public File call() throws Exception {
113 if (download(url, artifactPath)) {
114 verify(url, artifactPath);
115 }
116 return artifactFile;
117 }
118
119 });
120 maven.executor.execute(artifact);
121 return createPom(url);
122 }
123 }
124 }
125 }
126 return null;
127 } finally {
128 saveProperties();
129 // lock.release();
130 }
131 }
132
133 /**
134 * Download a resource from the given repo.
135 *
136 * @param url
137 * The base url for the repo
138 * @param path
139 * The path part
140 * @return
141 * @throws MalformedURLException
142 */
143 private boolean download(URI repo, String path) throws MalformedURLException {
144 try {
145 URL url = toURL(repo, path);
146 System.out.println("Downloading " + repo + " path " + path + " url " + url);
147 File file = new File(root, path);
148 IO.copy(url.openStream(), file);
149 System.out.println("Downloaded " + url);
150 return true;
151 } catch (Exception e) {
152 System.err.println("debug: " + e);
153 return false;
154 }
155 }
156
157 /**
158 * Converts a repo + path to a URL..
159 *
160 * @param base
161 * The base repo
162 * @param path
163 * The path in the directory + url
164 * @return a URL that points to the file in the repo
165 *
166 * @throws MalformedURLException
167 */
168 URL toURL(URI base, String path) throws MalformedURLException {
169 StringBuilder r = new StringBuilder();
170 r.append(base.toString());
171 if (r.charAt(r.length() - 1) != '/')
172 r.append('/');
173 r.append(path);
174 return new URL(r.toString());
175 }
176
177 /**
178 * Check if this is a valid cache directory, might probably need some more
179 * stuff.
180 *
181 * @return true if valid
182 */
183 private boolean isValid() {
184 return pomFile.isFile() && pomFile.length() > 100 && artifactFile.isFile()
185 && artifactFile.length() > 100;
186 }
187
188 /**
189 * We maintain a set of bnd properties in the cache directory.
190 *
191 * @param key
192 * The key for the property
193 * @param value
194 * The value for the property
195 */
196 private void setProperty(String key, String value) {
197 Properties properties = getProperties();
198 properties.setProperty(key, value);
199 propertiesChanged = true;
200 }
201
202 /**
203 * Answer the properties, loading if needed.
204 */
205 protected Properties getProperties() {
206 if (properties == null) {
207 properties = new Properties();
208 File props = new File(dir, "bnd.properties");
209 if (props.exists()) {
210 try {
211 FileInputStream in = new FileInputStream(props);
212 properties.load(in);
213 } catch (Exception e) {
214 // we ignore for now, will handle it on safe
215 }
216 }
217 }
218 return properties;
219 }
220
221 /**
222 * Answer a property.
223 *
224 * @param key
225 * The key
226 * @return The value
227 */
228 private String getProperty(String key) {
229 Properties properties = getProperties();
230 return properties.getProperty(key);
231 }
232
233 private void saveProperties() throws IOException {
234 if (propertiesChanged) {
235 FileOutputStream fout = new FileOutputStream(propertiesFile);
236 try {
237 properties.store(fout, "");
238 } finally {
239 properties = null;
240 propertiesChanged = false;
241 fout.close();
242 }
243 }
244 }
245
246 /**
247 * Help function to create the POM and record its source.
248 *
249 * @param url
250 * the repo from which it was constructed
251 * @return the new pom
252 * @throws Exception
253 */
254 private CachedPom createPom(URI url) throws Exception {
255 CachedPom pom = new CachedPom(this, url);
256 pom.parse();
257 poms.put(url, pom);
258 setProperty(url.toASCIIString(), "true");
259 return pom;
260 }
261
262 /**
263 * Verify that the repo has a checksum file for the given path and that this
264 * checksum matchs.
265 *
266 * @param repo
267 * The repo
268 * @param path
269 * The file id
270 * @return true if there is a digest and it matches one of the algorithms
271 * @throws Exception
272 */
273 boolean verify(URI repo, String path) throws Exception {
274 for (String algorithm : Maven.ALGORITHMS) {
275 if (verify(repo, path, algorithm))
276 return true;
277 }
278 return false;
279 }
280
281 /**
282 * Verify the path against its digest for the given algorithm.
283 *
284 * @param repo
285 * @param path
286 * @param algorithm
287 * @return
288 * @throws Exception
289 */
290 private boolean verify(URI repo, String path, String algorithm) throws Exception {
291 String digestPath = path + "." + algorithm;
292 File actualFile = new File(root, path);
293
294 if (download(repo, digestPath)) {
295 File digestFile = new File(root, digestPath);
296 final MessageDigest md = MessageDigest.getInstance(algorithm);
297 IO.copy(actualFile, new OutputStream() {
298 @Override public void write(int c) throws IOException {
299 md.update((byte) c);
300 }
301
302 @Override public void write(byte[] buffer, int offset, int length) {
303 md.update(buffer, offset, length);
304 }
305 });
306 byte[] digest = md.digest();
307 String source = IO.collect(digestFile).toUpperCase();
308 String hex = Hex.toHexString(digest).toUpperCase();
309 if (source.startsWith(hex)) {
310 System.out.println("Verified ok " + actualFile + " digest " + algorithm);
311 return true;
312 }
313 }
314 System.out.println("Failed to verify " + actualFile + " for digest " + algorithm);
315 return false;
316 }
317
318 public File getArtifact() throws Exception {
319 if (artifact == null )
320 return artifactFile;
321 return artifact.get();
322 }
323
324 public File getPomFile() {
325 return pomFile;
326 }
327
328 public void close() throws IOException {
329
330 }
331
332 public void remove() {
333 if (dir.getParentFile() != null) {
334 IO.delete(dir);
335 }
336 }
337
338}