Add more protocol stuff
This commit is contained in:
parent
72b4e547fb
commit
e802d50863
@ -2,4 +2,4 @@ package me.mrletsplay.shareclientcore.connection;
|
|||||||
|
|
||||||
import me.mrletsplay.shareclientcore.document.Char;
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
|
|
||||||
public record Change(int document, ChangeType type, Char character) {}
|
public record Change(String documentPath, ChangeType type, Char character) {}
|
||||||
|
@ -9,6 +9,11 @@ public class DummyConnection implements RemoteConnection {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSiteID() {
|
public int getSiteID() {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -11,6 +11,8 @@ public interface RemoteConnection {
|
|||||||
|
|
||||||
public void connect(String sessionID) throws ConnectionException;
|
public void connect(String sessionID) throws ConnectionException;
|
||||||
|
|
||||||
|
public void disconnect();
|
||||||
|
|
||||||
public int getSiteID();
|
public int getSiteID();
|
||||||
|
|
||||||
public void send(Message message) throws ConnectionException;
|
public void send(Message message) throws ConnectionException;
|
||||||
|
@ -11,6 +11,7 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.java_websocket.client.WebSocketClient;
|
import org.java_websocket.client.WebSocketClient;
|
||||||
|
import org.java_websocket.framing.CloseFrame;
|
||||||
import org.java_websocket.handshake.ServerHandshake;
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
|
|
||||||
import me.mrletsplay.shareclientcore.connection.message.ClientHelloMessage;
|
import me.mrletsplay.shareclientcore.connection.message.ClientHelloMessage;
|
||||||
@ -43,14 +44,22 @@ public class WebSocketConnection implements RemoteConnection {
|
|||||||
try {
|
try {
|
||||||
if(!client.connectBlocking(30, TimeUnit.SECONDS)) throw new IOException("Failed to connect to WebSocket server");
|
if(!client.connectBlocking(30, TimeUnit.SECONDS)) throw new IOException("Failed to connect to WebSocket server");
|
||||||
send(new ClientHelloMessage(username, sessionID));
|
send(new ClientHelloMessage(username, sessionID));
|
||||||
wait.wait(30_000L);
|
synchronized(wait) { wait.wait(30_000L); }
|
||||||
if(!helloReceived) throw new ConnectionException("Server did not send hello");
|
if(!helloReceived) {
|
||||||
|
client.close();
|
||||||
|
throw new ConnectionException("Server did not send hello");
|
||||||
|
}
|
||||||
if(connectException != null) throw connectException;
|
if(connectException != null) throw connectException;
|
||||||
} catch (InterruptedException | IOException e) {
|
} catch (InterruptedException | IOException e) {
|
||||||
throw new ConnectionException("Failed to establish connection", e);
|
throw new ConnectionException("Failed to establish connection", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
client.close(CloseFrame.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSiteID() {
|
public int getSiteID() {
|
||||||
return siteID;
|
return siteID;
|
||||||
@ -120,7 +129,7 @@ public class WebSocketConnection implements RemoteConnection {
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
wait.notifyAll();
|
synchronized(wait) { wait.notifyAll(); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
public interface AddressableMessage extends Message {
|
||||||
|
|
||||||
|
public int siteID();
|
||||||
|
|
||||||
|
}
|
@ -17,14 +17,14 @@ public record ChangeMessage(Change change) implements Message {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(DataOutputStream out) throws IOException {
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
out.writeInt(change.document());
|
out.writeUTF(change.documentPath());
|
||||||
out.writeUTF(change.type().name());
|
out.writeUTF(change.type().name());
|
||||||
change.character().serialize(out);
|
change.character().serialize(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChangeMessage deserialize(DataInputStream in) throws IOException {
|
public static ChangeMessage deserialize(DataInputStream in) throws IOException {
|
||||||
try {
|
try {
|
||||||
return new ChangeMessage(new Change(in.readInt(), ChangeType.valueOf(in.readUTF()), Char.deserialize(in)));
|
return new ChangeMessage(new Change(in.readUTF(), ChangeType.valueOf(in.readUTF()), Char.deserialize(in)));
|
||||||
}catch(IllegalArgumentException e) {
|
}catch(IllegalArgumentException e) {
|
||||||
throw new IOException("Invalid change type", e);
|
throw new IOException("Invalid change type", e);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record ChecksumMessage(int siteID, String documentPath, byte[] checksum) implements AddressableMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.CHECKSUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(siteID);
|
||||||
|
out.writeUTF(documentPath);
|
||||||
|
out.writeInt(checksum.length);
|
||||||
|
out.write(checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChecksumMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
try {
|
||||||
|
return new ChecksumMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt()));
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
throw new IOException("Invalid checksum length", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record FullSyncMessage(int siteID, String documentPath, byte[] content) implements AddressableMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.FULL_SYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(siteID);
|
||||||
|
out.writeUTF(documentPath);
|
||||||
|
out.writeInt(content.length);
|
||||||
|
out.write(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FullSyncMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
try {
|
||||||
|
return new FullSyncMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt()));
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
throw new IOException("Invalid content length", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record RequestChecksumMessage(int siteID, String documentPath) implements AddressableMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.REQUEST_CHECKSUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(siteID);
|
||||||
|
out.writeUTF(documentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequestChecksumMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new RequestChecksumMessage(in.readInt(), in.readUTF());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record RequestFullSyncMessage(int siteID, String documentPath) implements AddressableMessage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.REQUEST_FULL_SYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(siteID);
|
||||||
|
out.writeBoolean(documentPath != null);
|
||||||
|
if(documentPath != null) out.writeUTF(documentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequestFullSyncMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new RequestFullSyncMessage(in.readInt(), in.readBoolean() ? in.readUTF() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,12 +16,12 @@ public class SharedDocument implements MessageListener {
|
|||||||
|
|
||||||
private RemoteConnection connection;
|
private RemoteConnection connection;
|
||||||
private CharBag charBag;
|
private CharBag charBag;
|
||||||
private int document;
|
private String path;
|
||||||
private int site;
|
private int site;
|
||||||
private int lamport;
|
private int lamport;
|
||||||
private Set<DocumentListener> listeners;
|
private Set<DocumentListener> listeners;
|
||||||
|
|
||||||
public SharedDocument(RemoteConnection connection) {
|
public SharedDocument(RemoteConnection connection, String path) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
connection.addListener(this);
|
connection.addListener(this);
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ public class SharedDocument implements MessageListener {
|
|||||||
charBag.add(Char.START_OF_DOCUMENT);
|
charBag.add(Char.START_OF_DOCUMENT);
|
||||||
charBag.add(Char.END_OF_DOCUMENT);
|
charBag.add(Char.END_OF_DOCUMENT);
|
||||||
|
|
||||||
this.document = 0; // TODO: implement
|
this.path = path;
|
||||||
this.site = connection.getSiteID();
|
this.site = connection.getSiteID();
|
||||||
this.listeners = new HashSet<>();
|
this.listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ public class SharedDocument implements MessageListener {
|
|||||||
lamport++;
|
lamport++;
|
||||||
Char ch = new Char(newPos, lamport, chars[i]);
|
Char ch = new Char(newPos, lamport, chars[i]);
|
||||||
charBag.add(ch);
|
charBag.add(ch);
|
||||||
changes[i] = new Change(document, ChangeType.ADD, ch);
|
changes[i] = new Change(path, ChangeType.ADD, ch);
|
||||||
charBefore = ch;
|
charBefore = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ public class SharedDocument implements MessageListener {
|
|||||||
while(n-- > 0) {
|
while(n-- > 0) {
|
||||||
// TODO: more efficient implementation (e.g. range delete in CharBag)
|
// TODO: more efficient implementation (e.g. range delete in CharBag)
|
||||||
Char toRemove = charBag.get(index + 1);
|
Char toRemove = charBag.get(index + 1);
|
||||||
changes[n] = new Change(document, ChangeType.REMOVE, toRemove);
|
changes[n] = new Change(path, ChangeType.REMOVE, toRemove);
|
||||||
charBag.remove(toRemove);
|
charBag.remove(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +139,8 @@ public class SharedDocument implements MessageListener {
|
|||||||
public void onMessage(Message message) {
|
public void onMessage(Message message) {
|
||||||
if(message instanceof ChangeMessage change) {
|
if(message instanceof ChangeMessage change) {
|
||||||
Change c = change.change();
|
Change c = change.change();
|
||||||
|
if(!c.documentPath().equals(path)) return;
|
||||||
|
|
||||||
System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position()));
|
System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position()));
|
||||||
switch(c.type()) {
|
switch(c.type()) {
|
||||||
case ADD -> remoteInsert(c.character());
|
case ADD -> remoteInsert(c.character());
|
||||||
|
@ -12,7 +12,7 @@ public class DocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalInsert() {
|
public void testLocalInsert() {
|
||||||
SharedDocument doc = new SharedDocument(new DummyConnection());
|
SharedDocument doc = new SharedDocument(new DummyConnection(), "test");
|
||||||
doc.localInsert(0, "Hello");
|
doc.localInsert(0, "Hello");
|
||||||
assertEquals("Hello", doc.getContents());
|
assertEquals("Hello", doc.getContents());
|
||||||
doc.localInsert(5, " World");
|
doc.localInsert(5, " World");
|
||||||
@ -23,7 +23,7 @@ public class DocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalInsertInvalidIndexFails() {
|
public void testLocalInsertInvalidIndexFails() {
|
||||||
SharedDocument doc = new SharedDocument(new DummyConnection());
|
SharedDocument doc = new SharedDocument(new DummyConnection(), "test");
|
||||||
doc.localInsert(0, "Hello");
|
doc.localInsert(0, "Hello");
|
||||||
assertThrows(IllegalArgumentException.class, () -> doc.localInsert(-1, "Test"));
|
assertThrows(IllegalArgumentException.class, () -> doc.localInsert(-1, "Test"));
|
||||||
assertThrows(IllegalArgumentException.class, () -> doc.localInsert(6, "Test"));
|
assertThrows(IllegalArgumentException.class, () -> doc.localInsert(6, "Test"));
|
||||||
@ -31,7 +31,7 @@ public class DocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalDelete() {
|
public void testLocalDelete() {
|
||||||
SharedDocument doc = new SharedDocument(new DummyConnection());
|
SharedDocument doc = new SharedDocument(new DummyConnection(), "test");
|
||||||
doc.localInsert(0, "Hello World!");
|
doc.localInsert(0, "Hello World!");
|
||||||
doc.localDelete(5, 6);
|
doc.localDelete(5, 6);
|
||||||
assertEquals("Hello!", doc.getContents());
|
assertEquals("Hello!", doc.getContents());
|
||||||
@ -39,7 +39,7 @@ public class DocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalDeleteInvalidIndexFails() {
|
public void testLocalDeleteInvalidIndexFails() {
|
||||||
SharedDocument doc = new SharedDocument(new DummyConnection());
|
SharedDocument doc = new SharedDocument(new DummyConnection(), "test");
|
||||||
doc.localInsert(0, "Hello World!");
|
doc.localInsert(0, "Hello World!");
|
||||||
assertThrows(IllegalArgumentException.class, () -> doc.localDelete(-1, 10));
|
assertThrows(IllegalArgumentException.class, () -> doc.localDelete(-1, 10));
|
||||||
assertThrows(IllegalArgumentException.class, () -> doc.localDelete(12, 1));
|
assertThrows(IllegalArgumentException.class, () -> doc.localDelete(12, 1));
|
||||||
|
Loading…
Reference in New Issue
Block a user