1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.codegen.poet.transform; 17 18 import com.squareup.javapoet.ClassName; 19 import com.squareup.javapoet.MethodSpec; 20 import com.squareup.javapoet.ParameterizedTypeName; 21 import com.squareup.javapoet.TypeName; 22 import com.squareup.javapoet.TypeSpec; 23 import java.util.ArrayList; 24 import java.util.List; 25 import javax.lang.model.element.Modifier; 26 import software.amazon.awssdk.annotations.SdkInternalApi; 27 import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; 28 import software.amazon.awssdk.codegen.model.intermediate.ShapeModel; 29 import software.amazon.awssdk.codegen.poet.ClassSpec; 30 import software.amazon.awssdk.codegen.poet.PoetExtension; 31 import software.amazon.awssdk.codegen.poet.PoetUtils; 32 import software.amazon.awssdk.codegen.poet.transform.protocols.EventStreamJsonMarshallerSpec; 33 import software.amazon.awssdk.codegen.poet.transform.protocols.JsonMarshallerSpec; 34 import software.amazon.awssdk.codegen.poet.transform.protocols.MarshallerProtocolSpec; 35 import software.amazon.awssdk.codegen.poet.transform.protocols.QueryMarshallerSpec; 36 import software.amazon.awssdk.codegen.poet.transform.protocols.XmlMarshallerSpec; 37 import software.amazon.awssdk.core.exception.SdkClientException; 38 import software.amazon.awssdk.core.runtime.transform.Marshaller; 39 import software.amazon.awssdk.http.SdkHttpFullRequest; 40 import software.amazon.awssdk.utils.Validate; 41 42 public class MarshallerSpec implements ClassSpec { 43 44 private final IntermediateModel intermediateModel; 45 private final ShapeModel shapeModel; 46 private final ClassName baseMashallerName; 47 private final TypeName httpRequestName; 48 private final ClassName requestName; 49 private final ClassName className; 50 private final ClassName requestClassName; 51 private final MarshallerProtocolSpec protocolSpec; 52 MarshallerSpec(IntermediateModel intermediateModel, ShapeModel shapeModel)53 public MarshallerSpec(IntermediateModel intermediateModel, ShapeModel shapeModel) { 54 this.intermediateModel = intermediateModel; 55 this.shapeModel = shapeModel; 56 String modelPackage = intermediateModel.getMetadata().getFullModelPackageName(); 57 this.baseMashallerName = ClassName.get(Marshaller.class); 58 this.httpRequestName = ClassName.get(SdkHttpFullRequest.class); 59 this.requestName = ClassName.get(modelPackage, shapeModel.getShapeName()); 60 this.className = new PoetExtension(intermediateModel).getRequestTransformClass(shapeModel.getShapeName() + "Marshaller"); 61 this.requestClassName = ClassName.get(modelPackage, shapeModel.getShapeName()); 62 this.protocolSpec = getProtocolSpecs(intermediateModel.getMetadata().getProtocol()); 63 } 64 65 @Override poetSpec()66 public TypeSpec poetSpec() { 67 return TypeSpec.classBuilder(className) 68 .addJavadoc("{@link $T} Marshaller", requestClassName) 69 .addModifiers(Modifier.PUBLIC) 70 .addAnnotation(PoetUtils.generatedAnnotation()) 71 .addAnnotation(SdkInternalApi.class) 72 .addSuperinterface(ParameterizedTypeName.get(baseMashallerName, requestName)) 73 .addFields(protocolSpec.memberVariables()) 74 .addFields(protocolSpec.additionalFields()) 75 .addMethods(methods()) 76 .build(); 77 } 78 79 80 @Override className()81 public ClassName className() { 82 return className; 83 } 84 methods()85 private List<MethodSpec> methods() { 86 List<MethodSpec> methodSpecs = new ArrayList<>(); 87 88 protocolSpec.constructor().ifPresent(methodSpecs::add); 89 methodSpecs.add(marshallMethod()); 90 methodSpecs.addAll(protocolSpec.additionalMethods()); 91 return methodSpecs; 92 } 93 marshallMethod()94 private MethodSpec marshallMethod() { 95 String variableName = shapeModel.getVariable().getVariableName(); 96 MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("marshall") 97 .addAnnotation(Override.class) 98 .addModifiers(Modifier.PUBLIC) 99 .addParameter(requestClassName, variableName) 100 .returns(httpRequestName); 101 102 methodSpecBuilder.addStatement("$T.paramNotNull($L, $S)", ClassName.get(Validate.class), variableName, variableName); 103 methodSpecBuilder.beginControlFlow("try"); 104 105 methodSpecBuilder.addCode(protocolSpec.marshalCodeBlock(requestClassName)); 106 107 methodSpecBuilder.endControlFlow(); 108 methodSpecBuilder.beginControlFlow("catch (Exception e)"); 109 methodSpecBuilder.addStatement("throw $T.builder().message(\"Unable to marshall request to JSON: \" + " + 110 "e.getMessage()).cause(e).build()", ClassName 111 .get(SdkClientException.class)); 112 methodSpecBuilder.endControlFlow(); 113 return methodSpecBuilder.build(); 114 } 115 getProtocolSpecs(software.amazon.awssdk.codegen.model.intermediate.Protocol protocol)116 private MarshallerProtocolSpec getProtocolSpecs(software.amazon.awssdk.codegen.model.intermediate.Protocol protocol) { 117 switch (protocol) { 118 case REST_JSON: 119 case CBOR: 120 case AWS_JSON: 121 return getJsonMarshallerSpec(); 122 123 case QUERY: 124 case EC2: 125 return new QueryMarshallerSpec(intermediateModel, shapeModel); 126 127 case REST_XML: 128 return new XmlMarshallerSpec(intermediateModel, shapeModel); 129 130 default: 131 throw new RuntimeException("Unknown protocol: " + protocol.name()); 132 } 133 } 134 getJsonMarshallerSpec()135 private MarshallerProtocolSpec getJsonMarshallerSpec() { 136 if (shapeModel.isEvent()) { 137 return new EventStreamJsonMarshallerSpec(intermediateModel, shapeModel); 138 } 139 return new JsonMarshallerSpec(shapeModel); 140 } 141 } 142