You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2018/06/04 09:39:01 UTC
[maven-wagon] 03/03: [WAGON-495] Fix checkoutDirectory leak
This is an automated email from the ASF dual-hosted git repository.
michaelo pushed a commit to branch wagon-scm-git
in repository https://gitbox.apache.org/repos/asf/maven-wagon.git
commit 2e70c858b7031b105951ee33252410590ae5e785
Author: Ilya Basin <ba...@gmail.com>
AuthorDate: Thu Feb 22 11:13:52 2018 +0300
[WAGON-495] Fix checkoutDirectory leak
This closes #46
---
.../apache/maven/wagon/providers/scm/ScmWagon.java | 248 ++++++++++++---------
1 file changed, 139 insertions(+), 109 deletions(-)
diff --git a/wagon-providers/wagon-scm/src/main/java/org/apache/maven/wagon/providers/scm/ScmWagon.java b/wagon-providers/wagon-scm/src/main/java/org/apache/maven/wagon/providers/scm/ScmWagon.java
index 09b014d..d6531ee 100644
--- a/wagon-providers/wagon-scm/src/main/java/org/apache/maven/wagon/providers/scm/ScmWagon.java
+++ b/wagon-providers/wagon-scm/src/main/java/org/apache/maven/wagon/providers/scm/ScmWagon.java
@@ -19,6 +19,13 @@ package org.apache.maven.wagon.providers.scm;
* under the License.
*/
+import java.io.File;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
import org.apache.maven.scm.CommandParameter;
import org.apache.maven.scm.CommandParameters;
import org.apache.maven.scm.ScmBranch;
@@ -32,6 +39,7 @@ import org.apache.maven.scm.ScmVersion;
import org.apache.maven.scm.command.add.AddScmResult;
import org.apache.maven.scm.command.checkout.CheckOutScmResult;
import org.apache.maven.scm.command.list.ListScmResult;
+import org.apache.maven.scm.command.update.UpdateScmResult;
import org.apache.maven.scm.manager.NoSuchScmProviderException;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.provider.ScmProvider;
@@ -49,14 +57,6 @@ import org.apache.maven.wagon.resource.Resource;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
-import java.io.File;
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.Stack;
-
/**
* Wagon provider to get and put files from and to SCM systems, using Maven-SCM as underlying transport.
* <p/>
@@ -95,6 +95,11 @@ public class ScmWagon
*/
private String scmVersionType;
+ /**
+ * Empty string or subdir ending with slash.
+ */
+ private String partCOSubdir = "";
+
private File checkoutDirectory;
/**
@@ -371,18 +376,19 @@ public class ScmWagon
ScmProvider scmProvider = getScmProvider( scmRepository.getProvider() );
- String checkoutTargetName = source.isDirectory() ? targetName : getDirname( targetName );
- String relPath = checkOut( scmProvider, scmRepository, checkoutTargetName, target );
+ boolean isDirectory = source.isDirectory();
+ String checkoutTargetName = isDirectory ? targetName : getDirname( targetName );
+ String relPath = ensureDirs( scmProvider, scmRepository, checkoutTargetName, target );
File newCheckoutDirectory = new File( checkoutDirectory, relPath );
- File scmFile = new File( newCheckoutDirectory, source.isDirectory() ? "" : getFilename( targetName ) );
+ File scmFile = new File( newCheckoutDirectory, isDirectory ? "" : FileUtils.removePath( targetName, '/' ) );
boolean fileAlreadyInScm = scmFile.exists();
if ( !scmFile.equals( source ) )
{
- if ( source.isDirectory() )
+ if ( isDirectory )
{
FileUtils.copyDirectoryStructure( source, scmFile );
}
@@ -395,7 +401,7 @@ public class ScmWagon
if ( !fileAlreadyInScm || scmFile.isDirectory() )
{
int addedFiles = addFiles( scmProvider, scmRepository, newCheckoutDirectory,
- source.isDirectory() ? "" : scmFile.getName() );
+ isDirectory ? "" : scmFile.getName() );
if ( !fileAlreadyInScm && addedFiles == 0 )
{
@@ -439,14 +445,16 @@ public class ScmWagon
* @param targetName
* @return
* @throws TransferFailedException
+ * @throws IOException
*/
- private String checkOut( ScmProvider scmProvider, ScmRepository scmRepository, String targetName,
+ private String ensureDirs( ScmProvider scmProvider, ScmRepository scmRepository, String targetName,
Resource resource )
- throws TransferFailedException
+ throws TransferFailedException, IOException
{
- checkoutDirectory = createCheckoutDirectory();
-
- Stack<String> stack = new Stack<String>();
+ if ( checkoutDirectory == null )
+ {
+ checkoutDirectory = createCheckoutDirectory();
+ }
String target = targetName;
@@ -455,87 +463,82 @@ public class ScmWagon
// Check whether targetName, which is a relative path into the scm, exists.
// If it doesn't, check the parent, etc.
- try
+ for ( ;; )
{
- while ( target.length() > 0 && !scmProvider.list( scmRepository,
- new ScmFileSet( new File( "." ), new File( target ) ),
- false, makeScmVersion() ).isSuccess() )
+ try
+ {
+ ScmResult res = tryPartialCheckout( target );
+ if ( !res.isSuccess() )
+ {
+ throw new ScmException( "command failed: " + res.getCommandOutput().trim() );
+ }
+ break;
+ }
+ catch ( ScmException e )
{
- stack.push( getFilename( target ) );
+ if ( partCOSubdir.length() == 0 )
+ {
+ fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+
+ throw new TransferFailedException( "Error checking out: " + e.getMessage(), e );
+ }
target = getDirname( target );
}
}
- catch ( ScmException e )
- {
- fireTransferError( resource, e, TransferEvent.REQUEST_GET );
- throw new TransferFailedException( "Error listing repository: " + e.getMessage(), e );
- }
+ // now create the subdirs in target, if it's a parent of targetName
+
+ String res =
+ partCOSubdir.length() >= targetName.length() ? "" : targetName.substring( partCOSubdir.length() ) + '/';
- // ok, we've established that target exists, or is empty.
- // Check the resource out; if it doesn't exist, that means we're in the svn repo url root,
- // and the configuration is incorrect. We will not try repo.getParent since most scm's don't
- // implement that.
+ ArrayList<File> createdDirs = new ArrayList<File>();
+ File deepDir = new File( checkoutDirectory, res );
- target = target.replace( '\\', '/' );
+ boolean added = false;
try
{
- String repoUrl = getRepository().getUrl();
- if ( "svn".equals( scmProvider.getScmType() ) )
+ mkdirsThrow( deepDir, createdDirs );
+ if ( createdDirs.size() != 0 )
{
- // Subversion is the only SCM that adds path structure to represent tags and branches.
- // The rest use scmVersion and scmVersionType.
- if ( target.length() > 0 )
- {
- repoUrl += "/" + target;
- target = "";
- }
- }
- scmRepository = getScmRepository( repoUrl );
-
- CheckOutScmResult ret =
- checkOut( scmProvider, scmRepository, new ScmFileSet( new File( checkoutDirectory, "" ) ) );
+ File topNewDir = createdDirs.get( 0 );
+ String relTopNewDir =
+ topNewDir.getPath().substring( checkoutDirectory.getPath().length() + 1 ).replace( '\\', '/' );
- checkScmResult( ret );
+ addFiles( scmProvider, scmRepository, checkoutDirectory, relTopNewDir );
+ added = true;
+ }
}
catch ( ScmException e )
{
- fireTransferError( resource, e, TransferEvent.REQUEST_GET );
+ fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
- throw new TransferFailedException( "Error checking out: " + e.getMessage(), e );
+ throw new TransferFailedException( "Failed to add directory " + createdDirs.get( 0 ) + " to working copy",
+ e );
}
-
- // now create the subdirs in target, if it's a parent of targetName
-
- String relPath = target.concat( target.length() > 0 ? "/" : "" );
-
- while ( !stack.isEmpty() )
+ finally
{
- String p = stack.pop();
- relPath += p + "/";
-
- File newDir = new File( checkoutDirectory, relPath );
- newDir.mkdir();
- if ( !newDir.isDirectory() )
+ if ( !added && createdDirs.size() != 0 )
{
- throw new TransferFailedException(
- "Failed to create directory " + newDir.getAbsolutePath() + "; parent should exist: "
- + checkoutDirectory );
+ FileUtils.deleteDirectory( createdDirs.get( 0 ) );
}
+ }
- try
- {
- addFiles( scmProvider, scmRepository, checkoutDirectory, relPath );
- }
- catch ( ScmException e )
- {
- fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
+ return res;
+ }
- throw new TransferFailedException( "Failed to add directory " + newDir + " to working copy", e );
+ private static void mkdirsThrow( File f, List<File> createdDirs )
+ throws IOException
+ {
+ if ( !f.isDirectory() )
+ {
+ File parent = f.getParentFile();
+ mkdirsThrow( parent, createdDirs );
+ if ( !f.mkdir() )
+ {
+ throw new IOException( "Failed to create directory " + f.getAbsolutePath() );
}
+ createdDirs.add( f );
}
-
- return relPath;
}
/**
@@ -623,6 +626,12 @@ public class ScmWagon
return true;
}
+ private boolean supportsPartialCheckout( ScmProvider scmProvider )
+ {
+ String scmType = scmProvider.getScmType();
+ return ( "svn".equals( scmType ) || "cvs".equals( scmType ) );
+ }
+
public void putDirectory( File sourceDirectory, String destinationDirectory )
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
{
@@ -677,16 +686,19 @@ public class ScmWagon
fireGetInitiated( resource, destination );
- String url = getRepository().getUrl() + "/" + resourceName;
-
- // remove the file
- url = url.substring( 0, url.lastIndexOf( '/' ) );
+ fireGetStarted( resource, destination );
try
{
- ScmRepository scmRepository = getScmRepository( url );
-
- fireGetStarted( resource, destination );
+ String subdir = getDirname( resourceName );
+ ScmResult res = tryPartialCheckout( subdir );
+ if ( !res.isSuccess() && ( partCOSubdir.length() == 0 || res instanceof UpdateScmResult ) )
+ {
+ // inability to checkout SVN or CVS subdir is not fatal. We just assume it doesn't exist
+ // inability to update existing subdir or checkout root is fatal
+ throw new ScmException( "command failed: " + res.getCommandOutput().trim() );
+ }
+ resourceName = resourceName.substring( partCOSubdir.length() );
// TODO: limitations:
// - destination filename must match that in the repository - should allow the "-d" CVS equiv to be passed
@@ -697,24 +709,6 @@ public class ScmWagon
File scmFile = new File( checkoutDirectory, resourceName );
- File basedir = scmFile.getParentFile();
-
- ScmProvider scmProvider = getScmProvider( scmRepository.getProvider() );
-
- String reservedScmFile = scmProvider.getScmSpecificFilename();
-
- if ( reservedScmFile != null && new File( basedir, reservedScmFile ).exists() )
- {
- scmProvider.update( scmRepository, new ScmFileSet( basedir ), makeScmVersion() );
- }
- else
- {
- // TODO: this should be checking out a full hierarchy (requires the -d equiv)
- basedir.mkdirs();
-
- checkOut( scmProvider, scmRepository, new ScmFileSet( basedir ) );
- }
-
if ( !scmFile.exists() )
{
throw new ResourceDoesNotExistException( "Unable to find resource " + destination + " after checkout" );
@@ -743,6 +737,49 @@ public class ScmWagon
fireGetCompleted( resource, destination );
}
+ private ScmResult tryPartialCheckout( String subdir )
+ throws ScmException, IOException
+ {
+ String url = getRepository().getUrl();
+
+ String desiredPartCOSubdir = "";
+
+ ScmRepository scmRepository = getScmRepository( url );
+ ScmProvider scmProvider = getScmProvider( scmRepository.getProvider() );
+ if ( subdir.length() != 0 && supportsPartialCheckout( scmProvider ) )
+ {
+ url += ( url.endsWith( "/" ) ? "" : "/" ) + subdir;
+
+ desiredPartCOSubdir = subdir + "/";
+
+ scmRepository = getScmRepository( url );
+ }
+
+ if ( !desiredPartCOSubdir.equals( partCOSubdir ) )
+ {
+ FileUtils.deleteDirectory( checkoutDirectory );
+ partCOSubdir = desiredPartCOSubdir;
+ }
+
+ ScmResult res;
+ if ( checkoutDirExists( scmProvider ) )
+ {
+ res = scmProvider.update( scmRepository, new ScmFileSet( checkoutDirectory ), makeScmVersion() );
+ }
+ else
+ {
+ res = checkOut( scmProvider, scmRepository, new ScmFileSet( checkoutDirectory ) );
+ }
+ return res;
+ }
+
+ private boolean checkoutDirExists( ScmProvider scmProvider )
+ {
+ String reservedScmFile = scmProvider.getScmSpecificFilename();
+ File pathToCheck = reservedScmFile == null ? checkoutDirectory : new File( checkoutDirectory, reservedScmFile );
+ return pathToCheck.exists();
+ }
+
/**
* @return a List<String> with filenames/directories at the resourcepath.
* @see org.apache.maven.wagon.AbstractWagon#getFileList(java.lang.String)
@@ -795,15 +832,8 @@ public class ScmWagon
}
}
- private String getFilename( String filename )
- {
- String fname = StringUtils.replace( filename, "/", File.separator );
- return FileUtils.filename( fname );
- }
-
- private String getDirname( String filename )
+ private String getDirname( String resourceName )
{
- String fname = StringUtils.replace( filename, "/", File.separator );
- return FileUtils.dirname( fname );
+ return FileUtils.getPath( resourceName, '/' );
}
}
--
To stop receiving notification emails like this one, please contact
michaelo@apache.org.