diff --git a/core/build.gradle b/core/build.gradle index 45186a0e140..86935b15a61 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -170,7 +170,6 @@ dependencies { implementation deps['com.google.oauth-client:google-oauth-client-servlet'] implementation deps['com.google.re2j:re2j'] implementation deps['org.freemarker:freemarker'] - implementation deps['com.googlecode.json-simple:json-simple'] implementation deps['com.github.mwiede:jsch'] implementation deps['com.zaxxer:HikariCP'] implementation deps['com.squareup.okhttp3:okhttp'] diff --git a/core/gradle.lockfile b/core/gradle.lockfile index 26f67966487..9b854803129 100644 --- a/core/gradle.lockfile +++ b/core/gradle.lockfile @@ -192,7 +192,6 @@ com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationPr com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle diff --git a/core/src/main/java/google/registry/flows/CheckApiAction.java b/core/src/main/java/google/registry/flows/CheckApiAction.java index e46dc7527bd..47455617137 100644 --- a/core/src/main/java/google/registry/flows/CheckApiAction.java +++ b/core/src/main/java/google/registry/flows/CheckApiAction.java @@ -36,13 +36,13 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm; import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.util.DomainNameUtils.canonicalizeHostname; -import static org.json.simple.JSONValue.toJSONString; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; import com.google.common.net.InternetDomainName; import com.google.common.net.MediaType; +import com.google.gson.Gson; import dagger.Module; import dagger.Provides; import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException; @@ -83,6 +83,7 @@ public class CheckApiAction implements Runnable { @Inject Response response; @Inject CheckApiMetric.Builder metricBuilder; @Inject CheckApiMetrics checkApiMetrics; + @Inject Gson gson; @Inject CheckApiAction() {} @@ -94,7 +95,7 @@ public void run() { response.setHeader("X-Content-Type-Options", "nosniff"); response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); response.setContentType(MediaType.JSON_UTF_8); - response.setPayload(toJSONString(doCheck())); + response.setPayload(gson.toJson(doCheck())); } finally { CheckApiMetric metric = metricBuilder.build(); checkApiMetrics.incrementCheckApiRequest(metric); diff --git a/core/src/main/java/google/registry/flows/EppController.java b/core/src/main/java/google/registry/flows/EppController.java index d69776d9653..60c7a7515d4 100644 --- a/core/src/main/java/google/registry/flows/EppController.java +++ b/core/src/main/java/google/registry/flows/EppController.java @@ -24,6 +24,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.flogger.FluentLogger; +import com.google.gson.Gson; import google.registry.flows.FlowModule.EppExceptionInProviderException; import google.registry.model.eppcommon.Trid; import google.registry.model.eppinput.EppInput; @@ -34,7 +35,6 @@ import google.registry.monitoring.whitebox.EppMetric; import jakarta.inject.Inject; import java.util.Optional; -import org.json.simple.JSONValue; /** * An implementation of the EPP command/response protocol. @@ -50,6 +50,8 @@ public final class EppController { @Inject EppMetric.Builder eppMetricBuilder; @Inject EppMetrics eppMetrics; @Inject ServerTridProvider serverTridProvider; + @Inject Gson gson; + @Inject EppController() {} /** Reads EPP XML, executes the matching flow, and returns an {@link EppOutput}. */ @@ -72,7 +74,7 @@ public EppOutput handleEppCommand( e.getMessage(), lazy( () -> - JSONValue.toJSONString( + gson.toJson( ImmutableMap.of( "clientId", nullToEmpty(sessionMetadata.getRegistrarId()), diff --git a/core/src/main/java/google/registry/flows/FlowReporter.java b/core/src/main/java/google/registry/flows/FlowReporter.java index 39c2b1d2914..b4a6b2578d6 100644 --- a/core/src/main/java/google/registry/flows/FlowReporter.java +++ b/core/src/main/java/google/registry/flows/FlowReporter.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Streams; import com.google.common.flogger.FluentLogger; +import com.google.gson.Gson; import google.registry.flows.FlowModule.InputXml; import google.registry.flows.FlowModule.RegistrarId; import google.registry.flows.annotations.ReportingSpec; @@ -30,7 +31,6 @@ import google.registry.model.eppinput.EppInput; import jakarta.inject.Inject; import java.util.Optional; -import org.json.simple.JSONValue; /** Reporter used by {@link FlowRunner} to record flow execution data for reporting. */ public class FlowReporter { @@ -49,6 +49,8 @@ public class FlowReporter { @Inject @InputXml byte[] inputXmlBytes; @Inject EppInput eppInput; @Inject Class flowClass; + @Inject Gson gson; + @Inject FlowReporter() {} /** Records information about the current flow execution in the request logs. */ @@ -61,7 +63,7 @@ public void recordToLogs() { logger.atInfo().log( "%s: %s", METADATA_LOG_SIGNATURE, - JSONValue.toJSONString( + gson.toJson( new ImmutableMap.Builder() .put("serverTrid", trid.getServerTransactionId()) .put("clientId", registrarId) diff --git a/core/src/main/java/google/registry/request/JsonResponse.java b/core/src/main/java/google/registry/request/JsonResponse.java index 6f7cf06e1a5..7eb5d4f8386 100644 --- a/core/src/main/java/google/registry/request/JsonResponse.java +++ b/core/src/main/java/google/registry/request/JsonResponse.java @@ -18,8 +18,8 @@ import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION; import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS; import static com.google.common.net.MediaType.JSON_UTF_8; -import static org.json.simple.JSONValue.toJSONString; +import com.google.gson.Gson; import jakarta.inject.Inject; import java.time.Instant; import java.util.Map; @@ -31,10 +31,12 @@ public class JsonResponse { public static final String JSON_SAFETY_PREFIX = ")]}'\n"; protected final Response response; + protected final Gson gson; @Inject - public JsonResponse(Response rsp) { + public JsonResponse(Response rsp, Gson gson) { this.response = rsp; + this.gson = gson; } /** @@ -55,7 +57,7 @@ public void setPayload(Map responseMap) { // response, even if all else fails. It's basically another anti-sniffing mechanism in the sense // that if you hit this url directly, it would try to download the file instead of showing it. response.setHeader(CONTENT_DISPOSITION, "attachment"); - response.setPayload(JSON_SAFETY_PREFIX + toJSONString(checkNotNull(responseMap))); + response.setPayload(JSON_SAFETY_PREFIX + gson.toJson(checkNotNull(responseMap))); } /** diff --git a/core/src/main/java/google/registry/request/RequestModule.java b/core/src/main/java/google/registry/request/RequestModule.java index 3355a2268b3..3d158098acf 100644 --- a/core/src/main/java/google/registry/request/RequestModule.java +++ b/core/src/main/java/google/registry/request/RequestModule.java @@ -14,6 +14,7 @@ package google.registry.request; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.net.MediaType.JSON_UTF_8; import static google.registry.dns.PublishDnsUpdatesAction.CLOUD_TASKS_RETRY_HEADER; import static google.registry.model.tld.Tlds.assertTldExists; @@ -28,7 +29,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.io.ByteStreams; @@ -36,6 +36,8 @@ import com.google.common.net.MediaType; import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; import com.google.protobuf.ByteString; import dagger.Module; import dagger.Provides; @@ -50,8 +52,6 @@ import java.io.IOException; import java.util.Map; import java.util.Optional; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; /** Dagger module for servlets. */ @Module @@ -202,18 +202,16 @@ static LockHandler provideLockHandler(LockHandlerImpl lockHandler) { @Provides @JsonPayload - @SuppressWarnings("unchecked") static Map provideJsonPayload( - @Header("Content-Type") MediaType contentType, @Payload String payload) { + @Header("Content-Type") MediaType contentType, @Payload String payload, Gson gson) { if (!JSON_UTF_8.is(contentType.withCharset(UTF_8))) { throw new UnsupportedMediaTypeException( String.format("Expected %s Content-Type", JSON_UTF_8.withoutParameters())); } try { - return (Map) JSONValue.parseWithException(payload); - } catch (ParseException e) { - throw new BadRequestException( - "Malformed JSON", new VerifyException("Malformed JSON:\n" + payload, e)); + return checkNotNull(gson.fromJson(payload, new TypeToken<>() {})); + } catch (JsonSyntaxException | NullPointerException e) { + throw new BadRequestException("Malformed JSON:\n" + payload); } } diff --git a/core/src/main/java/google/registry/security/JsonHttp.java b/core/src/main/java/google/registry/security/JsonHttp.java index 0dbf152a63f..3927d3f3bc3 100644 --- a/core/src/main/java/google/registry/security/JsonHttp.java +++ b/core/src/main/java/google/registry/security/JsonHttp.java @@ -18,10 +18,13 @@ import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION; import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS; import static com.google.common.net.MediaType.JSON_UTF_8; -import static org.json.simple.JSONValue.writeJSONString; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import google.registry.tools.GsonUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @@ -29,8 +32,6 @@ import java.io.Writer; import java.util.Map; import javax.annotation.Nullable; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; /** * Helper class for servlets that read or write JSON. @@ -41,6 +42,8 @@ public final class JsonHttp { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private static final Gson GSON = GsonUtils.provideGson(); + /** String prefixed to all JSON-like responses. */ public static final String JSON_SAFETY_PREFIX = ")]}'\n"; @@ -51,7 +54,6 @@ public final class JsonHttp { * @throws IOException if we failed to read from {@code req}. */ @Nullable - @SuppressWarnings("unchecked") public static Map read(HttpServletRequest req) throws IOException { if (!"POST".equals(req.getMethod()) && !"PUT".equals(req.getMethod())) { @@ -64,8 +66,8 @@ public final class JsonHttp { } try (Reader jsonReader = req.getReader()) { try { - return checkNotNull((Map) JSONValue.parseWithException(jsonReader)); - } catch (ParseException | NullPointerException | ClassCastException e) { + return checkNotNull(GSON.fromJson(jsonReader, new TypeToken<>() {})); + } catch (JsonSyntaxException | NullPointerException | ClassCastException e) { logger.atWarning().withCause(e).log("Malformed JSON."); return null; } @@ -88,7 +90,7 @@ public static void write(HttpServletResponse rsp, Map jsonObject) thr rsp.setHeader(CONTENT_DISPOSITION, "attachment"); try (Writer writer = rsp.getWriter()) { writer.write(JSON_SAFETY_PREFIX); - writeJSONString(jsonObject, writer); + GSON.toJson(jsonObject, writer); } } } diff --git a/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java b/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java index fc416a38986..0760db78c9e 100644 --- a/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java +++ b/core/src/main/java/google/registry/tools/GenerateDnsReportCommand.java @@ -25,6 +25,7 @@ import com.beust.jcommander.Parameters; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; import google.registry.model.domain.Domain; import google.registry.model.host.Host; import google.registry.persistence.transaction.QueryComposer.Comparator; @@ -38,7 +39,6 @@ import java.time.Instant; import java.util.List; import java.util.Map; -import org.json.simple.JSONValue; /** Command to generate a report of all DNS data. */ @Parameters(separators = " =", commandDescription = "Generate report of all DNS data in a TLD.") @@ -57,6 +57,7 @@ final class GenerateDnsReportCommand implements Command { private Path output = Paths.get("/dev/stdout"); @Inject Clock clock; + @Inject Gson gson; @Override public void run() throws Exception { @@ -144,7 +145,7 @@ private void writeJson(Map map) { } else { result.append(",\n"); } - result.append(JSONValue.toJSONString(map)); + result.append(gson.toJson(map)); } } } diff --git a/core/src/main/java/google/registry/tools/GsonUtils.java b/core/src/main/java/google/registry/tools/GsonUtils.java index f50ad8e50ec..676e6762be7 100644 --- a/core/src/main/java/google/registry/tools/GsonUtils.java +++ b/core/src/main/java/google/registry/tools/GsonUtils.java @@ -16,6 +16,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.ToNumberPolicy; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; @@ -82,6 +83,7 @@ public static Gson provideGson() { .registerTypeAdapter(Serializable.class, new SerializableJsonTypeAdapter()) .registerTypeAdapterFactory(new ClassProcessingTypeAdapterFactory()) .registerTypeAdapterFactory(new GsonPostProcessableTypeAdapterFactory()) + .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) .excludeFieldsWithoutExposeAnnotation() .create(); } diff --git a/core/src/main/java/google/registry/tools/ListObjectsCommand.java b/core/src/main/java/google/registry/tools/ListObjectsCommand.java index 0343917ea47..1c97a4ee82b 100644 --- a/core/src/main/java/google/registry/tools/ListObjectsCommand.java +++ b/core/src/main/java/google/registry/tools/ListObjectsCommand.java @@ -23,10 +23,13 @@ import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableMap; import com.google.common.net.MediaType; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; import java.util.List; import java.util.Map; import javax.annotation.Nullable; -import org.json.simple.JSONValue; /** * Abstract base class for commands that list objects by calling a server task. @@ -35,6 +38,8 @@ */ abstract class ListObjectsCommand implements CommandWithConnection { + private static final Gson GSON = GsonUtils.provideGson(); + @Nullable @Parameter( names = {"-f", "--fields"}, @@ -87,14 +92,13 @@ public void run() throws Exception { connection.sendPostRequest( getCommandPath(), params.build(), MediaType.PLAIN_TEXT_UTF_8, new byte[0]); // Parse the returned JSON and make sure it's a map. - Object obj = JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length())); - if (!(obj instanceof Map)) { + JsonElement element = JsonParser.parseString(response.substring(JSON_SAFETY_PREFIX.length())); + if (!element.isJsonObject()) { throw new VerifyException("Server returned unexpected JSON: " + response); } - @SuppressWarnings("unchecked") - Map responseMap = (Map) obj; + Map responseMap = GSON.fromJson(element, new TypeToken<>() {}); // Get the status. - obj = responseMap.get("status"); + Object obj = responseMap.get("status"); if (obj == null) { throw new VerifyException("Server returned no status"); } diff --git a/core/src/main/java/google/registry/tools/ServiceConnection.java b/core/src/main/java/google/registry/tools/ServiceConnection.java index 24fef077739..cb9131d9c4b 100644 --- a/core/src/main/java/google/registry/tools/ServiceConnection.java +++ b/core/src/main/java/google/registry/tools/ServiceConnection.java @@ -34,6 +34,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.io.CharStreams; import com.google.common.net.MediaType; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import google.registry.config.RegistryConfig.Config; import google.registry.request.Action.Service; import jakarta.inject.Inject; @@ -42,7 +44,6 @@ import java.net.URL; import java.util.Map; import javax.annotation.Nullable; -import org.json.simple.JSONValue; /** * An HTTP connection to a service. @@ -55,23 +56,25 @@ public class ServiceConnection { private final Service service; private final boolean useCanary; private final HttpRequestFactory requestFactory; + private final Gson gson; @Inject ServiceConnection( - @Config("useCanary") boolean useCanary, - HttpRequestFactory requestFactory) { - this(Service.BACKEND, requestFactory, useCanary); + @Config("useCanary") boolean useCanary, HttpRequestFactory requestFactory, Gson gson) { + this(Service.BACKEND, requestFactory, useCanary, gson); } - private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) { + private ServiceConnection( + Service service, HttpRequestFactory requestFactory, boolean useCanary, Gson gson) { this.service = service; this.requestFactory = requestFactory; this.useCanary = useCanary; + this.gson = gson; } /** Returns a copy of this connection that talks to a different service endpoint. */ public ServiceConnection withService(Service service, boolean useCanary) { - return new ServiceConnection(service, requestFactory, useCanary); + return new ServiceConnection(service, requestFactory, useCanary, gson); } /** Returns the HTML from the connection error stream, if any, otherwise the empty string. */ @@ -99,7 +102,7 @@ url, new ByteArrayContent(contentType.toString(), payload)) request.setFollowRedirects(false); request.setThrowExceptionOnExecuteError(false); request.setUnsuccessfulResponseHandler( - (request1, response, supportsRetry) -> { + (request1, response, _) -> { String error = getErrorHtmlAsString(response); throw new IOException( String.format( @@ -137,14 +140,10 @@ public String sendGetRequest(String endpoint, Map params) throws IOEx return internalSend(endpoint, params, MediaType.PLAIN_TEXT_UTF_8, null); } - @SuppressWarnings("unchecked") public Map sendJson(String endpoint, Map object) throws IOException { String response = sendPostRequest( - endpoint, - ImmutableMap.of(), - JSON_UTF_8, - JSONValue.toJSONString(object).getBytes(UTF_8)); - return (Map) JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length())); + endpoint, ImmutableMap.of(), JSON_UTF_8, gson.toJson(object).getBytes(UTF_8)); + return gson.fromJson(response.substring(JSON_SAFETY_PREFIX.length()), new TypeToken<>() {}); } } diff --git a/core/src/test/java/google/registry/flows/CheckApiActionTest.java b/core/src/test/java/google/registry/flows/CheckApiActionTest.java index 927edef4e03..4cb19e3c0d1 100644 --- a/core/src/test/java/google/registry/flows/CheckApiActionTest.java +++ b/core/src/test/java/google/registry/flows/CheckApiActionTest.java @@ -29,6 +29,7 @@ import static google.registry.util.DateTimeUtils.START_INSTANT; import static org.mockito.Mockito.verify; +import com.google.gson.reflect.TypeToken; import google.registry.bsa.persistence.BsaTestingUtils; import google.registry.model.tld.Tld; import google.registry.monitoring.whitebox.CheckApiMetric; @@ -39,10 +40,10 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; +import google.registry.tools.GsonUtils; import java.time.Instant; import java.util.Map; import java.util.Optional; -import org.json.simple.JSONValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -83,9 +84,9 @@ void beforeEach() { .build()); } - @SuppressWarnings("unchecked") private Map getCheckResponse(String domain) { CheckApiAction action = new CheckApiAction(); + action.gson = GsonUtils.provideGson(); action.domain = domain; action.response = new FakeResponse(); action.metricBuilder = CheckApiMetric.builder(fakeClock); @@ -94,7 +95,8 @@ private Map getCheckResponse(String domain) { endTime = fakeClock.now(); action.run(); - return (Map) JSONValue.parse(((FakeResponse) action.response).getPayload()); + return action.gson.fromJson( + ((FakeResponse) action.response).getPayload(), new TypeToken<>() {}); } @Test diff --git a/core/src/test/java/google/registry/flows/EppControllerTest.java b/core/src/test/java/google/registry/flows/EppControllerTest.java index 1b53d7efb7c..5a2a3e2c385 100644 --- a/core/src/test/java/google/registry/flows/EppControllerTest.java +++ b/core/src/test/java/google/registry/flows/EppControllerTest.java @@ -31,6 +31,8 @@ import com.google.common.base.Splitter; import com.google.common.testing.TestLogHandler; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.EppTestComponent.FakeServerTridProvider; import google.registry.flows.FlowModule.EppExceptionInProviderException; @@ -43,6 +45,7 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; +import google.registry.tools.GsonUtils; import google.registry.util.Clock; import google.registry.xml.ValidationMode; import java.time.Instant; @@ -50,7 +53,6 @@ import java.util.Map; import java.util.logging.LogRecord; import java.util.logging.Logger; -import org.json.simple.JSONValue; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,6 +84,7 @@ class EppControllerTest { @Mock Result result; private static final Instant START_TIME = Instant.parse("2016-09-01T00:00:00Z"); + private static final Gson GSON = GsonUtils.provideGson(); private final Clock clock = new FakeClock(START_TIME); private final TestLogHandler logHandler = new TestLogHandler(); @@ -110,6 +113,7 @@ void beforeEach() throws Exception { when(result.getCode()).thenReturn(Code.SUCCESS_WITH_NO_MESSAGES); eppController = new EppController(); + eppController.gson = GSON; eppController.eppMetricBuilder = EppMetric.builderForRequest(clock); when(flowRunner.run(eppController.eppMetricBuilder)).thenReturn(eppOutput); eppController.flowComponentBuilder = flowComponentBuilder; @@ -247,8 +251,7 @@ void testHandleEppCommand_throwsRuntimeException_loggedAtSevere() throws Excepti assertThat(logRecord.getThrown()).isInstanceOf(IllegalStateException.class); } - @SuppressWarnings("unchecked") private static Map parseJsonMap(String json) throws Exception { - return (Map) JSONValue.parseWithException(json); + return GSON.fromJson(json, new TypeToken<>() {}); } } diff --git a/core/src/test/java/google/registry/flows/EppTestComponent.java b/core/src/test/java/google/registry/flows/EppTestComponent.java index 1a4d621ee73..8fa0779dab9 100644 --- a/core/src/test/java/google/registry/flows/EppTestComponent.java +++ b/core/src/test/java/google/registry/flows/EppTestComponent.java @@ -28,6 +28,7 @@ import google.registry.flows.domain.DomainDeletionTimeCache; import google.registry.flows.domain.DomainFlowTmchUtils; import google.registry.monitoring.whitebox.EppMetric; +import google.registry.request.Modules.GsonModule; import google.registry.request.RequestScope; import google.registry.request.lock.LockHandler; import google.registry.testing.CloudTasksHelper; @@ -42,7 +43,8 @@ /** Dagger component for running EPP tests. */ @Singleton -@Component(modules = {ConfigModule.class, EppTestComponent.FakesAndMocksModule.class}) +@Component( + modules = {ConfigModule.class, GsonModule.class, EppTestComponent.FakesAndMocksModule.class}) public interface EppTestComponent { RequestComponent startRequest(); diff --git a/core/src/test/java/google/registry/flows/FlowReporterTest.java b/core/src/test/java/google/registry/flows/FlowReporterTest.java index 47313e82f68..564bb5b54ae 100644 --- a/core/src/test/java/google/registry/flows/FlowReporterTest.java +++ b/core/src/test/java/google/registry/flows/FlowReporterTest.java @@ -22,22 +22,26 @@ import com.google.common.collect.ImmutableList; import com.google.common.testing.TestLogHandler; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import google.registry.flows.annotations.ReportingSpec; import google.registry.model.eppcommon.Trid; import google.registry.model.eppinput.EppInput; import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting; import google.registry.model.eppoutput.EppResponse; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; +import google.registry.tools.GsonUtils; import google.registry.util.JdkLoggerConfig; import java.util.Map; import java.util.Optional; -import org.json.simple.JSONValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** Unit tests for {@link FlowReporter}. */ class FlowReporterTest { + private static final Gson GSON = GsonUtils.provideGson(); + static class TestCommandFlow implements Flow { @Override public ResponseOrGreeting run() { @@ -60,6 +64,7 @@ public ResponseOrGreeting run() { void beforeEach() { JdkLoggerConfig.getConfig(FlowReporter.class).addHandler(handler); flowReporter.trid = Trid.create("client-123", "server-456"); + flowReporter.gson = GSON; flowReporter.registrarId = "TheRegistrar"; flowReporter.inputXmlBytes = "".getBytes(UTF_8); flowReporter.flowClass = TestCommandFlow.class; @@ -205,8 +210,7 @@ void testRecordToLogs_metadata_domainWithoutPeriod_noTld() throws Exception { assertThat(json).containsEntry("tlds", ImmutableList.of()); } - @SuppressWarnings("unchecked") private static Map parseJsonMap(String json) throws Exception { - return (Map) JSONValue.parseWithException(json); + return GSON.fromJson(json, new TypeToken<>() {}); } } diff --git a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java index ceddfcc5055..8b8e241d60c 100644 --- a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java +++ b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.testing.TestLogHandler; +import com.google.gson.Gson; import google.registry.model.EppResource; import google.registry.model.ForeignKeyUtils; import google.registry.model.domain.DomainBase; @@ -34,13 +35,13 @@ import google.registry.model.tmch.ClaimsListDao; import google.registry.testing.DatabaseHelper; import google.registry.testing.TestCacheExtension; +import google.registry.tools.GsonUtils; import google.registry.util.JdkLoggerConfig; import google.registry.util.TypeUtils.TypeInstantiator; import java.time.Duration; import java.time.Instant; import java.util.logging.Level; import javax.annotation.Nullable; -import org.json.simple.JSONValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; @@ -53,6 +54,8 @@ public abstract class ResourceFlowTestCase extends FlowTestCase { + private static final Gson GSON = GsonUtils.provideGson(); + protected final TestLogHandler logHandler = new TestLogHandler(); @RegisterExtension @@ -108,21 +111,23 @@ protected void assertClientIdFieldLogged(String registrarId) { .that(logHandler) .hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA") .which() - .contains("\"clientId\":" + JSONValue.toJSONString(registrarId)); + .contains("\"clientId\":" + GSON.toJson(registrarId)); } protected void assertTldsFieldLogged(String... tlds) { - assertAboutLogs().that(logHandler) + assertAboutLogs() + .that(logHandler) .hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA") .which() - .contains("\"tlds\":" + JSONValue.toJSONString(ImmutableList.copyOf(tlds))); + .contains("\"tlds\":" + GSON.toJson(ImmutableList.copyOf(tlds))); } protected void assertIcannReportingActivityFieldLogged(String fieldName) { - assertAboutLogs().that(logHandler) + assertAboutLogs() + .that(logHandler) .hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA") .which() - .contains("\"icannActivityReportField\":" + JSONValue.toJSONString(fieldName)); + .contains("\"icannActivityReportField\":" + GSON.toJson(fieldName)); } protected void assertLastHistoryContainsResource(EppResource resource) { diff --git a/core/src/test/java/google/registry/request/JsonResponseTest.java b/core/src/test/java/google/registry/request/JsonResponseTest.java index cf674b57aeb..89899cfd9dc 100644 --- a/core/src/test/java/google/registry/request/JsonResponseTest.java +++ b/core/src/test/java/google/registry/request/JsonResponseTest.java @@ -18,17 +18,21 @@ import static google.registry.request.JsonResponse.JSON_SAFETY_PREFIX; import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import google.registry.testing.FakeResponse; +import google.registry.tools.GsonUtils; import java.time.Instant; import java.util.Map; -import org.json.simple.JSONValue; import org.junit.jupiter.api.Test; /** Unit tests for {@link JsonResponse}. */ class JsonResponseTest { + private static final Gson GSON = GsonUtils.provideGson(); + private FakeResponse fakeResponse = new FakeResponse(); - private JsonResponse jsonResponse = new JsonResponse(fakeResponse); + private JsonResponse jsonResponse = new JsonResponse(fakeResponse, GSON); @Test void testSetStatus() { @@ -44,9 +48,8 @@ void testSetResponseValue() { jsonResponse.setPayload(responseValues); String payload = fakeResponse.getPayload(); assertThat(payload).startsWith(JSON_SAFETY_PREFIX); - @SuppressWarnings("unchecked") - Map responseMap = (Map) - JSONValue.parse(payload.substring(JSON_SAFETY_PREFIX.length())); + Map responseMap = + GSON.fromJson(payload.substring(JSON_SAFETY_PREFIX.length()), new TypeToken<>() {}); assertThat(responseMap).containsExactlyEntriesIn(responseValues); } diff --git a/core/src/test/java/google/registry/request/RequestModuleTest.java b/core/src/test/java/google/registry/request/RequestModuleTest.java index 483d478ffcf..04d62c2138b 100644 --- a/core/src/test/java/google/registry/request/RequestModuleTest.java +++ b/core/src/test/java/google/registry/request/RequestModuleTest.java @@ -19,21 +19,26 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.net.MediaType; +import com.google.gson.Gson; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.UnsupportedMediaTypeException; +import google.registry.tools.GsonUtils; import org.junit.jupiter.api.Test; /** Unit tests for {@link RequestModule}. */ final class RequestModuleTest { + private static final Gson GSON = GsonUtils.provideGson(); + @Test void testProvideJsonPayload() { - assertThat(provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":\"v\"}")).containsExactly("k", "v"); + assertThat(provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":\"v\"}", GSON)) + .containsExactly("k", "v"); } @Test void testProvideJsonPayload_contentTypeWithoutCharsetAllowed() { - assertThat(provideJsonPayload(MediaType.JSON_UTF_8.withoutParameters(), "{\"k\":\"v\"}")) + assertThat(provideJsonPayload(MediaType.JSON_UTF_8.withoutParameters(), "{\"k\":\"v\"}", GSON)) .containsExactly("k", "v"); } @@ -41,14 +46,16 @@ void testProvideJsonPayload_contentTypeWithoutCharsetAllowed() { void testProvideJsonPayload_malformedInput_throws500() { BadRequestException thrown = assertThrows( - BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":")); + BadRequestException.class, + () -> provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":", GSON)); assertThat(thrown).hasMessageThat().contains("Malformed JSON"); } @Test void testProvideJsonPayload_emptyInput_throws500() { BadRequestException thrown = - assertThrows(BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, "")); + assertThrows( + BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, "", GSON)); assertThat(thrown).hasMessageThat().contains("Malformed JSON"); } @@ -56,13 +63,13 @@ void testProvideJsonPayload_emptyInput_throws500() { void testProvideJsonPayload_nonJsonContentType_throws415() { assertThrows( UnsupportedMediaTypeException.class, - () -> provideJsonPayload(MediaType.PLAIN_TEXT_UTF_8, "{}")); + () -> provideJsonPayload(MediaType.PLAIN_TEXT_UTF_8, "{}", GSON)); } @Test void testProvideJsonPayload_contentTypeWithWeirdParam_throws415() { assertThrows( UnsupportedMediaTypeException.class, - () -> provideJsonPayload(MediaType.JSON_UTF_8.withParameter("omg", "handel"), "{}")); + () -> provideJsonPayload(MediaType.JSON_UTF_8.withParameter("omg", "handel"), "{}", GSON)); } } diff --git a/core/src/test/java/google/registry/security/JsonHttpTestUtils.java b/core/src/test/java/google/registry/security/JsonHttpTestUtils.java index 4a407cbfead..bcf18627e50 100644 --- a/core/src/test/java/google/registry/security/JsonHttpTestUtils.java +++ b/core/src/test/java/google/registry/security/JsonHttpTestUtils.java @@ -20,21 +20,25 @@ import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX; import com.google.common.base.Supplier; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import google.registry.tools.GsonUtils; import java.io.BufferedReader; import java.io.StringReader; import java.io.StringWriter; import java.util.Map; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; /** * Helper class for testing JSON RPC servlets. */ public final class JsonHttpTestUtils { + private static final Gson GSON = GsonUtils.provideGson(); + /** Returns JSON payload for mocked result of {@code rsp.getReader()}. */ public static BufferedReader createJsonPayload(Map object) { - return createJsonPayload(JSONValue.toJSONString(object)); + return createJsonPayload(GSON.toJson(object)); } /** @see #createJsonPayload(Map) */ @@ -58,10 +62,8 @@ public static Map getJsonResponse(StringWriter writer) { assertThat(jsonText).startsWith(JSON_SAFETY_PREFIX); jsonText = jsonText.substring(JSON_SAFETY_PREFIX.length()); try { - @SuppressWarnings("unchecked") - Map json = (Map) JSONValue.parseWithException(jsonText); - return json; - } catch (ClassCastException | ParseException e) { + return GSON.fromJson(jsonText, new TypeToken<>() {}); + } catch (ClassCastException | JsonSyntaxException e) { assertWithMessage("Bad JSON: %s\n%s", e.getMessage(), jsonText).fail(); throw new AssertionError(); } diff --git a/core/src/test/java/google/registry/testing/FakeJsonResponse.java b/core/src/test/java/google/registry/testing/FakeJsonResponse.java index 28783411c35..221f4e4b1c7 100644 --- a/core/src/test/java/google/registry/testing/FakeJsonResponse.java +++ b/core/src/test/java/google/registry/testing/FakeJsonResponse.java @@ -15,6 +15,7 @@ package google.registry.testing; import google.registry.request.JsonResponse; +import google.registry.tools.GsonUtils; import java.util.Map; /** Fake implementation of {@link JsonResponse} for testing. */ @@ -23,7 +24,7 @@ public final class FakeJsonResponse extends JsonResponse { private Map responseMap; public FakeJsonResponse() { - super(new FakeResponse()); + super(new FakeResponse(), GsonUtils.provideGson()); } @Override diff --git a/core/src/test/java/google/registry/tools/GcpProjectConnectionTest.java b/core/src/test/java/google/registry/tools/GcpProjectConnectionTest.java index 0071471ebdd..a8f7c744e5a 100644 --- a/core/src/test/java/google/registry/tools/GcpProjectConnectionTest.java +++ b/core/src/test/java/google/registry/tools/GcpProjectConnectionTest.java @@ -85,7 +85,8 @@ void beforeEach() throws Exception { when(lowLevelHttpResponse.getStatusCode()).thenReturn(200); httpTransport = new TestHttpTransport(); - connection = new ServiceConnection(false, httpTransport.createRequestFactory()); + connection = + new ServiceConnection(false, httpTransport.createRequestFactory(), GsonUtils.provideGson()); } @Test diff --git a/core/src/test/java/google/registry/tools/GenerateDnsReportCommandTest.java b/core/src/test/java/google/registry/tools/GenerateDnsReportCommandTest.java index ef35caa39b6..70e0bb03a5e 100644 --- a/core/src/test/java/google/registry/tools/GenerateDnsReportCommandTest.java +++ b/core/src/test/java/google/registry/tools/GenerateDnsReportCommandTest.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.net.InetAddresses; +import com.google.gson.Gson; import google.registry.model.domain.Domain; import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.eppcommon.StatusValue; @@ -41,19 +42,19 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** Unit tests for {@link GenerateDnsReportCommand}. */ class GenerateDnsReportCommandTest extends CommandTestCase { + private static final Gson GSON = GsonUtils.provideGson(); + private Path output; - private Object getOutputAsJson() throws IOException, ParseException { + private Object getOutputAsJson() throws IOException { try (Reader reader = Files.newBufferedReader(output, UTF_8)) { - return JSONValue.parseWithException(reader); + return GSON.fromJson(reader, Object.class); } } @@ -114,6 +115,7 @@ private Object getOutputAsJson() throws IOException, ParseException { void beforeEach() { output = tmpDir.resolve("out.dat"); command.clock = fakeClock; + command.gson = GSON; createTlds("xn--q9jyb4c", "example"); nameserver1 = diff --git a/core/src/test/java/google/registry/tools/ServiceConnectionTest.java b/core/src/test/java/google/registry/tools/ServiceConnectionTest.java index 026fa57de93..cb339efa885 100644 --- a/core/src/test/java/google/registry/tools/ServiceConnectionTest.java +++ b/core/src/test/java/google/registry/tools/ServiceConnectionTest.java @@ -26,6 +26,7 @@ import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponse; import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; import google.registry.request.Action.Service; import java.io.ByteArrayInputStream; import org.junit.jupiter.api.Test; @@ -33,10 +34,12 @@ /** Unit tests for {@link google.registry.tools.ServiceConnection}. */ public class ServiceConnectionTest { + private static final Gson GSON = GsonUtils.provideGson(); + @Test void testSuccess_serverUrl_notCanary() { ServiceConnection connection = - new ServiceConnection(false, null).withService(Service.FRONTEND, false); + new ServiceConnection(false, null, GSON).withService(Service.FRONTEND, false); String serverUrl = connection.getServer().toString(); assertThat(serverUrl).isEqualTo("https://frontend.registry.test"); // See default-config.yaml } @@ -52,7 +55,7 @@ void testSuccess_serverUrl_gke_canary() throws Exception { when(request.execute()).thenReturn(response); when(response.getContent()).thenReturn(ByteArrayInputStream.nullInputStream()); ServiceConnection connection = - new ServiceConnection(false, factory).withService(Service.PUBAPI, true); + new ServiceConnection(false, factory, GSON).withService(Service.PUBAPI, true); String serverUrl = connection.getServer().toString(); assertThat(serverUrl).isEqualTo("https://pubapi.registry.test"); connection.sendGetRequest("/path", ImmutableMap.of()); diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleOteActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleOteActionTest.java index 21bb589e8b0..95a17856d78 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleOteActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleOteActionTest.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.gson.reflect.TypeToken; import google.registry.model.OteStatsTestHelper; import google.registry.model.console.GlobalRole; import google.registry.model.console.User; @@ -44,7 +45,6 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import org.json.simple.JSONArray; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -131,7 +131,7 @@ void testSuccess_oteCreated() { Optional.of(new OteCreateData("theregistrar", "contact@registry.example"))); action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils(); action.run(); - var obsResponse = GSON.fromJson(response.getPayload(), Map.class); + Map obsResponse = GSON.fromJson(response.getPayload(), new TypeToken<>() {}); assertThat( ImmutableMap.of( "theregistrar-1", "theregistrar-sunrise", @@ -175,7 +175,7 @@ void testSuccess_finishedOte() throws Exception { Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty()); action.run(); - List> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class); + List> responseMaps = GSON.fromJson(response.getPayload(), new TypeToken<>() {}); assertThat(response.getStatus()).isEqualTo(SC_OK); assertTrue( responseMaps.stream().allMatch(status -> Boolean.TRUE.equals(status.get("completed")))); @@ -191,7 +191,7 @@ void testSuccess_unfinishedOte() throws Exception { Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty()); action.run(); - List> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class); + List> responseMaps = GSON.fromJson(response.getPayload(), new TypeToken<>() {}); assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat( responseMaps.stream() diff --git a/dependencies.gradle b/dependencies.gradle index b69c99535b8..1abeee03c19 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -116,7 +116,6 @@ ext { 'com.google.oauth-client:google-oauth-client:[1.31.4,)', 'com.google.re2j:re2j:[1.6,)', 'com.google.truth:truth:[1.1.2,)', - 'com.googlecode.json-simple:json-simple:[1.1.1,)', 'com.squareup.okhttp3:okhttp:[4.10.0,)', 'org.freemarker:freemarker:[2.3.32,)', 'com.ibm.icu:icu4j:[68.2,)', diff --git a/java_common.gradle b/java_common.gradle index bd7622e1ece..f2a79b89c7a 100644 --- a/java_common.gradle +++ b/java_common.gradle @@ -55,8 +55,7 @@ configurations { matching { it.name in ['runtimeClasspath', 'compileClasspath'] }.all { - // JUnit is from org.apache.beam:beam-runners-google-cloud-dataflow-java, - // and json-simple. + // JUnit is from org.apache.beam:beam-runners-google-cloud-dataflow-java exclude group: 'junit' // Mockito is from org.apache.beam:beam-runners-google-cloud-dataflow-java // See https://issues.apache.org/jira/browse/BEAM-8862 diff --git a/jetty/gradle.lockfile b/jetty/gradle.lockfile index eb065a8b661..fa3455a3ba6 100644 --- a/jetty/gradle.lockfile +++ b/jetty/gradle.lockfile @@ -162,7 +162,6 @@ com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProce com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.googlecode.json-simple:json-simple:1.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.ibm.icu:icu4j:73.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle