Skip to content

Commit d4eb9a2

Browse files
committed
Migrate from GSON to our own serializer/deserializer
There's a problem where GSON will merrily decode a number as a double. While technically correct, this causes Bad Things to happen with interop. Fortunatley our own JSON <-> Bean converters Do The Right Thing already. Switch to using those.
1 parent f9aa0e2 commit d4eb9a2

File tree

9 files changed

+63
-50
lines changed

9 files changed

+63
-50
lines changed

java/server/src/org/openqa/selenium/remote/server/AllHandlers.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
import com.google.common.collect.ImmutableList;
2424
import com.google.common.collect.ImmutableMap;
2525
import com.google.common.collect.ImmutableSet;
26-
import com.google.gson.Gson;
27-
import com.google.gson.GsonBuilder;
2826

27+
import org.openqa.selenium.remote.BeanToJsonConverter;
28+
import org.openqa.selenium.remote.JsonToBeanConverter;
2929
import org.openqa.selenium.remote.SessionId;
3030
import org.openqa.selenium.remote.http.HttpMethod;
3131
import org.openqa.selenium.remote.server.commandhandler.GetAllSessions;
@@ -51,12 +51,16 @@
5151

5252
class AllHandlers {
5353

54-
private final static Gson GSON = new GsonBuilder().setLenient().serializeNulls().create();
54+
private final JsonToBeanConverter toBean;
55+
private final BeanToJsonConverter toJson;
5556
private final ActiveSessions allSessions;
5657

5758
private final Map<HttpMethod, ImmutableList<Function<String, CommandHandler>>> additionalHandlers;
5859

5960
public AllHandlers(ActiveSessions allSessions) {
61+
this.toBean = new JsonToBeanConverter();
62+
this.toJson = new BeanToJsonConverter();
63+
6064
this.allSessions = allSessions;
6165

6266
additionalHandlers = ImmutableMap.of(
@@ -99,12 +103,12 @@ public CommandHandler match(HttpServletRequest req) {
99103
if (id != null) {
100104
ActiveSession session = allSessions.get(id);
101105
if (session == null) {
102-
return new NoSessionHandler(id);
106+
return new NoSessionHandler(toJson, id);
103107
}
104108
return session;
105109
}
106110

107-
return new NoHandler();
111+
return new NoHandler(toJson);
108112
}
109113

110114
private <H extends CommandHandler> Function<String, CommandHandler> handler(
@@ -119,7 +123,8 @@ private <H extends CommandHandler> Function<String, CommandHandler> handler(
119123

120124
ImmutableSet.Builder<Object> args = ImmutableSet.builder();
121125
args.add(allSessions);
122-
args.add(GSON);
126+
args.add(toBean);
127+
args.add(toJson);
123128
if (match.getParameters().containsKey("sessionId")) {
124129
SessionId id = new SessionId(match.getParameters().get("sessionId"));
125130
args.add(id);

java/server/src/org/openqa/selenium/remote/server/commandhandler/GetAllSessions.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import static org.openqa.selenium.remote.ErrorCodes.SUCCESS;
2424

2525
import com.google.common.collect.ImmutableMap;
26-
import com.google.gson.GsonBuilder;
2726

27+
import org.openqa.selenium.remote.BeanToJsonConverter;
2828
import org.openqa.selenium.remote.http.HttpRequest;
2929
import org.openqa.selenium.remote.http.HttpResponse;
3030
import org.openqa.selenium.remote.server.ActiveSessions;
@@ -34,13 +34,16 @@
3434
import java.util.ArrayList;
3535
import java.util.List;
3636
import java.util.Map;
37+
import java.util.Objects;
3738

3839
public class GetAllSessions implements CommandHandler {
3940

40-
private volatile ActiveSessions allSessions;
41+
private final ActiveSessions allSessions;
42+
private final BeanToJsonConverter toJson;
4143

42-
public GetAllSessions(ActiveSessions allSessions) {
43-
this.allSessions = allSessions;
44+
public GetAllSessions(ActiveSessions allSessions, BeanToJsonConverter toJson) {
45+
this.allSessions = Objects.requireNonNull(allSessions);
46+
this.toJson = Objects.requireNonNull(toJson);
4447
}
4548

4649
@Override
@@ -55,10 +58,7 @@ public void execute(HttpRequest req, HttpResponse resp) throws IOException {
5558
"value", value);
5659

5760
// Write out a minimal W3C status response.
58-
byte[] payload = new GsonBuilder()
59-
.serializeNulls()
60-
.create()
61-
.toJson(payloadObj).getBytes(UTF_8);
61+
byte[] payload = toJson.convert(payloadObj).getBytes(UTF_8);
6262

6363
resp.setStatus(HTTP_OK);
6464
resp.setHeader("Content-Type", JSON_UTF_8.toString());

java/server/src/org/openqa/selenium/remote/server/commandhandler/GetLogTypes.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,28 @@
2121
import static org.openqa.selenium.remote.http.HttpMethod.GET;
2222

2323
import com.google.common.collect.ImmutableSet;
24-
import com.google.gson.Gson;
25-
import com.google.gson.reflect.TypeToken;
2624

2725
import org.openqa.selenium.logging.LogType;
2826
import org.openqa.selenium.remote.ErrorCodes;
27+
import org.openqa.selenium.remote.JsonToBeanConverter;
2928
import org.openqa.selenium.remote.Response;
3029
import org.openqa.selenium.remote.http.HttpRequest;
3130
import org.openqa.selenium.remote.http.HttpResponse;
3231
import org.openqa.selenium.remote.server.ActiveSession;
3332
import org.openqa.selenium.remote.server.CommandHandler;
3433

3534
import java.io.IOException;
36-
import java.lang.reflect.Type;
3735
import java.util.Collection;
3836
import java.util.Map;
3937
import java.util.Objects;
4038

4139
public class GetLogTypes implements CommandHandler {
4240

43-
private static final Type MAP_TYPE = new TypeToken<Map<String, Object>>(){}.getType();
44-
private final Gson gson;
41+
private final JsonToBeanConverter toBean;
4542
private final ActiveSession session;
4643

47-
public GetLogTypes(Gson gson, ActiveSession session) {
48-
this.gson = Objects.requireNonNull(gson);
44+
public GetLogTypes(JsonToBeanConverter toBean, ActiveSession session) {
45+
this.toBean = Objects.requireNonNull(toBean);
4946
this.session = Objects.requireNonNull(session);
5047
}
5148

@@ -60,7 +57,7 @@ public void execute(HttpRequest req, HttpResponse resp) throws IOException {
6057
types.add(LogType.SERVER);
6158

6259
if (upRes.getStatus() == HTTP_OK) {
63-
Map<String, Object> upstream = gson.fromJson(upRes.getContentString(), MAP_TYPE);
60+
Map<?, ?> upstream = toBean.convert(Map.class, upRes.getContentString());
6461
Object raw = upstream.get("value");
6562
if (raw instanceof Collection) {
6663
((Collection<?>) raw).stream().map(String::valueOf).forEach(types::add);

java/server/src/org/openqa/selenium/remote/server/commandhandler/GetLogsOfType.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,10 @@
2020
import static java.nio.charset.StandardCharsets.UTF_8;
2121
import static org.openqa.selenium.remote.http.HttpMethod.POST;
2222

23-
import com.google.gson.Gson;
24-
2523
import org.openqa.selenium.logging.LogEntries;
2624
import org.openqa.selenium.logging.LogType;
2725
import org.openqa.selenium.remote.ErrorCodes;
26+
import org.openqa.selenium.remote.JsonToBeanConverter;
2827
import org.openqa.selenium.remote.Response;
2928
import org.openqa.selenium.remote.http.HttpRequest;
3029
import org.openqa.selenium.remote.http.HttpResponse;
@@ -34,22 +33,23 @@
3433

3534
import java.io.IOException;
3635
import java.util.Map;
36+
import java.util.Objects;
3737

3838
public class GetLogsOfType implements CommandHandler {
3939

40-
private final Gson gson;
40+
private final JsonToBeanConverter toBean;
4141
private final ActiveSession session;
4242

43-
public GetLogsOfType(Gson gson, ActiveSession session) {
44-
this.gson = gson;
45-
this.session = session;
43+
public GetLogsOfType(JsonToBeanConverter toBean, ActiveSession session) {
44+
this.toBean = Objects.requireNonNull(toBean);
45+
this.session = Objects.requireNonNull(session);
4646
}
4747

4848
@Override
4949
public void execute(HttpRequest req, HttpResponse resp) throws IOException {
5050
String originalPayload = req.getContentString();
5151

52-
Map<?, ?> args = gson.fromJson(originalPayload, Map.class);
52+
Map<?, ?> args = toBean.convert(Map.class, originalPayload);
5353
String type = (String) args.get("type");
5454

5555
if (!LogType.SERVER.equals(type)) {

java/server/src/org/openqa/selenium/remote/server/commandhandler/NoHandler.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import static org.openqa.selenium.remote.ErrorCodes.UNKNOWN_COMMAND;
2424

2525
import com.google.common.collect.ImmutableMap;
26-
import com.google.gson.GsonBuilder;
2726

27+
import org.openqa.selenium.remote.BeanToJsonConverter;
2828
import org.openqa.selenium.remote.http.HttpRequest;
2929
import org.openqa.selenium.remote.http.HttpResponse;
3030
import org.openqa.selenium.remote.server.CommandHandler;
@@ -33,9 +33,16 @@
3333
import java.util.Collections;
3434
import java.util.HashMap;
3535
import java.util.Map;
36+
import java.util.Objects;
3637

3738
public class NoHandler implements CommandHandler {
3839

40+
private final BeanToJsonConverter toJson;
41+
42+
public NoHandler(BeanToJsonConverter toJson) {
43+
this.toJson = Objects.requireNonNull(toJson);
44+
}
45+
3946
@Override
4047
public void execute(HttpRequest req, HttpResponse resp) throws IOException {
4148
// We're not using ImmutableMap for the outer map because it disallows null values.
@@ -51,8 +58,7 @@ public void execute(HttpRequest req, HttpResponse resp) throws IOException {
5158
"stacktrace", ""));
5259
responseMap = Collections.unmodifiableMap(responseMap);
5360

54-
byte[] payload = new GsonBuilder().serializeNulls().create().toJson(responseMap)
55-
.getBytes(UTF_8);
61+
byte[] payload = toJson.convert(responseMap).getBytes(UTF_8);
5662

5763
resp.setStatus(HTTP_NOT_FOUND);
5864
resp.setHeader("Content-Type", JSON_UTF_8.toString());

java/server/src/org/openqa/selenium/remote/server/commandhandler/NoSessionHandler.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import static org.openqa.selenium.remote.ErrorCodes.NO_SUCH_SESSION;
2424

2525
import com.google.common.collect.ImmutableMap;
26-
import com.google.gson.GsonBuilder;
2726

27+
import org.openqa.selenium.remote.BeanToJsonConverter;
2828
import org.openqa.selenium.remote.SessionId;
2929
import org.openqa.selenium.remote.http.HttpRequest;
3030
import org.openqa.selenium.remote.http.HttpResponse;
@@ -34,13 +34,16 @@
3434
import java.util.Collections;
3535
import java.util.HashMap;
3636
import java.util.Map;
37+
import java.util.Objects;
3738

3839
public class NoSessionHandler implements CommandHandler {
3940

41+
private final BeanToJsonConverter toJson;
4042
private final SessionId sessionId;
4143

42-
public NoSessionHandler(SessionId sessionId) {
43-
this.sessionId = sessionId;
44+
public NoSessionHandler(BeanToJsonConverter toJson, SessionId sessionId) {
45+
this.toJson = Objects.requireNonNull(toJson);
46+
this.sessionId = Objects.requireNonNull(sessionId);
4447
}
4548

4649
@Override
@@ -55,8 +58,7 @@ public void execute(HttpRequest req, HttpResponse resp) throws IOException {
5558
"stacktrace", ""));
5659
responseMap = Collections.unmodifiableMap(responseMap);
5760

58-
byte[] payload = new GsonBuilder().serializeNulls().create().toJson(responseMap)
59-
.getBytes(UTF_8);
61+
byte[] payload = toJson.convert(responseMap).getBytes(UTF_8);
6062

6163
resp.setStatus(HTTP_NOT_FOUND);
6264
resp.setHeader("Content-Type", JSON_UTF_8.toString());

java/server/src/org/openqa/selenium/remote/server/commandhandler/Status.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,25 @@
2323
import static org.openqa.selenium.remote.ErrorCodes.SUCCESS;
2424

2525
import com.google.common.collect.ImmutableMap;
26-
import com.google.gson.GsonBuilder;
2726

2827
import org.openqa.selenium.internal.BuildInfo;
28+
import org.openqa.selenium.remote.BeanToJsonConverter;
2929
import org.openqa.selenium.remote.http.HttpRequest;
3030
import org.openqa.selenium.remote.http.HttpResponse;
3131
import org.openqa.selenium.remote.server.CommandHandler;
3232

3333
import java.io.IOException;
3434
import java.util.Map;
35+
import java.util.Objects;
3536

3637
public class Status implements CommandHandler {
3738

39+
private final BeanToJsonConverter toJson;
40+
41+
Status(BeanToJsonConverter toJson) {
42+
this.toJson = Objects.requireNonNull(toJson);
43+
}
44+
3845
@Override
3946
public void execute(HttpRequest req, HttpResponse resp) throws IOException {
4047
ImmutableMap.Builder<String, Object> value = ImmutableMap.builder();
@@ -63,10 +70,7 @@ public void execute(HttpRequest req, HttpResponse resp) throws IOException {
6370
"value", value.build());
6471

6572
// Write out a minimal W3C status response.
66-
byte[] payload = new GsonBuilder()
67-
.serializeNulls()
68-
.create()
69-
.toJson(payloadObj).getBytes(UTF_8);
73+
byte[] payload = toJson.convert(payloadObj).getBytes(UTF_8);
7074

7175
resp.setStatus(HTTP_OK);
7276
resp.setHeader("Content-Type", JSON_UTF_8.toString());

java/server/src/org/openqa/selenium/remote/server/commandhandler/UploadFile.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@
1717

1818
package org.openqa.selenium.remote.server.commandhandler;
1919

20-
import com.google.gson.Gson;
21-
2220
import org.openqa.selenium.WebDriverException;
2321
import org.openqa.selenium.io.Zip;
2422
import org.openqa.selenium.remote.ErrorCodes;
23+
import org.openqa.selenium.remote.JsonToBeanConverter;
2524
import org.openqa.selenium.remote.Response;
2625
import org.openqa.selenium.remote.http.HttpRequest;
2726
import org.openqa.selenium.remote.http.HttpResponse;
@@ -35,17 +34,17 @@
3534

3635
public class UploadFile implements CommandHandler {
3736

38-
private final Gson gson;
37+
private final JsonToBeanConverter toBean;
3938
private final ActiveSession session;
4039

41-
public UploadFile(Gson gson, ActiveSession session) {
42-
this.gson = Objects.requireNonNull(gson);
40+
public UploadFile(JsonToBeanConverter toBean, ActiveSession session) {
41+
this.toBean = Objects.requireNonNull(toBean);
4342
this.session = Objects.requireNonNull(session);
4443
}
4544

4645
@Override
4746
public void execute(HttpRequest req, HttpResponse resp) throws IOException {
48-
Map<?, ?> args = gson.fromJson(req.getContentString(), Map.class);
47+
Map<?, ?> args = toBean.convert(Map.class, req.getContentString());
4948
String file = (String) args.get("file");
5049

5150
File tempDir = session.getFileSystem().createTempDir("upload", "file");

java/server/test/org/openqa/selenium/remote/server/commandhandler/UploadFileTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void shouldWriteABase64EncodedZippedFileToDiskAndKeepName() throws Except
7979
String encoded = Zip.zip(tempFile);
8080

8181
Gson gson = new Gson();
82-
UploadFile uploadFile = new UploadFile(gson, session);
82+
UploadFile uploadFile = new UploadFile(new JsonToBeanConverter(), session);
8383
Map<String, Object> args = ImmutableMap.of("file", (Object) encoded);
8484
HttpRequest request = new HttpRequest(HttpMethod.POST, "/session/%d/se/file");
8585
request.setContent(gson.toJson(args).getBytes(UTF_8));
@@ -106,7 +106,7 @@ public void shouldThrowAnExceptionIfMoreThanOneFileIsSent() throws Exception {
106106
String encoded = Zip.zip(baseDir);
107107

108108
Gson gson = new Gson();
109-
UploadFile uploadFile = new UploadFile(gson, session);
109+
UploadFile uploadFile = new UploadFile(new JsonToBeanConverter(), session);
110110
Map<String, Object> args = ImmutableMap.of("file", (Object) encoded);
111111
HttpRequest request = new HttpRequest(HttpMethod.POST, "/session/%d/se/file");
112112
request.setContent(gson.toJson(args).getBytes(UTF_8));

0 commit comments

Comments
 (0)