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