JDK-8031435 : Ftp download does not work properly for ftp user without password
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 7u45
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • Submitted: 2014-01-09
  • Updated: 2015-01-21
  • Resolved: 2014-08-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u76Fixed 8u40Fixed 9Fixed
Description
FULL PRODUCT VERSION :
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) Client VM (build 24.45-b08, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Ftp download using URLConnection and url string does not work properly for ftp user without password.

I have looked at the changes between Java 6 and 7 in the class FtpURLConnection.
Following method throws the NullPointerExcpetion:
FtpURLConnection.connect
in this line:
ftp.login(user, password.toCharArray());
in Java 7.

This line in Java 6 looks like:
ftp.login(user, password);

So in the new version of JAVA nobody checks if the variable password is null (if it is possible to call the method toCharArray()).
With new Java version there is not possbile to use ftp urls with given user without password like this one used in the code I send: "ftp://test@localhost".




STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Look at the test case (main method).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Does not throw any exception.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.NullPointerException
at sun.net.www.protocol.ftp.FtpURLConnection.connect(FtpURLConnection.java:304)
at sun.net.www.protocol.ftp.FtpURLConnection.getInputStream(FtpURLConnection.java:393)
at com.kamil.ftp.FakeFtpTest.main(FakeFtpTest.java:36)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.mockftpserver.fake.FakeFtpServer;
import org.mockftpserver.fake.UserAccount;
import org.mockftpserver.fake.filesystem.DirectoryEntry;
import org.mockftpserver.fake.filesystem.FileEntry;
import org.mockftpserver.fake.filesystem.FileSystemEntry;
import org.mockftpserver.fake.filesystem.UnixFakeFileSystem;

public class FakeFtpTest {

private static final String FTP_URL = "ftp://test@localhost";
private static final String FTP_FILE_PATH = "/document.txt";
private static final String FTP_FILE_CONTENT = "test document content";

public static void main(String[] args) throws IOException {
String ftpFileUrl = FTP_URL + FTP_FILE_PATH;
System.out.println("START ftp server and try to download a file from " + ftpFileUrl);

FakeFtpServer fakeFtp = createFakeFtp();
fakeFtp.start();
if (!fakeFtp.isStarted()) {
System.out.println("Wasn't able to start an instance of a FakeFtpServer.");
return;
}

URL url = new URL(ftpFileUrl);
// InputStream in = url.openStream();
// line above is equivalent to 2 lines below
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream(); // throws NullPointerException
// It works properly when
// FTP_URL =
// "ftp://test:@localhost"

in.close();
fakeFtp.stop();

System.out.println("STOP");
}

private static FakeFtpServer createFakeFtp() {
final FakeFtpServer fakeFtp = new FakeFtpServer();
fakeFtp.setFileSystem(new UnixFakeFileSystem());
fakeFtp.setServerControlPort(21);

FileSystemEntry entry = new DirectoryEntry("/");
fakeFtp.getFileSystem().add(entry);
fakeFtp.getFileSystem().add(new FileEntry(FTP_FILE_PATH, FTP_FILE_CONTENT));

UserAccount userAccount = new UserAccount("test", null, "/");
userAccount.setAccountRequiredForLogin(false);
userAccount.setPasswordCheckedDuringValidation(false);
userAccount.setPasswordRequiredForLogin(false);
fakeFtp.addUserAccount(userAccount);

return fakeFtp;
}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I have described the workaround in the code of the test case.
It works fine when instead of the url: "ftp://test@localhost" there is also used ':' for empty password: "ftp://test:@localhost".