22// SPDX-License-Identifier: Apache-2.0
33package software .amazon .lambda .durable ;
44
5+ import java .io .IOException ;
6+ import java .io .InputStream ;
57import java .time .Duration ;
68import java .util .Objects ;
9+ import java .util .Properties ;
710import java .util .concurrent .ExecutorService ;
811import java .util .concurrent .Executors ;
912import org .slf4j .Logger ;
1013import org .slf4j .LoggerFactory ;
1114import software .amazon .awssdk .auth .credentials .EnvironmentVariableCredentialsProvider ;
1215import software .amazon .awssdk .core .SdkSystemSetting ;
16+ import software .amazon .awssdk .core .client .config .SdkAdvancedClientOption ;
1317import software .amazon .awssdk .regions .Region ;
1418import software .amazon .awssdk .services .lambda .LambdaClient ;
19+ import software .amazon .awssdk .services .lambda .LambdaClientBuilder ;
1520import software .amazon .awssdk .services .lambda .model .GetDurableExecutionStateRequest ;
1621import software .amazon .lambda .durable .client .DurableExecutionClient ;
1722import software .amazon .lambda .durable .client .LambdaDurableFunctionsClient ;
@@ -64,6 +69,10 @@ public final class DurableConfig {
6469 */
6570 private static final String DEFAULT_REGION = "us-east-1" ;
6671
72+ private static final String VERSION_FILE = "/version.prop" ;
73+ private static final String PROJECT_VERSION = getProjectVersion (VERSION_FILE );
74+ private static final String USER_AGENT_SUFFIX = "@aws/durable-execution-sdk-java/" + PROJECT_VERSION ;
75+
6776 private final DurableExecutionClient durableExecutionClient ;
6877 private final SerDes serDes ;
6978 private final ExecutorService executorService ;
@@ -169,9 +178,9 @@ private static DurableExecutionClient createDefaultDurableExecutionClient() {
169178 logger .debug ("AWS_REGION not set, defaulting to: {}" , region );
170179 }
171180
172- var lambdaClient = LambdaClient .builder ()
173- .credentialsProvider (EnvironmentVariableCredentialsProvider .create ())
174- .region (Region .of (region ))
181+ var lambdaClient = addUserAgentSuffix ( LambdaClient .builder ()
182+ .credentialsProvider (EnvironmentVariableCredentialsProvider .create ())
183+ .region (Region .of (region ) ))
175184 .build ();
176185
177186 try {
@@ -194,6 +203,27 @@ private static DurableExecutionClient createDefaultDurableExecutionClient() {
194203 return new LambdaDurableFunctionsClient (lambdaClient );
195204 }
196205
206+ static LambdaClientBuilder addUserAgentSuffix (LambdaClientBuilder builder ) {
207+ return builder .overrideConfiguration (builder .overrideConfiguration ().toBuilder ()
208+ .putAdvancedOption (SdkAdvancedClientOption .USER_AGENT_SUFFIX , USER_AGENT_SUFFIX )
209+ .build ());
210+ }
211+
212+ private static String getProjectVersion (String versionFile ) {
213+ InputStream stream = DurableConfig .class .getResourceAsStream (versionFile );
214+ if (stream == null ) {
215+ return "UNKNOWN" ;
216+ }
217+ Properties props = new Properties ();
218+ try {
219+ props .load (stream );
220+ stream .close ();
221+ return (String ) props .get ("version" );
222+ } catch (IOException e ) {
223+ return "UNKNOWN" ;
224+ }
225+ }
226+
197227 /**
198228 * Creates a default ExecutorService for running user-defined operations. Uses a cached thread pool with daemon
199229 * threads by default.
@@ -230,23 +260,23 @@ private Builder() {}
230260 * <p>Example:
231261 *
232262 * <pre>{@code
233- * LambdaClient lambdaClient = LambdaClient.builder()
263+ * LambdaClientBuilder lambdaClientBuilder = LambdaClient.builder()
234264 * .region(Region.US_WEST_2)
235- * .credentialsProvider(ProfileCredentialsProvider.create("my-profile"))
236- * .build();
265+ * .credentialsProvider(ProfileCredentialsProvider.create("my-profile"));
237266 *
238267 * DurableConfig.builder()
239- * .withLambdaClient(lambdaClient )
268+ * .withLambdaClientBuilder(lambdaClientBuilder )
240269 * .build();
241270 * }</pre>
242271 *
243- * @param lambdaClient Custom LambdaClient instance
272+ * @param lambdaClientBuilder Custom LambdaClientBuilder instance
244273 * @return This builder
245274 * @throws NullPointerException if lambdaClient is null
246275 */
247- public Builder withLambdaClient (LambdaClient lambdaClient ) {
248- Objects .requireNonNull (lambdaClient , "LambdaClient cannot be null" );
249- this .durableExecutionClient = new LambdaDurableFunctionsClient (lambdaClient );
276+ public Builder withLambdaClientBuilder (LambdaClientBuilder lambdaClientBuilder ) {
277+ Objects .requireNonNull (lambdaClientBuilder , "LambdaClient cannot be null" );
278+ this .durableExecutionClient = new LambdaDurableFunctionsClient (
279+ addUserAgentSuffix (lambdaClientBuilder ).build ());
250280 return this ;
251281 }
252282
@@ -255,7 +285,7 @@ public Builder withLambdaClient(LambdaClient lambdaClient) {
255285 *
256286 * <p><b>Note:</b> This method is primarily intended for testing with mock clients (e.g.,
257287 * {@code LocalMemoryExecutionClient}). For production use with a custom AWS SDK client, prefer
258- * {@link #withLambdaClient(LambdaClient )}.
288+ * {@link #withLambdaClientBuilder(LambdaClientBuilder )}.
259289 *
260290 * @param durableExecutionClient Custom DurableExecutionClient instance
261291 * @return This builder
0 commit comments