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