1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.repository.legacy;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.lang.reflect.Method;
24 import java.security.NoSuchAlgorithmException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import org.apache.maven.artifact.Artifact;
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.plugin.LegacySupport;
35 import org.apache.maven.wagon.ConnectionException;
36 import org.apache.maven.wagon.ResourceDoesNotExistException;
37 import org.apache.maven.wagon.TransferFailedException;
38 import org.apache.maven.wagon.UnsupportedProtocolException;
39 import org.apache.maven.wagon.Wagon;
40 import org.apache.maven.wagon.authentication.AuthenticationException;
41 import org.apache.maven.wagon.authentication.AuthenticationInfo;
42 import org.apache.maven.wagon.authorization.AuthorizationException;
43 import org.apache.maven.wagon.events.TransferListener;
44 import org.apache.maven.wagon.observers.ChecksumObserver;
45 import org.apache.maven.wagon.proxy.ProxyInfo;
46 import org.apache.maven.wagon.repository.Repository;
47 import org.codehaus.plexus.PlexusContainer;
48 import org.codehaus.plexus.component.annotations.Component;
49 import org.codehaus.plexus.component.annotations.Requirement;
50 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
51 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
52 import org.codehaus.plexus.logging.Logger;
53 import org.codehaus.plexus.util.FileUtils;
54 import org.eclipse.aether.ConfigurationProperties;
55 import org.eclipse.aether.util.ConfigUtils;
56
57
58
59
60
61
62
63
64 @Component(role = WagonManager.class)
65 public class DefaultWagonManager implements WagonManager {
66
67 private static final String[] CHECKSUM_IDS = {"md5", "sha1"};
68
69
70
71
72 private static final String[] CHECKSUM_ALGORITHMS = {"MD5", "SHA-1"};
73
74 @Requirement
75 private Logger logger;
76
77 @Requirement
78 private PlexusContainer container;
79
80 @Requirement
81 private UpdateCheckManager updateCheckManager;
82
83 @Requirement
84 private LegacySupport legacySupport;
85
86
87
88
89 @Override
90 public void getArtifact(
91 Artifact artifact, ArtifactRepository repository, TransferListener downloadMonitor, boolean force)
92 throws TransferFailedException, ResourceDoesNotExistException {
93 String remotePath = repository.pathOf(artifact);
94
95 ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
96
97 if (!policy.isEnabled()) {
98 logger.debug(
99 "Skipping disabled repository " + repository.getId() + " for resolution of " + artifact.getId());
100
101 } else if (artifact.isSnapshot() || !artifact.getFile().exists()) {
102 if (force || updateCheckManager.isUpdateRequired(artifact, repository)) {
103 logger.debug("Trying repository " + repository.getId() + " for resolution of " + artifact.getId()
104 + " from " + remotePath);
105
106 try {
107 getRemoteFile(
108 repository,
109 artifact.getFile(),
110 remotePath,
111 downloadMonitor,
112 policy.getChecksumPolicy(),
113 false);
114
115 updateCheckManager.touch(artifact, repository, null);
116 } catch (ResourceDoesNotExistException e) {
117 updateCheckManager.touch(artifact, repository, null);
118 throw e;
119 } catch (TransferFailedException e) {
120 String error = (e.getMessage() != null)
121 ? e.getMessage()
122 : e.getClass().getSimpleName();
123 updateCheckManager.touch(artifact, repository, error);
124 throw e;
125 }
126
127 logger.debug(" Artifact " + artifact.getId() + " resolved to " + artifact.getFile());
128
129 artifact.setResolved(true);
130 } else if (!artifact.getFile().exists()) {
131 String error = updateCheckManager.getError(artifact, repository);
132 if (error != null) {
133 throw new TransferFailedException("Failure to resolve " + remotePath + " from "
134 + repository.getUrl()
135 + " was cached in the local repository. "
136 + "Resolution will not be reattempted until the update interval of "
137 + repository.getId() + " has elapsed or updates are forced. Original error: " + error);
138
139 } else {
140 throw new ResourceDoesNotExistException(
141 "Failure to resolve " + remotePath + " from " + repository.getUrl()
142 + " was cached in the local repository. "
143 + "Resolution will not be reattempted until the update interval of "
144 + repository.getId() + " has elapsed or updates are forced.");
145 }
146 }
147 }
148 }
149
150 @Override
151 public void getArtifact(
152 Artifact artifact,
153 List<ArtifactRepository> remoteRepositories,
154 TransferListener downloadMonitor,
155 boolean force)
156 throws TransferFailedException, ResourceDoesNotExistException {
157 TransferFailedException tfe = null;
158
159 for (ArtifactRepository repository : remoteRepositories) {
160 try {
161 getArtifact(artifact, repository, downloadMonitor, force);
162
163 if (artifact.isResolved()) {
164 artifact.setRepository(repository);
165 break;
166 }
167 } catch (ResourceDoesNotExistException e) {
168
169
170
171 logger.debug(
172 "Unable to find artifact " + artifact.getId() + " in repository " + repository.getId() + " ("
173 + repository.getUrl() + ")",
174 e);
175
176 } catch (TransferFailedException e) {
177 tfe = e;
178
179 String msg = "Unable to get artifact " + artifact.getId() + " from repository " + repository.getId()
180 + " (" + repository.getUrl() + "): " + e.getMessage();
181
182 if (logger.isDebugEnabled()) {
183 logger.warn(msg, e);
184 } else {
185 logger.warn(msg);
186 }
187 }
188 }
189
190
191 if (!artifact.getFile().exists()) {
192 if (tfe != null) {
193 throw tfe;
194 } else {
195 throw new ResourceDoesNotExistException("Unable to download the artifact from any repository");
196 }
197 }
198 }
199
200 @Override
201 public void getArtifactMetadata(
202 ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy)
203 throws TransferFailedException, ResourceDoesNotExistException {
204 String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata);
205
206 getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true);
207 }
208
209 @Override
210 public void getArtifactMetadataFromDeploymentRepository(
211 ArtifactMetadata metadata, ArtifactRepository repository, File destination, String checksumPolicy)
212 throws TransferFailedException, ResourceDoesNotExistException {
213 String remotePath = repository.pathOfRemoteRepositoryMetadata(metadata);
214
215 getRemoteFile(repository, destination, remotePath, null, checksumPolicy, true);
216 }
217
218
219
220
221
222
223
224
225
226
227 private void connectWagon(Wagon wagon, ArtifactRepository repository)
228 throws ConnectionException, AuthenticationException {
229
230
231 if (legacySupport.getRepositorySession() != null) {
232 String userAgent = ConfigUtils.getString(
233 legacySupport.getRepositorySession(), null, ConfigurationProperties.USER_AGENT);
234
235 if (userAgent == null) {
236 Properties headers = new Properties();
237
238 headers.put(
239 "User-Agent",
240 ConfigUtils.getString(
241 legacySupport.getRepositorySession(), "Maven", ConfigurationProperties.USER_AGENT));
242 try {
243 Method setHttpHeaders = wagon.getClass().getMethod("setHttpHeaders", Properties.class);
244 setHttpHeaders.invoke(wagon, headers);
245 } catch (NoSuchMethodException e) {
246
247 } catch (Exception e) {
248 logger.debug("Could not set user agent for wagon "
249 + wagon.getClass().getName() + ": " + e);
250 }
251 }
252 }
253
254 if (repository.getProxy() != null && logger.isDebugEnabled()) {
255 logger.debug("Using proxy " + repository.getProxy().getHost() + ":"
256 + repository.getProxy().getPort() + " for " + repository.getUrl());
257 }
258
259 if (repository.getAuthentication() != null && repository.getProxy() != null) {
260 wagon.connect(
261 new Repository(repository.getId(), repository.getUrl()),
262 authenticationInfo(repository),
263 proxyInfo(repository));
264
265 } else if (repository.getAuthentication() != null) {
266 wagon.connect(new Repository(repository.getId(), repository.getUrl()), authenticationInfo(repository));
267
268 } else if (repository.getProxy() != null) {
269 wagon.connect(new Repository(repository.getId(), repository.getUrl()), proxyInfo(repository));
270 } else {
271 wagon.connect(new Repository(repository.getId(), repository.getUrl()));
272 }
273 }
274
275 private AuthenticationInfo authenticationInfo(ArtifactRepository repository) {
276 AuthenticationInfo ai = new AuthenticationInfo();
277 ai.setUserName(repository.getAuthentication().getUsername());
278 ai.setPassword(repository.getAuthentication().getPassword());
279 return ai;
280 }
281
282 private ProxyInfo proxyInfo(ArtifactRepository repository) {
283 ProxyInfo proxyInfo = new ProxyInfo();
284 proxyInfo.setHost(repository.getProxy().getHost());
285 proxyInfo.setType(repository.getProxy().getProtocol());
286 proxyInfo.setPort(repository.getProxy().getPort());
287 proxyInfo.setNonProxyHosts(repository.getProxy().getNonProxyHosts());
288 proxyInfo.setUserName(repository.getProxy().getUserName());
289 proxyInfo.setPassword(repository.getProxy().getPassword());
290 return proxyInfo;
291 }
292
293 @SuppressWarnings("checkstyle:methodlength")
294 @Override
295 public void getRemoteFile(
296 ArtifactRepository repository,
297 File destination,
298 String remotePath,
299 TransferListener downloadMonitor,
300 String checksumPolicy,
301 boolean force)
302 throws TransferFailedException, ResourceDoesNotExistException {
303 String protocol = repository.getProtocol();
304
305 Wagon wagon;
306
307 try {
308 wagon = getWagon(protocol);
309 } catch (UnsupportedProtocolException e) {
310 throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e);
311 }
312
313 if (downloadMonitor != null) {
314 wagon.addTransferListener(downloadMonitor);
315 }
316
317 File temp = new File(destination + ".tmp");
318
319 temp.deleteOnExit();
320
321 boolean downloaded = false;
322
323 try {
324 connectWagon(wagon, repository);
325
326 boolean firstRun = true;
327 boolean retry = true;
328
329
330
331
332 while (firstRun || retry) {
333 ChecksumObserver md5ChecksumObserver = null;
334 ChecksumObserver sha1ChecksumObserver = null;
335 try {
336
337 int i = 0;
338
339 md5ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
340 sha1ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
341
342
343 retry = false;
344
345
346 if (destination.exists() && !force) {
347 try {
348 downloaded = wagon.getIfNewer(remotePath, temp, destination.lastModified());
349
350 if (!downloaded) {
351
352 destination.setLastModified(System.currentTimeMillis());
353 }
354 } catch (UnsupportedOperationException e) {
355
356 wagon.get(remotePath, temp);
357
358 downloaded = true;
359 }
360 } else {
361 wagon.get(remotePath, temp);
362 downloaded = true;
363 }
364 } finally {
365 wagon.removeTransferListener(md5ChecksumObserver);
366 wagon.removeTransferListener(sha1ChecksumObserver);
367 }
368
369 if (downloaded) {
370
371 if (downloadMonitor != null) {
372 wagon.removeTransferListener(downloadMonitor);
373 }
374
375
376 try {
377 verifyChecksum(sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon);
378 } catch (ChecksumFailedException e) {
379
380
381
382
383 if (firstRun) {
384 logger.warn("*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING");
385 retry = true;
386 } else {
387 handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
388 }
389 } catch (ResourceDoesNotExistException sha1TryException) {
390 logger.debug("SHA1 not found, trying MD5: " + sha1TryException.getMessage());
391
392
393
394 try {
395 verifyChecksum(md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon);
396 } catch (ChecksumFailedException e) {
397
398
399 if (firstRun) {
400 retry = true;
401 } else {
402 handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
403 }
404 } catch (ResourceDoesNotExistException md5TryException) {
405
406 handleChecksumFailure(
407 checksumPolicy,
408 "Error retrieving checksum file for " + remotePath,
409 md5TryException);
410 }
411 }
412
413
414 if (downloadMonitor != null) {
415 wagon.addTransferListener(downloadMonitor);
416 }
417 }
418
419
420 firstRun = false;
421 }
422 } catch (ConnectionException e) {
423 throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
424 } catch (AuthenticationException e) {
425 throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
426 } catch (AuthorizationException e) {
427 throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
428 } finally {
429
430 if (downloadMonitor != null) {
431 wagon.removeTransferListener(downloadMonitor);
432 }
433
434 disconnectWagon(wagon);
435
436 releaseWagon(protocol, wagon);
437 }
438
439 if (downloaded) {
440 if (!temp.exists()) {
441 throw new ResourceDoesNotExistException("Downloaded file does not exist: " + temp);
442 }
443
444
445
446
447
448
449 if (!temp.renameTo(destination)) {
450 try {
451 FileUtils.copyFile(temp, destination);
452
453 if (!temp.delete()) {
454 temp.deleteOnExit();
455 }
456 } catch (IOException e) {
457 throw new TransferFailedException(
458 "Error copying temporary file to the final destination: " + e.getMessage(), e);
459 }
460 }
461 }
462 }
463
464
465
466
467 @Override
468 public void putArtifact(
469 File source, Artifact artifact, ArtifactRepository deploymentRepository, TransferListener downloadMonitor)
470 throws TransferFailedException {
471 putRemoteFile(deploymentRepository, source, deploymentRepository.pathOf(artifact), downloadMonitor);
472 }
473
474 @Override
475 public void putArtifactMetadata(File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository)
476 throws TransferFailedException {
477 logger.info("Uploading " + artifactMetadata);
478 putRemoteFile(repository, source, repository.pathOfRemoteRepositoryMetadata(artifactMetadata), null);
479 }
480
481 @Override
482 public void putRemoteFile(
483 ArtifactRepository repository, File source, String remotePath, TransferListener downloadMonitor)
484 throws TransferFailedException {
485 String protocol = repository.getProtocol();
486
487 Wagon wagon;
488 try {
489 wagon = getWagon(protocol);
490 } catch (UnsupportedProtocolException e) {
491 throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e);
492 }
493
494 if (downloadMonitor != null) {
495 wagon.addTransferListener(downloadMonitor);
496 }
497
498 Map<String, ChecksumObserver> checksums = new HashMap<>(2);
499
500 Map<String, String> sums = new HashMap<>(2);
501
502
503 for (int i = 0; i < CHECKSUM_IDS.length; i++) {
504 checksums.put(CHECKSUM_IDS[i], addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i]));
505 }
506
507 List<File> temporaryFiles = new ArrayList<>();
508
509 try {
510 try {
511 connectWagon(wagon, repository);
512
513 wagon.put(source, remotePath);
514 } finally {
515 if (downloadMonitor != null) {
516 wagon.removeTransferListener(downloadMonitor);
517 }
518 }
519
520
521 for (String extension : checksums.keySet()) {
522 ChecksumObserver observer = checksums.get(extension);
523 sums.put(extension, observer.getActualChecksum());
524 }
525
526
527 for (String extension : checksums.keySet()) {
528
529 File temp = File.createTempFile("maven-artifact", null);
530 temp.deleteOnExit();
531 FileUtils.fileWrite(temp.getAbsolutePath(), "UTF-8", sums.get(extension));
532
533 temporaryFiles.add(temp);
534 wagon.put(temp, remotePath + "." + extension);
535 }
536 } catch (ConnectionException e) {
537 throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
538 } catch (AuthenticationException e) {
539 throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
540 } catch (AuthorizationException e) {
541 throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
542 } catch (ResourceDoesNotExistException e) {
543 throw new TransferFailedException("Resource to deploy not found: " + e.getMessage(), e);
544 } catch (IOException e) {
545 throw new TransferFailedException("Error creating temporary file for deployment: " + e.getMessage(), e);
546 } finally {
547
548 cleanupTemporaryFiles(temporaryFiles);
549
550
551 for (String id : CHECKSUM_IDS) {
552 TransferListener checksumListener = checksums.get(id);
553 if (checksumListener != null) {
554 wagon.removeTransferListener(checksumListener);
555 }
556 }
557
558 disconnectWagon(wagon);
559
560 releaseWagon(protocol, wagon);
561 }
562 }
563
564 private void cleanupTemporaryFiles(List<File> files) {
565 for (File file : files) {
566
567 if (!file.delete()) {
568 logger.warn("skip failed to delete temporary file : " + file.getAbsolutePath());
569 file.deleteOnExit();
570 }
571 }
572 }
573
574 private ChecksumObserver addChecksumObserver(Wagon wagon, String algorithm) throws TransferFailedException {
575 try {
576 ChecksumObserver checksumObserver = new ChecksumObserver(algorithm);
577 wagon.addTransferListener(checksumObserver);
578 return checksumObserver;
579 } catch (NoSuchAlgorithmException e) {
580 throw new TransferFailedException("Unable to add checksum for unsupported algorithm " + algorithm, e);
581 }
582 }
583
584 private void handleChecksumFailure(String checksumPolicy, String message, Throwable cause)
585 throws ChecksumFailedException {
586 if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(checksumPolicy)) {
587 throw new ChecksumFailedException(message, cause);
588 } else if (!ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy)) {
589
590 logger.warn("*** CHECKSUM FAILED - " + message + " - IGNORING");
591 }
592
593 }
594
595 private void verifyChecksum(
596 ChecksumObserver checksumObserver,
597 File destination,
598 File tempDestination,
599 String remotePath,
600 String checksumFileExtension,
601 Wagon wagon)
602 throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException {
603 try {
604
605 String actualChecksum = checksumObserver.getActualChecksum();
606
607 File tempChecksumFile = new File(tempDestination + checksumFileExtension + ".tmp");
608 tempChecksumFile.deleteOnExit();
609 wagon.get(remotePath + checksumFileExtension, tempChecksumFile);
610
611 String expectedChecksum = FileUtils.fileRead(tempChecksumFile, "UTF-8");
612
613
614 expectedChecksum = expectedChecksum.trim();
615
616
617 if (expectedChecksum.regionMatches(true, 0, "MD", 0, 2)
618 || expectedChecksum.regionMatches(true, 0, "SHA", 0, 3)) {
619 int lastSpacePos = expectedChecksum.lastIndexOf(' ');
620 expectedChecksum = expectedChecksum.substring(lastSpacePos + 1);
621 } else {
622
623 int spacePos = expectedChecksum.indexOf(' ');
624
625 if (spacePos != -1) {
626 expectedChecksum = expectedChecksum.substring(0, spacePos);
627 }
628 }
629 if (expectedChecksum.equalsIgnoreCase(actualChecksum)) {
630 File checksumFile = new File(destination + checksumFileExtension);
631 if (checksumFile.exists()) {
632 checksumFile.delete();
633 }
634 FileUtils.copyFile(tempChecksumFile, checksumFile);
635 if (!tempChecksumFile.delete()) {
636 tempChecksumFile.deleteOnExit();
637 }
638 } else {
639 throw new ChecksumFailedException("Checksum failed on download: local = '" + actualChecksum
640 + "'; remote = '" + expectedChecksum + "'");
641 }
642 } catch (IOException e) {
643 throw new ChecksumFailedException("Invalid checksum file", e);
644 }
645 }
646
647 private void disconnectWagon(Wagon wagon) {
648 try {
649 wagon.disconnect();
650 } catch (ConnectionException e) {
651 logger.error("Problem disconnecting from wagon - ignoring: " + e.getMessage());
652 }
653 }
654
655 private void releaseWagon(String protocol, Wagon wagon) {
656 try {
657 container.release(wagon);
658 } catch (ComponentLifecycleException e) {
659 logger.error("Problem releasing wagon - ignoring: " + e.getMessage());
660 logger.debug("", e);
661 }
662 }
663
664 @Override
665 @Deprecated
666 public Wagon getWagon(Repository repository) throws UnsupportedProtocolException {
667 return getWagon(repository.getProtocol());
668 }
669
670 @Override
671 @Deprecated
672 public Wagon getWagon(String protocol) throws UnsupportedProtocolException {
673 if (protocol == null) {
674 throw new UnsupportedProtocolException("Unspecified protocol");
675 }
676
677 String hint = protocol.toLowerCase(java.util.Locale.ENGLISH);
678
679 Wagon wagon;
680 try {
681 wagon = container.lookup(Wagon.class, hint);
682 } catch (ComponentLookupException e) {
683 throw new UnsupportedProtocolException(
684 "Cannot find wagon which supports the requested protocol: " + protocol, e);
685 }
686
687 return wagon;
688 }
689 }