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
HSEARCH-2775 Migrate internal file helpers and SPIs from File to NIO … #1450
Conversation
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.
Nice cleanup :)
A few comments below; they may look like nitpicking for some, but if we don't talk about this now, I don't think we ever will, so...
Note some problems may be very important, most notably unclosed streams.
return false; | ||
} | ||
return indexName.equals( ( (FSDirectoryProvider) obj ).indexName ); | ||
throw new AssertionFailure( "this type can not be compared reliably" ); |
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.
You could also simply throw an exception when this method is called before initialization, which would be a bit easier to callers.
But why do we need a custom implementation of equals on this class anyway?
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.
Ok, I just saw how unnecessarily complex the other implementations are. You're right, let's just forbid its use.
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.
We used to need this, so for that reason they had "reasonable" implementations.
That requirement is gone - since many years a I suspect - and since it's damn complex to do it "right" I prefer to introduce the assertion to make sure we never rely on it.
try { | ||
new File( destination, CURRENT_DIR_NAME[index] ).createNewFile(); | ||
Files.delete( destination.resolve( CURRENT_DIR_NAME[oldIndex] ) ); |
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.
So you don't want to use log.unableToRemovePreviousMarket
anymore... ?
(By the way, that method should be renamed unableToRemovePreviousMarke*r*
)
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 right. The File API doesn't throw an error when it fails, it would just return false.
The NIO API throws appropriate exceptions.
} | ||
|
||
// copy is required | ||
int oldIndex = current; | ||
int index = oldIndex == 1 ? 2 : 1; | ||
File destinationFile = new File( destination, Integer.valueOf( index ).toString() ); | ||
Path destinationFile = destination.resolve( Integer.valueOf( index ).toString() ); | ||
try { | ||
log.tracef( "Copying %s into %s", sourceFile, destinationFile ); | ||
FileHelper.synchronize( sourceFile, destinationFile, true, copyChunkSize ); |
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.
Seems like you forgot some occurrences of File
below (new File( indexName, "current" + oldIndex ).delete()
for instance)
@@ -31,5 +32,5 @@ | |||
* @return the lock factory as configured, or a factory adhering to the "simple" strategy in case of configuration | |||
* errors or as a default. | |||
*/ | |||
LockFactory createLockFactory(File indexDir, Properties dirConfiguration); | |||
LockFactory createLockFactory(Path indexDir, Properties dirConfiguration); |
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's also a reference to File
in the javadoc above ({@link File}
).
tryDelete( destination ); | ||
} | ||
final File sourceFile = source.toFile(); | ||
final File destinationFile = destination.toFile(); |
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.
Any reason why you don't want to convert copyFile
to use Path
too? It would avoid such conversions, and it would make the main code (tests excluded) entirely File
-free!
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.
ok, but I just wanted to remove a File
reference from a deprecated SPI and look what it got me into :)
Files.createDirectories( destination ); | ||
Set<Path> sources = Files.list( source ).collect( Collectors.toSet() ); | ||
Set<String> sourceFilenames = sources.stream().map( v -> v.getFileName().toString() ).collect( Collectors.toSet() ); | ||
Set<Path> dests = Files.list( destination ).collect( Collectors.toSet() ); |
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 pretty sure you don't need this set. Just using Files.list( destination ).map
instead of dests.stream().map
when building destFilenames
below would work equally well.
And while we're at it, I don't think you need to collect destination files at all. This would work just fine for deletion, and destination files are not used anywhere else:
//delete files not present in source
try ( DirectoryStream<Path> dests = Files.newDirectoryStream( source ) ) {
for ( Path dest : dests ) {
if ( !sourceFilenames.contains( dest.getFileName().toString() ) ) {
delete( dest );
}
}
}
You could also avoid collecting source file names and just resolve each dest file name against the source, but I'm not sure there's a benefit in that.
String[] dests = destination.list(); | ||
if ( Files.isDirectory( source ) ) { | ||
Files.createDirectories( destination ); | ||
Set<Path> sources = Files.list( source ).collect( Collectors.toSet() ); |
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 must admit I'm not very familiar with nio, but shouldn't we explicitly close the streams? (stream.close()
)
From the javadoc of Files.list
:
* <p> The returned stream encapsulates a {@link DirectoryStream}.
* If timely disposal of file system resources is required, the
* {@code try}-with-resources construct should be used to ensure that the
* stream's {@link Stream#close close} method is invoked after the stream
* operations are completed.
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.
Good catch. I assumed this was handled by the Stream/Collector framework but apparently it will only do that if you flatmap the stream (?!)
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.
@Sanne Unless I'm mistaken, you didn't address this... ?
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.
@yrodiere you're right. How embarrassing. There are two blocks of code which are very similar, I fixed one and missed the other.
private void writeDummyDataToFile(File file) throws IOException { | ||
FileOutputStream os = new FileOutputStream( file, true ); | ||
private void writeDummyDataToFile(Path file) throws IOException { | ||
FileOutputStream os = new FileOutputStream( file.toFile(), true ); |
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 think the nio way would be: Files.newOutputStream( file, StandardOpenOption.APPEND )
;
log.debugf( "Index directory: %s", indexDir.getPath() ); | ||
sourceDir = DirectoryProviderHelper.getSourceDirectoryPath( directoryProviderName, properties, true ); | ||
log.debugf( "Source directory: %s", sourceDir ); | ||
indexDir = DirectoryHelper.getVerifiedIndexPath( directoryProviderName, properties, true ); |
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.
You may want to normalize the path at some point (indexDir = indexDir.normalize()
), since previously we used indexDir.getCanonicalPath()
to set indexName
. Or maybe it's not important now that we don't use it for equals
anymore?
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 think it's no longer crucial, but yes I'll do it. Will at least look better when logging the dir..
} | ||
sourceIndexDir = DirectoryProviderHelper.getSourceDirectoryPath( directoryProviderName, properties, false ); | ||
log.debugf( "Source directory: %s", sourceIndexDir ); | ||
indexDir = DirectoryHelper.getVerifiedIndexPath( directoryProviderName, properties, true ); |
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.
Same remark as in FSMasterDirectoryProvider.java
about normalizing indexDir
.
rebased and updated |
Fixed it an added a bonus commit for: |
Merged! Thanks |
…APIs
A bit of a mindless, boring translation task.. but IMO the code looks much more readable now, and I could get rid of several deprecated methods.
https://hibernate.atlassian.net/browse/HSEARCH-2775