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