Skip to content

Conversation

@calixtus
Copy link
Member

@calixtus calixtus commented May 27, 2024

First step towards implementing proper dependency injection (DI)

Major changes

  • Migrates the old globals to the Injector map modelsAndServices
  • Removes Globals class
  • Removes DefaultInjector class
  • Refactored KeyBindingRepository to singleton
  • Replaced convertToString implementation with Guava string splitter and added tests
  • Collected logging intialization in one method
  • Refactored Launcher
  • Moved initialization stuff in JabRefGUI to a new method

Follows JabRef#687

Mandatory checks

  • Change in CHANGELOG.md described in a way that is understandable for the average user (if applicable)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • Screenshots added in PR description (for UI changes)
  • Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small nitpick comments ^^

Comment on lines +96 to +97
PreferencesService preferencesService = Injector.instantiateModelOrService(PreferencesService.class);
BibtexParser parser = new BibtexParser(preferencesService.getImportFormatPreferences());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to Injector.instantiateModelOrService(ImportFormatPreferences)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would require to register the ImportFormatPreferences at launch time of jabref in the afterburner. Now we have lazy loading implemented. Would probably increase the launch time, as some of the preferences objects require some more construction time.

Comment on lines 250 to 252
preferencesService.getXmpPreferences(),
preferencesService.getFilePreferences(),
preferencesService.getLibraryPreferences().getDefaultBibDatabaseMode(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to

Injector.instantiateModelOrService(XmpPreferences.class),
Injector.instantiateModelOrService(FilePreferences.class),
Injector.instantiateModelOrService(LibraryPreferences.class),

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

preferencesService.getLibraryPreferences().getDefaultBibDatabaseMode(),
Globals.entryTypesManager,
Injector.instantiateModelOrService(BibEntryTypesManager.class),
preferencesService.getFieldPreferences(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injector.instantiateModelOrService(FieldPreferences.class),

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

* @param pr The result of the BIB parse operation.
*/
void performAction(ParserResult pr, DialogService dialogService);
void performAction(ParserResult pr, DialogService dialogService, PreferencesService preferencesService);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the injector be used "downstream"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a proper DI framework, yes. In the next iteration step

fieldColumn.setCellValueFactory(cellData -> BindingsHelper.constantOf(cellData.getValue()));
new ValueTableCellFactory<Field, Field>()
.withText(FieldsUtil::getNameWithType)
.withText(item -> FieldsUtil.getNameWithType(item, preferencesService, undoManager))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the Injector be used inside FieldsUtil to keep the method signature small?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be argued. For me the idea was the same with EntryEditor and the RelatedArticlesTab. As afterburner is not an di framework it just provides injection for a presenter in the PresenterFactory. Using the Injector class is not really the intended use and would not be really cleaner than using the Globals class. So I decided for constructor injection here.

styleSource,
outputFormat,
stateManager.getActiveDatabase().get(),
Injector.instantiateModelOrService(BibEntryTypesManager.class));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't the Injector be used inside CitationStyleGenerator?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, could be, but using a proper DI framework is just the next iteration step.

pdfIndexer = PdfIndexerManager.getIndexer(Globals.stateManager.getActiveDatabase().get(), Globals.prefs.getFilePreferences());
StateManager stateManager = Injector.instantiateModelOrService(StateManager.class);
PreferencesService preferencesService = Injector.instantiateModelOrService(PreferencesService.class);
pdfIndexer = PdfIndexerManager.getIndexer(stateManager.getActiveDatabase().get(), preferencesService.getFilePreferences());
Copy link
Member

@koppor koppor Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Injector inside PdfIndexerManger to get an instance of FilePrefererences instead of using the constructor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using contructor injection is always the cleanest solution for testing. About injection of smaller objects see above. Would require to abandon lazy loading of the preferences objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked at jvm-german for the first point (https://jvm-german.slack.com/archives/C4HLX61PY/p1717757611057629).

For the second point, I don't fully understand as I thought that DI really leads to lazy loading, because the injection happens at the latest possible time (if the Injector is called directly). Let's first solve the general discussion injection via constructor vs. injection at variables vs. injection using direct calls. And then, the other things will also get solves surely.

koppor
koppor previously approved these changes Jun 17, 2024
# Conflicts:
#	src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
#	src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java
#	src/main/java/org/jabref/gui/entryeditor/citationrelationtab/CitationRelationsTab.java
# Conflicts:
#	src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
#	src/main/java/org/jabref/gui/entryeditor/RelatedArticlesTab.java
#	src/main/java/org/jabref/gui/fieldeditors/ISSNEditor.java
#	src/main/java/org/jabref/gui/fieldeditors/JournalEditor.java
#	src/main/java/org/jabref/gui/fieldeditors/OwnerEditor.java
#	src/main/java/org/jabref/gui/fieldeditors/PersonsEditor.java
#	src/main/java/org/jabref/gui/fieldeditors/SimpleEditor.java
@koppor koppor enabled auto-merge June 17, 2024 20:47
koppor
koppor previously approved these changes Jun 17, 2024
@koppor
Copy link
Member

koppor commented Jun 17, 2024

The code locally checked out is not the same as treated by GitHub 😱

Update: We had some remote branch issues (same branch name on two upstreams).

@koppor koppor disabled auto-merge June 17, 2024 20:54
@koppor koppor enabled auto-merge June 17, 2024 20:59
@koppor koppor added this pull request to the merge queue Jun 17, 2024
@github-actions
Copy link
Contributor

github-actions bot commented Jun 17, 2024

The build for this PR is no longer available. Please visit https://builds.jabref.org/main/ for the latest build.

Merged via the queue into main with commit e6b5470 Jun 17, 2024
@koppor koppor deleted the di branch June 17, 2024 21:14
@LoayGhreeb
Copy link
Member

@calixtus, saving preferences throws an exception:

ERROR: Uncaught exception occurred in Thread[#53,JavaFX Application Thread,5,main]: java.lang.IllegalStateException: Cannot instantiate view: interface org.jabref.gui.frame.UiMessageHandler
	at afterburner.fx/com.airhacks.afterburner.injection.Injector.lambda$getDefaultInstanceSupplier$6(Injector.java:237)
	at afterburner.fx/com.airhacks.afterburner.injection.Injector.instantiateModelOrService(Injector.java:111)
	at afterburner.fx/com.airhacks.afterburner.injection.Injector.instantiateModelOrService(Injector.java:104)
	at [email protected]/org.jabref.gui.preferences.general.GeneralTabViewModel.storeSettings(GeneralTabViewModel.java:265)
	at [email protected]/org.jabref.gui.preferences.AbstractPreferenceTabView.storeSettings(AbstractPreferenceTabView.java:34)
	at [email protected]/org.jabref.gui.preferences.PreferencesDialogViewModel.storeAllSettings(PreferencesDialogViewModel.java:183)
	at [email protected]/org.jabref.gui.preferences.PreferencesDialogView.savePreferencesAndCloseDialog(PreferencesDialogView.java:132)
	at [email protected]/org.jabref.gui.preferences.PreferencesDialogView.lambda$new$0(PreferencesDialogView.java:56)
	at [email protected]/org.jabref.gui.util.ControlHelper.lambda$setAction$0(ControlHelper.java:29)

@calixtus
Copy link
Member Author

Thanks. Hotfix created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: keybinding component: logging dev: code-quality Issues related to code or architecture decisions status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants