Skip to content

Commit e315c34

Browse files
authored
Salande/ua appid 2 (#5654)
* Adds the user agent appId metadata tag to client and system configuration options * appId length check * Fix imports * moving appId to client override config * remove appid from test
1 parent 5a74caa commit e315c34

File tree

14 files changed

+427
-15
lines changed

14 files changed

+427
-15
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Adds an option to set 'appId' metadata to the client builder or to system settings and config files. This metadata string value will be added to the user agent string as `app/somevalue`"
6+
}

core/profiles/src/main/java/software/amazon/awssdk/profiles/ProfileProperty.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ public final class ProfileProperty {
177177
*/
178178
public static final String ENDPOINT_URL = "endpoint_url";
179179

180+
/**
181+
* Configure an optional identification value to be appended to the user agent header.
182+
* The value should be less than 50 characters in length and is null by default.
183+
*/
184+
public static final String SDK_UA_APP_ID = "sdk_ua_app_id";
185+
180186
private ProfileProperty() {
181187
}
182188
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,13 @@ public enum SdkSystemSetting implements SystemSetting {
224224
* Defines a file path from which partition metadata should be loaded. If this isn't specified, the partition
225225
* metadata deployed with the SDK client will be used instead.
226226
*/
227-
AWS_PARTITIONS_FILE("aws.partitionsFile", null)
227+
AWS_PARTITIONS_FILE("aws.partitionsFile", null),
228+
229+
/**
230+
* Configure an optional identification value to be appended to the user agent header.
231+
* The value should be less than 50 characters in length and is null by default.
232+
*/
233+
AWS_SDK_UA_APP_ID("sdk.ua.appId", null)
228234

229235
;
230236

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkClientBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,5 @@ default B addPlugin(SdkPlugin plugin) {
9595
default List<SdkPlugin> plugins() {
9696
throw new UnsupportedOperationException();
9797
}
98+
9899
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
4949
import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
5050
import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT;
51+
import static software.amazon.awssdk.core.client.config.SdkClientOption.USER_AGENT_APP_ID;
52+
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID;
5153
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP;
5254
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.INTERNAL_METADATA_MARKER;
5355
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.IO;
@@ -90,6 +92,7 @@
9092
import software.amazon.awssdk.core.internal.http.pipeline.stages.CompressRequestStage;
9193
import software.amazon.awssdk.core.internal.interceptor.HttpChecksumValidationInterceptor;
9294
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
95+
import software.amazon.awssdk.core.internal.useragent.AppIdResolver;
9396
import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties;
9497
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
9598
import software.amazon.awssdk.core.retry.RetryMode;
@@ -151,7 +154,6 @@ public abstract class SdkDefaultClientBuilder<B extends SdkClientBuilder<B, C>,
151154
private final List<SdkPlugin> plugins = new ArrayList<>();
152155

153156

154-
155157
protected SdkDefaultClientBuilder() {
156158
this(DEFAULT_HTTP_CLIENT_BUILDER, DEFAULT_ASYNC_HTTP_CLIENT_BUILDER);
157159
}
@@ -413,7 +415,7 @@ private String resolveClientUserAgent(LazyValueSource config) {
413415
SdkClientUserAgentProperties clientProperties = new SdkClientUserAgentProperties();
414416

415417
ClientType clientType = config.get(CLIENT_TYPE);
416-
ClientType resolvedClientType = clientType == null ? ClientType.UNKNOWN : config.get(CLIENT_TYPE);
418+
ClientType resolvedClientType = clientType == null ? ClientType.UNKNOWN : clientType;
417419

418420
clientProperties.putProperty(RETRY_MODE, StringUtils.lowerCase(resolveRetryMode(config.get(RETRY_POLICY),
419421
config.get(RETRY_STRATEGY))));
@@ -422,10 +424,20 @@ private String resolveClientUserAgent(LazyValueSource config) {
422424
clientProperties.putProperty(HTTP, SdkHttpUtils.urlEncode(clientName(resolvedClientType,
423425
config.get(SYNC_HTTP_CLIENT),
424426
config.get(ASYNC_HTTP_CLIENT))));
425-
427+
String appId = config.get(USER_AGENT_APP_ID);
428+
String resolvedAppId = appId == null ? resolveAppId(config) : appId;
429+
clientProperties.putProperty(APP_ID, resolvedAppId);
426430
return SdkUserAgentBuilder.buildClientUserAgentString(SystemUserAgent.getOrCreate(), clientProperties);
427431
}
428432

433+
private String resolveAppId(LazyValueSource config) {
434+
Optional<String> appIdFromConfig = AppIdResolver.create()
435+
.profileFile(config.get(PROFILE_FILE_SUPPLIER))
436+
.profileName(config.get(PROFILE_NAME))
437+
.resolve();
438+
return appIdFromConfig.orElse(null);
439+
}
440+
429441
private static String clientName(ClientType clientType, SdkHttpClient syncHttpClient, SdkAsyncHttpClient asyncHttpClient) {
430442
if (clientType == SYNC) {
431443
return syncHttpClient == null ? "null" : syncHttpClient.clientName();
@@ -446,7 +458,7 @@ private RetryStrategy resolveRetryStrategy(LazyValueSource config) {
446458
.resolve();
447459
return SdkDefaultRetryStrategy.forRetryMode(retryMode);
448460
}
449-
461+
450462
/**
451463
* Finalize which sync HTTP client will be used for the created client.
452464
*/

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
3535
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
3636
import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
37+
import static software.amazon.awssdk.core.client.config.SdkClientOption.USER_AGENT_APP_ID;
3738
import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unmanagedScheduledExecutor;
3839
import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unwrapUnmanagedScheduledExecutor;
3940

@@ -120,6 +121,7 @@ public final class ClientOverrideConfiguration
120121
options.add(CONFIGURED_RETRY_STRATEGY);
121122
options.add(CONFIGURED_RETRY_CONFIGURATOR);
122123
options.add(CONFIGURED_RETRY_MODE);
124+
options.add(USER_AGENT_APP_ID);
123125
CLIENT_OVERRIDE_OPTIONS = Collections.unmodifiableSet(options);
124126

125127
Set<ClientOption<?>> resolvedOptions = new HashSet<>();
@@ -381,6 +383,14 @@ public Optional<CompressionConfiguration> compressionConfiguration() {
381383
return Optional.ofNullable(compressionConfig);
382384
}
383385

386+
/**
387+
* An optional user specified identification value to be appended to the user agent header.
388+
* For more information, see {@link SdkClientOption#USER_AGENT_APP_ID}.
389+
*/
390+
public Optional<String> appId() {
391+
return Optional.ofNullable(config.option(USER_AGENT_APP_ID));
392+
}
393+
384394
@Override
385395
public String toString() {
386396
return ToString.builder("ClientOverrideConfiguration")
@@ -395,6 +405,7 @@ public String toString() {
395405
.add("profileName", defaultProfileName().orElse(null))
396406
.add("scheduledExecutorService", scheduledExecutorService().orElse(null))
397407
.add("compressionConfiguration", compressionConfiguration().orElse(null))
408+
.add("appId", appId().orElse(null))
398409
.build();
399410
}
400411

@@ -757,6 +768,16 @@ default Builder compressionConfiguration(Consumer<CompressionConfiguration.Build
757768
}
758769

759770
CompressionConfiguration compressionConfiguration();
771+
772+
/**
773+
* Sets the appId for this client. See {@link SdkClientOption#USER_AGENT_APP_ID}.
774+
*/
775+
Builder appId(String appId);
776+
777+
/**
778+
* The appId for this client. See {@link SdkClientOption#USER_AGENT_APP_ID}.
779+
*/
780+
String appId();
760781
}
761782

762783
/**
@@ -1089,6 +1110,17 @@ public CompressionConfiguration compressionConfiguration() {
10891110
return config.option(CONFIGURED_COMPRESSION_CONFIGURATION);
10901111
}
10911112

1113+
@Override
1114+
public String appId() {
1115+
return config.option(USER_AGENT_APP_ID);
1116+
}
1117+
1118+
@Override
1119+
public Builder appId(String appId) {
1120+
config.option(USER_AGENT_APP_ID, appId);
1121+
return this;
1122+
}
1123+
10921124
@Override
10931125
public ClientOverrideConfiguration build() {
10941126
return new ClientOverrideConfiguration(config.build(), resolvedConfig.build());

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,24 @@ public final class SdkClientOption<T> extends ClientOption<T> {
319319
public static final SdkClientOption<CompressionConfiguration> COMPRESSION_CONFIGURATION =
320320
new SdkClientOption<>(CompressionConfiguration.class);
321321

322+
/**
323+
* An optional identification value to be appended to the user agent header.
324+
* The value should be less than 50 characters in length and is null by default.
325+
* <p>
326+
* Users can additionally supply the appId value through environment and JVM settings, and
327+
* it will be resolved using the following order of precedence (highest first):
328+
* <ol>
329+
* <li>This client option configuration </li>
330+
* <li>The {@code AWS_SDK_UA_APP_ID} environment variable</li>
331+
* <li>The {@code sdk.ua.appId} JVM system property</li>
332+
* <li>The {@code sdk_ua_app_id} setting in the profile file for the active profile</li>
333+
* </ol>
334+
* <p>
335+
* This configuration option supersedes {@link SdkAdvancedClientOption#USER_AGENT_PREFIX} and
336+
* {@link SdkAdvancedClientOption#USER_AGENT_SUFFIX} and should be used instead of those options.
337+
*/
338+
public static final SdkClientOption<String> USER_AGENT_APP_ID = new SdkClientOption<>(String.class);
339+
322340
/**
323341
* Option to specify a reference to the SDK client in use.
324342
*/
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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.core.internal.useragent;
17+
18+
import java.util.Optional;
19+
import java.util.function.Supplier;
20+
import software.amazon.awssdk.annotations.SdkInternalApi;
21+
import software.amazon.awssdk.core.SdkSystemSetting;
22+
import software.amazon.awssdk.profiles.ProfileFile;
23+
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
24+
import software.amazon.awssdk.profiles.ProfileProperty;
25+
import software.amazon.awssdk.utils.OptionalUtils;
26+
27+
@SdkInternalApi
28+
public final class AppIdResolver {
29+
30+
private Supplier<ProfileFile> profileFile;
31+
private String profileName;
32+
33+
private AppIdResolver() {
34+
}
35+
36+
public static AppIdResolver create() {
37+
return new AppIdResolver();
38+
}
39+
40+
public AppIdResolver profileFile(Supplier<ProfileFile> profileFile) {
41+
this.profileFile = profileFile;
42+
return this;
43+
}
44+
45+
public AppIdResolver profileName(String profileName) {
46+
this.profileName = profileName;
47+
return this;
48+
}
49+
50+
public Optional<String> resolve() {
51+
return OptionalUtils.firstPresent(fromSystemSettings(),
52+
() -> fromProfileFile(profileFile, profileName));
53+
}
54+
55+
private Optional<String> fromSystemSettings() {
56+
return SdkSystemSetting.AWS_SDK_UA_APP_ID.getStringValue();
57+
}
58+
59+
private Optional<String> fromProfileFile(Supplier<ProfileFile> profileFile, String profileName) {
60+
profileFile = profileFile != null ? profileFile : ProfileFile::defaultProfileFile;
61+
profileName = profileName != null ? profileName : ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow();
62+
return profileFile.get()
63+
.profile(profileName)
64+
.flatMap(p -> p.property(ProfileProperty.SDK_UA_APP_ID));
65+
}
66+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/SdkUserAgentBuilder.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.core.internal.useragent;
1717

18+
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.APP_ID;
1819
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.CONFIG_METADATA;
1920
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.ENV_METADATA;
2021
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP;
@@ -34,6 +35,7 @@
3435
import software.amazon.awssdk.annotations.SdkProtectedApi;
3536
import software.amazon.awssdk.annotations.ThreadSafe;
3637
import software.amazon.awssdk.core.util.SystemUserAgent;
38+
import software.amazon.awssdk.utils.Logger;
3739
import software.amazon.awssdk.utils.StringUtils;
3840

3941
/**
@@ -43,6 +45,8 @@
4345
@SdkProtectedApi
4446
public final class SdkUserAgentBuilder {
4547

48+
private static final Logger log = Logger.loggerFor(SdkUserAgentBuilder.class);
49+
4650
private SdkUserAgentBuilder() {
4751
}
4852

@@ -77,6 +81,12 @@ public static String buildClientUserAgentString(SystemUserAgent systemValues,
7781
appendFieldAndSpace(uaString, CONFIG_METADATA, uaPair(RETRY_MODE, retryMode));
7882
}
7983

84+
String appId = userAgentProperties.getProperty(APP_ID);
85+
if (!StringUtils.isEmpty(appId)) {
86+
checkLengthAndWarn(appId);
87+
appendFieldAndSpace(uaString, APP_ID, appId);
88+
}
89+
8090
removeFinalWhitespace(uaString);
8191
return uaString.toString();
8292
}
@@ -124,4 +134,12 @@ private static void appendAdditionalJvmMetadata(StringBuilder builder, SystemUse
124134
appendNonEmptyField(builder, METADATA, lang);
125135
}
126136
}
137+
138+
private static void checkLengthAndWarn(String appId) {
139+
if (appId.length() > 50) {
140+
log.warn(() -> String.format("The configured appId '%s' is longer than the recommended maximum length of 50. "
141+
+ "This could result in not being able to transmit and log the whole user agent string, "
142+
+ "including the complete value of this string.", appId));
143+
}
144+
}
127145
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/UserAgentConstant.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public final class UserAgentConstant {
3636
public static final String FRAMEWORK_METADATA = "lib";
3737
public static final String METADATA = "md";
3838
public static final String INTERNAL_METADATA_MARKER = "internal";
39+
public static final String APP_ID = "app";
3940

4041
//Separators used in SDK user agent
4142
public static final String SLASH = "/";

0 commit comments

Comments
 (0)