Verified Owner
Top Description Inners Fields Constructors Methods
org.jabref.logic.shared

public Class OfflineChanges

extends Object
Class Inheritance
Annotations
@NullMarked
Imports
java.io.IOException, java.nio.charset.StandardCharsets, java.nio.file.Files, .Path, .StandardCopyOption, java.util.Collection, .LinkedHashMap, .List, .Map, .Objects, .Optional, .TreeMap, java.util.stream.Stream, org.jabref.model.entry.BibEntry, org.jabref.model.entry.event.EntriesEventSource, org.jabref.model.entry.field.Field, .FieldFactory, org.jabref.model.entry.types.EntryTypeFactory, com.google.common.hash.Hashing, com.google.gson.Gson, .GsonBuilder, .JsonParseException, org.jspecify.annotations.NullMarked, .Nullable, org.slf4j.Logger, .LoggerFactory

Local changes that have not reached the shared database because the connection was down. Kept in memory and mirrored to one file per database, so that they survive a restart and are synchronized on the next connect (see DBMSSynchronizer).

Nested and Inner Type Summary

Modifier and TypeClass and Description
public static record
OfflineChanges.EntryState

An entry as last seen locally.

public static record
OfflineChanges.Recorded

Everything recorded up to a peek.

Field Summary

Modifier and TypeField and Description
private final Map<Integer, OfflineChanges.EntryState>
private final Path
private static final Gson
private static final Logger
private @Nullable Map<String, String>
private @Nullable Map<String, String>
private final Map<String, OfflineChanges.EntryState>
private final Map<Integer, Integer>

Constructor Summary

AccessConstructor and Description
private

Method Summary

Modifier and TypeMethod and Description
pack-priv static String
fileName(DatabaseConnectionProperties properties)

One file per database: identified by user, host, port and database name (or the JDBC URL in expert mode), hashed so that the name is file-system safe

public synchronized void
forget(Collection<BibEntry> entries)

Drops the records of entries that reached the shared database after all

public synchronized void
forgetMetaData()

Drops the recorded metadata after it reached the shared database

public synchronized void
forgetRemovals(Collection<Integer> sharedIds)

Drops the records of removals that reached the shared database (or need not, because the shared entry is gone or was kept)

public synchronized boolean
public static OfflineChanges
load(Path directory, DatabaseConnectionProperties properties)

Loads the changes recorded for the given database, if any

public synchronized OfflineChanges.Recorded
peek()

Hands out everything recorded so far.

public synchronized void
recordAsNew(int sharedId, BibEntry entry)

Turns the record of a change into the record of a new entry, because the shared entry it was recorded against is gone and the entry is inserted afresh instead.

public synchronized void
public synchronized void
public synchronized void
recordMetaData(Map<String, String> serializedMetaData, @Nullable Map<String, String>
the shared metadata as last known - kept from the first record, the metadata cannot move on while offline
base
)

public synchronized void
public synchronized void
rekeyInserts(Map<String, BibEntry> restoredEntriesByRecordedId)

Re-keys the records of new entries restored after a restart under fresh local ids, so that a failed insert records them under the ids it knows instead of next to the old records

private void
reload()

Takes over what is on disk, so that a change is never applied to a snapshot another JabRef session has meanwhile written to.

private void
save()

private OfflineChanges.Recorded
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

changedEntriesback to summary
private final Map<Integer, OfflineChanges.EntryState> changedEntries
fileback to summary
private final Path file
GSONback to summary
private static final Gson GSON
LOGGERback to summary
private static final Logger LOGGER
metaDataback to summary
private @Nullable Map<String, String> metaData
metaDataBaseback to summary
private @Nullable Map<String, String> metaDataBase
newEntriesback to summary
private final Map<String, OfflineChanges.EntryState> newEntries
removedEntriesback to summary
private final Map<Integer, Integer> removedEntries

Constructor Detail

OfflineChangesback to summary
private OfflineChanges(Path file)

Method Detail

fileNameback to summary
pack-priv static String fileName(DatabaseConnectionProperties properties)

One file per database: identified by user, host, port and database name (or the JDBC URL in expert mode), hashed so that the name is file-system safe

forgetback to summary
public synchronized void forget(Collection<BibEntry> entries)

Drops the records of entries that reached the shared database after all

forgetMetaDataback to summary
public synchronized void forgetMetaData()

Drops the recorded metadata after it reached the shared database

forgetRemovalsback to summary
public synchronized void forgetRemovals(Collection<Integer> sharedIds)

Drops the records of removals that reached the shared database (or need not, because the shared entry is gone or was kept)

isEmptyback to summary
public synchronized boolean isEmpty()
loadback to summary
public static OfflineChanges load(Path directory, DatabaseConnectionProperties properties)

Loads the changes recorded for the given database, if any

peekback to summary
public synchronized OfflineChanges.Recorded peek()

Hands out everything recorded so far. The records are kept until each of them is written (see the forget methods), so that nothing is lost when the replay fails or is interrupted.

recordAsNewback to summary
public synchronized void recordAsNew(int sharedId, BibEntry entry)

Turns the record of a change into the record of a new entry, because the shared entry it was recorded against is gone and the entry is inserted afresh instead. One step, so that no moment exists in which the entry is recorded as neither.

recordChangeback to summary
public synchronized void recordChange(BibEntry entry)
recordInsertback to summary
public synchronized void recordInsert(List<BibEntry> entries)
recordMetaDataback to summary
public synchronized void recordMetaData(Map<String, String> serializedMetaData, @Nullable Map<String, String> base)
Parameters
base:@Nullable Map<String, String>

the shared metadata as last known - kept from the first record, the metadata cannot move on while offline

recordRemovalback to summary
public synchronized void recordRemoval(List<BibEntry> entries)
rekeyInsertsback to summary
public synchronized void rekeyInserts(Map<String, BibEntry> restoredEntriesByRecordedId)

Re-keys the records of new entries restored after a restart under fresh local ids, so that a failed insert records them under the ids it knows instead of next to the old records

reloadback to summary
private void reload()

Takes over what is on disk, so that a change is never applied to a snapshot another JabRef session has meanwhile written to. Called before every modification.

ponytail: read and write are not one atomic step - two sessions recording at the very same moment can still lose a record; a lock file would close that window

saveback to summary
private void save()
snapshotback to summary
private OfflineChanges.Recorded snapshot()
org.jabref.logic.shared back to summary

public final Record OfflineChanges.EntryState

extends Record
Class Inheritance
Record Components
baseVersion:int 
entryType:String 
fields:Map<String, String> 

An entry as last seen locally. baseVersion is the shared version the change was made against: the optimistic lock needs it to notice that the shared entry moved on meanwhile.

Field Summary

Modifier and TypeField and Description
private final int
baseVersion

Record Component accessed by baseVersion().

private final String
entryType

Record Component accessed by entryType().

private final Map<String, String>
fields

Record Component accessed by fields().

Constructor Summary

AccessConstructor and Description
public
EntryState(int baseVersion, String entryType, Map<String, String> fields)

Method Summary

Modifier and TypeMethod and Description
public void
applyTo(BibEntry entry)

Restores this state on the given entry without triggering synchronization

public int
baseVersion()

Record Component getter of baseVersion.

public String
entryType()

Record Component getter of entryType.

public final boolean
equals(Object o)

Implements abstract java.lang.Record.equals.

Indicates whether some other object is "equal to" this one.

public Map<String, String>
fields()

Record Component getter of fields.

public final int
hashCode()

Implements abstract java.lang.Record.hashCode.

Returns a hash code value for the record.

pack-priv static OfflineChanges.EntryState
of(BibEntry entry)

public BibEntry
public final String
toString()

Implements abstract java.lang.Record.toString.

Returns a string representation of the record.

Field Detail

baseVersionback to summary
private final int baseVersion

Record Component accessed by baseVersion().

entryTypeback to summary
private final String entryType

Record Component accessed by entryType().

fieldsback to summary
private final Map<String, String> fields

Record Component accessed by fields().

Constructor Detail

EntryStateback to summary
public EntryState(int baseVersion, String entryType, Map<String, String> fields)

Method Detail

applyToback to summary
public void applyTo(BibEntry entry)

Restores this state on the given entry without triggering synchronization

baseVersionback to summary
public int baseVersion()

Record Component getter of baseVersion.

entryTypeback to summary
public String entryType()

Record Component getter of entryType.

equalsback to summary
public final boolean equals(Object o)

Implements abstract java.lang.Record.equals.

Doc from java.lang.Record.equals.

Indicates whether some other object is "equal to" this one. In addition to the general contract of Object.equals, record classes must further obey the invariant that when a record instance is "copied" by passing the result of the record component accessor methods to the canonical constructor, as follows:

    R copy = new R(r.c1(), r.c2(), ..., r.cn());
then it must be the case that r.equals(copy).
fieldsback to summary
public Map<String, String> fields()

Record Component getter of fields.

hashCodeback to summary
public final int hashCode()

Implements abstract java.lang.Record.hashCode.

Doc from java.lang.Record.hashCode.

Returns a hash code value for the record. Obeys the general contract of Object.hashCode. For records, hashing behavior is constrained by the refined contract of Record.equals, so that any two records created from the same components must have the same hash code.

ofback to summary
pack-priv static OfflineChanges.EntryState of(BibEntry entry)
toBibEntryback to summary
public BibEntry toBibEntry()
toStringback to summary
public final String toString()

Implements abstract java.lang.Record.toString.

Doc from java.lang.Record.toString.

Returns a string representation of the record. In accordance with the general contract of Object#toString(), the toString method returns a string that "textually represents" this record. The result should be a concise but informative representation that is easy for a person to read.

In addition to this general contract, record classes must further participate in the invariant that any two records which are equal must produce equal strings. This invariant is necessarily relaxed in the rare case where corresponding equal component values might fail to produce equal strings for themselves.

org.jabref.logic.shared back to summary

public final Record OfflineChanges.Recorded

extends Record
Class Inheritance
Record Components
changedEntries:Map<Integer, OfflineChanges.EntryState> 
newEntries:Map<String, OfflineChanges.EntryState> 
removedEntries:Map<Integer, Integer>
the shared version each removed entry had when it was removed: the optimistic lock needs it to notice that the shared entry moved on
metaData:@Nullable Map<String, String> 
metaDataBase:@Nullable Map<String, String>
the shared metadata as last known before the recorded metadata was changed - the merge base for mergeMetaDataInto

Everything recorded up to a peek. New entries are keyed by their local entry id, so that a reconnect without restart finds them in the local library.

Field Summary

Modifier and TypeField and Description
private final Map<Integer, OfflineChanges.EntryState>
changedEntries

Record Component accessed by changedEntries().

private final @Nullable Map<String, String>
metaData

Record Component accessed by metaData().

private final @Nullable Map<String, String>
metaDataBase

Record Component accessed by metaDataBase().

the shared metadata as last known before the recorded metadata was changed - the merge base for mergeMetaDataInto
private final Map<String, OfflineChanges.EntryState>
newEntries

Record Component accessed by newEntries().

private final Map<Integer, Integer>
removedEntries

Record Component accessed by removedEntries().

the shared version each removed entry had when it was removed: the optimistic lock needs it to notice that the shared entry moved on

Constructor Summary

AccessConstructor and Description
public
Recorded(Map<Integer, OfflineChanges.EntryState> changedEntries, Map<String, OfflineChanges.EntryState> newEntries, Map<Integer, Integer> removedEntries, @Nullable Map<String, String> metaData, @Nullable Map<String, String> metaDataBase)

Method Summary

Modifier and TypeMethod and Description
public Map<Integer, OfflineChanges.EntryState>
changedEntries()

Record Component getter of changedEntries.

public final boolean
equals(Object o)

Implements abstract java.lang.Record.equals.

Indicates whether some other object is "equal to" this one.

public final int
hashCode()

Implements abstract java.lang.Record.hashCode.

Returns a hash code value for the record.

public boolean
public Map<String, String>
mergeMetaDataInto(Map<String, String> sharedMetaData)

Metadata has no version to lock on, so the recorded metadata is merged key by key: a key the user changed while offline wins (also over a concurrent change on the shared side - the user was promised that these changes are kept), every other key stays as the shared side has it now.

public @Nullable Map<String, String>
metaData()

Record Component getter of metaData.

public @Nullable Map<String, String>
metaDataBase()

Record Component getter of metaDataBase.

Record Component the shared metadata as last known before the recorded metadata was changed - the merge base for mergeMetaDataInto

public Map<String, OfflineChanges.EntryState>
newEntries()

Record Component getter of newEntries.

public Map<Integer, Integer>
removedEntries()

Record Component getter of removedEntries.

Record Component the shared version each removed entry had when it was removed: the optimistic lock needs it to notice that the shared entry moved on

public final String
toString()

Implements abstract java.lang.Record.toString.

Returns a string representation of the record.

Field Detail

changedEntriesback to summary
private final Map<Integer, OfflineChanges.EntryState> changedEntries

Record Component accessed by changedEntries().

metaDataback to summary
private final @Nullable Map<String, String> metaData

Record Component accessed by metaData().

metaDataBaseback to summary
private final @Nullable Map<String, String> metaDataBase

Record Component accessed by metaDataBase().

the shared metadata as last known before the recorded metadata was changed - the merge base for mergeMetaDataInto

newEntriesback to summary
private final Map<String, OfflineChanges.EntryState> newEntries

Record Component accessed by newEntries().

removedEntriesback to summary
private final Map<Integer, Integer> removedEntries

Record Component accessed by removedEntries().

the shared version each removed entry had when it was removed: the optimistic lock needs it to notice that the shared entry moved on

Constructor Detail

Recordedback to summary
public Recorded(Map<Integer, OfflineChanges.EntryState> changedEntries, Map<String, OfflineChanges.EntryState> newEntries, Map<Integer, Integer> removedEntries, @Nullable Map<String, String> metaData, @Nullable Map<String, String> metaDataBase)

Method Detail

changedEntriesback to summary
public Map<Integer, OfflineChanges.EntryState> changedEntries()

Record Component getter of changedEntries.

equalsback to summary
public final boolean equals(Object o)

Implements abstract java.lang.Record.equals.

Doc from java.lang.Record.equals.

Indicates whether some other object is "equal to" this one. In addition to the general contract of Object.equals, record classes must further obey the invariant that when a record instance is "copied" by passing the result of the record component accessor methods to the canonical constructor, as follows:

    R copy = new R(r.c1(), r.c2(), ..., r.cn());
then it must be the case that r.equals(copy).
hashCodeback to summary
public final int hashCode()

Implements abstract java.lang.Record.hashCode.

Doc from java.lang.Record.hashCode.

Returns a hash code value for the record. Obeys the general contract of Object.hashCode. For records, hashing behavior is constrained by the refined contract of Record.equals, so that any two records created from the same components must have the same hash code.

isEmptyback to summary
public boolean isEmpty()
mergeMetaDataIntoback to summary
public Map<String, String> mergeMetaDataInto(Map<String, String> sharedMetaData)

Metadata has no version to lock on, so the recorded metadata is merged key by key: a key the user changed while offline wins (also over a concurrent change on the shared side - the user was promised that these changes are kept), every other key stays as the shared side has it now.

metaDataback to summary
public @Nullable Map<String, String> metaData()

Record Component getter of metaData.

metaDataBaseback to summary
public @Nullable Map<String, String> metaDataBase()

Record Component getter of metaDataBase.

Record Component
the shared metadata as last known before the recorded metadata was changed - the merge base for mergeMetaDataInto
newEntriesback to summary
public Map<String, OfflineChanges.EntryState> newEntries()

Record Component getter of newEntries.

removedEntriesback to summary
public Map<Integer, Integer> removedEntries()

Record Component getter of removedEntries.

Record Component
the shared version each removed entry had when it was removed: the optimistic lock needs it to notice that the shared entry moved on
toStringback to summary
public final String toString()

Implements abstract java.lang.Record.toString.

Doc from java.lang.Record.toString.

Returns a string representation of the record. In accordance with the general contract of Object#toString(), the toString method returns a string that "textually represents" this record. The result should be a concise but informative representation that is easy for a person to read.

In addition to this general contract, record classes must further participate in the invariant that any two records which are equal must produce equal strings. This invariant is necessarily relaxed in the rare case where corresponding equal component values might fail to produce equal strings for themselves.