-
Enhancement
-
Resolution: Unresolved
-
P4
-
25
When `HttpClient` was first introduced, `RequestPublishers.FilePublisher` was using `FileInputStream::new` to stream the contents of a file. It was reported in JDK-8235459 that "[`FIS::new`] does not work if the file is in a different file system than the default". There examples from ZIP file system and Jimfs[1] are shared. If a `Path` is provided using one of these file systems, `BodyPublishers::ofFile(Path)` was failing as follows:
java.lang.UnsupportedOperationException
at jdk.zipfs/jdk.nio.zipfs.ZipPath.toFile(ZipPath.java:673)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.<init>(RequestPublishers.java:265)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.create(RequestPublishers.java:260)
at java.net.http/java.net.http.HttpRequest$BodyPublishers.ofFile(HttpRequest.java:626)
java.lang.UnsupportedOperationException
at com.google.common.jimfs.JimfsPath.toFile(JimfsPath.java:411)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.<init>(RequestPublishers.java:265)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.create(RequestPublishers.java:260)
at java.net.http/java.net.http.HttpRequest$BodyPublishers.ofFile(HttpRequest.java:626)
If one would look closely, the journey that started with `ofFile(Path)` fails with `UnsupportedOperationException` on a `Path::toFile` call which is not supported by the associated file system. Hence, AFAICT, the diagnosis inJDK-8235459 involving the notion of "default file system" is misleadingly worded. Instead, the failure is due to file systems whose `Path` implementations don't support `toFile`. Since this historical context is scattered, following lines:
boolean defaultFS = true;
try {
path.toFile().getPath();
} catch (UnsupportedOperationException uoe) {
// path not associated with the default file system provider
defaultFS = false;
}
even got maintainers confused[2] that are later on involved in `SecurityManager`-removal (JDK-8344228). Looking back, if `defaultFS` would have been named as `toFileSupported`, the confusion could have been avoided to a great extent – naming is difficult.
Note: Per `Path::toFile` specification[3], except the default one obtained by `FileSystems::getDefault`, `FileSystem` implementations are expected to throw `UnsupportedOperationException` on `Path::toFile`. Though this is enforced by the specification; there is technically nothing blocking a custom `FileSystem` implementation from returning a `File` (i.e., not throwing `UOE`) from `Path::toFile`.
Since `Path::toFile` did not work, the file cannot be read using `FileInputStream`.JDK-8235459 solved the problem by failling back to `Files::newFileInputStream(Path)`:
return defaultFS
? new FileInputStream(path.toFile())
: Files.newInputStream(path);
This worked, because `Files::newInputStream` did not need `Path::toFile` and only operated using `Path`. Yet this bares the question: why didn't we completely replace `FIS::new` with `Files::newInputStream`? This is due to backward compatibility concerns: `FIS::new` and `Files::newInputStream` differ on exceptions they throw in certain situations:
- If file is not a regular file, `FIS::new` throws `FileNotFoundException`, while `Files::newInputStream` throws `UncheckedIOException`
- If file does not exist, `FIS::new` throws `FileNotFoundException`, while `Files::newInputStream` throws `NoSuchFileException`
In conclusion, we can simplify the `FilePublisher` code by completely switching to `Files::newInputStream`, granted necessary exception translations are performed.
During this archaeological study, it has been found that, `FilePublisherPermsTest` and `SecureZipFSProvider` added inJDK-8235459 are forgotten to be removed during the `SecurityManager`-removal (JDK-8344228). (Note that `FilePublisherPermsTest` still has a `FilePublisherTest` counterpart that does not involve SM checks.)
[1] An in-memory file system: https://github.com/google/jimfs
[2] https://github.com/openjdk/jdk/pull/22118#discussion_r1842886201
[3] https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/nio/file/Path.html#toFile()
java.lang.UnsupportedOperationException
at jdk.zipfs/jdk.nio.zipfs.ZipPath.toFile(ZipPath.java:673)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.<init>(RequestPublishers.java:265)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.create(RequestPublishers.java:260)
at java.net.http/java.net.http.HttpRequest$BodyPublishers.ofFile(HttpRequest.java:626)
java.lang.UnsupportedOperationException
at com.google.common.jimfs.JimfsPath.toFile(JimfsPath.java:411)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.<init>(RequestPublishers.java:265)
at java.net.http/jdk.internal.net.http.RequestPublishers$FilePublisher.create(RequestPublishers.java:260)
at java.net.http/java.net.http.HttpRequest$BodyPublishers.ofFile(HttpRequest.java:626)
If one would look closely, the journey that started with `ofFile(Path)` fails with `UnsupportedOperationException` on a `Path::toFile` call which is not supported by the associated file system. Hence, AFAICT, the diagnosis in
boolean defaultFS = true;
try {
path.toFile().getPath();
} catch (UnsupportedOperationException uoe) {
// path not associated with the default file system provider
defaultFS = false;
}
even got maintainers confused[2] that are later on involved in `SecurityManager`-removal (
Note: Per `Path::toFile` specification[3], except the default one obtained by `FileSystems::getDefault`, `FileSystem` implementations are expected to throw `UnsupportedOperationException` on `Path::toFile`. Though this is enforced by the specification; there is technically nothing blocking a custom `FileSystem` implementation from returning a `File` (i.e., not throwing `UOE`) from `Path::toFile`.
Since `Path::toFile` did not work, the file cannot be read using `FileInputStream`.
return defaultFS
? new FileInputStream(path.toFile())
: Files.newInputStream(path);
This worked, because `Files::newInputStream` did not need `Path::toFile` and only operated using `Path`. Yet this bares the question: why didn't we completely replace `FIS::new` with `Files::newInputStream`? This is due to backward compatibility concerns: `FIS::new` and `Files::newInputStream` differ on exceptions they throw in certain situations:
- If file is not a regular file, `FIS::new` throws `FileNotFoundException`, while `Files::newInputStream` throws `UncheckedIOException`
- If file does not exist, `FIS::new` throws `FileNotFoundException`, while `Files::newInputStream` throws `NoSuchFileException`
In conclusion, we can simplify the `FilePublisher` code by completely switching to `Files::newInputStream`, granted necessary exception translations are performed.
During this archaeological study, it has been found that, `FilePublisherPermsTest` and `SecureZipFSProvider` added in
[1] An in-memory file system: https://github.com/google/jimfs
[2] https://github.com/openjdk/jdk/pull/22118#discussion_r1842886201
[3] https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/nio/file/Path.html#toFile()
- relates to
-
JDK-8344228 Revisit SecurityManager usage in java.net.http after JEP 486 integration
-
- Resolved
-
-
JDK-8235459 HttpRequest.BodyPublishers#ofFile(Path) assumes the default file system
-
- Closed
-
- links to
-
Review(master) openjdk/jdk/25662