Skip to content
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

Merged
merged 2 commits into from Jun 15, 2017

Conversation

Sanne
Copy link
Member

@Sanne Sanne commented Jun 14, 2017

…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

@yrodiere yrodiere self-assigned this Jun 15, 2017
Copy link
Member

@yrodiere yrodiere left a 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" );
Copy link
Member

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?

Copy link
Member

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.

Copy link
Member Author

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] ) );
Copy link
Member

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*)

Copy link
Member Author

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 );
Copy link
Member

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);
Copy link
Member

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();
Copy link
Member

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!

Copy link
Member Author

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() );
Copy link
Member

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() );
Copy link
Member

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.

Copy link
Member Author

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 (?!)

Copy link
Member

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... ?

Copy link
Member Author

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 );
Copy link
Member

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 );
Copy link
Member

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?

Copy link
Member Author

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 );
Copy link
Member

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.

@Sanne
Copy link
Member Author

Sanne commented Jun 15, 2017

rebased and updated

@Sanne
Copy link
Member Author

Sanne commented Jun 15, 2017

Fixed it an added a bonus commit for:

@yrodiere yrodiere merged commit e339f2e into hibernate:master Jun 15, 2017
@yrodiere
Copy link
Member

Merged! Thanks

@Sanne Sanne deleted the HSEARCH-2775 branch June 15, 2017 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants