-
-
Notifications
You must be signed in to change notification settings - Fork 9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[JENKINS-47324] - Reduce usage of File.mkdirs() #3173
[JENKINS-47324] - Reduce usage of File.mkdirs() #3173
Conversation
I have opened this PR early despite the code clearly not being ready yet as I am looking for feedback as I go - this is quite a challenging change for me so any help is appreciated! |
@@ -1962,7 +1963,7 @@ public void renameTo(final FilePath target) throws IOException, InterruptedExcep | |||
act(new SecureFileCallable<Void>() { | |||
private static final long serialVersionUID = 1L; | |||
public Void invoke(File f, VirtualChannel channel) throws IOException { | |||
reading(f).renameTo(creating(new File(target.remote))); | |||
Files.move(reading(f).toPath(), creating(new File(target.remote)).toPath(), StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't try/catch here as Files.move()
throws an IOException
, which is what we would like to propagate out from this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, but you do want to merge with master and use Util#fileToPath
anywhere you use File#toPath
so that the runtime exception InvalidPathException
is wrapped as an IOException
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the documentation for Files#move doesn't include StandardCopyOption.COPY_ATTRIBUTES
, so I'd remove it here. (I think most implementations of Files#move
attempt to copy attributes by default.)
@@ -1121,7 +1122,7 @@ public URI toURI() throws IOException, InterruptedException { | |||
return act(new SecureFileCallable<URI>() { | |||
private static final long serialVersionUID = 1L; | |||
public URI invoke(File f, VirtualChannel channel) { | |||
return f.toURI(); | |||
return f.toPath().toUri(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a tangible benefit of this change - can anyone explain? It is listed as the 'new' equivalent of the old method here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no strong benefit. However, in your case you are at risk of a new InvalidPathException
, which may be thrown by toPath()
Replace mkdir() with Files.createTempDirectory()
@@ -1121,7 +1122,7 @@ public URI toURI() throws IOException, InterruptedException { | |||
return act(new SecureFileCallable<URI>() { | |||
private static final long serialVersionUID = 1L; | |||
public URI invoke(File f, VirtualChannel channel) { | |||
return f.toURI(); | |||
return f.toPath().toUri(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no strong benefit. However, in your case you are at risk of a new InvalidPathException
, which may be thrown by toPath()
@@ -587,11 +588,11 @@ private void unzip(File dir, File zipFile) throws IOException { | |||
ZipEntry e = entries.nextElement(); | |||
File f = new File(dir, e.getName()); | |||
if (e.isDirectory()) { | |||
mkdirs(f); | |||
Files.createDirectories(f.toPath()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Util#toPath()
created by @dwnusbaum a while ago. Otherwise there is a risk of unhandled runtime InvalidPathException
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also FilePath#mkdirs(File)
calls filterNonNull().mkdirs(dir);
, which checks permissions when doing remote operations. This removes that check. I would update the mkdirs
method itself to use Files#createDirectories
, and then you won't need to change all of its callers.
Marked the PR as "work-in-progress" according to the comment |
@@ -1409,7 +1410,7 @@ public FilePath createTempDir(final String prefix, final String suffix) throws I | |||
public String invoke(File dir, VirtualChannel channel) throws IOException { | |||
File f = File.createTempFile(prefix, suffix, dir); | |||
f.delete(); | |||
f.mkdir(); | |||
Files.createTempDirectory(f.toPath(), null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is being updated by #3161, so I'd remove this change as it will cause a merge conflict.
@@ -1962,7 +1963,7 @@ public void renameTo(final FilePath target) throws IOException, InterruptedExcep | |||
act(new SecureFileCallable<Void>() { | |||
private static final long serialVersionUID = 1L; | |||
public Void invoke(File f, VirtualChannel channel) throws IOException { | |||
reading(f).renameTo(creating(new File(target.remote))); | |||
Files.move(reading(f).toPath(), creating(new File(target.remote)).toPath(), StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, but you do want to merge with master and use Util#fileToPath
anywhere you use File#toPath
so that the runtime exception InvalidPathException
is wrapped as an IOException
.
@@ -587,11 +588,11 @@ private void unzip(File dir, File zipFile) throws IOException { | |||
ZipEntry e = entries.nextElement(); | |||
File f = new File(dir, e.getName()); | |||
if (e.isDirectory()) { | |||
mkdirs(f); | |||
Files.createDirectories(f.toPath()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also FilePath#mkdirs(File)
calls filterNonNull().mkdirs(dir);
, which checks permissions when doing remote operations. This removes that check. I would update the mkdirs
method itself to use Files#createDirectories
, and then you won't need to change all of its callers.
@@ -1962,7 +1963,7 @@ public void renameTo(final FilePath target) throws IOException, InterruptedExcep | |||
act(new SecureFileCallable<Void>() { | |||
private static final long serialVersionUID = 1L; | |||
public Void invoke(File f, VirtualChannel channel) throws IOException { | |||
reading(f).renameTo(creating(new File(target.remote))); | |||
Files.move(reading(f).toPath(), creating(new File(target.remote)).toPath(), StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the documentation for Files#move doesn't include StandardCopyOption.COPY_ATTRIBUTES
, so I'd remove it here. (I think most implementations of Files#move
attempt to copy attributes by default.)
I had a crawl through how |
@KrishanBhasin Yes definitely, I've been working on that in #3169 if you want to take a look and review it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current state looks good. If you want to clean up the rest of the code that creates directories, you could also update IOUtils#mkdirs
which is called by FilePath#mkdirsE
, and then FilePath#mkdirs
which has one last call to File#mkdirs
(around line 1168) that could be switched to use mkdirs(f)
. That would get rid of all calls to File#mkdirs
and File#mkdir
(after #3161 is merged) from FilePath
, which would be great.
import java.nio.file.Files; | ||
import java.nio.file.InvalidPathException; | ||
import java.nio.file.StandardCopyOption; | ||
import java.nio.file.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I personally prefer using single-class imports (especially for non-static imports) to make it obvious what classes are being used and avoid any name clashes if new classes are added to the package. I don't know if there is a preference either way in Jenkins in general.
move mkdirs() to using FilePath method instead of File method.
@dwnusbaum I think I've covered off those two methods you pointed out. I thought this Jira ticket was going to involve a lot more changes, it looked a lot more intimidating than it actually was! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just another File#toPath()
} catch (InterruptedException e) { | ||
// ignore | ||
return Files.createDirectories(dir.toPath()).toFile(); | ||
} catch (UnsupportedOperationException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also https://docs.oracle.com/javase/7/docs/api/java/nio/file/InvalidPathException.html . @dwnusbaum has added a utility method for that recently, so you could replace dir.toPath()
byt fileToPath(dir)
import static hudson.Util.deleteFile; | ||
import static hudson.Util.fixEmpty; | ||
import static hudson.Util.isSymlink; | ||
import static hudson.Util.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend to avoid doing it in a production code to avoid ambiguity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I had set Intellij to avoid doing this! I have fixed the settings & the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -2957,11 +2960,12 @@ private File deleting(File f) { | |||
return f; | |||
} | |||
|
|||
private boolean mkdirs(File dir) { | |||
private boolean mkdirs(File dir) throws IOException { | |||
if (dir.exists()) return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel this method should check whether the file is a directory and throw the exception otherwise. It is not related to this change, I will follow-up in a pull request. Maybe we can kill the method at all to be on the safe side
// ignore | ||
return Files.createDirectories(fileToPath(dir)).toFile(); | ||
} catch (UnsupportedOperationException e) { | ||
throw new IOException(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure what Ant <mkdir> task
was referring to here before. It would be closer to the original code to try again if an IOException
is thrown, but I can't think of a common error that would actually be fixed by doing that, so I'm ok with getting rid of the sleep with this change.
Thanks @KrishanBhasin ! |
Thanks @oleg-nenashev and @dwnusbaum for all the feedback as I went! I'll keep any eye out for another low hanging issue that I can try picking up :) |
See JENKINS-47324.
Proposed changelog entries
renameTo()
Submitter checklist
* Use the
Internal:
prefix if the change has no user-visible impact (API, test frameworks, etc.)Desired reviewers
@dwnusbaum