From 72b4e547fbd5d718de29200284a02bde4ee4a6c7 Mon Sep 17 00:00:00 2001 From: MrLetsplay Date: Sun, 3 Dec 2023 21:31:27 +0100 Subject: [PATCH] Basic protocol --- .../connection/ConnectionException.java | 25 +++ .../connection/DummyConnection.java | 12 +- .../connection/MessageListener.java | 12 ++ .../connection/RemoteConnection.java | 14 +- .../connection/RemoteListener.java | 10 -- .../connection/SerializableObject.java | 10 ++ .../connection/WebSocketConnection.java | 142 +++++++++--------- .../connection/message/BasicMessage.java | 16 ++ .../connection/message/ChangeMessage.java | 33 ++++ .../message/ClientHelloMessage.java | 24 +++ .../connection/message/Message.java | 38 +++++ .../connection/message/MessageType.java | 61 ++++++++ .../connection/message/PeerJoinMessage.java | 24 +++ .../connection/message/PeerLeaveMessage.java | 23 +++ .../message/ServerHelloMessage.java | 24 +++ .../shareclientcore/document/Char.java | 31 +++- .../document/SharedDocument.java | 31 +++- 17 files changed, 427 insertions(+), 103 deletions(-) create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/ConnectionException.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/MessageListener.java delete mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/RemoteListener.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/SerializableObject.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/BasicMessage.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/ChangeMessage.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/ClientHelloMessage.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/MessageType.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerJoinMessage.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerLeaveMessage.java create mode 100644 src/main/java/me/mrletsplay/shareclientcore/connection/message/ServerHelloMessage.java diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/ConnectionException.java b/src/main/java/me/mrletsplay/shareclientcore/connection/ConnectionException.java new file mode 100644 index 0000000..eb4bbef --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/ConnectionException.java @@ -0,0 +1,25 @@ +package me.mrletsplay.shareclientcore.connection; + +public class ConnectionException extends Exception { + + private static final long serialVersionUID = -6133726852202889620L; + + public ConnectionException() { + super(); + } + + public ConnectionException(String message, Throwable cause) { + super(message, cause); + } + + public ConnectionException(String message) { + super(message); + } + + public ConnectionException(Throwable cause) { + super(cause); + } + + + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/DummyConnection.java b/src/main/java/me/mrletsplay/shareclientcore/connection/DummyConnection.java index c95bd4c..e930e22 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/DummyConnection.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/DummyConnection.java @@ -1,31 +1,31 @@ package me.mrletsplay.shareclientcore.connection; -import java.io.IOException; +import me.mrletsplay.shareclientcore.connection.message.Message; public class DummyConnection implements RemoteConnection { @Override - public void connect() throws IOException, InterruptedException { + public void connect(String sessionID) throws ConnectionException { } @Override - public int retrieveSiteID() { + public int getSiteID() { return 0; } @Override - public void send(Change... changes) { + public void send(Message message) { } @Override - public void addListener(RemoteListener listener) { + public void addListener(MessageListener listener) { } @Override - public void removeListener(RemoteListener listener) { + public void removeListener(MessageListener listener) { } diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/MessageListener.java b/src/main/java/me/mrletsplay/shareclientcore/connection/MessageListener.java new file mode 100644 index 0000000..6540d92 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/MessageListener.java @@ -0,0 +1,12 @@ +package me.mrletsplay.shareclientcore.connection; + +import me.mrletsplay.shareclientcore.connection.message.Message; + +/** + * A message listener that can receive remote messages + */ +public interface MessageListener { + + public void onMessage(Message message); + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteConnection.java b/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteConnection.java index b0aaa7a..1993934 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteConnection.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteConnection.java @@ -1,20 +1,22 @@ package me.mrletsplay.shareclientcore.connection; -import java.io.IOException; +import me.mrletsplay.shareclientcore.connection.message.Message; /** * Represents a connection to a remote user or server */ public interface RemoteConnection { - public void connect() throws IOException, InterruptedException; + public static final int PROTOCOL_VERSION = 1; - public int retrieveSiteID(); + public void connect(String sessionID) throws ConnectionException; - public void send(Change... changes); + public int getSiteID(); - public void addListener(RemoteListener listener); + public void send(Message message) throws ConnectionException; - public void removeListener(RemoteListener listener); + public void addListener(MessageListener listener); + + public void removeListener(MessageListener listener); } diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteListener.java b/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteListener.java deleted file mode 100644 index 7950c8a..0000000 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/RemoteListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.mrletsplay.shareclientcore.connection; - -/** - * Represents something that can receive remote changes - */ -public interface RemoteListener { - - public void onRemoteChange(Change... changes); - -} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/SerializableObject.java b/src/main/java/me/mrletsplay/shareclientcore/connection/SerializableObject.java new file mode 100644 index 0000000..2432f21 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/SerializableObject.java @@ -0,0 +1,10 @@ +package me.mrletsplay.shareclientcore.connection; + +import java.io.DataOutputStream; +import java.io.IOException; + +public interface SerializableObject { + + public void serialize(DataOutputStream out) throws IOException; + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/WebSocketConnection.java b/src/main/java/me/mrletsplay/shareclientcore/connection/WebSocketConnection.java index cbaed7f..90dee4f 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/WebSocketConnection.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/WebSocketConnection.java @@ -1,109 +1,86 @@ package me.mrletsplay.shareclientcore.connection; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Map; -import java.util.Random; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; -import me.mrletsplay.shareclientcore.document.Char; -import me.mrletsplay.shareclientcore.document.Identifier; +import me.mrletsplay.shareclientcore.connection.message.ClientHelloMessage; +import me.mrletsplay.shareclientcore.connection.message.Message; +import me.mrletsplay.shareclientcore.connection.message.ServerHelloMessage; public class WebSocketConnection implements RemoteConnection { private WSClient client; - private Set listeners; + private String username; + private Set listeners; + private int siteID; - public WebSocketConnection(URI uri, Map httpHeaders) { + private Object wait = new Object(); + private boolean helloReceived; + private ConnectionException connectException; + + public WebSocketConnection(URI uri, String username, Map httpHeaders) { this.client = new WSClient(uri, httpHeaders); + this.username = username; this.listeners = new HashSet<>(); } - public WebSocketConnection(URI uri) { - this(uri, null); + public WebSocketConnection(URI uri, String username) { + this(uri, username, null); } @Override - public void connect() throws IOException, InterruptedException { - if(!client.connectBlocking()) throw new IOException("Failed to connect to WebSocket server"); - } - - @Override - public int retrieveSiteID() { - // TODO: implement - return new Random().nextInt(); - } - - @Override - public void send(Change... changes) { - for(Change c : changes) { - client.send(serialize(c)); + public void connect(String sessionID) throws ConnectionException { + try { + if(!client.connectBlocking(30, TimeUnit.SECONDS)) throw new IOException("Failed to connect to WebSocket server"); + send(new ClientHelloMessage(username, sessionID)); + wait.wait(30_000L); + if(!helloReceived) throw new ConnectionException("Server did not send hello"); + if(connectException != null) throw connectException; + } catch (InterruptedException | IOException e) { + throw new ConnectionException("Failed to establish connection", e); } } @Override - public void addListener(RemoteListener listener) { + public int getSiteID() { + return siteID; + } + + @Override + public void send(Message message) throws ConnectionException { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DataOutputStream dOut = new DataOutputStream(bOut); + + try { + dOut.writeUTF(message.getType().name()); + message.serialize(dOut); + } catch (IOException e) { + throw new ConnectionException("Failed to serialize message", e); + } + + client.send(bOut.toByteArray()); + } + + @Override + public void addListener(MessageListener listener) { listeners.add(listener); } @Override - public void removeListener(RemoteListener listener) { + public void removeListener(MessageListener listener) { listeners.remove(listener); } - private byte[] serialize(Change change) { - try { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - DataOutputStream dOut = new DataOutputStream(bOut); - dOut.writeInt(change.document()); - dOut.writeUTF(change.type().name()); - - Char ch = change.character(); - dOut.writeInt(ch.position().length); - for(int i = 0; i < ch.position().length; i++) { - Identifier id = ch.position()[i]; - dOut.writeInt(id.digit()); - dOut.writeInt(id.site()); - } - - dOut.writeInt(ch.lamport()); - dOut.writeChar(ch.value()); - return bOut.toByteArray(); - }catch(IOException e) { - throw new RuntimeException("Something went very wrong", e); - } - } - - private Change deserialize(byte[] bytes) { - try { - DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(bytes)); - int document = dIn.readInt(); - ChangeType type = ChangeType.valueOf(dIn.readUTF()); - - Identifier[] pos = new Identifier[dIn.readInt()]; - for(int i = 0; i < pos.length; i++) { - pos[i] = new Identifier(dIn.readInt(), dIn.readInt()); - } - - int lamport = dIn.readInt(); - char value = dIn.readChar(); - return new Change(document, type, new Char(pos, lamport, value)); - }catch(IllegalArgumentException e) { - throw new IllegalArgumentException("Failed to deserialize change", e); - }catch(IOException e) { - throw new RuntimeException("Something went very wrong", e); - } - } - private class WSClient extends WebSocketClient { public WSClient(URI serverUri) { @@ -116,7 +93,7 @@ public class WebSocketConnection implements RemoteConnection { @Override public void onOpen(ServerHandshake handshakedata) { - // TODO: request site id + } @Override @@ -126,9 +103,28 @@ public class WebSocketConnection implements RemoteConnection { @Override public void onMessage(ByteBuffer bytes) { - byte[] bytesArray = new byte[bytes.remaining()]; - bytes.get(bytesArray); - listeners.forEach(l -> l.onRemoteChange(deserialize(bytesArray))); + Message m; + try { + m = Message.deserialize(bytes); + }catch(IOException e) { + e.printStackTrace(); // TODO: custom logging (e.g. via error callback) + return; + } + + if(m instanceof ServerHelloMessage hello) { + helloReceived = true; + siteID = hello.siteID(); + + if(hello.protocolVersion() != PROTOCOL_VERSION) { + connectException = new ConnectionException(String.format("Protocol version mismatch: (Server has %s, Client has %s)", hello.protocolVersion(), PROTOCOL_VERSION)); + close(); + } + + wait.notifyAll(); + return; + } + + listeners.forEach(l -> l.onMessage(m)); } @Override diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/BasicMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/BasicMessage.java new file mode 100644 index 0000000..27ae73f --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/BasicMessage.java @@ -0,0 +1,16 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataOutputStream; +import java.io.IOException; + +public record BasicMessage(MessageType type) implements Message { + + @Override + public MessageType getType() { + return type; + } + + @Override + public void serialize(DataOutputStream out) throws IOException {} + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChangeMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChangeMessage.java new file mode 100644 index 0000000..8038952 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChangeMessage.java @@ -0,0 +1,33 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import me.mrletsplay.shareclientcore.connection.Change; +import me.mrletsplay.shareclientcore.connection.ChangeType; +import me.mrletsplay.shareclientcore.document.Char; + +public record ChangeMessage(Change change) implements Message { + + @Override + public MessageType getType() { + return MessageType.CHANGE; + } + + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeInt(change.document()); + out.writeUTF(change.type().name()); + change.character().serialize(out); + } + + public static ChangeMessage deserialize(DataInputStream in) throws IOException { + try { + return new ChangeMessage(new Change(in.readInt(), ChangeType.valueOf(in.readUTF()), Char.deserialize(in))); + }catch(IllegalArgumentException e) { + throw new IOException("Invalid change type", e); + } + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/ClientHelloMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ClientHelloMessage.java new file mode 100644 index 0000000..7e58263 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ClientHelloMessage.java @@ -0,0 +1,24 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public record ClientHelloMessage(String username, String sessionID) implements Message { + + @Override + public MessageType getType() { + return MessageType.CLIENT_HELLO; + } + + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeUTF(username); + out.writeUTF(sessionID); + } + + public static ClientHelloMessage deserialize(DataInputStream in) throws IOException { + return new ClientHelloMessage(in.readUTF(), in.readUTF()); + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java new file mode 100644 index 0000000..bf0d21e --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java @@ -0,0 +1,38 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import me.mrletsplay.shareclientcore.connection.SerializableObject; + +public interface Message extends SerializableObject { + + public MessageType getType(); + + public static Message deserialize(ByteBuffer buffer) throws IOException { + byte[] bytesArray = new byte[buffer.remaining()]; + buffer.get(bytesArray); + + try { + DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(bytesArray)); + MessageType type = MessageType.valueOf(dIn.readUTF()); + + Message m; + switch(type) { + case CLIENT_HELLO -> m = ClientHelloMessage.deserialize(dIn); + case SERVER_HELLO -> m = ServerHelloMessage.deserialize(dIn); + case PEER_JOIN -> m = PeerJoinMessage.deserialize(dIn); + case PEER_LEAVE -> m = PeerLeaveMessage.deserialize(dIn); + case CHANGE -> m = ChangeMessage.deserialize(dIn); + default -> m = new BasicMessage(type); + } + + return m; + }catch(IllegalArgumentException e) { + throw new IOException("Invalid message type", e); + } + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/MessageType.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/MessageType.java new file mode 100644 index 0000000..6b3fd28 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/MessageType.java @@ -0,0 +1,61 @@ +package me.mrletsplay.shareclientcore.connection.message; + +public enum MessageType { + + /** + * Client hello containing user information + */ + CLIENT_HELLO, + + /** + * Server hello containing protocol information + */ + SERVER_HELLO, + + /** + * Peer has joined + */ + PEER_JOIN, + + /** + * Peer has left + */ + PEER_LEAVE, + + /** + * Full synchronization message, containing the full contents of a file + */ + FULL_SYNC, + + /** + * Request for the synchronization of a particular file or all shared files + */ + REQUEST_FULL_SYNC, + + /** + * A single change made by one peer + */ + CHANGE, + + /** + * Request for the checksum of a file + */ + REQUEST_CHECKSUM, + + /** + * Checksum of a file + */ + CHECKSUM, + + /** + * Creation of a file + */ + CREATE_FILE, + + /** + * Deletion of a file + */ + DELETE_FILE, + ; + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerJoinMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerJoinMessage.java new file mode 100644 index 0000000..0a34741 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerJoinMessage.java @@ -0,0 +1,24 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public record PeerJoinMessage(String peerName, int peerSiteID) implements Message { + + @Override + public MessageType getType() { + return MessageType.PEER_JOIN; + } + + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeUTF(peerName); + out.writeInt(peerSiteID); + } + + public static PeerJoinMessage deserialize(DataInputStream in) throws IOException { + return new PeerJoinMessage(in.readUTF(), in.readInt()); + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerLeaveMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerLeaveMessage.java new file mode 100644 index 0000000..7e1a912 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/PeerLeaveMessage.java @@ -0,0 +1,23 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public record PeerLeaveMessage(int peerSiteID) implements Message { + + @Override + public MessageType getType() { + return MessageType.PEER_LEAVE; + } + + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeInt(peerSiteID); + } + + public static PeerLeaveMessage deserialize(DataInputStream in) throws IOException { + return new PeerLeaveMessage(in.readInt()); + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/ServerHelloMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ServerHelloMessage.java new file mode 100644 index 0000000..bb3eee9 --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ServerHelloMessage.java @@ -0,0 +1,24 @@ +package me.mrletsplay.shareclientcore.connection.message; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public record ServerHelloMessage(int protocolVersion, int siteID) implements Message { + + @Override + public MessageType getType() { + return MessageType.SERVER_HELLO; + } + + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeInt(protocolVersion); + out.writeInt(siteID); + } + + public static ServerHelloMessage deserialize(DataInputStream in) throws IOException { + return new ServerHelloMessage(in.readInt(), in.readInt()); + } + +} diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/Char.java b/src/main/java/me/mrletsplay/shareclientcore/document/Char.java index 46fd10c..5c24427 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/Char.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/Char.java @@ -1,9 +1,14 @@ package me.mrletsplay.shareclientcore.document; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.Arrays; import java.util.Objects; -public record Char(Identifier[] position, int lamport, char value) { +import me.mrletsplay.shareclientcore.connection.SerializableObject; + +public record Char(Identifier[] position, int lamport, char value) implements SerializableObject { public static final Char START_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(1, 0) }, 0, '^'); public static final Char END_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(Util.BASE - 1, 0) }, 0, '$'); @@ -28,4 +33,28 @@ public record Char(Identifier[] position, int lamport, char value) { return lamport == other.lamport && Arrays.equals(position, other.position) && value == other.value; } + @Override + public void serialize(DataOutputStream out) throws IOException { + out.writeInt(position.length); + for(int i = 0; i < position.length; i++) { + Identifier id = position[i]; + out.writeInt(id.digit()); + out.writeInt(id.site()); + } + + out.writeInt(lamport); + out.writeChar(value); + } + + public static Char deserialize(DataInputStream in) throws IOException { + Identifier[] pos = new Identifier[in.readInt()]; + for(int i = 0; i < pos.length; i++) { + pos[i] = new Identifier(in.readInt(), in.readInt()); + } + + int lamport = in.readInt(); + char value = in.readChar(); + return new Char(pos, lamport, value); + } + } diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java index d91ebe1..921bfdc 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java @@ -6,10 +6,13 @@ import java.util.Set; import me.mrletsplay.shareclientcore.connection.Change; import me.mrletsplay.shareclientcore.connection.ChangeType; +import me.mrletsplay.shareclientcore.connection.ConnectionException; +import me.mrletsplay.shareclientcore.connection.MessageListener; import me.mrletsplay.shareclientcore.connection.RemoteConnection; -import me.mrletsplay.shareclientcore.connection.RemoteListener; +import me.mrletsplay.shareclientcore.connection.message.ChangeMessage; +import me.mrletsplay.shareclientcore.connection.message.Message; -public class SharedDocument implements RemoteListener { +public class SharedDocument implements MessageListener { private RemoteConnection connection; private CharBag charBag; @@ -27,7 +30,7 @@ public class SharedDocument implements RemoteListener { charBag.add(Char.END_OF_DOCUMENT); this.document = 0; // TODO: implement - this.site = connection.retrieveSiteID(); + this.site = connection.getSiteID(); this.listeners = new HashSet<>(); } @@ -53,7 +56,13 @@ public class SharedDocument implements RemoteListener { charBefore = ch; } - connection.send(changes); + for(Change c : changes) { + try { + connection.send(new ChangeMessage(c)); + } catch (ConnectionException e) { + e.printStackTrace(); // TODO: throw error + } + } } /** @@ -73,7 +82,14 @@ public class SharedDocument implements RemoteListener { charBag.remove(toRemove); } - connection.send(changes); + + for(Change c : changes) { + try { + connection.send(new ChangeMessage(c)); + } catch (ConnectionException e) { + e.printStackTrace(); // TODO: throw error + } + } } /** @@ -120,8 +136,9 @@ public class SharedDocument implements RemoteListener { } @Override - public void onRemoteChange(Change... changes) { - for(Change c : changes) { + public void onMessage(Message message) { + if(message instanceof ChangeMessage change) { + Change c = change.change(); System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position())); switch(c.type()) { case ADD -> remoteInsert(c.character());