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