Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up[JENKINS-42934] Avoid using new FileInputStream / new FileOutputStream #2816
Conversation
reviewbybees
commented
Mar 20, 2017
|
This pull request originates from a CloudBees employee. At CloudBees, we require that all pull requests be reviewed by other CloudBees employees before we seek to have the change accepted. If you want to learn more about our process please see this explanation. |
|
It looks like the same change is needed in git client plugin for its use of new FileInputStream. Nice catch |
|
Sounds good to me but should there be a FindBugs rule for this? Or should we let the JDK team decide whether these classes should be deprecated unless and until a proper fix is made? (In practice they seem very reluctant to deprecate anything, even in cases where there is a demonstrable danger.) |
|
Some of the test failures seem legitimate |
|
@jglick a findbugs rule to catch any use of FileInputStream / FileOutputStream would probably be a good idea for a follow-up... as would corresponding changes in all plugins... but let's get this change tested and even better if we can run it through the ATH (not sure how to do that as the ATH has never worked on my machine) |
|
The most of the changes look good to me, but the approach in |
| @@ -490,9 +494,13 @@ private void lockFileForDeletion(File f) throws IOException, InterruptedExceptio | |||
| // On unix, can't use "chattr +u" because ext fs ignores it. | |||
| // On Windows, we can't delete files that are open for reading, so we use that. | |||
| assert Functions.isWindows(); | |||
| final InputStream s = new FileInputStream(f); | |||
| final FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE); | |||
| final FileLock lock = channel.lock(); | |||
This comment has been minimized.
This comment has been minimized.
oleg-nenashev
Mar 21, 2017
Member
OverlappingFileLockException if the lock is being already held by the JVM. It will cause an unhadled and random Runtime exception here. The best pattern is a loop with tryLock(), but surprisingly it also has to handle this exception.
This comment has been minimized.
This comment has been minimized.
stephenc
Mar 21, 2017
Author
Member
How is this worse than the code that was there before?
The point of code review is not to get the reviewed code perfect, but to ensure that the code quality is an increasing function. i.e. dQ/dC < 0 is grounds to put a bug but dQ/dC == 0 is not (though dQ/dC > 0 is preferred obviously)
If we keep the attitude that dQ/dC == ∞ then we will never make any changes
(where Q is quality and C is changes to the code)
With this code, if run on a windows filesystem that does not support locking we will get a test Error rather than a test Failure... which IMHO is actually the correct result... so to put the point further, I would argue that this is actually the correct behaviour from test code (although my intent had been like for like so this is more of an unintended virtuous side-effect ;-) )
This comment has been minimized.
This comment has been minimized.
oleg-nenashev
Mar 21, 2017
Member
Likely it was a response to #2816 (comment), or maybe I do not get the response
OverlappingFileLockException will be happening on the system, which does support file locking. And, ridiculously, these exceptions happen on the success path (JVMs synchronize between each other, but JVM does not sync locks within itself. likely a bad design, but as designed).
| @@ -490,9 +494,13 @@ private void lockFileForDeletion(File f) throws IOException, InterruptedExceptio | |||
| // On unix, can't use "chattr +u" because ext fs ignores it. | |||
| // On Windows, we can't delete files that are open for reading, so we use that. | |||
| assert Functions.isWindows(); | |||
| final InputStream s = new FileInputStream(f); | |||
| final FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE); | |||
This comment has been minimized.
This comment has been minimized.
oleg-nenashev
Mar 21, 2017
Member
Though it is correct change for file locking API, it follows the presumption that All windows FileSystems support locking. And it is not correct. If the locking is not supported, IIRC the lock() call throws the IOException, which goes outside the method. Maybe it would be better to check if locking is supported in assert/assume
| current = new FileOutputStream(out,false); | ||
| } catch (FileNotFoundException e) { | ||
| current = Files.newOutputStream(out.toPath(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); | ||
| } catch (FileNotFoundException | NoSuchFileException e) { |
This comment has been minimized.
This comment has been minimized.
oleg-nenashev
Mar 21, 2017
Member
Hmm, why? I would understand the InvalidPathException: https://docs.oracle.com/javase/7/docs/api/java/io/File.html#toPath()
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
stephenc
Mar 21, 2017
Author
Member
It's from Files.newOutputStream IIUC
Checked exception thrown when an attempt is made to access a file that does not exist.
So basically it seems that the JVM had NIH with itself
This comment has been minimized.
This comment has been minimized.
oleg-nenashev
Mar 21, 2017
Member
Uh oh, you are right. http://stackoverflow.com/questions/14263725/java-files-write-nosuchfileexception. Does it make sense to catch it in other usages then?
|
|
|
|
|
@reviewbybees done |
reviewbybees
commented
Mar 23, 2017
|
This pull request has completed our internal processes and we now respectfully request the maintainers of this repository to consider our proposal contained within this pull request for merging. |
| @@ -78,22 +76,22 @@ public CompressedFile(File file) { | |||
| /** | |||
| * Gets the OutputStream to write to the file. | |||
| */ | |||
| public OutputStream write() throws FileNotFoundException { | |||
| public OutputStream write() throws IOException { | |||
This comment has been minimized.
This comment has been minimized.
daniel-beck
Mar 23, 2017
Member
Is this safe to do? It may result in unhandled checked exceptions, right?
This comment has been minimized.
This comment has been minimized.
stephenc
Mar 29, 2017
Author
Member
Well every case I could find was catching the IOException anyway
| } catch (FileNotFoundException e) { | ||
| current = Files.newOutputStream(out.toPath(), StandardOpenOption.CREATE, | ||
| appendOnNextOpen ? StandardOpenOption.APPEND : StandardOpenOption.TRUNCATE_EXISTING); | ||
| } catch (FileNotFoundException | NoSuchFileException e) { |
This comment has been minimized.
This comment has been minimized.
daniel-beck
Mar 23, 2017
Member
Assuming newOutputStream throws the latter, isn't the former obsolete now?
This comment has been minimized.
This comment has been minimized.
stephenc
Mar 29, 2017
Author
Member
The javadoc for newOutputStream does not mandate which of these should be thrown so safer to catch both
| @@ -137,16 +139,9 @@ public static void safeTransform(@Nonnull Source source, @Nonnull Result out) th | |||
| throw new IllegalArgumentException(String.format("File %s does not exist or is not a 'normal' file.", file.getAbsolutePath())); | |||
| } | |||
|
|
|||
| FileInputStream fileInputStream = new FileInputStream(file); | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
stephenc
Mar 29, 2017
Author
Member
I was trying to keep the diff minimal... if you want I can optimize imports on the modified files, but that was re-sorting them for me with my current IDE and generating a large diff
This comment has been minimized.
This comment has been minimized.
daniel-beck
Mar 29, 2017
Member
IntelliJ highlights (low-lights?) unused imports and makes them trivial to remove.
Still, just wondered, not required IMO.
|
@stephenc There is still some unaddressed comments |
| @@ -174,11 +173,8 @@ public static int remotePost(String[] args) throws Exception { | |||
| con.setFixedLengthStreamingMode((int)tmpFile.length()); | |||
| con.connect(); | |||
| // send the data | |||
| FileInputStream in = new FileInputStream(tmpFile); | |||
| try { | |||
| try (InputStream in = Files.newInputStream(tmpFile.toPath())) { | |||
| Util.copyStream(in,con.getOutputStream()); | |||
This comment has been minimized.
This comment has been minimized.
| @@ -1134,7 +1134,7 @@ public static String xmlEscape(@Nonnull String text) { | |||
| * Creates an empty file. | |||
| */ | |||
| public static void touch(@Nonnull File file) throws IOException { | |||
| new FileOutputStream(file).close(); | |||
| Files.newOutputStream(file.toPath()).close(); | |||
This comment has been minimized.
This comment has been minimized.
|
Looks reasonable to me -- there may be opportunities for additional improvements in some of the IO methods, but better to make a small change now than wait for perfection. |
…ith PR: jenkinsci/jenkins#2816
…tream behavior after jenkinsci/jenkins#2816.
stephenc commentedMar 20, 2017
See JENKINS-42934 and JDK-8080225
@reviewbybees