Skip to content

Commit d4cd73c

Browse files
committed
sdk layer metrics features
1 parent 7bf12fc commit d4cd73c

5 files changed

Lines changed: 263 additions & 7 deletions

File tree

sdk/src/main/java/software/amazon/awssdk/iot/AwsIotMqtt5ClientBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import software.amazon.awssdk.crt.mqtt5.TopicAliasingOptions;
3838
import software.amazon.awssdk.crt.utils.PackageInfo;
3939
import software.amazon.awssdk.crt.mqtt5.packets.UserProperty;
40+
import software.amazon.awssdk.crt.internal.IoTDeviceSDKMetrics;
4041

4142
/**
4243
* Builders for making MQTT5 clients with different connection methods for AWS IoT Core.
@@ -50,6 +51,7 @@ public class AwsIotMqtt5ClientBuilder extends software.amazon.awssdk.crt.CrtReso
5051
private ConnectPacketBuilder configConnect;
5152
private TlsContextOptions configTls;
5253
private MqttConnectCustomAuthConfig configCustomAuth;
54+
private CertificateSource certificateSource = null;
5355

5456
private AwsIotMqtt5ClientBuilder(String hostName, Long port, TlsContextOptions tlsContext) {
5557
config = new Mqtt5ClientOptionsBuilder(hostName, port);
@@ -80,6 +82,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithMtlsFromPath(Stri
8082
if (TlsContextOptions.isAlpnSupported()) {
8183
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
8284
}
85+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
8386
return builder;
8487
}
8588

@@ -98,6 +101,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithMtlsFromMemory(St
98101
if (TlsContextOptions.isAlpnSupported()) {
99102
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
100103
}
104+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
101105
return builder;
102106
}
103107

@@ -117,6 +121,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithMtlsFromPkcs11(St
117121
if (TlsContextOptions.isAlpnSupported()) {
118122
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
119123
}
124+
builder.certificateSource = CertificateSource.PKCS11;
120125
return builder;
121126
}
122127

@@ -136,6 +141,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMtlsCustomKeyOperationsBuilder(S
136141
if (TlsContextOptions.isAlpnSupported()) {
137142
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
138143
}
144+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
139145
return builder;
140146
}
141147

@@ -157,6 +163,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithMtlsFromWindowsCe
157163
if (TlsContextOptions.isAlpnSupported()) {
158164
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
159165
}
166+
builder.certificateSource = CertificateSource.WINDOWS_CERT_STORE;
160167
return builder;
161168
}
162169

@@ -214,6 +221,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithMtlsFromPkcs12(St
214221
if (TlsContextOptions.isAlpnSupported()) {
215222
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
216223
}
224+
builder.certificateSource = CertificateSource.PKCS12_FILE;
217225
return builder;
218226
}
219227

@@ -328,6 +336,7 @@ public static AwsIotMqtt5ClientBuilder newDirectMqttBuilderWithJavaKeystore(
328336
if (TlsContextOptions.isAlpnSupported()) {
329337
builder.configTls.withAlpnList("x-amzn-mqtt-ca");
330338
}
339+
builder.certificateSource = CertificateSource.JAVA_KEYSTORE;
331340
return builder;
332341
}
333342

@@ -866,6 +875,10 @@ public Mqtt5Client build() {
866875

867876
this.config.withConnectOptions(this.configConnect.build());
868877

878+
// Set SDK metrics for the CRT layer to embed in the CONNECT packet username
879+
IoTDeviceSDKMetrics sdkMetrics = IoTSdkMetrics.buildSdkMetrics(this.certificateSource);
880+
this.config.withMetrics(sdkMetrics);
881+
869882
Mqtt5Client returnClient = new Mqtt5Client(this.config.build());
870883

871884
// Keep a reference to the TLS configuration so any possible Websockets-related CrtResources are kept alive

sdk/src/main/java/software/amazon/awssdk/iot/AwsIotMqttConnectionBuilder.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import software.amazon.awssdk.crt.mqtt.MqttMessage;
3737
import software.amazon.awssdk.crt.mqtt.QualityOfService;
3838
import software.amazon.awssdk.crt.mqtt.WebsocketHandshakeTransformArgs;
39+
import software.amazon.awssdk.crt.internal.IoTDeviceSDKMetrics;
3940

4041
/**
4142
* A central class for building Mqtt connections without manually managing a large variety of native objects (some
@@ -60,6 +61,7 @@ public final class AwsIotMqttConnectionBuilder extends CrtResource {
6061
private boolean resetLazilyCreatedResources = true;
6162
// Used to detect if we need to set the ALPN list for custom authorizer
6263
private boolean isUsingCustomAuthorizer = false;
64+
private CertificateSource certificateSource = null;
6365

6466
private void resetDefaultPort() {
6567
if (TlsContextOptions.isAlpnSupported()) {
@@ -106,7 +108,9 @@ protected void releaseNativeHandle() {}
106108
*/
107109
public static AwsIotMqttConnectionBuilder newMtlsBuilderFromPath(String certPath, String privateKeyPath) {
108110
try (TlsContextOptions tlsContextOptions = TlsContextOptions.createWithMtlsFromPath(certPath, privateKeyPath)) {
109-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
111+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
112+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
113+
return builder;
110114
}
111115
}
112116

@@ -119,7 +123,9 @@ public static AwsIotMqttConnectionBuilder newMtlsBuilderFromPath(String certPath
119123
*/
120124
public static AwsIotMqttConnectionBuilder newMtlsBuilder(String certificate, String privateKey) {
121125
try (TlsContextOptions tlsContextOptions = TlsContextOptions.createWithMtls(certificate, privateKey)) {
122-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
126+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
127+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
128+
return builder;
123129
}
124130
}
125131

@@ -146,7 +152,9 @@ public static AwsIotMqttConnectionBuilder newMtlsBuilder(byte[] certificate, byt
146152
*/
147153
public static AwsIotMqttConnectionBuilder newMtlsPkcs11Builder(TlsContextPkcs11Options pkcs11Options) {
148154
try (TlsContextOptions tlsContextOptions = TlsContextOptions.createWithMtlsPkcs11(pkcs11Options)) {
149-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
155+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
156+
builder.certificateSource = CertificateSource.PKCS11;
157+
return builder;
150158
}
151159
}
152160

@@ -158,7 +166,9 @@ public static AwsIotMqttConnectionBuilder newMtlsPkcs11Builder(TlsContextPkcs11O
158166
*/
159167
public static AwsIotMqttConnectionBuilder newMtlsCustomKeyOperationsBuilder(TlsContextCustomKeyOperationOptions operationOptions) {
160168
try (TlsContextOptions tlsContextOptions = TlsContextOptions.createWithMtlsCustomKeyOperations(operationOptions)) {
161-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
169+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
170+
builder.certificateSource = CertificateSource.CERTIFICATE_FILES;
171+
return builder;
162172
}
163173
}
164174

@@ -176,7 +186,9 @@ public static AwsIotMqttConnectionBuilder newMtlsCustomKeyOperationsBuilder(TlsC
176186
public static AwsIotMqttConnectionBuilder newMtlsWindowsCertStorePathBuilder(String certificatePath) {
177187
try (TlsContextOptions tlsContextOptions = TlsContextOptions
178188
.createWithMtlsWindowsCertStorePath(certificatePath)) {
179-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
189+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
190+
builder.certificateSource = CertificateSource.WINDOWS_CERT_STORE;
191+
return builder;
180192
}
181193
}
182194

@@ -195,7 +207,9 @@ public static AwsIotMqttConnectionBuilder newJavaKeystoreBuilder(
195207
java.security.KeyStore keyStore, String certificateAlias, String certificatePassword) throws CrtRuntimeException {
196208
try (TlsContextOptions tlsContextOptions = TlsContextOptions
197209
.createWithMtlsJavaKeystore(keyStore, certificateAlias, certificatePassword)) {
198-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
210+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
211+
builder.certificateSource = CertificateSource.JAVA_KEYSTORE;
212+
return builder;
199213
}
200214
}
201215

@@ -211,7 +225,9 @@ public static AwsIotMqttConnectionBuilder newJavaKeystoreBuilder(
211225
public static AwsIotMqttConnectionBuilder newMtlsPkcs12Builder(
212226
String pkcs12Path, String pkcs12Password) {
213227
try (TlsContextOptions tlsContextOptions = TlsContextOptions.createWithMtlsPkcs12(pkcs12Path, pkcs12Password)) {
214-
return new AwsIotMqttConnectionBuilder(tlsContextOptions);
228+
AwsIotMqttConnectionBuilder builder = new AwsIotMqttConnectionBuilder(tlsContextOptions);
229+
builder.certificateSource = CertificateSource.PKCS12_FILE;
230+
return builder;
215231
}
216232
}
217233

@@ -759,6 +775,10 @@ public MqttClientConnection build() {
759775

760776
resetLazilyCreatedResources = false;
761777

778+
// Set SDK metrics for the CRT layer to embed in the CONNECT packet username
779+
IoTDeviceSDKMetrics sdkMetrics = IoTSdkMetrics.buildSdkMetrics(this.certificateSource);
780+
config.setMetrics(sdkMetrics);
781+
762782
// Connection create
763783
try (MqttConnectionConfig connectionConfig = config.clone()) {
764784

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package software.amazon.awssdk.iot;
7+
8+
/**
9+
* Certificate source identifiers for metrics feature I.
10+
* Each value corresponds to a specific authentication method used by MQTT connection.
11+
* The single-character value is what gets encoded into the metrics string.
12+
*/
13+
public enum CertificateSource {
14+
/** Client certificate and private key provided as file paths or in-memory bytes.*/
15+
CERTIFICATE_FILES("A"),
16+
17+
/** Private key stored in a PKCS#11 compatible hardware security module. */
18+
PKCS11("B"),
19+
20+
/** Certificate retrieved from the windows system certificate source. */
21+
WINDOWS_CERT_STORE("C"),
22+
23+
/** Certificate and private key loaded from a Java keyStore. */
24+
JAVA_KEYSTORE("D"),
25+
26+
/** Certificate and private key bundled in a PKCS#12 file. */
27+
PKCS12_FILE("E");
28+
29+
private final String value;
30+
31+
CertificateSource(String value) {
32+
this.value = value;
33+
}
34+
35+
/**
36+
* @return the single-character identifier encoded into the metrics string.
37+
*/
38+
public String getValue() {
39+
return value;
40+
}
41+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package software.amazon.awssdk.iot;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
import software.amazon.awssdk.crt.internal.IoTDeviceSDKMetrics;
12+
import software.amazon.awssdk.crt.internal.IoTMetricsMetadata;
13+
import software.amazon.awssdk.crt.internal.IoTMetricEncoder;
14+
15+
/**
16+
* Builds SDK-layer metrics for embedding in the MQTT CONNECT packet username field.
17+
* Collects SDK-layer feature usage (e.g., CERTIFICATE_SOURCE) and packages it
18+
* into an IoTDeviceSDKMetrics object for the CRT layer to merge with CRT features.
19+
*/
20+
public class IoTSdkMetrics {
21+
22+
private static final String CERTIFICATE_SOURCE = "I";
23+
24+
/**
25+
* Returns the installed SDK version string.
26+
*/
27+
private static String getSdkVersion(){
28+
try{
29+
Package pkg = IoTSdkMetrics.class.getPackage();
30+
String version = pkg.getSpecificationVersion();
31+
if(version == null){
32+
version = pkg.getImplementationVersion();
33+
}
34+
if(version == null){
35+
version = "dev";
36+
}
37+
return version;
38+
}catch (Exception e){
39+
return "dev";
40+
}
41+
}
42+
43+
/**
44+
* Encodes SDK features into "ID/Value" format.
45+
* @param certificateSource the certificate method in use or null if none
46+
* @return encoded feature string (e.g., "I/A"), or empty string if no feature.
47+
*/
48+
private static String encodedFeatureList(CertificateSource certificateSource){
49+
if(certificateSource!=null){
50+
return CERTIFICATE_SOURCE + "/" + certificateSource.getValue();
51+
}
52+
return "";
53+
}
54+
55+
/**
56+
* Builds an IoTDeviceSDKMetrics instance for CRT layer.
57+
* Always include IoTSDKVersion. When a certificate source is provided,
58+
* also includes IoTSDKFeature and IoTSDKMetricsVersion.
59+
*
60+
* @param certificateSource the certificate method used, or null for connections
61+
* without client certs (websocket, custom auth)
62+
* @return metrics object ready to pass to CRT via withMetrics() or setMetrics()
63+
*/
64+
public static IoTDeviceSDKMetrics buildSdkMetrics(CertificateSource certificateSource) {
65+
List<IoTMetricsMetadata> metadata = new ArrayList<>();
66+
67+
metadata.add(new IoTMetricsMetadata("IoTSDKVersion", getSdkVersion()));
68+
69+
String featureList = encodedFeatureList(certificateSource);
70+
if (!featureList.isEmpty()) {
71+
metadata.add(new IoTMetricsMetadata("IoTSDKFeature", featureList));
72+
metadata.add(new IoTMetricsMetadata("IoTSDKMetricsVersion",
73+
String.valueOf(IoTMetricEncoder.IOT_SDK_METRICS_FEATURE_VERSION)));
74+
}
75+
return new IoTDeviceSDKMetrics("IoTDeviceSDK/Java", metadata);
76+
}
77+
78+
}

0 commit comments

Comments
 (0)