From 7c1df88f789051e85c156e161647ca60f36e604c Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 9 Apr 2023 00:28:47 +0200
Subject: [PATCH 01/22] Introduces test http server
- Resources
- /libraries
- /libraries/{id} (both (embedded) BibTeX as string and CSL data)
- Documentation / code style
- Refined BibEntry class (JavaDoc, builder)
- Comments to InternalField
- move StyleTester to "test" module - package ...testutils/interactive...
- Makes use of Jersey, Grizzly
- Makes use of HK2 as dependency injection framework
- Introduces "application/x-bibtex-library-csl+json" mimetype
- Preparation for client/server sync (BibEntryDTO)
- Minor
- Made class "JabRefItemDataProvider" more visible
- Encoding of a .bib file can now be asked for externally
- Resorts modle-info.java
- Fixes typo in NetworkTabViewModel
- Installs SLF4J logging router: If a tool uses java commons logging, tinylog now also handles these logs
---
.gitattributes | 2 +
build.gradle | 20 ++
.../0027-http-return-bibtex-string.md | 53 +++++
src/main/java/module-info.java | 72 +++---
src/main/java/org/jabref/cli/Launcher.java | 21 +-
.../network/NetworkTabViewModel.java | 2 +-
src/main/java/org/jabref/http/MediaType.java | 6 +
.../java/org/jabref/http/dto/BibEntryDTO.java | 71 ++++++
.../java/org/jabref/http/dto/GsonFactory.java | 18 ++
.../org/jabref/http/server/Application.java | 32 +++
.../org/jabref/http/server/CORSFilter.java | 23 ++
.../jabref/http/server/LibrariesResource.java | 29 +++
.../jabref/http/server/LibraryResource.java | 101 +++++++++
.../org/jabref/http/server/RootResource.java | 22 ++
.../java/org/jabref/http/server/Server.java | 65 ++++++
.../logic/citationstyle/CSLAdapter.java | 177 ---------------
.../citationstyle/JabRefItemDataProvider.java | 212 ++++++++++++++++++
.../importer/fileformat/BibtexImporter.java | 53 +++--
.../importer/fileformat/BibtexParser.java | 1 -
.../jabref/logic/net/ssl/SSLCertificate.java | 3 +-
.../logic/net/ssl/TrustStoreManager.java | 2 +-
.../java/org/jabref/model/entry/BibEntry.java | 77 ++++++-
.../model/entry/SharedBibEntryData.java | 23 +-
.../model/entry/field/InternalField.java | 41 +++-
.../jabref/preferences/JabRefPreferences.java | 4 +
.../preferences/PreferenceServiceFactory.java | 14 ++
.../preferences/PreferencesService.java | 2 +
.../jabref/gui/edit/CopyMoreActionTest.java | 6 +-
.../http/server/LibrariesResourceTest.java | 33 +++
.../http/server/LibraryResourceTest.java | 38 ++++
.../org/jabref/http/server/ServerTest.java | 92 ++++++++
.../org/jabref/http/server/TestBibFile.java | 18 ++
.../org/jabref/http/server/TestServer.java | 14 ++
.../jabref/http/server/mwessl/GServer.java | 66 ++++++
.../http/server/mwessl/SimpleHttpHandler.java | 16 ++
.../http/server/mwessl/package-info.java | 8 +
.../JabRefItemDataProviderTest.java | 49 ++++
.../testutils/interactive/http/rest-api.http | 35 +++
.../interactive}/styletester/StyleTester.fxml | 2 +-
.../styletester/StyleTesterMain.java | 2 +-
.../styletester/StyleTesterView.java | 2 +-
.../http/server/general-server-test.bib | 7 +
42 files changed, 1284 insertions(+), 250 deletions(-)
create mode 100644 docs/decisions/0027-http-return-bibtex-string.md
create mode 100644 src/main/java/org/jabref/http/MediaType.java
create mode 100644 src/main/java/org/jabref/http/dto/BibEntryDTO.java
create mode 100644 src/main/java/org/jabref/http/dto/GsonFactory.java
create mode 100644 src/main/java/org/jabref/http/server/Application.java
create mode 100644 src/main/java/org/jabref/http/server/CORSFilter.java
create mode 100644 src/main/java/org/jabref/http/server/LibrariesResource.java
create mode 100644 src/main/java/org/jabref/http/server/LibraryResource.java
create mode 100644 src/main/java/org/jabref/http/server/RootResource.java
create mode 100644 src/main/java/org/jabref/http/server/Server.java
create mode 100644 src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
create mode 100644 src/main/java/org/jabref/preferences/PreferenceServiceFactory.java
create mode 100644 src/test/java/org/jabref/http/server/LibrariesResourceTest.java
create mode 100644 src/test/java/org/jabref/http/server/LibraryResourceTest.java
create mode 100644 src/test/java/org/jabref/http/server/ServerTest.java
create mode 100644 src/test/java/org/jabref/http/server/TestBibFile.java
create mode 100644 src/test/java/org/jabref/http/server/TestServer.java
create mode 100644 src/test/java/org/jabref/http/server/mwessl/GServer.java
create mode 100644 src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
create mode 100644 src/test/java/org/jabref/http/server/mwessl/package-info.java
create mode 100644 src/test/java/org/jabref/logic/citationstyle/JabRefItemDataProviderTest.java
create mode 100644 src/test/java/org/jabref/testutils/interactive/http/rest-api.http
rename src/{main/java/org/jabref => test/java/org/jabref/testutils/interactive}/styletester/StyleTester.fxml (99%)
rename src/{main/java/org/jabref => test/java/org/jabref/testutils/interactive}/styletester/StyleTesterMain.java (96%)
rename src/{main/java/org/jabref => test/java/org/jabref/testutils/interactive}/styletester/StyleTesterView.java (96%)
create mode 100644 src/test/resources/org/jabref/http/server/general-server-test.bib
diff --git a/.gitattributes b/.gitattributes
index 18bbb30fc6d..9a7729617c8 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,6 +12,8 @@ gradlew text eol=lf
# .bib files have to be written using OS specific line endings to enable our tests working
*.bib text !eol
+# Exception: The files used for the http server test - they should have linux line endings
+src/test/resources/org/jabref/http/server/*.bib text eol=lf
# Citavi needs to be LF line ending
# This overwrites the setting of "*.bib"
diff --git a/build.gradle b/build.gradle
index 9d8f1e7bff2..bbc02475859 100644
--- a/build.gradle
+++ b/build.gradle
@@ -179,6 +179,8 @@ dependencies {
implementation "org.tinylog:tinylog-api:2.6.1"
implementation "org.tinylog:slf4j-tinylog:2.6.1"
implementation "org.tinylog:tinylog-impl:2.6.1"
+ // route all requests to java.util.logging to SLF4J (which in turn routes to tinylog)
+ implementation 'org.slf4j:jul-to-slf4j:2.0.7'
implementation 'de.undercouch:citeproc-java:3.0.0-beta.2'
@@ -199,6 +201,24 @@ dependencies {
implementation group: 'net.harawata', name: 'appdirs', version: '1.2.1'
+ // JAX-RS implemented by Jersey
+ // API
+ implementation 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0'
+ // Implementation of the API
+ implementation 'org.glassfish.jersey.core:jersey-server:3.1.1'
+ // injection framework
+ implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.1'
+ implementation 'org.glassfish.hk2:hk2-api:2.6.1'
+ // testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4'
+ // implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4'
+ // testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4'
+ // HTTP server
+ // implementation 'org.glassfish.jersey.containers:jersey-container-netty-http:3.1.1'
+ implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.1'
+ testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.1'
+ // Allow objects "magically" to be mapped to JSON using GSON
+ // implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1'
+
testImplementation 'io.github.classgraph:classgraph:4.8.157'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.2'
diff --git a/docs/decisions/0027-http-return-bibtex-string.md b/docs/decisions/0027-http-return-bibtex-string.md
new file mode 100644
index 00000000000..29658c423a3
--- /dev/null
+++ b/docs/decisions/0027-http-return-bibtex-string.md
@@ -0,0 +1,53 @@
+---
+nav_order: 27
+parent: Decision Records
+---
+
+
+# Return BibTeX string and CSL Item JSON in the API
+
+## Context and Problem Statement
+
+In the context of an http server, when a http client `GETs` a JSON data structure containing BibTeX data, which format should that have?
+
+## Considered Options
+
+* Offer both, BibTeX string and CSL JSON
+* Return BibTeX as is as string
+* Convert BibTeX to JSON
+
+## Decision Outcome
+
+Chosen option: "Offer both, BibTeX string and CSL JSON", because there are many browser libraries out there being able to parse BibTeX. Thus, we don't need to convert it.
+
+## Pros and Cons of the Options
+
+### Offer both, BibTeX string and CSL JSON
+
+- Good, because this follows "Backend for Frontend"
+- Good, because Word Addin works seamless with the data provided (and does not need another dependency)
+- Good, because other clients can work with BibTeX data
+- Bad, because two serializations have to be kept
+
+### Return BibTeX as is as string
+
+- Good, because we don't need to think about any conversion
+- Bad, because it is unclear how to ship BibTeX data where the entry is dependent on
+- Bad, because client needs add additional parsing logic
+
+### Convert BibTeX to JSON
+
+More thought has to be done when converting to JSON.
+There seems to be a JSON format from [@citation-js/plugin-bibtex](https://www.npmjs.com/package/@citation-js/plugin-bibtex).
+We could do an additional self-made JSON format, but this increases the number of available JSON serializations for BibTeX.
+
+- Good, because it could flatten BibTeX data (example: `author = first # " and " # second`)
+- Bad, because conversion is difficult in BibTeX special cases. For instance, if Strings are used (example: `author = first # " and " # second`) and one doesn't want to flatten ("normalize") this.
+
+## More Information
+
+Existing JavaScript BibTeX libraries:
+
+* [bibtex-js](https://github.com/digitalheir/bibtex-js)
+* [bibtexParseJS](https://github.com/ORCID/bibtexParseJs)
+* [@citation-js/plugin-bibtex](https://www.npmjs.com/package/@citation-js/plugin-bibtex)
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 475aa85f61d..fd13ffaaf17 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -15,6 +15,9 @@
requires afterburner.fx;
requires com.jfoenix;
requires de.saxsys.mvvmfx;
+ requires reactfx;
+ requires de.saxsys.mvvmfx.validation;
+ requires org.fxmisc.flowless;
requires org.kordamp.ikonli.core;
requires org.kordamp.ikonli.javafx;
@@ -38,6 +41,7 @@
// Logging
requires org.slf4j;
+ requires jul.to.slf4j;
requires org.tinylog.api;
requires org.tinylog.api.slf4j;
requires org.tinylog.impl;
@@ -46,47 +50,58 @@
with org.jabref.gui.logging.GuiWriter,
org.jabref.gui.logging.ApplicationInsightsWriter;
- // Preferences and XML
requires java.prefs;
+
+ // Annotations (@PostConstruct)
+ requires jakarta.annotation;
+ requires jakarta.inject;
+
+ // http server and client exchange
+ requires java.net.http;
+ requires jakarta.ws.rs;
+ requires grizzly.framework;
+
+ // data mapping
requires jakarta.xml.bind;
+ requires jdk.xml.dom;
+ requires com.google.gson;
+ requires com.fasterxml.jackson.databind;
+ requires com.fasterxml.jackson.dataformat.yaml;
+ requires com.fasterxml.jackson.datatype.jsr310;
// needs to be loaded here as it's otherwise not found at runtime
requires org.glassfish.jaxb.runtime;
- requires jdk.xml.dom;
- // Annotations (@PostConstruct)
- requires jakarta.annotation;
+ // dependency injection using HK2
+ requires org.glassfish.hk2.api;
// Microsoft application insights
requires applicationinsights.core;
requires applicationinsights.logging.log4j2;
- // Libre Office
- requires org.libreoffice.uno;
-
- // Other modules
- requires com.google.common;
- requires jakarta.inject;
- requires reactfx;
- requires commons.cli;
- requires com.github.tomtung.latex2unicode;
- requires fastparse;
- requires jbibtex;
- requires citeproc.java;
- requires de.saxsys.mvvmfx.validation;
- requires com.google.gson;
+ // http clients
requires unirest.java;
requires org.apache.httpcomponents.httpclient;
requires org.jsoup;
- requires org.apache.commons.csv;
- requires io.github.javadiffutils;
- requires java.string.similarity;
+
+ // SQL databases
requires ojdbc10;
requires org.postgresql.jdbc;
requires org.mariadb.jdbc;
uses org.mariadb.jdbc.credential.CredentialPlugin;
+
+ // Apache Commons and other (similar) helper libraries
+ requires commons.cli;
+ requires org.apache.commons.csv;
requires org.apache.commons.lang3;
- requires org.antlr.antlr4.runtime;
- requires org.fxmisc.flowless;
+ requires com.google.common;
+ requires io.github.javadiffutils;
+ requires java.string.similarity;
+
+ requires com.github.tomtung.latex2unicode;
+ requires fastparse;
+
+ requires jbibtex;
+ requires citeproc.java;
requires pdfbox;
requires xmpbox;
@@ -95,21 +110,16 @@
requires flexmark;
requires flexmark.util.ast;
requires flexmark.util.data;
- requires com.h2database.mvstore;
// fulltext search
requires org.apache.lucene.core;
// In case the version is updated, please also adapt SearchFieldConstants#VERSION to the newly used version
uses org.apache.lucene.codecs.lucene94.Lucene94Codec;
-
requires org.apache.lucene.queryparser;
uses org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
requires org.apache.lucene.analysis.common;
requires org.apache.lucene.highlighter;
- requires com.fasterxml.jackson.databind;
- requires com.fasterxml.jackson.dataformat.yaml;
- requires com.fasterxml.jackson.datatype.jsr310;
requires net.harawata.appdirs;
requires com.sun.jna;
requires com.sun.jna.platform;
@@ -117,4 +127,10 @@
requires org.eclipse.jgit;
uses org.eclipse.jgit.transport.SshSessionFactory;
uses org.eclipse.jgit.lib.GpgSigner;
+
+ // other libraries
+ requires com.h2database.mvstore;
+ requires org.antlr.antlr4.runtime;
+ requires org.libreoffice.uno;
+
}
diff --git a/src/main/java/org/jabref/cli/Launcher.java b/src/main/java/org/jabref/cli/Launcher.java
index bc9dd85d26e..0a87bfe5178 100644
--- a/src/main/java/org/jabref/cli/Launcher.java
+++ b/src/main/java/org/jabref/cli/Launcher.java
@@ -33,6 +33,7 @@
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.slf4j.bridge.SLF4JBridgeHandler;
import org.tinylog.configuration.Configuration;
/**
@@ -47,6 +48,7 @@ public class Launcher {
private static String[] ARGUMENTS;
public static void main(String[] args) {
+ routeLoggingToSlf4J();
ARGUMENTS = args;
addLogToDisk();
try {
@@ -85,6 +87,11 @@ public static void main(String[] args) {
}
}
+ private static void routeLoggingToSlf4J() {
+ SLF4JBridgeHandler.removeHandlersForRootLogger();
+ SLF4JBridgeHandler.install();
+ }
+
/**
* This needs to be called as early as possible. After the first log write, it
* is not possible to alter
@@ -93,10 +100,10 @@ public static void main(String[] args) {
private static void addLogToDisk() {
Path directory = Path.of(AppDirsFactory.getInstance()
.getUserDataDir(
- OS.APP_DIR_APP_NAME,
- "logs",
- OS.APP_DIR_APP_AUTHOR))
- .resolve(new BuildInfo().version.toString());
+ OS.APP_DIR_APP_NAME,
+ "logs",
+ OS.APP_DIR_APP_AUTHOR))
+ .resolve(new BuildInfo().version.toString());
try {
Files.createDirectories(directory);
} catch (IOException e) {
@@ -187,9 +194,9 @@ private static void clearOldSearchIndices() {
&& !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);
+ .sorted(Comparator.reverseOrder())
+ .map(Path::toFile)
+ .forEach(File::delete);
}
}
} catch (IOException e) {
diff --git a/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java b/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
index 8af629de4cf..9e74c55722b 100644
--- a/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/network/NetworkTabViewModel.java
@@ -376,7 +376,7 @@ public void addCertificateFile() {
.build();
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(certPath -> SSLCertificate.fromPath(certPath).ifPresent(sslCertificate -> {
- if (!trustStoreManager.isCertificateExist(formatCustomAlias(sslCertificate.getSHA256Thumbprint()))) {
+ if (!trustStoreManager.certificateExists(formatCustomAlias(sslCertificate.getSHA256Thumbprint()))) {
customCertificateListProperty.add(CustomCertificateViewModel.fromSSLCertificate(sslCertificate)
.setPath(certPath.toAbsolutePath().toString()));
} else {
diff --git a/src/main/java/org/jabref/http/MediaType.java b/src/main/java/org/jabref/http/MediaType.java
new file mode 100644
index 00000000000..b37297ebd71
--- /dev/null
+++ b/src/main/java/org/jabref/http/MediaType.java
@@ -0,0 +1,6 @@
+package org.jabref.http;
+
+public class MediaType {
+ public static final String BIBTEX = "application/x-bibtex";
+ public static final String JSON_CSL_ITEM = "application/x-bibtex-library-csl+json";
+}
diff --git a/src/main/java/org/jabref/http/dto/BibEntryDTO.java b/src/main/java/org/jabref/http/dto/BibEntryDTO.java
new file mode 100644
index 00000000000..a0d20d4353e
--- /dev/null
+++ b/src/main/java/org/jabref/http/dto/BibEntryDTO.java
@@ -0,0 +1,71 @@
+package org.jabref.http.dto;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.jabref.logic.bibtex.BibEntryWriter;
+import org.jabref.logic.bibtex.FieldWriter;
+import org.jabref.logic.bibtex.FieldWriterPreferences;
+import org.jabref.logic.exporter.BibWriter;
+import org.jabref.model.database.BibDatabaseMode;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.entry.SharedBibEntryData;
+
+import com.google.common.base.MoreObjects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The data transfer object (DTO) for an BibEntry
+ *
+ * @param sharingMetadata the data used for sharing
+ * @param userComments the comments before the BibTeX entry
+ * @param citationKey the citation key (duplicated from BibEntry to ease processing by the client)
+ * @param bibtex the BibEntry as BibTeX string (see ADR-0027 for more information, why we don't use a HashMap / JSON)
+ */
+public record BibEntryDTO(SharedBibEntryData sharingMetadata, String userComments, String citationKey, String bibtex) implements Comparable {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(BibEntryDTO.class);
+
+ public BibEntryDTO(BibEntry bibEntry, BibDatabaseMode bibDatabaseMode, FieldWriterPreferences fieldWriterPreferences, BibEntryTypesManager bibEntryTypesManager) {
+ this(bibEntry.getSharedBibEntryData(),
+ bibEntry.getUserComments(),
+ bibEntry.getCitationKey().orElse(""),
+ convertToString(bibEntry, bibDatabaseMode, fieldWriterPreferences, bibEntryTypesManager)
+ );
+ }
+
+ private static String convertToString(BibEntry entry, BibDatabaseMode bibDatabaseMode, FieldWriterPreferences fieldWriterPreferences, BibEntryTypesManager bibEntryTypesManager) {
+ StringWriter rawEntry = new StringWriter();
+ BibWriter bibWriter = new BibWriter(rawEntry, "\n");
+ BibEntryWriter bibtexEntryWriter = new BibEntryWriter(new FieldWriter(fieldWriterPreferences), bibEntryTypesManager);
+ try {
+ bibtexEntryWriter.write(entry, bibWriter, bibDatabaseMode);
+ } catch (IOException e) {
+ LOGGER.warn("Problem creating BibTeX entry.", e);
+ return "error";
+ }
+ return rawEntry.toString();
+ }
+
+ @Override
+ public int compareTo(BibEntryDTO o) {
+ int sharingComparison = sharingMetadata.compareTo(o.sharingMetadata);
+ if (sharingComparison != 0) {
+ return sharingComparison;
+ }
+ LOGGER.error("Comparing equal DTOs");
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("sharingMetadata", sharingMetadata)
+ .add("userComments", userComments)
+ .add("citationkey", citationKey)
+ .add("bibtex", bibtex)
+ .toString();
+ }
+}
diff --git a/src/main/java/org/jabref/http/dto/GsonFactory.java b/src/main/java/org/jabref/http/dto/GsonFactory.java
new file mode 100644
index 00000000000..77da67f9ba4
--- /dev/null
+++ b/src/main/java/org/jabref/http/dto/GsonFactory.java
@@ -0,0 +1,18 @@
+package org.jabref.http.dto;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.glassfish.hk2.api.Factory;
+
+public class GsonFactory implements Factory {
+ @Override
+ public Gson provide() {
+ return new GsonBuilder()
+ .setPrettyPrinting()
+ .create();
+ }
+
+ @Override
+ public void dispose(Gson instance) {
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/Application.java b/src/main/java/org/jabref/http/server/Application.java
new file mode 100644
index 00000000000..00567335edb
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/Application.java
@@ -0,0 +1,32 @@
+package org.jabref.http.server;
+
+import java.util.Set;
+
+import org.jabref.http.dto.GsonFactory;
+import org.jabref.preferences.PreferenceServiceFactory;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.ApplicationPath;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
+
+@ApplicationPath("/")
+public class Application extends jakarta.ws.rs.core.Application {
+
+ @Inject
+ ServiceLocator serviceLocator;
+
+ @Override
+ public Set> getClasses() {
+ initialize();
+ return Set.of(RootResource.class, LibrariesResource.class, LibraryResource.class, CORSFilter.class);
+ }
+
+ /**
+ * Separate initialization method, because @Inject does not support injection at the constructor
+ */
+ private void initialize() {
+ ServiceLocatorUtilities.addFactoryConstants(serviceLocator, new GsonFactory());
+ ServiceLocatorUtilities.addFactoryConstants(serviceLocator, new PreferenceServiceFactory());
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/CORSFilter.java b/src/main/java/org/jabref/http/server/CORSFilter.java
new file mode 100644
index 00000000000..7489305808f
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/CORSFilter.java
@@ -0,0 +1,23 @@
+package org.jabref.http.server;
+
+import jakarta.ws.rs.container.ContainerRequestContext;
+import jakarta.ws.rs.container.ContainerResponseContext;
+import jakarta.ws.rs.container.ContainerResponseFilter;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+public class CORSFilter implements ContainerResponseFilter {
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
+ String requestOrigin = requestContext.getHeaderString("Origin");
+ if (requestOrigin == null) {
+ // IntelliJ's rest client is calling
+ responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
+ } else if (requestOrigin.contains("://localhost")) {
+ responseContext.getHeaders().add("Access-Control-Allow-Origin", requestOrigin);
+ }
+ responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ responseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept");
+ responseContext.getHeaders().add("Access-Control-Allow-Credentials", "false");
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/LibrariesResource.java b/src/main/java/org/jabref/http/server/LibrariesResource.java
new file mode 100644
index 00000000000..a5ea28c0ee1
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/LibrariesResource.java
@@ -0,0 +1,29 @@
+package org.jabref.http.server;
+
+import java.util.List;
+
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.preferences.PreferencesService;
+
+import com.google.gson.Gson;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("libraries")
+public class LibrariesResource {
+ @Inject
+ PreferencesService preferences;
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public String get() {
+ List fileNamesWithUniqueSuffix = preferences.getGuiPreferences().getLastFilesOpened().stream()
+ .map(java.nio.file.Path::of)
+ .map(p -> p.getFileName() + "-" + BackupFileUtil.getUniqueFilePrefix(p))
+ .toList();
+ return new Gson().toJson(fileNamesWithUniqueSuffix);
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/LibraryResource.java b/src/main/java/org/jabref/http/server/LibraryResource.java
new file mode 100644
index 00000000000..9e10afa7fa1
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/LibraryResource.java
@@ -0,0 +1,101 @@
+package org.jabref.http.server;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.Objects;
+
+import org.jabref.gui.Globals;
+import org.jabref.http.dto.BibEntryDTO;
+import org.jabref.logic.citationstyle.JabRefItemDataProvider;
+import org.jabref.logic.importer.ParserResult;
+import org.jabref.logic.importer.fileformat.BibtexImporter;
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.util.DummyFileUpdateMonitor;
+import org.jabref.preferences.PreferencesService;
+
+import com.google.gson.Gson;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.InternalServerErrorException;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Path("libraries/{id}")
+public class LibraryResource {
+ public static final Logger LOGGER = LoggerFactory.getLogger(LibraryResource.class);
+
+ @Inject
+ PreferencesService preferences;
+
+ @Inject
+ Gson gson;
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getJson(@PathParam("id") String id) {
+ ParserResult parserResult = getParserResult(id);
+ List list = parserResult.getDatabase().getEntries().stream()
+ .map(bibEntry -> {
+ bibEntry.getSharedBibEntryData().setSharedID(Objects.hash(bibEntry));
+ return bibEntry;
+ })
+ .map(entry -> new BibEntryDTO(entry, parserResult.getDatabaseContext().getMode(), preferences.getFieldWriterPreferences(), Globals.entryTypesManager))
+ .toList();
+ return gson.toJson(list);
+ }
+
+ @GET
+ @Produces(org.jabref.http.MediaType.JSON_CSL_ITEM)
+ public String getClsItemJson(@PathParam("id") String id) {
+ ParserResult parserResult = getParserResult(id);
+ JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
+ jabRefItemDataProvider.setData(parserResult.getDatabaseContext(), new BibEntryTypesManager());
+ return jabRefItemDataProvider.toJson();
+ }
+
+ private ParserResult getParserResult(String id) {
+ java.nio.file.Path library = getLibraryPath(id);
+ ParserResult parserResult;
+ try {
+ parserResult = new BibtexImporter(preferences.getImportFormatPreferences(), new DummyFileUpdateMonitor()).importDatabase(library);
+ } catch (IOException e) {
+ LOGGER.warn("Could not find open library file {}", library, e);
+ throw new InternalServerErrorException("Could not parse library", e);
+ }
+ return parserResult;
+ }
+
+ @GET
+ @Produces(org.jabref.http.MediaType.BIBTEX)
+ public Response getBibtex(@PathParam("id") String id) {
+ java.nio.file.Path library = getLibraryPath(id);
+ String libraryAsString;
+ try {
+ libraryAsString = Files.readString(library);
+ } catch (IOException e) {
+ LOGGER.error("Could not read library {}", library, e);
+ throw new InternalServerErrorException("Could not read library " + library, e);
+ }
+ return Response.ok()
+ .entity(libraryAsString)
+ .build();
+ }
+
+ private java.nio.file.Path getLibraryPath(String id) {
+ java.nio.file.Path library = preferences.getGuiPreferences().getLastFilesOpened()
+ .stream()
+ .map(java.nio.file.Path::of)
+ .filter(p -> (p.getFileName() + "-" + BackupFileUtil.getUniqueFilePrefix(p)).equals(id))
+ .findAny()
+ .orElseThrow(() -> new NotFoundException());
+ return library;
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/RootResource.java b/src/main/java/org/jabref/http/server/RootResource.java
new file mode 100644
index 00000000000..c55f2583db3
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/RootResource.java
@@ -0,0 +1,22 @@
+package org.jabref.http.server;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/")
+public class RootResource {
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public String get() {
+ return """
+
+
+
+ JabRef http API runs. Please navigate to libraries.
+
+
+""";
+ }
+}
diff --git a/src/main/java/org/jabref/http/server/Server.java b/src/main/java/org/jabref/http/server/Server.java
new file mode 100644
index 00000000000..92ed926ba6e
--- /dev/null
+++ b/src/main/java/org/jabref/http/server/Server.java
@@ -0,0 +1,65 @@
+package org.jabref.http.server;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.concurrent.CountDownLatch;
+
+import javax.net.ssl.SSLContext;
+
+import org.jabref.logic.util.OS;
+
+import jakarta.ws.rs.SeBootstrap;
+import net.harawata.appdirs.AppDirsFactory;
+import org.glassfish.grizzly.ssl.SSLContextConfigurator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Server {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
+
+ private static SeBootstrap.Instance serverInstance;
+
+ static void startServer(CountDownLatch latch) {
+ SSLContext sslContext = getSslContext();
+ SeBootstrap.Configuration configuration = SeBootstrap.Configuration
+ .builder()
+ .sslContext(sslContext)
+ .protocol("HTTPS")
+ .port(6051)
+ .build();
+ SeBootstrap.start(Application.class, configuration).thenAccept(instance -> {
+ instance.stopOnShutdown(stopResult ->
+ System.out.printf("Stop result: %s [Native stop result: %s].%n", stopResult,
+ stopResult.unwrap(Object.class)));
+ final URI uri = instance.configuration().baseUri();
+ System.out.printf("Instance %s running at %s [Native handle: %s].%n", instance, uri,
+ instance.unwrap(Object.class));
+ System.out.println("Send SIGKILL to shutdown.");
+ serverInstance = instance;
+ latch.countDown();
+ });
+ }
+
+ private static SSLContext getSslContext() {
+ SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
+
+ // "server.jks" Needs to be generated using following command inside that directory:
+ // keytool -genkey -keyalg RSA -alias selfsigned -keystore server.jks -storepass changeit -validity 365 -keysize 2048 -dname "CN=localhost, OU=YourOrganizationUnit, O=YourOrganization, L=YourCity, S=YourState, C=YourCountry"
+
+ String keystorePath = Path.of(AppDirsFactory.getInstance()
+ .getUserDataDir(
+ OS.APP_DIR_APP_NAME,
+ "ssl",
+ OS.APP_DIR_APP_AUTHOR))
+ .resolve("server.p12").toString();
+
+ sslContextConfig.setKeyStoreFile(keystorePath);
+ sslContextConfig.setKeyStorePass("changeit");
+
+ return sslContextConfig.createSSLContext();
+ }
+
+ static void stopServer() {
+ serverInstance.stop();
+ }
+}
diff --git a/src/main/java/org/jabref/logic/citationstyle/CSLAdapter.java b/src/main/java/org/jabref/logic/citationstyle/CSLAdapter.java
index 6bdd2149b52..215c9a14719 100644
--- a/src/main/java/org/jabref/logic/citationstyle/CSLAdapter.java
+++ b/src/main/java/org/jabref/logic/citationstyle/CSLAdapter.java
@@ -1,37 +1,17 @@
package org.jabref.logic.citationstyle;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import org.jabref.logic.formatter.bibtexfields.RemoveNewlinesFormatter;
-import org.jabref.logic.integrity.PagesChecker;
import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.BibEntryType;
import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.entry.Month;
-import org.jabref.model.entry.field.Field;
-import org.jabref.model.entry.field.StandardField;
-import org.jabref.model.entry.types.StandardEntryType;
-import org.jabref.model.strings.LatexToUnicodeAdapter;
import de.undercouch.citeproc.CSL;
import de.undercouch.citeproc.DefaultAbbreviationProvider;
-import de.undercouch.citeproc.ItemDataProvider;
-import de.undercouch.citeproc.bibtex.BibTeXConverter;
-import de.undercouch.citeproc.csl.CSLItemData;
import de.undercouch.citeproc.output.Bibliography;
-import org.jbibtex.BibTeXEntry;
-import org.jbibtex.DigitStringValue;
-import org.jbibtex.Key;
/**
* Provides an adapter class to CSL. It holds a CSL instance under the hood that is only recreated when
@@ -49,7 +29,6 @@
*/
public class CSLAdapter {
- private static final BibTeXConverter BIBTEX_CONVERTER = new BibTeXConverter();
private final JabRefItemDataProvider dataProvider = new JabRefItemDataProvider();
private String style;
private CitationStyleOutputFormat format;
@@ -90,160 +69,4 @@ private void initialize(String newStyle, CitationStyleOutputFormat newFormat) th
format = newFormat;
}
}
-
- /**
- * Custom ItemDataProvider that allows to set the data so that we don't have to instantiate a new CSL object
- * every time.
- */
- private static class JabRefItemDataProvider implements ItemDataProvider {
-
- private final List data = new ArrayList<>();
- private BibDatabaseContext bibDatabaseContext;
- private BibEntryTypesManager entryTypesManager;
- private PagesChecker pagesChecker;
-
- /**
- * Converts the {@link BibEntry} into {@link CSLItemData}.
- *
- *
- *
- *
- *
- *BibTeX |
- *BibLaTeX |
- *EntryPreview/CSL |
- *proposed logic, conditions and info |
- *
- *
- *
- *
- *volume |
- *volume |
- *volume |
- * |
- *
- *
- *number |
- *issue |
- *issue |
- *For conversion to CSL or BibTeX: BibLaTeX number takes priority and supersedes BibLaTeX issue |
- *
- *
- *number |
- *number |
- *issue |
- *same as above |
- *
- *
- *pages |
- *eid |
- *number |
- *Some journals put the article-number (= eid) into the pages field. If BibLaTeX eid exists, provide csl number to the style. If pages exists, provide csl page . If eid WITHIN the pages field exists, detect the eid and provide csl number . If both eid and pages exists, ideally provide both csl number and csl page . Ideally the citationstyle should be able to flexibly choose the rendering. |
- *
- *
- *pages |
- *pages |
- *page |
- *same as above |
- *
- *
- *
- */
- private CSLItemData bibEntryToCSLItemData(BibEntry originalBibEntry, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
- // We need to make a deep copy, because we modify the entry according to the logic presented at
- // https://github.com/JabRef/jabref/issues/8372#issuecomment-1014941935
- BibEntry bibEntry = (BibEntry) originalBibEntry.clone();
- String citeKey = bibEntry.getCitationKey().orElse("");
- BibTeXEntry bibTeXEntry = new BibTeXEntry(new Key(bibEntry.getType().getName()), new Key(citeKey));
-
- // Not every field is already generated into latex free fields
- RemoveNewlinesFormatter removeNewlinesFormatter = new RemoveNewlinesFormatter();
-
- Optional entryType = entryTypesManager.enrich(bibEntry.getType(), bibDatabaseContext.getMode());
-
- if (bibEntry.getType().equals(StandardEntryType.Article)) {
- // Patch bibEntry to contain the right BibTeX (not BibLaTeX) fields
- // Note that we do not need to convert from "pages" to "page", because CiteProc already handles it
- // See BibTeXConverter
- if (bibDatabaseContext.isBiblatexMode()) {
- // Map "number" to CSL "issue", unless no number exists
- Optional numberField = bibEntry.getField(StandardField.NUMBER);
- numberField.ifPresent(number -> {
- bibEntry.setField(StandardField.ISSUE, number);
- bibEntry.clearField(StandardField.NUMBER);
- }
- );
-
- bibEntry.getField(StandardField.EID).ifPresent(eid -> {
- if (!bibEntry.hasField(StandardField.NUMBER)) {
- bibEntry.setField(StandardField.NUMBER, eid);
- bibEntry.clearField(StandardField.EID);
- }
- });
- } else {
- // BibTeX mode
- bibEntry.getField(StandardField.NUMBER).ifPresent(number -> {
- bibEntry.setField(StandardField.ISSUE, number);
- bibEntry.clearField(StandardField.NUMBER);
- });
- bibEntry.getField(StandardField.PAGES).ifPresent(pages -> {
- if (pages.toLowerCase(Locale.ROOT).startsWith("article ")) {
- pages = pages.substring("Article ".length());
- bibEntry.setField(StandardField.NUMBER, pages);
- }
- });
- bibEntry.getField(StandardField.EID).ifPresent(eid -> {
- if (!bibEntry.hasField(StandardField.PAGES)) {
- bibEntry.setField(StandardField.PAGES, eid);
- bibEntry.clearField(StandardField.EID);
- }
- });
- }
- }
-
- Set fields = entryType.map(BibEntryType::getAllFields).orElse(bibEntry.getFields());
- fields.addAll(bibEntry.getFields());
- for (Field key : fields) {
- bibEntry.getResolvedFieldOrAlias(key, bibDatabaseContext.getDatabase())
- .map(removeNewlinesFormatter::format)
- .map(LatexToUnicodeAdapter::format)
- .ifPresent(value -> {
- if (StandardField.MONTH.equals(key)) {
- // Change month from #mon# to mon because CSL does not support the former format
- value = bibEntry.getMonth().map(Month::getShortName).orElse(value);
- }
- bibTeXEntry.addField(new Key(key.getName()), new DigitStringValue(value));
- });
- }
- return BIBTEX_CONVERTER.toItemData(bibTeXEntry);
- }
-
- public void setData(List data, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
- this.data.clear();
- this.data.addAll(data);
- this.bibDatabaseContext = bibDatabaseContext;
- this.entryTypesManager = entryTypesManager;
-
- // Quick solution to always use BibLaTeX mode at the checker to allow pages ranges with single dash, too
- // Example: pages = {1-2}
- BibDatabaseContext ctx = new BibDatabaseContext();
- ctx.setMode(BibDatabaseMode.BIBLATEX);
- this.pagesChecker = new PagesChecker(ctx);
- }
-
- @Override
- public CSLItemData retrieveItem(String id) {
- return data.stream()
- .filter(entry -> entry.getCitationKey().orElse("").equals(id))
- .map(entry -> bibEntryToCSLItemData(entry, bibDatabaseContext, entryTypesManager))
- .findFirst().orElse(null);
- }
-
- @Override
- public Collection getIds() {
- return data.stream()
- .map(entry -> entry.getCitationKey().orElse(""))
- .toList();
- }
- }
}
diff --git a/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
new file mode 100644
index 00000000000..32c3d95ef6e
--- /dev/null
+++ b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
@@ -0,0 +1,212 @@
+package org.jabref.logic.citationstyle;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.jabref.logic.formatter.bibtexfields.RemoveNewlinesFormatter;
+import org.jabref.logic.integrity.PagesChecker;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.database.BibDatabaseMode;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.BibEntryType;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.entry.Month;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.StandardField;
+import org.jabref.model.entry.types.StandardEntryType;
+import org.jabref.model.strings.LatexToUnicodeAdapter;
+
+import de.undercouch.citeproc.ItemDataProvider;
+import de.undercouch.citeproc.bibtex.BibTeXConverter;
+import de.undercouch.citeproc.csl.CSLItemData;
+import de.undercouch.citeproc.helper.json.StringJsonBuilderFactory;
+import org.jbibtex.BibTeXEntry;
+import org.jbibtex.DigitStringValue;
+import org.jbibtex.Key;
+
+/**
+ * Custom {@link ItemDataProvider} that allows to set the data so that we don't have to instantiate a new CSL object
+ * every time.
+ */
+public class JabRefItemDataProvider implements ItemDataProvider {
+
+ private static final BibTeXConverter BIBTEX_CONVERTER = new BibTeXConverter();
+
+ private final StringJsonBuilderFactory stringJsonBuilderFactory;
+
+ private final List data = new ArrayList<>();
+
+ private BibDatabaseContext bibDatabaseContext;
+ private BibEntryTypesManager entryTypesManager;
+ private PagesChecker pagesChecker;
+
+ public JabRefItemDataProvider() {
+ stringJsonBuilderFactory = new StringJsonBuilderFactory();
+ }
+
+ /**
+ * Converts the {@link BibEntry} into {@link CSLItemData}.
+ *
+ *
+ *
+ *
+ *
+ * BibTeX |
+ * BibLaTeX |
+ * EntryPreview/CSL |
+ * proposed logic, conditions and info |
+ *
+ *
+ *
+ *
+ * volume |
+ * volume |
+ * volume |
+ * |
+ *
+ *
+ * number |
+ * issue |
+ * issue |
+ * For conversion to CSL or BibTeX: BibLaTeX number takes priority and supersedes BibLaTeX issue |
+ *
+ *
+ * number |
+ * number |
+ * issue |
+ * same as above |
+ *
+ *
+ * pages |
+ * eid |
+ * number |
+ * Some journals put the article-number (= eid) into the pages field. If BibLaTeX eid exists, provide csl number to the style. If pages exists, provide csl page . If eid WITHIN the pages field exists, detect the eid and provide csl number . If both eid and pages exists, ideally provide both csl number and csl page . Ideally the citationstyle should be able to flexibly choose the rendering. |
+ *
+ *
+ * pages |
+ * pages |
+ * page |
+ * same as above |
+ *
+ *
+ *
+ */
+ private CSLItemData bibEntryToCSLItemData(BibEntry originalBibEntry, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
+ // We need to make a deep copy, because we modify the entry according to the logic presented at
+ // https://github.com/JabRef/jabref/issues/8372#issuecomment-1014941935
+ BibEntry bibEntry = (BibEntry) originalBibEntry.clone();
+ String citeKey = bibEntry.getCitationKey().orElse("");
+ BibTeXEntry bibTeXEntry = new BibTeXEntry(new Key(bibEntry.getType().getName()), new Key(citeKey));
+
+ // Not every field is already generated into latex free fields
+ RemoveNewlinesFormatter removeNewlinesFormatter = new RemoveNewlinesFormatter();
+
+ Optional entryType = entryTypesManager.enrich(bibEntry.getType(), bibDatabaseContext.getMode());
+
+ if (bibEntry.getType().equals(StandardEntryType.Article)) {
+ // Patch bibEntry to contain the right BibTeX (not BibLaTeX) fields
+ // Note that we do not need to convert from "pages" to "page", because CiteProc already handles it
+ // See BibTeXConverter
+ if (bibDatabaseContext.isBiblatexMode()) {
+ // Map "number" to CSL "issue", unless no number exists
+ Optional numberField = bibEntry.getField(StandardField.NUMBER);
+ numberField.ifPresent(number -> {
+ bibEntry.setField(StandardField.ISSUE, number);
+ bibEntry.clearField(StandardField.NUMBER);
+ }
+ );
+
+ bibEntry.getField(StandardField.EID).ifPresent(eid -> {
+ if (!bibEntry.hasField(StandardField.NUMBER)) {
+ bibEntry.setField(StandardField.NUMBER, eid);
+ bibEntry.clearField(StandardField.EID);
+ }
+ });
+ } else {
+ // BibTeX mode
+ bibEntry.getField(StandardField.NUMBER).ifPresent(number -> {
+ bibEntry.setField(StandardField.ISSUE, number);
+ bibEntry.clearField(StandardField.NUMBER);
+ });
+ bibEntry.getField(StandardField.PAGES).ifPresent(pages -> {
+ if (pages.toLowerCase(Locale.ROOT).startsWith("article ")) {
+ pages = pages.substring("Article ".length());
+ bibEntry.setField(StandardField.NUMBER, pages);
+ }
+ });
+ bibEntry.getField(StandardField.EID).ifPresent(eid -> {
+ if (!bibEntry.hasField(StandardField.PAGES)) {
+ bibEntry.setField(StandardField.PAGES, eid);
+ bibEntry.clearField(StandardField.EID);
+ }
+ });
+ }
+ }
+
+ Set fields = entryType.map(BibEntryType::getAllFields).orElse(bibEntry.getFields());
+ fields.addAll(bibEntry.getFields());
+ for (Field key : fields) {
+ bibEntry.getResolvedFieldOrAlias(key, bibDatabaseContext.getDatabase())
+ .map(removeNewlinesFormatter::format)
+ .map(LatexToUnicodeAdapter::format)
+ .ifPresent(value -> {
+ if (StandardField.MONTH.equals(key)) {
+ // Change month from #mon# to mon because CSL does not support the former format
+ value = bibEntry.getMonth().map(Month::getShortName).orElse(value);
+ }
+ bibTeXEntry.addField(new Key(key.getName()), new DigitStringValue(value));
+ });
+ }
+ return BIBTEX_CONVERTER.toItemData(bibTeXEntry);
+ }
+
+ /**
+ * Fills the data with all entries in given bibDatabaseContext
+ */
+ public void setData(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
+ this.setData(bibDatabaseContext.getEntries(), bibDatabaseContext, entryTypesManager);
+ }
+
+ public void setData(List data, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
+ this.data.clear();
+ this.data.addAll(data);
+ this.bibDatabaseContext = bibDatabaseContext;
+ this.entryTypesManager = entryTypesManager;
+
+ // Quick solution to always use BibLaTeX mode at the checker to allow pages ranges with single dash, too
+ // Example: pages = {1-2}
+ BibDatabaseContext ctx = new BibDatabaseContext();
+ ctx.setMode(BibDatabaseMode.BIBLATEX);
+ this.pagesChecker = new PagesChecker(ctx);
+ }
+
+ public String toJson() {
+ List entries = bibDatabaseContext.getEntries();
+ this.setData(entries, bibDatabaseContext, entryTypesManager);
+ return entries.stream()
+ .map(entry -> bibEntryToCSLItemData(entry, bibDatabaseContext, entryTypesManager))
+ .map(item -> item.toJson(stringJsonBuilderFactory.createJsonBuilder()))
+ .map(item -> (String) item)
+ .collect(Collectors.joining(",", "[", "]"));
+ }
+
+ @Override
+ public CSLItemData retrieveItem(String id) {
+ return data.stream()
+ .filter(entry -> entry.getCitationKey().orElse("").equals(id))
+ .map(entry -> bibEntryToCSLItemData(entry, bibDatabaseContext, entryTypesManager))
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public Collection getIds() {
+ return data.stream()
+ .map(entry -> entry.getCitationKey().orElse(""))
+ .toList();
+ }
+}
diff --git a/src/main/java/org/jabref/logic/importer/fileformat/BibtexImporter.java b/src/main/java/org/jabref/logic/importer/fileformat/BibtexImporter.java
index 1b67e2df572..500a14a69c9 100644
--- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexImporter.java
+++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexImporter.java
@@ -54,6 +54,36 @@ public boolean isRecognizedFormat(BufferedReader reader) {
@Override
public ParserResult importDatabase(Path filePath) throws IOException {
+ EncodingResult result = getEncodingResult(filePath);
+
+ // We replace unreadable characters
+ // Unfortunately, no warning will be issued to the user
+ // As this is a very seldom case, we accept that
+ CharsetDecoder decoder = result.encoding().newDecoder();
+ decoder.onMalformedInput(CodingErrorAction.REPLACE);
+
+ try (InputStreamReader inputStreamReader = new InputStreamReader(Files.newInputStream(filePath), decoder);
+ BufferedReader reader = new BufferedReader(inputStreamReader)) {
+ ParserResult parserResult = this.importDatabase(reader);
+ parserResult.getMetaData().setEncoding(result.encoding());
+ parserResult.getMetaData().setEncodingExplicitlySupplied(result.encodingExplicitlySupplied());
+ parserResult.setPath(filePath);
+ if (parserResult.getMetaData().getMode().isEmpty()) {
+ parserResult.getMetaData().setMode(BibDatabaseModeDetection.inferMode(parserResult.getDatabase()));
+ }
+ return parserResult;
+ }
+ }
+
+ public static Charset getEncoding(Path filePath) throws IOException {
+ return getEncodingResult(filePath).encoding();
+ }
+
+ /**
+ * Determines the encoding of the supplied BibTeX file. If a JabRef encoding information is present, this information is used.
+ * If there is none present, {@link com.ibm.icu.text.CharsetDetector#CharsetDetector()} is used.
+ */
+ private static EncodingResult getEncodingResult(Path filePath) throws IOException {
// We want to check if there is a JabRef encoding heading in the file, because that would tell us
// which character encoding is used.
@@ -81,29 +111,16 @@ public ParserResult importDatabase(Path filePath) throws IOException {
encoding = suppliedEncoding.orElse(detectedCharset);
LOGGER.debug("Encoding used to read the file: {}", encoding);
}
+ EncodingResult result = new EncodingResult(encoding, encodingExplicitlySupplied);
+ return result;
+ }
- // We replace unreadable characters
- // Unfortunately, no warning will be issued to the user
- // As this is a very seldom case, we accept that
- CharsetDecoder decoder = encoding.newDecoder();
- decoder.onMalformedInput(CodingErrorAction.REPLACE);
-
- try (InputStreamReader inputStreamReader = new InputStreamReader(Files.newInputStream(filePath), decoder);
- BufferedReader reader = new BufferedReader(inputStreamReader)) {
- ParserResult parserResult = this.importDatabase(reader);
- parserResult.getMetaData().setEncoding(encoding);
- parserResult.getMetaData().setEncodingExplicitlySupplied(encodingExplicitlySupplied);
- parserResult.setPath(filePath);
- if (parserResult.getMetaData().getMode().isEmpty()) {
- parserResult.getMetaData().setMode(BibDatabaseModeDetection.inferMode(parserResult.getDatabase()));
- }
- return parserResult;
- }
+ private record EncodingResult(Charset encoding, boolean encodingExplicitlySupplied) {
}
/**
* This method does not set the metadata encoding information. The caller needs to set the encoding of the supplied
- * reader manually to the meta data
+ * reader manually to the metadata
*/
@Override
public ParserResult importDatabase(BufferedReader reader) throws IOException {
diff --git a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
index 5d24472dba0..3be461ab02c 100644
--- a/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
+++ b/src/main/java/org/jabref/logic/importer/fileformat/BibtexParser.java
@@ -226,7 +226,6 @@ private ParserResult parseFileContent() throws IOException {
skipWhitespace();
}
- // Instantiate meta data
try {
parserResult.setMetaData(metaDataParser.parse(meta, importFormatPreferences.bibEntryPreferences().getKeywordSeparator()));
} catch (ParseException exception) {
diff --git a/src/main/java/org/jabref/logic/net/ssl/SSLCertificate.java b/src/main/java/org/jabref/logic/net/ssl/SSLCertificate.java
index deeca70f806..d2229b87850 100644
--- a/src/main/java/org/jabref/logic/net/ssl/SSLCertificate.java
+++ b/src/main/java/org/jabref/logic/net/ssl/SSLCertificate.java
@@ -63,8 +63,7 @@ public Integer getVersion() {
/**
* @return the SHA-256 of the DER encoding
- *
- * */
+ */
public String getSHA256Thumbprint() {
return sha256Thumbprint;
}
diff --git a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
index e5faac98bf2..70c0bed0659 100644
--- a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
+++ b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
@@ -61,7 +61,7 @@ public void deleteCertificate(String alias) {
}
}
- public boolean isCertificateExist(String alias) {
+ public boolean certificateExists(String alias) {
Objects.requireNonNull(alias);
try {
return store.isCertificateEntry(alias);
diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java
index 38e37c40db7..3ec0822ec3e 100644
--- a/src/main/java/org/jabref/model/entry/BibEntry.java
+++ b/src/main/java/org/jabref/model/entry/BibEntry.java
@@ -50,9 +50,45 @@
import org.slf4j.LoggerFactory;
/**
- * Represents a BibTex / BibLaTeX entry.
+ * Represents a Bib(La)TeX entry, which can be BibTeX or BibLaTeX.
+ *
+ * Example:
+ *
+ *
{@code
+ * Some commment
+ * @misc{key,
+ * fieldName = {fieldValue},
+ * otherFieldName = {otherVieldValue}
+ * }
+ * }
+ *
+ * Then,
+ *
+ * - "Some comment" is the comment before the entry,
+ * - "misc" is the entry type
+ * - "key" the citation key
+ * - "fieldName" and "otherFieldName" the fields of the BibEntry
+ *
+ *
+ *
+ * A BibTeX entry has following properties:
+ *
+ * - comments before entry
+ * - entry type
+ * - citation key
+ * - fields
+ *
+ * In JabRef, this is modeled the following way:
+ *
+ * - comments before entry --> {@link BibEntry#commentsBeforeEntry}
+ * - entry type --> {@link BibEntry#type}
+ * - citation key --> contained in {@link BibEntry#fields} using they hashmap key {@link InternalField#KEY_FIELD}
+ * - fields --> contained in {@link BibEntry#fields}
+ *
+ *
*
* In case you search for a builder as described in Item 2 of the book "Effective Java", you won't find one. Please use the methods {@link #withCitationKey(String)} and {@link #withField(Field, String)}.
+ *
*/
@AllowedToUseLogic("because it needs access to parser and writers")
public class BibEntry implements Cloneable {
@@ -814,6 +850,17 @@ public SharedBibEntryData getSharedBibEntryData() {
return sharedBibEntryData;
}
+ public BibEntry withSharedBibEntryData(int sharedId, int version) {
+ sharedBibEntryData.setSharedID(sharedId);
+ sharedBibEntryData.setVersion(version);
+ return this;
+ }
+
+ public BibEntry withSharedBibEntryData(SharedBibEntryData sharedBibEntryData) {
+ sharedBibEntryData = sharedBibEntryData;
+ return this;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -828,9 +875,20 @@ public boolean equals(Object o) {
&& Objects.equals(commentsBeforeEntry, entry.commentsBeforeEntry);
}
+ /**
+ * On purpose, this hashes the "content" of the BibEntry, not the {@link #sharedBibEntryData}.
+ *
+ * The content is
+ *
+ *
+ * - comments before entry
+ * - entry type
+ * - fields (including the citation key {@link InternalField#KEY_FIELD}
+ *
+ */
@Override
public int hashCode() {
- return Objects.hash(type.getValue(), fields);
+ return Objects.hash(commentsBeforeEntry, type.getValue(), fields);
}
public void registerListener(Object object) {
@@ -852,6 +910,15 @@ public BibEntry withField(Field field, String value) {
return this;
}
+ /**
+ * A copy is made of the parameter
+ */
+ public BibEntry withFields(Map content) {
+ this.fields = FXCollections.observableMap(new HashMap<>(content));
+ return this;
+ }
+
+
public BibEntry withDate(Date date) {
setDate(date);
this.setChanged(false);
@@ -871,6 +938,11 @@ public String getUserComments() {
return commentsBeforeEntry;
}
+ public BibEntry withUserComments(String commentsBeforeEntry) {
+ this.commentsBeforeEntry = commentsBeforeEntry;
+ return this;
+ }
+
public List getEntryLinkList(Field field, BibDatabase database) {
return getField(field).map(fieldValue -> EntryLinkList.parse(fieldValue, database))
.orElse(Collections.emptyList());
@@ -1086,4 +1158,5 @@ public void mergeWith(BibEntry other, Set otherPrioritizedFields) {
}
}
}
+
}
diff --git a/src/main/java/org/jabref/model/entry/SharedBibEntryData.java b/src/main/java/org/jabref/model/entry/SharedBibEntryData.java
index c323a9bc3fe..69c3c6bf976 100644
--- a/src/main/java/org/jabref/model/entry/SharedBibEntryData.java
+++ b/src/main/java/org/jabref/model/entry/SharedBibEntryData.java
@@ -1,16 +1,20 @@
package org.jabref.model.entry;
+import com.google.common.base.MoreObjects;
+
/**
* Stores all information needed to manage entries on a shared (SQL) database.
*/
-public class SharedBibEntryData {
+public class SharedBibEntryData implements Comparable {
// This id is set by the remote database system (DBS).
// It has to be unique on remote DBS for all connected JabRef instances.
// The old id above does not satisfy this requirement.
+ // This is "ID" in JabDrive sync
private int sharedID;
// Needed for version controlling if used on shared database
+ // This is "Revision" in JabDrive sync
private int version;
public SharedBibEntryData() {
@@ -33,4 +37,21 @@ public int getVersion() {
public void setVersion(int version) {
this.version = version;
}
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("sharedID", sharedID)
+ .add("version", version)
+ .toString();
+ }
+
+ @Override
+ public int compareTo(SharedBibEntryData o) {
+ if (this.sharedID == o.sharedID) {
+ return Integer.compare(this.version, o.version);
+ } else {
+ return Integer.compare(this.sharedID, o.sharedID);
+ }
+ }
}
diff --git a/src/main/java/org/jabref/model/entry/field/InternalField.java b/src/main/java/org/jabref/model/entry/field/InternalField.java
index 75b1573fcdb..d284af643c4 100644
--- a/src/main/java/org/jabref/model/entry/field/InternalField.java
+++ b/src/main/java/org/jabref/model/entry/field/InternalField.java
@@ -6,20 +6,49 @@
import java.util.Set;
/**
- * JabRef internal fields. These are not normal fields but mostly place holders with special functions.
+ * JabRef internal fields. These are not normal fields but mostly placeholders with special functions.
*/
public enum InternalField implements Field {
+ /**
+ * The BibTeX key (which is used at \cite{key} in LaTeX
+ */
KEY_FIELD("citationkey"),
+
/**
* field which indicates the entrytype
+ *
+ * Example: @misc{key}
*/
TYPE_HEADER("entrytype"),
+
+ /**
+ * Used in old layout files
+ */
OBSOLETE_TYPE_HEADER("bibtextype"),
- MARKED_INTERNAL("__markedentry"), // used in old versions of JabRef. Currently used for conversion only
- // all field names starting with "Jabref-internal-" are not appearing in .bib files
- BIBTEX_STRING("__string"), // marker that the content is just a BibTeX string
- INTERNAL_ALL_FIELD("all"), // virtual field to denote "all fields". Used in the meta data serialiization for save actions.
- INTERNAL_ALL_TEXT_FIELDS_FIELD("all-text-fields"), // virtual field to denote "all text fields". Used in the meta data serialiization for save actions.
+
+ /**
+ * used in old versions of JabRef. Currently used for conversion only
+ */
+ MARKED_INTERNAL("__markedentry"),
+
+ /**
+ * Marker that the content is just a BibTeX string
+ */
+ BIBTEX_STRING("__string"),
+
+ /**
+ * virtual field to denote "all fields". Used in the metadata serialization for save actions.
+ */
+ INTERNAL_ALL_FIELD("all"),
+
+ /**
+ * virtual field to denote "all text fields". Used in the metadata serialization for save actions.
+ */
+ INTERNAL_ALL_TEXT_FIELDS_FIELD("all-text-fields"),
+
+ /**
+ * all field names starting with "Jabref-internal-" are not appearing in .bib files
+ */
INTERNAL_ID_FIELD("JabRef-internal-id");
private final String name;
diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java
index 4b6f3c30fe9..ccf0d3fba60 100644
--- a/src/main/java/org/jabref/preferences/JabRefPreferences.java
+++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java
@@ -116,7 +116,9 @@
import org.jabref.model.strings.StringUtil;
import com.tobiasdiez.easybind.EasyBind;
+import jakarta.inject.Singleton;
import net.harawata.appdirs.AppDirsFactory;
+import org.jvnet.hk2.annotations.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -130,6 +132,8 @@
* There are still some similar preferences classes (OpenOfficePreferences and SharedDatabasePreferences) which also use
* the {@code java.util.prefs} API.
*/
+@Singleton
+@Service
public class JabRefPreferences implements PreferencesService {
// Push to application preferences
diff --git a/src/main/java/org/jabref/preferences/PreferenceServiceFactory.java b/src/main/java/org/jabref/preferences/PreferenceServiceFactory.java
new file mode 100644
index 00000000000..15d323c4caf
--- /dev/null
+++ b/src/main/java/org/jabref/preferences/PreferenceServiceFactory.java
@@ -0,0 +1,14 @@
+package org.jabref.preferences;
+
+import org.glassfish.hk2.api.Factory;
+
+public class PreferenceServiceFactory implements Factory {
+ @Override
+ public PreferencesService provide() {
+ return JabRefPreferences.getInstance();
+ }
+
+ @Override
+ public void dispose(PreferencesService instance) {
+ }
+}
diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java
index 5018e68d966..fba07fb2006 100644
--- a/src/main/java/org/jabref/preferences/PreferencesService.java
+++ b/src/main/java/org/jabref/preferences/PreferencesService.java
@@ -42,7 +42,9 @@
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.Field;
import org.jabref.model.metadata.SaveOrderConfig;
+import org.jvnet.hk2.annotations.Contract;
+@Contract
public interface PreferencesService {
InternalPreferences getInternalPreferences();
diff --git a/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java b/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java
index 594664f4f47..cd6db4c3716 100644
--- a/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java
+++ b/src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java
@@ -38,9 +38,9 @@ public class CopyMoreActionTest {
private PreferencesService preferencesService = mock(PreferencesService.class);
private StateManager stateManager = mock(StateManager.class);
private BibEntry entry;
- private List titles = new ArrayList();
- private List keys = new ArrayList();
- private List dois = new ArrayList();
+ private List titles = new ArrayList<>();
+ private List keys = new ArrayList<>();
+ private List dois = new ArrayList<>();
@BeforeEach
public void setUp() {
diff --git a/src/test/java/org/jabref/http/server/LibrariesResourceTest.java b/src/test/java/org/jabref/http/server/LibrariesResourceTest.java
new file mode 100644
index 00000000000..df7d7784bae
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/LibrariesResourceTest.java
@@ -0,0 +1,33 @@
+package org.jabref.http.server;
+
+import java.util.EnumSet;
+import java.util.stream.Collectors;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class LibrariesResourceTest extends ServerTest {
+
+ @Override
+ protected jakarta.ws.rs.core.Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig(LibrariesResource.class);
+ addPreferencesToResourceConfig(resourceConfig);
+ return resourceConfig.getApplication();
+ }
+
+ @Test
+ void defaultOneTestLibrary() throws Exception {
+ assertEquals("[\"" + TestBibFile.GENERAL_SERVER_TEST.id + "\"]", target("/libraries").request().get(String.class));
+ }
+
+ @Test
+ void twoTestLibraries() {
+ EnumSet availableLibraries = EnumSet.of(TestBibFile.GENERAL_SERVER_TEST, TestBibFile.JABREF_AUTHORS);
+ setAvailableLibraries(availableLibraries);
+ // We cannot use a string constant as the path changes from OS to OS. Therefore, we need to dynamically create the expected result.
+ String expected = availableLibraries.stream().map(file -> file.id).collect(Collectors.joining("\",\"", "[\"", "\"]"));
+ assertEquals(expected, target("/libraries").request().get(String.class));
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/LibraryResourceTest.java b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
new file mode 100644
index 00000000000..650999edab2
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
@@ -0,0 +1,38 @@
+package org.jabref.http.server;
+
+import org.jabref.http.MediaType;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class LibraryResourceTest extends ServerTest {
+
+ @Override
+ protected jakarta.ws.rs.core.Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig(LibraryResource.class, LibrariesResource.class);
+ addPreferencesToResourceConfig(resourceConfig);
+ addGsonToResourceConfig(resourceConfig);
+ return resourceConfig.getApplication();
+ }
+
+ @Test
+ void getJson() {
+ assertEquals("""
+ @Misc{Author2023test,
+ author = {Demo Author},
+ title = {Demo Title},
+ year = {2023},
+ }
+
+ @Comment{jabref-meta: databaseType:bibtex;}
+ """, target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(MediaType.BIBTEX).get(String.class));
+ }
+
+ @Test
+ void getClsItemJson() {
+ assertEquals("""
+ [{"id":"Author2023test","type":"article","author":[{"family":"Author","given":"Demo"}],"event-date":{"date-parts":[[2023]]},"issued":{"date-parts":[[2023]]},"title":"Demo Title"}]""", target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(MediaType.JSON_CSL_ITEM).get(String.class));
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/ServerTest.java b/src/test/java/org/jabref/http/server/ServerTest.java
new file mode 100644
index 00000000000..9395cc93171
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/ServerTest.java
@@ -0,0 +1,92 @@
+package org.jabref.http.server;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javafx.collections.FXCollections;
+
+import org.jabref.http.dto.GsonFactory;
+import org.jabref.logic.bibtex.FieldContentFormatterPreferences;
+import org.jabref.logic.bibtex.FieldWriterPreferences;
+import org.jabref.logic.importer.ImportFormatPreferences;
+import org.jabref.preferences.BibEntryPreferences;
+import org.jabref.preferences.GuiPreferences;
+import org.jabref.preferences.PreferencesService;
+
+import com.google.gson.Gson;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.slf4j.bridge.SLF4JBridgeHandler;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+abstract class ServerTest extends JerseyTest {
+
+ private static PreferencesService preferencesService;
+ private static GuiPreferences guiPreferences;
+
+ @BeforeAll
+ static void installLoggingBridge() {
+ // Grizzly uses java.commons.logging, but we use TinyLog
+ SLF4JBridgeHandler.removeHandlersForRootLogger();
+ SLF4JBridgeHandler.install();
+
+ initializePreferencesService();
+ }
+
+ protected void addGsonToResourceConfig(ResourceConfig resourceConfig) {
+ resourceConfig.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(new GsonFactory().provide()).to(Gson.class).ranked(2);
+ }
+ });
+ }
+
+ protected void addPreferencesToResourceConfig(ResourceConfig resourceConfig) {
+ resourceConfig.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(preferencesService).to(PreferencesService.class).ranked(2);
+ }
+ });
+ }
+
+ protected void setAvailableLibraries(EnumSet files) {
+ when(guiPreferences.getLastFilesOpened()).thenReturn(
+ FXCollections.observableArrayList(
+ files.stream()
+ .map(file -> file.path.toString())
+ .collect(Collectors.toList())));
+ }
+
+ private static void initializePreferencesService() {
+ preferencesService = mock(PreferencesService.class);
+
+ ImportFormatPreferences importFormatPreferences = mock(ImportFormatPreferences.class);
+ when(preferencesService.getImportFormatPreferences()).thenReturn(importFormatPreferences);
+
+ BibEntryPreferences bibEntryPreferences = mock(BibEntryPreferences.class);
+ when(importFormatPreferences.bibEntryPreferences()).thenReturn(bibEntryPreferences);
+ when(bibEntryPreferences.getKeywordSeparator()).thenReturn(',');
+
+ FieldWriterPreferences fieldWriterPreferences = mock(FieldWriterPreferences.class);
+ when(preferencesService.getFieldWriterPreferences()).thenReturn(fieldWriterPreferences);
+ when(fieldWriterPreferences.isResolveStrings()).thenReturn(false);
+
+ // defaults are in {@link org.jabref.preferences.JabRefPreferences.NON_WRAPPABLE_FIELDS}
+ FieldContentFormatterPreferences fieldContentFormatterPreferences = new FieldContentFormatterPreferences(List.of());
+ // used twice, once for reading and once for writing
+ when(importFormatPreferences.fieldContentFormatterPreferences()).thenReturn(fieldContentFormatterPreferences);
+ when(preferencesService.getFieldWriterPreferences().getFieldContentFormatterPreferences()).thenReturn(fieldContentFormatterPreferences);
+
+ guiPreferences = mock(GuiPreferences.class);
+ when(preferencesService.getGuiPreferences()).thenReturn(guiPreferences);
+
+ when(guiPreferences.getLastFilesOpened()).thenReturn(FXCollections.observableArrayList(TestBibFile.GENERAL_SERVER_TEST.path.toString()));
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/TestBibFile.java b/src/test/java/org/jabref/http/server/TestBibFile.java
new file mode 100644
index 00000000000..4897defde32
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/TestBibFile.java
@@ -0,0 +1,18 @@
+package org.jabref.http.server;
+
+import java.nio.file.Path;
+
+import org.jabref.logic.util.io.BackupFileUtil;
+
+public enum TestBibFile {
+ GENERAL_SERVER_TEST("src/test/resources/org/jabref/http/server/general-server-test.bib"),
+ JABREF_AUTHORS("src/test/resources/testbib/jabref-authors.bib");
+
+ public final Path path;
+ public final String id;
+
+ TestBibFile(String locationInSource) {
+ this.path = Path.of(locationInSource).toAbsolutePath();
+ this.id = path.getFileName() + "-" + BackupFileUtil.getUniqueFilePrefix(path);
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/TestServer.java b/src/test/java/org/jabref/http/server/TestServer.java
new file mode 100644
index 00000000000..6ad1edcd970
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/TestServer.java
@@ -0,0 +1,14 @@
+package org.jabref.http.server;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.slf4j.bridge.SLF4JBridgeHandler;
+
+public class TestServer {
+ public static void main(final String[] args) throws InterruptedException {
+ SLF4JBridgeHandler.removeHandlersForRootLogger();
+ SLF4JBridgeHandler.install();
+ Server.startServer(new CountDownLatch(1));
+ Thread.currentThread().join();
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/mwessl/GServer.java b/src/test/java/org/jabref/http/server/mwessl/GServer.java
new file mode 100644
index 00000000000..c5b028f316a
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/mwessl/GServer.java
@@ -0,0 +1,66 @@
+package org.jabref.http.server.mwessl;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jabref.http.server.Server;
+
+import org.glassfish.grizzly.Grizzly;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.ssl.SSLContextConfigurator;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+
+/**
+ * Secured standalone Java HTTP server.
+ */
+public class GServer {
+ private static final Logger LOGGER = Grizzly.logger(Server.class);
+
+ public static void main(String[] args) {
+ final HttpServer server = new HttpServer();
+ final ServerConfiguration config = server.getServerConfiguration();
+
+ // Register simple HttpHandler
+ config.addHttpHandler(new SimpleHttpHandler(), "/");
+
+ // create a network listener that listens on port 8080.
+ final NetworkListener networkListener = new NetworkListener("secured-listener", NetworkListener.DEFAULT_NETWORK_HOST,
+ NetworkListener.DEFAULT_NETWORK_PORT);
+
+ // Enable SSL on the listener
+ networkListener.setSecure(true);
+ networkListener.setSSLEngineConfig(createSslConfiguration());
+
+ server.addListener(networkListener);
+ try {
+ // Start the server
+ server.start();
+ System.out.println("The secured server is running.\nhttps://localhost:" + NetworkListener.DEFAULT_NETWORK_PORT + "\nPress enter to stop...");
+ System.in.read();
+ } catch (IOException ioe) {
+ LOGGER.log(Level.SEVERE, ioe.toString(), ioe);
+ } finally {
+ server.shutdownNow();
+ }
+ }
+
+ /**
+ * Initialize server side SSL configuration.
+ *
+ * @return server side {@link SSLEngineConfigurator}.
+ */
+ private static SSLEngineConfigurator createSslConfiguration() {
+ // Initialize SSLContext configuration
+ SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
+
+ sslContextConfig.setKeyStoreFile(Path.of("C:\\users\\koppor\\.keystore").toString());
+ sslContextConfig.setKeyStorePass("changeit");
+
+ // Create SSLEngine configurator
+ return new SSLEngineConfigurator(sslContextConfig.createSSLContext(), false, false, false);
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java b/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
new file mode 100644
index 00000000000..58722b8cd1f
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
@@ -0,0 +1,16 @@
+package org.jabref.http.server.mwessl;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+/**
+ * Simple {@link HttpHandler} implementation.
+ */
+public class SimpleHttpHandler extends HttpHandler {
+
+ public void service(final Request request, final Response response) throws Exception {
+ response.setContentType("text/plain");
+ response.getWriter().write("Hello world!");
+ }
+}
diff --git a/src/test/java/org/jabref/http/server/mwessl/package-info.java b/src/test/java/org/jabref/http/server/mwessl/package-info.java
new file mode 100644
index 00000000000..d74acbe1011
--- /dev/null
+++ b/src/test/java/org/jabref/http/server/mwessl/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Temporary code.
+ *
+ * Code based on https://github.com/eclipse-ee4j/grizzly/tree/master/samples/http-server-samples/src/main/java/org/glassfish/grizzly/samples/httpserver/secure
+ *
+ * BSD-3-Clause license
+ */
+package org.jabref.http.server.mwessl;
diff --git a/src/test/java/org/jabref/logic/citationstyle/JabRefItemDataProviderTest.java b/src/test/java/org/jabref/logic/citationstyle/JabRefItemDataProviderTest.java
new file mode 100644
index 00000000000..33172c35434
--- /dev/null
+++ b/src/test/java/org/jabref/logic/citationstyle/JabRefItemDataProviderTest.java
@@ -0,0 +1,49 @@
+package org.jabref.logic.citationstyle;
+
+import java.util.List;
+
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.entry.field.StandardField;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class JabRefItemDataProviderTest {
+
+ @Test
+ void toJsonOneEntry() {
+ BibDatabase bibDatabase = new BibDatabase(List.of(
+ new BibEntry()
+ .withCitationKey("key")
+ .withField(StandardField.AUTHOR, "Test Author")
+ ));
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(bibDatabase);
+ JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
+ jabRefItemDataProvider.setData(bibDatabaseContext, new BibEntryTypesManager());
+ assertEquals("""
+ [{"id":"key","type":"article","author":[{"family":"Author","given":"Test"}]}]""",
+ jabRefItemDataProvider.toJson());
+ }
+
+ @Test
+ void toJsonTwoEntries() {
+ BibDatabase bibDatabase = new BibDatabase(List.of(
+ new BibEntry()
+ .withCitationKey("key")
+ .withField(StandardField.AUTHOR, "Test Author"),
+ new BibEntry()
+ .withCitationKey("key2")
+ .withField(StandardField.AUTHOR, "Second Author")
+ ));
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(bibDatabase);
+ JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
+ jabRefItemDataProvider.setData(bibDatabaseContext, new BibEntryTypesManager());
+ assertEquals("""
+ [{"id":"key","type":"article","author":[{"family":"Author","given":"Test"}]},{"id":"key2","type":"article","author":[{"family":"Author","given":"Second"}]}]""",
+ jabRefItemDataProvider.toJson());
+ }
+}
diff --git a/src/test/java/org/jabref/testutils/interactive/http/rest-api.http b/src/test/java/org/jabref/testutils/interactive/http/rest-api.http
new file mode 100644
index 00000000000..4994efdd6e2
--- /dev/null
+++ b/src/test/java/org/jabref/testutils/interactive/http/rest-api.http
@@ -0,0 +1,35 @@
+// This file is for IntelliJ's HTTP Client, available in the Ultimate Edition
+
+GET https://localhost:6051
+
+###
+
+GET https://localhost:6051/libraries
+
+###
+
+GET https://localhost:6051/libraries/notfound
+
+###
+
+// if you have checkout the JabRef code at c:\git-repositories\jabref, then this
+// will show the contents of your first opened library as BibTeX
+
+GET https://localhost:6051/libraries/jabref-authors.bib-026bd7ec
+Accept: application/x-bibtex
+
+###
+
+// if you have checkout the JabRef code at c:\git-repositories\jabref, then this
+// will show the contents of your first opened library using CSL JSON
+
+GET https://localhost:6051/libraries/jabref-authors.bib-026bd7ec
+Accept: application/x-bibtex-library-csl+json
+
+###
+
+// if you have checkout the JabRef code at c:\git-repositories\jabref, then this
+// will show the contents of your first opened library using json + embedded BibTeX
+
+GET https://localhost:6051/libraries/jabref-authors.bib-026bd7ec
+Accept: application/json
diff --git a/src/main/java/org/jabref/styletester/StyleTester.fxml b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTester.fxml
similarity index 99%
rename from src/main/java/org/jabref/styletester/StyleTester.fxml
rename to src/test/java/org/jabref/testutils/interactive/styletester/StyleTester.fxml
index 1f9d65669cf..2240e83d8bb 100644
--- a/src/main/java/org/jabref/styletester/StyleTester.fxml
+++ b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTester.fxml
@@ -37,7 +37,7 @@
+ xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jabref.testutils.interactive.styletester.StyleTesterView">
diff --git a/src/main/java/org/jabref/styletester/StyleTesterMain.java b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterMain.java
similarity index 96%
rename from src/main/java/org/jabref/styletester/StyleTesterMain.java
rename to src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterMain.java
index a6dd6abf351..9941472abcb 100644
--- a/src/main/java/org/jabref/styletester/StyleTesterMain.java
+++ b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterMain.java
@@ -1,4 +1,4 @@
-package org.jabref.styletester;
+package org.jabref.testutils.interactive.styletester;
import javafx.application.Application;
import javafx.scene.Scene;
diff --git a/src/main/java/org/jabref/styletester/StyleTesterView.java b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterView.java
similarity index 96%
rename from src/main/java/org/jabref/styletester/StyleTesterView.java
rename to src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterView.java
index eb2db094606..c4e3c3779b0 100644
--- a/src/main/java/org/jabref/styletester/StyleTesterView.java
+++ b/src/test/java/org/jabref/testutils/interactive/styletester/StyleTesterView.java
@@ -1,4 +1,4 @@
-package org.jabref.styletester;
+package org.jabref.testutils.interactive.styletester;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
diff --git a/src/test/resources/org/jabref/http/server/general-server-test.bib b/src/test/resources/org/jabref/http/server/general-server-test.bib
new file mode 100644
index 00000000000..c1d828316d1
--- /dev/null
+++ b/src/test/resources/org/jabref/http/server/general-server-test.bib
@@ -0,0 +1,7 @@
+@Misc{Author2023test,
+ author = {Demo Author},
+ title = {Demo Title},
+ year = {2023},
+}
+
+@Comment{jabref-meta: databaseType:bibtex;}
From 37dd6b9220d1543cc351f5584b87de53dd3b1624 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 9 Apr 2023 01:15:27 +0200
Subject: [PATCH 02/22] Remove temporary code
---
.../jabref/http/server/mwessl/GServer.java | 66 -------------------
.../http/server/mwessl/SimpleHttpHandler.java | 16 -----
.../http/server/mwessl/package-info.java | 8 ---
3 files changed, 90 deletions(-)
delete mode 100644 src/test/java/org/jabref/http/server/mwessl/GServer.java
delete mode 100644 src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
delete mode 100644 src/test/java/org/jabref/http/server/mwessl/package-info.java
diff --git a/src/test/java/org/jabref/http/server/mwessl/GServer.java b/src/test/java/org/jabref/http/server/mwessl/GServer.java
deleted file mode 100644
index c5b028f316a..00000000000
--- a/src/test/java/org/jabref/http/server/mwessl/GServer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.jabref.http.server.mwessl;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.jabref.http.server.Server;
-
-import org.glassfish.grizzly.Grizzly;
-import org.glassfish.grizzly.http.server.HttpServer;
-import org.glassfish.grizzly.http.server.NetworkListener;
-import org.glassfish.grizzly.http.server.ServerConfiguration;
-import org.glassfish.grizzly.ssl.SSLContextConfigurator;
-import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
-
-/**
- * Secured standalone Java HTTP server.
- */
-public class GServer {
- private static final Logger LOGGER = Grizzly.logger(Server.class);
-
- public static void main(String[] args) {
- final HttpServer server = new HttpServer();
- final ServerConfiguration config = server.getServerConfiguration();
-
- // Register simple HttpHandler
- config.addHttpHandler(new SimpleHttpHandler(), "/");
-
- // create a network listener that listens on port 8080.
- final NetworkListener networkListener = new NetworkListener("secured-listener", NetworkListener.DEFAULT_NETWORK_HOST,
- NetworkListener.DEFAULT_NETWORK_PORT);
-
- // Enable SSL on the listener
- networkListener.setSecure(true);
- networkListener.setSSLEngineConfig(createSslConfiguration());
-
- server.addListener(networkListener);
- try {
- // Start the server
- server.start();
- System.out.println("The secured server is running.\nhttps://localhost:" + NetworkListener.DEFAULT_NETWORK_PORT + "\nPress enter to stop...");
- System.in.read();
- } catch (IOException ioe) {
- LOGGER.log(Level.SEVERE, ioe.toString(), ioe);
- } finally {
- server.shutdownNow();
- }
- }
-
- /**
- * Initialize server side SSL configuration.
- *
- * @return server side {@link SSLEngineConfigurator}.
- */
- private static SSLEngineConfigurator createSslConfiguration() {
- // Initialize SSLContext configuration
- SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
-
- sslContextConfig.setKeyStoreFile(Path.of("C:\\users\\koppor\\.keystore").toString());
- sslContextConfig.setKeyStorePass("changeit");
-
- // Create SSLEngine configurator
- return new SSLEngineConfigurator(sslContextConfig.createSSLContext(), false, false, false);
- }
-}
diff --git a/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java b/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
deleted file mode 100644
index 58722b8cd1f..00000000000
--- a/src/test/java/org/jabref/http/server/mwessl/SimpleHttpHandler.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.jabref.http.server.mwessl;
-
-import org.glassfish.grizzly.http.server.HttpHandler;
-import org.glassfish.grizzly.http.server.Request;
-import org.glassfish.grizzly.http.server.Response;
-
-/**
- * Simple {@link HttpHandler} implementation.
- */
-public class SimpleHttpHandler extends HttpHandler {
-
- public void service(final Request request, final Response response) throws Exception {
- response.setContentType("text/plain");
- response.getWriter().write("Hello world!");
- }
-}
diff --git a/src/test/java/org/jabref/http/server/mwessl/package-info.java b/src/test/java/org/jabref/http/server/mwessl/package-info.java
deleted file mode 100644
index d74acbe1011..00000000000
--- a/src/test/java/org/jabref/http/server/mwessl/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Temporary code.
- *
- * Code based on https://github.com/eclipse-ee4j/grizzly/tree/master/samples/http-server-samples/src/main/java/org/glassfish/grizzly/samples/httpserver/secure
- *
- * BSD-3-Clause license
- */
-package org.jabref.http.server.mwessl;
From 8f0567757182a832e628f7958981d032eaaee241 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 9 Apr 2023 01:17:10 +0200
Subject: [PATCH 03/22] Fix checkstyle
---
src/main/java/org/jabref/model/entry/BibEntry.java | 2 --
src/main/java/org/jabref/preferences/PreferencesService.java | 1 +
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/main/java/org/jabref/model/entry/BibEntry.java b/src/main/java/org/jabref/model/entry/BibEntry.java
index 3ec0822ec3e..5f5301d633a 100644
--- a/src/main/java/org/jabref/model/entry/BibEntry.java
+++ b/src/main/java/org/jabref/model/entry/BibEntry.java
@@ -918,7 +918,6 @@ public BibEntry withFields(Map content) {
return this;
}
-
public BibEntry withDate(Date date) {
setDate(date);
this.setChanged(false);
@@ -1158,5 +1157,4 @@ public void mergeWith(BibEntry other, Set otherPrioritizedFields) {
}
}
}
-
}
diff --git a/src/main/java/org/jabref/preferences/PreferencesService.java b/src/main/java/org/jabref/preferences/PreferencesService.java
index fba07fb2006..e4839db64f8 100644
--- a/src/main/java/org/jabref/preferences/PreferencesService.java
+++ b/src/main/java/org/jabref/preferences/PreferencesService.java
@@ -42,6 +42,7 @@
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.model.entry.field.Field;
import org.jabref.model.metadata.SaveOrderConfig;
+
import org.jvnet.hk2.annotations.Contract;
@Contract
From 057e982ec4fc60f37872c96e1fbd5b0aca59c815 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 10 Apr 2023 12:59:22 +0200
Subject: [PATCH 04/22] Create http-server.md with a how-to for the SSL
certificate generation
---
docs/code-howtos/http-server.md | 16 +++++++++++
.../java/org/jabref/http/server/Server.java | 28 +++++++++----------
.../org/jabref/http/server/ServerTest.java | 8 ++++++
3 files changed, 38 insertions(+), 14 deletions(-)
create mode 100644 docs/code-howtos/http-server.md
diff --git a/docs/code-howtos/http-server.md b/docs/code-howtos/http-server.md
new file mode 100644
index 00000000000..f3fa5d91578
--- /dev/null
+++ b/docs/code-howtos/http-server.md
@@ -0,0 +1,16 @@
+---
+parent: Code Howtos
+---
+# HTTP Server
+
+## Get SSL Working
+
+(Based on )
+
+Howto vor Windows - other operating systems work similar:
+
+1. As admin `choco install mkcert`
+2. As admin: `mkcert -install`
+3. `cd %APPDATA%\..\local\org.jabref\jabref\ssl`
+4. `mkcert -pkcs12 jabref.desktop jabref localhost 127.0.0.1 ::1`
+5. Rename the file to `server.p12`
diff --git a/src/main/java/org/jabref/http/server/Server.java b/src/main/java/org/jabref/http/server/Server.java
index 92ed926ba6e..88d57869da4 100644
--- a/src/main/java/org/jabref/http/server/Server.java
+++ b/src/main/java/org/jabref/http/server/Server.java
@@ -1,6 +1,7 @@
package org.jabref.http.server;
import java.net.URI;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CountDownLatch;
@@ -42,20 +43,19 @@ static void startServer(CountDownLatch latch) {
private static SSLContext getSslContext() {
SSLContextConfigurator sslContextConfig = new SSLContextConfigurator();
-
- // "server.jks" Needs to be generated using following command inside that directory:
- // keytool -genkey -keyalg RSA -alias selfsigned -keystore server.jks -storepass changeit -validity 365 -keysize 2048 -dname "CN=localhost, OU=YourOrganizationUnit, O=YourOrganization, L=YourCity, S=YourState, C=YourCountry"
-
- String keystorePath = Path.of(AppDirsFactory.getInstance()
- .getUserDataDir(
- OS.APP_DIR_APP_NAME,
- "ssl",
- OS.APP_DIR_APP_AUTHOR))
- .resolve("server.p12").toString();
-
- sslContextConfig.setKeyStoreFile(keystorePath);
- sslContextConfig.setKeyStorePass("changeit");
-
+ Path serverKeyStore = Path.of(AppDirsFactory.getInstance()
+ .getUserDataDir(
+ OS.APP_DIR_APP_NAME,
+ "ssl",
+ OS.APP_DIR_APP_AUTHOR))
+ .resolve("server.p12");
+ if (Files.exists(serverKeyStore)) {
+ sslContextConfig.setKeyStoreFile(serverKeyStore.toString());
+ sslContextConfig.setKeyStorePass("changeit");
+ } else {
+ LOGGER.error("Could not find server key store {}.", serverKeyStore);
+ LOGGER.error("One create one by following the steps described in [http-server.md](/docs/code-howtos/http-server.md), which is rendered at ");
+ }
return sslContextConfig.createSSLContext();
}
diff --git a/src/test/java/org/jabref/http/server/ServerTest.java b/src/test/java/org/jabref/http/server/ServerTest.java
index 9395cc93171..039b0e68131 100644
--- a/src/test/java/org/jabref/http/server/ServerTest.java
+++ b/src/test/java/org/jabref/http/server/ServerTest.java
@@ -24,6 +24,14 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+/**
+ * Abstract test class to
+ *
+ * - Initialize the JCL to SLF4J bridge
+ * - Provide injection capabilities of JabRef's preferences and Gson<./li>
+ *
+ * More information on testing with Jersey is available at the Jersey's testing documentation
.
+ */
abstract class ServerTest extends JerseyTest {
private static PreferencesService preferencesService;
From be1266495086b9057b2b155a10c9bf1b4b185a10 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Thu, 20 Apr 2023 11:41:31 +0200
Subject: [PATCH 05/22] Enables passing files to a test Server
---
build.gradle | 6 ++
.../java/org/jabref/http/server/Server.java | 62 ++++++++++++++++++-
.../java/org/jabref/logic/git/GitHandler.java | 2 +-
.../jabref/http/server/http-server-demo.bib | 14 +++++
src/main/resources/tinylog.properties | 2 +
.../org/jabref/http/server/TestServer.java | 14 -----
src/test/resources/testbib/jabref-authors.bib | 13 ++++
7 files changed, 95 insertions(+), 18 deletions(-)
create mode 100644 src/main/resources/org/jabref/http/server/http-server-demo.bib
delete mode 100644 src/test/java/org/jabref/http/server/TestServer.java
diff --git a/build.gradle b/build.gradle
index ac0799e23ee..5c012f27903 100644
--- a/build.gradle
+++ b/build.gradle
@@ -401,6 +401,12 @@ run {
'javafx.base/com.sun.javafx.event' : 'com.jfoenix'
]
}
+
+ if (project.hasProperty('application')){
+ if (application == 'httpserver'){
+ main = 'org.jabref.http.server.Server'
+ }
+ }
}
javadoc {
diff --git a/src/main/java/org/jabref/http/server/Server.java b/src/main/java/org/jabref/http/server/Server.java
index 88d57869da4..dc8411e8aa9 100644
--- a/src/main/java/org/jabref/http/server/Server.java
+++ b/src/main/java/org/jabref/http/server/Server.java
@@ -1,26 +1,81 @@
package org.jabref.http.server;
import java.net.URI;
+import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.concurrent.CountDownLatch;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import javax.net.ssl.SSLContext;
+import javafx.collections.ObservableList;
+
import org.jabref.logic.util.OS;
+import org.jabref.preferences.JabRefPreferences;
import jakarta.ws.rs.SeBootstrap;
import net.harawata.appdirs.AppDirsFactory;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.slf4j.bridge.SLF4JBridgeHandler;
public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
private static SeBootstrap.Instance serverInstance;
- static void startServer(CountDownLatch latch) {
+ /**
+ * Starts an http server serving the last files opened in JabRef
+ * More files can be provided as args.
+ */
+ public static void main(final String[] args) throws InterruptedException, URISyntaxException {
+ SLF4JBridgeHandler.removeHandlersForRootLogger();
+ SLF4JBridgeHandler.install();
+
+ final ObservableList lastFilesOpened = JabRefPreferences.getInstance().getGuiPreferences().getLastFilesOpened();
+
+ // The server serves the last opened files (see org.jabref.http.server.LibraryResource.getLibraryPath)
+ // In a testing environment, this might be difficult to handle
+ // This is a quick solution. The architectural fine solution would use some http context or other @Inject_ed variables in org.jabref.http.server.LibraryResource
+ if (args.length > 0) {
+ LOGGER.debug("Command line parameters passed");
+ List filesToAdd = Arrays.stream(args)
+ .map(Path::of)
+ .filter(Files::exists)
+ .map(Path::toString)
+ .filter(path -> !lastFilesOpened.contains(path))
+ .toList();
+
+ LOGGER.debug("Adding following files to the list of opened libraries: {}", filesToAdd);
+
+ // add the files in the front of the last opened libraries
+ Collections.reverse(filesToAdd);
+ for (String path : filesToAdd) {
+ lastFilesOpened.add(0, path);
+ }
+ }
+
+ if (lastFilesOpened.isEmpty()) {
+ LOGGER.debug("still no library available to serve, serve the demo library");
+ // Server.class.getResource("...") is always null here, thus trying relative path
+ // Path bibPath = Path.of(Server.class.getResource("http-server-demo.bib").toURI());
+ Path bibPath = Path.of("src/main/resources/org/jabref/http/server/http-server-demo.bib").toAbsolutePath();
+ LOGGER.debug("Location of demo library: {}", bibPath);
+ lastFilesOpened.add(bibPath.toString());
+ }
+
+ LOGGER.debug("Libraries served: {}", lastFilesOpened);
+
+ Server.startServer();
+
+ // Keep the http server running until user kills the process (e.g., presses Ctrl+C)
+ Thread.currentThread().join();
+ }
+
+ private static void startServer() {
SSLContext sslContext = getSslContext();
SeBootstrap.Configuration configuration = SeBootstrap.Configuration
.builder()
@@ -28,7 +83,9 @@ static void startServer(CountDownLatch latch) {
.protocol("HTTPS")
.port(6051)
.build();
+ LOGGER.debug("Starting server...");
SeBootstrap.start(Application.class, configuration).thenAccept(instance -> {
+ LOGGER.debug("Server started.");
instance.stopOnShutdown(stopResult ->
System.out.printf("Stop result: %s [Native stop result: %s].%n", stopResult,
stopResult.unwrap(Object.class)));
@@ -37,7 +94,6 @@ static void startServer(CountDownLatch latch) {
instance.unwrap(Object.class));
System.out.println("Send SIGKILL to shutdown.");
serverInstance = instance;
- latch.countDown();
});
}
diff --git a/src/main/java/org/jabref/logic/git/GitHandler.java b/src/main/java/org/jabref/logic/git/GitHandler.java
index 498e5c5f3df..e865e9b9299 100644
--- a/src/main/java/org/jabref/logic/git/GitHandler.java
+++ b/src/main/java/org/jabref/logic/git/GitHandler.java
@@ -71,7 +71,7 @@ void setupGitIgnore() {
FileUtil.copyFile(Path.of(this.getClass().getResource("git.gitignore").toURI()), gitignore, false);
}
} catch (URISyntaxException e) {
- LOGGER.error("Error occurred during copying of the gitignore file into the git repository.");
+ LOGGER.error("Error occurred during copying of the gitignore file into the git repository.", e);
}
}
diff --git a/src/main/resources/org/jabref/http/server/http-server-demo.bib b/src/main/resources/org/jabref/http/server/http-server-demo.bib
new file mode 100644
index 00000000000..f5374fef3ad
--- /dev/null
+++ b/src/main/resources/org/jabref/http/server/http-server-demo.bib
@@ -0,0 +1,14 @@
+@InProceedings{Kopp2018,
+ author = {Kopp, Oliver and Armbruster, Anita and Zimmermann, Olaf},
+ booktitle = {Proceedings of the 10th Central European Workshop on Services and their Composition ({ZEUS} 2018)},
+ title = {Markdown Architectural Decision Records: Format and Tool Support},
+ year = {2018},
+ editor = {Nico Herzberg and Christoph Hochreiner and Oliver Kopp and J{\"{o}}rg Lenhard},
+ pages = {55--62},
+ publisher = {CEUR-WS.org},
+ series = {{CEUR} Workshop Proceedings},
+ volume = {2072},
+ keywords = {ADR, MADR, architecture decision records, architectural decision records, Nygard},
+}
+
+@Comment{jabref-meta: databaseType:bibtex;}
diff --git a/src/main/resources/tinylog.properties b/src/main/resources/tinylog.properties
index b4340fa32e9..5347cea4ca5 100644
--- a/src/main/resources/tinylog.properties
+++ b/src/main/resources/tinylog.properties
@@ -7,3 +7,5 @@ writerAzure = application insights
exception = strip: jdk.internal
#level@org.jabref.model.entry.BibEntry = debug
+
+level@org.jabref.http.server.Server = debug
diff --git a/src/test/java/org/jabref/http/server/TestServer.java b/src/test/java/org/jabref/http/server/TestServer.java
deleted file mode 100644
index 6ad1edcd970..00000000000
--- a/src/test/java/org/jabref/http/server/TestServer.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.jabref.http.server;
-
-import java.util.concurrent.CountDownLatch;
-
-import org.slf4j.bridge.SLF4JBridgeHandler;
-
-public class TestServer {
- public static void main(final String[] args) throws InterruptedException {
- SLF4JBridgeHandler.removeHandlersForRootLogger();
- SLF4JBridgeHandler.install();
- Server.startServer(new CountDownLatch(1));
- Thread.currentThread().join();
- }
-}
diff --git a/src/test/resources/testbib/jabref-authors.bib b/src/test/resources/testbib/jabref-authors.bib
index 87a7c5eeb46..8687068e425 100644
--- a/src/test/resources/testbib/jabref-authors.bib
+++ b/src/test/resources/testbib/jabref-authors.bib
@@ -3003,6 +3003,19 @@ @InProceedings{SimonDietzDiezEtAl2019
priority = {prio1},
}
+@InProceedings{Kopp2018,
+ author = {Kopp, Oliver and Armbruster, Anita and Zimmermann, Olaf},
+ booktitle = {Proceedings of the 10th Central European Workshop on Services and their Composition ({ZEUS} 2018)},
+ date = {2018},
+ title = {Markdown Architectural Decision Records: Format and Tool Support},
+ editor = {Nico Herzberg and Christoph Hochreiner and Oliver Kopp and J{\"{o}}rg Lenhard},
+ pages = {55--62},
+ publisher = {CEUR-WS.org},
+ series = {{CEUR} Workshop Proceedings},
+ volume = {2072},
+ keywords = {ADR, MADR, architecture decision records, architectural decision records, Nygard},
+}
+
@Comment{jabref-meta: databaseType:biblatex;}
@Comment{jabref-meta: grouping:
From 9cde0a6b283a523dc662d4f710e660bf92d08e11 Mon Sep 17 00:00:00 2001
From: Siedlerchr
Date: Sun, 3 Sep 2023 21:41:32 +0200
Subject: [PATCH 06/22] fix some gradle tasks
---
build.gradle | 12 ++---
src/main/java/module-info.java | 1 +
.../java/org/jabref/http/dto/BibEntryDTO.java | 2 +-
.../citationstyle/JabRefItemDataProvider.java | 44 ++++++++++++++++---
4 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/build.gradle b/build.gradle
index c4ef64f8788..218c46a223f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -460,7 +460,7 @@ task fetcherTest(type: Test) {
}
}
-task guiTest(type: Test) {
+tasks.register('guiTest', Test) {
useJUnitPlatform {
includeTags 'GUITest'
}
@@ -473,7 +473,7 @@ task guiTest(type: Test) {
}
// Test result tasks
-task copyTestResources(type: Copy) {
+tasks.register('copyTestResources', Copy) {
from "${projectDir}/src/test/resources"
into "${buildDir}/classes/test"
}
@@ -483,11 +483,13 @@ tasks.withType(Test) {
reports.html.outputLocation.set(file("${reporting.baseDir}/${name}"))
}
-task jacocoPrepare() {
+tasks.register('jacocoPrepare') {
doFirst {
// Ignore failures of tests
- tasks.withType(Test) {
- ignoreFailures = true
+ tasks.withType(Test).tap {
+ configureEach {
+ ignoreFailures = true
+ }
}
}
}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index e4c66f4e4eb..ba516fee5fb 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -137,5 +137,6 @@
// other libraries
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
+ requires de.saxsys.mvvmfx.validation;
}
diff --git a/src/main/java/org/jabref/http/dto/BibEntryDTO.java b/src/main/java/org/jabref/http/dto/BibEntryDTO.java
index b9369d2566f..1e36564072d 100644
--- a/src/main/java/org/jabref/http/dto/BibEntryDTO.java
+++ b/src/main/java/org/jabref/http/dto/BibEntryDTO.java
@@ -4,8 +4,8 @@
import java.io.StringWriter;
import org.jabref.logic.bibtex.BibEntryWriter;
-import org.jabref.logic.bibtex.FieldWriter;
import org.jabref.logic.bibtex.FieldPreferences;
+import org.jabref.logic.bibtex.FieldWriter;
import org.jabref.logic.exporter.BibWriter;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
diff --git a/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
index c4eac582e22..8cf86c8e287 100644
--- a/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
+++ b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
@@ -31,8 +31,28 @@
import org.jbibtex.Key;
/**
- * Custom {@link ItemDataProvider} that allows to set the data so that we don't have to instantiate a new CSL object
- * every time.
+ * Custom
+ * {@link
+ * ItemDataProvider}
+ * that
+ * allows
+ * to
+ * set
+ * the
+ * data
+ * so
+ * that
+ * we
+ * don't
+ * have
+ * to
+ * instantiate
+ * a
+ * new
+ * CSL
+ * object
+ * every
+ * time.
*/
public class JabRefItemDataProvider implements ItemDataProvider {
@@ -51,7 +71,13 @@ public JabRefItemDataProvider() {
}
/**
- * Converts the {@link BibEntry} into {@link CSLItemData}.
+ * Converts
+ * the
+ * {@link
+ * BibEntry}
+ * into
+ * {@link
+ * CSLItemData}.
*
*
*
@@ -167,7 +193,15 @@ private CSLItemData bibEntryToCSLItemData(BibEntry originalBibEntry, BibDatabase
}
/**
- * Fills the data with all entries in given bibDatabaseContext
+ * Fills
+ * the
+ * data
+ * with
+ * all
+ * entries
+ * in
+ * given
+ * bibDatabaseContext
*/
public void setData(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
this.setData(bibDatabaseContext.getEntries(), bibDatabaseContext, entryTypesManager);
@@ -210,6 +244,4 @@ public String toJson() {
.map(String.class::cast)
.collect(Collectors.joining(",", "[", "]"));
}
-
-
}
From 7fb5100f83aca9dd05e7bd853fd6ff4a54ff6810 Mon Sep 17 00:00:00 2001
From: Siedlerchr
Date: Sun, 3 Sep 2023 22:25:13 +0200
Subject: [PATCH 07/22] fix test build
---
.../java/org/jabref/http/server/ServerTest.java | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/test/java/org/jabref/http/server/ServerTest.java b/src/test/java/org/jabref/http/server/ServerTest.java
index 039b0e68131..3785cf001d3 100644
--- a/src/test/java/org/jabref/http/server/ServerTest.java
+++ b/src/test/java/org/jabref/http/server/ServerTest.java
@@ -7,8 +7,7 @@
import javafx.collections.FXCollections;
import org.jabref.http.dto.GsonFactory;
-import org.jabref.logic.bibtex.FieldContentFormatterPreferences;
-import org.jabref.logic.bibtex.FieldWriterPreferences;
+import org.jabref.logic.bibtex.FieldPreferences;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.preferences.BibEntryPreferences;
import org.jabref.preferences.GuiPreferences;
@@ -82,15 +81,14 @@ private static void initializePreferencesService() {
when(importFormatPreferences.bibEntryPreferences()).thenReturn(bibEntryPreferences);
when(bibEntryPreferences.getKeywordSeparator()).thenReturn(',');
- FieldWriterPreferences fieldWriterPreferences = mock(FieldWriterPreferences.class);
- when(preferencesService.getFieldWriterPreferences()).thenReturn(fieldWriterPreferences);
- when(fieldWriterPreferences.isResolveStrings()).thenReturn(false);
+ FieldPreferences fieldWriterPreferences = mock(FieldPreferences.class);
+ when(preferencesService.getFieldPreferences()).thenReturn(fieldWriterPreferences);
+ when(fieldWriterPreferences.shouldResolveStrings()).thenReturn(false);
// defaults are in {@link org.jabref.preferences.JabRefPreferences.NON_WRAPPABLE_FIELDS}
- FieldContentFormatterPreferences fieldContentFormatterPreferences = new FieldContentFormatterPreferences(List.of());
+ FieldPreferences fieldContentFormatterPreferences = new FieldPreferences(false, List.of(), List.of());
// used twice, once for reading and once for writing
- when(importFormatPreferences.fieldContentFormatterPreferences()).thenReturn(fieldContentFormatterPreferences);
- when(preferencesService.getFieldWriterPreferences().getFieldContentFormatterPreferences()).thenReturn(fieldContentFormatterPreferences);
+ when(importFormatPreferences.fieldPreferences()).thenReturn(fieldContentFormatterPreferences);
guiPreferences = mock(GuiPreferences.class);
when(preferencesService.getGuiPreferences()).thenReturn(guiPreferences);
From 75e7de1d83d59fc446052e50e393f29bb0b5f405 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 22:28:13 +0200
Subject: [PATCH 08/22] Fix broken JavaDoc
---
.../citationstyle/JabRefItemDataProvider.java | 42 ++-----------------
1 file changed, 4 insertions(+), 38 deletions(-)
diff --git a/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
index 8cf86c8e287..2310a7c6b25 100644
--- a/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
+++ b/src/main/java/org/jabref/logic/citationstyle/JabRefItemDataProvider.java
@@ -31,28 +31,8 @@
import org.jbibtex.Key;
/**
- * Custom
- * {@link
- * ItemDataProvider}
- * that
- * allows
- * to
- * set
- * the
- * data
- * so
- * that
- * we
- * don't
- * have
- * to
- * instantiate
- * a
- * new
- * CSL
- * object
- * every
- * time.
+ * Custom {@link ItemDataProvider} that allows to set the data so that we don't have to instantiate a new CSL object
+ * every time.
*/
public class JabRefItemDataProvider implements ItemDataProvider {
@@ -71,13 +51,7 @@ public JabRefItemDataProvider() {
}
/**
- * Converts
- * the
- * {@link
- * BibEntry}
- * into
- * {@link
- * CSLItemData}.
+ * Converts the {@link BibEntry} into {@link CSLItemData}.
*
*
*
@@ -193,15 +167,7 @@ private CSLItemData bibEntryToCSLItemData(BibEntry originalBibEntry, BibDatabase
}
/**
- * Fills
- * the
- * data
- * with
- * all
- * entries
- * in
- * given
- * bibDatabaseContext
+ * Fills the data with all entries in given bibDatabaseContext
*/
public void setData(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager) {
this.setData(bibDatabaseContext.getEntries(), bibDatabaseContext, entryTypesManager);
From ef1c34a1c8f9bbd2662f55cd3ac3e9b3eb3ddf95 Mon Sep 17 00:00:00 2001
From: Siedlerchr
Date: Sun, 3 Sep 2023 22:29:18 +0200
Subject: [PATCH 09/22] upgrade jersey to 3.1.3
---
build.gradle | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/build.gradle b/build.gradle
index 218c46a223f..5694881f02f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -222,15 +222,15 @@ dependencies {
// Implementation of the API
implementation 'org.glassfish.jersey.core:jersey-server:3.1.1'
// injection framework
- implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.1'
+ implementation 'org.glassfish.jersey.inject:jersey-hk2:3.1.3'
implementation 'org.glassfish.hk2:hk2-api:2.6.1'
// testImplementation 'org.glassfish.hk2:hk2-testing:3.0.4'
// implementation 'org.glassfish.hk2:hk2-testing-jersey:3.0.4'
// testImplementation 'org.glassfish.hk2:hk2-junitrunner:3.0.4'
// HTTP server
// implementation 'org.glassfish.jersey.containers:jersey-container-netty-http:3.1.1'
- implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.1'
- testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.1'
+ implementation 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:3.1.3'
+ testImplementation 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:3.1.3'
// Allow objects "magically" to be mapped to JSON using GSON
// implementation 'org.glassfish.jersey.media:jersey-media-json-gson:3.1.1'
@@ -436,7 +436,7 @@ testlogger {
showSkipped false
}
-task databaseTest(type: Test) {
+tasks.register('databaseTest', Test) {
useJUnitPlatform {
includeTags 'DatabaseTest'
}
@@ -448,7 +448,7 @@ task databaseTest(type: Test) {
}
}
-task fetcherTest(type: Test) {
+tasks.register('fetcherTest', Test) {
useJUnitPlatform {
includeTags 'FetcherTest'
}
From 94a05ca53946f128f87b0358d438e34f2238488e Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 22:38:06 +0200
Subject: [PATCH 10/22] Fix checkstyle
---
src/test/java/org/jabref/http/server/ServerTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/org/jabref/http/server/ServerTest.java b/src/test/java/org/jabref/http/server/ServerTest.java
index 3785cf001d3..657a340e7c3 100644
--- a/src/test/java/org/jabref/http/server/ServerTest.java
+++ b/src/test/java/org/jabref/http/server/ServerTest.java
@@ -86,7 +86,7 @@ private static void initializePreferencesService() {
when(fieldWriterPreferences.shouldResolveStrings()).thenReturn(false);
// defaults are in {@link org.jabref.preferences.JabRefPreferences.NON_WRAPPABLE_FIELDS}
- FieldPreferences fieldContentFormatterPreferences = new FieldPreferences(false, List.of(), List.of());
+ FieldPreferences fieldContentFormatterPreferences = new FieldPreferences(false, List.of(), List.of());
// used twice, once for reading and once for writing
when(importFormatPreferences.fieldPreferences()).thenReturn(fieldContentFormatterPreferences);
From e09ead0eb9c668beea406f374a1270afa9aa57c2 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 23:20:00 +0200
Subject: [PATCH 11/22] Update http-server.md
---
docs/code-howtos/http-server.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/docs/code-howtos/http-server.md b/docs/code-howtos/http-server.md
index f3fa5d91578..99585c50814 100644
--- a/docs/code-howtos/http-server.md
+++ b/docs/code-howtos/http-server.md
@@ -7,10 +7,14 @@ parent: Code Howtos
(Based on )
-Howto vor Windows - other operating systems work similar:
+Howto for Windows - other operating systems work similar:
1. As admin `choco install mkcert`
2. As admin: `mkcert -install`
3. `cd %APPDATA%\..\local\org.jabref\jabref\ssl`
4. `mkcert -pkcs12 jabref.desktop jabref localhost 127.0.0.1 ::1`
5. Rename the file to `server.p12`
+
+Note: If you do not do this, you get following error message:
+
+ Could not find server key store C:\Users\USERNAME\AppData\Local\org.jabref\jabref\ssl\server.p12.
From 29072fcb00494d312337c1082205bb5a51faa47f Mon Sep 17 00:00:00 2001
From: Siedlerchr
Date: Sun, 3 Sep 2023 23:21:03 +0200
Subject: [PATCH 12/22] rewrite run
---
src/main/java/org/jabref/http/server/LibraryResource.java | 5 +++--
.../java/org/jabref/http/server/LibrariesResourceTest.java | 3 ++-
.../java/org/jabref/http/server/LibraryResourceTest.java | 3 ++-
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/jabref/http/server/LibraryResource.java b/src/main/java/org/jabref/http/server/LibraryResource.java
index 349cd272b3a..7a13aed89ed 100644
--- a/src/main/java/org/jabref/http/server/LibraryResource.java
+++ b/src/main/java/org/jabref/http/server/LibraryResource.java
@@ -6,6 +6,7 @@
import java.util.Objects;
import org.jabref.gui.Globals;
+import org.jabref.http.MediaType;
import org.jabref.http.dto.BibEntryDTO;
import org.jabref.logic.citationstyle.JabRefItemDataProvider;
import org.jabref.logic.importer.ParserResult;
@@ -50,7 +51,7 @@ public String getJson(@PathParam("id") String id) {
}
@GET
- @Produces(org.jabref.http.MediaType.JSON_CSL_ITEM)
+ @Produces(MediaType.JSON_CSL_ITEM)
public String getClsItemJson(@PathParam("id") String id) {
ParserResult parserResult = getParserResult(id);
JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
@@ -71,7 +72,7 @@ private ParserResult getParserResult(String id) {
}
@GET
- @Produces(org.jabref.http.MediaType.BIBTEX)
+ @Produces(MediaType.BIBTEX)
public Response getBibtex(@PathParam("id") String id) {
java.nio.file.Path library = getLibraryPath(id);
String libraryAsString;
diff --git a/src/test/java/org/jabref/http/server/LibrariesResourceTest.java b/src/test/java/org/jabref/http/server/LibrariesResourceTest.java
index df7d7784bae..1ec937d6daf 100644
--- a/src/test/java/org/jabref/http/server/LibrariesResourceTest.java
+++ b/src/test/java/org/jabref/http/server/LibrariesResourceTest.java
@@ -3,6 +3,7 @@
import java.util.EnumSet;
import java.util.stream.Collectors;
+import jakarta.ws.rs.core.Application;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
@@ -11,7 +12,7 @@
class LibrariesResourceTest extends ServerTest {
@Override
- protected jakarta.ws.rs.core.Application configure() {
+ protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(LibrariesResource.class);
addPreferencesToResourceConfig(resourceConfig);
return resourceConfig.getApplication();
diff --git a/src/test/java/org/jabref/http/server/LibraryResourceTest.java b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
index 650999edab2..2470c210b5c 100644
--- a/src/test/java/org/jabref/http/server/LibraryResourceTest.java
+++ b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
@@ -2,6 +2,7 @@
import org.jabref.http.MediaType;
+import jakarta.ws.rs.core.Application;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
@@ -10,7 +11,7 @@
class LibraryResourceTest extends ServerTest {
@Override
- protected jakarta.ws.rs.core.Application configure() {
+ protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(LibraryResource.class, LibrariesResource.class);
addPreferencesToResourceConfig(resourceConfig);
addGsonToResourceConfig(resourceConfig);
From 59788d03bdefbe0f342983a6652592a1854a915f Mon Sep 17 00:00:00 2001
From: Siedlerchr
Date: Sun, 3 Sep 2023 23:23:36 +0200
Subject: [PATCH 13/22] rename to avoid clash
---
.../jabref/http/{MediaType.java => JabrefMediaType.java} | 2 +-
src/main/java/org/jabref/http/server/LibraryResource.java | 6 +++---
.../java/org/jabref/http/server/LibraryResourceTest.java | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
rename src/main/java/org/jabref/http/{MediaType.java => JabrefMediaType.java} (85%)
diff --git a/src/main/java/org/jabref/http/MediaType.java b/src/main/java/org/jabref/http/JabrefMediaType.java
similarity index 85%
rename from src/main/java/org/jabref/http/MediaType.java
rename to src/main/java/org/jabref/http/JabrefMediaType.java
index b37297ebd71..b1d3598867e 100644
--- a/src/main/java/org/jabref/http/MediaType.java
+++ b/src/main/java/org/jabref/http/JabrefMediaType.java
@@ -1,6 +1,6 @@
package org.jabref.http;
-public class MediaType {
+public class JabrefMediaType {
public static final String BIBTEX = "application/x-bibtex";
public static final String JSON_CSL_ITEM = "application/x-bibtex-library-csl+json";
}
diff --git a/src/main/java/org/jabref/http/server/LibraryResource.java b/src/main/java/org/jabref/http/server/LibraryResource.java
index 7a13aed89ed..2ff4b970723 100644
--- a/src/main/java/org/jabref/http/server/LibraryResource.java
+++ b/src/main/java/org/jabref/http/server/LibraryResource.java
@@ -6,7 +6,7 @@
import java.util.Objects;
import org.jabref.gui.Globals;
-import org.jabref.http.MediaType;
+import org.jabref.http.JabrefMediaType;
import org.jabref.http.dto.BibEntryDTO;
import org.jabref.logic.citationstyle.JabRefItemDataProvider;
import org.jabref.logic.importer.ParserResult;
@@ -51,7 +51,7 @@ public String getJson(@PathParam("id") String id) {
}
@GET
- @Produces(MediaType.JSON_CSL_ITEM)
+ @Produces(JabrefMediaType.JSON_CSL_ITEM)
public String getClsItemJson(@PathParam("id") String id) {
ParserResult parserResult = getParserResult(id);
JabRefItemDataProvider jabRefItemDataProvider = new JabRefItemDataProvider();
@@ -72,7 +72,7 @@ private ParserResult getParserResult(String id) {
}
@GET
- @Produces(MediaType.BIBTEX)
+ @Produces(JabrefMediaType.BIBTEX)
public Response getBibtex(@PathParam("id") String id) {
java.nio.file.Path library = getLibraryPath(id);
String libraryAsString;
diff --git a/src/test/java/org/jabref/http/server/LibraryResourceTest.java b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
index 2470c210b5c..f4d43c12b77 100644
--- a/src/test/java/org/jabref/http/server/LibraryResourceTest.java
+++ b/src/test/java/org/jabref/http/server/LibraryResourceTest.java
@@ -1,6 +1,6 @@
package org.jabref.http.server;
-import org.jabref.http.MediaType;
+import org.jabref.http.JabrefMediaType;
import jakarta.ws.rs.core.Application;
import org.glassfish.jersey.server.ResourceConfig;
@@ -28,12 +28,12 @@ void getJson() {
}
@Comment{jabref-meta: databaseType:bibtex;}
- """, target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(MediaType.BIBTEX).get(String.class));
+ """, target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(JabrefMediaType.BIBTEX).get(String.class));
}
@Test
void getClsItemJson() {
assertEquals("""
- [{"id":"Author2023test","type":"article","author":[{"family":"Author","given":"Demo"}],"event-date":{"date-parts":[[2023]]},"issued":{"date-parts":[[2023]]},"title":"Demo Title"}]""", target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(MediaType.JSON_CSL_ITEM).get(String.class));
+ [{"id":"Author2023test","type":"article","author":[{"family":"Author","given":"Demo"}],"event-date":{"date-parts":[[2023]]},"issued":{"date-parts":[[2023]]},"title":"Demo Title"}]""", target("/libraries/" + TestBibFile.GENERAL_SERVER_TEST.id).request(JabrefMediaType.JSON_CSL_ITEM).get(String.class));
}
}
From 018277e9855e365185cd9de880f9b79dd92e053c Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 23:28:08 +0200
Subject: [PATCH 14/22] Hints on http server
---
docs/code-howtos/http-server.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/code-howtos/http-server.md b/docs/code-howtos/http-server.md
index 99585c50814..62fd3dad168 100644
--- a/docs/code-howtos/http-server.md
+++ b/docs/code-howtos/http-server.md
@@ -18,3 +18,7 @@ Howto for Windows - other operating systems work similar:
Note: If you do not do this, you get following error message:
Could not find server key store C:\Users\USERNAME\AppData\Local\org.jabref\jabref\ssl\server.p12.
+
+## Start http server
+
+The class starting the server is `org.jabref.http.server.Server`.
From 1c15c60321da99d5b892e91b9e0db55df4364cee Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 23:33:54 +0200
Subject: [PATCH 15/22] Update http-server.md
---
docs/code-howtos/http-server.md | 38 +++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/docs/code-howtos/http-server.md b/docs/code-howtos/http-server.md
index 62fd3dad168..4db0fdbc406 100644
--- a/docs/code-howtos/http-server.md
+++ b/docs/code-howtos/http-server.md
@@ -22,3 +22,41 @@ Note: If you do not do this, you get following error message:
## Start http server
The class starting the server is `org.jabref.http.server.Server`.
+
+Test files to server can be passed as arguments.
+If no files are passed, the last opened files are served.
+If that list is also empty, the file `src/main/resources/org/jabref/http/server/http-server-demo.bib` is served.
+
+### Starting with gradle
+
+Does not work.
+
+Current try:
+
+ ./gradlew run -Pcomment=httpserver
+
+However, there are with `ForkJoin` (discussion at https://discuss.gradle.org/t/is-it-ok-to-use-collection-parallelstream-or-other-potentially-multi-threaded-code-within-gradle-plugin-code/28003)
+
+Gradle output:
+
+```
+> Task :run
+2023-04-22 11:30:59 [main] org.jabref.http.server.Server.main()
+DEBUG: Libraries served: [C:\git-repositories\jabref-all\jabref\src\main\resources\org\jabref\http\server\http-server-demo.bib]
+2023-04-22 11:30:59 [main] org.jabref.http.server.Server.startServer()
+DEBUG: Starting server...
+<============-> 92% EXECUTING [2m 27s]
+> :run
+```
+
+IntelliJ output, if `org.jabref.http.server.Server#main` is executed:
+
+```
+DEBUG: Starting server...
+2023-04-22 11:44:59 [ForkJoinPool.commonPool-worker-1] org.glassfish.grizzly.http.server.NetworkListener.start()
+INFO: Started listener bound to [localhost:6051]
+2023-04-22 11:44:59 [ForkJoinPool.commonPool-worker-1] org.glassfish.grizzly.http.server.HttpServer.start()
+INFO: [HttpServer] Started.
+2023-04-22 11:44:59 [ForkJoinPool.commonPool-worker-1] org.jabref.http.server.Server.lambda$startServer$4()
+DEBUG: Server started.
+```
From 2e9ae7df39f61a7d7cef691d619077eb57f3b21e Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Sun, 3 Sep 2023 23:38:56 +0200
Subject: [PATCH 16/22] Better parameter name
---
build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 5694881f02f..07a682f4139 100644
--- a/build.gradle
+++ b/build.gradle
@@ -393,8 +393,8 @@ run {
]
}
- if (project.hasProperty('application')){
- if (application == 'httpserver'){
+ if (project.hasProperty('component')){
+ if (component == 'httpserver'){
main = 'org.jabref.http.server.Server'
}
}
From 2d9e8db3ad405f852a3398865d98b2eb3b17daeb Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 00:56:34 +0200
Subject: [PATCH 17/22] Add missing "requires"
---
src/main/java/module-info.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index ba516fee5fb..1c2be7cf4a5 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -16,6 +16,8 @@
requires com.dlsc.gemsfx;
uses com.dlsc.gemsfx.TagsField;
requires de.saxsys.mvvmfx;
+ requires reactfx;
+ requires org.fxmisc.flowless;
requires org.kordamp.ikonli.core;
requires org.kordamp.ikonli.javafx;
From bccf22d82283e657711471214c4e3727229eba9b Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 01:09:05 +0200
Subject: [PATCH 18/22] Create resource path only if required
---
src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
index 70c0bed0659..8573c968fc6 100644
--- a/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
+++ b/src/main/java/org/jabref/logic/net/ssl/TrustStoreManager.java
@@ -139,9 +139,9 @@ public X509Certificate getCertificate(String alias) {
public static void createTruststoreFileIfNotExist(Path storePath) {
try {
LOGGER.debug("Trust store path: {}", storePath.toAbsolutePath());
- Path storeResourcePath = Path.of(TrustStoreManager.class.getResource("/ssl/truststore.jks").toURI());
- Files.createDirectories(storePath.getParent());
if (Files.notExists(storePath)) {
+ Path storeResourcePath = Path.of(TrustStoreManager.class.getResource("/ssl/truststore.jks").toURI());
+ Files.createDirectories(storePath.getParent());
Files.copy(storeResourcePath, storePath);
}
} catch (IOException e) {
From 72d059398ccad07a5247736289a7686ad185bde3 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 01:41:20 +0200
Subject: [PATCH 19/22] Refine howto
---
.../intellij-12-build.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md
index e1feb9355a7..bdd8959042e 100644
--- a/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md
+++ b/docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.md
@@ -61,6 +61,8 @@ Then double click inside the cell "Compilation options" and enter following para
```text
--add-exports=javafx.controls/com.sun.javafx.scene.control=org.jabref
--add-exports=org.controlsfx.controls/impl.org.controlsfx.skin=org.jabref
+--add-reads org.jabref=org.fxmisc.flowless
+--add-reads org.jabref=org.apache.commons.csv
```
Press Enter to have the value really stored.
From 19e73e62cb5c0f1ca03d0d54fc7692701834c6fd Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 01:45:20 +0200
Subject: [PATCH 20/22] Add IntelliJ hint
---
docs/code-howtos/http-server.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/code-howtos/http-server.md b/docs/code-howtos/http-server.md
index 4db0fdbc406..0652cbe70c0 100644
--- a/docs/code-howtos/http-server.md
+++ b/docs/code-howtos/http-server.md
@@ -60,3 +60,8 @@ INFO: [HttpServer] Started.
2023-04-22 11:44:59 [ForkJoinPool.commonPool-worker-1] org.jabref.http.server.Server.lambda$startServer$4()
DEBUG: Server started.
```
+
+## Developing with IntelliJ
+
+IntelliJ Ultimate offers a Markdown-based http-client. One has to open the file `src/test/java/org/jabref/testutils/interactive/http/rest-api.http`.
+Then, there are play buttons appearing for interacting with the server.
From 6f51b0764e724bbfa58e9e419e81662b7084d8de Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 02:06:05 +0200
Subject: [PATCH 21/22] Add AllowedToUseStandardStreams
---
src/main/java/org/jabref/http/server/Server.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/main/java/org/jabref/http/server/Server.java b/src/main/java/org/jabref/http/server/Server.java
index dc8411e8aa9..e238dceb577 100644
--- a/src/main/java/org/jabref/http/server/Server.java
+++ b/src/main/java/org/jabref/http/server/Server.java
@@ -12,6 +12,7 @@
import javafx.collections.ObservableList;
+import org.jabref.architecture.AllowedToUseStandardStreams;
import org.jabref.logic.util.OS;
import org.jabref.preferences.JabRefPreferences;
@@ -22,6 +23,7 @@
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
+@AllowedToUseStandardStreams("This is a CLI application. It resides in the package http.server to be close to the other http server related classes.")
public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
From 8deec2448413a0589d4440bbd1538801e07b4b54 Mon Sep 17 00:00:00 2001
From: Oliver Kopp
Date: Mon, 4 Sep 2023 02:14:28 +0200
Subject: [PATCH 22/22] Also exclude TestBibFile
---
src/test/java/org/jabref/architecture/TestArchitectureTest.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/test/java/org/jabref/architecture/TestArchitectureTest.java b/src/test/java/org/jabref/architecture/TestArchitectureTest.java
index e9e66c3600f..81f73e5a117 100644
--- a/src/test/java/org/jabref/architecture/TestArchitectureTest.java
+++ b/src/test/java/org/jabref/architecture/TestArchitectureTest.java
@@ -36,6 +36,7 @@ public void testsAreIndependent(JavaClasses classes) {
public void testNaming(JavaClasses classes) {
classes().that().areTopLevelClasses()
.and().doNotHaveFullyQualifiedName("org.jabref.benchmarks.Benchmarks")
+ .and().doNotHaveFullyQualifiedName("org.jabref.http.server.TestBibFile")
.and().doNotHaveFullyQualifiedName("org.jabref.gui.autocompleter.AutoCompleterUtil")
.and().doNotHaveFullyQualifiedName("org.jabref.gui.search.TextFlowEqualityHelper")
.and().doNotHaveFullyQualifiedName("org.jabref.logic.bibtex.BibEntryAssert")