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