1   package org.apache.maven.wagon.providers.scm;
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  import org.apache.maven.scm.ScmBranch;
23  import org.apache.maven.scm.ScmException;
24  import org.apache.maven.scm.ScmFile;
25  import org.apache.maven.scm.ScmFileSet;
26  import org.apache.maven.scm.ScmResult;
27  import org.apache.maven.scm.ScmRevision;
28  import org.apache.maven.scm.ScmTag;
29  import org.apache.maven.scm.ScmVersion;
30  import org.apache.maven.scm.command.add.AddScmResult;
31  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
32  import org.apache.maven.scm.command.list.ListScmResult;
33  import org.apache.maven.scm.manager.NoSuchScmProviderException;
34  import org.apache.maven.scm.manager.ScmManager;
35  import org.apache.maven.scm.provider.ScmProvider;
36  import org.apache.maven.scm.provider.ScmProviderRepository;
37  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
38  import org.apache.maven.scm.repository.ScmRepository;
39  import org.apache.maven.scm.repository.ScmRepositoryException;
40  import org.apache.maven.wagon.AbstractWagon;
41  import org.apache.maven.wagon.ConnectionException;
42  import org.apache.maven.wagon.ResourceDoesNotExistException;
43  import org.apache.maven.wagon.TransferFailedException;
44  import org.apache.maven.wagon.authorization.AuthorizationException;
45  import org.apache.maven.wagon.events.TransferEvent;
46  import org.apache.maven.wagon.resource.Resource;
47  import org.codehaus.plexus.util.FileUtils;
48  import org.codehaus.plexus.util.StringUtils;
49  
50  import java.io.File;
51  import java.io.IOException;
52  import java.text.DecimalFormat;
53  import java.util.ArrayList;
54  import java.util.List;
55  import java.util.Random;
56  import java.util.Stack;
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  public class ScmWagon
75      extends AbstractWagon
76  {
77      
78  
79  
80      private volatile ScmManager scmManager;
81  
82      
83  
84  
85  
86  
87      private String scmVersion;
88  
89      
90  
91  
92  
93  
94      private String scmVersionType;
95  
96      private File checkoutDirectory;
97  
98      
99  
100 
101 
102 
103     public ScmManager getScmManager()
104     {
105         return scmManager;
106     }
107 
108     
109 
110 
111 
112 
113     public void setScmManager( ScmManager scmManager )
114     {
115         this.scmManager = scmManager;
116     }
117 
118     
119 
120 
121 
122 
123     public String getScmVersion()
124     {
125         return scmVersion;
126     }
127 
128     
129 
130 
131 
132 
133     public void setScmVersion( String scmVersion )
134     {
135         this.scmVersion = scmVersion;
136     }
137 
138     
139 
140 
141 
142 
143     public String getScmVersionType()
144     {
145         return scmVersionType;
146     }
147 
148     
149 
150 
151 
152 
153     public void setScmVersionType( String scmVersionType )
154     {
155         this.scmVersionType = scmVersionType;
156     }
157 
158     
159 
160 
161 
162 
163     public File getCheckoutDirectory()
164     {
165         return checkoutDirectory;
166     }
167 
168     
169 
170 
171 
172 
173     public void setCheckoutDirectory( File checkoutDirectory )
174     {
175         this.checkoutDirectory = checkoutDirectory;
176     }
177 
178     
179 
180 
181 
182 
183 
184 
185     public ScmProvider getScmProvider( String scmType )
186         throws NoSuchScmProviderException
187     {
188         return getScmManager().getProviderByType( scmType );
189     }
190 
191     
192 
193 
194     public void openConnectionInternal()
195         throws ConnectionException
196     {
197         if ( checkoutDirectory == null )
198         {
199             checkoutDirectory = createCheckoutDirectory();
200         }
201 
202         if ( checkoutDirectory.exists() )
203         {
204             removeCheckoutDirectory();
205         }
206 
207         checkoutDirectory.mkdirs();
208     }
209 
210     private File createCheckoutDirectory()
211     {
212         File checkoutDirectory;
213 
214         DecimalFormat fmt = new DecimalFormat( "#####" );
215 
216         Random rand = new Random( System.currentTimeMillis() + Runtime.getRuntime().freeMemory() );
217 
218         synchronized ( rand )
219         {
220             do
221             {
222                 checkoutDirectory = new File( System.getProperty( "java.io.tmpdir" ),
223                                               "wagon-scm" + fmt.format( Math.abs( rand.nextInt() ) ) + ".checkout" );
224             }
225             while ( checkoutDirectory.exists() );
226         }
227 
228         return checkoutDirectory;
229     }
230 
231 
232     private void removeCheckoutDirectory()
233         throws ConnectionException
234     {
235         if ( checkoutDirectory == null )
236         {
237             return; 
238         }
239 
240         try
241         {
242             FileUtils.deleteDirectory( checkoutDirectory );
243         }
244         catch ( IOException e )
245         {
246             throw new ConnectionException( "Unable to cleanup checkout directory", e );
247         }
248     }
249 
250     
251 
252 
253 
254 
255 
256     private ScmVersion makeScmVersion()
257     {
258         if ( StringUtils.isBlank( scmVersion ) )
259         {
260             return null;
261         }
262         if ( scmVersion.length() > 0 )
263         {
264             if ( "revision".equals( scmVersionType ) )
265             {
266                 return new ScmRevision( scmVersion );
267             }
268             else if ( "tag".equals( scmVersionType ) )
269             {
270                 return new ScmTag( scmVersion );
271             }
272             else if ( "branch".equals( scmVersionType ) )
273             {
274                 return new ScmBranch( scmVersion );
275             }
276         }
277 
278         return null;
279     }
280 
281     private ScmRepository getScmRepository( String url )
282         throws ScmRepositoryException, NoSuchScmProviderException
283     {
284         String username = null;
285 
286         String password = null;
287 
288         String privateKey = null;
289 
290         String passphrase = null;
291 
292         if ( authenticationInfo != null )
293         {
294             username = authenticationInfo.getUserName();
295 
296             password = authenticationInfo.getPassword();
297 
298             privateKey = authenticationInfo.getPrivateKey();
299 
300             passphrase = authenticationInfo.getPassphrase();
301         }
302 
303         ScmRepository scmRepository = getScmManager().makeScmRepository( url );
304 
305         ScmProviderRepository providerRepository = scmRepository.getProviderRepository();
306 
307         if ( StringUtils.isNotEmpty( username ) )
308         {
309             providerRepository.setUser( username );
310         }
311 
312         if ( StringUtils.isNotEmpty( password ) )
313         {
314             providerRepository.setPassword( password );
315         }
316 
317         if ( providerRepository instanceof ScmProviderRepositoryWithHost )
318         {
319             ScmProviderRepositoryWithHost providerRepo = (ScmProviderRepositoryWithHost) providerRepository;
320 
321             if ( StringUtils.isNotEmpty( privateKey ) )
322             {
323                 providerRepo.setPrivateKey( privateKey );
324             }
325 
326             if ( StringUtils.isNotEmpty( passphrase ) )
327             {
328                 providerRepo.setPassphrase( passphrase );
329             }
330         }
331 
332         return scmRepository;
333     }
334 
335     public void put( File source, String targetName )
336         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
337     {
338         if ( source.isDirectory() )
339         {
340             throw new IllegalArgumentException( "Source is a directory: " + source );
341         }
342         putInternal( source, targetName );
343     }
344 
345     
346 
347 
348 
349 
350 
351 
352     private void putInternal( File source, String targetName )
353         throws TransferFailedException
354     {
355         Resource target = new Resource( targetName );
356 
357         firePutInitiated( target, source );
358 
359         try
360         {
361             ScmRepository scmRepository = getScmRepository( getRepository().getUrl() );
362 
363             target.setContentLength( source.length() );
364             target.setLastModified( source.lastModified() );
365 
366             firePutStarted( target, source );
367 
368             String msg = "Wagon: Adding " + source.getName() + " to repository";
369 
370             ScmProvider scmProvider = getScmProvider( scmRepository.getProvider() );
371 
372             String checkoutTargetName = source.isDirectory() ? targetName : getDirname( targetName );
373             String relPath = checkOut( scmProvider, scmRepository, checkoutTargetName, target );
374 
375             File newCheckoutDirectory = new File( checkoutDirectory, relPath );
376 
377             File scmFile = new File( newCheckoutDirectory, source.isDirectory() ? "" : getFilename( targetName ) );
378 
379             boolean fileAlreadyInScm = scmFile.exists();
380 
381             if ( !scmFile.equals( source ) )
382             {
383                 if ( source.isDirectory() )
384                 {
385                     FileUtils.copyDirectoryStructure( source, scmFile );
386                 }
387                 else
388                 {
389                     FileUtils.copyFile( source, scmFile );
390                 }
391             }
392 
393             if ( !fileAlreadyInScm || scmFile.isDirectory() )
394             {
395                 int addedFiles = addFiles( scmProvider, scmRepository, newCheckoutDirectory,
396                                            source.isDirectory() ? "" : scmFile.getName() );
397 
398                 if ( !fileAlreadyInScm && addedFiles == 0 )
399                 {
400                     throw new ScmException(
401                         "Unable to add file to SCM: " + scmFile + "; see error messages above for more information" );
402                 }
403             }
404 
405             ScmResult result =
406                 scmProvider.checkIn( scmRepository, new ScmFileSet( checkoutDirectory ), makeScmVersion(), msg );
407 
408             checkScmResult( result );
409         }
410         catch ( ScmException e )
411         {
412             fireTransferError( target, e, TransferEvent.REQUEST_GET );
413 
414             throw new TransferFailedException( "Error interacting with SCM: " + e.getMessage(), e );
415         }
416         catch ( IOException e )
417         {
418             fireTransferError( target, e, TransferEvent.REQUEST_GET );
419 
420             throw new TransferFailedException( "Error interacting with SCM: " + e.getMessage(), e );
421         }
422 
423         if ( source.isFile() )
424         {
425             postProcessListeners( target, source, TransferEvent.REQUEST_PUT );
426         }
427 
428         firePutCompleted( target, source );
429     }
430 
431     
432 
433 
434 
435 
436 
437 
438 
439 
440 
441     private String checkOut( ScmProvider scmProvider, ScmRepository scmRepository, String targetName,
442                              Resource resource )
443         throws TransferFailedException
444     {
445         checkoutDirectory = createCheckoutDirectory();
446 
447         Stack<String> stack = new Stack<String>();
448 
449         String target = targetName;
450 
451         
452         
453         
454         
455 
456         try
457         {
458             while ( target.length() > 0 && !scmProvider.list( scmRepository,
459                                                               new ScmFileSet( new File( "." ), new File( target ) ),
460                                                               false, makeScmVersion() ).isSuccess() )
461             {
462                 stack.push( getFilename( target ) );
463                 target = getDirname( target );
464             }
465         }
466         catch ( ScmException e )
467         {
468             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
469 
470             throw new TransferFailedException( "Error listing repository: " + e.getMessage(), e );
471         }
472 
473         
474         
475         
476         
477 
478         try
479         {
480             String repoUrl = getRepository().getUrl();
481             if ( "svn".equals( scmProvider.getScmType() ) )
482             {
483                 
484                 
485                 repoUrl += "/" + target.replace( '\\', '/' );
486             }
487             scmRepository = getScmRepository( repoUrl );
488             CheckOutScmResult ret =
489                 scmProvider.checkOut( scmRepository, new ScmFileSet( new File( checkoutDirectory, "" ) ),
490                                       makeScmVersion(), false );
491 
492             checkScmResult( ret );
493         }
494         catch ( ScmException e )
495         {
496             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
497 
498             throw new TransferFailedException( "Error checking out: " + e.getMessage(), e );
499         }
500 
501         
502 
503         String relPath = "";
504 
505         while ( !stack.isEmpty() )
506         {
507             String p = stack.pop();
508             relPath += p + "/";
509 
510             File newDir = new File( checkoutDirectory, relPath );
511             if ( !newDir.mkdirs() )
512             {
513                 throw new TransferFailedException(
514                     "Failed to create directory " + newDir.getAbsolutePath() + "; parent should exist: "
515                         + checkoutDirectory );
516             }
517 
518             try
519             {
520                 addFiles( scmProvider, scmRepository, checkoutDirectory, relPath );
521             }
522             catch ( ScmException e )
523             {
524                 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
525 
526                 throw new TransferFailedException( "Failed to add directory " + newDir + " to working copy", e );
527             }
528         }
529 
530         return relPath;
531     }
532 
533     
534 
535 
536 
537 
538 
539 
540 
541 
542 
543 
544 
545     private int addFiles( ScmProvider scmProvider, ScmRepository scmRepository, File basedir, String scmFilePath )
546         throws ScmException
547     {
548         int addedFiles = 0;
549 
550         File scmFile = new File( basedir, scmFilePath );
551 
552         if ( scmFilePath.length() != 0 )
553         {
554             AddScmResult result = scmProvider.add( scmRepository, new ScmFileSet( basedir, new File( scmFilePath ) ) );
555 
556             
557 
558 
559 
560 
561             if ( !result.isSuccess() )
562             {
563                 result = scmProvider.add( scmRepository, new ScmFileSet( basedir, new File( scmFilePath ) ) );
564             }
565 
566             addedFiles = result.getAddedFiles().size();
567         }
568 
569         String reservedScmFile = scmProvider.getScmSpecificFilename();
570 
571         if ( scmFile.isDirectory() )
572         {
573             for ( File file : scmFile.listFiles() )
574             {
575                 if ( reservedScmFile != null && !reservedScmFile.equals( file.getName() ) )
576                 {
577                     addedFiles += addFiles( scmProvider, scmRepository, basedir,
578                                             ( scmFilePath.length() == 0 ? "" : scmFilePath + "/" )
579                                                 + file.getName() );
580                 }
581             }
582         }
583 
584         return addedFiles;
585     }
586 
587     
588 
589 
590     public boolean supportsDirectoryCopy()
591     {
592         return true;
593     }
594 
595     public void putDirectory( File sourceDirectory, String destinationDirectory )
596         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
597     {
598         if ( !sourceDirectory.isDirectory() )
599         {
600             throw new IllegalArgumentException( "Source is not a directory: " + sourceDirectory );
601         }
602 
603         putInternal( sourceDirectory, destinationDirectory );
604     }
605 
606     
607 
608 
609 
610 
611 
612 
613     private void checkScmResult( ScmResult result )
614         throws ScmException
615     {
616         if ( !result.isSuccess() )
617         {
618             throw new ScmException(
619                 "Unable to commit file. " + result.getProviderMessage() + " " + ( result.getCommandOutput() == null
620                     ? ""
621                     : result.getCommandOutput() ) );
622         }
623     }
624 
625     public void closeConnection()
626         throws ConnectionException
627     {
628         removeCheckoutDirectory();
629     }
630 
631     
632 
633 
634 
635 
636     public boolean getIfNewer( String resourceName, File destination, long timestamp )
637         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
638     {
639         throw new UnsupportedOperationException( "Not currently supported: getIfNewer" );
640     }
641 
642     public void get( String resourceName, File destination )
643         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
644     {
645         Resource resource = new Resource( resourceName );
646 
647         fireGetInitiated( resource, destination );
648 
649         String url = getRepository().getUrl() + "/" + resourceName;
650 
651         
652         url = url.substring( 0, url.lastIndexOf( '/' ) );
653 
654         try
655         {
656             ScmRepository scmRepository = getScmRepository( url );
657 
658             fireGetStarted( resource, destination );
659 
660             
661             
662             
663             
664             
665             
666 
667             File scmFile = new File( checkoutDirectory, resourceName );
668 
669             File basedir = scmFile.getParentFile();
670 
671             ScmProvider scmProvider = getScmProvider( scmRepository.getProvider() );
672 
673             String reservedScmFile = scmProvider.getScmSpecificFilename();
674 
675             if ( reservedScmFile != null && new File( basedir, reservedScmFile ).exists() )
676             {
677                 scmProvider.update( scmRepository, new ScmFileSet( basedir ), makeScmVersion() );
678             }
679             else
680             {
681                 
682                 basedir.mkdirs();
683 
684                 scmProvider.checkOut( scmRepository, new ScmFileSet( basedir ), makeScmVersion() );
685             }
686 
687             if ( !scmFile.exists() )
688             {
689                 throw new ResourceDoesNotExistException( "Unable to find resource " + destination + " after checkout" );
690             }
691 
692             if ( !scmFile.equals( destination ) )
693             {
694                 FileUtils.copyFile( scmFile, destination );
695             }
696         }
697         catch ( ScmException e )
698         {
699             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
700 
701             throw new TransferFailedException( "Error getting file from SCM", e );
702         }
703         catch ( IOException e )
704         {
705             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
706 
707             throw new TransferFailedException( "Error getting file from SCM", e );
708         }
709 
710         postProcessListeners( resource, destination, TransferEvent.REQUEST_GET );
711 
712         fireGetCompleted( resource, destination );
713     }
714 
715     
716 
717 
718 
719     public List<String> getFileList( String resourcePath )
720         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
721     {
722         try
723         {
724             ScmRepository repository = getScmRepository( getRepository().getUrl() );
725 
726             ScmProvider provider = getScmProvider( repository.getProvider() );
727 
728             ListScmResult result =
729                 provider.list( repository, new ScmFileSet( new File( "." ), new File( resourcePath ) ), false,
730                                makeScmVersion() );
731 
732             if ( !result.isSuccess() )
733             {
734                 throw new ResourceDoesNotExistException( result.getProviderMessage() );
735             }
736 
737             List<String> files = new ArrayList<String>();
738 
739             for ( ScmFile f : result.getFiles() )
740             {
741                 files.add( f.getPath() );
742             }
743 
744             return files;
745         }
746         catch ( ScmException e )
747         {
748             throw new TransferFailedException( "Error getting filelist from SCM", e );
749         }
750     }
751 
752     public boolean resourceExists( String resourceName )
753         throws TransferFailedException, AuthorizationException
754     {
755         try
756         {
757             getFileList( resourceName );
758 
759             return true;
760         }
761         catch ( ResourceDoesNotExistException e )
762         {
763             return false;
764         }
765     }
766 
767     private String getFilename( String filename )
768     {
769         String fname = StringUtils.replace( filename, "/", File.separator );
770         return FileUtils.filename( fname );
771     }
772 
773     private String getDirname( String filename )
774     {
775         String fname = StringUtils.replace( filename, "/", File.separator );
776         return FileUtils.dirname( fname );
777     }
778 }