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