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 }