• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.protocols.xml;
17 
18 import static java.util.Collections.unmodifiableList;
19 
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Optional;
23 import java.util.function.Function;
24 import java.util.function.Supplier;
25 import software.amazon.awssdk.annotations.SdkProtectedApi;
26 import software.amazon.awssdk.awscore.AwsResponse;
27 import software.amazon.awssdk.awscore.exception.AwsServiceException;
28 import software.amazon.awssdk.core.Response;
29 import software.amazon.awssdk.core.SdkPojo;
30 import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
31 import software.amazon.awssdk.core.client.config.SdkClientOption;
32 import software.amazon.awssdk.core.http.HttpResponseHandler;
33 import software.amazon.awssdk.core.http.MetricCollectingHttpResponseHandler;
34 import software.amazon.awssdk.core.internal.http.CombinedResponseHandler;
35 import software.amazon.awssdk.core.metrics.CoreMetric;
36 import software.amazon.awssdk.http.SdkHttpFullRequest;
37 import software.amazon.awssdk.http.SdkHttpFullResponse;
38 import software.amazon.awssdk.protocols.core.ExceptionMetadata;
39 import software.amazon.awssdk.protocols.core.OperationInfo;
40 import software.amazon.awssdk.protocols.core.OperationMetadataAttribute;
41 import software.amazon.awssdk.protocols.core.ProtocolMarshaller;
42 import software.amazon.awssdk.protocols.query.unmarshall.AwsXmlErrorProtocolUnmarshaller;
43 import software.amazon.awssdk.protocols.query.unmarshall.XmlElement;
44 import software.amazon.awssdk.protocols.xml.internal.marshall.XmlGenerator;
45 import software.amazon.awssdk.protocols.xml.internal.marshall.XmlProtocolMarshaller;
46 import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlErrorTransformer;
47 import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlResponseHandler;
48 import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlResponseTransformer;
49 import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlUnmarshallingContext;
50 import software.amazon.awssdk.protocols.xml.internal.unmarshall.XmlProtocolUnmarshaller;
51 import software.amazon.awssdk.protocols.xml.internal.unmarshall.XmlResponseHandler;
52 
53 /**
54  * Factory to generate the various protocol handlers and generators to be used for
55  * communicating with REST/XML services.
56  */
57 @SdkProtectedApi
58 public class AwsXmlProtocolFactory {
59 
60     /**
61      * Attribute for configuring the XML namespace to include in the xmlns attribute of the root element.
62      */
63     public static final OperationMetadataAttribute<String> XML_NAMESPACE_ATTRIBUTE =
64         new OperationMetadataAttribute<>(String.class);
65 
66     /**
67      * Some services like Route53 specifies the location for the request shape. This should be the root of the
68      * generated xml document.
69      *
70      * Other services Cloudfront, s3 don't specify location param for the request shape. For them, this value will be null.
71      */
72     public static final OperationMetadataAttribute<String> ROOT_MARSHALL_LOCATION_ATTRIBUTE =
73         new OperationMetadataAttribute<>(String.class);
74 
75     private static final XmlProtocolUnmarshaller XML_PROTOCOL_UNMARSHALLER = XmlProtocolUnmarshaller.create();
76 
77     private final List<ExceptionMetadata> modeledExceptions;
78     private final Supplier<SdkPojo> defaultServiceExceptionSupplier;
79     private final HttpResponseHandler<AwsServiceException> errorUnmarshaller;
80     private final SdkClientConfiguration clientConfiguration;
81 
AwsXmlProtocolFactory(Builder<?> builder)82     AwsXmlProtocolFactory(Builder<?> builder) {
83         this.modeledExceptions = unmodifiableList(builder.modeledExceptions);
84         this.defaultServiceExceptionSupplier = builder.defaultServiceExceptionSupplier;
85         this.clientConfiguration = builder.clientConfiguration;
86 
87         this.errorUnmarshaller = timeUnmarshalling(
88             AwsXmlErrorProtocolUnmarshaller.builder()
89                                            .defaultExceptionSupplier(defaultServiceExceptionSupplier)
90                                            .exceptions(modeledExceptions)
91                                            .errorUnmarshaller(XML_PROTOCOL_UNMARSHALLER)
92                                            .errorRootExtractor(this::getErrorRoot)
93                                            .build());
94     }
95 
96     /**
97      * Creates an instance of {@link XmlProtocolMarshaller} to be used for marshalling the request.
98      *
99      * @param operationInfo Info required to marshall the request
100      */
createProtocolMarshaller(OperationInfo operationInfo)101     public ProtocolMarshaller<SdkHttpFullRequest> createProtocolMarshaller(OperationInfo operationInfo) {
102         return XmlProtocolMarshaller.builder()
103                                     .endpoint(clientConfiguration.option(SdkClientOption.ENDPOINT))
104                                     .xmlGenerator(createGenerator(operationInfo))
105                                     .operationInfo(operationInfo)
106                                     .build();
107     }
108 
createResponseHandler(Supplier<SdkPojo> pojoSupplier, XmlOperationMetadata staxOperationMetadata)109     public <T extends SdkPojo> HttpResponseHandler<T> createResponseHandler(Supplier<SdkPojo> pojoSupplier,
110                                                                             XmlOperationMetadata staxOperationMetadata) {
111         return createResponseHandler(r -> pojoSupplier.get(), staxOperationMetadata);
112     }
113 
createResponseHandler(Function<SdkHttpFullResponse, SdkPojo> pojoSupplier, XmlOperationMetadata staxOperationMetadata)114     public <T extends SdkPojo> HttpResponseHandler<T> createResponseHandler(Function<SdkHttpFullResponse, SdkPojo> pojoSupplier,
115                                                                             XmlOperationMetadata staxOperationMetadata) {
116         return timeUnmarshalling(
117             new AwsXmlResponseHandler<>(
118                 new XmlResponseHandler<>(
119                     XML_PROTOCOL_UNMARSHALLER, pojoSupplier,
120                     staxOperationMetadata.isHasStreamingSuccessResponse())));
121     }
122 
createResponseTransformer( Supplier<SdkPojo> pojoSupplier)123     protected <T extends AwsResponse> Function<AwsXmlUnmarshallingContext, T> createResponseTransformer(
124         Supplier<SdkPojo> pojoSupplier) {
125 
126         return new AwsXmlResponseTransformer<>(
127             XML_PROTOCOL_UNMARSHALLER, r -> pojoSupplier.get());
128     }
129 
createErrorTransformer()130     protected Function<AwsXmlUnmarshallingContext, AwsServiceException> createErrorTransformer() {
131         return AwsXmlErrorTransformer.builder()
132                                      .defaultExceptionSupplier(defaultServiceExceptionSupplier)
133                                      .exceptions(modeledExceptions)
134                                      .errorUnmarshaller(XML_PROTOCOL_UNMARSHALLER)
135                                      .build();
136     }
137 
createErrorResponseHandler()138     public HttpResponseHandler<AwsServiceException> createErrorResponseHandler() {
139         return errorUnmarshaller;
140     }
141 
timeUnmarshalling(HttpResponseHandler<T> delegate)142     private <T> MetricCollectingHttpResponseHandler<T> timeUnmarshalling(HttpResponseHandler<T> delegate) {
143         return MetricCollectingHttpResponseHandler.create(CoreMetric.UNMARSHALLING_DURATION, delegate);
144     }
145 
createCombinedResponseHandler( Supplier<SdkPojo> pojoSupplier, XmlOperationMetadata staxOperationMetadata)146     public <T extends AwsResponse> HttpResponseHandler<Response<T>> createCombinedResponseHandler(
147         Supplier<SdkPojo> pojoSupplier, XmlOperationMetadata staxOperationMetadata) {
148 
149         return new CombinedResponseHandler<>(createResponseHandler(pojoSupplier, staxOperationMetadata),
150                                              createErrorResponseHandler());
151     }
152 
153     /**
154      * Extracts the <Error/> element from the root XML document. This method is protected as S3 has
155      * a slightly different location.
156      *
157      * @param document Root XML document.
158      * @return If error root is found than a fulfilled {@link Optional}, otherwise an empty one.
159      */
getErrorRoot(XmlElement document)160     Optional<XmlElement> getErrorRoot(XmlElement document) {
161         return document.getOptionalElementByName("Error");
162     }
163 
createGenerator(OperationInfo operationInfo)164     protected XmlGenerator createGenerator(OperationInfo operationInfo) {
165         return operationInfo.hasPayloadMembers() ?
166                XmlGenerator.create(operationInfo.addtionalMetadata(XML_NAMESPACE_ATTRIBUTE), false) :
167                null;
168     }
169 
builder()170     public static Builder builder() {
171         return new Builder();
172     }
173 
174     /**
175      * Builder for {@link AwsXmlProtocolFactory}.
176      */
177     public static class Builder<SubclassT extends Builder> {
178 
179         private final List<ExceptionMetadata> modeledExceptions = new ArrayList<>();
180         private Supplier<SdkPojo> defaultServiceExceptionSupplier;
181         private SdkClientConfiguration clientConfiguration;
182 
Builder()183         Builder() {
184         }
185 
186         /**
187          * Registers a new modeled exception by the error code.
188          *
189          * @param errorMetadata metadata for unmarshalling the exceptions
190          * @return This builder for method chaining.
191          */
registerModeledException(ExceptionMetadata errorMetadata)192         public final SubclassT registerModeledException(ExceptionMetadata errorMetadata) {
193             modeledExceptions.add(errorMetadata);
194             return getSubclass();
195         }
196 
197         /**
198          * A supplier for the services base exception builder. This is used when we can't identify any modeled
199          * exception to unmarshall into.
200          *
201          * @param exceptionBuilderSupplier Suppplier of the base service exceptions Builder.
202          * @return This builder for method chaining.
203          */
defaultServiceExceptionSupplier(Supplier<SdkPojo> exceptionBuilderSupplier)204         public SubclassT defaultServiceExceptionSupplier(Supplier<SdkPojo> exceptionBuilderSupplier) {
205             this.defaultServiceExceptionSupplier = exceptionBuilderSupplier;
206             return getSubclass();
207         }
208 
209         /**
210          * Sets the {@link SdkClientConfiguration} which contains the service endpoint.
211          *
212          * @param clientConfiguration Configuration of the client.
213          * @return This builder for method chaining.
214          */
clientConfiguration(SdkClientConfiguration clientConfiguration)215         public SubclassT clientConfiguration(SdkClientConfiguration clientConfiguration) {
216             this.clientConfiguration = clientConfiguration;
217             return getSubclass();
218         }
219 
220         @SuppressWarnings("unchecked")
getSubclass()221         private SubclassT getSubclass() {
222             return (SubclassT) this;
223         }
224 
build()225         public AwsXmlProtocolFactory build() {
226             return new AwsXmlProtocolFactory(this);
227         }
228     }
229 }
230