Skip to content

Commit b06190d

Browse files
nklijia2011meiliang86
authored andcommitted
Add TBase and TEnum type adapter for JsonDataConverter (#263)
* add TBase and TEnum gson adapter factory * add the test * remove special handling for thrift in JsonDataConverter for single param
1 parent cc71829 commit b06190d

File tree

5 files changed

+254
-24
lines changed

5 files changed

+254
-24
lines changed

src/main/java/com/uber/cadence/converter/JsonDataConverter.java

+3-22
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@
3939
import java.util.function.Function;
4040
import java.util.regex.Matcher;
4141
import java.util.regex.Pattern;
42-
import org.apache.thrift.TBase;
43-
import org.apache.thrift.TDeserializer;
44-
import org.apache.thrift.TSerializer;
4542
import org.apache.thrift.protocol.TJSONProtocol;
4643
import org.slf4j.Logger;
4744
import org.slf4j.LoggerFactory;
@@ -97,7 +94,9 @@ public JsonDataConverter(Function<GsonBuilder, GsonBuilder> builderInterceptor)
9794
GsonBuilder gsonBuilder =
9895
new GsonBuilder()
9996
.serializeNulls()
100-
.registerTypeAdapterFactory(new ThrowableTypeAdapterFactory());
97+
.registerTypeAdapterFactory(new ThrowableTypeAdapterFactory())
98+
.registerTypeAdapterFactory(new TBaseTypeAdapterFactory())
99+
.registerTypeAdapterFactory(new TEnumTypeAdapterFactory());
101100
GsonBuilder intercepted = builderInterceptor.apply(gsonBuilder);
102101
gson = intercepted.create();
103102
}
@@ -115,10 +114,6 @@ public byte[] toData(Object... values) throws DataConverterException {
115114
try {
116115
if (values.length == 1) {
117116
Object value = values[0];
118-
// Serialize thrift objects using Thrift serializer
119-
if (value instanceof TBase) {
120-
return newThriftSerializer().toString((TBase) value).getBytes(StandardCharsets.UTF_8);
121-
}
122117
try {
123118
String json = gson.toJson(value);
124119
return json.getBytes(StandardCharsets.UTF_8);
@@ -151,12 +146,6 @@ public <T> T fromData(byte[] content, Class<T> valueClass, Type valueType)
151146
return null;
152147
}
153148
try {
154-
// Deserialize thrift values.
155-
if (TBase.class.isAssignableFrom(valueClass)) {
156-
T instance = valueClass.getConstructor().newInstance();
157-
newThriftDeserializer().deserialize((TBase) instance, content);
158-
return instance;
159-
}
160149
return gson.fromJson(new String(content, StandardCharsets.UTF_8), valueType);
161150
} catch (Exception e) {
162151
throw new DataConverterException(content, new Type[] {valueType}, e);
@@ -389,12 +378,4 @@ private static StackTraceElement parseStackTraceElement(String line) {
389378
}
390379
return new StackTraceElement(declaringClass, methodName, fileName, lineNumber);
391380
}
392-
393-
private static TSerializer newThriftSerializer() {
394-
return new TSerializer(new TJSONProtocol.Factory());
395-
}
396-
397-
private static TDeserializer newThriftDeserializer() {
398-
return new TDeserializer(new TJSONProtocol.Factory());
399-
}
400381
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.converter;
19+
20+
import com.google.gson.Gson;
21+
import com.google.gson.TypeAdapter;
22+
import com.google.gson.TypeAdapterFactory;
23+
import com.google.gson.reflect.TypeToken;
24+
import com.google.gson.stream.JsonReader;
25+
import com.google.gson.stream.JsonWriter;
26+
import java.io.IOException;
27+
import java.nio.charset.StandardCharsets;
28+
import org.apache.thrift.TBase;
29+
import org.apache.thrift.TDeserializer;
30+
import org.apache.thrift.TException;
31+
import org.apache.thrift.TSerializer;
32+
import org.apache.thrift.protocol.TJSONProtocol;
33+
34+
/**
35+
* Special handling of TBase message serialization and deserialization. This is to support for
36+
* inline Thrift fields in Java class.
37+
*/
38+
public class TBaseTypeAdapterFactory implements TypeAdapterFactory {
39+
40+
@Override
41+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
42+
// this class only serializes 'TBase' and its subtypes
43+
if (!TBase.class.isAssignableFrom(typeToken.getRawType())) {
44+
return null;
45+
}
46+
TypeAdapter<T> result =
47+
new TypeAdapter<T>() {
48+
@Override
49+
public void write(JsonWriter jsonWriter, T value) throws IOException {
50+
try {
51+
String result =
52+
newThriftSerializer().toString((TBase) value, StandardCharsets.UTF_8.name());
53+
jsonWriter.value(result);
54+
} catch (TException e) {
55+
throw new DataConverterException("Failed to serialize TBase", e);
56+
}
57+
}
58+
59+
@Override
60+
public T read(JsonReader jsonReader) throws IOException {
61+
String value = jsonReader.nextString();
62+
try {
63+
@SuppressWarnings("unchecked")
64+
T instance = (T) typeToken.getRawType().getConstructor().newInstance();
65+
newThriftDeserializer()
66+
.deserialize((TBase) instance, value, StandardCharsets.UTF_8.name());
67+
return instance;
68+
} catch (Exception e) {
69+
throw new DataConverterException("Failed to deserialize TBase", e);
70+
}
71+
}
72+
}.nullSafe();
73+
return result;
74+
}
75+
76+
private static TSerializer newThriftSerializer() {
77+
return new TSerializer(new TJSONProtocol.Factory());
78+
}
79+
80+
private static TDeserializer newThriftDeserializer() {
81+
return new TDeserializer(new TJSONProtocol.Factory());
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.converter;
19+
20+
import com.google.gson.Gson;
21+
import com.google.gson.TypeAdapter;
22+
import com.google.gson.TypeAdapterFactory;
23+
import com.google.gson.reflect.TypeToken;
24+
import com.google.gson.stream.JsonReader;
25+
import com.google.gson.stream.JsonWriter;
26+
import java.io.IOException;
27+
import java.lang.reflect.Method;
28+
import org.apache.thrift.TEnum;
29+
30+
/**
31+
* Special handling of TEnum serialization and deserialization. This is to support for inline TEnum
32+
* fields in Java class. The default gson serde serialize the TEnum with its String name
33+
* representation, this adapter serialize the TEnum class with its int representation.
34+
*/
35+
public class TEnumTypeAdapterFactory implements TypeAdapterFactory {
36+
37+
@Override
38+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
39+
// this class only serializes 'TEnum' and its subtypes
40+
if (!TEnum.class.isAssignableFrom(typeToken.getRawType())) {
41+
return null;
42+
}
43+
TypeAdapter<T> result =
44+
new TypeAdapter<T>() {
45+
@Override
46+
public void write(JsonWriter jsonWriter, T value) throws IOException {
47+
jsonWriter.value(((TEnum) value).getValue());
48+
}
49+
50+
@Override
51+
public T read(JsonReader jsonReader) throws IOException {
52+
int value = jsonReader.nextInt();
53+
try {
54+
Method m = (typeToken.getRawType().getDeclaredMethod("findByValue", Integer.TYPE));
55+
@SuppressWarnings("unchecked")
56+
T instance = (T) m.invoke(null, value);
57+
return instance;
58+
} catch (Exception e) {
59+
throw new DataConverterException("Failed to deserilize TEnum", e);
60+
}
61+
}
62+
}.nullSafe();
63+
return result;
64+
}
65+
}

src/main/java/com/uber/cadence/internal/replay/ReplayDecider.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,7 @@ private void completeWorkflow() {
289289
}
290290
}
291291

292-
long nanoTime =
293-
TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
292+
long nanoTime = TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
294293
com.uber.m3.util.Duration d = com.uber.m3.util.Duration.ofNanos(nanoTime - wfStartTimeNanos);
295294
metricsScope.timer(MetricsType.WORKFLOW_E2E_LATENCY).record(d);
296295
}

src/test/java/com/uber/cadence/converter/JsonDataConverterTest.java

+102
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import static org.junit.Assert.assertEquals;
2121

22+
import com.uber.cadence.EventType;
2223
import com.uber.cadence.History;
2324
import com.uber.cadence.HistoryEvent;
2425
import com.uber.cadence.TaskList;
@@ -29,13 +30,44 @@
2930
import java.nio.charset.StandardCharsets;
3031
import java.util.ArrayList;
3132
import java.util.List;
33+
import java.util.Objects;
3234
import java.util.UUID;
3335
import org.junit.Test;
3436

3537
public class JsonDataConverterTest {
3638

3739
private final DataConverter converter = JsonDataConverter.getInstance();
3840

41+
static class TestData {
42+
String val1;
43+
// TBase value;
44+
HistoryEvent val2;
45+
// TEnum value;
46+
EventType val3;
47+
48+
public TestData(String val1, HistoryEvent val2, EventType val3) {
49+
this.val1 = val1;
50+
this.val2 = val2;
51+
this.val3 = val3;
52+
}
53+
54+
@Override
55+
public boolean equals(Object o) {
56+
if (this == o) return true;
57+
if (!(o instanceof TestData)) return false;
58+
TestData testData = (TestData) o;
59+
return Objects.equals(val1, testData.val1)
60+
&& Objects.equals(val2, testData.val2)
61+
&& val3 == testData.val3;
62+
}
63+
64+
@Override
65+
public int hashCode() {
66+
67+
return Objects.hash(val1, val2, val3);
68+
}
69+
}
70+
3971
@Test
4072
public void testThrift() {
4173
List<HistoryEvent> events = new ArrayList<>();
@@ -57,6 +89,76 @@ public void testThrift() {
5789
assertEquals(new String(converted, StandardCharsets.UTF_8), history, fromConverted);
5890
}
5991

92+
@Test
93+
public void testThriftArray() {
94+
List<HistoryEvent> events = new ArrayList<>();
95+
WorkflowExecutionStartedEventAttributes started =
96+
new WorkflowExecutionStartedEventAttributes()
97+
.setExecutionStartToCloseTimeoutSeconds(11)
98+
.setIdentity("testIdentity")
99+
.setInput("input".getBytes(StandardCharsets.UTF_8))
100+
.setWorkflowType(new WorkflowType().setName("workflowType1"))
101+
.setTaskList(new TaskList().setName("taskList1"));
102+
events.add(
103+
new HistoryEvent()
104+
.setTimestamp(1234567)
105+
.setEventId(321)
106+
.setWorkflowExecutionStartedEventAttributes(started));
107+
History history = new History().setEvents(events);
108+
byte[] converted = converter.toData("abc", history);
109+
Object[] fromConverted = converter.fromDataArray(converted, String.class, History.class);
110+
assertEquals(new String(converted, StandardCharsets.UTF_8), "abc", fromConverted[0]);
111+
assertEquals(new String(converted, StandardCharsets.UTF_8), history, fromConverted[1]);
112+
}
113+
114+
@Test
115+
public void testThriftFieldsInPOJO() {
116+
WorkflowExecutionStartedEventAttributes started =
117+
new WorkflowExecutionStartedEventAttributes()
118+
.setExecutionStartToCloseTimeoutSeconds(11)
119+
.setIdentity("testIdentity")
120+
.setInput("input".getBytes(StandardCharsets.UTF_8))
121+
.setWorkflowType(new WorkflowType().setName("workflowType1"))
122+
.setTaskList(new TaskList().setName("taskList1"));
123+
124+
HistoryEvent historyEvent =
125+
new HistoryEvent()
126+
.setTimestamp(1234567)
127+
.setEventId(321)
128+
.setWorkflowExecutionStartedEventAttributes(started);
129+
130+
TestData testData = new TestData("test-thrift", historyEvent, EventType.ActivityTaskCompleted);
131+
132+
byte[] converted = converter.toData(testData);
133+
TestData fromConverted = converter.fromData(converted, TestData.class, TestData.class);
134+
assertEquals(new String(converted, StandardCharsets.UTF_8), testData, fromConverted);
135+
}
136+
137+
@Test
138+
public void testThriftFieldsInPOJOArray() {
139+
WorkflowExecutionStartedEventAttributes started =
140+
new WorkflowExecutionStartedEventAttributes()
141+
.setExecutionStartToCloseTimeoutSeconds(11)
142+
.setIdentity("testIdentity")
143+
.setInput("input".getBytes(StandardCharsets.UTF_8))
144+
.setWorkflowType(new WorkflowType().setName("workflowType1"))
145+
.setTaskList(new TaskList().setName("taskList1"));
146+
147+
HistoryEvent historyEvent =
148+
new HistoryEvent()
149+
.setTimestamp(1234567)
150+
.setEventId(321)
151+
.setWorkflowExecutionStartedEventAttributes(started);
152+
153+
TestData testData = new TestData("test-thrift", historyEvent, EventType.ActivityTaskCompleted);
154+
155+
byte[] converted = converter.toData("abc", testData);
156+
Object[] fromConverted = converter.fromDataArray(converted, String.class, TestData.class);
157+
assertEquals(new String(converted, StandardCharsets.UTF_8), "abc", fromConverted[0]);
158+
assertEquals(new String(converted, StandardCharsets.UTF_8), testData, fromConverted[1]);
159+
}
160+
161+
60162
public static void foo(List<UUID> arg) {}
61163

62164
@Test

0 commit comments

Comments
 (0)