Class JniYDoc
- All Implemented Interfaces:
AutoCloseable,net.carcdr.ycrdt.YDoc
This class wraps the native Rust implementation of y-crdt (yrs) and provides a Java API for working with collaborative documents.
Usage example:
try (YDoc doc = new JniYDoc()) {
long clientId = doc.getClientId();
String guid = doc.getGuid();
// Get document state as update
byte[] state = doc.encodeStateAsUpdate();
// Apply update to another document
try (YDoc doc2 = new JniYDoc()) {
doc2.applyUpdate(state);
}
}
Thread Safety: JniYDoc instances are not thread-safe. If you need to access a document from multiple threads, you must provide external synchronization.
Memory Management: JniYDoc implements Closeable and must be closed
when no longer needed to free native resources. Use try-with-resources to ensure
proper cleanup.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidapplyUpdate(byte[] update) Applies a binary update to this document (creates implicit transaction).voidapplyUpdate(net.carcdr.ycrdt.YTransaction txn, byte[] update) Applies a binary update to this document within an existing transaction.Begin a new transaction for batching operations.voidclose()Closes this document and frees its native resources.byte[]encodeDiff(byte[] stateVector) Encodes a differential update containing only changes not yet observed by the remote peer (creates implicit transaction).byte[]encodeDiff(net.carcdr.ycrdt.YTransaction txn, byte[] stateVector) Encodes a differential update containing only changes not yet observed by the remote peer within an existing transaction.byte[]Encodes the current state of the document as a binary update (creates implicit transaction).byte[]encodeStateAsUpdate(net.carcdr.ycrdt.YTransaction txn) Encodes the current state of the document as a binary update within an existing transaction.byte[]Encodes the current state vector of this document (creates implicit transaction).byte[]encodeStateVector(net.carcdr.ycrdt.YTransaction txn) Encodes the current state vector of this document within an existing transaction.static byte[]encodeStateVectorFromUpdate(byte[] update) Extracts the state vector from an encoded update without applying it.Gets or creates a YArray instance with the specified name.longGets the client ID of this document.getGuid()Gets the globally unique identifier (GUID) of this document.Gets or creates a YMap instance with the specified name.net.carcdr.ycrdt.ObserverErrorHandlerGets the current error handler for observer exceptions.Gets or creates a YText instance with the specified name.getXmlElement(String name) Gets or creates a YXmlElement instance with the specified name.getXmlFragment(String name) Gets or creates a YXmlFragment instance with the specified name.getXmlText(String name) Gets or creates a YXmlText instance with the specified name.booleanisClosed()Checks if this document has been closed.static byte[]mergeUpdates(byte[][] updates) Merges multiple updates into a single compact update.net.carcdr.ycrdt.YSubscriptionobserveUpdateV1(net.carcdr.ycrdt.UpdateObserver observer) Observes all updates to this document.voidsetObserverErrorHandler(net.carcdr.ycrdt.ObserverErrorHandler handler) Sets the error handler for observer exceptions.voidtransaction(Consumer<net.carcdr.ycrdt.YTransaction> fn) Execute operations within a transaction using a callback.voidunobserveById(long subscriptionId) Unregisters an update observer by subscription ID.
-
Constructor Details
-
JniYDoc
public JniYDoc()Creates a new JniYDoc instance with a randomly generated client ID.- Throws:
RuntimeException- if native initialization fails
-
JniYDoc
public JniYDoc(long clientId) Creates a new JniYDoc instance with a specific client ID.The client ID is used to identify the source of changes in the CRDT. Each client should have a unique ID to prevent conflicts.
- Parameters:
clientId- the client ID to assign to this document- Throws:
RuntimeException- if native initialization failsIllegalArgumentException- if clientId is negative
-
-
Method Details
-
getClientId
public long getClientId()Gets the client ID of this document.The client ID uniquely identifies this document instance in a distributed system. It is used to track the source of changes.
- Specified by:
getClientIdin interfacenet.carcdr.ycrdt.YDoc- Returns:
- the client ID
- Throws:
IllegalStateException- if this document has been closed
-
getGuid
Gets the globally unique identifier (GUID) of this document.The GUID is a UUID that uniquely identifies this document across all instances. All replicas of the same document share the same GUID.
- Specified by:
getGuidin interfacenet.carcdr.ycrdt.YDoc- Returns:
- the GUID as a string
- Throws:
IllegalStateException- if this document has been closed
-
encodeStateAsUpdate
public byte[] encodeStateAsUpdate(net.carcdr.ycrdt.YTransaction txn) Encodes the current state of the document as a binary update within an existing transaction.This method captures the entire state of the document and returns it as a byte array that can be transmitted to other clients or stored for later use.
The returned byte array can be applied to another YDoc instance using
applyUpdate(byte[])to synchronize their states.- Specified by:
encodeStateAsUpdatein interfacenet.carcdr.ycrdt.YDoc- Parameters:
txn- The transaction to use for this operation- Returns:
- a byte array containing the encoded document state
- Throws:
IllegalArgumentException- if txn is nullIllegalStateException- if this document has been closedRuntimeException- if encoding fails
-
encodeStateAsUpdate
public byte[] encodeStateAsUpdate()Encodes the current state of the document as a binary update (creates implicit transaction).This method captures the entire state of the document and returns it as a byte array that can be transmitted to other clients or stored for later use.
The returned byte array can be applied to another YDoc instance using
applyUpdate(byte[])to synchronize their states.- Specified by:
encodeStateAsUpdatein interfacenet.carcdr.ycrdt.YDoc- Returns:
- a byte array containing the encoded document state
- Throws:
IllegalStateException- if this document has been closedRuntimeException- if encoding fails
-
applyUpdate
public void applyUpdate(net.carcdr.ycrdt.YTransaction txn, byte[] update) Applies a binary update to this document within an existing transaction.This method merges changes from another document into this one. The update is typically obtained by calling
encodeStateAsUpdate()on another YDoc instance.Updates are idempotent - applying the same update multiple times has the same effect as applying it once.
- Specified by:
applyUpdatein interfacenet.carcdr.ycrdt.YDoc- Parameters:
txn- The transaction to use for this operationupdate- the binary update to apply- Throws:
IllegalArgumentException- if txn or update is nullIllegalStateException- if this document has been closedRuntimeException- if the update is invalid or cannot be applied
-
applyUpdate
public void applyUpdate(byte[] update) Applies a binary update to this document (creates implicit transaction).This method merges changes from another document into this one. The update is typically obtained by calling
encodeStateAsUpdate()on another YDoc instance.Updates are idempotent - applying the same update multiple times has the same effect as applying it once.
- Specified by:
applyUpdatein interfacenet.carcdr.ycrdt.YDoc- Parameters:
update- the binary update to apply- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if update is nullRuntimeException- if the update is invalid or cannot be applied
-
encodeStateVector
public byte[] encodeStateVector(net.carcdr.ycrdt.YTransaction txn) Encodes the current state vector of this document within an existing transaction.A state vector is a compact representation of all known blocks inserted and integrated into this document. It serves as a logical timestamp describing which updates this document has observed.
State vectors are used to generate differential updates - by sending a state vector to a remote peer, they can determine which changes this document has not yet seen and send only those changes.
- Specified by:
encodeStateVectorin interfacenet.carcdr.ycrdt.YDoc- Parameters:
txn- The transaction to use for this operation- Returns:
- a byte array containing the encoded state vector
- Throws:
IllegalArgumentException- if txn is nullIllegalStateException- if this document has been closedRuntimeException- if encoding fails
-
encodeStateVector
public byte[] encodeStateVector()Encodes the current state vector of this document (creates implicit transaction).A state vector is a compact representation of all known blocks inserted and integrated into this document. It serves as a logical timestamp describing which updates this document has observed.
State vectors are used to generate differential updates - by sending a state vector to a remote peer, they can determine which changes this document has not yet seen and send only those changes.
- Specified by:
encodeStateVectorin interfacenet.carcdr.ycrdt.YDoc- Returns:
- a byte array containing the encoded state vector
- Throws:
IllegalStateException- if this document has been closedRuntimeException- if encoding fails
-
encodeDiff
public byte[] encodeDiff(net.carcdr.ycrdt.YTransaction txn, byte[] stateVector) Encodes a differential update containing only changes not yet observed by the remote peer within an existing transaction.This method generates an update that includes only the changes that are present in this document but not reflected in the provided state vector. This is more efficient than sending the entire document state when synchronizing with a peer that already has some of the data.
Typical usage:
// Remote peer sends their state vector byte[] remoteStateVector = ...; try (YTransaction txn = doc.beginTransaction()) { // Generate differential update byte[] diff = doc.encodeDiff(txn, remoteStateVector); // Send diff to remote peer // Remote peer applies it with applyUpdate(diff) }- Specified by:
encodeDiffin interfacenet.carcdr.ycrdt.YDoc- Parameters:
txn- The transaction to use for this operationstateVector- the state vector from the remote peer- Returns:
- a byte array containing the differential update
- Throws:
IllegalArgumentException- if txn or stateVector is nullIllegalStateException- if this document has been closedRuntimeException- if encoding fails
-
encodeDiff
public byte[] encodeDiff(byte[] stateVector) Encodes a differential update containing only changes not yet observed by the remote peer (creates implicit transaction).This method generates an update that includes only the changes that are present in this document but not reflected in the provided state vector. This is more efficient than sending the entire document state when synchronizing with a peer that already has some of the data.
Typical usage:
// Remote peer sends their state vector byte[] remoteStateVector = ...; // Generate differential update byte[] diff = doc.encodeDiff(remoteStateVector); // Send diff to remote peer // Remote peer applies it with applyUpdate(diff)- Specified by:
encodeDiffin interfacenet.carcdr.ycrdt.YDoc- Parameters:
stateVector- the state vector from the remote peer- Returns:
- a byte array containing the differential update
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if stateVector is nullRuntimeException- if encoding fails
-
mergeUpdates
public static byte[] mergeUpdates(byte[][] updates) Merges multiple updates into a single compact update.This static method takes an array of updates and combines them into a single update that has the same effect as applying all the individual updates in sequence. This is useful for reducing network overhead and storage requirements.
The merged update is often smaller than the sum of the individual updates because redundant operations are eliminated during the merge process.
Example:
byte[] update1 = doc1.encodeStateAsUpdate(); byte[] update2 = doc2.encodeStateAsUpdate(); byte[] update3 = doc3.encodeStateAsUpdate(); // Merge into single update byte[] merged = JniYDoc.mergeUpdates(new byte[][]{update1, update2, update3}); // Apply merged update to target document targetDoc.applyUpdate(merged);- Parameters:
updates- array of updates to merge- Returns:
- a byte array containing the merged update
- Throws:
IllegalArgumentException- if updates is null or empty, or contains null elementsRuntimeException- if merging fails
-
encodeStateVectorFromUpdate
public static byte[] encodeStateVectorFromUpdate(byte[] update) Extracts the state vector from an encoded update without applying it.This method decodes an update and returns its state vector, which represents the logical timestamp of the document state after applying the update. This is useful for understanding what changes an update contains without actually applying it to a document.
Example:
byte[] update = remoteDoc.encodeStateAsUpdate(); // Check what state this update represents byte[] stateVector = JniYDoc.encodeStateVectorFromUpdate(update); // Can be used to determine if we need this update- Parameters:
update- the update to extract the state vector from- Returns:
- a byte array containing the encoded state vector
- Throws:
IllegalArgumentException- if update is nullRuntimeException- if extraction fails or update is invalid
-
getText
Gets or creates a YText instance with the specified name.This method returns a collaborative text type that can be shared between multiple clients. If a text with this name already exists in the document, it will be returned; otherwise, a new one will be created.
The returned YText instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YText text = doc.getText("mytext")) { text.insert(0, "Hello World"); System.out.println(text.toString()); }- Specified by:
getTextin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the text object- Returns:
- a YText instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if text creation fails
-
getArray
Gets or creates a YArray instance with the specified name.This method returns a collaborative array type that can be shared between multiple clients. If an array with this name already exists in the document, it will be returned; otherwise, a new one will be created.
The returned YArray instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YArray array = doc.getArray("myarray")) { array.pushString("Hello"); array.pushDouble(42.0); System.out.println(array.toJson()); }- Specified by:
getArrayin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the array object- Returns:
- a YArray instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if array creation fails
-
getMap
Gets or creates a YMap instance with the specified name.This method returns a collaborative map type that can be shared between multiple clients. If a map with this name already exists in the document, it will be returned; otherwise, a new one will be created.
The returned YMap instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YMap map = doc.getMap("mymap")) { map.setString("name", "Alice"); map.setDouble("age", 30.0); System.out.println(map.toJson()); }- Specified by:
getMapin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the map object- Returns:
- a YMap instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if map creation fails
-
getXmlText
Gets or creates a YXmlText instance with the specified name.This method returns a collaborative XML text type that can be shared between multiple clients. If an XML text with this name already exists in the document, it will be returned; otherwise, a new one will be created.
The returned YXmlText instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YXmlText xmlText = doc.getXmlText("myxmltext")) { xmlText.push("Hello"); System.out.println(xmlText.toString()); }- Specified by:
getXmlTextin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the XML text object- Returns:
- a YXmlText instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if XML text creation fails
-
getXmlElement
Gets or creates a YXmlElement instance with the specified name.This method returns a collaborative XML element type that can be shared between multiple clients. If an XML element with this name already exists in the document, it will be returned; otherwise, a new one will be created.
The returned YXmlElement instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YXmlElement element = doc.getXmlElement("div")) { element.setAttribute("class", "container"); System.out.println(element.getTag()); }- Specified by:
getXmlElementin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the XML element object- Returns:
- a YXmlElement instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if XML element creation fails
-
getXmlFragment
Gets or creates a YXmlFragment instance with the specified name.This method returns a collaborative XML fragment that can contain multiple XML nodes (elements and text) in a hierarchical structure. Fragments are the foundation for building XML trees and support full parent-child relationships.
The returned YXmlFragment instance must be closed when no longer needed to free native resources. Use try-with-resources for automatic cleanup.
Example:
try (YDoc doc = new JniYDoc(); YXmlFragment fragment = doc.getXmlFragment("document")) { fragment.insertElement(0, "div"); fragment.insertText(1, "Hello World"); System.out.println(fragment.toXmlString()); }- Specified by:
getXmlFragmentin interfacenet.carcdr.ycrdt.YDoc- Parameters:
name- the name of the XML fragment- Returns:
- a YXmlFragment instance
- Throws:
IllegalStateException- if this document has been closedIllegalArgumentException- if name is nullRuntimeException- if XML fragment creation fails- Since:
- 0.2.0
-
beginTransaction
Begin a new transaction for batching operations.Transactions allow multiple CRDT operations to be batched together, resulting in better performance, single observer notifications, and more efficient update encoding for synchronization.
Use with try-with-resources for automatic cleanup:
try (YTransaction txn = doc.beginTransaction()) { text.insert(txn, 0, "Hello"); array.pushString(txn, "World"); } // Auto-commitsThe transaction must be closed (either explicitly or via try-with-resources) to commit the changes. Uncommitted transactions will be automatically committed when the transaction object is garbage collected, but this is not recommended.
- Specified by:
beginTransactionin interfacenet.carcdr.ycrdt.YDoc- Returns:
- transaction handle (use with try-with-resources)
- Throws:
IllegalStateException- if this document has been closedRuntimeException- if transaction creation fails- See Also:
-
transaction
Execute operations within a transaction using a callback.This is a convenience method that automatically manages transaction lifecycle. The transaction is automatically committed when the callback completes successfully, or rolled back if an exception is thrown.
Convenience method for simple transaction usage:
doc.transaction(txn -> { text.insert(txn, 0, "Hello"); array.pushString(txn, "World"); });- Specified by:
transactionin interfacenet.carcdr.ycrdt.YDoc- Parameters:
fn- function receiving transaction handle- Throws:
IllegalStateException- if this document has been closedRuntimeException- if transaction creation fails or fn throws an exception- See Also:
-
observeUpdateV1
public net.carcdr.ycrdt.YSubscription observeUpdateV1(net.carcdr.ycrdt.UpdateObserver observer) Observes all updates to this document.The observer will be called whenever any Y type within this document is modified, providing the binary-encoded update. This is useful for:
- Persisting document changes
- Broadcasting updates to remote peers
- Logging or auditing changes
- Triggering side effects on document changes
Example usage:
try (YDoc doc = new JniYDoc()) { UpdateObserver observer = (update, origin) -> { System.out.println("Document updated: " + update.length + " bytes"); // Persist to database, broadcast to peers, etc. }; try (YSubscription sub = doc.observeUpdateV1(observer)) { try (YText text = doc.getText("mytext")) { text.insert(0, "Hello"); // Triggers observer } } }Important: The observer is called synchronously on the thread that modifies the document. Observers should perform minimal work to avoid blocking document operations. For expensive operations, schedule them asynchronously.
Reentrancy Warning: Observers should NOT modify the same document that triggered the callback, as this may cause undefined behavior or deadlocks.
- Specified by:
observeUpdateV1in interfacenet.carcdr.ycrdt.YDoc- Parameters:
observer- the observer to register- Returns:
- a subscription that can be closed to unregister the observer
- Throws:
IllegalArgumentException- if observer is nullIllegalStateException- if this document has been closed- See Also:
-
unobserveById
public void unobserveById(long subscriptionId) Unregisters an update observer by subscription ID.This is called automatically when a YSubscription is closed. You typically don't need to call this directly.
- Parameters:
subscriptionId- the subscription ID to remove
-
setObserverErrorHandler
public void setObserverErrorHandler(net.carcdr.ycrdt.ObserverErrorHandler handler) Sets the error handler for observer exceptions.When an observer throws an exception, this handler will be called instead of letting the exception propagate. The default handler prints to stderr for backwards compatibility.
- Specified by:
setObserverErrorHandlerin interfacenet.carcdr.ycrdt.YDoc- Parameters:
handler- the error handler to use, or null to use the default handler- See Also:
-
getObserverErrorHandler
public net.carcdr.ycrdt.ObserverErrorHandler getObserverErrorHandler()Gets the current error handler for observer exceptions.- Specified by:
getObserverErrorHandlerin interfacenet.carcdr.ycrdt.YDoc- Returns:
- the current error handler (never null)
-
close
public void close()Closes this document and frees its native resources.After calling this method, any further operations on this document will throw
IllegalStateException.This method is idempotent - calling it multiple times is safe.
- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfacenet.carcdr.ycrdt.YDoc
-
isClosed
public boolean isClosed()Checks if this document has been closed.- Specified by:
isClosedin interfacenet.carcdr.ycrdt.YDoc- Returns:
- true if this document has been closed, false otherwise
-