This is an automated email from the ASF dual-hosted git repository.
collado pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new 7b2cca4a Add a FileIO implementation for WASB (#360)
7b2cca4a is described below
commit 7b2cca4add23e7be369971774a55ce3dae7cca14
Author: Eric Maynard <[email protected]>
AuthorDate: Thu Oct 10 13:25:23 2024 -0400
Add a FileIO implementation for WASB (#360)
* multiple prefixes
* stable tests
* autolint
* add todos
* implement todos
* autolint
* more fixes
* autolint
* fix test
* fix check
* move tenant ID to env var
* another fix
* autolint
* debugging branch
* autolint
* fix
* fix another location check (?)
* autolint
* yank unsupported methods
* autolint
* polish
* resolve conflicts
* autolint
* refactor
* autolint
* fix
---
.../aws/AwsCredentialsStorageIntegrationTest.java | 37 +++------
polaris-server.yml | 2 +-
.../apache/polaris/service/PolarisApplication.java | 2 +-
.../service/catalog/BasePolarisCatalog.java | 1 +
.../catalog/{ => io}/DefaultFileIOFactory.java | 2 +-
.../service/catalog/{ => io}/FileIOFactory.java | 2 +-
.../service/catalog/io/WasbTranslatingFileIO.java | 93 ++++++++++++++++++++++
.../WasbTranslatingFileIOFactory.java} | 14 ++--
.../service/config/PolarisApplicationConfig.java | 2 +-
.../context/PolarisCallContextCatalogFactory.java | 2 +-
.../polaris/service/task/TaskFileIOSupplier.java | 2 +-
.../services/io.dropwizard.jackson.Discoverable | 2 +-
...pache.polaris.service.catalog.io.FileIOFactory} | 3 +-
.../service/admin/PolarisAuthzTestBase.java | 2 +-
.../service/catalog/BasePolarisCatalogTest.java | 3 +
.../catalog/BasePolarisCatalogViewTest.java | 1 +
.../PolarisCatalogHandlerWrapperAuthzTest.java | 1 +
.../catalog/{ => io}/MeasuredFileIOFactory.java | 4 +-
18 files changed, 131 insertions(+), 44 deletions(-)
diff --git
a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
index f152bd88..a7527d18 100644
---
a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
+++
b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
@@ -217,15 +217,10 @@ class AwsCredentialsStorageIntegrationTest {
.getSubscopedCreds(
Mockito.mock(PolarisDiagnostics.class),
new AwsStorageConfigurationInfo(
- storageType,
- List.of(s3Path(bucket, warehouseKeyPrefix, storageType)),
- roleARN,
- externalId),
+ storageType, List.of(s3Path(bucket, warehouseKeyPrefix)),
roleARN, externalId),
true,
- Set.of(
- s3Path(bucket, firstPath, storageType),
- s3Path(bucket, secondPath, storageType)),
- Set.of(s3Path(bucket, firstPath, storageType)));
+ Set.of(s3Path(bucket, firstPath), s3Path(bucket, secondPath)),
+ Set.of(s3Path(bucket, firstPath)));
assertThat(credentials)
.isNotEmpty()
.containsEntry(PolarisCredentialProperty.AWS_TOKEN, "sess")
@@ -313,14 +308,12 @@ class AwsCredentialsStorageIntegrationTest {
Mockito.mock(PolarisDiagnostics.class),
new AwsStorageConfigurationInfo(
PolarisStorageConfigurationInfo.StorageType.S3,
- List.of(s3Path(bucket, warehouseKeyPrefix, storageType)),
+ List.of(s3Path(bucket, warehouseKeyPrefix)),
roleARN,
externalId),
false, /* allowList = false*/
- Set.of(
- s3Path(bucket, firstPath, storageType),
- s3Path(bucket, secondPath, storageType)),
- Set.of(s3Path(bucket, firstPath, storageType)));
+ Set.of(s3Path(bucket, firstPath), s3Path(bucket, secondPath)),
+ Set.of(s3Path(bucket, firstPath)));
assertThat(credentials)
.isNotEmpty()
.containsEntry(PolarisCredentialProperty.AWS_TOKEN, "sess")
@@ -405,14 +398,9 @@ class AwsCredentialsStorageIntegrationTest {
.getSubscopedCreds(
Mockito.mock(PolarisDiagnostics.class),
new AwsStorageConfigurationInfo(
- storageType,
- List.of(s3Path(bucket, warehouseKeyPrefix, storageType)),
- roleARN,
- externalId),
+ storageType, List.of(s3Path(bucket, warehouseKeyPrefix)),
roleARN, externalId),
true, /* allowList = true */
- Set.of(
- s3Path(bucket, firstPath, storageType),
- s3Path(bucket, secondPath, storageType)),
+ Set.of(s3Path(bucket, firstPath), s3Path(bucket, secondPath)),
Set.of());
assertThat(credentials)
.isNotEmpty()
@@ -469,11 +457,7 @@ class AwsCredentialsStorageIntegrationTest {
Mockito.mock(PolarisDiagnostics.class),
new AwsStorageConfigurationInfo(
PolarisStorageConfigurationInfo.StorageType.S3,
- List.of(
- s3Path(
- bucket,
- warehouseKeyPrefix,
- PolarisStorageConfigurationInfo.StorageType.S3)),
+ List.of(s3Path(bucket, warehouseKeyPrefix)),
roleARN,
externalId),
true, /* allowList = true */
@@ -494,8 +478,7 @@ class AwsCredentialsStorageIntegrationTest {
return bucketArn + "/" + keyPrefix + "/*";
}
- private static @NotNull String s3Path(
- String bucket, String keyPrefix,
PolarisStorageConfigurationInfo.StorageType storageType) {
+ private static @NotNull String s3Path(String bucket, String keyPrefix) {
return "s3://" + bucket + "/" + keyPrefix;
}
}
diff --git a/polaris-server.yml b/polaris-server.yml
index f3813ac8..e147d4d4 100644
--- a/polaris-server.yml
+++ b/polaris-server.yml
@@ -88,7 +88,7 @@ metaStoreManager:
# persistence-unit: polaris
io:
- factoryType: default
+ factoryType: wasb
# TODO - avoid duplicating token broker config
oauth2:
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
index c782c6a9..dd0597b8 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
@@ -84,11 +84,11 @@ import
org.apache.polaris.service.admin.api.PolarisCatalogsApi;
import org.apache.polaris.service.admin.api.PolarisPrincipalRolesApi;
import org.apache.polaris.service.admin.api.PolarisPrincipalsApi;
import org.apache.polaris.service.auth.DiscoverableAuthenticator;
-import org.apache.polaris.service.catalog.FileIOFactory;
import org.apache.polaris.service.catalog.IcebergCatalogAdapter;
import org.apache.polaris.service.catalog.api.IcebergRestCatalogApi;
import org.apache.polaris.service.catalog.api.IcebergRestConfigurationApi;
import org.apache.polaris.service.catalog.api.IcebergRestOAuth2Api;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.config.ConfigurationStoreAware;
import org.apache.polaris.service.config.HasEntityManagerFactory;
import org.apache.polaris.service.config.OAuth2ApiService;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java
index fc552a5b..1f08f6bb 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java
@@ -99,6 +99,7 @@ import
org.apache.polaris.core.storage.PolarisStorageConfigurationInfo;
import org.apache.polaris.core.storage.PolarisStorageIntegration;
import org.apache.polaris.core.storage.StorageLocation;
import org.apache.polaris.core.storage.aws.PolarisS3FileIOClientFactory;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.task.TaskExecutor;
import org.apache.polaris.service.types.NotificationRequest;
import org.apache.polaris.service.types.NotificationType;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java
similarity index 96%
copy from
polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
copy to
polaris-service/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java
index 0ff8034b..cf1ee6f8 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.service.catalog;
+package org.apache.polaris.service.catalog.io;
import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.Map;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/FileIOFactory.java
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java
similarity index 96%
rename from
polaris-service/src/main/java/org/apache/polaris/service/catalog/FileIOFactory.java
rename to
polaris-service/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java
index 173864e1..206aaeaa 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/FileIOFactory.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.service.catalog;
+package org.apache.polaris.service.catalog.io;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.dropwizard.jackson.Discoverable;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIO.java
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIO.java
new file mode 100644
index 00000000..38e182db
--- /dev/null
+++
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIO.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.service.catalog.io;
+
+import java.util.Map;
+import org.apache.iceberg.io.FileIO;
+import org.apache.iceberg.io.InputFile;
+import org.apache.iceberg.io.OutputFile;
+import org.apache.polaris.core.storage.StorageLocation;
+import org.apache.polaris.core.storage.azure.AzureLocation;
+
+/**
+ * A {@link FileIO} implementation that translates WASB paths into ABFS paths
and then delegates to
+ * another underlying FileIO implementation
+ */
+public class WasbTranslatingFileIO implements FileIO {
+ private final FileIO io;
+
+ private static final String WASB_SCHEME = "wasb";
+ private static final String ABFS_SCHEME = "abfs";
+
+ public WasbTranslatingFileIO(FileIO io) {
+ this.io = io;
+ }
+
+ private static String translate(String path) {
+ if (path == null) {
+ return null;
+ } else {
+ StorageLocation storageLocation = StorageLocation.of(path);
+ if (storageLocation instanceof AzureLocation azureLocation) {
+ String scheme = azureLocation.getScheme();
+ if (scheme.startsWith(WASB_SCHEME)) {
+ scheme = scheme.replaceFirst(WASB_SCHEME, ABFS_SCHEME);
+ }
+ return String.format(
+ "%s://%s@%s.dfs.core.windows.net/%s",
+ scheme,
+ azureLocation.getContainer(),
+ azureLocation.getStorageAccount(),
+ azureLocation.getFilePath());
+ } else {
+ return path;
+ }
+ }
+ }
+
+ @Override
+ public InputFile newInputFile(String path) {
+ return io.newInputFile(translate(path));
+ }
+
+ @Override
+ public OutputFile newOutputFile(String path) {
+ return io.newOutputFile(translate(path));
+ }
+
+ @Override
+ public void deleteFile(String path) {
+ io.deleteFile(translate(path));
+ }
+
+ @Override
+ public Map<String, String> properties() {
+ return io.properties();
+ }
+
+ @Override
+ public void initialize(Map<String, String> properties) {
+ io.initialize(properties);
+ }
+
+ @Override
+ public void close() {
+ io.close();
+ }
+}
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java
similarity index 69%
rename from
polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
rename to
polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java
index 0ff8034b..0483a5de 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/catalog/DefaultFileIOFactory.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.service.catalog;
+package org.apache.polaris.service.catalog.io;
import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.Map;
@@ -24,11 +24,13 @@ import org.apache.hadoop.conf.Configuration;
import org.apache.iceberg.CatalogUtil;
import org.apache.iceberg.io.FileIO;
-/** A simple FileIOFactory implementation that defers all the work to the
Iceberg SDK */
-@JsonTypeName("default")
-public class DefaultFileIOFactory implements FileIOFactory {
+/** A {@link FileIOFactory} that translates WASB paths to ABFS ones */
+@JsonTypeName("wasb")
+public class WasbTranslatingFileIOFactory implements FileIOFactory {
@Override
- public FileIO loadFileIO(String impl, Map<String, String> properties) {
- return CatalogUtil.loadFileIO(impl, properties, new Configuration());
+ public FileIO loadFileIO(String ioImpl, Map<String, String> properties) {
+ WasbTranslatingFileIO wrapped =
+ new WasbTranslatingFileIO(CatalogUtil.loadFileIO(ioImpl, properties,
new Configuration()));
+ return wrapped;
}
}
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
b/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
index f02222f5..e623f253 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
@@ -30,7 +30,7 @@ import org.apache.polaris.core.PolarisConfigurationStore;
import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal;
import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
import org.apache.polaris.service.auth.DiscoverableAuthenticator;
-import org.apache.polaris.service.catalog.FileIOFactory;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.context.CallContextResolver;
import org.apache.polaris.service.context.RealmContextResolver;
import org.apache.polaris.service.ratelimiter.RateLimiter;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/context/PolarisCallContextCatalogFactory.java
b/polaris-service/src/main/java/org/apache/polaris/service/context/PolarisCallContextCatalogFactory.java
index 4fef6a84..76c67c89 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/context/PolarisCallContextCatalogFactory.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/context/PolarisCallContextCatalogFactory.java
@@ -31,7 +31,7 @@ import org.apache.polaris.core.entity.PolarisBaseEntity;
import org.apache.polaris.core.persistence.PolarisEntityManager;
import org.apache.polaris.core.persistence.resolver.PolarisResolutionManifest;
import org.apache.polaris.service.catalog.BasePolarisCatalog;
-import org.apache.polaris.service.catalog.FileIOFactory;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
import org.apache.polaris.service.config.RealmEntityManagerFactory;
import org.apache.polaris.service.task.TaskExecutor;
import org.slf4j.Logger;
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java
b/polaris-service/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java
index 1d6a367e..c84eebd9 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java
@@ -29,7 +29,7 @@ import org.apache.polaris.core.entity.PolarisTaskConstants;
import org.apache.polaris.core.entity.TaskEntity;
import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
-import org.apache.polaris.service.catalog.FileIOFactory;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
public class TaskFileIOSupplier implements Function<TaskEntity, FileIO> {
private final MetaStoreManagerFactory metaStoreManagerFactory;
diff --git
a/polaris-service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable
b/polaris-service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable
index d0deb183..95d1f8ec 100644
---
a/polaris-service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable
+++
b/polaris-service/src/main/resources/META-INF/services/io.dropwizard.jackson.Discoverable
@@ -23,5 +23,5 @@ org.apache.polaris.service.config.OAuth2ApiService
org.apache.polaris.service.context.RealmContextResolver
org.apache.polaris.service.context.CallContextResolver
org.apache.polaris.service.auth.TokenBrokerFactory
-org.apache.polaris.service.catalog.FileIOFactory
+org.apache.polaris.service.catalog.io.FileIOFactory
org.apache.polaris.service.ratelimiter.RateLimiter
diff --git
a/polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.FileIOFactory
b/polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.io.FileIOFactory
similarity index 86%
rename from
polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.FileIOFactory
rename to
polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.io.FileIOFactory
index e4808ecc..6b280ad7 100644
---
a/polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.FileIOFactory
+++
b/polaris-service/src/main/resources/META-INF/services/org.apache.polaris.service.catalog.io.FileIOFactory
@@ -17,4 +17,5 @@
# under the License.
#
-org.apache.polaris.service.catalog.DefaultFileIOFactory
+org.apache.polaris.service.catalog.io.DefaultFileIOFactory
+org.apache.polaris.service.catalog.io.WasbTranslatingFileIOFactory
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
index 19177f07..702acf41 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
@@ -61,8 +61,8 @@ import
org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.resolver.PolarisResolutionManifest;
import org.apache.polaris.core.storage.cache.StorageCredentialCache;
import org.apache.polaris.service.catalog.BasePolarisCatalog;
-import org.apache.polaris.service.catalog.DefaultFileIOFactory;
import org.apache.polaris.service.catalog.PolarisPassthroughResolutionView;
+import org.apache.polaris.service.catalog.io.DefaultFileIOFactory;
import org.apache.polaris.service.config.DefaultConfigurationStore;
import org.apache.polaris.service.config.RealmEntityManagerFactory;
import org.apache.polaris.service.context.PolarisCallContextCatalogFactory;
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogTest.java
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogTest.java
index 1888bdef..4f6a28ef 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogTest.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogTest.java
@@ -84,6 +84,9 @@ import
org.apache.polaris.core.storage.aws.AwsCredentialsStorageIntegration;
import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo;
import org.apache.polaris.core.storage.cache.StorageCredentialCache;
import org.apache.polaris.service.admin.PolarisAdminService;
+import org.apache.polaris.service.catalog.io.DefaultFileIOFactory;
+import org.apache.polaris.service.catalog.io.FileIOFactory;
+import org.apache.polaris.service.catalog.io.MeasuredFileIOFactory;
import
org.apache.polaris.service.persistence.InMemoryPolarisMetaStoreManagerFactory;
import org.apache.polaris.service.task.TableCleanupTaskHandler;
import org.apache.polaris.service.task.TaskExecutor;
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
index f8051aa0..d64ac3bb 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
@@ -47,6 +47,7 @@ import
org.apache.polaris.core.persistence.PolarisEntityManager;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.storage.cache.StorageCredentialCache;
import org.apache.polaris.service.admin.PolarisAdminService;
+import org.apache.polaris.service.catalog.io.DefaultFileIOFactory;
import
org.apache.polaris.service.persistence.InMemoryPolarisMetaStoreManagerFactory;
import
org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
import org.jetbrains.annotations.Nullable;
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapperAuthzTest.java
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapperAuthzTest.java
index 0f6fc41e..b4a9d955 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapperAuthzTest.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapperAuthzTest.java
@@ -61,6 +61,7 @@ import
org.apache.polaris.core.persistence.PolarisEntityManager;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.resolver.PolarisResolutionManifest;
import org.apache.polaris.service.admin.PolarisAuthzTestBase;
+import org.apache.polaris.service.catalog.io.DefaultFileIOFactory;
import org.apache.polaris.service.config.RealmEntityManagerFactory;
import org.apache.polaris.service.context.PolarisCallContextCatalogFactory;
import org.apache.polaris.service.types.NotificationRequest;
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/MeasuredFileIOFactory.java
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java
similarity index 96%
rename from
polaris-service/src/test/java/org/apache/polaris/service/catalog/MeasuredFileIOFactory.java
rename to
polaris-service/src/test/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java
index ba8107e6..0def6dc8 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/MeasuredFileIOFactory.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.service.catalog;
+package org.apache.polaris.service.catalog.io;
+import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -31,6 +32,7 @@ import org.apache.iceberg.io.InputFile;
import org.apache.iceberg.io.OutputFile;
/** A FileIOFactory that measures the number of bytes read, files written, and
files deleted. */
+@JsonTypeName("measured")
public class MeasuredFileIOFactory implements FileIOFactory {
private final List<MeasuredFileIO> ios;