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