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 temp.delete();
509 }
510 catch ( IOException e )
511 {
512 throw new TransferFailedException( "Error copying temporary file to the final destination: "
513 + e.getMessage(), e );
514 }
515 }
516 }
517 }
518
519
520
521
522 public void putArtifact( File source, Artifact artifact, ArtifactRepository deploymentRepository,
523 TransferListener downloadMonitor )
524 throws TransferFailedException
525 {
526 putRemoteFile( deploymentRepository, source, deploymentRepository.pathOf( artifact ), downloadMonitor );
527 }
528
529 public void putArtifactMetadata( File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository )
530 throws TransferFailedException
531 {
532 logger.info( "Uploading " + artifactMetadata );
533 putRemoteFile( repository, source, repository.pathOfRemoteRepositoryMetadata( artifactMetadata ), null );
534 }
535
536 public void putRemoteFile( ArtifactRepository repository, File source, String remotePath,
537 TransferListener downloadMonitor )
538 throws TransferFailedException
539 {
540 String protocol = repository.getProtocol();
541
542 Wagon wagon;
543 try
544 {
545 wagon = getWagon( protocol );
546 }
547 catch ( UnsupportedProtocolException e )
548 {
549 throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
550 }
551
552 if ( downloadMonitor != null )
553 {
554 wagon.addTransferListener( downloadMonitor );
555 }
556
557 Map<String, ChecksumObserver> checksums = new HashMap<String, ChecksumObserver>( 2 );
558
559 Map<String, String> sums = new HashMap<String, String>( 2 );
560
561
562 for ( int i = 0; i < CHECKSUM_IDS.length; i++ )
563 {
564 checksums.put( CHECKSUM_IDS[i], addChecksumObserver( wagon, CHECKSUM_ALGORITHMS[i] ) );
565 }
566
567 List<File> temporaryFiles = new ArrayList<File>();
568
569 try
570 {
571 try
572 {
573 connectWagon( wagon, repository );
574
575 wagon.put( source, remotePath );
576 }
577 finally
578 {
579 if ( downloadMonitor != null )
580 {
581 wagon.removeTransferListener( downloadMonitor );
582 }
583 }
584
585
586 for ( String extension : checksums.keySet() )
587 {
588 ChecksumObserver observer = checksums.get( extension );
589 sums.put( extension, observer.getActualChecksum() );
590 }
591
592
593 for ( String extension : checksums.keySet() )
594 {
595
596 File temp = File.createTempFile( "maven-artifact", null );
597 temp.deleteOnExit();
598 FileUtils.fileWrite( temp.getAbsolutePath(), "UTF-8", sums.get( extension ) );
599
600 temporaryFiles.add( temp );
601 wagon.put( temp, remotePath + "." + extension );
602 }
603 }
604 catch ( ConnectionException e )
605 {
606 throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
607 }
608 catch ( AuthenticationException e )
609 {
610 throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
611 }
612 catch ( AuthorizationException e )
613 {
614 throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
615 }
616 catch ( ResourceDoesNotExistException e )
617 {
618 throw new TransferFailedException( "Resource to deploy not found: " + e.getMessage(), e );
619 }
620 catch ( IOException e )
621 {
622 throw new TransferFailedException( "Error creating temporary file for deployment: " + e.getMessage(), e );
623 }
624 finally
625 {
626
627 cleanupTemporaryFiles( temporaryFiles );
628
629
630 for ( String aCHECKSUM_IDS : CHECKSUM_IDS )
631 {
632 TransferListener checksumListener = checksums.get( aCHECKSUM_IDS );
633 if ( checksumListener != null )
634 {
635 wagon.removeTransferListener( checksumListener );
636 }
637 }
638
639 disconnectWagon( wagon );
640
641 releaseWagon( protocol, wagon );
642 }
643 }
644
645 private void cleanupTemporaryFiles( List<File> files )
646 {
647 for ( File file : files )
648 {
649
650 try
651 {
652 file.delete();
653 }
654 catch ( Exception e )
655 {
656 logger.warn( "skip failed to delete temporary file : " + file.getAbsolutePath() + " , message "
657 + e.getMessage() );
658 }
659 }
660
661 }
662
663 private ChecksumObserver addChecksumObserver( Wagon wagon, String algorithm )
664 throws TransferFailedException
665 {
666 try
667 {
668 ChecksumObserver checksumObserver = new ChecksumObserver( algorithm );
669 wagon.addTransferListener( checksumObserver );
670 return checksumObserver;
671 }
672 catch ( NoSuchAlgorithmException e )
673 {
674 throw new TransferFailedException( "Unable to add checksum for unsupported algorithm " + algorithm, e );
675 }
676 }
677
678 private void handleChecksumFailure( String checksumPolicy, String message, Throwable cause )
679 throws ChecksumFailedException
680 {
681 if ( ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
682 {
683 throw new ChecksumFailedException( message, cause );
684 }
685 else if ( !ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals( checksumPolicy ) )
686 {
687
688 logger.warn( "*** CHECKSUM FAILED - " + message + " - IGNORING" );
689 }
690
691 }
692
693 private void verifyChecksum( ChecksumObserver checksumObserver, File destination, File tempDestination,
694 String remotePath, String checksumFileExtension, Wagon wagon )
695 throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
696 {
697 try
698 {
699
700 String actualChecksum = checksumObserver.getActualChecksum();
701
702 File tempChecksumFile = new File( tempDestination + checksumFileExtension + ".tmp" );
703 tempChecksumFile.deleteOnExit();
704 wagon.get( remotePath + checksumFileExtension, tempChecksumFile );
705
706 String expectedChecksum = FileUtils.fileRead( tempChecksumFile, "UTF-8" );
707
708
709 expectedChecksum = expectedChecksum.trim();
710
711
712 if ( expectedChecksum.regionMatches( true, 0, "MD", 0, 2 )
713 || expectedChecksum.regionMatches( true, 0, "SHA", 0, 3 ) )
714 {
715 int lastSpacePos = expectedChecksum.lastIndexOf( ' ' );
716 expectedChecksum = expectedChecksum.substring( lastSpacePos + 1 );
717 }
718 else
719 {
720
721 int spacePos = expectedChecksum.indexOf( ' ' );
722
723 if ( spacePos != -1 )
724 {
725 expectedChecksum = expectedChecksum.substring( 0, spacePos );
726 }
727 }
728 if ( expectedChecksum.equalsIgnoreCase( actualChecksum ) )
729 {
730 File checksumFile = new File( destination + checksumFileExtension );
731 if ( checksumFile.exists() )
732 {
733 checksumFile.delete();
734 }
735 FileUtils.copyFile( tempChecksumFile, checksumFile );
736 tempChecksumFile.delete();
737 }
738 else
739 {
740 throw new ChecksumFailedException( "Checksum failed on download: local = '" + actualChecksum
741 + "'; remote = '" + expectedChecksum + "'" );
742 }
743 }
744 catch ( IOException e )
745 {
746 throw new ChecksumFailedException( "Invalid checksum file", e );
747 }
748 }
749
750 private void disconnectWagon( Wagon wagon )
751 {
752 try
753 {
754 wagon.disconnect();
755 }
756 catch ( ConnectionException e )
757 {
758 logger.error( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
759 }
760 }
761
762 private void releaseWagon( String protocol, Wagon wagon )
763 {
764 try
765 {
766 container.release( wagon );
767 }
768 catch ( ComponentLifecycleException e )
769 {
770 logger.error( "Problem releasing wagon - ignoring: " + e.getMessage() );
771 logger.debug( "", e );
772 }
773 }
774
775 @Deprecated
776 public Wagon getWagon( Repository repository )
777 throws UnsupportedProtocolException
778 {
779 return getWagon( repository.getProtocol() );
780 }
781
782 @Deprecated
783 public Wagon getWagon( String protocol )
784 throws UnsupportedProtocolException
785 {
786 if ( protocol == null )
787 {
788 throw new UnsupportedProtocolException( "Unspecified protocol" );
789 }
790
791 String hint = protocol.toLowerCase( java.util.Locale.ENGLISH );
792
793 Wagon wagon;
794 try
795 {
796 wagon = container.lookup( Wagon.class, hint );
797 }
798 catch ( ComponentLookupException e )
799 {
800 throw new UnsupportedProtocolException( "Cannot find wagon which supports the requested protocol: "
801 + protocol, e );
802 }
803
804 return wagon;
805 }
806
807 }