Skip to content

Commit a031aca

Browse files
committed
Added abstract liveobjects serialization impl. using msgpack and interfaces
1 parent 375328e commit a031aca

File tree

8 files changed

+146
-2
lines changed

8 files changed

+146
-2
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.ably.lib.objects;
2+
3+
import io.ably.lib.util.Log;
4+
5+
public class Helpers {
6+
7+
private static final String TAG = Helpers.class.getName();
8+
public static final LiveObjectsSerializer liveObjectsSerializer = getLiveObjectsSerializer();
9+
10+
private static LiveObjectsSerializer getLiveObjectsSerializer() {
11+
try {
12+
// Replace with the fully qualified name of the implementing class
13+
Class<?> clazz = Class.forName("io.ably.lib.objects.DefaultLiveObjectsSerializer");
14+
return (LiveObjectsSerializer) clazz.getDeclaredConstructor().newInstance();
15+
} catch (Exception e) {
16+
// log the error using Log.e
17+
Log.e(TAG, ": Failed to create LiveObjectsSerializer instance", e);
18+
return null;
19+
}
20+
}
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.ably.lib.objects;
2+
3+
import io.ably.lib.plugins.PluginSerializer;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.msgpack.core.MessagePacker;
6+
import org.msgpack.core.MessageUnpacker;
7+
8+
import java.io.IOException;
9+
10+
public abstract class LiveObjectsSerializer implements PluginSerializer {
11+
@NotNull
12+
public Object[] readMsgpackArray(@NotNull MessageUnpacker unpacker) throws IOException {
13+
int count = unpacker.unpackArrayHeader();
14+
Object[] result = new Object[count];
15+
for(int i = 0; i < count; i++)
16+
result[i] = readMsgpack(unpacker);
17+
return result;
18+
}
19+
20+
public void writeMsgpackArray(@NotNull Object[] objects, @NotNull MessagePacker packer) throws IOException {
21+
int count = objects.length;
22+
packer.packArrayHeader(count);
23+
for(Object object : objects) {
24+
writeMsgpack(object, packer);
25+
}
26+
}
27+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.ably.lib.plugins;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.msgpack.core.MessagePacker;
5+
import org.msgpack.core.MessageUnpacker;
6+
7+
import java.io.IOException;
8+
9+
/**
10+
* The `PluginSerializer` interface defines methods for serializing and deserializing objects
11+
* using the MessagePack format. Implementations of this interface are responsible for
12+
* converting objects to and from MessagePack binary format.
13+
*/
14+
public interface PluginSerializer {
15+
16+
/**
17+
* Reads and deserializes an object from a `MessageUnpacker` instance.
18+
*
19+
* @param unpacker The `MessageUnpacker` used to read the serialized data.
20+
* @return The deserialized object.
21+
* @throws IOException If an I/O error occurs during deserialization.
22+
*/
23+
@NotNull
24+
Object readMsgpack(@NotNull MessageUnpacker unpacker) throws IOException;
25+
26+
/**
27+
* Serializes an object and writes it to a `MessagePacker` instance.
28+
*
29+
* @param obj The object to be serialized.
30+
* @param packer The `MessagePacker` used to write the serialized data.
31+
* @throws IOException If an I/O error occurs during serialization.
32+
*/
33+
void writeMsgpack(@NotNull Object obj, @NotNull MessagePacker packer) throws IOException;
34+
}

lib/src/main/java/io/ably/lib/types/ProtocolMessage.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.reflect.Type;
55
import java.util.Map;
66

7+
import org.jetbrains.annotations.Nullable;
78
import org.msgpack.core.MessageFormat;
89
import org.msgpack.core.MessagePacker;
910
import org.msgpack.core.MessageUnpacker;
@@ -18,6 +19,8 @@
1819

1920
import io.ably.lib.util.Log;
2021

22+
import static io.ably.lib.objects.Helpers.liveObjectsSerializer;
23+
2124
/**
2225
* A message sent and received over the Realtime protocol.
2326
* A ProtocolMessage always relates to a single channel only, but
@@ -116,6 +119,11 @@ public ProtocolMessage(Action action, String channel) {
116119
public ConnectionDetails connectionDetails;
117120
public AuthDetails auth;
118121
public Map<String, String> params;
122+
/**
123+
* This will be null if we skipped decoding this property due to user not requesting Objects functionality
124+
*/
125+
@Nullable
126+
public Object[] state;
119127

120128
public boolean hasFlag(final Flag flag) {
121129
return (flags & flag.getMask()) == flag.getMask();
@@ -139,6 +147,7 @@ void writeMsgpack(MessagePacker packer) throws IOException {
139147
if(flags != 0) ++fieldCount;
140148
if(params != null) ++fieldCount;
141149
if(channelSerial != null) ++fieldCount;
150+
if(state != null) ++fieldCount;
142151
packer.packMapHeader(fieldCount);
143152
packer.packString("action");
144153
packer.packInt(action.getValue());
@@ -174,6 +183,14 @@ void writeMsgpack(MessagePacker packer) throws IOException {
174183
packer.packString("channelSerial");
175184
packer.packString(channelSerial);
176185
}
186+
if(state != null) {
187+
if (liveObjectsSerializer != null) {
188+
packer.packString("state");
189+
liveObjectsSerializer.writeMsgpackArray(state, packer);
190+
} else {
191+
Log.w(TAG, "Skipping 'state' field serialization because LiveObjectsSerializer is not set");
192+
}
193+
}
177194
}
178195

179196
ProtocolMessage readMsgpack(MessageUnpacker unpacker) throws IOException {
@@ -233,6 +250,14 @@ ProtocolMessage readMsgpack(MessageUnpacker unpacker) throws IOException {
233250
case "params":
234251
params = MessageSerializer.readStringMap(unpacker);
235252
break;
253+
case "state":
254+
if (liveObjectsSerializer != null) {
255+
state = liveObjectsSerializer.readMsgpackArray(unpacker);
256+
} else {
257+
Log.w(TAG, "Skipping 'state' field deserialization because LiveObjectsSerializer is not set");
258+
unpacker.skipValue();
259+
}
260+
break;
236261
default:
237262
Log.v(TAG, "Unexpected field: " + fieldName);
238263
unpacker.skipValue();

live-objects/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ repositories {
99

1010
dependencies {
1111
implementation(project(":java"))
12+
implementation(libs.bundles.common)
1213
testImplementation(kotlin("test"))
1314
implementation(libs.coroutine.core)
1415

live-objects/src/main/kotlin/io/ably/lib/objects/Helpers.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal suspend fun LiveObjectsAdapter.sendAsync(message: ProtocolMessage) {
2323
deferred.await()
2424
}
2525

26-
internal enum class MessageFormat(private val value: String) {
26+
internal enum class ProtocolMessageFormat(private val value: String) {
2727
MSGPACK("msgpack"),
2828
JSON("json");
2929

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectMessage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.ably.lib.objects
22

3+
import org.msgpack.core.MessageUnpacker
34
import java.nio.ByteBuffer
45

56
/**
@@ -195,7 +196,7 @@ internal data class ObjectOperation(
195196
/** The initial value encoding defines how the initialValue should be interpreted.
196197
* Spec: OOP3i
197198
*/
198-
val initialValueEncoding: MessageFormat? = null
199+
val initialValueEncoding: ProtocolMessageFormat? = null
199200
)
200201

201202
/**
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.ably.lib.objects
2+
3+
import org.msgpack.core.MessagePacker
4+
import org.msgpack.core.MessageUnpacker
5+
6+
/**
7+
* Base class for serializing and deserializing live objects.
8+
* Initializes with a default serializer that uses MessagePack format.
9+
* Initialized by the LiveObjectsAdapter.
10+
*/
11+
internal class DefaultLiveObjectsSerializer: LiveObjectsSerializer() {
12+
override fun readMsgpack(unpacker: MessageUnpacker): Any {
13+
return getObjectMessageFromMsgpack(unpacker)
14+
}
15+
16+
override fun writeMsgpack(obj: Any, packer: MessagePacker) {
17+
if (obj is ObjectMessage) {
18+
obj.writeMsgpack(packer)
19+
}
20+
}
21+
}
22+
23+
/**
24+
* Extension function to deserialize an ObjectMessage from a MessageUnpacker.
25+
*/
26+
internal fun getObjectMessageFromMsgpack(unpacker: MessageUnpacker): ObjectMessage {
27+
TODO("Not yet implemented")
28+
}
29+
30+
/**
31+
* Extension function to serialize an ObjectMessage to a MessagePacker.
32+
*/
33+
private fun ObjectMessage.writeMsgpack(packer: MessagePacker) {
34+
TODO("Not yet implemented")
35+
}

0 commit comments

Comments
 (0)