httpClientProperties) {
this.proxyEndpoint =
PropertyUtil.propertyAsString(
httpClientProperties, HttpClientProperties.PROXY_ENDPOINT, null);
+ this.proxyUseSystemPropertyValues =
+ PropertyUtil.propertyAsNullableBoolean(
+ httpClientProperties, HttpClientProperties.PROXY_USE_SYSTEM_PROPERTY_VALUES);
+ this.proxyUseEnvironmentVariableValues =
+ PropertyUtil.propertyAsNullableBoolean(
+ httpClientProperties, HttpClientProperties.PROXY_USE_ENVIRONMENT_VARIABLE_VALUES);
}
@VisibleForTesting
@@ -113,9 +121,26 @@ void configureApacheHttpClientBuilder(ApacheHttpClient.Builder apacheHttpClientB
if (useIdleConnectionReaperEnabled != null) {
apacheHttpClientBuilder.useIdleConnectionReaper(useIdleConnectionReaperEnabled);
}
- if (proxyEndpoint != null) {
- apacheHttpClientBuilder.proxyConfiguration(
- ProxyConfiguration.builder().endpoint(URI.create(proxyEndpoint)).build());
+ configureProxy(apacheHttpClientBuilder);
+ }
+
+ private void configureProxy(ApacheHttpClient.Builder apacheHttpClientBuilder) {
+ if (proxyEndpoint != null
+ || proxyUseSystemPropertyValues != null
+ || proxyUseEnvironmentVariableValues != null) {
+ ProxyConfiguration.Builder proxyBuilder = ProxyConfiguration.builder();
+
+ if (proxyEndpoint != null) {
+ proxyBuilder.endpoint(URI.create(proxyEndpoint));
+ }
+ if (proxyUseSystemPropertyValues != null) {
+ proxyBuilder.useSystemPropertyValues(proxyUseSystemPropertyValues);
+ }
+ if (proxyUseEnvironmentVariableValues != null) {
+ proxyBuilder.useEnvironmentVariableValues(proxyUseEnvironmentVariableValues);
+ }
+
+ apacheHttpClientBuilder.proxyConfiguration(proxyBuilder.build());
}
}
@@ -138,6 +163,8 @@ protected String generateHttpClientCacheKey() {
keyComponents.put("tcpKeepAliveEnabled", tcpKeepAliveEnabled);
keyComponents.put("useIdleConnectionReaperEnabled", useIdleConnectionReaperEnabled);
keyComponents.put("proxyEndpoint", proxyEndpoint);
+ keyComponents.put("proxyUseSystemPropertyValues", proxyUseSystemPropertyValues);
+ keyComponents.put("proxyUseEnvironmentVariableValues", proxyUseEnvironmentVariableValues);
return keyComponents.entrySet().stream()
.map(entry -> entry.getKey() + "=" + Objects.toString(entry.getValue(), "null"))
diff --git a/aws/src/main/java/org/apache/iceberg/aws/HttpClientProperties.java b/aws/src/main/java/org/apache/iceberg/aws/HttpClientProperties.java
index 438ae5bb0431..870d8e23651c 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/HttpClientProperties.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/HttpClientProperties.java
@@ -61,6 +61,30 @@ public class HttpClientProperties implements Serializable {
*/
public static final String PROXY_ENDPOINT = "http-client.proxy-endpoint";
+ /**
+ * Used to enable reading proxy configuration from Java system properties (http.proxyHost,
+ * http.proxyPort, http.nonProxyHosts, etc.). Default is true.
+ *
+ * For more details, see
+ * https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.html
+ * and
+ * https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ProxyConfiguration.html
+ */
+ public static final String PROXY_USE_SYSTEM_PROPERTY_VALUES =
+ "http-client.proxy-use-system-property-values";
+
+ /**
+ * Used to enable reading proxy configuration from environment variables (HTTP_PROXY, HTTPS_PROXY,
+ * NO_PROXY, etc.). Default is true.
+ *
+ *
For more details, see
+ * https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.html
+ * and
+ * https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ProxyConfiguration.html
+ */
+ public static final String PROXY_USE_ENVIRONMENT_VARIABLE_VALUES =
+ "http-client.proxy-use-environment-variable-values";
+
/**
* Used to configure the connection timeout in milliseconds for {@link
* software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient.Builder}. This flag only
diff --git a/aws/src/main/java/org/apache/iceberg/aws/RESTSigV4AuthSession.java b/aws/src/main/java/org/apache/iceberg/aws/RESTSigV4AuthSession.java
index 98808ead4f0b..48281841be37 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/RESTSigV4AuthSession.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/RESTSigV4AuthSession.java
@@ -18,12 +18,15 @@
*/
package org.apache.iceberg.aws;
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
+import org.apache.iceberg.io.CloseableGroup;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.rest.HTTPHeaders;
import org.apache.iceberg.rest.HTTPHeaders.HTTPHeader;
@@ -64,16 +67,23 @@ public class RESTSigV4AuthSession implements AuthSession {
private final Region signingRegion;
private final String signingName;
private final AwsCredentialsProvider credentialsProvider;
+ private final CloseableGroup closeableGroup;
@SuppressWarnings("deprecation")
public RESTSigV4AuthSession(
Aws4Signer aws4Signer, AuthSession delegateAuthSession, AwsProperties awsProperties) {
+ this.closeableGroup = new CloseableGroup();
+ this.closeableGroup.setSuppressCloseFailure(true);
this.signer = Preconditions.checkNotNull(aws4Signer, "Invalid signer: null");
this.delegate = Preconditions.checkNotNull(delegateAuthSession, "Invalid delegate: null");
+ this.closeableGroup.addCloseable(this.delegate);
Preconditions.checkNotNull(awsProperties, "Invalid AWS properties: null");
this.signingRegion = awsProperties.restSigningRegion();
this.signingName = awsProperties.restSigningName();
this.credentialsProvider = awsProperties.restCredentialsProvider();
+ if (credentialsProvider instanceof AutoCloseable closeableCredentialsProvider) {
+ this.closeableGroup.addCloseable(closeableCredentialsProvider);
+ }
}
public AuthSession delegate() {
@@ -87,7 +97,11 @@ public HTTPRequest authenticate(HTTPRequest request) {
@Override
public void close() {
- delegate.close();
+ try {
+ closeableGroup.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
@SuppressWarnings("deprecation")
diff --git a/aws/src/main/java/org/apache/iceberg/aws/S3FileIOAwsClientFactories.java b/aws/src/main/java/org/apache/iceberg/aws/S3FileIOAwsClientFactories.java
index 4aec0bda2a13..3306163baffd 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/S3FileIOAwsClientFactories.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/S3FileIOAwsClientFactories.java
@@ -32,7 +32,7 @@ private S3FileIOAwsClientFactories() {}
/**
* Attempts to load an AWS client factory class for S3 file IO defined in the catalog property
* {@link S3FileIOProperties#CLIENT_FACTORY}. If the property wasn't set, fallback to {@link
- * AwsClientFactories#from(Map) to intialize an AWS client factory class}
+ * AwsClientFactories#from(Map) to initialize an AWS client factory class}
*
* @param properties catalog properties
* @return an instance of a factory class
diff --git a/aws/src/main/java/org/apache/iceberg/aws/UrlConnectionHttpClientConfigurations.java b/aws/src/main/java/org/apache/iceberg/aws/UrlConnectionHttpClientConfigurations.java
index 273baa674804..fbd845852ca9 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/UrlConnectionHttpClientConfigurations.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/UrlConnectionHttpClientConfigurations.java
@@ -35,6 +35,8 @@ class UrlConnectionHttpClientConfigurations extends BaseHttpClientConfigurations
private Long httpClientUrlConnectionConnectionTimeoutMs;
private Long httpClientUrlConnectionSocketTimeoutMs;
private String proxyEndpoint;
+ private Boolean proxyUseSystemPropertyValues;
+ private Boolean proxyUseEnvironmentVariableValues;
private UrlConnectionHttpClientConfigurations() {}
@@ -56,6 +58,12 @@ private void initialize(Map httpClientProperties) {
this.proxyEndpoint =
PropertyUtil.propertyAsString(
httpClientProperties, HttpClientProperties.PROXY_ENDPOINT, null);
+ this.proxyUseSystemPropertyValues =
+ PropertyUtil.propertyAsNullableBoolean(
+ httpClientProperties, HttpClientProperties.PROXY_USE_SYSTEM_PROPERTY_VALUES);
+ this.proxyUseEnvironmentVariableValues =
+ PropertyUtil.propertyAsNullableBoolean(
+ httpClientProperties, HttpClientProperties.PROXY_USE_ENVIRONMENT_VARIABLE_VALUES);
}
@VisibleForTesting
@@ -69,9 +77,26 @@ void configureUrlConnectionHttpClientBuilder(
urlConnectionHttpClientBuilder.socketTimeout(
Duration.ofMillis(httpClientUrlConnectionSocketTimeoutMs));
}
- if (proxyEndpoint != null) {
- urlConnectionHttpClientBuilder.proxyConfiguration(
- ProxyConfiguration.builder().endpoint(URI.create(proxyEndpoint)).build());
+ configureProxy(urlConnectionHttpClientBuilder);
+ }
+
+ private void configureProxy(UrlConnectionHttpClient.Builder urlConnectionHttpClientBuilder) {
+ if (proxyEndpoint != null
+ || proxyUseSystemPropertyValues != null
+ || proxyUseEnvironmentVariableValues != null) {
+ ProxyConfiguration.Builder proxyBuilder = ProxyConfiguration.builder();
+
+ if (proxyEndpoint != null) {
+ proxyBuilder.endpoint(URI.create(proxyEndpoint));
+ }
+ if (proxyUseSystemPropertyValues != null) {
+ proxyBuilder.useSystemPropertyValues(proxyUseSystemPropertyValues);
+ }
+ if (proxyUseEnvironmentVariableValues != null) {
+ proxyBuilder.useEnvironmentVariablesValues(proxyUseEnvironmentVariableValues);
+ }
+
+ urlConnectionHttpClientBuilder.proxyConfiguration(proxyBuilder.build());
}
}
@@ -87,6 +112,8 @@ protected String generateHttpClientCacheKey() {
keyComponents.put("connectionTimeoutMs", httpClientUrlConnectionConnectionTimeoutMs);
keyComponents.put("socketTimeoutMs", httpClientUrlConnectionSocketTimeoutMs);
keyComponents.put("proxyEndpoint", proxyEndpoint);
+ keyComponents.put("proxyUseSystemPropertyValues", proxyUseSystemPropertyValues);
+ keyComponents.put("proxyUseEnvironmentVariableValues", proxyUseEnvironmentVariableValues);
return keyComponents.entrySet().stream()
.map(entry -> entry.getKey() + "=" + Objects.toString(entry.getValue(), "null"))
diff --git a/aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbCatalog.java b/aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbCatalog.java
index 0c991af75076..7c75f99d6d69 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbCatalog.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbCatalog.java
@@ -53,6 +53,7 @@
import org.apache.iceberg.relocated.com.google.common.collect.Lists;
import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.util.LocationUtil;
+import org.apache.iceberg.util.PropertyUtil;
import org.apache.iceberg.util.Tasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -112,6 +113,7 @@ public class DynamoDbCatalog extends BaseMetastoreCatalog
private FileIO fileIO;
private CloseableGroup closeableGroup;
private Map catalogProperties;
+ private boolean uniqueTableLocation;
public DynamoDbCatalog() {}
@@ -123,12 +125,21 @@ public void initialize(String name, Map properties) {
properties.get(CatalogProperties.WAREHOUSE_LOCATION),
new AwsProperties(properties),
AwsClientFactories.from(properties).dynamo(),
- initializeFileIO(properties));
+ initializeFileIO(properties),
+ PropertyUtil.propertyAsBoolean(
+ properties,
+ CatalogProperties.UNIQUE_TABLE_LOCATION,
+ CatalogProperties.UNIQUE_TABLE_LOCATION_DEFAULT));
}
@VisibleForTesting
void initialize(
- String name, String path, AwsProperties properties, DynamoDbClient client, FileIO io) {
+ String name,
+ String path,
+ AwsProperties properties,
+ DynamoDbClient client,
+ FileIO io,
+ boolean uniqTableLocation) {
Preconditions.checkArgument(
!Strings.isNullOrEmpty(path),
"Cannot initialize DynamoDbCatalog because warehousePath must not be null or empty");
@@ -138,6 +149,7 @@ void initialize(
this.warehousePath = LocationUtil.stripTrailingSlash(path);
this.dynamo = client;
this.fileIO = io;
+ this.uniqueTableLocation = uniqTableLocation;
this.closeableGroup = new CloseableGroup();
closeableGroup.addCloseable(dynamo);
@@ -177,12 +189,12 @@ protected String defaultWarehouseLocation(TableIdentifier tableIdentifier) {
}
String defaultLocationCol = toPropertyCol(PROPERTY_DEFAULT_LOCATION);
+ String tableLocation = LocationUtil.tableLocation(tableIdentifier, uniqueTableLocation);
if (response.item().containsKey(defaultLocationCol)) {
- return String.format(
- "%s/%s", response.item().get(defaultLocationCol).s(), tableIdentifier.name());
+ return String.format("%s/%s", response.item().get(defaultLocationCol).s(), tableLocation);
} else {
return String.format(
- "%s/%s.db/%s", warehousePath, tableIdentifier.namespace(), tableIdentifier.name());
+ "%s/%s.db/%s", warehousePath, tableIdentifier.namespace(), tableLocation);
}
}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java
index 47807a2b9f37..94e53cc1ab69 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java
@@ -89,6 +89,7 @@ public class GlueCatalog extends BaseMetastoreCatalog
private Object hadoopConf;
private String catalogName;
private String warehousePath;
+ private boolean uniqueTableLocation;
private AwsProperties awsProperties;
private S3FileIOProperties s3FileIOProperties;
private LockManager lockManager;
@@ -144,7 +145,11 @@ public void initialize(String name, Map properties) {
new AwsProperties(properties),
new S3FileIOProperties(properties),
awsClientFactory.glue(),
- initializeLockManager(properties));
+ initializeLockManager(properties),
+ PropertyUtil.propertyAsBoolean(
+ properties,
+ CatalogProperties.UNIQUE_TABLE_LOCATION,
+ CatalogProperties.UNIQUE_TABLE_LOCATION_DEFAULT));
}
private LockManager initializeLockManager(Map properties) {
@@ -172,7 +177,17 @@ void initialize(
LockManager lock,
Map catalogProps) {
this.catalogProperties = catalogProps;
- initialize(name, path, properties, s3Properties, client, lock);
+ initialize(
+ name,
+ path,
+ properties,
+ s3Properties,
+ client,
+ lock,
+ PropertyUtil.propertyAsBoolean(
+ catalogProps,
+ CatalogProperties.UNIQUE_TABLE_LOCATION,
+ CatalogProperties.UNIQUE_TABLE_LOCATION_DEFAULT));
}
@VisibleForTesting
@@ -182,13 +197,15 @@ void initialize(
AwsProperties properties,
S3FileIOProperties s3Properties,
GlueClient client,
- LockManager lock) {
+ LockManager lock,
+ boolean uniqTableLocation) {
this.catalogName = name;
this.awsProperties = properties;
this.s3FileIOProperties = s3Properties;
this.warehousePath = Strings.isNullOrEmpty(path) ? null : LocationUtil.stripTrailingSlash(path);
this.glue = client;
this.lockManager = lock;
+ this.uniqueTableLocation = uniqTableLocation;
this.closeableGroup = new CloseableGroup();
this.fileIOTracker = new FileIOTracker();
@@ -278,9 +295,10 @@ protected String defaultWarehouseLocation(TableIdentifier tableIdentifier) {
tableIdentifier, awsProperties.glueCatalogSkipNameValidation()))
.build());
String dbLocationUri = response.database().locationUri();
+ String tableLocation = LocationUtil.tableLocation(tableIdentifier, uniqueTableLocation);
if (dbLocationUri != null) {
dbLocationUri = LocationUtil.stripTrailingSlash(dbLocationUri);
- return String.format("%s/%s", dbLocationUri, tableIdentifier.name());
+ return String.format("%s/%s", dbLocationUri, tableLocation);
}
ValidationException.check(
@@ -292,7 +310,7 @@ protected String defaultWarehouseLocation(TableIdentifier tableIdentifier) {
warehousePath,
IcebergToGlueConverter.getDatabaseName(
tableIdentifier, awsProperties.glueCatalogSkipNameValidation()),
- tableIdentifier.name());
+ tableLocation);
}
@Override
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIOProperties.java b/aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIOProperties.java
index ad5181fd2798..922010d61d27 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIOProperties.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIOProperties.java
@@ -295,6 +295,18 @@ public class S3FileIOProperties implements Serializable {
public static final boolean REMOTE_SIGNING_ENABLED_DEFAULT = false;
+ /**
+ * Enables or disables chunked encoding for S3 requests.
+ *
+ * This feature is enabled by default to match the AWS SDK default behavior.
+ *
+ *
For more details see:
+ * https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Configuration.html#chunkedEncodingEnabled()
+ */
+ public static final String CHUNKED_ENCODING_ENABLED = "s3.chunked-encoding-enabled";
+
+ public static final boolean CHUNKED_ENCODING_ENABLED_DEFAULT = true;
+
/** Configure the batch size used when deleting multiple files from a given S3 bucket */
public static final String DELETE_BATCH_SIZE = "s3.delete.batch-size";
@@ -509,6 +521,7 @@ public class S3FileIOProperties implements Serializable {
private String stagingDirectory;
private ObjectCannedACL acl;
private boolean isChecksumEnabled;
+ private boolean isChunkedEncodingEnabled;
private final Set writeTags;
private boolean isWriteTableTagEnabled;
private boolean isWriteNamespaceTagEnabled;
@@ -551,6 +564,7 @@ public S3FileIOProperties() {
this.deleteBatchSize = DELETE_BATCH_SIZE_DEFAULT;
this.stagingDirectory = System.getProperty("java.io.tmpdir");
this.isChecksumEnabled = CHECKSUM_ENABLED_DEFAULT;
+ this.isChunkedEncodingEnabled = CHUNKED_ENCODING_ENABLED_DEFAULT;
this.writeTags = Sets.newHashSet();
this.isWriteTableTagEnabled = WRITE_TABLE_TAG_ENABLED_DEFAULT;
this.isWriteNamespaceTagEnabled = WRITE_NAMESPACE_TAG_ENABLED_DEFAULT;
@@ -641,6 +655,9 @@ public S3FileIOProperties(Map properties) {
"Cannot support S3 CannedACL " + aclType);
this.isChecksumEnabled =
PropertyUtil.propertyAsBoolean(properties, CHECKSUM_ENABLED, CHECKSUM_ENABLED_DEFAULT);
+ this.isChunkedEncodingEnabled =
+ PropertyUtil.propertyAsBoolean(
+ properties, CHUNKED_ENCODING_ENABLED, CHUNKED_ENCODING_ENABLED_DEFAULT);
this.deleteBatchSize =
PropertyUtil.propertyAsInt(properties, DELETE_BATCH_SIZE, DELETE_BATCH_SIZE_DEFAULT);
Preconditions.checkArgument(
@@ -808,6 +825,10 @@ public boolean isChecksumEnabled() {
return this.isChecksumEnabled;
}
+ public boolean isChunkedEncodingEnabled() {
+ return this.isChunkedEncodingEnabled;
+ }
+
public boolean isRemoteSigningEnabled() {
return this.isRemoteSigningEnabled;
}
@@ -994,6 +1015,7 @@ public void applyServiceConfigurations(T builder) {
.pathStyleAccessEnabled(isPathStyleAccess)
.useArnRegionEnabled(isUseArnRegionEnabled)
.accelerateModeEnabled(isAccelerationEnabled)
+ .chunkedEncodingEnabled(isChunkedEncodingEnabled)
.build());
}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3ObjectMapper.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3ObjectMapper.java
index 89145b2465e5..7f1d6c3cc848 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3ObjectMapper.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3ObjectMapper.java
@@ -40,6 +40,10 @@
import org.apache.iceberg.rest.responses.ErrorResponse;
import org.apache.iceberg.rest.responses.OAuthTokenResponse;
+/**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@code RESTObjectMapper} instead.
+ */
+@Deprecated
public class S3ObjectMapper {
private static final JsonFactory FACTORY = new JsonFactory();
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequest.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequest.java
index 879ce8599352..995f6e7e4860 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequest.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequest.java
@@ -18,31 +18,13 @@
*/
package org.apache.iceberg.aws.s3.signer;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.apache.iceberg.rest.RESTRequest;
+import org.apache.iceberg.rest.requests.RemoteSignRequest;
import org.immutables.value.Value;
+/**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link RemoteSignRequest} instead.
+ */
+@Deprecated
@Value.Immutable
-public interface S3SignRequest extends RESTRequest {
- String region();
-
- String method();
-
- URI uri();
-
- Map> headers();
-
- Map properties();
-
- @Value.Default
- @Nullable
- default String body() {
- return null;
- }
-
- @Override
- default void validate() {}
-}
+@SuppressWarnings("immutables:subtype")
+public interface S3SignRequest extends RemoteSignRequest {}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequestParser.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequestParser.java
index 3b5eb83612e2..5d2a7d684460 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequestParser.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignRequestParser.java
@@ -21,108 +21,47 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
-import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
-import org.apache.iceberg.relocated.com.google.common.collect.Maps;
-import org.apache.iceberg.util.JsonUtil;
+import org.apache.iceberg.rest.requests.RemoteSignRequest;
+import org.apache.iceberg.rest.requests.RemoteSignRequestParser;
+/**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link RemoteSignRequestParser} instead.
+ */
+@Deprecated
public class S3SignRequestParser {
- private static final String REGION = "region";
- private static final String METHOD = "method";
- private static final String URI = "uri";
- private static final String HEADERS = "headers";
- private static final String PROPERTIES = "properties";
- private static final String BODY = "body";
-
private S3SignRequestParser() {}
public static String toJson(S3SignRequest request) {
- return toJson(request, false);
+ return RemoteSignRequestParser.toJson(request, false);
}
public static String toJson(S3SignRequest request, boolean pretty) {
- return JsonUtil.generate(gen -> toJson(request, gen), pretty);
+ return RemoteSignRequestParser.toJson(request, pretty);
}
public static void toJson(S3SignRequest request, JsonGenerator gen) throws IOException {
- Preconditions.checkArgument(null != request, "Invalid s3 sign request: null");
-
- gen.writeStartObject();
-
- gen.writeStringField(REGION, request.region());
- gen.writeStringField(METHOD, request.method());
- gen.writeStringField(URI, request.uri().toString());
- headersToJson(HEADERS, request.headers(), gen);
-
- if (!request.properties().isEmpty()) {
- JsonUtil.writeStringMap(PROPERTIES, request.properties(), gen);
- }
-
- if (request.body() != null && !request.body().isEmpty()) {
- gen.writeStringField(BODY, request.body());
- }
-
- gen.writeEndObject();
+ RemoteSignRequestParser.toJson(request, gen);
}
public static S3SignRequest fromJson(String json) {
- return JsonUtil.parse(json, S3SignRequestParser::fromJson);
+ RemoteSignRequest request = RemoteSignRequestParser.fromJson(json);
+ return ImmutableS3SignRequest.builder().from(request).build();
}
public static S3SignRequest fromJson(JsonNode json) {
- Preconditions.checkArgument(null != json, "Cannot parse s3 sign request from null object");
- Preconditions.checkArgument(
- json.isObject(), "Cannot parse s3 sign request from non-object: %s", json);
-
- String region = JsonUtil.getString(REGION, json);
- String method = JsonUtil.getString(METHOD, json);
- java.net.URI uri = java.net.URI.create(JsonUtil.getString(URI, json));
- Map> headers = headersFromJson(HEADERS, json);
-
- ImmutableS3SignRequest.Builder builder =
- ImmutableS3SignRequest.builder().region(region).method(method).uri(uri).headers(headers);
-
- if (json.has(PROPERTIES)) {
- builder.properties(JsonUtil.getStringMap(PROPERTIES, json));
- }
-
- if (json.has(BODY)) {
- builder.body(JsonUtil.getString(BODY, json));
- }
-
- return builder.build();
+ RemoteSignRequest request = RemoteSignRequestParser.fromJson(json);
+ return ImmutableS3SignRequest.builder().from(request).build();
}
static void headersToJson(String property, Map> headers, JsonGenerator gen)
throws IOException {
- gen.writeObjectFieldStart(property);
- for (Entry> entry : headers.entrySet()) {
- gen.writeFieldName(entry.getKey());
-
- gen.writeStartArray();
- for (String val : entry.getValue()) {
- gen.writeString(val);
- }
- gen.writeEndArray();
- }
- gen.writeEndObject();
+ RemoteSignRequestParser.headersToJson(property, headers, gen);
}
static Map> headersFromJson(String property, JsonNode json) {
- Map> headers = Maps.newHashMap();
- JsonNode headersNode = JsonUtil.get(property, json);
- headersNode
- .properties()
- .forEach(
- entry -> {
- String key = entry.getKey();
- List values = Arrays.asList(JsonUtil.getStringArray(entry.getValue()));
- headers.put(key, values);
- });
- return headers;
+ return RemoteSignRequestParser.headersFromJson(property, json);
}
}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponse.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponse.java
index 40c2059488f8..6fbaa90fe7af 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponse.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponse.java
@@ -18,18 +18,13 @@
*/
package org.apache.iceberg.aws.s3.signer;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import org.apache.iceberg.rest.RESTResponse;
+import org.apache.iceberg.rest.responses.RemoteSignResponse;
import org.immutables.value.Value;
+/**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link RemoteSignResponse} instead.
+ */
+@Deprecated
@Value.Immutable
-public interface S3SignResponse extends RESTResponse {
- URI uri();
-
- Map> headers();
-
- @Override
- default void validate() {}
-}
+@SuppressWarnings("immutables:subtype")
+public interface S3SignResponse extends RemoteSignResponse {}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponseParser.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponseParser.java
index 69d6de8f04ac..be63a51b38fb 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponseParser.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3SignResponseParser.java
@@ -21,49 +21,37 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
-import org.apache.iceberg.util.JsonUtil;
+import org.apache.iceberg.rest.responses.RemoteSignResponse;
+import org.apache.iceberg.rest.responses.RemoteSignResponseParser;
+/**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link RemoteSignResponseParser}
+ * instead.
+ */
+@Deprecated
public class S3SignResponseParser {
- private static final String URI = "uri";
- private static final String HEADERS = "headers";
-
private S3SignResponseParser() {}
- public static String toJson(S3SignResponse request) {
- return toJson(request, false);
+ public static String toJson(S3SignResponse response) {
+ return RemoteSignResponseParser.toJson(response, false);
}
- public static String toJson(S3SignResponse request, boolean pretty) {
- return JsonUtil.generate(gen -> toJson(request, gen), pretty);
+ public static String toJson(S3SignResponse response, boolean pretty) {
+ return RemoteSignResponseParser.toJson(response, pretty);
}
public static void toJson(S3SignResponse response, JsonGenerator gen) throws IOException {
- Preconditions.checkArgument(null != response, "Invalid s3 sign response: null");
-
- gen.writeStartObject();
-
- gen.writeStringField(URI, response.uri().toString());
- S3SignRequestParser.headersToJson(HEADERS, response.headers(), gen);
-
- gen.writeEndObject();
+ RemoteSignResponseParser.toJson(response, gen);
}
public static S3SignResponse fromJson(String json) {
- return JsonUtil.parse(json, S3SignResponseParser::fromJson);
+ RemoteSignResponse result = RemoteSignResponseParser.fromJson(json);
+ return ImmutableS3SignResponse.builder().from(result).build();
}
public static S3SignResponse fromJson(JsonNode json) {
- Preconditions.checkArgument(null != json, "Cannot parse s3 sign response from null object");
- Preconditions.checkArgument(
- json.isObject(), "Cannot parse s3 sign response from non-object: %s", json);
-
- java.net.URI uri = java.net.URI.create(JsonUtil.getString(URI, json));
- Map> headers = S3SignRequestParser.headersFromJson(HEADERS, json);
-
- return ImmutableS3SignResponse.builder().uri(uri).headers(headers).build();
+ RemoteSignResponse result = RemoteSignResponseParser.fromJson(json);
+ return ImmutableS3SignResponse.builder().from(result).build();
}
}
diff --git a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
index 84b67bbdafc2..7a463abd3d2d 100644
--- a/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
+++ b/aws/src/main/java/org/apache/iceberg/aws/s3/signer/S3V4RestSignerClient.java
@@ -37,6 +37,7 @@
import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.rest.ErrorHandlers;
import org.apache.iceberg.rest.HTTPClient;
+import org.apache.iceberg.rest.RESTCatalogProperties;
import org.apache.iceberg.rest.RESTClient;
import org.apache.iceberg.rest.RESTUtil;
import org.apache.iceberg.rest.ResourcePaths;
@@ -45,6 +46,9 @@
import org.apache.iceberg.rest.auth.AuthSession;
import org.apache.iceberg.rest.auth.OAuth2Properties;
import org.apache.iceberg.rest.auth.OAuth2Util;
+import org.apache.iceberg.rest.requests.ImmutableRemoteSignRequest;
+import org.apache.iceberg.rest.requests.RemoteSignRequest;
+import org.apache.iceberg.rest.responses.RemoteSignResponse;
import org.apache.iceberg.util.PropertyUtil;
import org.immutables.value.Value;
import org.slf4j.Logger;
@@ -64,13 +68,30 @@ public abstract class S3V4RestSignerClient
extends AbstractAws4Signer implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(S3V4RestSignerClient.class);
- public static final String S3_SIGNER_URI = "s3.signer.uri";
- public static final String S3_SIGNER_ENDPOINT = "s3.signer.endpoint";
- static final String S3_SIGNER_DEFAULT_ENDPOINT = "v1/aws/s3/sign";
- static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
- static final String CACHE_CONTROL = "Cache-Control";
- static final String CACHE_CONTROL_PRIVATE = "private";
- static final String CACHE_CONTROL_NO_CACHE = "no-cache";
+
+ public static final String S3_PROVIDER = "s3";
+
+ /**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link
+ * RESTCatalogProperties#SIGNER_URI} instead.
+ */
+ @Deprecated public static final String S3_SIGNER_URI = "s3.signer.uri";
+
+ /**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; use {@link
+ * RESTCatalogProperties#SIGNER_URI} instead.
+ */
+ @Deprecated public static final String S3_SIGNER_ENDPOINT = "s3.signer.endpoint";
+
+ /**
+ * @deprecated since 1.11.0, will be removed in 1.12.0; there is no replacement.
+ */
+ @Deprecated static final String S3_SIGNER_DEFAULT_ENDPOINT = "v1/aws/s3/sign";
+
+ @VisibleForTesting static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
+
+ private static final String CACHE_CONTROL = "Cache-Control";
+ private static final String CACHE_CONTROL_PRIVATE = "private";
private static final Cache SIGNED_COMPONENT_CACHE =
Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).maximumSize(100).build();
@@ -94,13 +115,28 @@ public Supplier