Improved disconnect handling in FtpClient: retrieveFile never returns under
certain conditions and calling FtpClient.disconnect does not terminate the
transfer.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Key: NET-406
URL: https://issues.apache.org/jira/browse/NET-406
Project: Commons Net
Issue Type: Improvement
Components: FTP
Affects Versions: 2.0
Environment: Linux
Reporter: Nils Martin Sande
During file download it is possible for the transfer to stop without any time
out being triggered in FtpClient. The result is a "lost" thread (it never
returns from retrieveFile). Calling interrupt on the missing thread does not do
anything.
When we encountered this error we fist tried to monitor the downloaded file and
then call FtpClient.disconnect if the size on disk remained unchanged for a
given period of time. This did not solve the problem since FtpClient.disconnect
does not close the socket used by the transfer. Changing FtpClient so that it
keeps track of active sockets and then closing all active sockets on
FtpClient.disconnect solved the problem for us. Although this error is not very
common, the ability to completely kill the connection including all transfers
seems like a useful feature. I have included the details of the modifications
we made, maybe someone will find them useful. If there is a better way to get
around this please let me know.
{code:title=FtpClient.java_retrieveFile|borderStyle=solid}
//The modified retrieveFile method (__activeSockets is a synchronized
List<Socket>)
public boolean retrieveFile(String remote, OutputStream local)
throws IOException {
InputStream input;
Socket socket;
if ((socket = _openDataConnection_(FTPCommand.RETR, remote)) == null) {
return false;
}
__activeSockets.add(socket);
try {
input = new BufferedInputStream(socket.getInputStream(),
getBufferSize());
if (__fileType == ASCII_FILE_TYPE) {
input = new FromNetASCIIInputStream(input);
}
// Treat everything else as binary for now
try {
Util.copyStream(input, local, getBufferSize(),
CopyStreamEvent.UNKNOWN_STREAM_SIZE, null,
false);
} catch (IOException e) {
try {
socket.close();
} catch (IOException f) {
}
throw e;
}
socket.close();
return completePendingCommand();
} finally {
__activeSockets.remove(socket);
}
}
//The modified disconnect method
public void disconnect() throws IOException {
super.disconnect();
__initDefaults();
IOException exception = null;
for (Socket socket : __activeSockets) {
try {
socket.close();
} catch (IOException ex) {
exception = ex;
}
}
__activeSockets.clear();
if (exception != null) {
throw exception;
}
}
{code}
--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira