1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.artifact.repository.metadata;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.nio.file.Files;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31
32 import org.apache.maven.artifact.metadata.ArtifactMetadata;
33 import org.apache.maven.artifact.repository.ArtifactRepository;
34 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
35 import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
36 import org.apache.maven.artifact.repository.RepositoryRequest;
37 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
38 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
39 import org.apache.maven.repository.legacy.UpdateCheckManager;
40 import org.apache.maven.repository.legacy.WagonManager;
41 import org.apache.maven.wagon.ResourceDoesNotExistException;
42 import org.apache.maven.wagon.TransferFailedException;
43 import org.codehaus.plexus.component.annotations.Component;
44 import org.codehaus.plexus.component.annotations.Requirement;
45 import org.codehaus.plexus.logging.AbstractLogEnabled;
46 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
47
48
49
50
51 @Component(role = RepositoryMetadataManager.class)
52 public class DefaultRepositoryMetadataManager extends AbstractLogEnabled implements RepositoryMetadataManager {
53 @Requirement
54 private WagonManager wagonManager;
55
56 @Requirement
57 private UpdateCheckManager updateCheckManager;
58
59 public void resolve(
60 RepositoryMetadata metadata,
61 List<ArtifactRepository> remoteRepositories,
62 ArtifactRepository localRepository)
63 throws RepositoryMetadataResolutionException {
64 RepositoryRequest request = new DefaultRepositoryRequest();
65 request.setLocalRepository(localRepository);
66 request.setRemoteRepositories(remoteRepositories);
67 resolve(metadata, request);
68 }
69
70 public void resolve(RepositoryMetadata metadata, RepositoryRequest request)
71 throws RepositoryMetadataResolutionException {
72 ArtifactRepository localRepo = request.getLocalRepository();
73 List<ArtifactRepository> remoteRepositories = request.getRemoteRepositories();
74
75 if (!request.isOffline()) {
76 Date localCopyLastModified = null;
77 if (metadata.getBaseVersion() != null) {
78 localCopyLastModified = getLocalCopyLastModified(localRepo, metadata);
79 }
80
81 for (ArtifactRepository repository : remoteRepositories) {
82 ArtifactRepositoryPolicy policy = metadata.getPolicy(repository);
83
84 File file =
85 new File(localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata(metadata, repository));
86 boolean update;
87
88 if (!policy.isEnabled()) {
89 update = false;
90
91 if (getLogger().isDebugEnabled()) {
92 getLogger()
93 .debug("Skipping update check for " + metadata.getKey() + " (" + file
94 + ") from disabled repository " + repository.getId() + " ("
95 + repository.getUrl() + ")");
96 }
97 } else if (request.isForceUpdate()) {
98 update = true;
99 } else if (localCopyLastModified != null && !policy.checkOutOfDate(localCopyLastModified)) {
100 update = false;
101
102 if (getLogger().isDebugEnabled()) {
103 getLogger()
104 .debug("Skipping update check for " + metadata.getKey() + " (" + file
105 + ") from repository " + repository.getId() + " (" + repository.getUrl()
106 + ") in favor of local copy");
107 }
108 } else {
109 update = updateCheckManager.isUpdateRequired(metadata, repository, file);
110 }
111
112 if (update) {
113 getLogger().info(metadata.getKey() + ": checking for updates from " + repository.getId());
114 try {
115 wagonManager.getArtifactMetadata(metadata, repository, file, policy.getChecksumPolicy());
116 } catch (ResourceDoesNotExistException e) {
117 getLogger().debug(metadata + " could not be found on repository: " + repository.getId());
118
119
120 if (file.exists()) {
121 if (!file.delete()) {
122
123 try {
124 Thread.sleep(10);
125 } catch (InterruptedException ie) {
126
127 }
128 file.delete();
129 }
130 }
131 } catch (TransferFailedException e) {
132 getLogger()
133 .warn(metadata + " could not be retrieved from repository: " + repository.getId()
134 + " due to an error: " + e.getMessage());
135 getLogger().debug("Exception", e);
136 } finally {
137 updateCheckManager.touch(metadata, repository, file);
138 }
139 }
140
141
142
143 if (file.exists()) {
144 file.setLastModified(System.currentTimeMillis());
145 }
146 }
147 }
148
149 try {
150 mergeMetadata(metadata, remoteRepositories, localRepo);
151 } catch (RepositoryMetadataStoreException e) {
152 throw new RepositoryMetadataResolutionException(
153 "Unable to store local copy of metadata: " + e.getMessage(), e);
154 }
155 }
156
157 private Date getLocalCopyLastModified(ArtifactRepository localRepository, RepositoryMetadata metadata) {
158 String metadataPath = localRepository.pathOfLocalRepositoryMetadata(metadata, localRepository);
159 File metadataFile = new File(localRepository.getBasedir(), metadataPath);
160 return metadataFile.isFile() ? new Date(metadataFile.lastModified()) : null;
161 }
162
163 private void mergeMetadata(
164 RepositoryMetadata metadata,
165 List<ArtifactRepository> remoteRepositories,
166 ArtifactRepository localRepository)
167 throws RepositoryMetadataStoreException {
168
169
170
171
172
173 Map<ArtifactRepository, Metadata> previousMetadata = new HashMap<>();
174 ArtifactRepository selected = null;
175 for (ArtifactRepository repository : remoteRepositories) {
176 ArtifactRepositoryPolicy policy = metadata.getPolicy(repository);
177
178 if (policy.isEnabled() && loadMetadata(metadata, repository, localRepository, previousMetadata)) {
179 metadata.setRepository(repository);
180 selected = repository;
181 }
182 }
183 if (loadMetadata(metadata, localRepository, localRepository, previousMetadata)) {
184 metadata.setRepository(null);
185 selected = localRepository;
186 }
187
188 updateSnapshotMetadata(metadata, previousMetadata, selected, localRepository);
189 }
190
191 private void updateSnapshotMetadata(
192 RepositoryMetadata metadata,
193 Map<ArtifactRepository, Metadata> previousMetadata,
194 ArtifactRepository selected,
195 ArtifactRepository localRepository)
196 throws RepositoryMetadataStoreException {
197
198 if (metadata.isSnapshot()) {
199 Metadata prevMetadata = metadata.getMetadata();
200
201 for (ArtifactRepository repository : previousMetadata.keySet()) {
202 Metadata m = previousMetadata.get(repository);
203 if (repository.equals(selected)) {
204 if (m.getVersioning() == null) {
205 m.setVersioning(new Versioning());
206 }
207
208 if (m.getVersioning().getSnapshot() == null) {
209 m.getVersioning().setSnapshot(new Snapshot());
210 }
211 } else {
212 if ((m.getVersioning() != null)
213 && (m.getVersioning().getSnapshot() != null)
214 && m.getVersioning().getSnapshot().isLocalCopy()) {
215 m.getVersioning().getSnapshot().setLocalCopy(false);
216 metadata.setMetadata(m);
217 metadata.storeInLocalRepository(localRepository, repository);
218 }
219 }
220 }
221
222 metadata.setMetadata(prevMetadata);
223 }
224 }
225
226 private boolean loadMetadata(
227 RepositoryMetadata repoMetadata,
228 ArtifactRepository remoteRepository,
229 ArtifactRepository localRepository,
230 Map<ArtifactRepository, Metadata> previousMetadata) {
231 boolean setRepository = false;
232
233 File metadataFile = new File(
234 localRepository.getBasedir(),
235 localRepository.pathOfLocalRepositoryMetadata(repoMetadata, remoteRepository));
236
237 if (metadataFile.exists()) {
238 Metadata metadata;
239
240 try {
241 metadata = readMetadata(metadataFile);
242 } catch (RepositoryMetadataReadException e) {
243 if (getLogger().isDebugEnabled()) {
244 getLogger().warn(e.getMessage(), e);
245 } else {
246 getLogger().warn(e.getMessage());
247 }
248 return setRepository;
249 }
250
251 if (repoMetadata.isSnapshot() && (previousMetadata != null)) {
252 previousMetadata.put(remoteRepository, metadata);
253 }
254
255 if (repoMetadata.getMetadata() != null) {
256 setRepository = repoMetadata.getMetadata().merge(metadata);
257 } else {
258 repoMetadata.setMetadata(metadata);
259 setRepository = true;
260 }
261 }
262 return setRepository;
263 }
264
265
266
267
268 protected Metadata readMetadata(File mappingFile) throws RepositoryMetadataReadException {
269
270 MetadataXpp3Reader mappingReader = new MetadataXpp3Reader();
271 try (InputStream in = Files.newInputStream(mappingFile.toPath())) {
272 Metadata result = mappingReader.read(in, false);
273 return result;
274 } catch (FileNotFoundException e) {
275 throw new RepositoryMetadataReadException("Cannot read metadata from '" + mappingFile + "'", e);
276 } catch (IOException | XmlPullParserException e) {
277 throw new RepositoryMetadataReadException(
278 "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e);
279 }
280 }
281
282
283
284
285
286 private void fixTimestamp(File metadataFile, Metadata metadata, Metadata reference) {
287 boolean changed = false;
288
289 if (metadata != null && reference != null) {
290 Versioning versioning = metadata.getVersioning();
291 Versioning versioningRef = reference.getVersioning();
292 if (versioning != null && versioningRef != null) {
293 String lastUpdated = versioning.getLastUpdated();
294 String now = versioningRef.getLastUpdated();
295 if (lastUpdated != null && now != null && now.compareTo(lastUpdated) < 0) {
296 getLogger()
297 .warn("The last updated timestamp in " + metadataFile + " refers to the future (now = "
298 + now
299 + ", lastUpdated = " + lastUpdated + "). Please verify that the clocks of all"
300 + " deploying machines are reasonably synchronized.");
301 versioning.setLastUpdated(now);
302 changed = true;
303 }
304 }
305 }
306
307 if (changed) {
308 getLogger().debug("Repairing metadata in " + metadataFile);
309
310 try (OutputStream out = Files.newOutputStream(metadataFile.toPath())) {
311 new MetadataXpp3Writer().write(out, metadata);
312 } catch (IOException e) {
313 String msg = "Could not write fixed metadata to " + metadataFile + ": " + e.getMessage();
314 if (getLogger().isDebugEnabled()) {
315 getLogger().warn(msg, e);
316 } else {
317 getLogger().warn(msg);
318 }
319 }
320 }
321 }
322
323 public void resolveAlways(
324 RepositoryMetadata metadata, ArtifactRepository localRepository, ArtifactRepository remoteRepository)
325 throws RepositoryMetadataResolutionException {
326 File file;
327 try {
328 file = getArtifactMetadataFromDeploymentRepository(metadata, localRepository, remoteRepository);
329 } catch (TransferFailedException e) {
330 throw new RepositoryMetadataResolutionException(
331 metadata + " could not be retrieved from repository: " + remoteRepository.getId()
332 + " due to an error: " + e.getMessage(),
333 e);
334 }
335
336 try {
337 if (file.exists()) {
338 Metadata prevMetadata = readMetadata(file);
339 metadata.setMetadata(prevMetadata);
340 }
341 } catch (RepositoryMetadataReadException e) {
342 throw new RepositoryMetadataResolutionException(e.getMessage(), e);
343 }
344 }
345
346 private File getArtifactMetadataFromDeploymentRepository(
347 ArtifactMetadata metadata, ArtifactRepository localRepo, ArtifactRepository remoteRepository)
348 throws TransferFailedException {
349 File file =
350 new File(localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata(metadata, remoteRepository));
351
352 try {
353 wagonManager.getArtifactMetadataFromDeploymentRepository(
354 metadata, remoteRepository, file, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN);
355 } catch (ResourceDoesNotExistException e) {
356 getLogger()
357 .info(metadata + " could not be found on repository: " + remoteRepository.getId()
358 + ", so will be created");
359
360
361 if (file.exists()) {
362 if (!file.delete()) {
363
364 try {
365 Thread.sleep(10);
366 } catch (InterruptedException ie) {
367
368 }
369 file.delete();
370 }
371 }
372 } finally {
373 if (metadata instanceof RepositoryMetadata) {
374 updateCheckManager.touch((RepositoryMetadata) metadata, remoteRepository, file);
375 }
376 }
377 return file;
378 }
379
380 public void deploy(
381 ArtifactMetadata metadata, ArtifactRepository localRepository, ArtifactRepository deploymentRepository)
382 throws RepositoryMetadataDeploymentException {
383 File file;
384 if (metadata instanceof RepositoryMetadata) {
385 getLogger().info("Retrieving previous metadata from " + deploymentRepository.getId());
386 try {
387 file = getArtifactMetadataFromDeploymentRepository(metadata, localRepository, deploymentRepository);
388 } catch (TransferFailedException e) {
389 throw new RepositoryMetadataDeploymentException(
390 metadata + " could not be retrieved from repository: " + deploymentRepository.getId()
391 + " due to an error: " + e.getMessage(),
392 e);
393 }
394
395 if (file.isFile()) {
396 try {
397 fixTimestamp(file, readMetadata(file), ((RepositoryMetadata) metadata).getMetadata());
398 } catch (RepositoryMetadataReadException e) {
399
400 }
401 }
402 } else {
403
404 file = new File(
405 localRepository.getBasedir(),
406 localRepository.pathOfLocalRepositoryMetadata(metadata, deploymentRepository));
407 }
408
409 try {
410 metadata.storeInLocalRepository(localRepository, deploymentRepository);
411 } catch (RepositoryMetadataStoreException e) {
412 throw new RepositoryMetadataDeploymentException("Error installing metadata: " + e.getMessage(), e);
413 }
414
415 try {
416 wagonManager.putArtifactMetadata(file, metadata, deploymentRepository);
417 } catch (TransferFailedException e) {
418 throw new RepositoryMetadataDeploymentException("Error while deploying metadata: " + e.getMessage(), e);
419 }
420 }
421
422 public void install(ArtifactMetadata metadata, ArtifactRepository localRepository)
423 throws RepositoryMetadataInstallationException {
424 try {
425 metadata.storeInLocalRepository(localRepository, localRepository);
426 } catch (RepositoryMetadataStoreException e) {
427 throw new RepositoryMetadataInstallationException("Error installing metadata: " + e.getMessage(), e);
428 }
429 }
430 }