Skip to content

Commit 5e452c2

Browse files
committed
- Declared ObjectSerializer interface for json/msgpack encoding/decoding
- Implemented JsonSerializer annotation for better json handling
1 parent 34c3bbe commit 5e452c2

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.ably.lib.object.serialization;
2+
3+
import com.google.gson.*;
4+
import io.ably.lib.util.Log;
5+
6+
import java.lang.reflect.Type;
7+
8+
public class ObjectJsonSerializer implements JsonSerializer<Object[]>, JsonDeserializer<Object[]> {
9+
private static final String TAG = ObjectJsonSerializer.class.getName();
10+
11+
@Override
12+
public Object[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
13+
ObjectSerializer serializer = ObjectSerializer.tryGet();
14+
if (serializer == null) {
15+
Log.w(TAG, "Skipping 'state' field json deserialization because ObjectsSerializer not found.");
16+
return null;
17+
}
18+
if (!json.isJsonArray()) {
19+
throw new JsonParseException("Expected a JSON array for 'state' field, but got: " + json);
20+
}
21+
return serializer.readFromJsonArray(json.getAsJsonArray());
22+
}
23+
24+
@Override
25+
public JsonElement serialize(Object[] src, Type typeOfSrc, JsonSerializationContext context) {
26+
ObjectSerializer serializer = ObjectSerializer.tryGet();
27+
if (serializer == null) {
28+
Log.w(TAG, "Skipping 'state' field json serialization because ObjectsSerializer not found.");
29+
return JsonNull.INSTANCE;
30+
}
31+
return serializer.asJsonArray(src);
32+
}
33+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.ably.lib.object.serialization;
2+
3+
import com.google.gson.JsonArray;
4+
import io.ably.lib.util.Log;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
import org.msgpack.core.MessagePacker;
8+
import org.msgpack.core.MessageUnpacker;
9+
10+
import java.io.IOException;
11+
import java.lang.reflect.InvocationTargetException;
12+
13+
/**
14+
* Serializer interface for converting between objects and their MessagePack or JSON representations.
15+
*/
16+
public interface ObjectSerializer {
17+
18+
/**
19+
* Reads a MessagePack array from the given unpacker and deserializes it into an Object array.
20+
*
21+
* @param unpacker the MessageUnpacker to read from
22+
* @return the deserialized Object array
23+
* @throws IOException if an I/O error occurs during unpacking
24+
*/
25+
@NotNull
26+
Object[] readMsgpackArray(@NotNull MessageUnpacker unpacker) throws IOException;
27+
28+
/**
29+
* Serializes the given Object array as a MessagePack array using the provided packer.
30+
*
31+
* @param objects the Object array to serialize
32+
* @param packer the MessagePacker to write to
33+
* @throws IOException if an I/O error occurs during packing
34+
*/
35+
void writeMsgpackArray(@NotNull Object[] objects, @NotNull MessagePacker packer) throws IOException;
36+
37+
/**
38+
* Reads a JSON array from the given {@link JsonArray} and deserializes it into an Object array.
39+
*
40+
* @param json the {@link JsonArray} representing the array to deserialize
41+
* @return the deserialized Object array
42+
*/
43+
@NotNull
44+
Object[] readFromJsonArray(@NotNull JsonArray json);
45+
46+
/**
47+
* Serializes the given Object array as a JSON array.
48+
*
49+
* @param objects the Object array to serialize
50+
* @return the resulting JsonArray
51+
*/
52+
@NotNull
53+
JsonArray asJsonArray(@NotNull Object[] objects);
54+
55+
/**
56+
* Returns the lazily-initialized, process-wide {@link ObjectSerializer} singleton, reflectively
57+
* loaded from the LiveObjects plugin on the classpath. Returns {@code null} if the plugin is not
58+
* present; the lookup is retried on subsequent calls until it succeeds.
59+
*
60+
* @return the shared {@link ObjectSerializer} instance, or {@code null} if the plugin is unavailable.
61+
*/
62+
@Nullable
63+
static ObjectSerializer tryGet() {
64+
return Holder.getSerializer();
65+
}
66+
67+
/**
68+
* Holds the lazily-initialized {@link ObjectSerializer} singleton. Interfaces cannot declare
69+
* mutable static fields, so the cache lives here while {@link #tryGet()} delegates to it.
70+
*/
71+
final class Holder {
72+
private static final String TAG = ObjectSerializer.Holder.class.getName();
73+
private static final String IMPLEMENTATION_CLASS = "io.ably.lib.object.serialization.DefaultObjectsSerializer";
74+
private static volatile ObjectSerializer objectsSerializer;
75+
76+
private Holder() {}
77+
78+
@Nullable
79+
static ObjectSerializer getSerializer() {
80+
if (objectsSerializer == null) {
81+
synchronized (Holder.class) {
82+
if (objectsSerializer == null) { // Double-Checked Locking (DCL)
83+
try {
84+
Class<?> serializerClass = Class.forName(IMPLEMENTATION_CLASS);
85+
objectsSerializer = (ObjectSerializer) serializerClass.getDeclaredConstructor().newInstance();
86+
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
87+
NoSuchMethodException |
88+
InvocationTargetException e) {
89+
Log.w(TAG, "Failed to init ObjectsSerializer, LiveObjects plugin not included in the classpath", e);
90+
return null;
91+
}
92+
}
93+
}
94+
}
95+
return objectsSerializer;
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)