-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix #2739 null pointer exception on get fulltext #2744
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
Fix #2739 null pointer exception on get fulltext #2744
Conversation
'null pointer exception'.
pointer exception' when a databse is opened in a current directory.
re-create the necessary run environment.
also three regression tests for the old functionality.
the previous bug. Indeed, the new unit test fails both in Eclipse and in the CLI './gradlew build' run.
pointer exception' if a database is passed as a local file without explicite path. The tests in 'BibDatabaseContextTest.java' pass again.
'BibDatabaseContextTest'.
tobiasdiez
left a comment
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.
Thank for your PR. I've no experience with the file/path - related code, so I can't say anything about the actual fix (but it looks good in principle). So I just made a few comments regarding the test code.
A big thumps up that you create tests for your fixes. This is really appreciated! I look forward to more PRs from you 😄
| assert dbPath != null : "dbPath is null"; | ||
| Path parentPath = dbPath.getParent(); | ||
| if( parentPath == null ) { | ||
| parentPath = Paths.get(System.getProperty("user.dir")); |
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 sure about the fallback to the user directory and would have just encapsulated everything in an if (parentPath != null) statemant. @Siedlerchr what do you think?
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.
Not sure whether that helps: The directory lookup is explained at #459 (comment). The addition to help is still a TODO (https://github.com/JabRef/help.jabref.org/issues/160).
I also wait for @Siedlerchr s feedback 😇
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.
Hi, folks, thank you for the comments!
I think the
if (parentPath != null){parentPath = Paths.get(System.getProperty("user.dir"));}
fix is essential. It expresses a usual Unix/Windows CLI convention that invoking "jabref biblio.bib" is actually equivalent to "jabref ./biblio.bib" and "jabref $(pwd)/biblio.bib". Without this fix, running "jabref biblio.bib" and clicking the "Get fulltext" button in the "General" tab throws a "null pointer exception" in the searcher thread, and the main GUI thread just "hangs" forever without yielding any results (and without any error messages...). IMHO, this is a show stopper.
With the suggested fix, JabRef behaves as most users would expect -- it reads 'biblio.bib' in the current directory, and, if a database directory has to be used as a base for searching files, it uses the same current directory as a 'biblio.bib' base. Please note that when the directory is specified explicitely, as in 'jabref ~/papers/bibliography.bib', this workaround is not used.
The behaviour of the fix is totally orthogonal to the policies in #459 (comment): if any other policy of determining a search directory is used, the fix is inactive; also, when a database has explicit directory name, it is inactive as well. It is only when the directory is not specified that I suggest using System.getProperty("user.dir"); BTW, this seems to be the same policy as used when opening a database for the command 'jabref biblio.bib'.
| public void setUp() { | ||
| Map<String, String> mapFieldDirs = new HashMap<>(); | ||
| preferences = new FileDirectoryPreferences("saulius", mapFieldDirs, true); | ||
| currentWorkingDir = Paths.get(System.getProperty("user.dir")).toString(); |
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 is preferable to use temporary folder rule of junit here, so that the test files are automatically deleted afterwards.
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 directory named in the 'currentWorkingDir' value is not intended to hold any created files; actually, these tests should not create any files (I'll double check that).
The 'currentWorkingDir' variable is used to re-create the part of the JabRef internal state that we get when the 'jabref biblio.bib' command is invoked from a command line (on Unix/Linux, but I guess on Windows clones as well). Since the directory is different on various computers that invoke the test, I can not hard-code it into the test but must determine it at run-time.
Should I comment this in the code, or find a better name for the 'currentWorkingDir' variable?
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.
+1 for commenting in 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.
Checked: the tests do not leave the 'biblio.bib' file behind. The File object, as I understand, holds an abstract system-independent path to a file, but does not open or create the file unless exlicitely requested.
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.
+1 for commenting in the code.
Commenting the use of the variables in the test.
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 can influence the fileDirectoryPreferences by mocking them, otherwise you always depend on the the settings of the user and can't have a reliable value to test.
In the CleanupTests for Renaming/Moving Files I mocked the relevant options like this:
fileDirPrefs = mock(FileDirectoryPreferences.class); when(fileDirPrefs.isBibLocationAsPrimary()).thenReturn(false);
Have a look at MoveFilesCleanupTest
| private String currentWorkingDir; | ||
|
|
||
| @Rule | ||
| public ExpectedException thrown = ExpectedException.none(); |
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 rule is not used, right?
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.
As I understand, it "Returns a rule that expects no exception to be thrown" (http://junit.org/junit4/javadoc/latest/org/junit/rules/ExpectedException.html). Although this is the default behaviour, it explicitly states that these tests MUST NOT through an exception, since this is what we test for. The same construct is used in BibDatabaseTest.java, and make IMHO the intent of the test more explicit and easier to read. I suggest leaving it.
| BibDatabaseContext dbContext = new BibDatabaseContext(); | ||
| dbContext.setDatabaseFile(new File("biblio.bib")); | ||
| List<String> fileDirectories = dbContext.getFileDirectories( "file", preferences ); | ||
| assertTrue(fileDirectories.get(0).equals(currentWorkingDir)); |
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.
please use assertEquals so that you get a better error message in case the test fails. For the same reason, assertEquals(Collections.singletonList(expected item), actual list) is the preferred way for lists.
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 are right. assertEquals(Collections.singletonList(... will check also the length of the list. Fixed.
| // 4. BIB file directory | ||
| getDatabasePath().ifPresent(dbPath -> { | ||
| String parentDir = dbPath.getParent().toAbsolutePath().toString(); | ||
| assert dbPath != null : "dbPath is 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.
We do not use assert statements in our code. We always do Objects.requireNonNull(...). Is it possible to change that?
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.
Sure it is :). IMHO, assert seems a bit more applicable here, since I see asserts intended for developers, and Objects.requireNonNull() will probably end up with the message for the end user.
The assert statements are off by default but can be switched on 'in the field', unless the they were deliberately optimised away. As I understand, Objects.requireNonNull() will be always on and will always run. If this is what your policy is, I can change it, for sure.
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.
Fixed 'asserts' in the ee4e023 commit.
| String parentDir = dbPath.getParent().toAbsolutePath().toString(); | ||
| assert dbPath != null : "dbPath is null"; | ||
| Path parentPath = dbPath.getParent(); | ||
| if( parentPath == 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.
Can you adjust formatting? In JabRef we do if (...) {
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.
Fixed in e56e68c. Sorry, I've overlooked that (and Eclipse overlooked it as well ;)
| assert dbPath != null : "dbPath is null"; | ||
| Path parentPath = dbPath.getParent(); | ||
| if( parentPath == null ) { | ||
| parentPath = Paths.get(System.getProperty("user.dir")); |
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.
Not sure whether that helps: The directory lookup is explained at #459 (comment). The addition to help is still a TODO (https://github.com/JabRef/help.jabref.org/issues/160).
I also wait for @Siedlerchr s feedback 😇
|
Thank you for the PR! I'm not sure whether the Codacy coplaint can be fixed. Unfortunately, we want to release a beta version today. Since it's a beta version, I'd like to postpone your improvement. Please forgive me for that 😄 I hope, we get the improvment in the next days. |
|
The Codacy complaint should be fixed by now (commit 2deedc7). |
BibDatabaseContextTest unit test.
No problem. It would be great, though, if the PR is not lost 😄. We essentially depend on its behaviour in our use of JabRef, but I can easily install my local branch on our computers, and install the next release as it comes out. Regards, |
| public void getFileDirectoriesWithRelativeDbParent() { | ||
| String dbDirectory = "relative/subdir"; | ||
| BibDatabaseContext dbContext = new BibDatabaseContext(); | ||
| dbContext.setDatabaseFile(new File(dbDirectory + "/" + "biblio.bib")); |
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.
Please use the methods of the Paths class. You can then just call Paths.get(dbDirectory).resolve("bib")
| @Test | ||
| public void getFileDirectoriesWithEmptyDbParent() { | ||
| BibDatabaseContext dbContext = new BibDatabaseContext(); | ||
| dbContext.setDatabaseFile(new File("biblio.bib")); |
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.
And here also please use Paths.get().toFile()
Siedlerchr
left a comment
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.
Please mock the prefs and use methods of the Paths. class.
Otherwise looks good!
|
Thank you very much for your PR! I only have a few small remarks. The rest looks very good to me! |
Fixing issue #2739: a check in the BibDatabaseContext.getFileDirectories(String, FileDirectoryPreferences) method is added (BibDatabaseContext.java:203) to make sure that "dbPath.getParent()" is not null before its toAbsolutePath() method is called. This fixes 'null pointer exception' when searching files for databases opened without explicit path, as in the CLI 'jabref bibliography.bib' command.
Asserts were added to make sure that dbPath.getParent() and other variables are not null before invoking methods.
gradle localizationUpdate? -- no localisation changes.