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