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

Prepares multi module build #11921

Merged
merged 18 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We removed support for case-sensitive and exact search. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We removed the description of search strings. [#11542](https://github.com/JabRef/jabref/pull/11542)
- We removed support for importing using the SilverPlatterImporter (`Record INSPEC`). [#11576](https://github.com/JabRef/jabref/pull/11576)

- We removed support for automatically generating file links using the CLI (`--automaticallySetFileLinks`).



Expand Down
233 changes: 19 additions & 214 deletions src/main/java/org/jabref/Launcher.java
Original file line number Diff line number Diff line change
@@ -1,239 +1,44 @@
package org.jabref;

import java.io.File;
import java.io.IOException;
import java.net.Authenticator;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.jabref.cli.ArgumentProcessor;
import org.jabref.cli.JabRefCLI;
import org.jabref.cli.JabRefCli;
import org.jabref.gui.JabRefGUI;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.preferences.JabRefGuiPreferences;
import org.jabref.gui.util.DefaultDirectoryMonitor;
import org.jabref.gui.util.DefaultFileUpdateMonitor;
import org.jabref.logic.UiCommand;
import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.net.ProxyAuthenticator;
import org.jabref.logic.net.ProxyPreferences;
import org.jabref.logic.net.ProxyRegisterer;
import org.jabref.logic.net.ssl.SSLPreferences;
import org.jabref.logic.net.ssl.TrustStoreManager;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.protectedterms.ProtectedTermsLoader;
import org.jabref.logic.remote.RemotePreferences;
import org.jabref.logic.remote.client.RemoteClient;
import org.jabref.logic.util.BuildInfo;
import org.jabref.logic.util.Directories;
import org.jabref.logic.util.HeadlessExecutorService;
import org.jabref.logic.util.Version;
import org.jabref.migrations.PreferencesMigrations;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.util.DirectoryMonitor;
import org.jabref.model.util.FileUpdateMonitor;

import com.airhacks.afterburner.injection.Injector;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import org.tinylog.configuration.Configuration;

/**
* The main entry point for the JabRef application.
* <p>
* It has two main functions:
* - Handle the command line arguments
* - Start the JavaFX application (if not in cli mode)
*/
/// The main entry point for the JabRef application.
///
/// It has two main functions:
///
/// - Handle the command line arguments
/// - Start the JavaFX application (if not in CLI mode)
public class Launcher {
private static Logger LOGGER;

public static void main(String[] args) {
initLogging(args);
JabRefCli.initLogging(args);

try {
Injector.setModelOrService(BuildInfo.class, new BuildInfo());
// Initialize preferences
final JabRefGuiPreferences preferences = JabRefGuiPreferences.getInstance();
Injector.setModelOrService(CliPreferences.class, preferences);
Injector.setModelOrService(GuiPreferences.class, preferences);

// Initialize preferences
final JabRefGuiPreferences preferences = JabRefGuiPreferences.getInstance();
Injector.setModelOrService(CliPreferences.class, preferences);
Injector.setModelOrService(GuiPreferences.class, preferences);
DefaultFileUpdateMonitor fileUpdateMonitor = new DefaultFileUpdateMonitor();
HeadlessExecutorService.INSTANCE.executeInterruptableTask(fileUpdateMonitor, "FileUpdateMonitor");

// Early exit in case another instance is already running
if (!handleMultipleAppInstances(args, preferences.getRemotePreferences())) {
return;
}
List<UiCommand> uiCommands = JabRefCli.processArguments(args, preferences, fileUpdateMonitor);
// The method `processArguments` quites the whole JVM if no GUI is needed.

BibEntryTypesManager entryTypesManager = preferences.getCustomEntryTypesRepository();
Injector.setModelOrService(BibEntryTypesManager.class, entryTypesManager);
PreferencesMigrations.runMigrations(preferences);

PreferencesMigrations.runMigrations(preferences, entryTypesManager);

Injector.setModelOrService(JournalAbbreviationRepository.class, JournalAbbreviationLoader.loadRepository(preferences.getJournalAbbreviationPreferences()));
Injector.setModelOrService(ProtectedTermsLoader.class, new ProtectedTermsLoader(preferences.getProtectedTermsPreferences()));

configureProxy(preferences.getProxyPreferences());
configureSSL(preferences.getSSLPreferences());

clearOldSearchIndices();

try {
DefaultFileUpdateMonitor fileUpdateMonitor = new DefaultFileUpdateMonitor();
Injector.setModelOrService(FileUpdateMonitor.class, fileUpdateMonitor);
HeadlessExecutorService.INSTANCE.executeInterruptableTask(fileUpdateMonitor, "FileUpdateMonitor");

DirectoryMonitor directoryMonitor = new DefaultDirectoryMonitor();
Injector.setModelOrService(DirectoryMonitor.class, directoryMonitor);

// Process arguments
ArgumentProcessor argumentProcessor = new ArgumentProcessor(
args,
ArgumentProcessor.Mode.INITIAL_START,
preferences,
preferences,
fileUpdateMonitor,
entryTypesManager);
argumentProcessor.processArguments();
if (argumentProcessor.shouldShutDown()) {
LOGGER.debug("JabRef shut down after processing command line arguments");
// A clean shutdown takes 60s time
// We don't need the clean shutdown here
System.exit(0);
}

List<UiCommand> uiCommands = new ArrayList<>(argumentProcessor.getUiCommands());
JabRefGUI.setup(uiCommands, preferences, fileUpdateMonitor);
JabRefGUI.launch(JabRefGUI.class, args);
} catch (ParseException e) {
LOGGER.error("Problem parsing arguments", e);
JabRefCLI.printUsage(preferences);
}
} catch (Exception ex) {
LOGGER.error("Unexpected exception", ex);
}
}

/**
* This needs to be called as early as possible. After the first log write, it
* is not possible to alter the log configuration programmatically anymore.
*/
private static void initLogging(String[] args) {
// routeLoggingToSlf4J
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

// We must configure logging as soon as possible, which is why we cannot wait for the usual
// argument parsing workflow to parse logging options .e.g. --debug
boolean isDebugEnabled;
try {
JabRefCLI jabRefCLI = new JabRefCLI(args);
isDebugEnabled = jabRefCLI.isDebugLogging();
} catch (ParseException e) {
isDebugEnabled = false;
}

// addLogToDisk
Version version = Injector.instantiateModelOrService(BuildInfo.class).version;
Path directory = Directories.getLogDirectory(version);
try {
Files.createDirectories(directory);
} catch (IOException e) {
LOGGER = LoggerFactory.getLogger(Launcher.class);
LOGGER.error("Could not create log directory {}", directory, e);
return;
}

// The "Shared File Writer" is explained at
// https://tinylog.org/v2/configuration/#shared-file-writer
Map<String, String> configuration = Map.of(
"level", isDebugEnabled ? "debug" : "info",
"writerFile", "rolling file",
"writerFile.level", isDebugEnabled ? "debug" : "info",
// We need to manually join the path, because ".resolve" does not work on Windows, because ":" is not allowed in file names on Windows
"writerFile.file", directory + File.separator + "log_{date:yyyy-MM-dd_HH-mm-ss}.txt",
"writerFile.charset", "UTF-8",
"writerFile.policies", "startup",
"writerFile.backups", "30");
configuration.forEach(Configuration::set);

LOGGER = LoggerFactory.getLogger(Launcher.class);
}

/**
* @return true if JabRef should continue starting up, false if it should quit.
*/
private static boolean handleMultipleAppInstances(String[] args, RemotePreferences remotePreferences) throws InterruptedException {
LOGGER.trace("Checking for remote handling...");
if (remotePreferences.useRemoteServer()) {
// Try to contact already running JabRef
RemoteClient remoteClient = new RemoteClient(remotePreferences.getPort());
if (remoteClient.ping()) {
LOGGER.debug("Pinging other instance succeeded.");
if (args.length == 0) {
// There is already a server out there, avoid showing log "Passing arguments" while no arguments are provided.
LOGGER.warn("This JabRef instance is already running. Please switch to that instance.");
} else {
// We are not alone, there is already a server out there, send command line arguments to other instance
LOGGER.debug("Passing arguments passed on to running JabRef...");
if (remoteClient.sendCommandLineArguments(args)) {
// So we assume it's all taken care of, and quit.
// Output to both to the log and the screen. Therefore, we do not have an additional System.out.println.
LOGGER.info("Arguments passed on to running JabRef instance. Shutting down.");
} else {
LOGGER.warn("Could not communicate with other running JabRef instance.");
}
}
// We do not launch a new instance in presence if there is another instance running
return false;
} else {
LOGGER.debug("Could not ping JabRef instance.");
}
}
return true;
}

private static void configureProxy(ProxyPreferences proxyPreferences) {
ProxyRegisterer.register(proxyPreferences);
if (proxyPreferences.shouldUseProxy() && proxyPreferences.shouldUseAuthentication()) {
Authenticator.setDefault(new ProxyAuthenticator());
}
}

private static void configureSSL(SSLPreferences sslPreferences) {
TrustStoreManager.createTruststoreFileIfNotExist(Path.of(sslPreferences.getTruststorePath()));
}

private static void clearOldSearchIndices() {
Path currentIndexPath = Directories.getFulltextIndexBaseDirectory();
Path appData = currentIndexPath.getParent();

try {
Files.createDirectories(currentIndexPath);
} catch (IOException e) {
LOGGER.error("Could not create index directory {}", appData, e);
}

try (DirectoryStream<Path> stream = Files.newDirectoryStream(appData)) {
for (Path path : stream) {
if (Files.isDirectory(path) && !path.toString().endsWith("ssl") && path.toString().contains("lucene")
&& !path.equals(currentIndexPath)) {
LOGGER.info("Deleting out-of-date fulltext search index at {}.", path);
Files.walk(path)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
} catch (IOException e) {
LOGGER.error("Could not access app-directory at {}", appData, e);
}
JabRefGUI.setup(uiCommands, preferences, fileUpdateMonitor);
JabRefGUI.launch(JabRefGUI.class, args);
}
}
Loading
Loading