forked from ndrppnc/cloud-hw1-starter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.html
207 lines (201 loc) · 12 KB
/
README.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<h1>Building the client</h1>
<p>Before proceeding, install a <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html">JDK</a> (must be Java 8 or later) and <a href="https://maven.apache.org/install.html">Apache Maven</a>.</p>
<p>Ensure <code>JAVA_HOME</code> is set correctly and the <code>mvn</code> executable is available on your PATH.</p>
<p>There are two approaches to building the client.</p>
<ol>
<li>You can compile just the client and publish to a repository that handles dependency management like MavenCentral. (Recommended)</li>
<li>You can build a "fat jar" that contains all required dependencies; this jar can be added to your customers' classpath.</li>
</ol>
<h2>1. Building a single jar</h2>
<p>Run the following command in a terminal/console.</p>
<pre><code class="language-bash">mvn package
</code></pre>
<p>This compiles the client into a jar located at <code>./target/DiningConcierge-1.0-SNAPSHOT.jar</code>. Note that this jar does not include any dependencies.</p>
<h3>Distributing the client package</h3>
<p>It's recommended to publish your client to a repository (<a href="https://maven.apache.org/">Maven</a> or <a href="http://ant.apache.org/ivy/">Ivy</a> for example) and have your customers declare a dependency on the project in their build system.</p>
<p>To depend on this project in Gradle (after the package is published to an accessible repository), add the following to your build.gradle file.</p>
<pre><code class="language-perl">dependencies {
compile 'sdk:DiningConcierge:1.0-SNAPSHOT'
}
</code></pre>
<p>To depend on this project in Apache Maven (after the package published to an accessible repository), add the following to your pom.xml file.</p>
<pre><code class="language-xml"><dependencies>
<dependency>
<groupId>sdk</groupId>
<artifactId>DiningConcierge</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</code></pre>
<p>For more information on managing dependencies with Maven and publishing artifacts, see:</p>
<ul>
<li><a href="https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html">https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html</a></li>
<li><a href="http://central.sonatype.org/pages/ossrh-guide.html">http://central.sonatype.org/pages/ossrh-guide.html</a></li>
</ul>
<h2>2. Building a Standalone Jar</h2>
<p>If your customers aren't using Maven or Gradle and you would prefer to distribute a single JAR with all dependencies, you can use the following command to build a fat jar.</p>
<pre><code class="language-bash">mvn clean package -Pstandalone-jar
</code></pre>
<p>This will compile the client and package all dependencies into a jar located at <code>./target/DiningConcierge-1.0-SNAPSHOT.jar</code>.</p>
<h1>Developer Guide</h1>
<h2>Creating a client</h2>
<p>To create a client, use the client builder. You can obtain an instance of the builder via a static factory method located on the client interface.</p>
<pre><code class="language-java">DiningConciergeClientBuilder builder = DiningConcierge.builder();
</code></pre>
<p>The builder exposes many fluent configuration methods that can be chained to configure a service client. Here's a simple example that sets a few optional configuration options and then builds the service client.</p>
<pre><code class="language-java">DiningConcierge client = DiningConcierge.builder()
.connectionConfiguration(new ConnectionConfiguration()
.maxConnections(100)
.connectionMaxIdleMillis(1000))
.timeoutConfiguration(new TimeoutConfiguration()
.httpRequestTimeout(3000)
.totalExecutionTimeout(10000)
.socketTimeout(2000))
.build();
</code></pre>
<h3>Client Lifecycle</h3>
<p>Note that each client created has its own connection pool. It's recommended to treat the clients as long-lived objects. Clients are immutable and thread safe.
Clients clean up their resources when garbage collected but if you want to explicitly shut down the client you can do the following:</p>
<pre><code class="language-java">DiningConcierge client = DiningConcierge.builder().build();
client.shutdown();
// Client is now unusable
</code></pre>
<h2>Making requests</h2>
<p>After a client is configured and created, you can make a request to the service. A method on the client interface (<code>DiningConcierge</code>) is created for all actions (resource + method) in your API.</p>
<p>For each API method, classes are generated that represent the request and response of that API. The request class has setters for any path parameters, query parameters, headers, and payload model that are defined in the API. The response class exposes getters for any modeled headers and for the modeled payload.</p>
<pre><code class="language-java">DiningConcierge client = DiningConcierge.builder().build();
SendMessageResult result = client.sendMessage(new SendMessageRequest());
</code></pre>
<h3>Request Configuration</h3>
<p>In addition to client-level configuration configured by the builder, each request class exposes configuration methods that are scoped to that request alone. Request level configuration takes precedence over client level configuration.</p>
<p>The request config also allows adding headers and query parameters that aren't modeled by the API.</p>
<pre><code class="language-java">DiningConcierge client = DiningConcierge.builder().build();
client.sendMessage(new SendMessageRequest().sdkRequestConfig(
SdkRequestConfig.builder()
.httpRequestTimeout(1500)
.totalExecutionTimeout(5000)
.customHeader("CustomHeaderName", "foo")
.customQueryParam("CustomQueryParamName", "bar")
.build()
));
</code></pre>
<h3>Response Metadata</h3>
<p>In addition to the modeled data present in result objects, the SDK exposes access to additional HTTP metadata. This metadata is useful for debugging issues or accessing unmodeled data from the HTTP response.</p>
<pre><code class="language-java">SendMessageResult result = client.sendMessage(new SendMessageRequest());
System.out.println(result.sdkResponseMetadata().requestId());
System.out.println(result.sdkResponseMetadata().httpStatusCode());
// Full access to all HTTP headers (including modeled ones)
result.sdkResponseMetadata().header("Content-Length").ifPresent(System.out::println);
</code></pre>
<h2>Exception Handling</h2>
<p>Service exceptions and client exceptions can be handled separately. Any exceptions modeled in the API will be a subtype of DiningConciergeException.</p>
<pre><code class="language-java">try {
client.sendMessage(...);
} catch(InternalServerErrorException e) {
// This is a modeled exception defined in the API
} catch(DiningConciergeException e) {
// All service exceptions will extend from DiningConciergeException.
// Any unknown or unmodeled service exceptions will be represented as a DiningConciergeException.
} catch(SdkClientException e) {
// Client exceptions include timeouts, IOExceptions, or any other exceptional situation where a response
// is not received from the service.
}
</code></pre>
<p>All exceptions that can be thrown by the SDK are a subtype of SdkBaseException. To handle any exception in the same way, you can directly catch this exception. This covers both client and service exceptions.</p>
<pre><code class="language-java">try {
client.sendMessage(...);
} catch(SdkBaseException e) {
// All exceptions thrown from the client will be a subtype of SdkBaseException.
}
</code></pre>
<p>All service exceptions expose metadata about the HTTP response for logging or debugging purposes.</p>
<pre><code class="language-java">try {
client.sendMessage(...);
} catch(DiningConciergeException e) {
int statusCode = e.sdkHttpMetadata().httpStatusCode();
String requestId = e.sdkHttpMetadata().requestId();
Optional<String> contentLength = e.sdkHttpMetadata().header("Content-Length");
ByteBuffer responseContent = e.sdkHttpMetadata().responseContent();
}
</code></pre>
<p>Some client exceptions thrown are subtypes of SdkClientException. This provides greater granularity to deal with client-side exceptions.</p>
<pre><code class="language-java">try {
client.sendMessage(...);
} catch(ClientExecutionTimeoutException e) {
// Specific client exception thrown when the totalExecutionTimeout is triggered.
} catch(AbortedException e) {
// Thrown when the client thread is interrupted while making a request.
} catch(SdkClientException e) {
// All other exceptions can be handled here.
}
</code></pre>
<h2>Retries</h2>
<p>Out of the box, the generated client retries on throttling errors (HTTP status code 429) and connection exceptions. If a different retry policy is desired, a custom one can be set via the client builder.</p>
<p>The easiest way to create a custom retry policy is to use the RetryPolicyBuilder. It provides a declarative API to specify when to retry.</p>
<pre><code class="language-java">/**
* The policy below will retry if the cause of the failed request matches any of the exceptions
* given OR if the HTTP response from the service has one of the provided status codes.
*/
DiningConcierge client = DiningConcierge.builder()
.retryPolicy(RetryPolicyBuilder.standard()
.retryOnExceptions(InternalServerErrorException.class, SocketTimeoutException.class)
.retryOnStatusCodes(429, 500)
.maxNumberOfRetries(10)
.fixedBackoff(100)
.build())
.build();
</code></pre>
<p>You can also directly implement the RetryPolicy interface to define your own implementation. RetryPolicyContext contains useful metadata about the state of the failed request that can be used to drive dynamic retry decisions or compute backoff delays.</p>
<pre><code class="language-java">/**
* Simple implementation of {@link com.amazonaws.retry.v2.RetryPolicy}
*/
public static class CustomRetryPolicy implements RetryPolicy {
@Override
public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
return 100;
}
@Override
public boolean shouldRetry(RetryPolicyContext context) {
return context.retriesAttempted() < 3;
}
}
// Using a custom retry policy via the builder
DiningConcierge client = DiningConcierge.builder()
.retryPolicy(new CustomRetryPolicy())
.build();
</code></pre>
<p>You can implement a RetryCondition and BackoffStrategy separately and combine them into a single policy.</p>
<pre><code class="language-java">/**
* Retry on 429 status codes. It's important to note that status code may be null if no response was returned from the
* service. See {@link com.amazonaws.retry.v2.RetryCondition}
*/
public static class RetryOnThrottlingCondition implements RetryCondition {
@Override
public boolean shouldRetry(RetryPolicyContext context) {
return context.httpStatusCode() != null && context.httpStatusCode() == 429;
}
}
/**
* Simple implementation of {@link BackoffStrategy} that always backs off 100 milliseconds.
*/
public static class Backoff100MillisecondsStrategy implements BackoffStrategy {
@Override
public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
return 100;
}
}
/**
* Uses {@link com.amazonaws.retry.v2.SimpleRetryPolicy} to combine a separately implemented RetryCondition and BackoffStrategy.
*/
DiningConcierge client = DiningConcierge.builder()
.retryPolicy(new SimpleRetryPolicy(new RetryOnThrottlingCondition(), new Backoff100MillisecondsStrategy()))
.build();
/**
* The RetryCondition and BackoffStrategy interfaces are functional interfaces so lambda expressions and method
* references may also be used. This is equivalent to the above example.
*/
DiningConcierge client = DiningConcierge.builder()
.retryPolicy(new SimpleRetryPolicy(c -> c.httpStatusCode() != null && c.httpStatusCode() == 429,
c -> 100))
.build();
</code></pre>