diff --git a/build.gradle b/build.gradle index b737695ffe2..50c462786a2 100644 --- a/build.gradle +++ b/build.gradle @@ -113,7 +113,7 @@ dependencies { // VersionEye states that 6.0.5 is the most recent version, but http://dev.mysql.com/downloads/connector/j/ shows that as "Development Release" compile 'mysql:mysql-connector-java:5.1.46' - compile 'com.impossibl.pgjdbc-ng:pgjdbc-ng:0.7.1' + compile 'org.postgresql:postgresql:42.2.2' compile 'net.java.dev.glazedlists:glazedlists_java15:1.9.1' diff --git a/src/main/java/org/jabref/gui/shared/ConnectToSharedDatabaseDialog.java b/src/main/java/org/jabref/gui/shared/ConnectToSharedDatabaseDialog.java index 869a73f8774..a0a454bc509 100644 --- a/src/main/java/org/jabref/gui/shared/ConnectToSharedDatabaseDialog.java +++ b/src/main/java/org/jabref/gui/shared/ConnectToSharedDatabaseDialog.java @@ -26,7 +26,6 @@ import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; @@ -156,8 +155,9 @@ public void openSharedDatabase() { return; // setLoadingConnectButtonText(false) should not be reached regularly. } catch (SQLException | InvalidDBMSConnectionPropertiesException exception) { - JOptionPane.showMessageDialog(ConnectToSharedDatabaseDialog.this, exception.getMessage(), - Localization.lang("Connection error"), JOptionPane.ERROR_MESSAGE); + + DefaultTaskExecutor.runInJavaFXThread(() -> frame.getDialogService().showErrorDialogAndWait(Localization.lang("Connection error"), exception)); + } catch (DatabaseNotSupportedException exception) { new MigrationHelpDialog(this).setVisible(true); } @@ -187,7 +187,7 @@ public void actionPerformed(ActionEvent e) { openSharedDatabase(); } catch (JabRefException exception) { - frame.getDialogService().showErrorDialogAndWait(Localization.lang("Warning"), exception); + DefaultTaskExecutor.runInJavaFXThread(() -> frame.getDialogService().showErrorDialogAndWait(Localization.lang("Warning"), exception)); } } diff --git a/src/main/java/org/jabref/logic/shared/PostgreSQLProcessor.java b/src/main/java/org/jabref/logic/shared/PostgreSQLProcessor.java index a6c3432876a..d02ba9025ed 100644 --- a/src/main/java/org/jabref/logic/shared/PostgreSQLProcessor.java +++ b/src/main/java/org/jabref/logic/shared/PostgreSQLProcessor.java @@ -4,27 +4,20 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.logging.Level; -import java.util.logging.Logger; import org.jabref.logic.shared.listener.PostgresSQLNotificationListener; import org.jabref.model.database.shared.DatabaseConnection; import org.jabref.model.entry.BibEntry; -import com.impossibl.postgres.api.jdbc.PGConnection; -import com.impossibl.postgres.jdbc.PGDataSource; -import com.impossibl.postgres.jdbc.ThreadedHousekeeper; +import org.postgresql.PGConnection; /** * Processes all incoming or outgoing bib data to PostgreSQL database and manages its structure. */ public class PostgreSQLProcessor extends DBMSProcessor { - private PGConnection pgConnection; - private PostgresSQLNotificationListener listener; - public PostgreSQLProcessor(DatabaseConnection connection) { super(connection); } @@ -37,36 +30,36 @@ public PostgreSQLProcessor(DatabaseConnection connection) { @Override public void setUp() throws SQLException { connection.createStatement().executeUpdate( - "CREATE TABLE IF NOT EXISTS \"ENTRY\" (" + - "\"SHARED_ID\" SERIAL PRIMARY KEY, " + - "\"TYPE\" VARCHAR, " + - "\"VERSION\" INTEGER DEFAULT 1)"); + "CREATE TABLE IF NOT EXISTS \"ENTRY\" (" + + "\"SHARED_ID\" SERIAL PRIMARY KEY, " + + "\"TYPE\" VARCHAR, " + + "\"VERSION\" INTEGER DEFAULT 1)"); connection.createStatement().executeUpdate( - "CREATE TABLE IF NOT EXISTS \"FIELD\" (" + - "\"ENTRY_SHARED_ID\" INTEGER REFERENCES \"ENTRY\"(\"SHARED_ID\") ON DELETE CASCADE, " + - "\"NAME\" VARCHAR, " + - "\"VALUE\" TEXT)"); + "CREATE TABLE IF NOT EXISTS \"FIELD\" (" + + "\"ENTRY_SHARED_ID\" INTEGER REFERENCES \"ENTRY\"(\"SHARED_ID\") ON DELETE CASCADE, " + + "\"NAME\" VARCHAR, " + + "\"VALUE\" TEXT)"); connection.createStatement().executeUpdate( - "CREATE TABLE IF NOT EXISTS \"METADATA\" (" - + "\"KEY\" VARCHAR," - + "\"VALUE\" TEXT)"); + "CREATE TABLE IF NOT EXISTS \"METADATA\" (" + + "\"KEY\" VARCHAR," + + "\"VALUE\" TEXT)"); } @Override protected void insertIntoEntryTable(BibEntry bibEntry) { // Inserting into ENTRY table StringBuilder insertIntoEntryQuery = new StringBuilder() - .append("INSERT INTO ") - .append(escape("ENTRY")) - .append("(") - .append(escape("TYPE")) - .append(") VALUES(?)"); + .append("INSERT INTO ") + .append(escape("ENTRY")) + .append("(") + .append(escape("TYPE")) + .append(") VALUES(?)"); // This is the only method to get generated keys which is accepted by MySQL, PostgreSQL and Oracle. try (PreparedStatement preparedEntryStatement = connection.prepareStatement(insertIntoEntryQuery.toString(), - Statement.RETURN_GENERATED_KEYS)) { + Statement.RETURN_GENERATED_KEYS)) { preparedEntryStatement.setString(1, bibEntry.getType()); preparedEntryStatement.executeUpdate(); @@ -89,23 +82,14 @@ String escape(String expression) { @Override public void startNotificationListener(DBMSSynchronizer dbmsSynchronizer) { // Disable cleanup output of ThreadedHousekeeper - Logger.getLogger(ThreadedHousekeeper.class.getName()).setLevel(Level.SEVERE); - - this.listener = new PostgresSQLNotificationListener(dbmsSynchronizer); - - PGDataSource dataSource = new PGDataSource(); - dataSource.setHost(connectionProperties.getHost()); - dataSource.setPort(connectionProperties.getPort()); - dataSource.setDatabase(connectionProperties.getDatabase()); - dataSource.setUser(connectionProperties.getUser()); - dataSource.setPassword(connectionProperties.getPassword()); - + //Logger.getLogger(ThreadedHousekeeper.class.getName()).setLevel(Level.SEVERE); try { - pgConnection = (PGConnection) dataSource.getConnection(); - pgConnection.createStatement().execute("LISTEN jabrefLiveUpdate"); + connection.createStatement().execute("LISTEN jabrefLiveUpdate"); // Do not use `new PostgresSQLNotificationListener(...)` as the object has to exist continuously! // Otherwise the listener is going to be deleted by GC. - pgConnection.addNotificationListener(listener); + PGConnection pgConnection = connection.unwrap(PGConnection.class); + listener = new PostgresSQLNotificationListener(dbmsSynchronizer, pgConnection); + listener.start(); } catch (SQLException e) { LOGGER.error("SQL Error: ", e); } @@ -114,7 +98,7 @@ public void startNotificationListener(DBMSSynchronizer dbmsSynchronizer) { @Override public void stopNotificationListener() { try { - pgConnection.close(); + connection.close(); } catch (SQLException e) { LOGGER.error("SQL Error: ", e); } @@ -123,7 +107,7 @@ public void stopNotificationListener() { @Override public void notifyClients() { try { - pgConnection.createStatement().execute("NOTIFY jabrefLiveUpdate, '" + PROCESSOR_ID + "';"); + connection.createStatement().execute("NOTIFY jabrefLiveUpdate, '" + PROCESSOR_ID + "';"); } catch (SQLException e) { LOGGER.error("SQL Error: ", e); } diff --git a/src/main/java/org/jabref/logic/shared/listener/PostgresSQLNotificationListener.java b/src/main/java/org/jabref/logic/shared/listener/PostgresSQLNotificationListener.java index d607b896c75..cf59f7a5641 100644 --- a/src/main/java/org/jabref/logic/shared/listener/PostgresSQLNotificationListener.java +++ b/src/main/java/org/jabref/logic/shared/listener/PostgresSQLNotificationListener.java @@ -1,27 +1,50 @@ package org.jabref.logic.shared.listener; +import java.sql.SQLException; + import org.jabref.logic.shared.DBMSProcessor; import org.jabref.logic.shared.DBMSSynchronizer; -import com.impossibl.postgres.api.jdbc.PGNotificationListener; +import org.postgresql.PGConnection; +import org.postgresql.PGNotification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A listener for PostgreSQL database notifications. */ -public class PostgresSQLNotificationListener implements PGNotificationListener { +public class PostgresSQLNotificationListener extends Thread { - private final DBMSSynchronizer dbmsSynchronizer; + private static final Logger LOGGER = LoggerFactory.getLogger(PostgresSQLNotificationListener.class); + private final DBMSSynchronizer dbmsSynchronizer; + private final PGConnection pgConnection; - public PostgresSQLNotificationListener(DBMSSynchronizer dbmsSynchronizer) { + public PostgresSQLNotificationListener(DBMSSynchronizer dbmsSynchronizer, PGConnection pgConnection) { this.dbmsSynchronizer = dbmsSynchronizer; + this.pgConnection = pgConnection; } @Override - public void notification(int processId, String channel, String payload) { - if (!payload.equals(DBMSProcessor.PROCESSOR_ID)) { - dbmsSynchronizer.pullChanges(); + public void run() { + try { + //noinspection InfiniteLoopStatement + while (true) { + PGNotification notifications[] = pgConnection.getNotifications(); + + if (notifications != null) { + for (PGNotification notification : notifications) { + if (!notification.getName().equals(DBMSProcessor.PROCESSOR_ID)) { + dbmsSynchronizer.pullChanges(); + } + } + } + + // Wait a while before checking again for new notifications + Thread.sleep(500); + } + } catch (SQLException | InterruptedException exception) { + LOGGER.error("Error while listening for updates to PostgresSQL", exception); } } - } diff --git a/src/main/java/org/jabref/model/database/shared/DBMSType.java b/src/main/java/org/jabref/model/database/shared/DBMSType.java index 8b3334abcc6..4bf87c36189 100644 --- a/src/main/java/org/jabref/model/database/shared/DBMSType.java +++ b/src/main/java/org/jabref/model/database/shared/DBMSType.java @@ -8,25 +8,15 @@ */ public enum DBMSType { - MYSQL( - "MySQL", - "com.mysql.jdbc.Driver", - "jdbc:mysql://%s:%d/%s", 3306), - ORACLE( - "Oracle", - "oracle.jdbc.driver.OracleDriver", - "jdbc:oracle:thin:@%s:%d:%s", 1521), - POSTGRESQL( - "PostgreSQL", - "com.impossibl.postgres.jdbc.PGDriver", - "jdbc:pgsql://%s:%d/%s", 5432); + MYSQL("MySQL", "com.mysql.jdbc.Driver", "jdbc:mysql://%s:%d/%s", 3306), + ORACLE("Oracle", "oracle.jdbc.driver.OracleDriver", "jdbc:oracle:thin:@%s:%d:%s", 1521), + POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "jdbc:postgresql://%s:%d/%s", 5432); private final String type; private final String driverPath; private final String urlPattern; private final int defaultPort; - private DBMSType(String type, String driverPath, String urlPattern, int defaultPort) { this.type = type; this.driverPath = driverPath;