adammurdoch    2003/01/21 15:51:24

  Modified:    vfs/src/java/org/apache/commons/vfs/provider/ftp
                        FtpFileSystem.java FtpFileObject.java
  Log:
  Generalised the solution to the deadlock problem when copying files within the same 
FTP
  file system, as the problem actually applies to any operation on an FTP file system 
while
  a file's content is being read from or written to.  Now the FTP file system will 
spin up
  new connections as required.
  
  Revision  Changes    Path
  1.14      +92 -34    
jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileSystem.java
  
  Index: FtpFileSystem.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileSystem.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- FtpFileSystem.java        23 Nov 2002 00:41:10 -0000      1.13
  +++ FtpFileSystem.java        21 Jan 2003 23:51:24 -0000      1.14
  @@ -75,7 +75,12 @@
   final class FtpFileSystem
       extends AbstractFileSystem
   {
  -    private final FTPClient client;
  +    private final String hostname;
  +    private final String username;
  +    private final String password;
  +
  +    // An idle client
  +    private FTPClient idleClient;
   
       public FtpFileSystem( final FileName rootName,
                             final String hostname,
  @@ -84,40 +89,18 @@
           throws FileSystemException
       {
           super( rootName, null );
  -        try
  -        {
  -            client = new FTPClient();
  -            client.connect( hostname );
  -
  -            int reply = client.getReplyCode();
  -            if ( !FTPReply.isPositiveCompletion( reply ) )
  -            {
  -                throw new FileSystemException( 
"vfs.provider.ftp/connect-rejected.error", hostname );
  -            }
  -
  -            // Login
  -            if ( !client.login( username, password ) )
  -            {
  -                throw new FileSystemException( "vfs.provider.ftp/login.error", new 
Object[]{hostname, username}, null );
  -            }
  -
  -            // Set binary mode
  -            if ( !client.setFileType( FTP.BINARY_FILE_TYPE ) )
  -            {
  -                throw new FileSystemException( "vfs.provider.ftp/set-binary.error", 
hostname );
  -            }
  -        }
  -        catch ( final Exception exc )
  -        {
  -            closeConnection();
  -            throw new FileSystemException( "vfs.provider.ftp/connect.error", new 
Object[]{hostname}, exc );
  -        }
  +        this.hostname = hostname;
  +        this.username = username;
  +        this.password = password;
       }
   
       public void close()
       {
           // Clean up the connection
  -        closeConnection();
  +        if ( idleClient != null )
  +        {
  +            closeConnection( idleClient );
  +        }
   
           super.close();
       }
  @@ -138,7 +121,7 @@
       /**
        * Cleans up the connection to the server.
        */
  -    private void closeConnection()
  +    private void closeConnection( final FTPClient client )
       {
           try
           {
  @@ -155,12 +138,38 @@
       }
   
       /**
  -     * Returns an FTP client to use.
  +     * Creates an FTP client to use.
        */
  -    public FTPClient getClient()
  +    public FTPClient getClient() throws FileSystemException
       {
           // TODO - connect on demand, and garbage collect connections
  -        return client;
  +        if ( idleClient == null )
  +        {
  +            return createConnection( hostname, username, password );
  +        }
  +        else
  +        {
  +            final FTPClient client = idleClient;
  +            idleClient = null;
  +            return client;
  +        }
  +    }
  +
  +    /**
  +     * Returns an FTP client after use.
  +     */
  +    public void putClient( final FTPClient client )
  +    {
  +        if ( idleClient == null )
  +        {
  +            // Hang on to client for later
  +            idleClient = client;
  +        }
  +        else
  +        {
  +            // Close the client
  +            closeConnection( client );
  +        }
       }
   
       /**
  @@ -171,4 +180,53 @@
       {
           return new FtpFileObject( name, this, getRootName() );
       }
  +
  +    /**
  +     * Creates a new connection to the server.
  +     */
  +    private FTPClient createConnection( final String hostname,
  +                                        final String username,
  +                                        final String password )
  +        throws FileSystemException
  +    {
  +        try
  +        {
  +            final FTPClient client = new FTPClient();
  +
  +            try
  +            {
  +                client.connect( hostname );
  +
  +                int reply = client.getReplyCode();
  +                if ( !FTPReply.isPositiveCompletion( reply ) )
  +                {
  +                    throw new FileSystemException( 
"vfs.provider.ftp/connect-rejected.error", hostname );
  +                }
  +
  +                // Login
  +                if ( !client.login( username, password ) )
  +                {
  +                    throw new FileSystemException( "vfs.provider.ftp/login.error", 
new Object[]{hostname, username}, null );
  +                }
  +
  +                // Set binary mode
  +                if ( !client.setFileType( FTP.BINARY_FILE_TYPE ) )
  +                {
  +                    throw new FileSystemException( 
"vfs.provider.ftp/set-binary.error", hostname );
  +                }
  +            }
  +            catch ( final IOException e )
  +            {
  +                closeConnection( client );
  +                throw e;
  +            }
  +
  +            return client;
  +        }
  +        catch ( final Exception exc )
  +        {
  +            throw new FileSystemException( "vfs.provider.ftp/connect.error", new 
Object[]{hostname}, exc );
  +        }
  +    }
  +
   }
  
  
  
  1.11      +87 -85    
jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java
  
  Index: FtpFileObject.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- FtpFileObject.java        21 Jan 2003 02:44:57 -0000      1.10
  +++ FtpFileObject.java        21 Jan 2003 23:51:24 -0000      1.11
  @@ -55,13 +55,12 @@
    */
   package org.apache.commons.vfs.provider.ftp;
   
  +import java.io.IOException;
   import java.io.InputStream;
   import java.io.OutputStream;
   import org.apache.commons.net.ftp.FTPClient;
   import org.apache.commons.net.ftp.FTPFile;
   import org.apache.commons.vfs.FileName;
  -import org.apache.commons.vfs.FileObject;
  -import org.apache.commons.vfs.FileSelector;
   import org.apache.commons.vfs.FileSystemException;
   import org.apache.commons.vfs.FileType;
   import org.apache.commons.vfs.provider.AbstractFileObject;
  @@ -77,13 +76,16 @@
   {
       private static final FTPFile[] EMPTY_FTP_FILE_ARRAY = {};
   
  -    private FtpFileSystem ftpFs;
  +    private final FtpFileSystem ftpFs;
       private final String relPath;
   
       // Cached info
       private FTPFile fileInfo;
       private FTPFile[] children;
   
  +    // The client currently being used to read/write the content of this file
  +    private FTPClient client;
  +
       public FtpFileObject( final FileName name,
                             final FtpFileSystem fileSystem,
                             final FileName rootName )
  @@ -94,74 +96,13 @@
           relPath = rootName.getRelativeName( name );
       }
   
  -     /**
  -      * Copies another file, and all its descendents, to this file.
  -      *
  -      * If this file does not exist, it is created.  Its parent folder is also
  -      * created, if necessary.  If this file does exist, it is deleted first.
  -      *
  -      * <p>This method is not transactional.  If it fails and throws an
  -      * exception, this file will potentially only be partially copied.
  -      *
  -      * @param file The source file to copy.
  -      * @param selector The selector to use to select which files to copy.
  -      *
  -      * @throws FileSystemException
  -      *      If this file is read-only, or if the source file does not exist,
  -      *      or on error copying the file.
  -      */
  -     public void copyFrom( final FileObject file, final FileSelector selector )
  -             throws FileSystemException      {
  -             
  -             // We override copyFrom here for the specific case where the source 
  -             // and destination files in the copy are from the same FTPFileSystem.  
  -             // The problem is this can't be done through one FTP session
  -             // concurrently( I think it creates a race condition) So we 
  -             // temporarily create a new one just for use during this copy
  -             // and then return everything to the way it was afterward.
  -             
  -             // if we're copying to and from the same FileSystem
  -             if ( file.getFileSystem().equals( this.getFileSystem() ) ) {
  -                     
  -                     FtpFileNameParser parser = new FtpFileNameParser();
  -
  -                     // save the old file system for later
  -                     FtpFileSystem oldFs = this.ftpFs;
  -                     
  -                     // create a new file system for use temporarily
  -                     FtpUri uri = parser.parseFtpUri( this.getURL().toString() );
  -                     this.ftpFs = new FtpFileSystem( oldFs.getRoot().getName(),
  -                                                                                    
  uri.getHostName(),
  -                                                                                    
  uri.getUserName(),
  -                                                                                    
  uri.getPassword() ); 
  -
  -                     // use our parent's copy functionality
  -                     super.copyFrom( file, selector );
  -                     
  -                     // return the filesystem to the way it was
  -                     this.ftpFs.close();
  -                     this.ftpFs = oldFs;
  -             }
  -             // otherwise proceed normally
  -             else {
  -                     super.copyFrom( file, selector );
  -             }
  -     }
  -
       /**
        * Called by child file objects, to locate their ftp file info.
        */
       private FTPFile getChildFile( final String name ) throws Exception
       {
  -        if ( children == null )
  -        {
  -            // List the children of this file
  -            children = ftpFs.getClient().listFiles( relPath );
  -            if ( children == null )
  -            {
  -                children = EMPTY_FTP_FILE_ARRAY;
  -            }
  -        }
  +        // List the children of this file
  +        doGetChildren();
   
           // Look for the requested child
           // TODO - use hash table
  @@ -179,6 +120,31 @@
       }
   
       /**
  +     * Fetches the children of this file, if not already cached.
  +     */
  +    private void doGetChildren() throws IOException
  +    {
  +        if ( children != null )
  +        {
  +            return;
  +        }
  +
  +        final FTPClient client = ftpFs.getClient();
  +        try
  +        {
  +            children = client.listFiles( relPath );
  +            if ( children == null )
  +            {
  +                children = EMPTY_FTP_FILE_ARRAY;
  +            }
  +        }
  +        finally
  +        {
  +            ftpFs.putClient( client );
  +        }
  +    }
  +
  +    /**
        * Attaches this file object to its file resource.
        */
       protected void doAttach()
  @@ -243,15 +209,8 @@
       protected String[] doListChildren()
           throws Exception
       {
  -        if ( children == null )
  -        {
  -            // List the children of this file
  -            children = ftpFs.getClient().listFiles( relPath );
  -            if ( children == null )
  -            {
  -                children = EMPTY_FTP_FILE_ARRAY;
  -            }
  -        }
  +        // List the children of this file
  +        doGetChildren();
   
           final String[] childNames = new String[ children.length ];
           for ( int i = 0; i < children.length; i++ )
  @@ -268,16 +227,24 @@
        */
       protected void doDelete() throws Exception
       {
  -        final FTPClient ftpClient = ftpFs.getClient();
           final boolean ok;
  -        if ( fileInfo.isDirectory() )
  +        final FTPClient ftpClient = ftpFs.getClient();
  +        try
           {
  -            ok = ftpClient.removeDirectory( relPath );
  +            if ( fileInfo.isDirectory() )
  +            {
  +                ok = ftpClient.removeDirectory( relPath );
  +            }
  +            else
  +            {
  +                ok = ftpClient.deleteFile( relPath );
  +            }
           }
  -        else
  +        finally
           {
  -            ok = ftpClient.deleteFile( relPath );
  +            ftpFs.putClient( ftpClient );
           }
  +
           if ( !ok )
           {
               throw new FileSystemException( "vfs.provider.ftp/delete-file.error", 
getName() );
  @@ -292,7 +259,18 @@
       protected void doCreateFolder()
           throws Exception
       {
  -        if ( !ftpFs.getClient().makeDirectory( relPath ) )
  +        final boolean ok;
  +        final FTPClient client = ftpFs.getClient();
  +        try
  +        {
  +            ok = client.makeDirectory( relPath );
  +        }
  +        finally
  +        {
  +            ftpFs.putClient( client );
  +        }
  +
  +        if ( !ok )
           {
               throw new FileSystemException( "vfs.provider.ftp/create-folder.error", 
getName() );
           }
  @@ -312,7 +290,8 @@
        */
       protected InputStream doGetInputStream() throws Exception
       {
  -        return ftpFs.getClient().retrieveFileStream( relPath );
  +        client = ftpFs.getClient();
  +        return client.retrieveFileStream( relPath );
       }
   
       /**
  @@ -321,7 +300,18 @@
       protected void doEndInput()
           throws Exception
       {
  -        if ( !ftpFs.getClient().completePendingCommand() )
  +        final boolean ok;
  +        try
  +        {
  +            ok = client.completePendingCommand();
  +        }
  +        finally
  +        {
  +            ftpFs.putClient( client );
  +            client = null;
  +        }
  +
  +        if ( !ok )
           {
               throw new FileSystemException( "vfs.provider.ftp/finish-get.error", 
getName() );
           }
  @@ -333,7 +323,8 @@
       protected OutputStream doGetOutputStream()
           throws Exception
       {
  -        return ftpFs.getClient().storeFileStream( relPath );
  +        client = ftpFs.getClient();
  +        return client.storeFileStream( relPath );
       }
   
       /**
  @@ -342,7 +333,18 @@
       protected void doEndOutput()
           throws Exception
       {
  -        if ( !ftpFs.getClient().completePendingCommand() )
  +        final boolean ok;
  +        try
  +        {
  +            ok = client.completePendingCommand();
  +        }
  +        finally
  +        {
  +            ftpFs.putClient( client );
  +            client = null;
  +        }
  +
  +        if ( !ok )
           {
               throw new FileSystemException( "vfs.provider.ftp/finish-put.error", 
getName() );
           }
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to