EntriesEvent
using Google's Guava EventBus.
Threading model:
listen methods run on the thread posting the event - the UI thread while typing - and
therefore never touch the database themselves. Every database access runs on syncExecutor,
a single worker, so that writes and pulls happen in the order they were queued.
remoteUpdateExecutor, the UI thread in
the GUI): a pull fetches on the database worker and applies the result on the model thread.
pullLock, because in headless use (tests)
the notification listener thread applies concurrently with the caller. EventBus-dispatched
listen methods only tryLock and skip the work when the lock is contended - they may hold
EventBus monitors that the lock holder needs for posting its own events.
Connection loss: once a write finds the connection dead, the synchronizer goes offline - every
change is recorded in OfflineChanges (in memory and on disk) instead of being written, pulls
are skipped, and a background loop reconnects with backoff. Back online, the recorded changes
are written through the same optimistic lock as any other change, then everything is pulled.
Undo: a change pulled from the database is applied to the local model without going on the undo
journal, and that is a decision rather than an omission - see ADR-0072. Undoing one locally
would take back something the shared database still holds, leaving this copy out of step until
the next push, and what happens then is a question about who owns a change. What the local user
meets instead: their own recorded change refuses to apply once a pulled change has moved the
value on, and says so, rather than overwriting the newer value
(org.); and the pulled change marks the library as needing a
save, because it arrives as org..
| Modifier and Type | Class and Description |
|---|---|
| private static interface | |
| private static record | DBMSSynchronizer.
What the shared database has that the local library has not. |
| Modifier and Type | Field and Description |
|---|---|
| private final BibDatabase | |
| private final BibDatabaseContext | |
| private volatile boolean | |
| private final AtomicBoolean | |
| private Connection | |
| private DatabaseConnection | |
| private DBMSProcessor | |
| private String | |
| private final AtomicReference | |
| private final EventBus | |
| private final FieldPreferences | |
| private final FileUpdateMonitor | |
| private final GlobalCitationKeyPatterns | |
| private static final long | |
| private final Character | |
| private volatile Map | |
| private static final Logger | |
| private static final long | |
| private MetaData | |
| private Notifier | |
| private OfflineChanges | |
| private final Path | |
| private final @Nullable ExecutorService | |
| private final ReentrantLock | |
| private final Executor | |
| private boolean | |
| private final Set | |
| private final Executor | |
| private final TaskExecutor | |
| private final String |
| Access | Constructor and Description |
|---|---|
| public | DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor)
|
| public | DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor, Executor remoteUpdateExecutor)
|
| pack-priv | DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor, Executor remoteUpdateExecutor, Executor syncExecutor, Path offlineChangesDirectory)
|
| Modifier and Type | Method and Description |
|---|---|
| public void | |
| private void | |
| public void | |
| private void | |
| private boolean | |
| private void | applySaveActionsToBufferedEntry()
Model thread: the save actions mutate the entry, which the database worker must not do. |
| private boolean | Returns: true if the connection is valid, else false.Checks whether the current SQL connection is valid; a dead one takes the synchronizer offline. |
| private static void | |
| public void | |
| private void | doApplyMetaData()
Model thread: applies the save actions locally; the entries they changed are written afterwards. |
| private void | |
| private DBMSSynchronizer. | |
| public DatabaseConnectionProperties | |
| public String | |
| private void | goOffline()
Takes the synchronizer offline: from now on changes are recorded instead of written, and a scheduled task tries to get a new connection. |
| public void | handleRemoteDatabaseChange()
Schedules a full synchronization requested by another shared-database client. |
| public void | handleRemoteFieldChange(FieldChange fieldChange)
Schedules a field update received from another shared-database client. |
| public void | handleRemoteMetaDataChange()
Schedules a metadata update received from another shared-database client. |
| private void | |
| private boolean | Returns: whether the work ranRuns the given work only when no other thread is applying a pull right now. |
| public void | initializeDatabases()
Sets the table structure of shared database if needed and pulls all shared entries to the new local database. |
| private boolean | Returns: whether the entries reached the shared databaseDatabase worker |
| private static boolean | isEventSourceAccepted(EntriesEvent event)
Only local changes (and their undo) are written; changes applied from the shared side are not echoed back. |
| private boolean | |
| public void | |
| public void | |
| public void | |
| public void | |
| private List | Returns: the entries whose shared version still is the one the removal was recorded againstDatabase worker: the optimistic lock for a recorded removal, which |
| public void | openSharedDatabase(DatabaseConnection connection)
Implements org.
|
| private static void | overwriteLocalEntry(BibEntry localEntry, BibEntry sharedEntry)
Writes a pulled entry over the local one. |
| public void | pullChanges()
Implements org. Synchronizes the local BibEntries and applies the fetched MetaData on them. |
| private void | pullEntries()
Brings the local entries up to date with the shared database: fetches on the database worker, applies on the model thread. |
| private void | |
| private void | |
| public void | registerListener(Object listener)
Implements org.
|
| private boolean | Returns: whether the removal reached the shared databaseDatabase worker |
| private boolean | Returns: whether there was anything to replaywhether the local library was just loaded from the shared database and
needs the recorded state restored - after a reconnect it holds the
changes already, plus micro-edits made since the record afterRestart)Writes the changes recorded while offline through the same paths as live changes: applied to the local library (model thread) where a restart lost them, then written (database worker) - a shared entry that moved on meanwhile refuses the write and the user merges, like any other conflict. |
| private void | reviveEntriesDeletedMeanwhile(List<BibEntry> entriesToWrite, List<BibEntry> entriesToInsert)
Database worker: a shared row deleted during the outage must not take the changes recorded against it with it. |
| private void | scheduleReconnect(long delayMillis)
One attempt per scheduled task, so that waiting for the next one occupies no thread |
| public void | |
| public void | |
| public void | synchronizeLocalDatabase()
Implements org. Synchronizes the local database with shared one. |
| public void | synchronizeSharedEntry(BibEntry bibEntry)
Implements org. Writes the merged entry of a resolved conflict. |
| private void | useConnection(DatabaseConnection newConnection)
Database worker: replaces the dead connection, then synchronizes what happened meanwhile |
| private void | |
| private void | |
| private boolean | Returns: whether the write reached the shared databaseDatabase worker: runs the write and asks other clients to pull, or records the change when the connection is gone |
| private boolean | writeRecordedMetaData(Map<String, String> recordedMetaData, OfflineChanges.
Database worker: recorded metadata is merged into what the shared side has now, so that metadata other clients changed during the outage (a group they added, say) survives |
| private boolean | Returns: whether the entry reached the shared database - a refused or failed (and thus rolled back) update must not be announced to other clientsDatabase worker. |
| private boolean |
| bibDatabase | back to summary |
|---|---|
| private final BibDatabase bibDatabase | |
| bibDatabaseContext | back to summary |
|---|---|
| private final BibDatabaseContext bibDatabaseContext | |
| closed | back to summary |
|---|---|
| private volatile boolean closed | |
| connected | back to summary |
|---|---|
| private final AtomicBoolean connected | |
| currentConnection | back to summary |
|---|---|
| private Connection currentConnection | |
| dbmsConnection | back to summary |
|---|---|
| private DatabaseConnection dbmsConnection | |
| dbmsProcessor | back to summary |
|---|---|
| private DBMSProcessor dbmsProcessor | |
| dbName | back to summary |
|---|---|
| private String dbName | |
| entryWithPendingChanges | back to summary |
|---|---|
| private final AtomicReference<BibEntry> entryWithPendingChanges | |
| eventBus | back to summary |
|---|---|
| private final EventBus eventBus | |
| fieldPreferences | back to summary |
|---|---|
| private final FieldPreferences fieldPreferences | |
| fileMonitor | back to summary |
|---|---|
| private final FileUpdateMonitor fileMonitor | |
| globalCiteKeyPattern | back to summary |
|---|---|
| private final GlobalCitationKeyPatterns globalCiteKeyPattern | |
| INITIAL_RECONNECT_DELAY_MILLIS | back to summary |
|---|---|
| private static final long INITIAL_RECONNECT_DELAY_MILLIS | |
| keywordSeparator | back to summary |
|---|---|
| private final Character keywordSeparator | |
| lastSharedMetaData | back to summary |
|---|---|
| private volatile Map<String, String> lastSharedMetaData | |
| LOGGER | back to summary |
|---|---|
| private static final Logger LOGGER | |
| MAX_RECONNECT_DELAY_MILLIS | back to summary |
|---|---|
| private static final long MAX_RECONNECT_DELAY_MILLIS | |
| metaData | back to summary |
|---|---|
| private MetaData metaData | |
| notifier | back to summary |
|---|---|
| private Notifier notifier | |
| offlineChanges | back to summary |
|---|---|
| private OfflineChanges offlineChanges | |
| offlineChangesDirectory | back to summary |
|---|---|
| private final Path offlineChangesDirectory | |
| ownedSyncExecutor | back to summary |
|---|---|
| private final @Nullable ExecutorService ownedSyncExecutor | |
| pullLock | back to summary |
|---|---|
| private final ReentrantLock pullLock | |
| remoteUpdateExecutor | back to summary |
|---|---|
| private final Executor remoteUpdateExecutor | |
| sharedDatabaseOpen | back to summary |
|---|---|
| private boolean sharedDatabaseOpen | |
| sharedIdsInConflict | back to summary |
|---|---|
| private final Set<Integer> sharedIdsInConflict | |
| syncExecutor | back to summary |
|---|---|
| private final Executor syncExecutor | |
| taskExecutor | back to summary |
|---|---|
| private final TaskExecutor taskExecutor | |
| userAndHost | back to summary |
|---|---|
| private final String userAndHost | |
| DBMSSynchronizer | back to summary |
|---|---|
| public DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor) | |
| DBMSSynchronizer | back to summary |
|---|---|
| public DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor, Executor remoteUpdateExecutor) | |
| DBMSSynchronizer | back to summary |
|---|---|
pack-priv DBMSSynchronizer(@NonNull BibDatabaseContext bibDatabaseContext, Character keywordSeparator, FieldPreferences fieldPreferences, @NonNull GlobalCitationKeyPatterns globalCiteKeyPattern, FileUpdateMonitor fileMonitor, String userAndHost, TaskExecutor taskExecutor, Executor remoteUpdateExecutor, Executor syncExecutor, Path offlineChangesDirectory)
| |
| applyMetaData | back to summary |
|---|---|
| public void applyMetaData() Applies the | |
| applyRemoteChanges | back to summary |
|---|---|
| private void applyRemoteChanges(DBMSSynchronizer. Model thread | |
| applyRemoteFieldChange | back to summary |
|---|---|
| public void applyRemoteFieldChange(FieldChange fieldChange) Applies a field change received from another client. Any state that does not exactly match the received change (content-less payload, unknown entry, diverged field value) falls back to pulling everything from the database. | |
| applyRemoteMetaData | back to summary |
|---|---|
| private void applyRemoteMetaData(Map<String, String> sharedMetaData) Model thread | |
| applySaveActions | back to summary |
|---|---|
private boolean applySaveActions(BibEntry bibEntry)
| |
| applySaveActionsToBufferedEntry | back to summary |
|---|---|
| private void applySaveActionsToBufferedEntry() Model thread: the save actions mutate the entry, which the database worker must not do. Only the flush queued from here includes them; a flush before a remote-triggered pull writes the entry as it is. | |
| checkCurrentConnection | back to summary |
|---|---|
| private boolean checkCurrentConnection() Checks whether the current SQL connection is valid; a dead one takes the synchronizer offline.
| |
| closeQuietly | back to summary |
|---|---|
| private static void closeQuietly(Connection connection) | |
| closeSharedDatabase | back to summary |
|---|---|
| public void closeSharedDatabase() Implements org.
| |
| doApplyMetaData | back to summary |
|---|---|
| private void doApplyMetaData() Model thread: applies the save actions locally; the entries they changed are written afterwards. | |
| doApplyRemoteFieldChange | back to summary |
|---|---|
| private void doApplyRemoteFieldChange(FieldChange fieldChange) | |
| fetchRemoteChanges | back to summary |
|---|---|
| private DBMSSynchronizer. Database worker. Transfers only what differs: the id/version mapping plus the entries that are new or newer on the shared side. | |
| getConnectionProperties | back to summary |
|---|---|
| public DatabaseConnectionProperties getConnectionProperties() Implements org.
| |
| getDBName | back to summary |
|---|---|
| public String getDBName() Implements org.
| |
| goOffline | back to summary |
|---|---|
| private void goOffline() Takes the synchronizer offline: from now on changes are recorded instead of written, and a scheduled task tries to get a new connection. | |
| handleRemoteDatabaseChange | back to summary |
|---|---|
| public void handleRemoteDatabaseChange() Schedules a full synchronization requested by another shared-database client. | |
| handleRemoteFieldChange | back to summary |
|---|---|
| public void handleRemoteFieldChange(FieldChange fieldChange) Schedules a field update received from another shared-database client. | |
| handleRemoteMetaDataChange | back to summary |
|---|---|
| public void handleRemoteMetaDataChange() Schedules a metadata update received from another shared-database client. | |
| handleWriteFailure | back to summary |
|---|---|
| private void handleWriteFailure(String message, SQLException exception, Runnable record) Database worker. A dead connection takes the synchronizer offline and the change is recorded for later; a failure on a live connection is transient and only reported (repeating that write would fail the same way). | |
| ifNotPullingAlready | back to summary |
|---|---|
| private boolean ifNotPullingAlready(Runnable work) Runs the given work only when no other thread is applying a pull right now.
| |
| initializeDatabases | back to summary |
|---|---|
| public void initializeDatabases() throws DatabaseNotSupportedException, SQLException Sets the table structure of shared database if needed and pulls all shared entries to the new local database.
| |
| insertSharedEntries | back to summary |
|---|---|
| private boolean insertSharedEntries(List<BibEntry> bibEntries) Database worker
| |
| isEventSourceAccepted | back to summary |
|---|---|
| private static boolean isEventSourceAccepted(EntriesEvent event) Only local changes (and their undo) are written; changes applied from the shared side are not echoed back. | |
| isPresentLocalBibEntry | back to summary |
|---|---|
| private boolean isPresentLocalBibEntry(BibEntry bibEntry) | |
| listen | back to summary |
|---|---|
| public void listen(EntriesAddedEvent event) Listening method. Inserts a new
| |
| listen | back to summary |
|---|---|
| public void listen(FieldChangedEvent event) Listening method. Updates an existing shared
| |
| listen | back to summary |
|---|---|
| public void listen(EntriesRemovedEvent event) Listening method. Deletes the given list of
| |
| listen | back to summary |
|---|---|
| public void listen(MetaDataChangedEvent event) Listening method. Synchronizes the shared
| |
| onlyUnchangedSharedEntries | back to summary |
|---|---|
| private List Database worker: the optimistic lock for a recorded removal, which | |
| openSharedDatabase | back to summary |
|---|---|
| public void openSharedDatabase(DatabaseConnection connection) throws DatabaseNotSupportedException, SQLException Implements org.
| |
| overwriteLocalEntry | back to summary |
|---|---|
| private static void overwriteLocalEntry(BibEntry localEntry, BibEntry sharedEntry) Writes a pulled entry over the local one. Nothing is recorded: the change is not this user's to take back — see the class javadoc. | |
| pullChanges | back to summary |
|---|---|
| public void pullChanges() Implements org. Synchronizes the local BibEntries and applies the fetched MetaData on them.
| |
| pullEntries | back to summary |
|---|---|
| private void pullEntries() Brings the local entries up to date with the shared database: fetches on the database worker, applies on the model thread. A failed fetch leaves the local library as it is. | |
| pullMetaData | back to summary |
|---|---|
| private void pullMetaData() | |
| reconnect | back to summary |
|---|---|
| private void reconnect(long delayMillis) | |
| registerListener | back to summary |
|---|---|
| public void registerListener(Object listener) Implements org.
| |
| removeSharedEntries | back to summary |
|---|---|
| private boolean removeSharedEntries(List<BibEntry> bibEntries) Database worker
| |
| replayOfflineChanges | back to summary |
|---|---|
| private boolean replayOfflineChanges(boolean afterRestart) Writes the changes recorded while offline through the same paths as live changes: applied to the local library (model thread) where a restart lost them, then written (database worker) - a shared entry that moved on meanwhile refuses the write and the user merges, like any other conflict. Ends with a pull. Every record is dropped only once it reached the shared database, so that closing, a crash or a refused write in between leaves it recorded for the next attempt.
| |
| reviveEntriesDeletedMeanwhile | back to summary |
|---|---|
| private void reviveEntriesDeletedMeanwhile(List<BibEntry> entriesToWrite, List<BibEntry> entriesToInsert) Database worker: a shared row deleted during the outage must not take the changes recorded
against it with it. Such an entry is re-inserted as a new shared entry instead of being
updated - an update would fail with | |
| scheduleReconnect | back to summary |
|---|---|
| private void scheduleReconnect(long delayMillis) One attempt per scheduled task, so that waiting for the next one occupies no thread | |
| setDBName | back to summary |
|---|---|
| public void setDBName(String dbName) Implements org.
| |
| setMetaData | back to summary |
|---|---|
| public void setMetaData(MetaData metaData) | |
| synchronizeLocalDatabase | back to summary |
|---|---|
| public void synchronizeLocalDatabase() Implements org. Synchronizes the local database with shared one. Possible update types are: removal, update, or insert of a
| |
| synchronizeSharedEntry | back to summary |
|---|---|
| public void synchronizeSharedEntry(BibEntry bibEntry) Implements org. Writes the merged entry of a resolved conflict. The entry is a detached copy, so the save actions may run on the caller's thread.
| |
| useConnection | back to summary |
|---|---|
| private void useConnection(DatabaseConnection newConnection) Database worker: replaces the dead connection, then synchronizes what happened meanwhile | |
| withPullLock | back to summary |
|---|---|
| private void withPullLock(Runnable work) | |
| writeBufferedEntry | back to summary |
|---|---|
| private void writeBufferedEntry() Database worker: writes the buffered micro-edited entry (if any) as a whole | |
| writeOrRecord | back to summary |
|---|---|
| private boolean writeOrRecord(String failureMessage, DBMSSynchronizer. Database worker: runs the write and asks other clients to pull, or records the change when the connection is gone
| |
| writeRecordedMetaData | back to summary |
|---|---|
| private boolean writeRecordedMetaData(Map<String, String> recordedMetaData, OfflineChanges. Database worker: recorded metadata is merged into what the shared side has now, so that metadata other clients changed during the outage (a group they added, say) survives | |
| writeSharedEntry | back to summary |
|---|---|
| private boolean writeSharedEntry(BibEntry bibEntry) Database worker.
| |
| writeSharedMetaData | back to summary |
|---|---|
| private boolean writeSharedMetaData(Map<String, String> serializedMetaData) Database worker. Other clients are notified by the database function (see DBMSProcessor.setUp). | |