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]>