From f302008d6ff187b70436285b68fa61b0eef8cb3b Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Wed, 2 Jun 2021 14:09:17 -0700 Subject: [PATCH 1/3] Moved Jackson from being an external SDK dependency to an internal dependency. Jackson-databind was removed from being a dependency (except in the code generator). Jackson-core and Jackson-dataformat-cbor were moved to shaded dependencies. This was done because Jackson versions are not 100% compatible between minor versions and keeping them up to date were problematic for SDK customers. Customers will see a change in artifact sizes. Customers which do not use Jackson-databind outside of the SDK today will see a ~1 MB artifact size decrease. Customers which do use Jackson-databind outside of the SDK will see a ~0.5 MB artifact size increase. This change required breaking inter-module (protected) APIs, so it should not be released outside of an SDK minor version bump. Customers will not be able to use older client versions with this version of the core libraries. Change summary: 1. Added 'jackson-core' and 'jackson-dataformat-cbor' which contain the SDK's shaded versions with the Jackson packages of the same names. 2. Removed 'aws-ion-protocol' module. This was not used by any public AWS services and can be re-added when service support is needed. 3. Added 'json-utils', a library for reading and parsing JSON. --- .../feature-AWSSDKforJavav2-9e94920.json | 6 + .gitignore | 4 + NOTICE.txt | 2 + bom-internal/pom.xml | 5 - bom/pom.xml | 20 +- bundle/pom.xml | 11 - codegen/pom.xml | 13 +- .../codegen/model/intermediate/Metadata.java | 5 - .../codegen/model/intermediate/Protocol.java | 3 +- .../codegen/poet/client/SyncClientClass.java | 1 - .../poet/client/specs/JsonProtocolSpec.java | 4 - .../poet/transform/MarshallerSpec.java | 1 - core/auth/pom.xml | 5 +- .../credentials/HttpCredentialsProvider.java | 24 +- .../ProcessCredentialsProvider.java | 33 +- .../aws-ion-protocol => json-utils}/pom.xml | 58 +- .../awssdk/protocols/jsoncore/JsonNode.java | 185 +++++++ .../protocols/jsoncore/JsonNodeParser.java | 231 ++++++++ .../jsoncore/internal/ArrayJsonNode.java | 87 +++ .../jsoncore/internal/BooleanJsonNode.java | 78 +++ .../internal/EmbeddedObjectJsonNode.java | 80 +++ .../jsoncore/internal/NullJsonNode.java | 81 +++ .../jsoncore/internal/NumberJsonNode.java | 78 +++ .../jsoncore/internal/ObjectJsonNode.java | 93 ++++ .../jsoncore/internal/StringJsonNode.java | 84 +++ .../protocols/jsoncore/JsonNodeTest.java | 269 ++++++++++ core/pom.xml | 1 + core/protocols/aws-cbor-protocol/pom.xml | 6 +- .../internal/AwsStructuredCborFactory.java | 2 +- .../cbor/internal/SdkCborGenerator.java | 4 +- .../internal/SdkStructuredCborFactory.java | 4 +- .../protocols/ion/AwsIonProtocolFactory.java | 88 --- .../ion/internal/AwsStructuredIonFactory.java | 67 --- .../internal/CompositeErrorCodeParser.java | 43 -- .../ion/internal/IonErrorCodeParser.java | 82 --- .../protocols/ion/internal/IonFactory.java | 88 --- .../protocols/ion/internal/IonParser.java | 415 -------------- .../ion/internal/SdkIonGenerator.java | 248 --------- .../ion/internal/SdkStructuredIonFactory.java | 42 -- .../ion/AwsStructuredIonFactoryTest.java | 200 ------- .../awssdk/protocols/ion/IonFactoryTest.java | 113 ---- .../awssdk/protocols/ion/IonParserTest.java | 424 --------------- .../protocols/ion/IonRoundtripTest.java | 504 ------------------ .../awssdk/protocols/ion/ValidSdkObjects.java | 40 -- core/protocols/aws-json-protocol/pom.xml | 9 +- .../json/BaseAwsJsonProtocolFactory.java | 6 +- .../json/BaseAwsStructuredJsonFactory.java | 2 +- .../awssdk/protocols/json/JsonContent.java | 24 +- .../protocols/json/SdkJsonGenerator.java | 4 +- .../protocols/json/StructuredJsonFactory.java | 2 +- .../AwsStructuredPlainJsonFactory.java | 5 +- .../json/internal/dom/JsonDomParser.java | 96 ---- .../json/internal/dom/SdkArrayNode.java | 87 --- .../json/internal/dom/SdkEmbeddedObject.java | 65 --- .../json/internal/dom/SdkJsonNode.java | 71 --- .../json/internal/dom/SdkNullNode.java | 54 -- .../json/internal/dom/SdkObjectNode.java | 98 ---- .../json/internal/dom/SdkScalarNode.java | 72 --- .../unmarshall/AwsJsonErrorMessageParser.java | 4 +- .../AwsJsonProtocolErrorUnmarshaller.java | 2 +- .../unmarshall/ErrorMessageParser.java | 4 +- .../unmarshall/HeaderUnmarshaller.java | 4 +- .../unmarshall/JsonErrorCodeParser.java | 8 +- .../unmarshall/JsonProtocolUnmarshaller.java | 46 +- .../internal/unmarshall/JsonUnmarshaller.java | 4 +- .../unmarshall/SdkJsonErrorMessageParser.java | 8 +- .../json/AwsJsonErrorMessageParserTest.java | 39 +- .../json/JsonErrorCodeParserTest.java | 9 +- .../protocols/json/SdkJsonGeneratorTest.java | 37 +- .../json/internal/dom/JsonDomParserTest.java | 261 --------- core/protocols/pom.xml | 1 - core/regions/pom.xml | 9 +- .../util/EC2MetadataUtilsIntegrationTest.java | 53 -- .../internal/util/EC2MetadataUtils.java | 275 +--------- .../regions/util/HttpResourcesUtils.java | 23 +- .../Ec2MetadataUtilsTt0049160280Test.java | 33 -- core/sdk-core/pom.xml | 9 - .../util/UnreliableFilterInputStream.java | 13 +- .../awssdk/core/util/json/JacksonUtils.java | 109 ---- pom.xml | 6 +- services/sso/pom.xml | 5 + .../sso/internal/SsoAccessTokenProvider.java | 20 +- .../src/main/java/module-info.java | 1 - test/protocol-tests-core/pom.xml | 5 - .../asserts/marshalling/IonBodyAssertion.java | 179 ------- .../marshalling/RequestBodyAssertion.java | 4 - .../protocol/suites/cases/ion-input.ion | 345 ------------ .../protocol/suites/cases/ion-output.ion | 309 ----------- .../awssdk/protocol/suites/ion-suite.json | 6 - test/tests-coverage-reporting/pom.xml | 10 +- third-party/jackson-core/pom.xml | 126 +++++ third-party/jackson-dataformat-cbor/pom.xml | 145 +++++ third-party/pom.xml | 66 +++ 93 files changed, 1859 insertions(+), 4746 deletions(-) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-9e94920.json rename core/{protocols/aws-ion-protocol => json-utils}/pom.xml (62%) create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeParser.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java create mode 100644 core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java create mode 100644 core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java delete mode 100644 core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java delete mode 100644 core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java delete mode 100644 core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java delete mode 100644 core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java delete mode 100644 core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java delete mode 100644 core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java delete mode 100644 core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java delete mode 100644 core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/util/json/JacksonUtils.java delete mode 100644 test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/IonBodyAssertion.java delete mode 100644 test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-input.ion delete mode 100644 test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-output.ion delete mode 100644 test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/ion-suite.json create mode 100644 third-party/jackson-core/pom.xml create mode 100644 third-party/jackson-dataformat-cbor/pom.xml create mode 100644 third-party/pom.xml diff --git a/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json b/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json new file mode 100644 index 000000000000..3d652842dad7 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json @@ -0,0 +1,6 @@ +{ + "category": "AWS SDK for Java v2", + "contributor": "", + "type": "feature", + "description": "Moved Jackson from being an external SDK dependency to an internal dependency.\n\nCustomers will see a change in artifact sizes. Customers which do not use Jackson-databind outside of the SDK today will see a ~1 MB artifact size decrease. Customers which do use Jackson-databind outside of the SDK will see a ~0.5 MB artifact size increase.\n\nThis change required breaking inter-module (protected) APIs, so customers will not be able to use older client versions with this version of the core libraries." +} diff --git a/.gitignore b/.gitignore index d9d47cbdf646..6dc9a39f1427 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,10 @@ # Maven target/ +# JEnv +.java-version + +# Shade **/dependency-reduced-pom.xml *.pyc diff --git a/NOTICE.txt b/NOTICE.txt index aa3a5ce58762..b3b042a1db2a 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -12,6 +12,8 @@ This software includes third party software subject to the following copyrights: - PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc. - Apache Commons Lang - https://github.com/apache/commons-lang - Netty Reactive Streams - https://github.com/playframework/netty-reactive-streams +- Jackson-core - https://github.com/FasterXML/jackson-core +- Jackson-dataformat-cbor - https://github.com/FasterXML/jackson-dataformats-binary The licenses for these third party components are included in LICENSE.txt diff --git a/bom-internal/pom.xml b/bom-internal/pom.xml index 70721df43365..038ca9407744 100644 --- a/bom-internal/pom.xml +++ b/bom-internal/pom.xml @@ -84,11 +84,6 @@ jackson-datatype-jsr310 ${jackson.version} - - software.amazon.ion - ion-java - ${ion.java.version} - org.apache.httpcomponents httpclient diff --git a/bom/pom.xml b/bom/pom.xml index 9b7b2ffaecc3..996a1fbe67bf 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -64,27 +64,37 @@ software.amazon.awssdk - auth + json-utils ${awsjavasdk.version} software.amazon.awssdk - aws-core + jackson-core ${awsjavasdk.version} software.amazon.awssdk - profiles + jackson-dataformat-cbor ${awsjavasdk.version} software.amazon.awssdk - aws-cbor-protocol + auth + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-core + ${awsjavasdk.version} + + + software.amazon.awssdk + profiles ${awsjavasdk.version} software.amazon.awssdk - aws-ion-protocol + aws-cbor-protocol ${awsjavasdk.version} diff --git a/bundle/pom.xml b/bundle/pom.xml index 82228b545c19..15c3d479c3ad 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -70,31 +70,20 @@ com.fasterxml.jackson.jr:* io.netty:* com.typesafe.netty:* - com.fasterxml.jackson.core:* - com.fasterxml.jackson.dataformat:jackson-dataformat-cbor org.apache.httpcomponents:* org.reactivestreams:* org.slf4j:* commons-codec:commons-codec - software.amazon.ion:ion-java software.amazon.awssdk:* software.amazon:* commons-logging:* - - com.fasterxml.jackson - software.amazon.awssdk.thirdparty.com.fasterxml.jackson - org.apache software.amazon.awssdk.thirdparty.org.apache - - software.amazon.ion - software.amazon.awssdk.thirdparty.ion - io.netty software.amazon.awssdk.thirdparty.io.netty diff --git a/codegen/pom.xml b/codegen/pom.xml index b22cbce07c88..49ad58d3c4a6 100644 --- a/codegen/pom.xml +++ b/codegen/pom.xml @@ -102,11 +102,6 @@ aws-cbor-protocol ${awsjavasdk.version} - - software.amazon.awssdk - aws-ion-protocol - ${awsjavasdk.version} - software.amazon.awssdk aws-query-protocol @@ -143,6 +138,14 @@ com.fasterxml.jackson.jr jackson-jr-stree + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + org.slf4j slf4j-api diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java index eb3a83d9fb20..14fc9a3a6cd3 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Metadata.java @@ -499,17 +499,12 @@ public Metadata withJsonVersion(String jsonVersion) { return this; } - public boolean isIonProtocol() { - return protocol == Protocol.ION; - } - public boolean isCborProtocol() { return protocol == Protocol.CBOR; } public boolean isJsonProtocol() { return protocol == Protocol.CBOR || - protocol == Protocol.ION || protocol == Protocol.AWS_JSON || protocol == Protocol.REST_JSON; } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Protocol.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Protocol.java index c24898551033..719d088c77e2 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Protocol.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/Protocol.java @@ -24,8 +24,7 @@ public enum Protocol { REST_JSON("rest-json"), CBOR("cbor"), QUERY("query"), - REST_XML("rest-xml"), - ION("ion"); + REST_XML("rest-xml"); private String protocol; diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java index 7fe594733993..e8c300619e5d 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java @@ -339,7 +339,6 @@ static ProtocolSpec getProtocolSpecs(PoetExtensions poetExtensions, Intermediate case AWS_JSON: case REST_JSON: case CBOR: - case ION: return new JsonProtocolSpec(poetExtensions, model); default: throw new RuntimeException("Unknown protocol: " + protocol.name()); diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/JsonProtocolSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/JsonProtocolSpec.java index efb03a8e7eb9..bfb151877f13 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/JsonProtocolSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/JsonProtocolSpec.java @@ -51,7 +51,6 @@ import software.amazon.awssdk.core.http.HttpResponseHandler; import software.amazon.awssdk.core.protocol.VoidSdkResponse; import software.amazon.awssdk.protocols.cbor.AwsCborProtocolFactory; -import software.amazon.awssdk.protocols.ion.AwsIonProtocolFactory; import software.amazon.awssdk.protocols.json.AwsJsonProtocol; import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory; import software.amazon.awssdk.protocols.json.BaseAwsJsonProtocolFactory; @@ -119,8 +118,6 @@ private CodeBlock customErrorCodeFieldName() { private Class protocolFactoryClass() { if (model.getMetadata().isCborProtocol()) { return AwsCborProtocolFactory.class; - } else if (model.getMetadata().isIonProtocol()) { - return AwsIonProtocolFactory.class; } else { return AwsJsonProtocolFactory.class; } @@ -377,7 +374,6 @@ public Optional createErrorResponseHandler() { private String protocolEnumName(software.amazon.awssdk.codegen.model.intermediate.Protocol protocol) { switch (protocol) { case CBOR: - case ION: case AWS_JSON: return AWS_JSON.name(); default: diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/transform/MarshallerSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/transform/MarshallerSpec.java index 3024048cd15c..8d62120355e4 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/transform/MarshallerSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/transform/MarshallerSpec.java @@ -117,7 +117,6 @@ private MarshallerProtocolSpec getProtocolSpecs(software.amazon.awssdk.codegen.m switch (protocol) { case REST_JSON: case CBOR: - case ION: case AWS_JSON: return getJsonMarshallerSpec(); diff --git a/core/auth/pom.xml b/core/auth/pom.xml index 3c173235b5a6..7b18b9222804 100644 --- a/core/auth/pom.xml +++ b/core/auth/pom.xml @@ -63,8 +63,9 @@ ${awsjavasdk.version} - com.fasterxml.jackson.core - jackson-databind + software.amazon.awssdk + json-utils + ${awsjavasdk.version} software.amazon.eventstream diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java index f2e1aa03d600..b67c1d5d91b3 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java @@ -15,15 +15,15 @@ package software.amazon.awssdk.auth.credentials; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.time.Duration; import java.time.Instant; +import java.util.Map; import java.util.Optional; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.regions.util.HttpResourcesUtils; import software.amazon.awssdk.regions.util.ResourcesEndpointProvider; import software.amazon.awssdk.utils.ComparableUtils; @@ -40,6 +40,11 @@ */ @SdkProtectedApi public abstract class HttpCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable { + private static final JsonNodeParser SENSITIVE_PARSER = + JsonNodeParser.builder() + .removeErrorLocations(true) + .build(); + private final Optional> credentialsCache; protected HttpCredentialsProvider(BuilderImpl builder) { @@ -73,7 +78,7 @@ private RefreshResult refreshCredentials() { try { String credentialsResponse = HttpResourcesUtils.instance().readResource(getCredentialsEndpointProvider()); - JsonNode node = JacksonUtils.sensitiveJsonNodeOf(credentialsResponse); + Map node = SENSITIVE_PARSER.parse(credentialsResponse).asObject(); JsonNode accessKey = node.get("AccessKeyId"); JsonNode secretKey = node.get("SecretAccessKey"); JsonNode token = node.get("Token"); @@ -83,8 +88,8 @@ private RefreshResult refreshCredentials() { Validate.notNull(secretKey, "Failed to load secret key."); AwsCredentials credentials = - token == null ? AwsBasicCredentials.create(accessKey.asText(), secretKey.asText()) - : AwsSessionCredentials.create(accessKey.asText(), secretKey.asText(), token.asText()); + token == null ? AwsBasicCredentials.create(accessKey.text(), secretKey.text()) + : AwsSessionCredentials.create(accessKey.text(), secretKey.text(), token.text()); Instant expiration = getExpiration(expirationNode).orElse(null); if (expiration != null && Instant.now().isAfter(expiration)) { @@ -98,11 +103,6 @@ private RefreshResult refreshCredentials() { .build(); } catch (SdkClientException e) { throw e; - } catch (JsonMappingException e) { - throw SdkClientException.builder() - .message("Unable to parse response returned from service endpoint.") - .cause(e) - .build(); } catch (RuntimeException | IOException e) { throw SdkClientException.builder() .message("Unable to load credentials from service endpoint.") @@ -114,7 +114,7 @@ private RefreshResult refreshCredentials() { private Optional getExpiration(JsonNode expirationNode) { return Optional.ofNullable(expirationNode).map(node -> { // Convert the expirationNode string to ISO-8601 format. - String expirationValue = node.asText().replaceAll("\\+0000$", "Z"); + String expirationValue = node.text().replaceAll("\\+0000$", "Z"); try { return DateUtils.parseIso8601Date(expirationValue); diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java index fac87fbb0e69..4a4cd1eb3cb6 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java @@ -15,7 +15,6 @@ package software.amazon.awssdk.auth.credentials; -import com.fasterxml.jackson.databind.JsonNode; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -25,11 +24,8 @@ import java.util.Collections; import java.util.List; import software.amazon.awssdk.annotations.SdkPublicApi; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.utils.DateUtils; import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.Platform; @@ -58,6 +54,10 @@ */ @SdkPublicApi public final class ProcessCredentialsProvider implements AwsCredentialsProvider { + private static final JsonNodeParser PARSER = JsonNodeParser.builder() + .removeErrorLocations(true) + .build(); + private final List command; private final Duration credentialRefreshThreshold; private final long processOutputLimit; @@ -129,14 +129,14 @@ private RefreshResult refreshCredentials() { * Parse the output from the credentials process. */ private JsonNode parseProcessOutput(String processOutput) { - JsonNode credentialsJson = JacksonUtils.sensitiveJsonNodeOf(processOutput); + JsonNode credentialsJson = PARSER.parse(processOutput); if (!credentialsJson.isObject()) { throw new IllegalStateException("Process did not return a JSON object."); } - JsonNode version = credentialsJson.get("Version"); - if (version == null || !version.isInt() || version.asInt() != 1) { + JsonNode version = credentialsJson.get("Version").orElse(null); + if (version == null || !version.isNumber() || !version.asNumber().equals("1")) { throw new IllegalStateException("Unsupported credential version: " + version); } return credentialsJson; @@ -174,21 +174,10 @@ private Instant credentialExpirationTime(JsonNode credentialsJson) { } /** - * Get a textual value from a json object, throwing an exception if the node is missing or not textual. + * Get a textual value from a json object. */ private String getText(JsonNode jsonObject, String nodeName) { - JsonNode subNode = jsonObject.get(nodeName); - - if (subNode == null) { - return null; - } - - if (!subNode.isTextual()) { - throw new IllegalStateException(nodeName + " from credential process should be textual, but was " + - subNode.getNodeType()); - } - - return subNode.asText(); + return jsonObject.get(nodeName).map(JsonNode::text).orElse(null); } /** diff --git a/core/protocols/aws-ion-protocol/pom.xml b/core/json-utils/pom.xml similarity index 62% rename from core/protocols/aws-ion-protocol/pom.xml rename to core/json-utils/pom.xml index 84cb6dbe3b9e..755c410bb6f1 100644 --- a/core/protocols/aws-ion-protocol/pom.xml +++ b/core/json-utils/pom.xml @@ -18,32 +18,32 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - protocols + core software.amazon.awssdk 2.16.83-SNAPSHOT 4.0.0 - aws-ion-protocol - AWS Java SDK :: Core :: Protocols :: AWS Ion Protocol - The AWS SDK for Java - module holds the classes for AWS Ion protocol - + json-utils + AWS Java SDK :: Core :: Protocols :: Json Utils https://aws.amazon.com/sdkforjava + + + + software.amazon.awssdk + bom-internal + ${project.version} + pom + import + + + + software.amazon.awssdk - aws-core - ${awsjavasdk.version} - - - software.amazon.awssdk - aws-json-protocol - ${awsjavasdk.version} - - - software.amazon.awssdk - sdk-core + utils ${awsjavasdk.version} @@ -53,38 +53,14 @@ software.amazon.awssdk - http-client-spi - ${awsjavasdk.version} - - - software.amazon.awssdk - utils - ${awsjavasdk.version} - - - software.amazon.ion - ion-java - - - com.fasterxml.jackson.core jackson-core - - - software.amazon.awssdk - protocol-core ${awsjavasdk.version} - test junit junit test - - org.hamcrest - hamcrest-all - test - org.assertj assertj-core @@ -105,7 +81,7 @@ - software.amazon.awssdk.protocols.ion + software.amazon.awssdk.protocols.jsoncore diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java new file mode 100644 index 000000000000..2ec4085ba679 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java @@ -0,0 +1,185 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.protocols.jsoncore.internal.ObjectJsonNode; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; + +/** + * A node in a JSON document. Either a number, string, boolean, array, object or null. Also can be an embedded object, + * which is a non-standard type used in JSON extensions, like CBOR. + * + *

Created from a JSON document via {@link #parser()} or {@link #parserBuilder()}. + * + *

The type of node can be determined using "is" methods like {@link #isNumber()} and {@link #isString()}. + * Once the type is determined, the value of the node can be extracted via the "as" methods, like {@link #asNumber()} + * and {@link #asString()}. + */ +@SdkProtectedApi +public interface JsonNode { + /** + * Create a {@link JsonNodeParser} for generating a {@link JsonNode} from a JSON document. + */ + static JsonNodeParser parser() { + return JsonNodeParser.create(); + } + + /** + * Create a {@link JsonNodeParser.Builder} for generating a {@link JsonNode} from a JSON document. + */ + static JsonNodeParser.Builder parserBuilder() { + return JsonNodeParser.builder(); + } + + /** + * Return an empty object node. + */ + static JsonNode emptyObjectNode() { + return new ObjectJsonNode(Collections.emptyMap()); + } + + /** + * Returns true if this node represents a JSON number: https://datatracker.ietf.org/doc/html/rfc8259#section-6 + * + * @see #asNumber() + */ + default boolean isNumber() { + return false; + } + + /** + * Returns true if this node represents a JSON string: https://datatracker.ietf.org/doc/html/rfc8259#section-7 + * + * @see #asString() + */ + default boolean isString() { + return false; + } + + /** + * Returns true if this node represents a JSON boolean: https://datatracker.ietf.org/doc/html/rfc8259#section-3 + * + * @see #asBoolean() + */ + default boolean isBoolean() { + return false; + } + + /** + * Returns true if this node represents a JSON null: https://datatracker.ietf.org/doc/html/rfc8259#section-3 + */ + default boolean isNull() { + return false; + } + + /** + * Returns true if this node represents a JSON array: https://datatracker.ietf.org/doc/html/rfc8259#section-5 + * + * @see #asArray() + */ + default boolean isArray() { + return false; + } + + /** + * Returns true if this node represents a JSON object: https://datatracker.ietf.org/doc/html/rfc8259#section-4 + * + * @see #asObject() + */ + default boolean isObject() { + return false; + } + + /** + * Returns true if this node represents a JSON "embedded object". This non-standard type is associated with JSON extensions, + * like CBOR or ION. It allows additional data types to be embedded in a JSON document, like a timestamp or a raw byte array. + * + *

Users who are only concerned with handling JSON can ignore this field. It will only be present when using a custom + * {@link JsonFactory} via {@link JsonNodeParser.Builder#jsonFactory(JsonFactory)}. + * + * @see #asEmbeddedObject() + */ + default boolean isEmbeddedObject() { + return false; + } + + /** + * When {@link #isNumber()} is true, this returns the number associated with this node. This will throw an exception if + * {@link #isNumber()} is false. + * + * @see #text() + */ + String asNumber(); + + /** + * When {@link #isString()}, is true, this returns the string associated with this node. This will throw an exception if + * {@link #isString()} ()} is false. + */ + String asString(); + + /** + * When {@link #isBoolean()} is true, this returns the boolean associated with this node. This will throw an exception if + * {@link #isBoolean()} is false. + */ + boolean asBoolean(); + + /** + * When {@link #isArray()} is true, this returns the array associated with this node. This will throw an exception if + * {@link #isArray()} is false. + */ + List asArray(); + + /** + * When {@link #isObject()} is true, this returns the object associated with this node. This will throw an exception if + * {@link #isObject()} is false. + */ + Map asObject(); + + /** + * When {@link #isEmbeddedObject()} is true, this returns the embedded object associated with this node. This will throw + * an exception if {@link #isEmbeddedObject()} is false. + * + * @see #isEmbeddedObject() + */ + Object asEmbeddedObject(); + + /** + * When {@link #isString()}, {@link #isBoolean()}, or {@link #isNumber()} is true, this will return the value of this node + * as a textual string. If this is any other type, this will return null. + */ + String text(); + + /** + * When {@link #isObject()} is true, this will return the result of {@code Optional.ofNullable(asObject().get(child))}. If + * this is any other type, this will return {@link Optional#empty()}. + */ + default Optional get(String child) { + return Optional.empty(); + } + + /** + * When {@link #isArray()} is true, this will return the result of {@code asArray().get(child)} if child is within bounds. If + * this is any other type or the child is out of bounds, this will return {@link Optional#empty()}. + */ + default Optional get(int child) { + return Optional.empty(); + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeParser.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeParser.java new file mode 100644 index 000000000000..f87100eab1f5 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeParser.java @@ -0,0 +1,231 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore; + +import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.protocols.jsoncore.internal.ArrayJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.BooleanJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.EmbeddedObjectJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.NullJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.NumberJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.ObjectJsonNode; +import software.amazon.awssdk.protocols.jsoncore.internal.StringJsonNode; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; +import software.amazon.awssdk.thirdparty.jackson.core.JsonParseException; +import software.amazon.awssdk.thirdparty.jackson.core.JsonParser; +import software.amazon.awssdk.thirdparty.jackson.core.JsonToken; +import software.amazon.awssdk.thirdparty.jackson.core.json.JsonReadFeature; + +/** + * Parses an JSON document into a simple DOM-like structure, {@link JsonNode}. + * + *

This is created using {@link #create()} or {@link #builder()}. + */ +@SdkProtectedApi +public final class JsonNodeParser { + /** + * The default {@link JsonFactory} used for {@link #create()} or if a factory is not configured via + * {@link Builder#jsonFactory(JsonFactory)}. + */ + public static final JsonFactory DEFAULT_JSON_FACTORY = + JsonFactory.builder() + .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true) + .build(); + + private final boolean removeErrorLocations; + private final JsonFactory jsonFactory; + + private JsonNodeParser(Builder builder) { + this.removeErrorLocations = builder.removeErrorLocations; + this.jsonFactory = builder.jsonFactory; + } + + /** + * Create a parser using the default configuration. + */ + public static JsonNodeParser create() { + return builder().build(); + } + + /** + * Create a parser using custom configuration. + */ + public static JsonNodeParser.Builder builder() { + return new Builder(); + } + + /** + * Parse the provided {@link InputStream} into a {@link JsonNode}. + */ + public JsonNode parse(InputStream content) { + return invokeSafely(() -> { + try (JsonParser parser = jsonFactory.createParser(content) + .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)) { + return parse(parser); + } + }); + } + + /** + * Parse the provided {@code byte[]} into a {@link JsonNode}. + */ + public JsonNode parse(byte[] content) { + return invokeSafely(() -> { + try (JsonParser parser = jsonFactory.createParser(content) + .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)) { + return parse(parser); + } + }); + } + + /** + * Parse the provided {@link String} into a {@link JsonNode}. + */ + public JsonNode parse(String content) { + return invokeSafely(() -> { + try (JsonParser parser = jsonFactory.createParser(content) + .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)) { + return parse(parser); + } + }); + } + + private JsonNode parse(JsonParser parser) throws IOException { + try { + return parseToken(parser, parser.nextToken()); + } catch (Exception e) { + removeErrorLocationsIfRequired(e); + throw e; + } + } + + private void removeErrorLocationsIfRequired(Throwable exception) { + if (removeErrorLocations) { + removeErrorLocations(exception); + } + } + + private void removeErrorLocations(Throwable exception) { + if (exception == null) { + return; + } + + if (exception instanceof JsonParseException) { + ((JsonParseException) exception).clearLocation(); + } + + removeErrorLocations(exception.getCause()); + } + + private JsonNode parseToken(JsonParser parser, JsonToken token) throws IOException { + if (token == null) { + return null; + } + switch (token) { + case VALUE_STRING: + return new StringJsonNode(parser.getText()); + case VALUE_FALSE: + return new BooleanJsonNode(false); + case VALUE_TRUE: + return new BooleanJsonNode(true); + case VALUE_NULL: + return NullJsonNode.instance(); + case VALUE_NUMBER_FLOAT: + case VALUE_NUMBER_INT: + return new NumberJsonNode(parser.getText()); + case START_OBJECT: + return parseObject(parser); + case START_ARRAY: + return parseArray(parser); + case VALUE_EMBEDDED_OBJECT: + return new EmbeddedObjectJsonNode(parser.getEmbeddedObject()); + default: + throw new IllegalArgumentException("Unexpected JSON token - " + token); + } + } + + private JsonNode parseObject(JsonParser parser) throws IOException { + JsonToken currentToken = parser.nextToken(); + Map object = new LinkedHashMap<>(); + while (currentToken != JsonToken.END_OBJECT) { + String fieldName = parser.getText(); + object.put(fieldName, parseToken(parser, parser.nextToken())); + currentToken = parser.nextToken(); + } + return new ObjectJsonNode(object); + } + + private JsonNode parseArray(JsonParser parser) throws IOException { + JsonToken currentToken = parser.nextToken(); + List array = new ArrayList<>(); + while (currentToken != JsonToken.END_ARRAY) { + array.add(parseToken(parser, currentToken)); + currentToken = parser.nextToken(); + } + return new ArrayJsonNode(array); + } + + /** + * A builder for configuring and creating {@link JsonNodeParser}. Created via {@link #builder()}. + */ + public static final class Builder { + private JsonFactory jsonFactory = DEFAULT_JSON_FACTORY; + private boolean removeErrorLocations = false; + + private Builder() { + } + + /** + * Whether error locations should be removed if parsing fails. This prevents the content of the JSON from appearing in + * error messages. This is useful when the content of the JSON may be sensitive and not want to be logged. + * + *

By default, this is false. + */ + public Builder removeErrorLocations(boolean removeErrorLocations) { + this.removeErrorLocations = removeErrorLocations; + return this; + } + + /** + * The {@link JsonFactory} implementation to be used when parsing the input. This allows JSON extensions like CBOR or + * Ion to be supported. + * + *

It's highly recommended us use a shared {@code JsonFactory} where possible, so they should be stored statically: + * http://wiki.fasterxml.com/JacksonBestPracticesPerformance + * + *

By default, this is {@link #DEFAULT_JSON_FACTORY}. + */ + public Builder jsonFactory(JsonFactory jsonFactory) { + this.jsonFactory = jsonFactory; + return this; + } + + /** + * Build a {@link JsonNodeParser} based on the current configuration of this builder. + */ + public JsonNodeParser build() { + return new JsonNodeParser(this); + } + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java new file mode 100644 index 000000000000..28e1dee3f288 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java @@ -0,0 +1,87 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; + +/** + * An array {@link JsonNode}. + */ +@SdkInternalApi +public final class ArrayJsonNode implements JsonNode { + private final List value; + + public ArrayJsonNode(List value) { + this.value = value; + } + + @Override + public boolean isArray() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON array cannot be converted to a number."); + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON array cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON array cannot be converted to a boolean."); + } + + @Override + public List asArray() { + return value; + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON array cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON array cannot be converted to an embedded object."); + } + + @Override + public String text() { + return null; + } + + @Override + public Optional get(int child) { + if (child < 0 || child >= value.size()) { + return Optional.empty(); + } + return Optional.of(value.get(child)); + } + + @Override + public String toString() { + return value.toString(); + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java new file mode 100644 index 000000000000..8b2665756cac --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; + +/** + * A boolean {@link JsonNode}. + */ +@SdkInternalApi +public final class BooleanJsonNode implements JsonNode { + private final boolean value; + + public BooleanJsonNode(boolean value) { + this.value = value; + } + + @Override + public boolean isBoolean() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON boolean cannot be converted to a number."); + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON boolean cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + return value; + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON boolean cannot be converted to an array."); + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON boolean cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON boolean cannot be converted to an embedded object."); + } + + @Override + public String text() { + return Boolean.toString(value); + } + + @Override + public String toString() { + return Boolean.toString(value); + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java new file mode 100644 index 000000000000..72f4b67f61a5 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java @@ -0,0 +1,80 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.utils.Validate; + +/** + * An embedded object {@link JsonNode}. + */ +@SdkInternalApi +public final class EmbeddedObjectJsonNode implements JsonNode { + private final Object embeddedObject; + + public EmbeddedObjectJsonNode(Object embeddedObject) { + Validate.paramNotNull(embeddedObject, "embeddedObject"); + this.embeddedObject = embeddedObject; + } + + @Override + public boolean isEmbeddedObject() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON embedded object cannot be converted to a number."); + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON embedded object cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON embedded object cannot be converted to a boolean."); + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON embedded object cannot be converted to an array."); + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON embedded object cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + return embeddedObject; + } + + @Override + public String text() { + return null; + } + + @Override + public String toString() { + return "<>"; + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java new file mode 100644 index 000000000000..814f81427471 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; + +/** + * A null {@link JsonNode}. + */ +@SdkInternalApi +public final class NullJsonNode implements JsonNode { + private static final NullJsonNode INSTANCE = new NullJsonNode(); + + private NullJsonNode() { + } + + public static NullJsonNode instance() { + return INSTANCE; + } + + @Override + public boolean isNull() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON null cannot be converted to a number."); + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON null cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON null cannot be converted to a boolean."); + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON null cannot be converted to an array."); + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON null cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON null cannot be converted to an embedded object."); + } + + @Override + public String text() { + return null; + } + + @Override + public String toString() { + return "null"; + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java new file mode 100644 index 000000000000..5a6172eef4c9 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; + +/** + * A numeric {@link JsonNode}. + */ +@SdkInternalApi +public final class NumberJsonNode implements JsonNode { + private final String value; + + public NumberJsonNode(String value) { + this.value = value; + } + + @Override + public boolean isNumber() { + return true; + } + + @Override + public String asNumber() { + return value; + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON number cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON number cannot be converted to a boolean."); + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON number cannot be converted to an array."); + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON number cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON number cannot be converted to an embedded object."); + } + + @Override + public String text() { + return value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java new file mode 100644 index 000000000000..103ddb1824da --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; + +/** + * An object {@link JsonNode}. + */ +@SdkInternalApi +public final class ObjectJsonNode implements JsonNode { + private final Map value; + + public ObjectJsonNode(Map value) { + this.value = value; + } + + @Override + public boolean isObject() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON object cannot be converted to a number."); + } + + @Override + public String asString() { + throw new UnsupportedOperationException("A JSON object cannot be converted to a string."); + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON object cannot be converted to a boolean."); + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON object cannot be converted to an array."); + } + + @Override + public Map asObject() { + return value; + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON object cannot be converted to an embedded object."); + } + + @Override + public String text() { + return null; + } + + @Override + public Optional get(String child) { + return Optional.ofNullable(value.get(child)); + } + + @Override + public String toString() { + if (value.isEmpty()) { + return "{}"; + } + + StringBuilder output = new StringBuilder(); + output.append("{"); + value.forEach((k, v) -> output.append("\"").append(k).append("\": ") + .append(v.toString()).append(",")); + output.setCharAt(output.length() - 1, '}'); + return output.toString(); + } +} diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java new file mode 100644 index 000000000000..2b9221794fb1 --- /dev/null +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.protocols.jsoncore.internal; + +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.utils.Validate; + +/** + * A string {@link JsonNode}. + */ +@SdkInternalApi +public final class StringJsonNode implements JsonNode { + private final String value; + + public StringJsonNode(String value) { + Validate.paramNotNull(value, "value"); + this.value = value; + } + + @Override + public boolean isString() { + return true; + } + + @Override + public String asNumber() { + throw new UnsupportedOperationException("A JSON string cannot be converted to a number."); + } + + @Override + public String asString() { + return value; + } + + @Override + public boolean asBoolean() { + throw new UnsupportedOperationException("A JSON string cannot be converted to a boolean."); + } + + @Override + public List asArray() { + throw new UnsupportedOperationException("A JSON string cannot be converted to an array."); + } + + @Override + public Map asObject() { + throw new UnsupportedOperationException("A JSON string cannot be converted to an object."); + } + + @Override + public Object asEmbeddedObject() { + throw new UnsupportedOperationException("A JSON string cannot be converted to an embedded object."); + } + + @Override + public String text() { + return value; + } + + @Override + public String toString() { + // Does not handle unicode control characters + return "\"" + + value.replace("\\", "\\\\") + .replace("\"", "\\\"") + + "\""; + } +} \ No newline at end of file diff --git a/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java b/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java new file mode 100644 index 000000000000..9d2c03cf71c1 --- /dev/null +++ b/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java @@ -0,0 +1,269 @@ +package software.amazon.awssdk.protocols.jsoncore; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Test; +import software.amazon.awssdk.utils.StringInputStream; + +public class JsonNodeTest { + private static final JsonNodeParser PARSER = JsonNode.parser(); + + @Test + public void parseString_works() { + assertThat(PARSER.parse("{}").isObject()).isTrue(); + } + + @Test + public void parseInputStream_works() { + assertThat(PARSER.parse(new StringInputStream("{}")).isObject()).isTrue(); + } + + @Test + public void parseByteArray_works() { + assertThat(PARSER.parse("{}".getBytes(UTF_8)).isObject()).isTrue(); + } + + @Test + public void parseNull_givesCorrectType() { + JsonNode node = PARSER.parse("null"); + + assertThat(node.isNull()).isTrue(); + assertThat(node.isBoolean()).isFalse(); + assertThat(node.isNumber()).isFalse(); + assertThat(node.isString()).isFalse(); + assertThat(node.isArray()).isFalse(); + assertThat(node.isObject()).isFalse(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asBoolean).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asNumber).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asString).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asArray).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asObject).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + + @Test + public void parseBoolean_givesCorrectType() { + String[] options = { "true", "false" }; + for (String option : options) { + JsonNode node = PARSER.parse(option); + + + assertThat(node.isNull()).isFalse(); + assertThat(node.isBoolean()).isTrue(); + assertThat(node.isNumber()).isFalse(); + assertThat(node.isString()).isFalse(); + assertThat(node.isArray()).isFalse(); + assertThat(node.isObject()).isFalse(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asNumber).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asString).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asArray).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asObject).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + } + + @Test + public void parseNumber_givesCorrectType() { + String[] options = { "-1e100", "-1", "0", "1", "1e100" }; + for (String option : options) { + JsonNode node = PARSER.parse(option); + + assertThat(node.isNull()).isFalse(); + assertThat(node.isBoolean()).isFalse(); + assertThat(node.isNumber()).isTrue(); + assertThat(node.isString()).isFalse(); + assertThat(node.isArray()).isFalse(); + assertThat(node.isObject()).isFalse(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asBoolean).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asString).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asArray).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asObject).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + } + + @Test + public void parseString_givesCorrectType() { + String[] options = { "\"\"", "\"foo\"" }; + for (String option : options) { + JsonNode node = PARSER.parse(option); + + assertThat(node.isNull()).isFalse(); + assertThat(node.isBoolean()).isFalse(); + assertThat(node.isNumber()).isFalse(); + assertThat(node.isString()).isTrue(); + assertThat(node.isArray()).isFalse(); + assertThat(node.isObject()).isFalse(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asBoolean).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asNumber).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asArray).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asObject).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + } + + @Test + public void parseArray_givesCorrectType() { + String[] options = { "[]", "[null]" }; + for (String option : options) { + JsonNode node = PARSER.parse(option); + + assertThat(node.isNull()).isFalse(); + assertThat(node.isBoolean()).isFalse(); + assertThat(node.isNumber()).isFalse(); + assertThat(node.isString()).isFalse(); + assertThat(node.isArray()).isTrue(); + assertThat(node.isObject()).isFalse(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asBoolean).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asNumber).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asString).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asObject).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + } + + @Test + public void parseObject_givesCorrectType() { + String[] options = { "{}", "{ \"foo\": null }" }; + for (String option : options) { + JsonNode node = PARSER.parse(option); + + assertThat(node.isNull()).isFalse(); + assertThat(node.isBoolean()).isFalse(); + assertThat(node.isNumber()).isFalse(); + assertThat(node.isString()).isFalse(); + assertThat(node.isArray()).isFalse(); + assertThat(node.isObject()).isTrue(); + assertThat(node.isEmbeddedObject()).isFalse(); + + assertThatThrownBy(node::asBoolean).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asNumber).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asString).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asArray).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(node::asEmbeddedObject).isInstanceOf(UnsupportedOperationException.class); + } + } + + @Test + public void parseBoolean_givesCorrectValue() { + assertThat(PARSER.parse("true").asBoolean()).isTrue(); + assertThat(PARSER.parse("false").asBoolean()).isFalse(); + } + + @Test + public void parseNumber_givesCorrectValue() { + assertThat(PARSER.parse("0").asNumber()).isEqualTo("0"); + assertThat(PARSER.parse("-1").asNumber()).isEqualTo("-1"); + assertThat(PARSER.parse("1").asNumber()).isEqualTo("1"); + assertThat(PARSER.parse("1e10000").asNumber()).isEqualTo("1e10000"); + assertThat(PARSER.parse("-1e10000").asNumber()).isEqualTo("-1e10000"); + assertThat(PARSER.parse("1.23").asNumber()).isEqualTo("1.23"); + assertThat(PARSER.parse("-1.23").asNumber()).isEqualTo("-1.23"); + } + + @Test + public void parseString_givesCorrectValue() { + assertThat(PARSER.parse("\"foo\"").asString()).isEqualTo("foo"); + assertThat(PARSER.parse("\"\"").asString()).isEqualTo(""); + assertThat(PARSER.parse("\" \"").asString()).isEqualTo(" "); + assertThat(PARSER.parse("\"%20\"").asString()).isEqualTo("%20"); + assertThat(PARSER.parse("\"\\\"\"").asString()).isEqualTo("\""); + assertThat(PARSER.parse("\" \"").asString()).isEqualTo(" "); + } + + @Test + public void parseArray_givesCorrectValue() { + assertThat(PARSER.parse("[]").asArray()).isEmpty(); + assertThat(PARSER.parse("[null, 1]").asArray()).satisfies(list -> { + assertThat(list).hasSize(2); + assertThat(list.get(0).isNull()).isTrue(); + assertThat(list.get(1).asNumber()).isEqualTo("1"); + }); + } + + @Test + public void parseObject_givesCorrectValue() { + assertThat(PARSER.parse("{}").asObject()).isEmpty(); + assertThat(PARSER.parse("{\"foo\": \"bar\", \"baz\": 0}").asObject()).satisfies(map -> { + assertThat(map).hasSize(2); + assertThat(map.get("foo").asString()).isEqualTo("bar"); + assertThat(map.get("baz").asNumber()).isEqualTo("0"); + }); + } + + @Test + public void text_returnsContent() { + assertThat(PARSER.parse("null").text()).isEqualTo(null); + assertThat(PARSER.parse("0").text()).isEqualTo("0"); + assertThat(PARSER.parse("\"foo\"").text()).isEqualTo("foo"); + assertThat(PARSER.parse("true").text()).isEqualTo("true"); + assertThat(PARSER.parse("[]").text()).isEqualTo(null); + assertThat(PARSER.parse("{}").text()).isEqualTo(null); + } + + @Test + public void getString_returnsContent() { + assertThat(PARSER.parse("null").get("")).isEmpty(); + assertThat(PARSER.parse("0").get("")).isEmpty(); + assertThat(PARSER.parse("\"foo\"").get("")).isEmpty(); + assertThat(PARSER.parse("true").get("")).isEmpty(); + assertThat(PARSER.parse("[]").get("")).isEmpty(); + assertThat(PARSER.parse("{\"\":0}").get("")).map(JsonNode::asNumber).hasValue("0"); + } + + @Test + public void getArray_returnsContent() { + assertThat(PARSER.parse("null").get(0)).isEmpty(); + assertThat(PARSER.parse("0").get(0)).isEmpty(); + assertThat(PARSER.parse("\"foo\"").get(0)).isEmpty(); + assertThat(PARSER.parse("true").get(0)).isEmpty(); + assertThat(PARSER.parse("[]").get(0)).isEmpty(); + assertThat(PARSER.parse("[null]").get(0)).map(JsonNode::isNull).hasValue(true); + assertThat(PARSER.parse("{}").get("")).isEmpty(); + } + + @Test + public void toStringIsCorrect() { + String input = "{" + + "\"1\": \"2\"," + + "\"3\": 4," + + "\"5\": null," + + "\"6\": false," + + "\"7\": [[{}]]," + + "\"8\": \"\\\\n\\\"\"" + + "}"; + assertThat(PARSER.parse(input).toString()).isEqualTo(input); + } + + @Test + public void exceptionsIncludeErrorLocation() { + assertThatThrownBy(() -> PARSER.parse("{{foo}")).hasMessageContaining("foo"); + } + + @Test + public void removeErrorLocations_removesErrorLocations() { + assertThatThrownBy(() -> JsonNode.parserBuilder() + .removeErrorLocations(true) + .build() + .parse("{{foo}")) + .satisfies(exception -> { + Throwable cause = exception; + while (cause != null) { + assertThat(cause.getMessage()).doesNotContain("foo"); + cause = cause.getCause(); + } + }); + } +} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 2b64bec49a27..1c00532a3db3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -42,6 +42,7 @@ regions protocols metrics-spi + json-utils diff --git a/core/protocols/aws-cbor-protocol/pom.xml b/core/protocols/aws-cbor-protocol/pom.xml index 9c26898500b5..c40ea79430b3 100644 --- a/core/protocols/aws-cbor-protocol/pom.xml +++ b/core/protocols/aws-cbor-protocol/pom.xml @@ -47,12 +47,14 @@ ${awsjavasdk.version} - com.fasterxml.jackson.dataformat + software.amazon.awssdk jackson-dataformat-cbor + ${awsjavasdk.version} - com.fasterxml.jackson.core + software.amazon.awssdk jackson-core + ${awsjavasdk.version} junit diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java index d9f9144d6bf2..64092399fef4 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java @@ -15,10 +15,10 @@ package software.amazon.awssdk.protocols.cbor.internal; -import com.fasterxml.jackson.core.JsonFactory; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.protocols.json.BaseAwsStructuredJsonFactory; import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; /** * Creates generators and protocol handlers for CBOR wire format. diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java index 0520530a5103..67f2bd7b21b0 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java @@ -15,13 +15,13 @@ package software.amazon.awssdk.protocols.cbor.internal; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.dataformat.cbor.CBORGenerator; import java.io.IOException; import java.time.Instant; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.protocols.json.SdkJsonGenerator; import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; +import software.amazon.awssdk.thirdparty.jackson.dataformat.cbor.CBORGenerator; /** * Thin wrapper around Jackson's JSON generator for CBOR. diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java index 3e014774db63..62d4d93ab8ce 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java @@ -15,11 +15,11 @@ package software.amazon.awssdk.protocols.cbor.internal; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.dataformat.cbor.CBORFactory; import java.util.function.BiFunction; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; +import software.amazon.awssdk.thirdparty.jackson.dataformat.cbor.CBORFactory; /** * Creates generators and protocol handlers for CBOR wire format. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java deleted file mode 100644 index 5e976913b51d..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import software.amazon.awssdk.annotations.SdkProtectedApi; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.protocols.ion.internal.AwsStructuredIonFactory; -import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory; -import software.amazon.awssdk.protocols.json.BaseAwsJsonProtocolFactory; -import software.amazon.awssdk.protocols.json.DefaultJsonContentTypeResolver; -import software.amazon.awssdk.protocols.json.JsonContentTypeResolver; -import software.amazon.awssdk.protocols.json.StructuredJsonFactory; - -/** - * Protocol factory for AWS/Ion protocols. Supports both JSON RPC and REST JSON versions of Ion. Defaults - * to Ion Binary but will use Ion Text if the system setting {@link SdkSystemSetting#BINARY_ION_ENABLED} is - * set to false. - */ -@SdkProtectedApi -public final class AwsIonProtocolFactory extends BaseAwsJsonProtocolFactory { - - /** - * Content type resolver implementation for Ion-enabled services. - */ - private static final JsonContentTypeResolver ION_BINARY = new DefaultJsonContentTypeResolver("application/x-amz-ion-"); - - /** - * Content type resolver implementation for debugging Ion-enabled services. - */ - private static final JsonContentTypeResolver ION_TEXT = new DefaultJsonContentTypeResolver("text/x-amz-ion-"); - - private AwsIonProtocolFactory(Builder builder) { - super(builder); - } - - public static Builder builder() { - return new Builder(); - } - - /** - * @return Content type resolver implementation to use. - */ - @Override - protected JsonContentTypeResolver getContentTypeResolver() { - return isIonBinaryEnabled() ? ION_BINARY : ION_TEXT; - } - - /** - * @return Instance of {@link StructuredJsonFactory} to use in creating handlers. - */ - @Override - protected StructuredJsonFactory getSdkFactory() { - return isIonBinaryEnabled() - ? AwsStructuredIonFactory.SDK_ION_BINARY_FACTORY - : AwsStructuredIonFactory.SDK_ION_TEXT_FACTORY; - } - - private boolean isIonBinaryEnabled() { - return SdkSystemSetting.BINARY_ION_ENABLED.getBooleanValueOrThrow(); - } - - /** - * Builder for {@link AwsJsonProtocolFactory}. - */ - public static final class Builder extends BaseAwsJsonProtocolFactory.Builder { - - private Builder() { - } - - public AwsIonProtocolFactory build() { - return new AwsIonProtocolFactory(this); - } - - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java deleted file mode 100644 index 85345e33245e..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import com.fasterxml.jackson.core.JsonFactory; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.protocols.json.BaseAwsStructuredJsonFactory; -import software.amazon.awssdk.protocols.json.ErrorCodeParser; -import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; -import software.amazon.ion.system.IonBinaryWriterBuilder; -import software.amazon.ion.system.IonTextWriterBuilder; -import software.amazon.ion.system.IonWriterBuilder; - -@SdkInternalApi -public final class AwsStructuredIonFactory extends SdkStructuredIonFactory { - private static final IonWriterBuilder BINARY_WRITER_BUILDER = IonBinaryWriterBuilder.standard().immutable(); - private static final IonWriterBuilder TEXT_WRITER_BUILDER = IonTextWriterBuilder.standard().immutable(); - - - public static final BaseAwsStructuredJsonFactory SDK_ION_BINARY_FACTORY = - new AwsIonFactory(JSON_FACTORY, BINARY_WRITER_BUILDER); - - public static final BaseAwsStructuredJsonFactory SDK_ION_TEXT_FACTORY = new AwsIonFactory(JSON_FACTORY, TEXT_WRITER_BUILDER); - - static class AwsIonFactory extends BaseAwsStructuredJsonFactory { - private final JsonFactory jsonFactory; - - private final IonWriterBuilder builder; - - - AwsIonFactory(JsonFactory jsonFactory, IonWriterBuilder builder) { - super(jsonFactory); - this.jsonFactory = jsonFactory; - this.builder = builder; - } - - @Override - protected StructuredJsonGenerator createWriter(JsonFactory jsonFactory, String contentType) { - return ION_GENERATOR_SUPPLIER.apply(builder, contentType); - } - - @Override - public JsonFactory getJsonFactory() { - return jsonFactory; - } - - @Override - public ErrorCodeParser getErrorCodeParser(String customErrorCodeFieldName) { - return new CompositeErrorCodeParser( - new IonErrorCodeParser(ION_SYSTEM), - super.getErrorCodeParser(customErrorCodeFieldName)); - } - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java deleted file mode 100644 index 0d3bdb20aeb0..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import java.util.Arrays; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.ErrorCodeParser; -import software.amazon.awssdk.protocols.json.JsonContent; - -@SdkInternalApi -class CompositeErrorCodeParser implements ErrorCodeParser { - private final Iterable parsers; - - CompositeErrorCodeParser(ErrorCodeParser... parsers) { - this.parsers = Arrays.asList(parsers); - } - - @Override - public String parseErrorCode(SdkHttpFullResponse response, JsonContent jsonContent) { - for (ErrorCodeParser parser : parsers) { - String errorCode = parser.parseErrorCode(response, jsonContent); - if (errorCode != null) { - return errorCode; - } - } - - return null; - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java deleted file mode 100644 index 64d91e3b080b..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.ErrorCodeParser; -import software.amazon.awssdk.protocols.json.JsonContent; -import software.amazon.awssdk.utils.IoUtils; -import software.amazon.ion.IonReader; -import software.amazon.ion.IonSystem; -import software.amazon.ion.IonType; - -//TODO Clean up ION parser -@SdkInternalApi -class IonErrorCodeParser implements ErrorCodeParser { - private static final Logger log = LoggerFactory.getLogger(IonErrorCodeParser.class); - - private static final String TYPE_PREFIX = "aws-type:"; - private static final String X_AMZN_REQUEST_ID_HEADER = "x-amzn-RequestId"; - - private final IonSystem ionSystem; - - IonErrorCodeParser(IonSystem ionSystem) { - this.ionSystem = ionSystem; - } - - private static String getRequestId(SdkHttpFullResponse response) { - return response.firstMatchingHeader(X_AMZN_REQUEST_ID_HEADER).orElse(null); - } - - @Override - public String parseErrorCode(SdkHttpFullResponse response, JsonContent jsonContents) { - IonReader reader = ionSystem.newReader(jsonContents.getRawContent()); - try { - IonType type = reader.next(); - if (type != IonType.STRUCT) { - throw SdkClientException.builder() - .message(String.format("Can only get error codes from structs (saw %s), request id %s", - type, getRequestId(response))) - .build(); - } - - boolean errorCodeSeen = false; - String errorCode = null; - String[] annotations = reader.getTypeAnnotations(); - for (String annotation : annotations) { - if (annotation.startsWith(TYPE_PREFIX)) { - if (errorCodeSeen) { - throw SdkClientException.builder() - .message(String.format("Multiple error code annotations found for request id %s", - getRequestId(response))) - .build(); - } else { - errorCodeSeen = true; - errorCode = annotation.substring(TYPE_PREFIX.length()); - } - } - } - - return errorCode; - } finally { - IoUtils.closeQuietly(reader, log); - } - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java deleted file mode 100644 index 7425516f5ee6..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.net.URL; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.ion.IonSystem; - -@SdkInternalApi -public final class IonFactory extends JsonFactory { - private static final long serialVersionUID = 1; - - private static final boolean SHOULD_CLOSE_READER_YES = true; - private static final boolean SHOULD_CLOSE_READER_NO = false; - - // TODO IonReaderBuilder will soon be available for constructing IonReaders - // without an IonSystem. This should use an IonReaderBuilder. - private final transient IonSystem ionSystem; - - public IonFactory(IonSystem ionSystem) { - this.ionSystem = ionSystem; - } - - @Override - public JsonParser createParser(InputStream in) { - return new IonParser(ionSystem.newReader(in), SHOULD_CLOSE_READER_NO); - } - - @Override - public JsonParser createParser(byte[] data) { - return new IonParser(ionSystem.newReader(data), SHOULD_CLOSE_READER_NO); - } - - @Override - public JsonParser createParser(byte[] data, int offset, int length) { - return new IonParser(ionSystem.newReader(data, offset, length), SHOULD_CLOSE_READER_NO); - } - - @Override - public JsonParser createParser(char[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public JsonParser createParser(char[] data, int offset, int length) { - throw new UnsupportedOperationException(); - } - - @Override - public JsonParser createParser(String data) { - return new IonParser(ionSystem.newReader(data), SHOULD_CLOSE_READER_NO); - } - - @Override - public JsonParser createParser(Reader data) { - return new IonParser(ionSystem.newReader(data), SHOULD_CLOSE_READER_NO); - } - - @Override - public JsonParser createParser(File data) throws IOException { - return new IonParser(ionSystem.newReader(new FileInputStream(data)), SHOULD_CLOSE_READER_YES); - } - - @Override - public JsonParser createParser(URL data) throws IOException { - return new IonParser(ionSystem.newReader(data.openStream()), SHOULD_CLOSE_READER_YES); - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java deleted file mode 100644 index f93568dd43af..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import static com.fasterxml.jackson.core.JsonParser.NumberType.BIG_DECIMAL; -import static com.fasterxml.jackson.core.JsonParser.NumberType.BIG_INTEGER; -import static com.fasterxml.jackson.core.JsonParser.NumberType.DOUBLE; -import static software.amazon.ion.IonType.STRUCT; - -import com.fasterxml.jackson.core.Base64Variant; -import com.fasterxml.jackson.core.JsonLocation; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonStreamContext; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.JsonTokenId; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.Version; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.ion.IonReader; -import software.amazon.ion.IonType; - -@SdkInternalApi -public final class IonParser extends JsonParser { - private final IonReader reader; - private final boolean shouldCloseReader; - private State state = State.BEFORE_VALUE; - private JsonToken currentToken; - private JsonToken lastClearedToken; - private boolean shouldSkipContainer; - private boolean closed; - - public IonParser(IonReader reader, boolean shouldCloseReader) { - super(Feature.collectDefaults()); - this.reader = reader; - this.shouldCloseReader = shouldCloseReader; - } - - @Override - public ObjectCodec getCodec() { - throw new UnsupportedOperationException(); - } - - @Override - public void setCodec(ObjectCodec c) { - throw new UnsupportedOperationException(); - } - - @Override - public Version version() { - throw new UnsupportedOperationException(); - } - - @Override - public void close() throws IOException { - if (shouldCloseReader) { - reader.close(); - } else if (Feature.AUTO_CLOSE_SOURCE.enabledIn(_features)) { - reader.close(); - } - closed = true; - } - - @Override - public JsonToken nextToken() { - currentToken = doNextToken(); - return currentToken; - } - - private JsonToken doNextToken() { - for (; ; ) { - switch (state) { - case BEFORE_VALUE: - IonType currentType = reader.next(); - - if (currentType == null) { - boolean topLevel = reader.getDepth() == 0; - if (topLevel) { - state = State.EOF; - continue; - } else { - state = State.END_OF_CONTAINER; - return reader.isInStruct() - ? JsonToken.END_OBJECT - : JsonToken.END_ARRAY; - } - } - - if (reader.isInStruct()) { - state = State.FIELD_NAME; - return JsonToken.FIELD_NAME; - } else { - state = State.VALUE; - return getJsonToken(); - } - - case END_OF_CONTAINER: - reader.stepOut(); - state = State.BEFORE_VALUE; - continue; - - case EOF: - return null; - - case FIELD_NAME: - state = State.VALUE; - return getJsonToken(); - - case VALUE: - state = State.BEFORE_VALUE; - if (IonType.isContainer(reader.getType()) && !reader.isNullValue() && !shouldSkipContainer) { - reader.stepIn(); - } - shouldSkipContainer = false; - continue; - default: - // Ignore. - } - } - } - - @Override - public JsonToken nextValue() { - JsonToken token = nextToken(); - return (token == JsonToken.FIELD_NAME) - ? nextToken() - : token; - } - - @Override - public JsonParser skipChildren() { - IonType currentType = reader.getType(); - if (IonType.isContainer(currentType)) { - shouldSkipContainer = true; - currentToken = currentType == STRUCT - ? JsonToken.END_OBJECT - : JsonToken.END_ARRAY; - } - return this; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public JsonToken getCurrentToken() { - return currentToken; - } - - @Override - public int getCurrentTokenId() { - return currentToken == null - ? JsonTokenId.ID_NO_TOKEN - : currentToken.id(); - } - - @Override - public boolean hasCurrentToken() { - return currentToken != null; - } - - @Override - public boolean hasTokenId(int id) { - return getCurrentTokenId() == id; - } - - @Override - public boolean hasToken(JsonToken t) { - return currentToken == t; - } - - @Override - public String getCurrentName() { - return reader.getFieldName(); - } - - @Override - public JsonStreamContext getParsingContext() { - throw new UnsupportedOperationException(); - } - - @Override - public JsonLocation getTokenLocation() { - throw new UnsupportedOperationException(); - } - - @Override - public JsonLocation getCurrentLocation() { - throw new UnsupportedOperationException(); - } - - @Override - public void clearCurrentToken() { - lastClearedToken = currentToken; - currentToken = null; - } - - @Override - public JsonToken getLastClearedToken() { - return lastClearedToken; - } - - @Override - public void overrideCurrentName(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String getText() { - if (state == State.FIELD_NAME) { - return reader.getFieldName(); - } - if (IonType.isText(reader.getType())) { - return reader.stringValue(); - } - if (currentToken == null) { - // start or end of stream - return null; - } - if (currentToken.isNumeric()) { - return getNumberValue().toString(); - } - return currentToken.asString(); - } - - @Override - public char[] getTextCharacters() { - throw new UnsupportedOperationException(); - } - - @Override - public int getTextLength() { - throw new UnsupportedOperationException(); - } - - @Override - public int getTextOffset() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasTextCharacters() { - return false; - } - - @Override - public Number getNumberValue() { - NumberType numberType = getNumberType(); - if (numberType == null) { - throw SdkClientException.builder() - .message(String.format("Unable to get number value for non-numeric token %s", - reader.getType())) - .build(); - } - switch (numberType) { - case BIG_DECIMAL: - return reader.bigDecimalValue(); - case BIG_INTEGER: - return reader.bigIntegerValue(); - case DOUBLE: - return reader.doubleValue(); - default: - throw SdkClientException.builder() - .message(String.format("Unable to get number value for number type %s", - numberType)) - .build(); - } - } - - @Override - public NumberType getNumberType() { - switch (reader.getType()) { - case DECIMAL: - return BIG_DECIMAL; - case FLOAT: - return DOUBLE; - case INT: - return BIG_INTEGER; - default: - return null; - } - } - - @Override - public int getIntValue() { - return reader.intValue(); - } - - @Override - public long getLongValue() { - return reader.longValue(); - } - - @Override - public BigInteger getBigIntegerValue() { - return reader.bigIntegerValue(); - } - - @Override - public float getFloatValue() { - return (float) reader.doubleValue(); - } - - @Override - public double getDoubleValue() { - return reader.doubleValue(); - } - - @Override - public BigDecimal getDecimalValue() { - return reader.decimalValue(); - } - - @Override - public Object getEmbeddedObject() { - if (currentToken != JsonToken.VALUE_EMBEDDED_OBJECT) { - return null; - } - IonType currentType = reader.getType(); - switch (currentType) { - case BLOB: - case CLOB: - return ByteBuffer.wrap(reader.newBytes()); - case TIMESTAMP: - return reader.timestampValue().dateValue(); - default: - throw SdkClientException.builder() - .message(String.format("Cannot return embedded object for Ion type %s", - currentType)) - .build(); - } - } - - @Override - public byte[] getBinaryValue(Base64Variant bv) { - throw new UnsupportedOperationException(); - } - - @Override - public String getValueAsString(String defaultValue) { - // The documentation is ambiguous about whether field names should - // return their text or the default value. To conform with the - // CBORParser, they will get the default value here. - if (currentToken != JsonToken.VALUE_STRING) { - if (currentToken == null || currentToken == JsonToken.VALUE_NULL || !currentToken.isScalarValue()) { - return defaultValue; - } - } - return getText(); - } - - private JsonToken getJsonToken() { - if (reader.isNullValue()) { - return JsonToken.VALUE_NULL; - } - - IonType currentType = reader.getType(); - switch (currentType) { - case BLOB: - case CLOB: - return JsonToken.VALUE_EMBEDDED_OBJECT; - case BOOL: - return reader.booleanValue() ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE; - case DECIMAL: - return JsonToken.VALUE_NUMBER_FLOAT; - case FLOAT: - return JsonToken.VALUE_NUMBER_FLOAT; - case INT: - return JsonToken.VALUE_NUMBER_INT; - case LIST: - return JsonToken.START_ARRAY; - case SEXP: - return JsonToken.START_ARRAY; - case STRING: - return JsonToken.VALUE_STRING; - case STRUCT: - return JsonToken.START_OBJECT; - case SYMBOL: - return JsonToken.VALUE_STRING; - case TIMESTAMP: - return JsonToken.VALUE_EMBEDDED_OBJECT; - default: - throw SdkClientException.builder() - .message(String.format("Unhandled Ion type %s", currentType)) - .build(); - } - } - - private enum State { - BEFORE_VALUE, - END_OF_CONTAINER, - EOF, - FIELD_NAME, - VALUE - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java deleted file mode 100644 index 4d758970d2d2..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.time.Instant; -import java.util.Date; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; -import software.amazon.awssdk.utils.BinaryUtils; -import software.amazon.ion.IonType; -import software.amazon.ion.IonWriter; -import software.amazon.ion.Timestamp; -import software.amazon.ion.system.IonWriterBuilder; - -@SdkInternalApi -public abstract class SdkIonGenerator implements StructuredJsonGenerator { - protected final IonWriter writer; - private final String contentType; - - private SdkIonGenerator(IonWriter writer, String contentType) { - this.writer = writer; - this.contentType = contentType; - } - - public static SdkIonGenerator create(IonWriterBuilder builder, String contentType) { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - IonWriter writer = builder.build(bytes); - return new ByteArraySdkIonGenerator(bytes, writer, contentType); - } - - @Override - public StructuredJsonGenerator writeStartArray() { - try { - writer.stepIn(IonType.LIST); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeNull() { - try { - writer.writeNull(); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeEndArray() { - try { - writer.stepOut(); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeStartObject() { - try { - writer.stepIn(IonType.STRUCT); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeEndObject() { - try { - writer.stepOut(); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeFieldName(String fieldName) { - writer.setFieldName(fieldName); - return this; - } - - @Override - public StructuredJsonGenerator writeValue(String val) { - try { - writer.writeString(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(boolean bool) { - try { - writer.writeBool(bool); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(long val) { - try { - writer.writeInt(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(double val) { - try { - writer.writeFloat(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(float val) { - try { - writer.writeFloat(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(short val) { - try { - writer.writeInt(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(int val) { - try { - writer.writeInt(val); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(ByteBuffer bytes) { - try { - writer.writeBlob(BinaryUtils.copyAllBytesFrom(bytes)); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(Instant instant) { - try { - Date d = instant != null ? Date.from(instant) : null; - writer.writeTimestamp(Timestamp.forDateZ(d)); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(BigDecimal value) { - try { - writer.writeDecimal(value); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeValue(BigInteger value) { - try { - writer.writeInt(value); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return this; - } - - @Override - public StructuredJsonGenerator writeNumber(String number) { - return writeValue(new BigDecimal(number)); - } - - @Override - public abstract byte[] getBytes(); - - @Override - public String getContentType() { - return contentType; - } - - private static class ByteArraySdkIonGenerator extends SdkIonGenerator { - private final ByteArrayOutputStream bytes; - - ByteArraySdkIonGenerator(ByteArrayOutputStream bytes, IonWriter writer, String contentType) { - super(writer, contentType); - this.bytes = bytes; - } - - @Override - public byte[] getBytes() { - try { - writer.finish(); - } catch (IOException e) { - throw SdkClientException.builder().cause(e).build(); - } - return bytes.toByteArray(); - } - } -} diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java deleted file mode 100644 index 639ea5e60bbe..000000000000 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion.internal; - -import com.fasterxml.jackson.core.JsonFactory; -import java.util.function.BiFunction; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; -import software.amazon.ion.IonSystem; -import software.amazon.ion.system.IonSystemBuilder; -import software.amazon.ion.system.IonWriterBuilder; - -@SdkInternalApi -abstract class SdkStructuredIonFactory { - - protected static final IonSystem ION_SYSTEM = IonSystemBuilder.standard().build(); - - protected static final JsonFactory JSON_FACTORY = new IonFactory(ION_SYSTEM); - - protected static final IonGeneratorSupplier ION_GENERATOR_SUPPLIER = SdkIonGenerator::create; - - SdkStructuredIonFactory() { - } - - @FunctionalInterface - protected interface IonGeneratorSupplier extends BiFunction { - StructuredJsonGenerator apply(IonWriterBuilder writerBuilder, String contentType); - } -} diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java deleted file mode 100644 index 2b2fab841717..000000000000 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.awscore.exception.AwsServiceException; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.http.AbortableInputStream; -import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.core.ExceptionMetadata; -import software.amazon.awssdk.protocols.json.JsonOperationMetadata; -import software.amazon.ion.IonStruct; -import software.amazon.ion.IonSystem; -import software.amazon.ion.IonWriter; -import software.amazon.ion.Timestamp; -import software.amazon.ion.system.IonSystemBuilder; - -public class AwsStructuredIonFactoryTest { - private static final String ERROR_PREFIX = "aws-type:"; - private static final String ERROR_TYPE = "InvalidParameterException"; - private static final String ERROR_MESSAGE = "foo"; - - private static final String NO_CUSTOM_ERROR_CODE_FIELD_NAME = null; - - private static IonSystem system; - - @BeforeClass - public static void beforeClass() { - system = IonSystemBuilder.standard().build(); - } - - private static IonStruct createPayload() { - IonStruct payload = system.newEmptyStruct(); - payload.add("NotValidJson", system.newTimestamp(Timestamp.nowZ())); - payload.add("errorMessage", system.newString(ERROR_MESSAGE)); - return payload; - } - - private static SdkHttpFullResponse createResponse(IonStruct payload) throws Exception { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - IonWriter writer = system.newBinaryWriter(bytes); - payload.writeTo(writer); - writer.close(); - - return ValidSdkObjects.sdkHttpFullResponse() - .content(AbortableInputStream.create(new ByteArrayInputStream(bytes.toByteArray()))) - .build(); - } - - @Test - public void handlesErrorsUsingHttpHeader() throws Exception { - IonStruct payload = createPayload(); - - SdkHttpFullResponse error = - createResponse(payload).toBuilder().putHeader("x-amzn-ErrorType", ERROR_TYPE).build(); - - AwsServiceException exception = handleError(error); - assertThat(exception).isInstanceOf(InvalidParameterException.class); - assertEquals(ERROR_MESSAGE, exception.awsErrorDetails().errorMessage()); - } - - @Test - public void handlesErrorsUsingMagicField() throws Exception { - IonStruct payload = createPayload(); - payload.add("__type", system.newString(ERROR_TYPE)); - - SdkHttpFullResponse error = createResponse(payload); - - AwsServiceException exception = handleError(error); - assertThat(exception).isInstanceOf(InvalidParameterException.class); - assertEquals(ERROR_MESSAGE, exception.awsErrorDetails().errorMessage()); - } - - @Test - public void handlesErrorsUsingAnnotation() throws Exception { - IonStruct payload = createPayload(); - payload.addTypeAnnotation(ERROR_PREFIX + ERROR_TYPE); - - SdkHttpFullResponse error = createResponse(payload); - - AwsServiceException exception = handleError(error); - assertThat(exception).isInstanceOf(InvalidParameterException.class); - assertEquals(ERROR_MESSAGE, exception.awsErrorDetails().errorMessage()); - } - - @Test(expected = SdkClientException.class) - public void rejectPayloadsWithMultipleErrorAnnotations() throws Exception { - IonStruct payload = createPayload(); - payload.addTypeAnnotation(ERROR_PREFIX + ERROR_TYPE); - payload.addTypeAnnotation(ERROR_PREFIX + "foo"); - - SdkHttpFullResponse error = createResponse(payload); - - handleError(error); - } - - @Test - public void handlesErrorsWithMutipleAnnotations() throws Exception { - IonStruct payload = createPayload(); - payload.addTypeAnnotation("foo"); - payload.addTypeAnnotation(ERROR_PREFIX + ERROR_TYPE); - payload.addTypeAnnotation("bar"); - - SdkHttpFullResponse error = createResponse(payload); - - AwsServiceException exception = handleError(error); - assertThat(exception).isInstanceOf(InvalidParameterException.class); - assertEquals(ERROR_MESSAGE, exception.awsErrorDetails().errorMessage()); - } - - private AwsServiceException handleError(SdkHttpFullResponse error) throws Exception { - return AwsIonProtocolFactory.builder() - .registerModeledException( - ExceptionMetadata.builder() - .exceptionBuilderSupplier(InvalidParameterException::builder) - .errorCode(ERROR_TYPE) - .build()) - .customErrorCodeFieldName(NO_CUSTOM_ERROR_CODE_FIELD_NAME) - .build() - .createErrorResponseHandler(JsonOperationMetadata.builder() - .hasStreamingSuccessResponse(false) - .isPayloadJson(true) - .build()) - .handle(error, new ExecutionAttributes()); - } - - private static class InvalidParameterException extends AwsServiceException { - private static final long serialVersionUID = 0; - - public InvalidParameterException(BeanStyleBuilder builder) { - super(builder); - } - - public static Class serializableBuilderClass() { - return BeanStyleBuilder.class; - } - - @Override - public Builder toBuilder() { - return new BeanStyleBuilder(this); - } - - public static Builder builder() { - return new BeanStyleBuilder(); - } - - public interface Builder extends AwsServiceException.Builder { - @Override - Builder message(String message); - - @Override - InvalidParameterException build(); - } - - private static class BeanStyleBuilder extends BuilderImpl implements Builder { - private String message; - - private BeanStyleBuilder() {} - - private BeanStyleBuilder(InvalidParameterException ex) { - this.message = ex.getMessage(); - } - - @Override - public Builder message(String message) { - this.message = message; - return this; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public InvalidParameterException build() { - return new InvalidParameterException(this); - } - } - } -} diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java deleted file mode 100644 index 31c9b82298a3..000000000000 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import static org.hamcrest.Matchers.instanceOf; -import static org.junit.Assert.assertThat; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.protocols.ion.internal.IonFactory; -import software.amazon.awssdk.protocols.ion.internal.IonParser; -import software.amazon.ion.system.IonSystemBuilder; - -public class IonFactoryTest { - private IonFactory factory; - - @Before - public void before() { - factory = new IonFactory(IonSystemBuilder.standard().build()); - } - - @Test - public void createParserFromByteArray() throws Exception { - assertThat(factory.createParser(new byte[0]), instanceOf(IonParser.class)); - } - - @Test(expected = UnsupportedOperationException.class) - public void createParserFromCharArray() throws Exception { - assertThat(factory.createParser(new char[0]), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromFile() throws Exception { - File file = File.createTempFile("IonFactoryTest", null); - file.deleteOnExit(); - assertThat(factory.createParser(file), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromInputStream() throws Exception { - assertThat(factory.createParser(new ByteArrayInputStream(new byte[0])), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromReader() throws Exception { - assertThat(factory.createParser(new InputStreamReader(new ByteArrayInputStream(new byte[0]))), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromString() throws Exception { - assertThat(factory.createParser(""), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromUrl() throws Exception { - class NullUrlConnection extends URLConnection { - protected NullUrlConnection(URL url) { - super(url); - } - - @Override - public void connect() throws IOException { - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(new byte[0]); - } - } - ; - - class NullUrlStreamHandler extends URLStreamHandler { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new NullUrlConnection(u); - } - } - ; - - assertThat(factory.createParser(new URL("foo", "bar", 99, "baz", new NullUrlStreamHandler())), instanceOf(IonParser.class)); - } - - @Test - public void createParserFromByteArrayWithOffsetAndLength() throws Exception { - assertThat(factory.createParser(new byte[0], 0, 0), instanceOf(IonParser.class)); - } - - @Test(expected = UnsupportedOperationException.class) - public void createParserFromCharArrayWithOffsetAndLength() throws Exception { - factory.createParser(new char[0], 0, 0); - } -} diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java deleted file mode 100644 index ca829c900c83..000000000000 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.protocols.ion.internal.IonParser; -import software.amazon.awssdk.protocols.ion.internal.SdkIonGenerator; -import software.amazon.ion.IonReader; -import software.amazon.ion.IonSystem; -import software.amazon.ion.IonWriter; -import software.amazon.ion.system.IonBinaryWriterBuilder; -import software.amazon.ion.system.IonSystemBuilder; -import software.amazon.ion.system.IonTextWriterBuilder; - -/** - * Tests the {@link IonParser} for conformity with the {@link JsonParser} API. - * Also tests that the IonParser correctly converts Ion-only value types to - * the correct {@link JsonToken}s. For testing of additional value types and - * roundtrip testing with the {@link SdkIonGenerator}, see {@link IonRoundtripTest}. - */ -@RunWith(Parameterized.class) -public class IonParserTest { - - private static IonSystem SYSTEM = IonSystemBuilder.standard().build(); - @Rule - public ExpectedException thrown = ExpectedException.none(); - private WriteFormat format; - - public IonParserTest(WriteFormat format) { - this.format = format; - } - - @Parameters - public static Collection data() { - List parameters = new ArrayList(); - for (WriteFormat format : WriteFormat.values()) { - parameters.add(new Object[] {format}); - } - return parameters; - } - - private IonParser parse(String data) throws IOException { - byte[] ion = format.write(data); - return new IonParser(SYSTEM.newReader(ion), false); - } - - @Test - public void testEmptySexp() throws IOException { - IonParser parser = parse("()"); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertNull(parser.nextToken()); - } - - @Test - public void testSexp() throws IOException { - IonParser parser = parse("(a+b)"); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("a", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("+", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("b", parser.getText()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertNull(parser.nextToken()); - } - - @Test - public void testNestedSexp() throws IOException { - IonParser parser = parse("((a)+(b))"); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("a", parser.getText()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("+", parser.getText()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("b", parser.getText()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertNull(parser.nextToken()); - } - - @Test - public void testSexpSkip() throws IOException { - IonParser parser = parse("(a+b)"); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); - assertNull(parser.nextToken()); - } - - @Test - public void testNestedSexpSkip() throws IOException { - IonParser parser = parse("((a)+(b))"); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("+", parser.getText()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertNull(parser.nextToken()); - } - - @Test - public void testEmptyClob() throws IOException { - IonParser parser = parse("{{}}"); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(ByteBuffer.wrap(new byte[0]), parser.getEmbeddedObject()); - assertNull(parser.nextToken()); - } - - @Test - public void testClob() throws IOException { - IonParser parser = parse("{{\"abc123\"}}"); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(ByteBuffer.wrap("abc123".getBytes(StandardCharsets.UTF_8)), parser.getEmbeddedObject()); - assertNull(parser.nextToken()); - } - - @Test - public void testSymbolValue() throws IOException { - IonParser parser = parse("a1 _1 $foo '123' 'sp ace'"); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("a1", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("_1", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("$foo", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("123", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("sp ace", parser.getText()); - assertNull(parser.nextToken()); - } - - @Test - public void testSkipChildrenNotAtContainerStartDoesNothing() throws IOException { - IonParser parser = parse("123 (a+b)"); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - parser.skipChildren(); // should do nothing - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.getCurrentToken()); - assertEquals(123, parser.getIntValue()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - parser.skipChildren(); // should do nothing - assertEquals(JsonToken.VALUE_STRING, parser.getCurrentToken()); - assertEquals("a", parser.getText()); - } - - @Test - public void testGetEmbeddedObjectOnBasicValueReturnsNull() throws IOException { - IonParser parser = parse("123 (a+b) abc"); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertNull(parser.getEmbeddedObject()); - assertEquals(123, parser.getIntValue()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertNull(parser.getEmbeddedObject()); - parser.skipChildren(); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertNull(parser.getEmbeddedObject()); - assertEquals("abc", parser.getText()); - assertNull(parser.nextToken()); - } - - @Test - public void testNulls() throws IOException { - IonParser parser = parse("null " - + "null.null " - + "null.bool " - + "null.int " - + "null.float " - + "null.decimal " - + "null.timestamp " - + "null.string " - + "null.symbol " - + "null.blob " - + "null.clob " - + "null.struct " - + "null.list " - + "null.sexp" - ); - JsonToken token = null; - int count = 0; - while ((token = parser.nextToken()) != null) { - assertEquals(JsonToken.VALUE_NULL, token); - count++; - } - assertEquals(14, count); - } - - @Test - public void testNextValue() throws IOException { - IonParser parser = parse("{foo:{bar:\"abc\"}, baz:123} 42.0"); - assertEquals(JsonToken.START_OBJECT, parser.nextValue()); - assertEquals(JsonToken.START_OBJECT, parser.nextValue()); - assertEquals("foo", parser.getCurrentName()); - assertEquals(JsonToken.VALUE_STRING, parser.nextValue()); - assertEquals("abc", parser.getText()); - assertEquals("bar", parser.getCurrentName()); - assertEquals(JsonToken.END_OBJECT, parser.nextValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextValue()); - assertEquals(123, parser.getIntValue()); - assertEquals("baz", parser.getCurrentName()); - assertEquals(JsonToken.END_OBJECT, parser.nextValue()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextValue()); - assertEquals(42.0, parser.getFloatValue(), 1e-9); - assertNull(parser.nextValue()); - } - - @Test - public void testGetCurrentNameNotAtFieldReturnsNull() throws IOException { - IonParser parser = parse("{foo:\"abc\"} [a, b] {{}} \"bar\""); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.VALUE_STRING, parser.nextValue()); - assertEquals(JsonToken.END_OBJECT, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertNull(parser.getCurrentName()); - assertNull(parser.nextToken()); - assertNull(parser.getCurrentName()); - } - - @Test - public void testClearCurrentToken() throws IOException { - IonParser parser = parse("{}"); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - parser.clearCurrentToken(); - assertNull(parser.getCurrentToken()); - assertFalse(parser.hasCurrentToken()); - assertEquals(JsonToken.START_OBJECT, parser.getLastClearedToken()); - } - - @Test - public void testGetText() throws IOException { - String defaultText = "default"; - String integer = String.valueOf(123); - String flt = String.valueOf(42.0); - IonParser parser = parse("{foo:" + integer + ", bar:" + flt + "} {{\"abc\"}} null true false"); - assertNull(parser.getText()); - assertEquals(defaultText, parser.getValueAsString(defaultText)); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - assertEquals(JsonToken.START_OBJECT.asString(), parser.getText()); // "{" - assertEquals(defaultText, parser.getValueAsString(defaultText)); - assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); - assertEquals("foo", parser.getText()); - assertEquals(defaultText, parser.getValueAsString(defaultText)); - assertEquals("foo", parser.getCurrentName()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(integer, parser.getText()); - assertEquals(integer, parser.getValueAsString(defaultText)); - assertEquals(123, parser.getIntValue()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextValue()); - assertEquals(flt, parser.getText()); - assertEquals(flt, parser.getValueAsString(defaultText)); - assertEquals(42.0, parser.getFloatValue(), 1e-9); - assertEquals("bar", parser.getCurrentName()); - assertEquals(JsonToken.END_OBJECT, parser.nextToken()); - assertEquals(JsonToken.END_OBJECT.asString(), parser.getText()); // "}" - assertEquals(defaultText, parser.getValueAsString(defaultText)); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertNull(parser.getText()); // embedded objects have undefined text - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - assertEquals(JsonToken.VALUE_NULL.asString(), parser.getText()); // "null" - assertEquals(defaultText, parser.getValueAsString(defaultText)); - assertEquals(JsonToken.VALUE_TRUE, parser.nextToken()); - assertEquals(JsonToken.VALUE_TRUE.asString(), parser.getText()); // "true" - assertEquals(JsonToken.VALUE_TRUE.asString(), parser.getValueAsString(defaultText)); - assertEquals(JsonToken.VALUE_FALSE, parser.nextToken()); - assertEquals(JsonToken.VALUE_FALSE.asString(), parser.getText()); // "false" - assertEquals(JsonToken.VALUE_FALSE.asString(), parser.getValueAsString(defaultText)); - assertNull(parser.nextToken()); - assertNull(parser.getText()); - assertEquals(defaultText, parser.getValueAsString(defaultText)); - } - - @Test - public void testGetNumberValue() throws IOException { - String integer = String.valueOf(Integer.MAX_VALUE); - String lng = String.valueOf(Long.MAX_VALUE); - String bigInteger = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE).toString(); - String flt = String.valueOf(Float.MAX_VALUE); - String dbl = String.valueOf(Double.MAX_VALUE); - String inf = "1.7976931348623157E309"; // Double.MAX_VALUE * 10; - String bigDecimal = new BigDecimal(inf).toString(); - IonParser parser = parse(integer + " " - + lng + " " - + bigInteger + " " - + flt + " " - + dbl + " " - + inf + " " - + bigDecimal.toLowerCase().replace("e", "D") - ); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(integer, parser.getNumberValue().toString()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(lng, parser.getNumberValue().toString()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(bigInteger, parser.getNumberValue().toString()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(flt, parser.getNumberValue().toString()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(dbl, parser.getNumberValue().toString()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isInfinite(parser.getDoubleValue())); - assertTrue(Double.isInfinite(parser.getFloatValue())); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(bigDecimal, parser.getNumberValue().toString()); - } - - @Test - public void testGetNumberValueNotOnNumberFails() throws IOException { - IonParser parser = parse("foo {{}} {abc:123}"); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertNull(parser.getNumberType()); - thrown.expect(SdkClientException.class); - parser.getNumberValue(); - } - - @Test - public void testSpecialFloatValues() throws IOException { - IonParser parser = parse("1.7976931348623157E309 " // Double.MAX_VALUE * 10 - + "-1.7976931348623157E309 " - + "+inf " - + "-inf " - + "nan" - ); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isInfinite(parser.getDoubleValue())); - assertTrue(Double.isInfinite(parser.getFloatValue())); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isInfinite(parser.getDoubleValue())); - assertTrue(Double.isInfinite(parser.getFloatValue())); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isInfinite(parser.getDoubleValue())); - assertTrue(Double.isInfinite(parser.getFloatValue())); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isInfinite(parser.getDoubleValue())); - assertTrue(Double.isInfinite(parser.getFloatValue())); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertTrue(Double.isNaN(parser.getDoubleValue())); - assertTrue(Double.isNaN(parser.getFloatValue())); - } - - private enum WriteFormat { - TEXT { - @Override - public byte[] write(String data) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - write(data, IonTextWriterBuilder.standard().build(out)); - return out.toByteArray(); - } - }, - BINARY { - @Override - public byte[] write(String data) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - write(data, IonBinaryWriterBuilder.standard().build(out)); - return out.toByteArray(); - } - }; - - public static void write(String data, IonWriter writer) throws IOException { - IonReader reader = SYSTEM.newReader(data); - writer.writeValues(reader); - writer.close(); - } - - public abstract byte[] write(String data) throws IOException; - } - -} diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java deleted file mode 100644 index ba5429205563..000000000000 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import com.fasterxml.jackson.core.JsonToken; -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import software.amazon.awssdk.protocols.ion.internal.IonParser; -import software.amazon.awssdk.protocols.ion.internal.SdkIonGenerator; -import software.amazon.ion.IonException; -import software.amazon.ion.IonSystem; -import software.amazon.ion.system.IonBinaryWriterBuilder; -import software.amazon.ion.system.IonSystemBuilder; - -/** - * Tests that data written by the {@link SdkIonGenerator} is correctly read - * by the {@link IonParser}. For additional stand-alone testing of the - * {@link IonParser}, see {@link IonParserTest}. - */ -@RunWith(Parameterized.class) -public class IonRoundtripTest { - - private static final IonSystem SYSTEM = IonSystemBuilder.standard().build(); - private final Data data; - - public IonRoundtripTest(Data data) { - this.data = data; - } - - @Parameters - public static Collection data() { - List parameters = new ArrayList(); - for (Data data : Data.values()) { - parameters.add(new Object[] {data}); - } - return parameters; - } - - @Test - public void testRoundtrip() throws IOException { - SdkIonGenerator generator = SdkIonGenerator.create(IonBinaryWriterBuilder.standard(), "foo"); - data.generate(generator); - IonParser parser = new IonParser(SYSTEM.newReader(generator.getBytes()), false); - data.parse(parser); - assertNull(parser.nextToken()); // Asserts data was read fully. - assertFalse(parser.hasCurrentToken()); - assertFalse(parser.isClosed()); - parser.close(); - assertTrue(parser.isClosed()); - } - - private enum Data { - NULL { - @Override - public void generate(SdkIonGenerator generator) { - // Is this the only way to write a null value? - generator.writeValue((String) null); - generator.writeValue((BigInteger) null); - generator.writeValue((BigDecimal) null); - generator.writeValue((Instant) null); - generator.writeValue((ByteBuffer) null); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); - } - }, - BOOL { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(true); - generator.writeValue(false); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_TRUE, parser.nextToken()); - assertEquals(true, parser.getBooleanValue()); - assertEquals(JsonToken.VALUE_FALSE, parser.nextToken()); - assertEquals(false, parser.getBooleanValue()); - } - }, - SHORT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(0); - // There's no writeValue(byte) method, but there is writeValue(short)... - generator.writeValue(Byte.MAX_VALUE); - generator.writeValue(Byte.MIN_VALUE); - generator.writeValue(Short.MAX_VALUE); - generator.writeValue(Short.MIN_VALUE); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(0, parser.getIntValue()); - assertEquals(0, parser.getLongValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Byte.MAX_VALUE, parser.getByteValue()); - assertEquals((short) Byte.MAX_VALUE, parser.getShortValue()); - assertEquals((int) Byte.MAX_VALUE, parser.getIntValue()); - assertEquals((long) Byte.MAX_VALUE, parser.getLongValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Byte.MIN_VALUE, parser.getByteValue()); - assertEquals((short) Byte.MIN_VALUE, parser.getShortValue()); - assertEquals((int) Byte.MIN_VALUE, parser.getIntValue()); - assertEquals((long) Byte.MIN_VALUE, parser.getLongValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Short.MAX_VALUE, parser.getShortValue()); - assertEquals((int) Short.MAX_VALUE, parser.getIntValue()); - assertEquals((long) Short.MAX_VALUE, parser.getLongValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Short.MIN_VALUE, parser.getShortValue()); - assertEquals((int) Short.MIN_VALUE, parser.getIntValue()); - assertEquals((long) Short.MIN_VALUE, parser.getLongValue()); - - } - }, - INT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(Integer.MAX_VALUE); - generator.writeValue(Integer.MIN_VALUE); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Integer.MAX_VALUE, parser.getIntValue()); - assertEquals((long) Integer.MAX_VALUE, parser.getLongValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Integer.MIN_VALUE, parser.getIntValue()); - assertEquals((long) Integer.MIN_VALUE, parser.getLongValue()); - } - }, - LONG { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(Long.MAX_VALUE); - generator.writeValue(Long.MIN_VALUE); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Long.MAX_VALUE, parser.getLongValue()); - assertEquals(BigInteger.valueOf(Long.MAX_VALUE), parser.getBigIntegerValue()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(Long.MIN_VALUE, parser.getLongValue()); - assertEquals(BigInteger.valueOf(Long.MIN_VALUE), parser.getBigIntegerValue()); - - } - }, - BIG_INTEGER { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE)); - generator.writeValue(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE)); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), parser.getBigIntegerValue()); - try { - parser.getLongValue(); - } catch (IonException e1) { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE), parser.getBigIntegerValue()); - try { - parser.getLongValue(); - } catch (IonException e2) { - return; - } - } - throw new AssertionError("number shouldn't fit in a long"); - } - }, - FLOAT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(Float.MAX_VALUE); - generator.writeValue(Float.MIN_VALUE); - generator.writeValue(-Float.MAX_VALUE); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(Float.MAX_VALUE, parser.getFloatValue(), 1e-9); - assertEquals((double) Float.MAX_VALUE, parser.getDoubleValue(), 1e-9); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(Float.MIN_VALUE, parser.getFloatValue(), 1e-9); - assertEquals((double) Float.MIN_VALUE, parser.getDoubleValue(), 1e-9); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(-Float.MAX_VALUE, parser.getFloatValue(), 1e-9); - assertEquals((double) -Float.MAX_VALUE, parser.getDoubleValue(), 1e-9); - - } - }, - DOUBLE { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(Double.MAX_VALUE); - generator.writeValue(Double.MIN_VALUE); - generator.writeValue(-Double.MAX_VALUE); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(Double.MAX_VALUE, parser.getDoubleValue(), 1e-9); - assertEquals(BigDecimal.valueOf(Double.MAX_VALUE), parser.getDecimalValue()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(Float.MIN_VALUE, parser.getDoubleValue(), 1e-9); - assertEquals(BigDecimal.valueOf(Double.MIN_VALUE), parser.getDecimalValue()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(-Double.MAX_VALUE, parser.getDoubleValue(), 1e-9); - assertEquals(BigDecimal.valueOf(-Double.MAX_VALUE), parser.getDecimalValue()); - - } - }, - BIG_DECIMAL { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE)); - generator.writeValue(BigDecimal.valueOf(-Double.MAX_VALUE).subtract(BigDecimal.ONE)); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE), parser.getDecimalValue()); - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); - assertEquals(BigDecimal.valueOf(-Double.MAX_VALUE).subtract(BigDecimal.ONE), parser.getDecimalValue()); - } - }, - TIMESTAMP { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(Instant.ofEpochMilli(0)); - // Note: dates too far in the future are rejected by Ion - generator.writeValue(Instant.ofEpochMilli(Integer.MAX_VALUE)); - generator.writeValue(Instant.ofEpochMilli(Integer.MIN_VALUE)); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(new Date(0), parser.getEmbeddedObject()); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(new Date(Integer.MAX_VALUE), parser.getEmbeddedObject()); - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(new Date(Integer.MIN_VALUE), parser.getEmbeddedObject()); - } - }, - BYTES { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(ByteBuffer.wrap("foobar".getBytes(StandardCharsets.UTF_8))); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); - assertEquals(ByteBuffer.wrap("foobar".getBytes(StandardCharsets.UTF_8)), parser.getEmbeddedObject()); - } - }, - EMPTY_STRUCT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartObject(); - generator.writeEndObject(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - assertEquals(JsonToken.END_OBJECT, parser.nextToken()); - } - }, - EMPTY_LIST { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartArray(); - generator.writeEndArray(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - } - }, - STRUCT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartObject(); - generator.writeFieldName("int"); - generator.writeValue(1); - generator.writeFieldName("string"); - generator.writeValue("foo"); - generator.writeFieldName("bool"); - generator.writeValue(false); - generator.writeEndObject(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); - assertEquals("int", parser.getText()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(1, parser.getIntValue()); - assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); - assertEquals("string", parser.getText()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); - assertEquals("bool", parser.getText()); - assertEquals(JsonToken.VALUE_FALSE, parser.nextToken()); - assertEquals(false, parser.getBooleanValue()); - assertEquals(JsonToken.END_OBJECT, parser.nextToken()); - } - }, - LIST { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartArray(); - generator.writeValue(1); - generator.writeValue("foo"); - generator.writeValue(true); - generator.writeEndArray(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(1, parser.getIntValue()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - assertEquals(JsonToken.VALUE_TRUE, parser.nextToken()); - assertEquals(true, parser.getBooleanValue()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - - } - }, - STRUCT_IN_LIST { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartArray(); - STRUCT.generate(generator); - generator.writeEndArray(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - STRUCT.parse(parser); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - } - }, - LIST_IN_STRUCT { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartObject(); - generator.writeFieldName("list"); - LIST.generate(generator); - generator.writeEndObject(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); - assertEquals("list", parser.getText()); - LIST.parse(parser); - assertEquals(JsonToken.END_OBJECT, parser.nextToken()); - } - }, - STRUCT_SKIP { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(42); - STRUCT.generate(generator); - generator.writeValue("foo"); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(42, parser.getIntValue()); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - } - }, - LIST_SKIP { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(42); - LIST.generate(generator); - generator.writeValue("foo"); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(42, parser.getIntValue()); - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - } - }, - NESTED_SKIP { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeValue(42); - LIST_IN_STRUCT.generate(generator); - generator.writeValue("foo"); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(42, parser.getIntValue()); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - } - }, - NESTED_INNER_SKIP { - @Override - public void generate(SdkIonGenerator generator) { - generator.writeStartArray(); - generator.writeValue(42); - STRUCT.generate(generator); - generator.writeValue("foo"); - generator.writeEndArray(); - } - - @Override - public void parse(IonParser parser) throws IOException { - assertEquals(JsonToken.START_ARRAY, parser.nextToken()); - assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); - assertEquals(42, parser.getIntValue()); - assertEquals(JsonToken.START_OBJECT, parser.nextToken()); - parser.skipChildren(); - assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); - assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); - assertEquals("foo", parser.getText()); - assertEquals(JsonToken.END_ARRAY, parser.nextToken()); - } - }; - - public abstract void generate(SdkIonGenerator generator); - - public abstract void parse(IonParser parser) throws IOException; - } - -} diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java deleted file mode 100644 index eabb349822ba..000000000000 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.ion; - -import java.net.URI; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.http.SdkHttpMethod; - -/** - * A collection of objects (or object builder) pre-populated with all required fields. This allows tests to focus on what data - * they care about, not necessarily what data is required. - */ -public final class ValidSdkObjects { - private ValidSdkObjects() {} - - public static SdkHttpFullRequest.Builder sdkHttpFullRequest() { - return SdkHttpFullRequest.builder() - .uri(URI.create("http://test.com:80")) - .method(SdkHttpMethod.GET); - } - - public static SdkHttpFullResponse.Builder sdkHttpFullResponse() { - return SdkHttpFullResponse.builder() - .statusCode(200); - } -} diff --git a/core/protocols/aws-json-protocol/pom.xml b/core/protocols/aws-json-protocol/pom.xml index c0091b45da71..38550bcb6d5c 100644 --- a/core/protocols/aws-json-protocol/pom.xml +++ b/core/protocols/aws-json-protocol/pom.xml @@ -62,13 +62,14 @@ ${awsjavasdk.version} - com.fasterxml.jackson.core + software.amazon.awssdk jackson-core + ${awsjavasdk.version} - com.fasterxml.jackson.core - jackson-databind - test + software.amazon.awssdk + json-utils + ${awsjavasdk.version} junit diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java index a1f58badcc28..9b9ef38cb785 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java @@ -41,13 +41,13 @@ import software.amazon.awssdk.protocols.core.OperationInfo; import software.amazon.awssdk.protocols.core.ProtocolMarshaller; import software.amazon.awssdk.protocols.json.internal.AwsStructuredPlainJsonFactory; -import software.amazon.awssdk.protocols.json.internal.dom.JsonDomParser; import software.amazon.awssdk.protocols.json.internal.marshall.JsonProtocolMarshallerBuilder; import software.amazon.awssdk.protocols.json.internal.unmarshall.AwsJsonErrorMessageParser; import software.amazon.awssdk.protocols.json.internal.unmarshall.AwsJsonProtocolErrorUnmarshaller; import software.amazon.awssdk.protocols.json.internal.unmarshall.AwsJsonResponseHandler; import software.amazon.awssdk.protocols.json.internal.unmarshall.JsonProtocolUnmarshaller; import software.amazon.awssdk.protocols.json.internal.unmarshall.JsonResponseHandler; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; @SdkProtectedApi public abstract class BaseAwsJsonProtocolFactory { @@ -72,7 +72,9 @@ protected BaseAwsJsonProtocolFactory(Builder builder) { this.clientConfiguration = builder.clientConfiguration; this.protocolUnmarshaller = JsonProtocolUnmarshaller .builder() - .parser(JsonDomParser.create(getSdkFactory().getJsonFactory())) + .parser(JsonNodeParser.builder() + .jsonFactory(getSdkFactory().getJsonFactory()) + .build()) .defaultTimestampFormats(getDefaultTimestampFormats()) .build(); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java index a27196287e4c..c1b6ca5deb45 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java @@ -15,9 +15,9 @@ package software.amazon.awssdk.protocols.json; -import com.fasterxml.jackson.core.JsonFactory; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.protocols.json.internal.unmarshall.JsonErrorCodeParser; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; /** * Generic implementation of a structured JSON factory that is pluggable for different variants of diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java index bb5edba9ee11..86f58e9049d0 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java @@ -15,16 +15,14 @@ package software.amazon.awssdk.protocols.json; -import com.fasterxml.jackson.core.JsonFactory; -import java.io.ByteArrayInputStream; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.JsonDomParser; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; -import software.amazon.awssdk.protocols.json.internal.dom.SdkObjectNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; import software.amazon.awssdk.utils.IoUtils; /** @@ -37,9 +35,9 @@ public class JsonContent { private static final Logger LOG = LoggerFactory.getLogger(JsonContent.class); private final byte[] rawContent; - private final SdkJsonNode jsonNode; + private final JsonNode jsonNode; - JsonContent(byte[] rawJsonContent, SdkJsonNode jsonNode) { + JsonContent(byte[] rawJsonContent, JsonNode jsonNode) { this.rawContent = rawJsonContent; this.jsonNode = jsonNode; } @@ -67,16 +65,16 @@ public static JsonContent createJsonContent(SdkHttpFullResponse httpResponse, return new JsonContent(rawJsonContent, jsonFactory); } - private static SdkJsonNode parseJsonContent(byte[] rawJsonContent, JsonFactory jsonFactory) { + private static JsonNode parseJsonContent(byte[] rawJsonContent, JsonFactory jsonFactory) { if (rawJsonContent == null || rawJsonContent.length == 0) { - return SdkObjectNode.emptyObject(); + return JsonNode.emptyObjectNode(); } try { - JsonDomParser parser = JsonDomParser.create(jsonFactory); - return parser.parse(new ByteArrayInputStream(rawJsonContent)); + JsonNodeParser parser = JsonNodeParser.builder().jsonFactory(jsonFactory).build(); + return parser.parse(rawJsonContent); } catch (Exception e) { LOG.debug("Unable to parse HTTP response content", e); - return SdkObjectNode.emptyObject(); + return JsonNode.emptyObjectNode(); } } @@ -84,7 +82,7 @@ public byte[] getRawContent() { return rawContent; } - public SdkJsonNode getJsonNode() { + public JsonNode getJsonNode() { return jsonNode; } } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java index 0e91bfbd019e..228333bdf724 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java @@ -15,8 +15,6 @@ package software.amazon.awssdk.protocols.json; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; @@ -25,6 +23,8 @@ import java.time.Instant; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; +import software.amazon.awssdk.thirdparty.jackson.core.JsonGenerator; import software.amazon.awssdk.utils.BinaryUtils; import software.amazon.awssdk.utils.DateUtils; diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java index c88840d4d927..0e4c726e6445 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java @@ -15,8 +15,8 @@ package software.amazon.awssdk.protocols.json; -import com.fasterxml.jackson.core.JsonFactory; import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; /** * Common interface for creating generators (writers) and protocol handlers for JSON like protocols. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java index 14d97b854627..fcdb6d705237 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java @@ -15,11 +15,12 @@ package software.amazon.awssdk.protocols.json.internal; -import com.fasterxml.jackson.core.JsonFactory; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.protocols.json.BaseAwsStructuredJsonFactory; import software.amazon.awssdk.protocols.json.SdkJsonGenerator; import software.amazon.awssdk.protocols.json.StructuredJsonGenerator; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; /** * Creates generators and protocol handlers for plain text JSON wire format. @@ -42,7 +43,7 @@ protected StructuredJsonGenerator createWriter(JsonFactory jsonFactory, @Override public JsonFactory getJsonFactory() { - return JSON_FACTORY; + return JsonNodeParser.DEFAULT_JSON_FACTORY; } }; diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java deleted file mode 100644 index 914376cd4db1..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import java.io.IOException; -import java.io.InputStream; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.exception.SdkClientException; - -/** - * Parses an JSON document into a simple DOM like structure, {@link SdkJsonNode}. - */ -@SdkInternalApi -public final class JsonDomParser { - - private final JsonFactory jsonFactory; - - private JsonDomParser(JsonFactory jsonFactory) { - this.jsonFactory = jsonFactory; - } - - public SdkJsonNode parse(InputStream content) throws IOException { - try (JsonParser parser = jsonFactory.createParser(content) - .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false)) { - return parseToken(parser, parser.nextToken()); - } - } - - private SdkJsonNode parseToken(JsonParser parser, JsonToken token) throws IOException { - if (token == null) { - return null; - } - switch (token) { - case VALUE_EMBEDDED_OBJECT: - return SdkEmbeddedObject.create(parser.getEmbeddedObject()); - case VALUE_STRING: - return SdkScalarNode.create(parser.getText()); - case VALUE_FALSE: - return SdkScalarNode.create("false"); - case VALUE_TRUE: - return SdkScalarNode.create("true"); - case VALUE_NULL: - return SdkNullNode.instance(); - case VALUE_NUMBER_FLOAT: - case VALUE_NUMBER_INT: - return SdkScalarNode.create(parser.getNumberValue().toString()); - case START_OBJECT: - return parseObject(parser); - case START_ARRAY: - return parseArray(parser); - default: - throw SdkClientException.create("Unexpected JSON token - " + token); - } - } - - private SdkJsonNode parseObject(JsonParser parser) throws IOException { - JsonToken currentToken = parser.nextToken(); - SdkObjectNode.Builder builder = SdkObjectNode.builder(); - while (currentToken != JsonToken.END_OBJECT) { - String fieldName = parser.getText(); - builder.putField(fieldName, parseToken(parser, parser.nextToken())); - currentToken = parser.nextToken(); - } - return builder.build(); - } - - private SdkJsonNode parseArray(JsonParser parser) throws IOException { - JsonToken currentToken = parser.nextToken(); - SdkArrayNode.Builder builder = SdkArrayNode.builder(); - while (currentToken != JsonToken.END_ARRAY) { - builder.addItem(parseToken(parser, currentToken)); - currentToken = parser.nextToken(); - } - return builder.build(); - } - - public static JsonDomParser create(JsonFactory jsonFactory) { - return new JsonDomParser(jsonFactory); - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java deleted file mode 100644 index 2f2521946205..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import static java.util.Collections.unmodifiableList; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Represents a JSON array. - */ -@SdkInternalApi -public final class SdkArrayNode implements SdkJsonNode { - - private final List items; - - private SdkArrayNode(Builder builder) { - this.items = unmodifiableList(new ArrayList<>(builder.items)); - } - - @Override - public List items() { - return items; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SdkArrayNode that = (SdkArrayNode) o; - return Objects.equals(items, that.items); - } - - @Override - public int hashCode() { - return Objects.hashCode(items); - } - - @Override - public String toString() { - return items.stream() - .map(Object::toString) - .collect(Collectors.joining(",", "[", "]")); - } - - static Builder builder() { - return new Builder(); - } - - static final class Builder { - - private final List items = new ArrayList<>(); - - private Builder() { - } - - Builder addItem(SdkJsonNode item) { - this.items.add(item); - return this; - } - - SdkArrayNode build() { - return new SdkArrayNode(this); - } - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java deleted file mode 100644 index 1d2d413df357..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import com.fasterxml.jackson.core.JsonParser; -import java.nio.ByteBuffer; -import java.util.Date; -import java.util.Objects; -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Represents an embedded object returned by a {@link JsonParser}. This is used for the ION - * format which embeds {@link Date} and {@link ByteBuffer} objects. - */ -@SdkInternalApi -public final class SdkEmbeddedObject implements SdkJsonNode { - - private final Object embeddedObject; - - private SdkEmbeddedObject(Object embeddedObject) { - this.embeddedObject = embeddedObject; - } - - /** - * @return The embedded object that was returned by the {@link JsonParser}. - */ - @Override - public Object embeddedObject() { - return embeddedObject; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SdkEmbeddedObject that = (SdkEmbeddedObject) o; - return Objects.equals(embeddedObject, that.embeddedObject); - } - - @Override - public int hashCode() { - return Objects.hashCode(embeddedObject); - } - - static SdkEmbeddedObject create(Object embeddedObject) { - return new SdkEmbeddedObject(embeddedObject); - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java deleted file mode 100644 index 83a93e7370b5..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * DOM interface for reading a JSON document. - */ -@SdkInternalApi -public interface SdkJsonNode { - - /** - * @return True if the node represents an explicit JSON null, false otherwise. - */ - default boolean isNull() { - return false; - } - - /** - * @return The value of the node as text. Returns null for most nodes except for {@link SdkScalarNode}. - */ - default String asText() { - return null; - } - - /** - * @return The embedded object value of the node. See {@link SdkEmbeddedObject}. - */ - default Object embeddedObject() { - return null; - } - - /** - * @param fieldName Field to get value for. - * @return Value of field in the JSON object if this node represents an object, otherwise returns null. - */ - default SdkJsonNode get(String fieldName) { - return null; - } - - /** - * @return If this node represents a JSON array, then this returns the list of items in that array. Otherwise returns null. - */ - default List items() { - return null; - } - - /** - * @return If this node represents a JSON object, then this returns the map of field names to field values in that - * object. Otherwise returns null. - */ - default Map fields() { - return null; - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java deleted file mode 100644 index 3d8739cd4345..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Represents an explicit JSON null. - */ -@SdkInternalApi -public final class SdkNullNode implements SdkJsonNode { - - private static final SdkNullNode INSTANCE = new SdkNullNode(); - - private SdkNullNode() { - } - - @Override - public boolean isNull() { - return true; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof SdkNullNode; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public String toString() { - return "null"; - } - - static SdkNullNode instance() { - return INSTANCE; - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java deleted file mode 100644 index 59ab40887a02..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import static java.util.Collections.unmodifiableMap; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import software.amazon.awssdk.annotations.SdkInternalApi; - -@SdkInternalApi -public final class SdkObjectNode implements SdkJsonNode { - - private static final SdkObjectNode EMPTY = SdkObjectNode.builder().build(); - - private final Map fields; - - private SdkObjectNode(Builder builder) { - this.fields = unmodifiableMap(new HashMap<>(builder.fields)); - } - - @Override - public SdkJsonNode get(String fieldName) { - return fields.get(fieldName); - } - - @Override - public Map fields() { - return fields; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SdkObjectNode that = (SdkObjectNode) o; - return Objects.equals(fields, that.fields); - } - - @Override - public int hashCode() { - return Objects.hashCode(fields); - } - - @Override - public String toString() { - return fields.entrySet().stream() - .map(e -> String.format("\"%s\": %s", e.getKey(), e.getValue())) - .collect(Collectors.joining(",\n", "{\n", "\n}")); - } - - static Builder builder() { - return new Builder(); - } - - /** - * @return An empty JSON object. - */ - public static SdkObjectNode emptyObject() { - return EMPTY; - } - - static final class Builder { - - private final Map fields = new HashMap<>(); - - private Builder() { - } - - Builder putField(String fieldName, SdkJsonNode value) { - fields.put(fieldName, value); - return this; - } - - SdkObjectNode build() { - return new SdkObjectNode(this); - } - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java deleted file mode 100644 index 98c1eb862b7e..000000000000 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - -import java.util.Objects; -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Represents a simple scalar JSON value. This can either be a JSON string, JSON number, or JSON boolean. All values - * are coerced into a string value {@link #value()}. - */ -@SdkInternalApi -public final class SdkScalarNode implements SdkJsonNode { - - private final String value; - - private SdkScalarNode(String value) { - this.value = value; - } - - public String value() { - return value; - } - - @Override - public String asText() { - return value; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SdkScalarNode that = (SdkScalarNode) o; - return Objects.equals(value, that.value); - } - - @Override - public int hashCode() { - return Objects.hashCode(value); - } - - /** - * This does not preserve the type of the original node. For example a JSON number will be printed out - * as a JSON string here. As such this should be used for debugging and tests only. - */ - @Override - public String toString() { - return "\"" + value + "\""; - } - - static SdkScalarNode create(String value) { - return new SdkScalarNode(value); - } -} diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java index 954504d7d25f..ca34e0e04d67 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java @@ -17,7 +17,7 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; @SdkInternalApi public final class AwsJsonErrorMessageParser implements ErrorMessageParser { @@ -52,7 +52,7 @@ public AwsJsonErrorMessageParser(SdkJsonErrorMessageParser errorMessageJsonLocat * @return Error Code of exceptional response or null if it can't be determined */ @Override - public String parseErrorMessage(SdkHttpFullResponse httpResponse, SdkJsonNode jsonNode) { + public String parseErrorMessage(SdkHttpFullResponse httpResponse, JsonNode jsonNode) { String headerMessage = httpResponse.firstMatchingHeader(X_AMZN_ERROR_MESSAGE).orElse(null); if (headerMessage != null) { return headerMessage; diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java index ea832b762e8e..9772d5d465b3 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java @@ -15,7 +15,6 @@ package software.amazon.awssdk.protocols.json.internal.unmarshall; -import com.fasterxml.jackson.core.JsonFactory; import java.time.Duration; import java.util.List; import java.util.Map; @@ -33,6 +32,7 @@ import software.amazon.awssdk.protocols.core.ExceptionMetadata; import software.amazon.awssdk.protocols.json.ErrorCodeParser; import software.amazon.awssdk.protocols.json.JsonContent; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; import software.amazon.awssdk.utils.http.SdkHttpUtils; /** diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java index 9eab0e57cd36..92d579e72e98 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java @@ -17,10 +17,10 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; @SdkInternalApi public interface ErrorMessageParser { - String parseErrorMessage(SdkHttpFullResponse httpResponse, SdkJsonNode jsonNode); + String parseErrorMessage(SdkHttpFullResponse httpResponse, JsonNode jsonNode); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java index cd088e2c6e7c..6f3ca47ea793 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java @@ -21,7 +21,7 @@ import software.amazon.awssdk.core.SdkField; import software.amazon.awssdk.core.traits.JsonValueTrait; import software.amazon.awssdk.protocols.core.StringToValueConverter; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; import software.amazon.awssdk.utils.BinaryUtils; /** @@ -78,7 +78,7 @@ private SimpleHeaderUnmarshaller(StringToValueConverter.StringToValue stringT @Override public T unmarshall(JsonUnmarshallerContext context, - SdkJsonNode jsonContent, + JsonNode jsonContent, SdkField field) { return context.response().firstMatchingHeader(field.locationName()) .map(s -> stringToValue.convert(s, field)) diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java index dcaf190e3565..a462cf09c149 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java @@ -25,7 +25,7 @@ import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.protocols.json.ErrorCodeParser; import software.amazon.awssdk.protocols.json.JsonContent; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; @SdkInternalApi public class JsonErrorCodeParser implements ErrorCodeParser { @@ -115,15 +115,15 @@ private String parseErrorCodeFromXAmzErrorType(String headerValue) { * "prefix#typeName" Examples : "AccessDeniedException", * "software.amazon.awssdk.dynamodb.v20111205#ProvisionedThroughputExceededException" */ - private String parseErrorCodeFromContents(SdkJsonNode jsonContents) { + private String parseErrorCodeFromContents(JsonNode jsonContents) { if (jsonContents == null) { return null; } - SdkJsonNode errorCodeField = jsonContents.get(errorCodeFieldName); + JsonNode errorCodeField = jsonContents.get(errorCodeFieldName).orElse(null); if (errorCodeField == null) { return null; } - String code = errorCodeField.asText(); + String code = errorCodeField.text(); int separator = code.lastIndexOf("#"); return code.substring(separator + 1); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java index ba7f5630f324..12d4c4662ebc 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java @@ -28,7 +28,6 @@ import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.core.SdkField; import software.amazon.awssdk.core.SdkPojo; -import software.amazon.awssdk.core.io.ReleasableInputStream; import software.amazon.awssdk.core.protocol.MarshallLocation; import software.amazon.awssdk.core.protocol.MarshallingType; import software.amazon.awssdk.core.traits.ListTrait; @@ -39,8 +38,8 @@ import software.amazon.awssdk.protocols.core.StringToInstant; import software.amazon.awssdk.protocols.core.StringToValueConverter; import software.amazon.awssdk.protocols.json.internal.MarshallerUtil; -import software.amazon.awssdk.protocols.json.internal.dom.JsonDomParser; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.utils.builder.Buildable; /** @@ -55,7 +54,7 @@ public final class JsonProtocolUnmarshaller { private final JsonUnmarshallerRegistry registry; - private final JsonDomParser parser; + private final JsonNodeParser parser; private JsonProtocolUnmarshaller(Builder builder) { this.parser = builder.parser; @@ -96,21 +95,21 @@ private static JsonUnmarshallerRegistry createUnmarshallerRegistry( } private static SdkBytes unmarshallSdkBytes(JsonUnmarshallerContext context, - SdkJsonNode jsonContent, + JsonNode jsonContent, SdkField field) { if (jsonContent == null || jsonContent.isNull()) { return null; } // Binary protocols like CBOR may already have the raw bytes extracted. - if (jsonContent.embeddedObject() != null) { - return SdkBytes.fromByteArray((byte[]) jsonContent.embeddedObject()); + if (jsonContent.isEmbeddedObject()) { + return SdkBytes.fromByteArray((byte[]) jsonContent.asEmbeddedObject()); } else { // Otherwise decode the JSON string as Base64 - return TO_SDK_BYTES.convert(jsonContent.asText(), field); + return TO_SDK_BYTES.convert(jsonContent.text(), field); } } - private static SdkPojo unmarshallStructured(JsonUnmarshallerContext context, SdkJsonNode jsonContent, SdkField f) { + private static SdkPojo unmarshallStructured(JsonUnmarshallerContext context, JsonNode jsonContent, SdkField f) { if (jsonContent == null || jsonContent.isNull()) { return null; } else { @@ -119,25 +118,25 @@ private static SdkPojo unmarshallStructured(JsonUnmarshallerContext context, Sdk } private static Map unmarshallMap(JsonUnmarshallerContext context, - SdkJsonNode jsonContent, + JsonNode jsonContent, SdkField> field) { if (jsonContent == null || jsonContent.isNull()) { return null; } SdkField valueInfo = field.getTrait(MapTrait.class).valueFieldInfo(); Map map = new HashMap<>(); - jsonContent.fields().forEach((fieldName, value) -> { + jsonContent.asObject().forEach((fieldName, value) -> { JsonUnmarshaller unmarshaller = context.getUnmarshaller(valueInfo.location(), valueInfo.marshallingType()); map.put(fieldName, unmarshaller.unmarshall(context, value, valueInfo)); }); return map; } - private static List unmarshallList(JsonUnmarshallerContext context, SdkJsonNode jsonContent, SdkField> field) { + private static List unmarshallList(JsonUnmarshallerContext context, JsonNode jsonContent, SdkField> field) { if (jsonContent == null || jsonContent.isNull()) { return null; } - return jsonContent.items() + return jsonContent.asArray() .stream() .map(item -> { SdkField memberInfo = field.getTrait(ListTrait.class).memberFieldInfo(); @@ -158,16 +157,16 @@ private SimpleTypeJsonUnmarshaller(StringToValueConverter.StringToValue strin @Override public T unmarshall(JsonUnmarshallerContext context, - SdkJsonNode jsonContent, + JsonNode jsonContent, SdkField field) { - return jsonContent != null && !jsonContent.isNull() ? stringToValue.convert(jsonContent.asText(), field) : null; + return jsonContent != null && !jsonContent.isNull() ? stringToValue.convert(jsonContent.text(), field) : null; } } public TypeT unmarshall(SdkPojo sdkPojo, SdkHttpFullResponse response) throws IOException { if (hasPayloadMembersOnUnmarshall(sdkPojo) && !hasExplicitBlobPayloadMember(sdkPojo) && response.content().isPresent()) { - SdkJsonNode jsonNode = parser.parse(ReleasableInputStream.wrap(response.content().get()).disableClose()); + JsonNode jsonNode = parser.parse(response.content().get()); return unmarshall(sdkPojo, response, jsonNode); } else { return unmarshall(sdkPojo, response, null); @@ -193,7 +192,7 @@ private boolean hasPayloadMembersOnUnmarshall(SdkPojo sdkPojo) { public TypeT unmarshall(SdkPojo sdkPojo, SdkHttpFullResponse response, - SdkJsonNode jsonContent) { + JsonNode jsonContent) { JsonUnmarshallerContext context = JsonUnmarshallerContext.builder() .unmarshallerRegistry(registry) .response(response) @@ -203,14 +202,14 @@ public TypeT unmarshall(SdkPojo sdkPojo, @SuppressWarnings("unchecked") private static TypeT unmarshallStructured(SdkPojo sdkPojo, - SdkJsonNode jsonContent, + JsonNode jsonContent, JsonUnmarshallerContext context) { for (SdkField field : sdkPojo.sdkFields()) { if (isExplicitPayloadMember(field) && field.marshallingType() == MarshallingType.SDK_BYTES && context.response().content().isPresent()) { field.set(sdkPojo, SdkBytes.fromInputStream(context.response().content().get())); } else { - SdkJsonNode jsonFieldContent = getSdkJsonNode(jsonContent, field); + JsonNode jsonFieldContent = getJsonNode(jsonContent, field); JsonUnmarshaller unmarshaller = context.getUnmarshaller(field.location(), field.marshallingType()); field.set(sdkPojo, unmarshaller.unmarshall(context, jsonFieldContent, (SdkField) field)); } @@ -218,11 +217,12 @@ private static TypeT unmarshallStructured(SdkPojo sdkPoj return (TypeT) ((Buildable) sdkPojo).build(); } - private static SdkJsonNode getSdkJsonNode(SdkJsonNode jsonContent, SdkField field) { + private static JsonNode getJsonNode(JsonNode jsonContent, SdkField field) { if (jsonContent == null) { return null; } - return isExplicitPayloadMember(field) ? jsonContent : jsonContent.get(field.locationName()); + return isExplicitPayloadMember(field) ? jsonContent : jsonContent.get(field.locationName()) + .orElse(null); } /** @@ -237,7 +237,7 @@ public static Builder builder() { */ public static final class Builder { - private JsonDomParser parser; + private JsonNodeParser parser; private Map defaultTimestampFormats; private Builder() { @@ -247,7 +247,7 @@ private Builder() { * @param parser JSON parser to use. * @return This builder for method chaining. */ - public Builder parser(JsonDomParser parser) { + public Builder parser(JsonNodeParser parser) { this.parser = parser; return this; } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java index b71e0adbbdac..fc6af9f6b4c8 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java @@ -17,7 +17,7 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.core.SdkField; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; /** * Interface for unmarshalling a field from a JSON based service. @@ -34,7 +34,7 @@ public interface JsonUnmarshaller { * @return Unmarshalled value. */ T unmarshall(JsonUnmarshallerContext context, - SdkJsonNode jsonContent, + JsonNode jsonContent, SdkField field); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java index 02fac7fb163b..295c23ce0caf 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java @@ -20,7 +20,7 @@ import java.util.List; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; @SdkInternalApi public class SdkJsonErrorMessageParser implements ErrorMessageParser { @@ -50,11 +50,11 @@ private SdkJsonErrorMessageParser(List errorMessageJsonLocations) { * @return Error Code of exceptional response or null if it can't be determined */ @Override - public String parseErrorMessage(SdkHttpFullResponse httpResponse, SdkJsonNode jsonNode) { + public String parseErrorMessage(SdkHttpFullResponse httpResponse, JsonNode jsonNode) { for (String field : errorMessageJsonLocations) { - SdkJsonNode value = jsonNode.get(field); + String value = jsonNode.get(field).map(JsonNode::text).orElse(null); if (value != null) { - return value.asText(); + return value; } } return null; diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java index 073ba292b3ca..54e2a89c591e 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java @@ -18,17 +18,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import com.fasterxml.jackson.core.JsonFactory; -import java.io.IOException; -import java.io.UncheckedIOException; import java.util.UUID; import org.junit.Before; import org.junit.Test; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.JsonDomParser; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; import software.amazon.awssdk.protocols.json.internal.unmarshall.AwsJsonErrorMessageParser; import software.amazon.awssdk.protocols.json.internal.unmarshall.ErrorMessageParser; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.utils.StringInputStream; public class AwsJsonErrorMessageParserTest { @@ -41,47 +38,39 @@ public class AwsJsonErrorMessageParserTest { private SdkHttpFullResponse.Builder responseBuilder; - private JsonDomParser jsonParser; + private JsonNodeParser jsonParser; @Before public void setup() { - jsonParser = JsonDomParser.create(new JsonFactory()); + jsonParser = JsonNode.parser(); responseBuilder = ValidSdkObjects.sdkHttpFullResponse(); } @Test public void testErrorMessageAt_message() { - SdkJsonNode jsonNode = parseJson("message", MESSAGE_CONTENT); + JsonNode jsonNode = parseJson("message", MESSAGE_CONTENT); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertEquals(MESSAGE_CONTENT, parsed); } - private SdkJsonNode parseJson(String fieldName, String value) { - try { - return jsonParser.parse(new StringInputStream(String.format("{\"%s\": \"%s\"}", fieldName, value))); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + private JsonNode parseJson(String fieldName, String value) { + return jsonParser.parse(new StringInputStream(String.format("{\"%s\": \"%s\"}", fieldName, value))); } - private SdkJsonNode parseJson(String json) { - try { - return jsonParser.parse(new StringInputStream(json)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + private JsonNode parseJson(String json) { + return jsonParser.parse(new StringInputStream(json)); } @Test public void testErrorMessageAt_Message() { - SdkJsonNode jsonNode = parseJson("Message", MESSAGE_CONTENT); + JsonNode jsonNode = parseJson("Message", MESSAGE_CONTENT); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertEquals(MESSAGE_CONTENT, parsed); } @Test public void testErrorMessageAt_errorMessage() { - SdkJsonNode jsonNode = parseJson("errorMessage", MESSAGE_CONTENT); + JsonNode jsonNode = parseJson("errorMessage", MESSAGE_CONTENT); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertEquals(MESSAGE_CONTENT, parsed); } @@ -94,14 +83,14 @@ public void testNoErrorMessage_ReturnsNull() { @Test public void testErrorMessageIsNumber_ReturnsStringValue() { - SdkJsonNode jsonNode = parseJson("{\"message\": 1}"); + JsonNode jsonNode = parseJson("{\"message\": 1}"); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertEquals("1", parsed); } @Test public void testErrorMessageIsObject_ReturnsNull() { - SdkJsonNode jsonNode = parseJson("{\"message\": {\"foo\": \"bar\"}}"); + JsonNode jsonNode = parseJson("{\"message\": {\"foo\": \"bar\"}}"); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertNull(parsed); } @@ -137,7 +126,7 @@ public void errorMessageInHeader_ReturnsHeaderValue_CaseInsensitive() { @Test public void errorMessageInHeader_TakesPrecedenceOverMessageInBody() { responseBuilder.putHeader(X_AMZN_ERROR_MESSAGE, MESSAGE_CONTENT); - SdkJsonNode jsonNode = parseJson("message", "other message in body"); + JsonNode jsonNode = parseJson("message", "other message in body"); String parsed = parser.parseErrorMessage(responseBuilder.build(), jsonNode); assertEquals(MESSAGE_CONTENT, parsed); } diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java index 9c4b94faee02..bc814da1d1ad 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java @@ -18,14 +18,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import com.fasterxml.jackson.core.JsonFactory; import java.io.IOException; import org.junit.Test; import software.amazon.awssdk.http.SdkHttpFullResponse; -import software.amazon.awssdk.protocols.json.internal.dom.JsonDomParser; -import software.amazon.awssdk.protocols.json.internal.dom.SdkJsonNode; -import software.amazon.awssdk.protocols.json.internal.dom.SdkObjectNode; import software.amazon.awssdk.protocols.json.internal.unmarshall.JsonErrorCodeParser; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; import software.amazon.awssdk.utils.StringInputStream; public class JsonErrorCodeParserTest { @@ -45,7 +42,7 @@ public class JsonErrorCodeParserTest { private final JsonErrorCodeParser parser = new JsonErrorCodeParser(ERROR_FIELD_NAME); private static JsonContent toJsonContent(String errorType) throws IOException { - SdkJsonNode node = JsonDomParser.create(new JsonFactory()).parse(new StringInputStream( + JsonNode node = JsonNode.parser().parse(new StringInputStream( String.format("{\"%s\": \"%s\"}", ERROR_FIELD_NAME, errorType))); return new JsonContent(null, node); } @@ -103,6 +100,6 @@ public void parseErrorType_NotPresentInHeadersAndNullContent_ReturnsNull() { @Test public void parseErrorType_NotPresentInHeadersAndEmptyContent_ReturnsNull() { assertNull(parser.parseErrorCode(httpResponseWithoutHeaders(), - new JsonContent(null, SdkObjectNode.emptyObject()))); + new JsonContent(null, JsonNode.emptyObjectNode()))); } } diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java index 7a81210c06fe..59058d76f251 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java @@ -19,21 +19,18 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.time.Instant; import org.junit.Before; import org.junit.Test; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory; import software.amazon.awssdk.utils.BinaryUtils; public class SdkJsonGeneratorTest { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - /** * Delta for comparing double values */ @@ -43,7 +40,7 @@ public class SdkJsonGeneratorTest { @Before public void setup() { - jsonGenerator = new SdkJsonGenerator(new JsonFactory(), "application/json"); + jsonGenerator = new SdkJsonGenerator(JsonFactory.builder().build(), "application/json"); } @Test @@ -56,10 +53,10 @@ public void simpleObject_AllPrimitiveTypes() throws IOException { jsonGenerator.writeEndObject(); JsonNode node = toJsonNode(); assertTrue(node.isObject()); - assertEquals("stringVal", node.get("stringProp").textValue()); - assertEquals(42, node.get("integralProp").longValue()); - assertEquals(true, node.get("booleanProp").booleanValue()); - assertEquals(123.456, node.get("doubleProp").doubleValue(), DELTA); + assertEquals("stringVal", node.asObject().get("stringProp").text()); + assertEquals("42", node.asObject().get("integralProp").asNumber()); + assertEquals(true, node.asObject().get("booleanProp").asBoolean()); + assertEquals(123.456, Double.parseDouble(node.asObject().get("doubleProp").asNumber()), DELTA); } @Test @@ -68,7 +65,7 @@ public void simpleObject_WithLongProperty_PreservesLongValue() throws IOExceptio jsonGenerator.writeFieldName("longProp").writeValue(Long.MAX_VALUE); jsonGenerator.writeEndObject(); JsonNode node = toJsonNode(); - assertEquals(Long.MAX_VALUE, node.get("longProp").longValue()); + assertEquals(Long.toString(Long.MAX_VALUE), node.asObject().get("longProp").asNumber()); } @Test @@ -78,7 +75,7 @@ public void simpleObject_WithBinaryData_WritesAsBase64() throws IOException { jsonGenerator.writeFieldName("binaryProp").writeValue(ByteBuffer.wrap(data)); jsonGenerator.writeEndObject(); JsonNode node = toJsonNode(); - assertEquals(BinaryUtils.toBase64(data), node.get("binaryProp").textValue()); + assertEquals(BinaryUtils.toBase64(data), node.asObject().get("binaryProp").text()); } @Test @@ -88,7 +85,7 @@ public void simpleObject_WithServiceDate() throws IOException { jsonGenerator.writeFieldName("dateProp").writeValue(instant); jsonGenerator.writeEndObject(); JsonNode node = toJsonNode(); - assertEquals(123.456, node.get("dateProp").doubleValue(), DELTA); + assertEquals(123.456, Double.parseDouble(node.asObject().get("dateProp").asNumber()), DELTA); } @Test @@ -100,9 +97,9 @@ public void stringArray() throws IOException { jsonGenerator.writeEndArray(); JsonNode node = toJsonNode(); assertTrue(node.isArray()); - assertEquals("valOne", node.get(0).textValue()); - assertEquals("valTwo", node.get(1).textValue()); - assertEquals("valThree", node.get(2).textValue()); + assertEquals("valOne", node.asArray().get(0).text()); + assertEquals("valTwo", node.asArray().get(1).text()); + assertEquals("valThree", node.asArray().get(2).text()); } @Test @@ -113,7 +110,7 @@ public void complexArray() throws IOException { jsonGenerator.writeEndObject(); jsonGenerator.writeEndArray(); JsonNode node = toJsonNode(); - assertEquals("nestedVal", node.get(0).get("nestedProp").textValue()); + assertEquals("nestedVal", node.asArray().get(0).asObject().get("nestedProp").text()); } @Test @@ -132,7 +129,7 @@ public void unclosedArray_AutoClosesOnClose() throws IOException { jsonGenerator.writeValue("valThree"); JsonNode node = toJsonNode(); assertTrue(node.isArray()); - assertEquals(3, node.size()); + assertEquals(3, node.asArray().size()); } // See https://forums.aws.amazon.com/thread.jspa?threadID=158756 @@ -175,7 +172,7 @@ public void testNumericNoQuote() { } private JsonNode toJsonNode() throws IOException { - return MAPPER.readTree(jsonGenerator.getBytes()); + return JsonNode.parser().parse(new ByteArrayInputStream(jsonGenerator.getBytes())); } } diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java deleted file mode 100644 index ab8ae765a369..000000000000 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocols.json.internal.dom; - - -import static org.assertj.core.api.Assertions.assertThat; - -import com.fasterxml.jackson.core.JsonFactory; -import java.io.IOException; -import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.utils.StringInputStream; - -public class JsonDomParserTest { - - private JsonDomParser parser; - - @Before - public void setup() { - parser = JsonDomParser.create(new JsonFactory()); - } - - @Test - public void simpleString_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("\"foo\""); - assertThat(node) - .isInstanceOf(SdkScalarNode.class) - .matches(s -> ((SdkScalarNode) s).value().equals("foo")); - } - - @Test - public void simpleNumber_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("42"); - assertThat(node) - .isInstanceOf(SdkScalarNode.class) - .matches(s -> ((SdkScalarNode) s).value().equals("42")); - } - - @Test - public void decimalNumber_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("1234.56"); - assertThat(node) - .isInstanceOf(SdkScalarNode.class) - .matches(s -> ((SdkScalarNode) s).value().equals("1234.56")); - } - - @Test - public void falseBoolean_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("false"); - assertThat(node) - .isInstanceOf(SdkScalarNode.class) - .matches(s -> ((SdkScalarNode) s).value().equals("false")); - } - - @Test - public void trueBoolean_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("true"); - assertThat(node) - .isInstanceOf(SdkScalarNode.class) - .matches(s -> ((SdkScalarNode) s).value().equals("true")); - } - - @Test - public void jsonNull_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("null"); - assertThat(node).isInstanceOf(SdkNullNode.class); - } - - @Test - public void emptyObject_ParsedCorrecty() throws IOException { - SdkJsonNode node = parse("{}"); - SdkObjectNode expected = SdkObjectNode.builder().build(); - assertThat(node).isInstanceOf(SdkObjectNode.class) - .isEqualTo(expected); - } - - @Test - public void simpleObjectOfScalars_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("{" - + " \"stringMember\": \"foo\"," - + " \"integerMember\": 42," - + " \"floatMember\": 1234.56," - + " \"booleanMember\": true," - + " \"nullMember\": null" - + "}"); - - SdkObjectNode expected = SdkObjectNode.builder() - .putField("stringMember", scalar("foo")) - .putField("integerMember", scalar("42")) - .putField("floatMember", scalar("1234.56")) - .putField("booleanMember", scalar("true")) - .putField("nullMember", nullNode()) - .build(); - assertThat(node).isInstanceOf(SdkObjectNode.class) - .isEqualTo(expected); - } - - @Test - public void nestedObject_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("{" - + " \"structMember\": {" - + " \"floatMember\": 1234.56," - + " \"booleanMember\": true," - + " \"nullMember\": null" - + " }," - + " \"integerMember\": 42" - + "}"); - - SdkObjectNode expected = SdkObjectNode.builder() - .putField("structMember", - SdkObjectNode.builder() - .putField("floatMember", scalar("1234.56")) - .putField("booleanMember", scalar("true")) - .putField("nullMember", nullNode()) - .build()) - .putField("integerMember", scalar("42")) - .build(); - assertThat(node).isInstanceOf(SdkObjectNode.class) - .isEqualTo(expected); - } - - @Test - public void emptyArray_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("[]"); - SdkArrayNode expected = SdkArrayNode.builder().build(); - assertThat(node).isInstanceOf(SdkArrayNode.class) - .isEqualTo(expected); - } - - @Test - public void arrayOfScalars_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("[\"foo\", 42, null, false, 1234.56]"); - SdkArrayNode expected = SdkArrayNode.builder() - .addItem(scalar("foo")) - .addItem(scalar("42")) - .addItem(nullNode()) - .addItem(scalar("false")) - .addItem(scalar("1234.56")) - .build(); - assertThat(node).isInstanceOf(SdkArrayNode.class) - .isEqualTo(expected); - } - - @Test - public void nestedArray_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("[[\"valOne\", \"valTwo\"], [\"valThree\", \"valFour\"]]"); - SdkArrayNode expected = SdkArrayNode.builder() - .addItem(array(scalar("valOne"), scalar("valTwo"))) - .addItem(array(scalar("valThree"), scalar("valFour"))) - .build(); - assertThat(node).isInstanceOf(SdkArrayNode.class) - .isEqualTo(expected); - } - - @Test - public void complexObject_ParsedCorrectly() throws IOException { - SdkJsonNode node = parse("{" - + " \"stringMember\":\"foo\"," - + " \"deeplyNestedArray\":[" - + " [\"valOne\", 42, null]," - + " \"valTwo\"," - + " [" - + " []," - + " [\"valThree\"]" - + " ]" - + " ]," - + " \"deeplyNestedObject\":{\n" - + " \"deeplyNestedArray\":[" - + " [\"valOne\", 42, null]," - + " \"valTwo\"," - + " [" - + " []," - + " [\"valThree\"]" - + " ]" - + " ]," - + " \"nestedObject\":{" - + " \"stringMember\":\"foo\"," - + " \"integerMember\":42," - + " \"floatMember\":1234.56," - + " \"booleanMember\":true," - + " \"furtherNestedObject\":{" - + " \"stringMember\":\"foo\"," - + " \"arrayMember\":[" - + " \"valOne\"," - + " \"valTwo\"" - + " ],\n" - + " \"nullMember\":null" - + " }" - + " }" - + " }" - + "}"); - SdkArrayNode deeplyNestedArray = array( - array(scalar("valOne"), scalar("42"), nullNode()), - scalar("valTwo"), - array(array(), array(scalar("valThree"))) - ); - - SdkObjectNode furtherNestedObject = - SdkObjectNode.builder() - .putField("stringMember", scalar("foo")) - .putField("arrayMember", array(scalar("valOne"), scalar("valTwo"))) - .putField("nullMember", nullNode()) - .build(); - - SdkObjectNode deeplyNestedObject = - SdkObjectNode.builder() - .putField("deeplyNestedArray", deeplyNestedArray) - .putField("nestedObject", - SdkObjectNode.builder() - .putField("stringMember", scalar("foo")) - .putField("integerMember", scalar("42")) - .putField("floatMember", scalar("1234.56")) - .putField("booleanMember", scalar("true")) - .putField("furtherNestedObject", furtherNestedObject) - .build()) - .build(); - - SdkObjectNode expected = SdkObjectNode.builder() - .putField("stringMember", scalar("foo")) - .putField("deeplyNestedArray", deeplyNestedArray) - .putField("deeplyNestedObject", - deeplyNestedObject) - .build(); - assertThat(node).isInstanceOf(SdkObjectNode.class) - .isEqualTo(expected); - } - - private SdkNullNode nullNode() { - return SdkNullNode.instance(); - } - - private SdkScalarNode scalar(String value) { - return SdkScalarNode.create(value); - } - - private SdkArrayNode array(SdkJsonNode... nodes) { - SdkArrayNode.Builder builder = SdkArrayNode.builder(); - Arrays.stream(nodes).forEach(builder::addItem); - return builder.build(); - } - - private SdkJsonNode parse(String json) throws IOException { - return parser.parse(new StringInputStream(json)); - } - - -} diff --git a/core/protocols/pom.xml b/core/protocols/pom.xml index 1451d6d42c54..d7fadaf049ec 100644 --- a/core/protocols/pom.xml +++ b/core/protocols/pom.xml @@ -34,7 +34,6 @@ aws-query-protocol aws-json-protocol aws-cbor-protocol - aws-ion-protocol aws-xml-protocol protocol-core diff --git a/core/regions/pom.xml b/core/regions/pom.xml index 089a673abb8b..4d85033b4494 100644 --- a/core/regions/pom.xml +++ b/core/regions/pom.xml @@ -49,12 +49,9 @@ ${awsjavasdk.version} - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations + software.amazon.awssdk + json-utils + ${awsjavasdk.version} org.slf4j diff --git a/core/regions/src/it/java/software/amazon/awssdk/regions/util/EC2MetadataUtilsIntegrationTest.java b/core/regions/src/it/java/software/amazon/awssdk/regions/util/EC2MetadataUtilsIntegrationTest.java index 07d94e80bf58..1764cedb5d9c 100644 --- a/core/regions/src/it/java/software/amazon/awssdk/regions/util/EC2MetadataUtilsIntegrationTest.java +++ b/core/regions/src/it/java/software/amazon/awssdk/regions/util/EC2MetadataUtilsIntegrationTest.java @@ -49,39 +49,6 @@ public static void cleanUp() throws IOException { System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property()); } - @Test - public void testIamInfo() { - EC2MetadataUtils.IamInfo info = EC2MetadataUtils - .getIamInstanceProfileInfo(); - - Assert.assertEquals("Success", info.code); - Assert.assertNull(info.message); - Assert.assertEquals("2014-04-07T08:18:41Z", info.lastUpdated); - Assert.assertEquals("foobar", info.instanceProfileArn); - Assert.assertEquals("moobily", info.instanceProfileId); - } - - @Test - public void testIamCredentials() { - Map map = EC2MetadataUtils - .getIamSecurityCredentials(); - - Assert.assertEquals(2, map.size()); - - for (Map.Entry entry : map - .entrySet()) { - - Assert.assertNotNull(entry.getKey()); - Assert.assertNotNull(entry.getValue().code); - Assert.assertNotNull(entry.getValue().lastUpdated); - Assert.assertEquals("AWS-HMAC", entry.getValue().type); - Assert.assertEquals("foobar", entry.getValue().accessKeyId); - Assert.assertEquals("moobily", entry.getValue().secretAccessKey); - Assert.assertEquals("beebop", entry.getValue().token); - Assert.assertNotNull(entry.getValue().expiration); - } - } - @Test(expected = SdkClientException.class) public void ec2MetadataDisabled_shouldThrowException() { try { @@ -92,26 +59,6 @@ public void ec2MetadataDisabled_shouldThrowException() { } } - @Test - public void testInstanceInfo() { - EC2MetadataUtils.InstanceInfo info = EC2MetadataUtils.getInstanceInfo(); - Assert.assertEquals("2014-08-07T22:07:46Z", info.getPendingTime()); - Assert.assertEquals("m1.small", info.getInstanceType()); - Assert.assertEquals("ami-a49665cc", info.getImageId()); - Assert.assertEquals("i-6b2de041", info.getInstanceId()); - Assert.assertEquals("foo", info.getBillingProducts()[0]); - Assert.assertEquals("x86_64", info.getArchitecture()); - Assert.assertEquals("599169622985", info.getAccountId()); - Assert.assertEquals("aki-919dcaf8", info.getKernelId()); - Assert.assertEquals("baz", info.getRamdiskId()); - Assert.assertEquals("us-east-1", info.getRegion()); - Assert.assertEquals("2010-08-31", info.getVersion()); - Assert.assertEquals("us-east-1b", info.getAvailabilityZone()); - Assert.assertEquals("10.201.215.38", info.getPrivateIp()); - Assert.assertEquals("bar", info.getDevpayProductCodes()[0]); - Assert.assertEquals("qaz", info.getMarketplaceProductCodes()[0]); - } - @Test public void testInstanceSignature() { String signature = EC2MetadataUtils.getInstanceSignature(); diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java b/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java index 985eda091c36..9edef845f0a0 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java @@ -15,16 +15,9 @@ package software.amazon.awssdk.regions.internal.util; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -40,7 +33,8 @@ import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.exception.SdkServiceException; import software.amazon.awssdk.core.internal.util.UserAgentUtils; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.regions.util.HttpResourcesUtils; import software.amazon.awssdk.regions.util.ResourcesEndpointProvider; @@ -67,6 +61,7 @@ //TODO: cleanup @SdkInternalApi public final class EC2MetadataUtils { + private static final JsonNodeParser JSON_PARSER = JsonNode.parser(); /** Default resource path for credentials in the Amazon EC2 Instance Metadata Service. */ private static final String REGION = "region"; @@ -80,9 +75,8 @@ public final class EC2MetadataUtils { private static final int DEFAULT_QUERY_RETRIES = 3; private static final int MINIMUM_RETRY_WAIT_TIME_MILLISECONDS = 250; - private static final ObjectMapper MAPPER = new ObjectMapper(); private static final Logger log = LoggerFactory.getLogger(EC2MetadataUtils.class); - private static Map cache = new ConcurrentHashMap<>(); + private static final Map CACHE = new ConcurrentHashMap<>(); private static final InstanceProviderTokenEndpointProvider TOKEN_ENDPOINT_PROVIDER = new InstanceProviderTokenEndpointProvider(); @@ -90,11 +84,6 @@ public final class EC2MetadataUtils { private EC2MetadataUtils() { } - static { - MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE); - } - /** * Get the AMI ID used to launch the instance. */ @@ -216,57 +205,6 @@ public static List getSecurityGroups() { return getItems(EC2_METADATA_ROOT + "/security-groups"); } - /** - * Get information about the last time the instance profile was updated, - * including the instance's LastUpdated date, InstanceProfileArn, and - * InstanceProfileId. - */ - public static IamInfo getIamInstanceProfileInfo() { - String json = getData(EC2_METADATA_ROOT + "/iam/info"); - if (null == json) { - return null; - } - - try { - - return MAPPER.readValue(json, IamInfo.class); - - } catch (Exception e) { - log.warn("Unable to parse IAM Instance profile info (" + json - + "): " + e.getMessage(), e); - return null; - } - } - - /** - * The instance info is only guaranteed to be a JSON document per - * http://docs - * .aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html - *

- * This method is only a best attempt to capture the instance info as a - * typed object. - *

- * Get an InstanceInfo object with dynamic information about this instance. - */ - public static InstanceInfo getInstanceInfo() { - return doGetInstanceInfo(getData( - EC2_DYNAMICDATA_ROOT + INSTANCE_IDENTITY_DOCUMENT)); - } - - static InstanceInfo doGetInstanceInfo(String json) { - if (null != json) { - try { - InstanceInfo instanceInfo = JacksonUtils.fromJsonString(json, - InstanceInfo.class); - return instanceInfo; - } catch (Exception e) { - log.warn("Unable to parse dynamic EC2 instance info (" + json - + ") : " + e.getMessage(), e); - } - } - return null; - } - /** * Get the signature of the instance. */ @@ -291,45 +229,17 @@ public static String getEC2InstanceRegion() { static String doGetEC2InstanceRegion(final String json) { if (null != json) { try { - JsonNode node = MAPPER.readTree(json.getBytes(StandardCharsets.UTF_8)); - JsonNode region = node.findValue(REGION); - return region.asText(); + return JSON_PARSER.parse(json) + .get(REGION) + .map(JsonNode::text) + .orElseThrow(() -> new IllegalStateException("Region not included in metadata.")); } catch (Exception e) { - log.warn("Unable to parse EC2 instance info (" + json - + ") : " + e.getMessage(), e); + log.warn("Unable to parse EC2 instance info (" + json + ") : " + e.getMessage(), e); } } return null; } - /** - * Returns the temporary security credentials (AccessKeyId, SecretAccessKey, - * SessionToken, and Expiration) associated with the IAM roles on the - * instance. - */ - public static Map getIamSecurityCredentials() { - Map credentialsInfoMap = new HashMap<>(); - - List credentials = getItems(EC2_METADATA_ROOT - + "/iam/security-credentials"); - - if (null != credentials) { - for (String credential : credentials) { - String json = getData(EC2_METADATA_ROOT - + "/iam/security-credentials/" + credential); - try { - IamSecurityCredential credentialInfo = MAPPER - .readValue(json, IamSecurityCredential.class); - credentialsInfoMap.put(credential, credentialInfo); - } catch (Exception e) { - log.warn("Unable to process the credential (" + credential - + "). " + e.getMessage(), e); - } - } - } - return credentialsInfoMap; - } - /** * Get the virtual devices associated with the ami, root, ebs, and swap. */ @@ -392,7 +302,7 @@ public static List getItems(String path, int tries) { @SdkTestInternalApi static void clearCache() { - cache.clear(); + CACHE.clear(); } private static List getItems(String path, int tries, boolean slurp) { @@ -468,10 +378,10 @@ private static String fetchData(String path, boolean force) { } try { - if (force || !cache.containsKey(path)) { - cache.put(path, getData(path)); + if (force || !CACHE.containsKey(path)) { + CACHE.put(path, getData(path)); } - return cache.get(path); + return CACHE.get(path); } catch (SdkClientException e) { throw e; } catch (RuntimeException e) { @@ -479,165 +389,6 @@ private static String fetchData(String path, boolean force) { } } - /** - * Information about the last time the instance profile was updated, - * including the instance's LastUpdated date, InstanceProfileArn, and - * InstanceProfileId. - */ - public static class IamInfo { - public String code; - public String message; - public String lastUpdated; - public String instanceProfileArn; - public String instanceProfileId; - } - - /** - * The temporary security credentials (AccessKeyId, SecretAccessKey, - * SessionToken, and Expiration) associated with the IAM role. - */ - public static class IamSecurityCredential { - public String code; - public String message; - public String lastUpdated; - public String type; - public String accessKeyId; - public String secretAccessKey; - public String token; - public String expiration; - - /** - * @deprecated because it is spelled incorrectly - * @see #accessKeyId - */ - @Deprecated - public String secretAcessKey; - } - - /** - * This POJO is a best attempt to capture the instance info which is only - * guaranteed to be a JSON document per - * http://docs.aws.amazon.com/AWSEC2/latest - * /UserGuide/ec2-instance-metadata.html - * - * Instance info includes dynamic information about the current instance - * such as region, instanceId, private IP address, etc. - */ - public static class InstanceInfo { - private final String pendingTime; - private final String instanceType; - private final String imageId; - private final String instanceId; - private final String[] billingProducts; - private final String architecture; - private final String accountId; - private final String kernelId; - private final String ramdiskId; - private final String region; - private final String version; - private final String availabilityZone; - private final String privateIp; - private final String[] devpayProductCodes; - private final String[] marketplaceProductCodes; - - @JsonCreator - public InstanceInfo( - @JsonProperty(value = "pendingTime", required = true) String pendingTime, - @JsonProperty(value = "instanceType", required = true) String instanceType, - @JsonProperty(value = "imageId", required = true) String imageId, - @JsonProperty(value = "instanceId", required = true) String instanceId, - @JsonProperty(value = "billingProducts", required = false) String[] billingProducts, - @JsonProperty(value = "architecture", required = true) String architecture, - @JsonProperty(value = "accountId", required = true) String accountId, - @JsonProperty(value = "kernelId", required = true) String kernelId, - @JsonProperty(value = "ramdiskId", required = false) String ramdiskId, - @JsonProperty(value = REGION, required = true) String region, - @JsonProperty(value = "version", required = true) String version, - @JsonProperty(value = "availabilityZone", required = true) String availabilityZone, - @JsonProperty(value = "privateIp", required = true) String privateIp, - @JsonProperty(value = "devpayProductCodes", required = false) String[] devpayProductCodes, - @JsonProperty(value = "marketplaceProductCodes", required = false) String[] marketplaceProductCodes) { - this.pendingTime = pendingTime; - this.instanceType = instanceType; - this.imageId = imageId; - this.instanceId = instanceId; - this.billingProducts = billingProducts == null - ? null : billingProducts.clone(); - this.architecture = architecture; - this.accountId = accountId; - this.kernelId = kernelId; - this.ramdiskId = ramdiskId; - this.region = region; - this.version = version; - this.availabilityZone = availabilityZone; - this.privateIp = privateIp; - this.devpayProductCodes = devpayProductCodes == null - ? null : devpayProductCodes.clone(); - this.marketplaceProductCodes = marketplaceProductCodes == null - ? null : marketplaceProductCodes.clone(); - } - - public String getPendingTime() { - return pendingTime; - } - - public String getInstanceType() { - return instanceType; - } - - public String getImageId() { - return imageId; - } - - public String getInstanceId() { - return instanceId; - } - - public String[] getBillingProducts() { - return billingProducts == null ? null : billingProducts.clone(); - } - - public String getArchitecture() { - return architecture; - } - - public String getAccountId() { - return accountId; - } - - public String getKernelId() { - return kernelId; - } - - public String getRamdiskId() { - return ramdiskId; - } - - public String getRegion() { - return region; - } - - public String getVersion() { - return version; - } - - public String getAvailabilityZone() { - return availabilityZone; - } - - public String getPrivateIp() { - return privateIp; - } - - public String[] getDevpayProductCodes() { - return devpayProductCodes == null ? null : devpayProductCodes.clone(); - } - - public String[] getMarketplaceProductCodes() { - return marketplaceProductCodes == null ? null : marketplaceProductCodes.clone(); - } - } - /** * All of the metada associated with a network interface on the instance. */ diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java b/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java index d87ed42aa076..d6d06a0ba1a2 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java @@ -15,17 +15,18 @@ package software.amazon.awssdk.regions.util; -import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; +import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.regions.internal.util.ConnectionUtils; import software.amazon.awssdk.utils.IoUtils; @@ -33,6 +34,7 @@ public final class HttpResourcesUtils { private static final Logger log = LoggerFactory.getLogger(HttpResourcesUtils.class); + private static final JsonNodeParser JSON_PARSER = JsonNode.parser(); private static volatile HttpResourcesUtils instance; @@ -154,21 +156,18 @@ private void handleErrorResponse(InputStream errorStream, int statusCode, String String errorResponse = IoUtils.toUtf8String(errorStream); try { - JsonNode node = JacksonUtils.jsonNodeOf(errorResponse); - JsonNode code = node.get("code"); - JsonNode message = node.get("message"); - if (code != null && message != null) { - responseMessage = message.asText(); + Optional message = JSON_PARSER.parse(errorResponse).get("message"); + if (message.isPresent()) { + responseMessage = message.get().text(); } } catch (RuntimeException exception) { log.debug("Unable to parse error stream", exception); } } - SdkServiceException exception = SdkServiceException.builder() - .message(responseMessage) - .statusCode(statusCode) - .build(); - throw exception; + throw SdkServiceException.builder() + .message(responseMessage) + .statusCode(statusCode) + .build(); } } diff --git a/core/regions/src/test/java/software/amazon/awssdk/regions/internal/util/Ec2MetadataUtilsTt0049160280Test.java b/core/regions/src/test/java/software/amazon/awssdk/regions/internal/util/Ec2MetadataUtilsTt0049160280Test.java index f58860822c5f..74be06885ddc 100644 --- a/core/regions/src/test/java/software/amazon/awssdk/regions/internal/util/Ec2MetadataUtilsTt0049160280Test.java +++ b/core/regions/src/test/java/software/amazon/awssdk/regions/internal/util/Ec2MetadataUtilsTt0049160280Test.java @@ -42,37 +42,4 @@ public void getRegionIntern() throws Exception { String region = EC2MetadataUtils.doGetEC2InstanceRegion(JSON); Assert.assertEquals("us-east-1", region); } - - @Test - public void tt0049160280() { - EC2MetadataUtils.InstanceInfo info = EC2MetadataUtils.doGetInstanceInfo(JSON); - String[] billingProducts = info.getBillingProducts(); - Assert.assertTrue(billingProducts.length == 1); - Assert.assertEquals(billingProducts[0], "bp-6ba54002"); - } - - @Test - public void devProductCodes() { - final String JSON = "{" - + " \"privateIp\" : \"172.31.56.174\"," - + " \"devpayProductCodes\" : [\"foo\", \"bar\"]," - + " \"availabilityZone\" : \"us-east-1b\"," - + " \"version\" : \"2010-08-31\"," - + " \"accountId\" : \"123456789012\"," - + " \"instanceId\" : \"i-b32c0064\"," - + " \"billingProducts\" : [\"bp-6ba54002\" ]," - + " \"imageId\" : \"ami-ac3a1cc4\"," - + " \"instanceType\" : \"t2.small\"," - + " \"kernelId\" : null," - + " \"ramdiskId\" : null," - + " \"pendingTime\" : \"2015-04-13T19:57:24Z\"," - + " \"architecture\" : \"x86_64\"," - + " \"region\" : \"us-east-1\"" - + "}"; - EC2MetadataUtils.InstanceInfo info = EC2MetadataUtils.doGetInstanceInfo(JSON); - String[] devpayProductCodes = info.getDevpayProductCodes(); - Assert.assertTrue(devpayProductCodes.length == 2); - Assert.assertEquals(devpayProductCodes[0], "foo"); - Assert.assertEquals(devpayProductCodes[1], "bar"); - } } diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml index e4e1f853c49e..2154efd82b55 100644 --- a/core/sdk-core/pom.xml +++ b/core/sdk-core/pom.xml @@ -56,19 +56,10 @@ profiles ${awsjavasdk.version} - org.slf4j slf4j-api - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - org.reactivestreams reactive-streams diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/UnreliableFilterInputStream.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/UnreliableFilterInputStream.java index c41876bbfae6..2397f615f794 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/UnreliableFilterInputStream.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/UnreliableFilterInputStream.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.io.InputStream; import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.utils.ToString; /** * An internal class used solely for the purpose of testing via failure @@ -168,6 +168,15 @@ public int getResetCount() { @Override public String toString() { - return JacksonUtils.toJsonString(this); + return ToString.builder("UnreliableFilterInputStream") + .add("isFakeIoException", isFakeIoException) + .add("maxNumErrors", maxNumErrors) + .add("currNumErrors", currNumErrors) + .add("bytesReadBeforeException", bytesReadBeforeException) + .add("marked", marked) + .add("position", position) + .add("resetCount", resetCount) + .add("resetIntervalBeforeException", resetIntervalBeforeException) + .toString(); } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/util/json/JacksonUtils.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/util/json/JacksonUtils.java deleted file mode 100644 index c379f52a3682..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/util/json/JacksonUtils.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.core.util.json; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import java.io.IOException; -import java.io.Writer; -import software.amazon.awssdk.annotations.SdkProtectedApi; -import software.amazon.awssdk.core.exception.SdkClientException; - -@SdkProtectedApi -public final class JacksonUtils { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final ObjectWriter WRITER = OBJECT_MAPPER.writer(); - - - private static final ObjectWriter PRETTY_WRITER = OBJECT_MAPPER.writerWithDefaultPrettyPrinter(); - - static { - OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true); - OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - private JacksonUtils() { - } - - public static String toJsonPrettyString(Object value) { - try { - return PRETTY_WRITER.writeValueAsString(value); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - public static String toJsonString(Object value) { - try { - return WRITER.writeValueAsString(value); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - /** - * Returns the deserialized object from the given json string and target - * class; or null if the given json string is null. - */ - public static T fromJsonString(String json, Class clazz) { - if (json == null) { - return null; - } - try { - return OBJECT_MAPPER.readValue(json, clazz); - } catch (Exception e) { - throw SdkClientException.builder().message("Unable to parse Json String.").cause(e).build(); - } - } - - /** - * Returns the deserialized object from the given json string and target - * class; or null if the given json string is null. Clears the JSON location in the event of an error - */ - public static T fromSensitiveJsonString(String json, Class clazz) { - if (json == null) { - return null; - } - try { - return OBJECT_MAPPER.readValue(json, clazz); - } catch (Exception e) { - // If underlying exception is a json parsing issue, clear out the location so that the exception message - // does not contain the raw json - if (e instanceof JsonParseException) { - ((JsonParseException) e).clearLocation(); - } - throw SdkClientException.builder().message("Unable to parse Json String.").cause(e).build(); - } - } - - public static JsonNode jsonNodeOf(String json) { - return fromJsonString(json, JsonNode.class); - } - - public static JsonNode sensitiveJsonNodeOf(String json) { - return fromSensitiveJsonString(json, JsonNode.class); - } - - public static JsonGenerator jsonGeneratorOf(Writer writer) throws IOException { - return new JsonFactory().createGenerator(writer); - } -} diff --git a/pom.xml b/pom.xml index f2a8f1ea407e..cf7407a93827 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,7 @@ codegen-lite codegen-lite-maven-plugin archetypes + third-party test/http-client-tests test/protocol-tests test/protocol-tests-core @@ -90,7 +91,6 @@ 2.12.3 2.12.3 1.0.1 - 1.2.0 3.12.0 2.18.0 1.7.30 @@ -514,6 +514,10 @@ software.amazon.awssdk.awscore.eventstream.* software.amazon.awssdk.protocols.json.BaseAwsJsonProtocolFactory software.amazon.awssdk.services.macie2.model.* + + + software.amazon.awssdk.core.util.json.JacksonUtils + software.amazon.awssdk.protocols.json.* diff --git a/services/sso/pom.xml b/services/sso/pom.xml index a8af9efef42c..6f10cfa6e827 100644 --- a/services/sso/pom.xml +++ b/services/sso/pom.xml @@ -56,6 +56,11 @@ aws-json-protocol ${awsjavasdk.version} + + software.amazon.awssdk + json-utils + ${awsjavasdk.version} + software.amazon.awssdk profiles diff --git a/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java b/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java index 242208af9356..d3fee4481d94 100644 --- a/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java +++ b/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java @@ -17,7 +17,6 @@ import static java.time.temporal.ChronoUnit.MINUTES; -import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -25,10 +24,12 @@ import java.nio.file.Path; import java.time.Instant; import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.util.json.JacksonUtils; +import software.amazon.awssdk.protocols.jsoncore.JsonNode; +import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser; import software.amazon.awssdk.services.sso.auth.ExpiredTokenException; import software.amazon.awssdk.services.sso.auth.SsoCredentialsProvider; import software.amazon.awssdk.utils.IoUtils; +import software.amazon.awssdk.utils.Validate; /** * Resolve the access token from the cached token file. If the token has expired then throw out an exception to ask the users to @@ -37,8 +38,9 @@ */ @SdkInternalApi public final class SsoAccessTokenProvider { + private static final JsonNodeParser PARSER = JsonNodeParser.builder().removeErrorLocations(true).build(); - private Path cachedTokenFilePath; + private final Path cachedTokenFilePath; public SsoAccessTokenProvider(Path cachedTokenFilePath) { this.cachedTokenFilePath = cachedTokenFilePath; @@ -53,18 +55,22 @@ public String resolveAccessToken() { } private String getTokenFromJson(String json) { - JsonNode jsonNode = JacksonUtils.sensitiveJsonNodeOf(json); + JsonNode jsonNode = PARSER.parse(json); + String expiration = jsonNode.get("expiresAt").map(JsonNode::text).orElse(null); - if (validateToken(jsonNode.get("expiresAt").asText())) { + Validate.notNull(expiration, + "The SSO session's expiration time could not be determined. Please refresh your SSO session."); + + if (tokenIsInvalid(expiration)) { throw ExpiredTokenException.builder().message("The SSO session associated with this profile has expired or is" + " otherwise invalid. To refresh this SSO session run aws sso" + " login with the corresponding profile.").build(); } - return jsonNode.get("accessToken").asText(); + return jsonNode.asObject().get("accessToken").text(); } - private boolean validateToken(String expirationTime) { + private boolean tokenIsInvalid(String expirationTime) { return Instant.now().isAfter(Instant.parse(expirationTime).minus(15, MINUTES)); } diff --git a/test/module-path-tests/src/main/java/module-info.java b/test/module-path-tests/src/main/java/module-info.java index 489563a381f5..9b2d7c36c49d 100644 --- a/test/module-path-tests/src/main/java/module-info.java +++ b/test/module-path-tests/src/main/java/module-info.java @@ -30,5 +30,4 @@ requires org.slf4j; requires slf4j.simple; - requires com.fasterxml.jackson.core; } diff --git a/test/protocol-tests-core/pom.xml b/test/protocol-tests-core/pom.xml index df94cee45b41..8684f842ee13 100644 --- a/test/protocol-tests-core/pom.xml +++ b/test/protocol-tests-core/pom.xml @@ -67,11 +67,6 @@ utils ${awsjavasdk.version} - - software.amazon.ion - ion-java - compile - org.apache.httpcomponents httpclient diff --git a/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/IonBodyAssertion.java b/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/IonBodyAssertion.java deleted file mode 100644 index 84fabc6460cf..000000000000 --- a/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/IonBodyAssertion.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.protocol.asserts.marshalling; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import com.github.tomakehurst.wiremock.verification.LoggedRequest; -import java.math.BigInteger; -import java.util.Objects; -import software.amazon.ion.Decimal; -import software.amazon.ion.IonReader; -import software.amazon.ion.IonSystem; -import software.amazon.ion.IonType; -import software.amazon.ion.Timestamp; -import software.amazon.ion.system.IonSystemBuilder; - -public class IonBodyAssertion extends MarshallingAssertion { - private static final double DOUBLE_DELTA = 0.0001d; - private static final IonSystem ION_SYSTEM = IonSystemBuilder.standard().build(); - - private final String ionEquals; - - public IonBodyAssertion(String ionEquals) { - this.ionEquals = ionEquals; - } - - @Override - protected void doAssert(LoggedRequest request) throws Exception { - IonReader expected = ION_SYSTEM.newReader(ionEquals); - IonReader actual = ION_SYSTEM.newReader(request.getBody()); - assertIonReaderEquals(expected, actual); - } - - private void assertIonReaderEquals(IonReader x, IonReader y) { - for (int token = 0; ; token++) { - IonType xType = x.next(); - IonType yType = y.next(); - - if (xType == null && yType == null) { - if (x.getDepth() == 0 && y.getDepth() == 0) { - return; - } else { - x.stepOut(); - y.stepOut(); - continue; - } - } - - if (!Objects.equals(xType, yType)) { - fail(String.format("Types (%s, %s) are unequal at token %s", xType, yType, token)); - } - - if (x.isInStruct() && y.isInStruct()) { - String xFieldName = x.getFieldName(); - String yFieldName = y.getFieldName(); - assertEquals( - String.format("Unequal field names (%s, %s) at token %s", xFieldName, yFieldName, token), - xFieldName, - yFieldName); - } - - boolean xNull = x.isNullValue(); - boolean yNull = y.isNullValue(); - if ((xNull && !yNull) || (yNull && !xNull)) { - fail(String.format("One value is null but the other is not at token %s", token)); - } else if (xNull && yNull) { - continue; - } - - switch (xType) { - case BLOB: - case CLOB: - int sizeX = x.byteSize(); - int sizeY = y.byteSize(); - assertEquals( - String.format("Unequal LOB sizes (%s, %s) at token %s", sizeX, sizeY, token), - sizeX, - sizeY); - - byte[] bufferX = new byte[sizeX]; - byte[] bufferY = new byte[sizeY]; - - x.getBytes(bufferX, 0, sizeX); - y.getBytes(bufferY, 0, sizeY); - - assertArrayEquals( - String.format("Unequal LOBs at token %s", token), - bufferX, - bufferY); - break; - - case BOOL: - boolean xBoolean = x.booleanValue(); - boolean yBoolean = y.booleanValue(); - assertEquals( - String.format("Unequal boolean values (%s, %s) at token %s", xBoolean, yBoolean, token), - xBoolean, - yBoolean); - break; - - case DECIMAL: - Decimal xDecimal = x.decimalValue(); - Decimal yDecimal = y.decimalValue(); - assertEquals( - String.format("Unequal decimal values (%s, %s) at token %s", xDecimal, yDecimal, token), - xDecimal, - yDecimal); - break; - - case FLOAT: - double xDouble = x.doubleValue(); - double yDouble = y.doubleValue(); - assertEquals( - String.format("Unequal float values (%s, %s) at token %s", xDouble, yDouble, token), - xDouble, - yDouble, - DOUBLE_DELTA); - break; - - case INT: - BigInteger xInteger = x.bigIntegerValue(); - BigInteger yInteger = y.bigIntegerValue(); - assertEquals( - String.format("Unequal integer values (%s, %s) at token %s", xInteger, yInteger, token), - xInteger, - yInteger); - break; - - case NULL: - throw new IllegalStateException("We should never fall through to the IonType.NULL block due to previous " + - "assertions for equal types and nullness"); - - case STRING: - case SYMBOL: - String xString = x.stringValue(); - String yString = y.stringValue(); - assertEquals( - String.format("Unequal string values (%s, %s) at token %s", xString, yString, token), - xString, - yString); - break; - - case LIST: - case SEXP: - case STRUCT: - x.stepIn(); - y.stepIn(); - break; - - case TIMESTAMP: - Timestamp xTimestamp = x.timestampValue(); - Timestamp yTimestamp = y.timestampValue(); - assertEquals( - String.format("Unequal timestamp values (%s, %s) at token %s", xTimestamp, yTimestamp, token), - xTimestamp, - yTimestamp); - break; - - default: - fail(String.format("Unrecognized IonType %s", xType)); - } - } - } -} diff --git a/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/RequestBodyAssertion.java b/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/RequestBodyAssertion.java index 701643ba2d7a..d93d93dc2149 100644 --- a/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/RequestBodyAssertion.java +++ b/test/protocol-tests-core/src/main/java/software/amazon/awssdk/protocol/asserts/marshalling/RequestBodyAssertion.java @@ -32,8 +32,4 @@ public void setXmlEquals(String xmlEquals) { public void setEquals(String equals) { addAssertion(new RawBodyAssertion(equals)); } - - public void setIonEquals(String ionEquals) { - addAssertion(new IonBodyAssertion(ionEquals)); - } } diff --git a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-input.ion b/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-input.ion deleted file mode 100644 index a102330cf0ab..000000000000 --- a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-input.ion +++ /dev/null @@ -1,345 +0,0 @@ -[ - { - "description": "Scalar Members are marshalled correctly", - "given": { - "input": { - "StringMember": "someVal", - "IntegerMember": 42, - "FloatMember": 1.234, - "DoubleMember": 5.678, - "LongMember": 2147483647 - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{StringMember:\"someVal\",IntegerMember:42,FloatMember:1.234e0,DoubleMember:5.678e0,LongMember:2147483647 }" - } - } - } - }, - { - "description": "Boolean member with value true is marshalled correctly", - "given": { - "input": { - "BooleanMember": true - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{BooleanMember:true}" - } - } - } - }, - { - "description": "Boolean member with value false is marshalled correctly", - "given": { - "input": { - "BooleanMember": false - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{BooleanMember:false}" - } - } - } - }, - { - "description": "Timestamp member in the payload is marshalled as seconds with millisecond precision", - "given": { - "input": { - "TimestampMember": 1422172801123 - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{TimestampMember: 2015-01-25T08:00:01.123Z}" - } - } - } - }, - { - "description": "Blob member in payload is marshalled as Base64 encoded text", - "given": { - "input": { - "BlobArg": "foo" - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{BlobArg: {{Zm9v}}}" - } - } - } - }, - { - "description": "Nested blob member in payload is marshalled as Base64 encoded text", - "given": { - "input": { - "StructWithNestedBlob": { - "NestedBlob": "foo" - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{StructWithNestedBlob: {NestedBlob: {{Zm9v}}}}" - } - } - } - }, - { - "description": "Blob member as map value is marshalled as Base64 encoded text", - "given": { - "input": { - "BlobMap": { - "key1": "foo", - "key2": "bar" - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{BlobMap: {key1: {{Zm9v}}, key2: {{YmFy}}}}" - } - } - } - }, - { - "description": "Blob as list member is marshalled as Base64 encoded text", - "given": { - "input": { - "ListOfBlobs": [ - "foo", - "bar" - ] - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{ListOfBlobs: [{{Zm9v}}, {{YmFy}}]}" - } - } - } - }, - { - "description": "Recursive structure with recursive member not set is marshalled correctly", - "given": { - "input": { - "RecursiveStruct": { - "NoRecurse": "foo" - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {NoRecurse: \"foo\"}}" - } - } - } - }, - { - "description": "Recursive structure with one level of recursion is marshalled correctly", - "given": { - "input": { - "RecursiveStruct": { - "RecursiveStruct": { - "NoRecurse": "foo" - } - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {RecursiveStruct: {NoRecurse: \"foo\"}}}" - } - } - } - }, - { - "description": "Recursive structure with several levels of recursion is marshalled correctly", - "given": { - "input": { - "RecursiveStruct": { - "RecursiveStruct": { - "RecursiveStruct": { - "RecursiveStruct": { - "NoRecurse": "foo" - } - } - } - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {RecursiveStruct: {RecursiveStruct: {RecursiveStruct: {NoRecurse: \"foo\"}}}}}" - } - } - } - }, - { - "description": "List of recursive structs is marshalled correctly when no recursive members are set", - "given": { - "input": { - "RecursiveStruct": { - "RecursiveList": [ - { - "NoRecurse": "foo" - }, - { - "NoRecurse": "bar" - } - ] - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {RecursiveList: [{NoRecurse: \"foo\"}, {NoRecurse: \"bar\"}]}}" - } - } - } - }, - { - "description": "List of recursive structs with one level of recursion is marshalled correctly", - "given": { - "input": { - "RecursiveStruct": { - "RecursiveList": [ - { - "NoRecurse": "foo" - }, - { - "RecursiveStruct": { - "NoRecurse": "bar" - } - } - ] - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {RecursiveList: [{NoRecurse: \"foo\"}, {RecursiveStruct: {NoRecurse: \"bar\"}}]}}" - } - } - } - }, - { - "description": "Recursive structure as map value is marshalled correctly", - "given": { - "input": { - "RecursiveStruct": { - "RecursiveMap": { - "foo": { - "NoRecurse": "foo" - }, - "bar": { - "NoRecurse": "bar", - "RecursiveStruct": { - "NoRecurse": "baz" - } - } - } - } - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{RecursiveStruct: {RecursiveMap: {bar: {NoRecurse: \"bar\", RecursiveStruct: { NoRecurse: \"baz\"}}, foo: {NoRecurse: \"foo\"}}}}" - } - } - } - }, - { - "description": "Empty maps are marshalled correctly", - "given": { - "input": { - "MapOfStringToString": {} - } - }, - "when": { - "action": "marshall", - "operation": "AllTypes" - }, - "then": { - "serializedAs": { - "body": { - "ionEquals": "{MapOfStringToString: {}}" - } - } - } - } -] diff --git a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-output.ion b/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-output.ion deleted file mode 100644 index 99e94438ff34..000000000000 --- a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/cases/ion-output.ion +++ /dev/null @@ -1,309 +0,0 @@ -[ - { - "description": "Scalar Members in payload are unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{StringMember: \"myname\", IntegerMember: 123, FloatMember: 1.2e0, DoubleMember: 1.3e0, LongMember: 200}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "StringMember": "myname", - "IntegerMember": 123, - "FloatMember": 1.2, - "DoubleMember": 1.3, - "LongMember": 200 - } - } - }, - { - "description": "Boolean member with value true in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{BooleanMember: true}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "BooleanMember": true - } - } - }, - { - "description": "Boolean member with value false in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{BooleanMember: false}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "BooleanMember": false - } - } - }, - { - "description": "Base64 encoded blob member is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{BlobArg: {{aGkh}}}}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "BlobArg": "hi!" - } - } - }, - { - "description": "Nested Base64 encoded blob member is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{StructWithNestedBlob: {NestedBlob: {{aGkh}}}}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "StructWithNestedBlob": { - "NestedBlob": "hi!" - } - } - } - }, - { - "description": "Base64 encoded blob as list member is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{ListOfBlobs: [{{Zm9v}}, {{YmFy}}]}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "ListOfBlobs": [ - "foo", - "bar" - ] - } - } - }, - { - "description": "Base64 encoded blob as map value is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{BlobMap: {foo: {{dGhlcmUh}}}}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "BlobMap": { - "foo": "there!" - } - } - } - }, - { - "description": "Timestamp member in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{TimestampMember: 2014-04-29T18:30:38.123Z}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "TimestampMember": 1398796238123 - } - } - }, - { - "description": "Nested Timestamp member in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{StructWithNestedTimestampMember: {NestedTimestamp: 2014-04-29T18:30:38.123Z}}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "StructWithNestedTimestampMember": { - "NestedTimestamp": 1398796238123 - } - } - } - }, - { - "description": "List of strings in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{SimpleList: [\"a\", \"b\", null]}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "SimpleList": [ - "a", - "b", - null - ] - } - } - }, - { - "description": "List of structs in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{ListOfStructs: [{StringMember: \"foo\"}, null, {}]}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "ListOfStructs": [ - { - "StringMember": "foo" - }, - null, - {} - ] - } - } - }, - { - "description": "List of maps in payload is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{ListOfMaps: [{keyOne: \"valOne\", keyTwo: \"valTwo\"}, null, {}]}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "ListOfMaps": [ - { - "keyOne": "valOne", - "keyTwo": "valTwo" - }, - null, - {} - ] - } - } - }, - { - "description": "Map of String to list of integers is unmarshalled correctly", - "given": { - "response": { - "status_code": 200, - "body": "{MapOfStringToIntegerList: {a: [1, 2], b: [3, 4]}}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - "MapOfStringToIntegerList": { - "a": [ - 1, - 2 - ], - "b": [ - 3, - 4 - ] - } - } - } - }, - { - "description": "Unmodeled data is ignored", - "given": { - "response": { - "status_code": 200, - "body": "{foo: \"bar\"}" - } - }, - "when": { - "action": "unmarshall", - "operation": "AllTypes" - }, - "then": { - "deserializedAs": { - } - } - }, - { - "description": "Operation with no output defined ignores any content in body", - "given": { - "response": { - "status_code": 200, - "body": "THIS ISN'T ION!!!" - } - }, - "when": { - "action": "unmarshall", - "operation": "OperationWithNoInputOrOutput" - }, - "then": { - "deserializedAs": { - } - } - } -] diff --git a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/ion-suite.json b/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/ion-suite.json deleted file mode 100644 index ba57b4451afc..000000000000 --- a/test/protocol-tests-core/src/main/resources/software/amazon/awssdk/protocol/suites/ion-suite.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "testCases": [ - "cases/ion-input.ion", - "cases/ion-output.ion" - ] -} diff --git a/test/tests-coverage-reporting/pom.xml b/test/tests-coverage-reporting/pom.xml index 2e6fe99795c9..47dd0f705dce 100644 --- a/test/tests-coverage-reporting/pom.xml +++ b/test/tests-coverage-reporting/pom.xml @@ -42,6 +42,11 @@ software.amazon.awssdk ${awsjavasdk.version} + + json-utils + software.amazon.awssdk + ${awsjavasdk.version} + aws-core software.amazon.awssdk @@ -72,11 +77,6 @@ software.amazon.awssdk ${awsjavasdk.version} - - aws-ion-protocol - software.amazon.awssdk - ${awsjavasdk.version} - aws-json-protocol software.amazon.awssdk diff --git a/third-party/jackson-core/pom.xml b/third-party/jackson-core/pom.xml new file mode 100644 index 000000000000..2eea37c3987c --- /dev/null +++ b/third-party/jackson-core/pom.xml @@ -0,0 +1,126 @@ + + + + + + third-party + software.amazon.awssdk + 2.16.83-SNAPSHOT + + 4.0.0 + + jackson-core + AWS Java SDK :: Third Party :: Jackson-core + https://aws.amazon.com/sdkforjava + + + + + software.amazon.awssdk + bom-internal + ${project.version} + pom + import + + + + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + true + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + + + com.fasterxml.jackson.core:* + + + + + com.fasterxml.jackson.core + software.amazon.awssdk.thirdparty.jackson.core + + + + + + false + true + ${project.build.directory}/dependency-reduced-pom.xml + + + + package + + shade + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + compile + package + + attach-artifact + + + + + ${basedir}/target/aws-sdk-java-jackson-core-${awsjavasdk.version}.jar + jar + optional + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.thirdparty.jackson.core + + + + + + + diff --git a/third-party/jackson-dataformat-cbor/pom.xml b/third-party/jackson-dataformat-cbor/pom.xml new file mode 100644 index 000000000000..d43f5917ed3f --- /dev/null +++ b/third-party/jackson-dataformat-cbor/pom.xml @@ -0,0 +1,145 @@ + + + + + + third-party + software.amazon.awssdk + 2.16.83-SNAPSHOT + + 4.0.0 + + jackson-dataformat-cbor + AWS Java SDK :: Third Party :: Jackson-dataformat-cbor + https://aws.amazon.com/sdkforjava + + + + + software.amazon.awssdk + bom-internal + ${project.version} + pom + import + + + + + + + software.amazon.awssdk + jackson-core + ${awsjavasdk.version} + + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + ${jackson.version} + true + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + + + com.fasterxml.jackson.dataformat:* + + + + + com.fasterxml.jackson.dataformat.cbor + software.amazon.awssdk.thirdparty.jackson.dataformat.cbor + + + com.fasterxml.jackson.core + software.amazon.awssdk.thirdparty.jackson.core + + + + + com.fasterxml.jackson.dataformat:* + + + com/fasterxml/jackson/dataformat/cbor/databind/** + + + + + + + false + true + ${project.build.directory}/dependency-reduced-pom.xml + + + + package + + shade + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + compile + package + + attach-artifact + + + + + ${basedir}/target/aws-sdk-java-jackson-dataformat-cbor-${project.version}.jar + jar + optional + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.thirdparty.jackson.dataformat.cbor + + + + + + + diff --git a/third-party/pom.xml b/third-party/pom.xml new file mode 100644 index 000000000000..c198920202d0 --- /dev/null +++ b/third-party/pom.xml @@ -0,0 +1,66 @@ + + + + + 4.0.0 + + aws-sdk-java-pom + software.amazon.awssdk + 2.16.83-SNAPSHOT + + + third-party + AWS Java SDK :: Third Party + pom + + The AWS SDK for Java - Third Party is an umbrella module that contains child modules which are shaded third- + party dependencies. + + + + jackson-core + jackson-dataformat-cbor + + + + + + software.amazon.awssdk + bom-internal + ${project.version} + pom + import + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + true + + + + + + From de225e6b2a273064a31b83a00af051080f2ad628 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 11 Jun 2021 11:27:14 -0700 Subject: [PATCH 2/3] Add 'third-party' in front of the shaded module names. --- bom/pom.xml | 4 ++-- core/json-utils/pom.xml | 2 +- core/protocols/aws-cbor-protocol/pom.xml | 4 ++-- core/protocols/aws-json-protocol/pom.xml | 2 +- third-party/pom.xml | 4 ++-- .../{jackson-core => third-party-jackson-core}/pom.xml | 4 ++-- .../pom.xml | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) rename third-party/{jackson-core => third-party-jackson-core}/pom.xml (97%) rename third-party/{jackson-dataformat-cbor => third-party-jackson-dataformat-cbor}/pom.xml (96%) diff --git a/bom/pom.xml b/bom/pom.xml index 996a1fbe67bf..5fefd37d03f4 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -69,12 +69,12 @@ software.amazon.awssdk - jackson-core + third-party-jackson-core ${awsjavasdk.version} software.amazon.awssdk - jackson-dataformat-cbor + third-party-jackson-dataformat-cbor ${awsjavasdk.version} diff --git a/core/json-utils/pom.xml b/core/json-utils/pom.xml index 755c410bb6f1..01652c42bc95 100644 --- a/core/json-utils/pom.xml +++ b/core/json-utils/pom.xml @@ -53,7 +53,7 @@ software.amazon.awssdk - jackson-core + third-party-jackson-core ${awsjavasdk.version} diff --git a/core/protocols/aws-cbor-protocol/pom.xml b/core/protocols/aws-cbor-protocol/pom.xml index c40ea79430b3..a4e4f6a52efc 100644 --- a/core/protocols/aws-cbor-protocol/pom.xml +++ b/core/protocols/aws-cbor-protocol/pom.xml @@ -48,12 +48,12 @@ software.amazon.awssdk - jackson-dataformat-cbor + third-party-jackson-dataformat-cbor ${awsjavasdk.version} software.amazon.awssdk - jackson-core + third-party-jackson-core ${awsjavasdk.version} diff --git a/core/protocols/aws-json-protocol/pom.xml b/core/protocols/aws-json-protocol/pom.xml index 38550bcb6d5c..63ba2752b540 100644 --- a/core/protocols/aws-json-protocol/pom.xml +++ b/core/protocols/aws-json-protocol/pom.xml @@ -63,7 +63,7 @@ software.amazon.awssdk - jackson-core + third-party-jackson-core ${awsjavasdk.version} diff --git a/third-party/pom.xml b/third-party/pom.xml index c198920202d0..4e1b3d949806 100644 --- a/third-party/pom.xml +++ b/third-party/pom.xml @@ -33,8 +33,8 @@ - jackson-core - jackson-dataformat-cbor + third-party-jackson-core + third-party-jackson-dataformat-cbor diff --git a/third-party/jackson-core/pom.xml b/third-party/third-party-jackson-core/pom.xml similarity index 97% rename from third-party/jackson-core/pom.xml rename to third-party/third-party-jackson-core/pom.xml index 2eea37c3987c..250665691c6c 100644 --- a/third-party/jackson-core/pom.xml +++ b/third-party/third-party-jackson-core/pom.xml @@ -24,7 +24,7 @@ 4.0.0 - jackson-core + third-party-jackson-core AWS Java SDK :: Third Party :: Jackson-core https://aws.amazon.com/sdkforjava @@ -101,7 +101,7 @@ - ${basedir}/target/aws-sdk-java-jackson-core-${awsjavasdk.version}.jar + ${basedir}/target/aws-sdk-java-third-party-jackson-core-${awsjavasdk.version}.jar jar optional diff --git a/third-party/jackson-dataformat-cbor/pom.xml b/third-party/third-party-jackson-dataformat-cbor/pom.xml similarity index 96% rename from third-party/jackson-dataformat-cbor/pom.xml rename to third-party/third-party-jackson-dataformat-cbor/pom.xml index d43f5917ed3f..53c7c7991b08 100644 --- a/third-party/jackson-dataformat-cbor/pom.xml +++ b/third-party/third-party-jackson-dataformat-cbor/pom.xml @@ -24,7 +24,7 @@ 4.0.0 - jackson-dataformat-cbor + third-party-jackson-dataformat-cbor AWS Java SDK :: Third Party :: Jackson-dataformat-cbor https://aws.amazon.com/sdkforjava @@ -43,7 +43,7 @@ software.amazon.awssdk - jackson-core + third-party-jackson-core ${awsjavasdk.version} @@ -120,7 +120,7 @@ - ${basedir}/target/aws-sdk-java-jackson-dataformat-cbor-${project.version}.jar + ${basedir}/target/aws-sdk-java-third-party-jackson-dataformat-cbor-${project.version}.jar jar optional From e4893621ec9e0ab7f59268c8acc67d4827ed412d Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Tue, 15 Jun 2021 16:33:45 -0700 Subject: [PATCH 3/3] Addressed comments. --- .../feature-AWSSDKforJavav2-9e94920.json | 2 +- .../ProcessCredentialsProvider.java | 4 +-- .../awssdk/protocols/jsoncore/JsonNode.java | 4 +-- .../jsoncore/internal/ArrayJsonNode.java | 21 ++++++++++++++- .../jsoncore/internal/BooleanJsonNode.java | 19 ++++++++++++++ .../internal/EmbeddedObjectJsonNode.java | 20 +++++++++++++- .../jsoncore/internal/NullJsonNode.java | 10 +++++++ .../jsoncore/internal/NumberJsonNode.java | 19 ++++++++++++++ .../jsoncore/internal/ObjectJsonNode.java | 21 ++++++++++++++- .../jsoncore/internal/StringJsonNode.java | 19 ++++++++++++++ .../protocols/jsoncore/JsonNodeTest.java | 26 +++++++++---------- .../unmarshall/JsonErrorCodeParser.java | 2 +- .../unmarshall/JsonProtocolUnmarshaller.java | 2 +- .../unmarshall/SdkJsonErrorMessageParser.java | 2 +- .../internal/util/EC2MetadataUtils.java | 2 +- .../regions/util/HttpResourcesUtils.java | 2 +- .../sso/internal/SsoAccessTokenProvider.java | 2 +- 17 files changed, 150 insertions(+), 27 deletions(-) diff --git a/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json b/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json index 3d652842dad7..c86476fa5f83 100644 --- a/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json +++ b/.changes/next-release/feature-AWSSDKforJavav2-9e94920.json @@ -2,5 +2,5 @@ "category": "AWS SDK for Java v2", "contributor": "", "type": "feature", - "description": "Moved Jackson from being an external SDK dependency to an internal dependency.\n\nCustomers will see a change in artifact sizes. Customers which do not use Jackson-databind outside of the SDK today will see a ~1 MB artifact size decrease. Customers which do use Jackson-databind outside of the SDK will see a ~0.5 MB artifact size increase.\n\nThis change required breaking inter-module (protected) APIs, so customers will not be able to use older client versions with this version of the core libraries." + "description": "Moved Jackson from being an external SDK dependency to an internal dependency.\n\nCustomers will see a change in artifact sizes. Customers which do not use Jackson-databind outside of the SDK today will see a ~1 MB artifact size decrease. Customers which do use Jackson-databind outside of the SDK will see a ~0.5 MB artifact size increase.\n\nThis change required breaking inter-module (protected) APIs, so customers will not be able to use older client versions with this version of the core libraries. Customers that have previously added Jackson classes to their module path may need to remove them if Jackson is no longer a runtime dependency of their application." } diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java index 4a4cd1eb3cb6..e45f556dc7a9 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java @@ -135,7 +135,7 @@ private JsonNode parseProcessOutput(String processOutput) { throw new IllegalStateException("Process did not return a JSON object."); } - JsonNode version = credentialsJson.get("Version").orElse(null); + JsonNode version = credentialsJson.field("Version").orElse(null); if (version == null || !version.isNumber() || !version.asNumber().equals("1")) { throw new IllegalStateException("Unsupported credential version: " + version); } @@ -177,7 +177,7 @@ private Instant credentialExpirationTime(JsonNode credentialsJson) { * Get a textual value from a json object. */ private String getText(JsonNode jsonObject, String nodeName) { - return jsonObject.get(nodeName).map(JsonNode::text).orElse(null); + return jsonObject.field(nodeName).map(JsonNode::text).orElse(null); } /** diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java index 2ec4085ba679..4590da9d42a9 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/JsonNode.java @@ -171,7 +171,7 @@ default boolean isEmbeddedObject() { * When {@link #isObject()} is true, this will return the result of {@code Optional.ofNullable(asObject().get(child))}. If * this is any other type, this will return {@link Optional#empty()}. */ - default Optional get(String child) { + default Optional field(String child) { return Optional.empty(); } @@ -179,7 +179,7 @@ default Optional get(String child) { * When {@link #isArray()} is true, this will return the result of {@code asArray().get(child)} if child is within bounds. If * this is any other type or the child is out of bounds, this will return {@link Optional#empty()}. */ - default Optional get(int child) { + default Optional index(int child) { return Optional.empty(); } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java index 28e1dee3f288..e8822c928c14 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ArrayJsonNode.java @@ -73,7 +73,7 @@ public String text() { } @Override - public Optional get(int child) { + public Optional index(int child) { if (child < 0 || child >= value.size()) { return Optional.empty(); } @@ -84,4 +84,23 @@ public Optional get(int child) { public String toString() { return value.toString(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ArrayJsonNode that = (ArrayJsonNode) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java index 8b2665756cac..6b71fcf317d2 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/BooleanJsonNode.java @@ -75,4 +75,23 @@ public String text() { public String toString() { return Boolean.toString(value); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BooleanJsonNode that = (BooleanJsonNode) o; + + return value == that.value; + } + + @Override + public int hashCode() { + return (value ? 1 : 0); + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java index 72f4b67f61a5..64d4cfbb10b4 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/EmbeddedObjectJsonNode.java @@ -29,7 +29,6 @@ public final class EmbeddedObjectJsonNode implements JsonNode { private final Object embeddedObject; public EmbeddedObjectJsonNode(Object embeddedObject) { - Validate.paramNotNull(embeddedObject, "embeddedObject"); this.embeddedObject = embeddedObject; } @@ -77,4 +76,23 @@ public String text() { public String toString() { return "<>"; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + EmbeddedObjectJsonNode that = (EmbeddedObjectJsonNode) o; + + return embeddedObject.equals(that.embeddedObject); + } + + @Override + public int hashCode() { + return embeddedObject.hashCode(); + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java index 814f81427471..74890ec13df8 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NullJsonNode.java @@ -78,4 +78,14 @@ public String text() { public String toString() { return "null"; } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return 0; + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java index 5a6172eef4c9..4c0489cf7f70 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/NumberJsonNode.java @@ -75,4 +75,23 @@ public String text() { public String toString() { return value; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + NumberJsonNode that = (NumberJsonNode) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java index 103ddb1824da..9da114a4e573 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/ObjectJsonNode.java @@ -73,7 +73,7 @@ public String text() { } @Override - public Optional get(String child) { + public Optional field(String child) { return Optional.ofNullable(value.get(child)); } @@ -90,4 +90,23 @@ public String toString() { output.setCharAt(output.length() - 1, '}'); return output.toString(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ObjectJsonNode that = (ObjectJsonNode) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } diff --git a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java index 2b9221794fb1..752b1d870c87 100644 --- a/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java +++ b/core/json-utils/src/main/java/software/amazon/awssdk/protocols/jsoncore/internal/StringJsonNode.java @@ -81,4 +81,23 @@ public String toString() { .replace("\"", "\\\"") + "\""; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + StringJsonNode that = (StringJsonNode) o; + + return value.equals(that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } } \ No newline at end of file diff --git a/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java b/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java index 9d2c03cf71c1..756c891a9225 100644 --- a/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java +++ b/core/json-utils/src/test/java/software/amazon/awssdk/protocols/jsoncore/JsonNodeTest.java @@ -215,23 +215,23 @@ public void text_returnsContent() { @Test public void getString_returnsContent() { - assertThat(PARSER.parse("null").get("")).isEmpty(); - assertThat(PARSER.parse("0").get("")).isEmpty(); - assertThat(PARSER.parse("\"foo\"").get("")).isEmpty(); - assertThat(PARSER.parse("true").get("")).isEmpty(); - assertThat(PARSER.parse("[]").get("")).isEmpty(); - assertThat(PARSER.parse("{\"\":0}").get("")).map(JsonNode::asNumber).hasValue("0"); + assertThat(PARSER.parse("null").field("")).isEmpty(); + assertThat(PARSER.parse("0").field("")).isEmpty(); + assertThat(PARSER.parse("\"foo\"").field("")).isEmpty(); + assertThat(PARSER.parse("true").field("")).isEmpty(); + assertThat(PARSER.parse("[]").field("")).isEmpty(); + assertThat(PARSER.parse("{\"\":0}").field("")).map(JsonNode::asNumber).hasValue("0"); } @Test public void getArray_returnsContent() { - assertThat(PARSER.parse("null").get(0)).isEmpty(); - assertThat(PARSER.parse("0").get(0)).isEmpty(); - assertThat(PARSER.parse("\"foo\"").get(0)).isEmpty(); - assertThat(PARSER.parse("true").get(0)).isEmpty(); - assertThat(PARSER.parse("[]").get(0)).isEmpty(); - assertThat(PARSER.parse("[null]").get(0)).map(JsonNode::isNull).hasValue(true); - assertThat(PARSER.parse("{}").get("")).isEmpty(); + assertThat(PARSER.parse("null").index(0)).isEmpty(); + assertThat(PARSER.parse("0").index(0)).isEmpty(); + assertThat(PARSER.parse("\"foo\"").index(0)).isEmpty(); + assertThat(PARSER.parse("true").index(0)).isEmpty(); + assertThat(PARSER.parse("[]").index(0)).isEmpty(); + assertThat(PARSER.parse("[null]").index(0)).map(JsonNode::isNull).hasValue(true); + assertThat(PARSER.parse("{}").field("")).isEmpty(); } @Test diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java index a462cf09c149..8bdffecadfaf 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java @@ -119,7 +119,7 @@ private String parseErrorCodeFromContents(JsonNode jsonContents) { if (jsonContents == null) { return null; } - JsonNode errorCodeField = jsonContents.get(errorCodeFieldName).orElse(null); + JsonNode errorCodeField = jsonContents.field(errorCodeFieldName).orElse(null); if (errorCodeField == null) { return null; } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java index 12d4c4662ebc..b6466efa694f 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java @@ -221,7 +221,7 @@ private static JsonNode getJsonNode(JsonNode jsonContent, SdkField field) { if (jsonContent == null) { return null; } - return isExplicitPayloadMember(field) ? jsonContent : jsonContent.get(field.locationName()) + return isExplicitPayloadMember(field) ? jsonContent : jsonContent.field(field.locationName()) .orElse(null); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java index 295c23ce0caf..9d10f40bea2a 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java @@ -52,7 +52,7 @@ private SdkJsonErrorMessageParser(List errorMessageJsonLocations) { @Override public String parseErrorMessage(SdkHttpFullResponse httpResponse, JsonNode jsonNode) { for (String field : errorMessageJsonLocations) { - String value = jsonNode.get(field).map(JsonNode::text).orElse(null); + String value = jsonNode.field(field).map(JsonNode::text).orElse(null); if (value != null) { return value; } diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java b/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java index 9edef845f0a0..3e19332e3107 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/internal/util/EC2MetadataUtils.java @@ -230,7 +230,7 @@ static String doGetEC2InstanceRegion(final String json) { if (null != json) { try { return JSON_PARSER.parse(json) - .get(REGION) + .field(REGION) .map(JsonNode::text) .orElseThrow(() -> new IllegalStateException("Region not included in metadata.")); } catch (Exception e) { diff --git a/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java b/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java index d6d06a0ba1a2..f3abfcc92506 100644 --- a/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java +++ b/core/regions/src/main/java/software/amazon/awssdk/regions/util/HttpResourcesUtils.java @@ -156,7 +156,7 @@ private void handleErrorResponse(InputStream errorStream, int statusCode, String String errorResponse = IoUtils.toUtf8String(errorStream); try { - Optional message = JSON_PARSER.parse(errorResponse).get("message"); + Optional message = JSON_PARSER.parse(errorResponse).field("message"); if (message.isPresent()) { responseMessage = message.get().text(); } diff --git a/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java b/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java index d3fee4481d94..d94065db2522 100644 --- a/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java +++ b/services/sso/src/main/java/software/amazon/awssdk/services/sso/internal/SsoAccessTokenProvider.java @@ -56,7 +56,7 @@ public String resolveAccessToken() { private String getTokenFromJson(String json) { JsonNode jsonNode = PARSER.parse(json); - String expiration = jsonNode.get("expiresAt").map(JsonNode::text).orElse(null); + String expiration = jsonNode.field("expiresAt").map(JsonNode::text).orElse(null); Validate.notNull(expiration, "The SSO session's expiration time could not be determined. Please refresh your SSO session.");