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 int i = 0;
347
348 md5ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
349 sha1ChecksumObserver = addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i++]);
350
351
352 retry = false;
353
354
355 if (destination.exists() && !force) {
356 try {
357 downloaded = wagon.getIfNewer(remotePath, temp, destination.lastModified());
358
359 if (!downloaded) {
360
361 destination.setLastModified(System.currentTimeMillis());
362 }
363 } catch (UnsupportedOperationException e) {
364
365 wagon.get(remotePath, temp);
366
367 downloaded = true;
368 }
369 } else {
370 wagon.get(remotePath, temp);
371 downloaded = true;
372 }
373 } finally {
374 wagon.removeTransferListener(md5ChecksumObserver);
375 wagon.removeTransferListener(sha1ChecksumObserver);
376 }
377
378 if (downloaded) {
379
380 if (downloadMonitor != null) {
381 wagon.removeTransferListener(downloadMonitor);
382 }
383
384
385 try {
386 verifyChecksum(sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon);
387 } catch (ChecksumFailedException e) {
388
389
390
391
392 if (firstRun) {
393 logger.warn("*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING");
394 retry = true;
395 } else {
396 handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
397 }
398 } catch (ResourceDoesNotExistException sha1TryException) {
399 logger.debug("SHA1 not found, trying MD5: " + sha1TryException.getMessage());
400
401
402
403 try {
404 verifyChecksum(md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon);
405 } catch (ChecksumFailedException e) {
406
407
408 if (firstRun) {
409 retry = true;
410 } else {
411 handleChecksumFailure(checksumPolicy, e.getMessage(), e.getCause());
412 }
413 } catch (ResourceDoesNotExistException md5TryException) {
414
415 handleChecksumFailure(
416 checksumPolicy,
417 "Error retrieving checksum file for " + remotePath,
418 md5TryException);
419 }
420 }
421
422
423 if (downloadMonitor != null) {
424 wagon.addTransferListener(downloadMonitor);
425 }
426 }
427
428
429 firstRun = false;
430 }
431 } catch (ConnectionException e) {
432 throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
433 } catch (AuthenticationException e) {
434 throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
435 } catch (AuthorizationException e) {
436 throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
437 } finally {
438
439 if (downloadMonitor != null) {
440 wagon.removeTransferListener(downloadMonitor);
441 }
442
443 disconnectWagon(wagon);
444
445 releaseWagon(protocol, wagon);
446 }
447
448 if (downloaded) {
449 if (!temp.exists()) {
450 throw new ResourceDoesNotExistException("Downloaded file does not exist: " + temp);
451 }
452
453
454
455
456
457
458 if (!temp.renameTo(destination)) {
459 try {
460 Files.copy(
461 temp.toPath(),
462 destination.toPath(),
463 StandardCopyOption.REPLACE_EXISTING,
464 StandardCopyOption.COPY_ATTRIBUTES);
465
466 if (!temp.delete()) {
467 temp.deleteOnExit();
468 }
469 } catch (IOException e) {
470 throw new TransferFailedException(
471 "Error copying temporary file to the final destination: " + e.getMessage(), e);
472 }
473 }
474 }
475 }
476
477
478
479
480 @Override
481 public void putArtifact(
482 File source, Artifact artifact, ArtifactRepository deploymentRepository, TransferListener downloadMonitor)
483 throws TransferFailedException {
484 putRemoteFile(deploymentRepository, source, deploymentRepository.pathOf(artifact), downloadMonitor);
485 }
486
487 @Override
488 public void putArtifactMetadata(File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository)
489 throws TransferFailedException {
490 logger.info("Uploading " + artifactMetadata);
491 putRemoteFile(repository, source, repository.pathOfRemoteRepositoryMetadata(artifactMetadata), null);
492 }
493
494 @Override
495 public void putRemoteFile(
496 ArtifactRepository repository, File source, String remotePath, TransferListener downloadMonitor)
497 throws TransferFailedException {
498 String protocol = repository.getProtocol();
499
500 Wagon wagon;
501 try {
502 wagon = getWagon(protocol);
503 } catch (UnsupportedProtocolException e) {
504 throw new TransferFailedException("Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e);
505 }
506
507 if (downloadMonitor != null) {
508 wagon.addTransferListener(downloadMonitor);
509 }
510
511 Map<String, ChecksumObserver> checksums = new HashMap<>(2);
512
513 Map<String, String> sums = new HashMap<>(2);
514
515
516 for (int i = 0; i < CHECKSUM_IDS.length; i++) {
517 checksums.put(CHECKSUM_IDS[i], addChecksumObserver(wagon, CHECKSUM_ALGORITHMS[i]));
518 }
519
520 List<File> temporaryFiles = new ArrayList<>();
521
522 try {
523 try {
524 connectWagon(wagon, repository);
525
526 wagon.put(source, remotePath);
527 } finally {
528 if (downloadMonitor != null) {
529 wagon.removeTransferListener(downloadMonitor);
530 }
531 }
532
533
534 for (String extension : checksums.keySet()) {
535 ChecksumObserver observer = checksums.get(extension);
536 sums.put(extension, observer.getActualChecksum());
537 }
538
539
540 for (String extension : checksums.keySet()) {
541
542 File temp = File.createTempFile("maven-artifact", null);
543 temp.deleteOnExit();
544 byte[] bytes = sums.get(extension).getBytes(StandardCharsets.UTF_8);
545 Files.write(
546 Paths.get(temp.getAbsolutePath()), bytes, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
547
548 temporaryFiles.add(temp);
549 wagon.put(temp, remotePath + "." + extension);
550 }
551 } catch (ConnectionException e) {
552 throw new TransferFailedException("Connection failed: " + e.getMessage(), e);
553 } catch (AuthenticationException e) {
554 throw new TransferFailedException("Authentication failed: " + e.getMessage(), e);
555 } catch (AuthorizationException e) {
556 throw new TransferFailedException("Authorization failed: " + e.getMessage(), e);
557 } catch (ResourceDoesNotExistException e) {
558 throw new TransferFailedException("Resource to deploy not found: " + e.getMessage(), e);
559 } catch (IOException e) {
560 throw new TransferFailedException("Error creating temporary file for deployment: " + e.getMessage(), e);
561 } finally {
562
563 cleanupTemporaryFiles(temporaryFiles);
564
565
566 for (String id : CHECKSUM_IDS) {
567 TransferListener checksumListener = checksums.get(id);
568 if (checksumListener != null) {
569 wagon.removeTransferListener(checksumListener);
570 }
571 }
572
573 disconnectWagon(wagon);
574
575 releaseWagon(protocol, wagon);
576 }
577 }
578
579 private void cleanupTemporaryFiles(List<File> files) {
580 for (File file : files) {
581
582 if (!file.delete()) {
583 logger.warn("skip failed to delete temporary file : " + file.getAbsolutePath());
584 file.deleteOnExit();
585 }
586 }
587 }
588
589 private ChecksumObserver addChecksumObserver(Wagon wagon, String algorithm) throws TransferFailedException {
590 try {
591 ChecksumObserver checksumObserver = new ChecksumObserver(algorithm);
592 wagon.addTransferListener(checksumObserver);
593 return checksumObserver;
594 } catch (NoSuchAlgorithmException e) {
595 throw new TransferFailedException("Unable to add checksum for unsupported algorithm " + algorithm, e);
596 }
597 }
598
599 private void handleChecksumFailure(String checksumPolicy, String message, Throwable cause)
600 throws ChecksumFailedException {
601 if (ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals(checksumPolicy)) {
602 throw new ChecksumFailedException(message, cause);
603 } else if (!ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals(checksumPolicy)) {
604
605 logger.warn("*** CHECKSUM FAILED - " + message + " - IGNORING");
606 }
607
608 }
609
610 private void verifyChecksum(
611 ChecksumObserver checksumObserver,
612 File destination,
613 File tempDestination,
614 String remotePath,
615 String checksumFileExtension,
616 Wagon wagon)
617 throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException {
618 try {
619
620 String actualChecksum = checksumObserver.getActualChecksum();
621
622 File tempChecksumFile = new File(tempDestination + checksumFileExtension + ".tmp");
623 tempChecksumFile.deleteOnExit();
624 wagon.get(remotePath + checksumFileExtension, tempChecksumFile);
625 byte[] bytes = Files.readAllBytes(tempChecksumFile.toPath());
626 String expectedChecksum = new String(bytes, StandardCharsets.UTF_8);
627
628
629 expectedChecksum = expectedChecksum.trim();
630
631
632 if (expectedChecksum.regionMatches(true, 0, "MD", 0, 2)
633 || expectedChecksum.regionMatches(true, 0, "SHA", 0, 3)) {
634 int lastSpacePos = expectedChecksum.lastIndexOf(' ');
635 expectedChecksum = expectedChecksum.substring(lastSpacePos + 1);
636 } else {
637
638 int spacePos = expectedChecksum.indexOf(' ');
639
640 if (spacePos != -1) {
641 expectedChecksum = expectedChecksum.substring(0, spacePos);
642 }
643 }
644 if (expectedChecksum.equalsIgnoreCase(actualChecksum)) {
645 File checksumFile = new File(destination + checksumFileExtension);
646 if (checksumFile.exists()) {
647 checksumFile.delete();
648 }
649 Files.copy(
650 tempChecksumFile.toPath(),
651 checksumFile.toPath(),
652 StandardCopyOption.REPLACE_EXISTING,
653 StandardCopyOption.COPY_ATTRIBUTES);
654
655 if (!tempChecksumFile.delete()) {
656 tempChecksumFile.deleteOnExit();
657 }
658 } else {
659 throw new ChecksumFailedException("Checksum failed on download: local = '" + actualChecksum
660 + "'; remote = '" + expectedChecksum + "'");
661 }
662 } catch (IOException e) {
663 throw new ChecksumFailedException("Invalid checksum file", e);
664 }
665 }
666
667 private void disconnectWagon(Wagon wagon) {
668 try {
669 wagon.disconnect();
670 } catch (ConnectionException e) {
671 logger.error("Problem disconnecting from wagon - ignoring: " + e.getMessage());
672 }
673 }
674
675 private void releaseWagon(String protocol, Wagon wagon) {
676 try {
677 container.release(wagon);
678 } catch (ComponentLifecycleException e) {
679 logger.error("Problem releasing wagon - ignoring: " + e.getMessage());
680 logger.debug("", e);
681 }
682 }
683
684 @Override
685 @Deprecated
686 public Wagon getWagon(Repository repository) throws UnsupportedProtocolException {
687 return getWagon(repository.getProtocol());
688 }
689
690 @Override
691 @Deprecated
692 public Wagon getWagon(String protocol) throws UnsupportedProtocolException {
693 if (protocol == null) {
694 throw new UnsupportedProtocolException("Unspecified protocol");
695 }
696
697 String hint = protocol.toLowerCase(java.util.Locale.ENGLISH);
698
699 Wagon wagon;
700 try {
701 wagon = container.lookup(Wagon.class, hint);
702 } catch (ComponentLookupException e) {
703 throw new UnsupportedProtocolException(
704 "Cannot find wagon which supports the requested protocol: " + protocol, e);
705 }
706
707 return wagon;
708 }
709 }