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