This is an automated email from the ASF dual-hosted git repository.

mattyb149 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new f31f803282 NIFI-12078: Add JDBC Catalog Controller Service for Iceberg 
processors
f31f803282 is described below

commit f31f8032822bdba1dfa9ef03bbafefdac6d04923
Author: Mark Bathori <[email protected]>
AuthorDate: Sun Aug 4 20:37:12 2024 +0200

    NIFI-12078: Add JDBC Catalog Controller Service for Iceberg processors
    
    Disable jdbc catalog tests on Windows OS
    
    Fix review comments
    
    Signed-off-by: Matt Burgess <[email protected]>
    
    This closes #9145
---
 .../nifi-iceberg-common/pom.xml                    |  22 ++-
 .../iceberg/catalog/IcebergCatalogFactory.java     |  40 ++++-
 .../iceberg/catalog/IcebergJdbcClientPool.java}    |  25 +--
 .../nifi-iceberg-processors/pom.xml                |  12 ++
 .../processors/iceberg/AbstractTestPutIceberg.java |  97 ++++++++++++
 .../processors/iceberg/TestDataFileActions.java    |  14 +-
 .../iceberg/TestPutIcebergCustomValidation.java    |  29 ++--
 .../iceberg/TestPutIcebergWithHadoopCatalog.java   |  90 ++++++-----
 .../iceberg/TestPutIcebergWithHiveCatalog.java     | 157 ++++---------------
 .../iceberg/TestPutIcebergWithJdbcCatalog.java     | 171 +++++++++++++++++++++
 .../iceberg/catalog/TestHadoopCatalogService.java  |  56 -------
 .../iceberg/catalog/TestHiveCatalogService.java    |  91 -----------
 .../processors/iceberg/util/IcebergTestUtils.java  |  17 ++
 .../services/iceberg/IcebergCatalogProperty.java   |  11 +-
 .../services/iceberg/IcebergCatalogService.java    |   2 +-
 .../nifi/services/iceberg/IcebergCatalogType.java  |   3 +-
 .../nifi-iceberg-services/pom.xml                  |   4 +
 .../services/iceberg/AbstractCatalogService.java   |  17 +-
 .../services/iceberg/FileIOImplementation.java     |  68 ++++++++
 .../services/iceberg/HadoopCatalogService.java     |  18 +--
 .../nifi/services/iceberg/HiveCatalogService.java  |  12 +-
 .../nifi/services/iceberg/JdbcCatalogService.java  |  90 +++++++++++
 .../org.apache.nifi.controller.ControllerService   |   3 +-
 .../apache/nifi/hive/metastore/MetastoreCore.java  |   2 +-
 24 files changed, 662 insertions(+), 389 deletions(-)

diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/pom.xml 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/pom.xml
index a4b3b6a187..7ef3aa0065 100644
--- a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/pom.xml
+++ b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/pom.xml
@@ -25,9 +25,8 @@
     <packaging>jar</packaging>
 
     <dependencies>
-        <!-- Internal dependencies -->
-
 
+        <!-- Internal dependencies -->
         <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-record</artifactId>
@@ -59,6 +58,10 @@
             <artifactId>nifi-hadoop-utils</artifactId>
             <version>2.0.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-dbcp-service-api</artifactId>
+        </dependency>
 
         <!-- External dependencies -->
         <dependency>
@@ -86,6 +89,21 @@
             <artifactId>iceberg-orc</artifactId>
             <version>${iceberg.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.iceberg</groupId>
+            <artifactId>iceberg-aws</artifactId>
+            <version>${iceberg.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iceberg</groupId>
+            <artifactId>iceberg-azure</artifactId>
+            <version>${iceberg.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.iceberg</groupId>
+            <artifactId>iceberg-gcp</artifactId>
+            <version>${iceberg.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.hadoop</groupId>
             <artifactId>hadoop-client</artifactId>
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergCatalogFactory.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergCatalogFactory.java
index 83f765df4a..19da644afb 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergCatalogFactory.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergCatalogFactory.java
@@ -19,16 +19,25 @@ package org.apache.nifi.processors.iceberg.catalog;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.iceberg.CatalogProperties;
+import org.apache.iceberg.CatalogUtil;
 import org.apache.iceberg.catalog.Catalog;
 import org.apache.iceberg.hadoop.HadoopCatalog;
 import org.apache.iceberg.hive.HiveCatalog;
+import org.apache.iceberg.io.FileIO;
+import org.apache.iceberg.jdbc.JdbcCatalog;
+import org.apache.iceberg.jdbc.JdbcClientPool;
+import org.apache.nifi.dbcp.DBCPService;
 import org.apache.nifi.services.iceberg.IcebergCatalogProperty;
 import org.apache.nifi.services.iceberg.IcebergCatalogService;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Function;
 
 import static 
org.apache.nifi.processors.iceberg.IcebergUtils.getConfigurationFromFiles;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.CATALOG_NAME;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.FILE_IO_IMPLEMENTATION;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.CLIENT_POOL_SERVICE;
 import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.METASTORE_URI;
 import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.WAREHOUSE_LOCATION;
 
@@ -44,6 +53,7 @@ public class IcebergCatalogFactory {
         return switch (catalogService.getCatalogType()) {
             case HIVE -> initHiveCatalog(catalogService);
             case HADOOP -> initHadoopCatalog(catalogService);
+            case JDBC -> initJdbcCatalog(catalogService);
         };
     }
 
@@ -55,15 +65,15 @@ public class IcebergCatalogFactory {
             catalog.setConf(configuration);
         }
 
-        final Map<IcebergCatalogProperty, String> catalogProperties = 
catalogService.getCatalogProperties();
-        final Map <String, String> properties = new HashMap<>();
+        final Map<IcebergCatalogProperty, Object> catalogProperties = 
catalogService.getCatalogProperties();
+        final Map<String, String> properties = new HashMap<>();
 
         if (catalogProperties.containsKey(METASTORE_URI)) {
-            properties.put(CatalogProperties.URI, 
catalogProperties.get(METASTORE_URI));
+            properties.put(CatalogProperties.URI, (String) 
catalogProperties.get(METASTORE_URI));
         }
 
         if (catalogProperties.containsKey(WAREHOUSE_LOCATION)) {
-            properties.put(CatalogProperties.WAREHOUSE_LOCATION, 
catalogProperties.get(WAREHOUSE_LOCATION));
+            properties.put(CatalogProperties.WAREHOUSE_LOCATION, (String) 
catalogProperties.get(WAREHOUSE_LOCATION));
         }
 
         catalog.initialize("hive-catalog", properties);
@@ -71,8 +81,8 @@ public class IcebergCatalogFactory {
     }
 
     private Catalog initHadoopCatalog(IcebergCatalogService catalogService) {
-        final Map<IcebergCatalogProperty, String> catalogProperties = 
catalogService.getCatalogProperties();
-        final String warehousePath = catalogProperties.get(WAREHOUSE_LOCATION);
+        final Map<IcebergCatalogProperty, Object> catalogProperties = 
catalogService.getCatalogProperties();
+        final String warehousePath = (String) 
catalogProperties.get(WAREHOUSE_LOCATION);
 
         if (catalogService.getConfigFilePaths() != null) {
             return new 
HadoopCatalog(getConfigurationFromFiles(catalogService.getConfigFilePaths()), 
warehousePath);
@@ -80,4 +90,22 @@ public class IcebergCatalogFactory {
             return new HadoopCatalog(new Configuration(), warehousePath);
         }
     }
+
+    private Catalog initJdbcCatalog(IcebergCatalogService catalogService) {
+        final Map<IcebergCatalogProperty, Object> catalogProperties = 
catalogService.getCatalogProperties();
+        final Map<String, String> properties = new HashMap<>();
+        properties.put(CatalogProperties.URI, "");
+        properties.put(CatalogProperties.WAREHOUSE_LOCATION, (String) 
catalogProperties.get(WAREHOUSE_LOCATION));
+
+        final Configuration configuration = 
getConfigurationFromFiles(catalogService.getConfigFilePaths());
+        final DBCPService dbcpService = (DBCPService) 
catalogProperties.get(CLIENT_POOL_SERVICE);
+
+        final Function<Map<String, String>, JdbcClientPool> clientPoolBuilder 
= props -> new IcebergJdbcClientPool(props, dbcpService);
+        final Function<Map<String, String>, FileIO> ioBuilder = props -> 
CatalogUtil.loadFileIO((String) catalogProperties.get(FILE_IO_IMPLEMENTATION), 
props, configuration);
+
+        JdbcCatalog catalog = new JdbcCatalog(ioBuilder, clientPoolBuilder, 
false);
+        catalog.setConf(configuration);
+        catalog.initialize((String) catalogProperties.get(CATALOG_NAME), 
properties);
+        return catalog;
+    }
 }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergJdbcClientPool.java
similarity index 60%
copy from 
nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
copy to 
nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergJdbcClientPool.java
index 56e595d2e9..1b3fa0f673 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-common/src/main/java/org/apache/nifi/processors/iceberg/catalog/IcebergJdbcClientPool.java
@@ -15,21 +15,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.services.iceberg;
+package org.apache.nifi.processors.iceberg.catalog;
 
-import org.apache.nifi.controller.ControllerService;
+import org.apache.iceberg.jdbc.JdbcClientPool;
+import org.apache.nifi.dbcp.DBCPService;
 
-import java.util.List;
+import java.sql.Connection;
 import java.util.Map;
 
-/**
- * Provides a basic connector to Iceberg catalog services.
- */
-public interface IcebergCatalogService extends ControllerService {
+public class IcebergJdbcClientPool extends JdbcClientPool {
+
+    private final DBCPService dbcpService;
 
-    IcebergCatalogType getCatalogType();
+    public IcebergJdbcClientPool(Map<String, String> properties, DBCPService 
dbcpService) {
+        super("", properties);
+        this.dbcpService = dbcpService;
+    }
 
-    Map<IcebergCatalogProperty, String> getCatalogProperties();
+    @Override
+    protected Connection newClient() {
+        return dbcpService.getConnection();
+    }
 
-    List<String> getConfigFilePaths();
 }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/pom.xml 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/pom.xml
index 1ccf8f2c8d..e16dd764b8 100644
--- a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/pom.xml
+++ b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/pom.xml
@@ -318,6 +318,18 @@
             <artifactId>nifi-kerberos-user-service-api</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-iceberg-services</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-dbcp-service</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/AbstractTestPutIceberg.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/AbstractTestPutIceberg.java
new file mode 100644
index 0000000000..cd1c5f37c8
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/AbstractTestPutIceberg.java
@@ -0,0 +1,97 @@
+/*
+ * 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.nifi.processors.iceberg;
+
+import org.apache.avro.Schema;
+import org.apache.commons.io.IOUtils;
+import org.apache.iceberg.catalog.Catalog;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.types.Types;
+import org.apache.nifi.avro.AvroTypeUtil;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.serialization.record.MockRecordParser;
+import org.apache.nifi.serialization.record.RecordField;
+import org.apache.nifi.serialization.record.RecordSchema;
+import org.apache.nifi.util.TestRunner;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.NAMESPACE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.RECORD_READER_SERVICE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.createTemporaryDirectory;
+
+public class AbstractTestPutIceberg {
+
+    protected static final String TABLE_NAME = "users";
+
+    protected static final TableIdentifier TABLE_IDENTIFIER = 
TableIdentifier.of(NAMESPACE, TABLE_NAME);
+
+    protected static final org.apache.iceberg.Schema USER_SCHEMA = new 
org.apache.iceberg.Schema(
+            Types.NestedField.required(1, "id", Types.IntegerType.get()),
+            Types.NestedField.required(2, "name", Types.StringType.get()),
+            Types.NestedField.required(3, "department", Types.StringType.get())
+    );
+
+    protected TestRunner runner;
+    protected PutIceberg processor;
+    protected Catalog catalog;
+    protected String warehousePath;
+    protected static Schema inputSchema;
+
+    protected void initRecordReader() throws InitializationException {
+        final MockRecordParser readerFactory = new MockRecordParser();
+        final RecordSchema recordSchema = 
AvroTypeUtil.createSchema(inputSchema);
+
+        for (RecordField recordField : recordSchema.getFields()) {
+            readerFactory.addSchemaField(recordField);
+        }
+
+        readerFactory.addRecord(0, "John", "Finance");
+        readerFactory.addRecord(1, "Jill", "Finance");
+        readerFactory.addRecord(2, "James", "Marketing");
+        readerFactory.addRecord(3, "Joana", "Sales");
+
+        runner.addControllerService(RECORD_READER_SERVICE, readerFactory);
+        runner.enableControllerService(readerFactory);
+
+        runner.setProperty(PutIceberg.RECORD_READER, RECORD_READER_SERVICE);
+    }
+
+    @BeforeAll
+    public static void initSchema() throws IOException {
+        final String avroSchema = 
IOUtils.toString(Files.newInputStream(Paths.get("src/test/resources/user.avsc")),
 StandardCharsets.UTF_8);
+        inputSchema = new Schema.Parser().parse(avroSchema);
+    }
+
+    @BeforeEach
+    public void setUp() {
+        warehousePath = createTemporaryDirectory().getAbsolutePath();
+        processor = new PutIceberg();
+    }
+
+    @AfterEach
+    public void tearDown() {
+        catalog.dropTable(TABLE_IDENTIFIER);
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestDataFileActions.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestDataFileActions.java
index e3b11a4874..6bdd7dd5f7 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestDataFileActions.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestDataFileActions.java
@@ -17,25 +17,24 @@
  */
 package org.apache.nifi.processors.iceberg;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.iceberg.AppendFiles;
 import org.apache.iceberg.DataFile;
 import org.apache.iceberg.FileFormat;
 import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.Table;
-import org.apache.iceberg.catalog.Catalog;
 import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.catalog.TableIdentifier;
 import org.apache.iceberg.data.Record;
 import org.apache.iceberg.exceptions.CommitFailedException;
+import org.apache.iceberg.hadoop.HadoopCatalog;
 import org.apache.iceberg.io.TaskWriter;
 import org.apache.iceberg.io.WriteResult;
 import org.apache.iceberg.types.Types;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.ProcessContext;
-import org.apache.nifi.processors.iceberg.catalog.IcebergCatalogFactory;
-import org.apache.nifi.processors.iceberg.catalog.TestHadoopCatalogService;
 import org.apache.nifi.processors.iceberg.converter.IcebergRecordConverter;
 import org.apache.nifi.processors.iceberg.writer.IcebergTaskWriterFactory;
 import org.apache.nifi.serialization.SimpleRecordSchema;
@@ -52,6 +51,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.mockito.Mockito;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
@@ -64,6 +64,7 @@ import static 
org.apache.nifi.processors.iceberg.PutIceberg.MAXIMUM_COMMIT_DURAT
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.MAXIMUM_COMMIT_WAIT_TIME;
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.MINIMUM_COMMIT_WAIT_TIME;
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.NUMBER_OF_COMMIT_RETRIES;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.createTemporaryDirectory;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.condition.OS.WINDOWS;
 import static org.mockito.Mockito.doThrow;
@@ -196,10 +197,9 @@ public class TestDataFileActions {
         verify(appender, times(2)).commit();
     }
 
-    private Table initCatalog() throws IOException {
-        TestHadoopCatalogService catalogService = new 
TestHadoopCatalogService();
-        IcebergCatalogFactory catalogFactory = new 
IcebergCatalogFactory(catalogService);
-        Catalog catalog = catalogFactory.create();
+    private Table initCatalog() {
+        final File warehousePath = createTemporaryDirectory();
+        final HadoopCatalog catalog = new HadoopCatalog(new Configuration(), 
warehousePath.getAbsolutePath());
 
         return catalog.createTable(TABLE_IDENTIFIER, ABORT_SCHEMA, 
PartitionSpec.unpartitioned());
     }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergCustomValidation.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergCustomValidation.java
index d6cf5bb91a..f1ab1d3983 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergCustomValidation.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergCustomValidation.java
@@ -18,17 +18,17 @@
 package org.apache.nifi.processors.iceberg;
 
 import org.apache.nifi.kerberos.KerberosUserService;
-import org.apache.nifi.processors.iceberg.catalog.TestHiveCatalogService;
 import org.apache.nifi.reporting.InitializationException;
 import org.apache.nifi.serialization.record.MockRecordParser;
+import org.apache.nifi.services.iceberg.HiveCatalogService;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.util.Collections;
-import java.util.List;
-
+import static 
org.apache.nifi.services.iceberg.AbstractCatalogService.HADOOP_CONFIGURATION_RESOURCES;
+import static 
org.apache.nifi.services.iceberg.HiveCatalogService.METASTORE_URI;
+import static 
org.apache.nifi.services.iceberg.HiveCatalogService.WAREHOUSE_LOCATION;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -58,10 +58,13 @@ public class TestPutIcebergCustomValidation {
         runner.setProperty(PutIceberg.RECORD_READER, RECORD_READER_NAME);
     }
 
-    private void initCatalogService(List<String> configFilePaths) throws 
InitializationException {
-        TestHiveCatalogService catalogService = new 
TestHiveCatalogService.Builder().withConfigFilePaths(configFilePaths).build();
-
+    private void initCatalogService(String configFilePaths) throws 
InitializationException {
+        final HiveCatalogService catalogService = new HiveCatalogService();
         runner.addControllerService(CATALOG_SERVICE_NAME, catalogService);
+        runner.setProperty(catalogService, METASTORE_URI, "test-metastore");
+        runner.setProperty(catalogService, WAREHOUSE_LOCATION, 
"test-warehouse");
+        runner.setProperty(catalogService, HADOOP_CONFIGURATION_RESOURCES, 
configFilePaths);
+
         runner.enableControllerService(catalogService);
 
         runner.setProperty(PutIceberg.CATALOG, CATALOG_SERVICE_NAME);
@@ -80,7 +83,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void 
testCustomValidateWithKerberosSecurityConfigAndWithoutKerberosUserService() 
throws InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/secured-core-site.xml"));
+        initCatalogService("src/test/resources/secured-core-site.xml");
 
         runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAMESPACE);
         runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
@@ -90,7 +93,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void 
testCustomValidateWithKerberosSecurityConfigAndKerberosUserService() throws 
InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/secured-core-site.xml"));
+        initCatalogService("src/test/resources/secured-core-site.xml");
 
         initKerberosUserService();
 
@@ -102,7 +105,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void 
testCustomValidateWithoutKerberosSecurityConfigAndKerberosUserService() throws 
InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/unsecured-core-site.xml"));
+        initCatalogService("src/test/resources/unsecured-core-site.xml");
 
         runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAMESPACE);
         runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
@@ -112,7 +115,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void 
testCustomValidateWithoutKerberosSecurityConfigAndWithKerberosUserService() 
throws InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/unsecured-core-site.xml"));
+        initCatalogService("src/test/resources/unsecured-core-site.xml");
 
         initKerberosUserService();
 
@@ -124,7 +127,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void testInvalidSnapshotSummaryDynamicProperty() throws 
InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/unsecured-core-site.xml"));
+        initCatalogService("src/test/resources/unsecured-core-site.xml");
 
         runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAMESPACE);
         runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
@@ -136,7 +139,7 @@ public class TestPutIcebergCustomValidation {
     @Test
     public void testValidSnapshotSummaryDynamicProperty() throws 
InitializationException {
         initRecordReader();
-        
initCatalogService(Collections.singletonList("src/test/resources/unsecured-core-site.xml"));
+        initCatalogService("src/test/resources/unsecured-core-site.xml");
 
         runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAMESPACE);
         runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHadoopCatalog.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHadoopCatalog.java
index 484dfc79cc..6d120ea2b5 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHadoopCatalog.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHadoopCatalog.java
@@ -24,24 +24,25 @@ import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Table;
 import org.apache.iceberg.TableProperties;
 import org.apache.iceberg.catalog.Catalog;
-import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.catalog.TableIdentifier;
 import org.apache.iceberg.types.Types;
 import org.apache.nifi.avro.AvroTypeUtil;
 import org.apache.nifi.processors.iceberg.catalog.IcebergCatalogFactory;
-import org.apache.nifi.processors.iceberg.catalog.TestHadoopCatalogService;
 import org.apache.nifi.reporting.InitializationException;
 import org.apache.nifi.serialization.record.MockRecordParser;
 import org.apache.nifi.serialization.record.RecordField;
 import org.apache.nifi.serialization.record.RecordSchema;
+import org.apache.nifi.services.iceberg.HadoopCatalogService;
 import org.apache.nifi.util.MockFlowFile;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -54,20 +55,21 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.ICEBERG_RECORD_COUNT;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.CATALOG_NAME;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.CATALOG_SERVICE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.NAMESPACE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.RECORD_READER_SERVICE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.createTemporaryDirectory;
 import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validateNumberOfDataFiles;
 import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validatePartitionFolders;
+import static 
org.apache.nifi.services.iceberg.HadoopCatalogService.WAREHOUSE_PATH;
 import static org.junit.jupiter.api.condition.OS.WINDOWS;
 
 public class TestPutIcebergWithHadoopCatalog {
 
-    private TestRunner runner;
-    private PutIceberg processor;
-    private Schema inputSchema;
-    private Catalog catalog;
+    protected static final String TABLE_NAME = "date_test";
 
-    private static final Namespace NAMESPACE = Namespace.of("default");
-
-    private static final TableIdentifier TABLE_IDENTIFIER = 
TableIdentifier.of(NAMESPACE, "date");
+    private static final TableIdentifier TABLE_IDENTIFIER = 
TableIdentifier.of(NAMESPACE, TABLE_NAME);
 
     private static final org.apache.iceberg.Schema DATE_SCHEMA = new 
org.apache.iceberg.Schema(
             Types.NestedField.required(1, "timeMicros", Types.TimeType.get()),
@@ -75,17 +77,26 @@ public class TestPutIcebergWithHadoopCatalog {
             Types.NestedField.required(3, "date", Types.DateType.get())
     );
 
-    @BeforeEach
-    public void setUp() throws Exception {
-        String avroSchema = 
IOUtils.toString(Files.newInputStream(Paths.get("src/test/resources/date.avsc")),
 StandardCharsets.UTF_8);
+    private TestRunner runner;
+    private PutIceberg processor;
+    private static Schema inputSchema;
+    private Catalog catalog;
+
+
+    @BeforeAll
+    public static void initSchema() throws IOException {
+        final String avroSchema = 
IOUtils.toString(Files.newInputStream(Paths.get("src/test/resources/date.avsc")),
 StandardCharsets.UTF_8);
         inputSchema = new Schema.Parser().parse(avroSchema);
+    }
 
+    @BeforeEach
+    public void setUp() {
         processor = new PutIceberg();
     }
 
     private void initRecordReader() throws InitializationException {
-        MockRecordParser readerFactory = new MockRecordParser();
-        RecordSchema recordSchema = AvroTypeUtil.createSchema(inputSchema);
+        final MockRecordParser readerFactory = new MockRecordParser();
+        final RecordSchema recordSchema = 
AvroTypeUtil.createSchema(inputSchema);
 
         for (RecordField recordField : recordSchema.getFields()) {
             readerFactory.addSchemaField(recordField.getFieldName(), 
recordField.getDataType().getFieldType(), recordField.isNullable());
@@ -96,27 +107,28 @@ public class TestPutIcebergWithHadoopCatalog {
         readerFactory.addRecord(Time.valueOf("15:30:30"), 
Timestamp.valueOf("2016-01-02 15:30:30.800"), Date.valueOf("2016-01-02"));
         readerFactory.addRecord(Time.valueOf("15:30:30"), 
Timestamp.valueOf("2017-01-10 15:30:30.800"), Date.valueOf("2017-01-10"));
 
-        runner.addControllerService("mock-reader-factory", readerFactory);
+        runner.addControllerService(RECORD_READER_SERVICE, readerFactory);
         runner.enableControllerService(readerFactory);
 
-        runner.setProperty(PutIceberg.RECORD_READER, "mock-reader-factory");
+        runner.setProperty(PutIceberg.RECORD_READER, RECORD_READER_SERVICE);
     }
 
-    private void initCatalog(PartitionSpec spec, FileFormat fileFormat) throws 
InitializationException, IOException {
-        TestHadoopCatalogService catalogService = new 
TestHadoopCatalogService();
-        IcebergCatalogFactory catalogFactory = new 
IcebergCatalogFactory(catalogService);
+    private void initCatalog(PartitionSpec spec, FileFormat fileFormat) throws 
InitializationException {
+        final File warehousePath = createTemporaryDirectory();
+        final HadoopCatalogService catalogService = new HadoopCatalogService();
+        runner.addControllerService(CATALOG_SERVICE, catalogService);
+        runner.setProperty(catalogService, WAREHOUSE_PATH, 
warehousePath.getAbsolutePath());
+        runner.enableControllerService(catalogService);
+
+        final IcebergCatalogFactory catalogFactory = new 
IcebergCatalogFactory(catalogService);
         catalog = catalogFactory.create();
 
-        Map<String, String> tableProperties = new HashMap<>();
-        tableProperties.put(TableProperties.FORMAT_VERSION, "2");
+        final Map<String, String> tableProperties = new HashMap<>();
         tableProperties.put(TableProperties.DEFAULT_FILE_FORMAT, 
fileFormat.name());
 
         catalog.createTable(TABLE_IDENTIFIER, DATE_SCHEMA, spec, 
tableProperties);
 
-        runner.addControllerService("catalog-service", catalogService);
-        runner.enableControllerService(catalogService);
-
-        runner.setProperty(PutIceberg.CATALOG, "catalog-service");
+        runner.setProperty(PutIceberg.CATALOG, CATALOG_SERVICE);
     }
 
     @DisabledOnOs(WINDOWS)
@@ -129,16 +141,16 @@ public class TestPutIcebergWithHadoopCatalog {
         runner = TestRunners.newTestRunner(processor);
         initRecordReader();
         initCatalog(spec, FileFormat.PARQUET);
-        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, "default");
-        runner.setProperty(PutIceberg.TABLE_NAME, "date");
+        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAME);
+        runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
         runner.setValidateExpressionUsage(false);
         runner.enqueue(new byte[0]);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
         Assertions.assertTrue(table.spec().isPartitioned());
         Assertions.assertEquals("4", 
flowFile.getAttribute(ICEBERG_RECORD_COUNT));
@@ -149,23 +161,23 @@ public class TestPutIcebergWithHadoopCatalog {
     @DisabledOnOs(WINDOWS)
     @Test
     public void onTriggerMonthTransform() throws Exception {
-        PartitionSpec spec = PartitionSpec.builderFor(DATE_SCHEMA)
+        final PartitionSpec spec = PartitionSpec.builderFor(DATE_SCHEMA)
                 .month("timestampMicros")
                 .build();
 
         runner = TestRunners.newTestRunner(processor);
         initRecordReader();
         initCatalog(spec, FileFormat.ORC);
-        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, "default");
-        runner.setProperty(PutIceberg.TABLE_NAME, "date");
+        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAME);
+        runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
         runner.setValidateExpressionUsage(false);
         runner.enqueue(new byte[0]);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
         Assertions.assertTrue(table.spec().isPartitioned());
         Assertions.assertEquals("4", 
flowFile.getAttribute(ICEBERG_RECORD_COUNT));
@@ -177,23 +189,23 @@ public class TestPutIcebergWithHadoopCatalog {
     @DisabledOnOs(WINDOWS)
     @Test
     public void onTriggerDayTransform() throws Exception {
-        PartitionSpec spec = PartitionSpec.builderFor(DATE_SCHEMA)
+        final PartitionSpec spec = PartitionSpec.builderFor(DATE_SCHEMA)
                 .day("timestampMicros")
                 .build();
 
         runner = TestRunners.newTestRunner(processor);
         initRecordReader();
         initCatalog(spec, FileFormat.AVRO);
-        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, "default");
-        runner.setProperty(PutIceberg.TABLE_NAME, "date");
+        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAME);
+        runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
         runner.setValidateExpressionUsage(false);
         runner.enqueue(new byte[0]);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
         Assertions.assertTrue(table.spec().isPartitioned());
         Assertions.assertEquals("4", 
flowFile.getAttribute(ICEBERG_RECORD_COUNT));
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHiveCatalog.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHiveCatalog.java
index d2b942cf58..21ff765c1f 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHiveCatalog.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithHiveCatalog.java
@@ -17,41 +17,25 @@
  */
 package org.apache.nifi.processors.iceberg;
 
-import org.apache.avro.Schema;
-import org.apache.commons.io.IOUtils;
 import org.apache.iceberg.FileFormat;
 import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Table;
 import org.apache.iceberg.TableProperties;
-import org.apache.iceberg.catalog.Catalog;
-import org.apache.iceberg.catalog.Namespace;
-import org.apache.iceberg.catalog.TableIdentifier;
 import org.apache.iceberg.data.Record;
-import org.apache.iceberg.types.Types;
-import org.apache.nifi.avro.AvroTypeUtil;
 import org.apache.nifi.hive.metastore.ThriftMetastore;
 import org.apache.nifi.processors.iceberg.catalog.IcebergCatalogFactory;
-import org.apache.nifi.processors.iceberg.catalog.TestHiveCatalogService;
 import org.apache.nifi.processors.iceberg.util.IcebergTestUtils;
 import org.apache.nifi.provenance.ProvenanceEventRecord;
 import org.apache.nifi.provenance.ProvenanceEventType;
 import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.serialization.record.MockRecordParser;
-import org.apache.nifi.serialization.record.RecordField;
-import org.apache.nifi.serialization.record.RecordSchema;
+import org.apache.nifi.services.iceberg.HiveCatalogService;
 import org.apache.nifi.util.MockFlowFile;
-import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledOnOs;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -60,130 +44,43 @@ import java.util.Map;
 
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.ICEBERG_RECORD_COUNT;
 import static 
org.apache.nifi.processors.iceberg.PutIceberg.ICEBERG_SNAPSHOT_SUMMARY_FLOWFILE_UUID;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.CATALOG_NAME;
 import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validateData;
 import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validateNumberOfDataFiles;
 import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validatePartitionFolders;
+import static 
org.apache.nifi.services.iceberg.HiveCatalogService.METASTORE_URI;
+import static 
org.apache.nifi.services.iceberg.HiveCatalogService.WAREHOUSE_LOCATION;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.condition.OS.WINDOWS;
 
 @DisabledOnOs(WINDOWS)
-public class TestPutIcebergWithHiveCatalog {
-
-    private TestRunner runner;
-    private PutIceberg processor;
-    private Schema inputSchema;
-    private Catalog catalog;
+public class TestPutIcebergWithHiveCatalog extends AbstractTestPutIceberg {
 
     @RegisterExtension
     public static ThriftMetastore metastore = new ThriftMetastore();
 
-    private static final String CATALOG_NAME = "test_metastore";
-    private static final String TABLE_NAME = "users";
-
-    private static final Namespace NAMESPACE = Namespace.of(CATALOG_NAME);
-
-    private static final TableIdentifier TABLE_IDENTIFIER = 
TableIdentifier.of(NAMESPACE, TABLE_NAME);
-
-    private static final org.apache.iceberg.Schema USER_SCHEMA = new 
org.apache.iceberg.Schema(
-            Types.NestedField.required(1, "id", Types.IntegerType.get()),
-            Types.NestedField.required(2, "name", Types.StringType.get()),
-            Types.NestedField.required(3, "department", Types.StringType.get())
-    );
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        String avroSchema = 
IOUtils.toString(Files.newInputStream(Paths.get("src/test/resources/user.avsc")),
 StandardCharsets.UTF_8);
-        inputSchema = new Schema.Parser().parse(avroSchema);
-
-        processor = new PutIceberg();
-    }
-
-    @AfterEach
-    public void tearDown() {
-        catalog.dropTable(TABLE_IDENTIFIER);
-    }
-
-    private void initRecordReader() throws InitializationException {
-        MockRecordParser readerFactory = new MockRecordParser();
-        RecordSchema recordSchema = AvroTypeUtil.createSchema(inputSchema);
-
-        for (RecordField recordField : recordSchema.getFields()) {
-            readerFactory.addSchemaField(recordField);
-        }
-
-        readerFactory.addRecord(0, "John", "Finance");
-        readerFactory.addRecord(1, "Jill", "Finance");
-        readerFactory.addRecord(2, "James", "Marketing");
-        readerFactory.addRecord(3, "Joana", "Sales");
-
-        runner.addControllerService("mock-reader-factory", readerFactory);
-        runner.enableControllerService(readerFactory);
-
-        runner.setProperty(PutIceberg.RECORD_READER, "mock-reader-factory");
-    }
-
     private void initCatalog(PartitionSpec spec, FileFormat fileFormat) throws 
InitializationException {
-        Map<String, String> tableProperties = new HashMap<>();
-        tableProperties.put(TableProperties.FORMAT_VERSION, "2");
+        final Map<String, String> tableProperties = new HashMap<>();
         tableProperties.put(TableProperties.DEFAULT_FILE_FORMAT, 
fileFormat.name());
 
-        TestHiveCatalogService catalogService = new 
TestHiveCatalogService.Builder()
-                .withMetastoreUri(metastore.getThriftConnectionUri())
-                .withWarehouseLocation(metastore.getWarehouseLocation())
-                .build();
+        final HiveCatalogService catalogService = new HiveCatalogService();
+        runner.addControllerService("catalog-service", catalogService);
+        runner.setProperty(catalogService, METASTORE_URI, 
metastore.getThriftConnectionUri());
+        runner.setProperty(catalogService, WAREHOUSE_LOCATION, warehousePath);
+        runner.enableControllerService(catalogService);
 
-        IcebergCatalogFactory catalogFactory = new 
IcebergCatalogFactory(catalogService);
+        final IcebergCatalogFactory catalogFactory = new 
IcebergCatalogFactory(catalogService);
         catalog = catalogFactory.create();
 
         catalog.createTable(TABLE_IDENTIFIER, USER_SCHEMA, spec, 
tableProperties);
 
-        runner.addControllerService("catalog-service", catalogService);
-        runner.enableControllerService(catalogService);
-
         runner.setProperty(PutIceberg.CATALOG, "catalog-service");
     }
 
-    @Test
-    public void onTriggerPartitioned() throws Exception {
-        PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
-                .bucket("department", 3)
-                .build();
-
-        runner = TestRunners.newTestRunner(processor);
-        initRecordReader();
-        initCatalog(spec, FileFormat.AVRO);
-        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAME);
-        runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
-        runner.setValidateExpressionUsage(false);
-        runner.enqueue(new byte[0]);
-        runner.run();
-
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
-
-        List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
-                .add(0, "John", "Finance")
-                .add(1, "Jill", "Finance")
-                .add(2, "James", "Marketing")
-                .add(3, "Joana", "Sales")
-                .build();
-
-        runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
-
-        String tableLocation = new URI(table.location()).getPath();
-        assertTrue(table.spec().isPartitioned());
-        assertEquals("4", flowFile.getAttribute(ICEBERG_RECORD_COUNT));
-        validateData(table, expectedRecords, 0);
-        validateNumberOfDataFiles(tableLocation, 3);
-        validatePartitionFolders(tableLocation, Arrays.asList(
-                "department_bucket=0", "department_bucket=1", 
"department_bucket=2"));
-        assertProvenanceEvents();
-    }
-
     @Test
     public void onTriggerIdentityPartitioned() throws Exception {
-        PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
+        final PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
                 .identity("department")
                 .build();
 
@@ -196,9 +93,9 @@ public class TestPutIcebergWithHiveCatalog {
         runner.enqueue(new byte[0]);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
-        List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
+        final List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
                 .add(0, "John", "Finance")
                 .add(1, "Jill", "Finance")
                 .add(2, "James", "Marketing")
@@ -206,9 +103,9 @@ public class TestPutIcebergWithHiveCatalog {
                 .build();
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
-        String tableLocation = new URI(table.location()).getPath();
+        final String tableLocation = new URI(table.location()).getPath();
         assertTrue(table.spec().isPartitioned());
         assertEquals("4", flowFile.getAttribute(ICEBERG_RECORD_COUNT));
         validateData(table, expectedRecords, 0);
@@ -220,7 +117,7 @@ public class TestPutIcebergWithHiveCatalog {
 
     @Test
     public void onTriggerMultiLevelIdentityPartitioned() throws Exception {
-        PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
+        final PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
                 .identity("name")
                 .identity("department")
                 .build();
@@ -234,9 +131,9 @@ public class TestPutIcebergWithHiveCatalog {
         runner.enqueue(new byte[0]);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
-        List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
+        final List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
                 .add(0, "John", "Finance")
                 .add(1, "Jill", "Finance")
                 .add(2, "James", "Marketing")
@@ -244,9 +141,9 @@ public class TestPutIcebergWithHiveCatalog {
                 .build();
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
-        String tableLocation = new URI(table.location()).getPath();
+        final String tableLocation = new URI(table.location()).getPath();
         assertTrue(table.spec().isPartitioned());
         assertEquals("4", flowFile.getAttribute(ICEBERG_RECORD_COUNT));
         validateData(table, expectedRecords, 0);
@@ -269,16 +166,16 @@ public class TestPutIcebergWithHiveCatalog {
         runner.setProperty(PutIceberg.TABLE_NAME, "${table.name}");
         runner.setProperty(PutIceberg.MAXIMUM_FILE_SIZE, "${max.filesize}");
         runner.setProperty("snapshot-property.additional-summary-property", 
"test summary property");
-        Map<String, String> attributes = new HashMap<>();
+        final Map<String, String> attributes = new HashMap<>();
         attributes.put("catalog.name", CATALOG_NAME);
         attributes.put("table.name", TABLE_NAME);
         attributes.put("max.filesize", "536870912"); // 512 MB
         runner.enqueue(new byte[0], attributes);
         runner.run();
 
-        Table table = catalog.loadTable(TABLE_IDENTIFIER);
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
 
-        List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
+        final List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
                 .add(0, "John", "Finance")
                 .add(1, "Jill", "Finance")
                 .add(2, "James", "Marketing")
@@ -286,7 +183,7 @@ public class TestPutIcebergWithHiveCatalog {
                 .build();
 
         runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
-        MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
 
         assertTrue(table.spec().isUnpartitioned());
         assertEquals("4", flowFile.getAttribute(ICEBERG_RECORD_COUNT));
@@ -305,7 +202,7 @@ public class TestPutIcebergWithHiveCatalog {
     }
 
     private void assertSnapshotSummaryProperties(Table table, Map<String, 
String> summaryProperties) {
-        Map<String, String> snapshotSummary = 
table.currentSnapshot().summary();
+        final Map<String, String> snapshotSummary = 
table.currentSnapshot().summary();
 
         
assertTrue(snapshotSummary.containsKey(ICEBERG_SNAPSHOT_SUMMARY_FLOWFILE_UUID));
 
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithJdbcCatalog.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithJdbcCatalog.java
new file mode 100644
index 0000000000..c03aff304b
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/TestPutIcebergWithJdbcCatalog.java
@@ -0,0 +1,171 @@
+/*
+ * 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.nifi.processors.iceberg;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.iceberg.CatalogProperties;
+import org.apache.iceberg.CatalogUtil;
+import org.apache.iceberg.FileFormat;
+import org.apache.iceberg.PartitionSpec;
+import org.apache.iceberg.Table;
+import org.apache.iceberg.TableProperties;
+import org.apache.iceberg.data.Record;
+import org.apache.iceberg.io.FileIO;
+import org.apache.iceberg.jdbc.JdbcCatalog;
+import org.apache.iceberg.jdbc.JdbcClientPool;
+import org.apache.nifi.dbcp.DBCPConnectionPool;
+import org.apache.nifi.processors.iceberg.catalog.IcebergJdbcClientPool;
+import org.apache.nifi.processors.iceberg.util.IcebergTestUtils;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.services.iceberg.JdbcCatalogService;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static org.apache.nifi.dbcp.utils.DBCPProperties.DATABASE_URL;
+import static org.apache.nifi.dbcp.utils.DBCPProperties.DB_DRIVERNAME;
+import static org.apache.nifi.dbcp.utils.DBCPProperties.DB_PASSWORD;
+import static org.apache.nifi.dbcp.utils.DBCPProperties.DB_USER;
+import static 
org.apache.nifi.processors.iceberg.PutIceberg.ICEBERG_RECORD_COUNT;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.CATALOG_NAME;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.CATALOG_SERVICE;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.createTemporaryDirectory;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.getSystemTemporaryDirectory;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validateData;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validateNumberOfDataFiles;
+import static 
org.apache.nifi.processors.iceberg.util.IcebergTestUtils.validatePartitionFolders;
+import static 
org.apache.nifi.services.iceberg.JdbcCatalogService.CONNECTION_POOL;
+import static 
org.apache.nifi.services.iceberg.JdbcCatalogService.WAREHOUSE_PATH;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.condition.OS.WINDOWS;
+
+@DisabledOnOs(WINDOWS)
+public class TestPutIcebergWithJdbcCatalog extends AbstractTestPutIceberg {
+
+    private static final String DERBY_LOG_PROPERTY = "derby.stream.error.file";
+    private static final String CONNECTION_POOL_SERVICE = 
"connection-pool-service";
+
+    private static DBCPConnectionPool connectionPool;
+
+    @BeforeAll
+    public static void initConnectionPool() {
+        setDerbyLog();
+        connectionPool = new DBCPConnectionPool();
+    }
+
+    @AfterAll
+    public static void clearDerbyLog() {
+        System.clearProperty(DERBY_LOG_PROPERTY);
+    }
+
+    private void initServices(PartitionSpec spec) throws 
InitializationException {
+        initDBCPService();
+        initRecordReader();
+        initCatalogService();
+        createTestTable(spec);
+    }
+
+    private void initCatalogService() throws InitializationException {
+        final JdbcCatalogService catalogService = new JdbcCatalogService();
+        runner.addControllerService(CATALOG_SERVICE, catalogService);
+        runner.setProperty(catalogService, CONNECTION_POOL, 
CONNECTION_POOL_SERVICE);
+        runner.setProperty(catalogService, WAREHOUSE_PATH, warehousePath);
+        runner.enableControllerService(catalogService);
+        runner.setProperty(PutIceberg.CATALOG, CATALOG_SERVICE);
+    }
+
+    private void initDBCPService() throws InitializationException {
+        final String url = String.format("jdbc:derby:%s;create=true", 
createTemporaryDirectory());
+        runner.addControllerService(CONNECTION_POOL_SERVICE, connectionPool);
+        runner.setProperty(connectionPool, DATABASE_URL, url);
+        runner.setProperty(connectionPool, DB_USER, 
String.class.getSimpleName());
+        runner.setProperty(connectionPool, DB_PASSWORD, 
String.class.getName());
+        runner.setProperty(connectionPool, DB_DRIVERNAME, 
"org.apache.derby.jdbc.EmbeddedDriver");
+        runner.enableControllerService(connectionPool);
+    }
+
+    private void createTestTable(PartitionSpec spec) {
+        final Map<String, String> properties = new HashMap<>();
+        properties.put(CatalogProperties.URI, "");
+        properties.put(CatalogProperties.WAREHOUSE_LOCATION, warehousePath);
+
+        final Function<Map<String, String>, JdbcClientPool> clientPoolBuilder 
= props -> new IcebergJdbcClientPool(props, connectionPool);
+        final Function<Map<String, String>, FileIO> ioBuilder = props -> 
CatalogUtil.loadFileIO("org.apache.iceberg.hadoop.HadoopFileIO", props, new 
Configuration());
+
+        catalog = new JdbcCatalog(ioBuilder, clientPoolBuilder, true);
+        catalog.initialize("jdbc-catalog", properties);
+
+        final Map<String, String> tableProperties = new HashMap<>();
+        tableProperties.put(TableProperties.DEFAULT_FILE_FORMAT, 
FileFormat.PARQUET.name());
+
+        catalog.createTable(TABLE_IDENTIFIER, USER_SCHEMA, spec, 
tableProperties);
+    }
+
+    public static void setDerbyLog() {
+        final File derbyLog = new File(getSystemTemporaryDirectory(), 
"derby.log");
+        derbyLog.deleteOnExit();
+        System.setProperty(DERBY_LOG_PROPERTY, derbyLog.getAbsolutePath());
+    }
+
+    @Test
+    public void onTriggerBucketPartitioned() throws Exception {
+        final PartitionSpec spec = PartitionSpec.builderFor(USER_SCHEMA)
+                .bucket("department", 3)
+                .build();
+
+        runner = TestRunners.newTestRunner(processor);
+        runner.setValidateExpressionUsage(false);
+        initServices(spec);
+        runner.setProperty(PutIceberg.CATALOG_NAMESPACE, CATALOG_NAME);
+        runner.setProperty(PutIceberg.TABLE_NAME, TABLE_NAME);
+        runner.enqueue(new byte[0]);
+        runner.run();
+
+        final Table table = catalog.loadTable(TABLE_IDENTIFIER);
+
+        final List<Record> expectedRecords = 
IcebergTestUtils.RecordsBuilder.newInstance(USER_SCHEMA)
+                .add(0, "John", "Finance")
+                .add(1, "Jill", "Finance")
+                .add(2, "James", "Marketing")
+                .add(3, "Joana", "Sales")
+                .build();
+
+        runner.assertTransferCount(PutIceberg.REL_SUCCESS, 1);
+        final MockFlowFile flowFile = 
runner.getFlowFilesForRelationship(PutIceberg.REL_SUCCESS).getFirst();
+
+        final String tableLocation = new URI(table.location()).getPath();
+        assertTrue(table.spec().isPartitioned());
+        assertEquals("4", flowFile.getAttribute(ICEBERG_RECORD_COUNT));
+        validateData(table, expectedRecords, 0);
+        validateNumberOfDataFiles(tableLocation, 3);
+        validatePartitionFolders(tableLocation, Arrays.asList(
+                "department_bucket=0", "department_bucket=1", 
"department_bucket=2"));
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHadoopCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHadoopCatalogService.java
deleted file mode 100644
index 9673b894a3..0000000000
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHadoopCatalogService.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.nifi.processors.iceberg.catalog;
-
-import org.apache.nifi.controller.AbstractControllerService;
-import org.apache.nifi.services.iceberg.IcebergCatalogProperty;
-import org.apache.nifi.services.iceberg.IcebergCatalogService;
-import org.apache.nifi.services.iceberg.IcebergCatalogType;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static java.nio.file.Files.createTempDirectory;
-
-public class TestHadoopCatalogService extends AbstractControllerService 
implements IcebergCatalogService {
-
-    private final Map<IcebergCatalogProperty, String> catalogProperties = new 
HashMap<>();
-
-    public TestHadoopCatalogService() throws IOException {
-        File warehouseLocation = createTempDirectory("metastore").toFile();
-        catalogProperties.put(IcebergCatalogProperty.WAREHOUSE_LOCATION, 
warehouseLocation.getAbsolutePath());
-    }
-
-    @Override
-    public IcebergCatalogType getCatalogType() {
-        return IcebergCatalogType.HADOOP;
-    }
-
-    @Override
-    public Map<IcebergCatalogProperty, String> getCatalogProperties() {
-        return catalogProperties;
-    }
-
-    @Override
-    public List<String> getConfigFilePaths() {
-        return null;
-    }
-}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHiveCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHiveCatalogService.java
deleted file mode 100644
index 0cd7f042a8..0000000000
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/catalog/TestHiveCatalogService.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.nifi.processors.iceberg.catalog;
-
-import org.apache.nifi.controller.AbstractControllerService;
-import org.apache.nifi.services.iceberg.IcebergCatalogProperty;
-import org.apache.nifi.services.iceberg.IcebergCatalogService;
-import org.apache.nifi.services.iceberg.IcebergCatalogType;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.METASTORE_URI;
-import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.WAREHOUSE_LOCATION;
-
-public class TestHiveCatalogService extends AbstractControllerService 
implements IcebergCatalogService {
-
-    private final List<String> configFilePaths;
-    private final Map<IcebergCatalogProperty, String> catalogProperties;
-
-    public TestHiveCatalogService(Map<IcebergCatalogProperty, String> 
catalogProperties, List<String> configFilePaths) {
-        this.catalogProperties = catalogProperties;
-        this.configFilePaths = configFilePaths;
-    }
-
-    @Override
-    public IcebergCatalogType getCatalogType() {
-        return IcebergCatalogType.HIVE;
-    }
-
-    @Override
-    public Map<IcebergCatalogProperty, String> getCatalogProperties() {
-        return catalogProperties;
-    }
-
-    @Override
-    public List<String> getConfigFilePaths() {
-        return configFilePaths;
-    }
-
-    public static class Builder {
-        private String metastoreUri;
-        private String warehouseLocation;
-        private List<String> configFilePaths;
-
-        public Builder withMetastoreUri(String metastoreUri) {
-            this.metastoreUri = metastoreUri;
-            return this;
-        }
-
-        public Builder withWarehouseLocation(String warehouseLocation) {
-            this.warehouseLocation = warehouseLocation;
-            return this;
-        }
-
-        public Builder withConfigFilePaths(List<String> configFilePaths) {
-            this.configFilePaths = configFilePaths;
-            return this;
-        }
-
-        public TestHiveCatalogService build() {
-            Map<IcebergCatalogProperty, String> properties = new HashMap<>();
-
-            if (metastoreUri != null) {
-                properties.put(METASTORE_URI, metastoreUri);
-            }
-
-            if (warehouseLocation != null) {
-                properties.put(WAREHOUSE_LOCATION, warehouseLocation);
-            }
-
-            return new TestHiveCatalogService(properties, configFilePaths);
-        }
-    }
-}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/util/IcebergTestUtils.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/util/IcebergTestUtils.java
index 6a24869520..06918893ac 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/util/IcebergTestUtils.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-processors/src/test/java/org/apache/nifi/processors/iceberg/util/IcebergTestUtils.java
@@ -21,11 +21,13 @@ import com.google.common.collect.Lists;
 import org.apache.commons.lang.Validate;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.Table;
+import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.data.GenericRecord;
 import org.apache.iceberg.data.IcebergGenerics;
 import org.apache.iceberg.data.Record;
 import org.apache.iceberg.io.CloseableIterable;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -34,12 +36,19 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class IcebergTestUtils {
 
+    public static final String RECORD_READER_SERVICE = "record-reader-service";
+    public static final String CATALOG_SERVICE = "catalog-service";
+    public static final String CATALOG_NAME = "iceberg_test";
+
+    public static final Namespace NAMESPACE = Namespace.of(CATALOG_NAME);
+
     /**
      * Validates whether the table contains the expected records. The results 
should be sorted by a unique key, so we do not end up with flaky tests.
      *
@@ -99,6 +108,14 @@ public class IcebergTestUtils {
         }
     }
 
+    public static String getSystemTemporaryDirectory() {
+        return System.getProperty("java.io.tmpdir");
+    }
+
+    public static File createTemporaryDirectory() {
+        return Paths.get(getSystemTemporaryDirectory(), 
UUID.randomUUID().toString()).toFile();
+    }
+
     public static class RecordsBuilder {
 
         private final List<Record> records = new ArrayList<>();
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogProperty.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogProperty.java
index f4c55c39c8..1e4987a0a5 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogProperty.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogProperty.java
@@ -19,11 +19,20 @@ package org.apache.nifi.services.iceberg;
 
 public enum IcebergCatalogProperty {
 
+    CATALOG_NAME,
     METASTORE_URI("hive.metastore.uris"),
-    WAREHOUSE_LOCATION("hive.metastore.warehouse.dir");
+    WAREHOUSE_LOCATION("hive.metastore.warehouse.dir"),
+    CLIENT_POOL_SERVICE,
+    FILE_IO_IMPLEMENTATION;
+
+    private static final String EMPTY_STRING = "";
 
     private final String hadoopPropertyName;
 
+    IcebergCatalogProperty() {
+        this.hadoopPropertyName = EMPTY_STRING;
+    }
+
     IcebergCatalogProperty(String hadoopPropertyName) {
         this.hadoopPropertyName = hadoopPropertyName;
     }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
index 56e595d2e9..065d5290ef 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogService.java
@@ -29,7 +29,7 @@ public interface IcebergCatalogService extends 
ControllerService {
 
     IcebergCatalogType getCatalogType();
 
-    Map<IcebergCatalogProperty, String> getCatalogProperties();
+    Map<IcebergCatalogProperty, Object> getCatalogProperties();
 
     List<String> getConfigFilePaths();
 }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogType.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogType.java
index 4b8640da1d..8aad41049e 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogType.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services-api/src/main/java/org/apache/nifi/services/iceberg/IcebergCatalogType.java
@@ -19,5 +19,6 @@ package org.apache.nifi.services.iceberg;
 
 public enum IcebergCatalogType {
     HIVE,
-    HADOOP
+    HADOOP,
+    JDBC
 }
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/pom.xml 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/pom.xml
index e00410723c..dbc4a5419d 100644
--- a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/pom.xml
+++ b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/pom.xml
@@ -31,6 +31,10 @@
             <version>2.0.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-dbcp-service-api</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.nifi</groupId>
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/AbstractCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/AbstractCatalogService.java
index f658940087..7091143160 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/AbstractCatalogService.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/AbstractCatalogService.java
@@ -23,6 +23,7 @@ import org.apache.nifi.components.resource.ResourceType;
 import org.apache.nifi.controller.AbstractControllerService;
 import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
 import org.w3c.dom.Document;
 
@@ -36,17 +37,16 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-
 /**
  * Abstract class holding common properties and methods for Catalog Service 
implementations.
  */
 public abstract class AbstractCatalogService extends AbstractControllerService 
implements IcebergCatalogService {
 
-    protected Map<IcebergCatalogProperty, String> catalogProperties = new 
HashMap<>();
+    protected Map<IcebergCatalogProperty, Object> catalogProperties = new 
HashMap<>();
 
     protected List<String> configFilePaths;
 
-    static final PropertyDescriptor HADOOP_CONFIGURATION_RESOURCES = new 
PropertyDescriptor.Builder()
+    public static final PropertyDescriptor HADOOP_CONFIGURATION_RESOURCES = 
new PropertyDescriptor.Builder()
             .name("hadoop-config-resources")
             .displayName("Hadoop Configuration Resources")
             .description("A file, or comma separated list of files, which 
contain the Hadoop configuration (core-site.xml, etc.). Without this, default 
configuration will be used.")
@@ -56,6 +56,15 @@ public abstract class AbstractCatalogService extends 
AbstractControllerService i
             .dynamicallyModifiesClasspath(true)
             .build();
 
+    public static final PropertyDescriptor WAREHOUSE_PATH = new 
PropertyDescriptor.Builder()
+            .name("warehouse-path")
+            .displayName("Warehouse Path")
+            .description("Path to the location of the warehouse.")
+            .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
+            .required(true)
+            .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
+            .build();
+
     protected List<Document> parseConfigFilePaths(String configFilePaths) {
         List<Document> documentList = new ArrayList<>();
         for (final String configFile : createFilePathList(configFilePaths)) {
@@ -82,7 +91,7 @@ public abstract class AbstractCatalogService extends 
AbstractControllerService i
     }
 
     @Override
-    public Map<IcebergCatalogProperty, String> getCatalogProperties() {
+    public Map<IcebergCatalogProperty, Object> getCatalogProperties() {
         return catalogProperties;
     }
 
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/FileIOImplementation.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/FileIOImplementation.java
new file mode 100644
index 0000000000..45c3d3cfaa
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/FileIOImplementation.java
@@ -0,0 +1,68 @@
+/*
+ * 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.nifi.services.iceberg;
+
+import org.apache.nifi.components.DescribedValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum FileIOImplementation implements DescribedValue {
+    HADOOP( "org.apache.iceberg.hadoop.HadoopFileIO", "Hadoop File IO"),
+    RESOLVING("org.apache.iceberg.io.ResolvingFileIO", "Resolving File IO"),
+    S3( "org.apache.iceberg.aws.s3.S3FileIO", "S3 File IO"),
+    GCS( "org.apache.iceberg.gcp.gcs.GCSFileIO", "GCS File IO"),
+    ADLS( "org.apache.iceberg.azure.adlsv2.ADLSFileIO", "ADLS File IO");
+
+    private static final Map<String, FileIOImplementation> ENUM_MAP = new 
HashMap<>();
+
+    static {
+        for (FileIOImplementation strategy : FileIOImplementation.values()) {
+            ENUM_MAP.put(strategy.getValue(), strategy);
+        }
+    }
+
+    private final String value;
+    private final String displayName;
+    private final String description;
+
+    FileIOImplementation(String value, String displayName) {
+        this(value, displayName, null);
+    }
+
+    FileIOImplementation(String value, String displayName, String description) 
{
+        this.value = value;
+        this.displayName = displayName;
+        this.description = description;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HadoopCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HadoopCatalogService.java
index 26853ffc80..42b364d23f 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HadoopCatalogService.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HadoopCatalogService.java
@@ -22,11 +22,7 @@ import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.ConfigurationContext;
-import org.apache.nifi.expression.ExpressionLanguageScope;
-import org.apache.nifi.processor.util.StandardValidators;
 
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.WAREHOUSE_LOCATION;
@@ -35,19 +31,7 @@ import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.WAREHOUSE_
 @CapabilityDescription("Catalog service that can use HDFS or similar file 
systems that support atomic rename.")
 public class HadoopCatalogService extends AbstractCatalogService {
 
-    static final PropertyDescriptor WAREHOUSE_PATH = new 
PropertyDescriptor.Builder()
-            .name("warehouse-path")
-            .displayName("Warehouse Path")
-            .description("Path to the location of the warehouse.")
-            .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
-            .required(true)
-            .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
-            .build();
-
-    private static final List<PropertyDescriptor> PROPERTIES = 
Collections.unmodifiableList(Arrays.asList(
-            WAREHOUSE_PATH,
-            HADOOP_CONFIGURATION_RESOURCES
-    ));
+    private static final List<PropertyDescriptor> PROPERTIES = 
List.of(WAREHOUSE_PATH, HADOOP_CONFIGURATION_RESOURCES);
 
     @Override
     protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HiveCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HiveCatalogService.java
index 2fcd333af0..c421c8c3b2 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HiveCatalogService.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/HiveCatalogService.java
@@ -30,16 +30,14 @@ import org.w3c.dom.Document;
 import org.w3c.dom.NodeList;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 @Tags({"iceberg", "catalog", "service", "metastore", "hive"})
 @CapabilityDescription("Catalog service that connects to a Hive metastore to 
keep track of Iceberg tables.")
 public class HiveCatalogService extends AbstractCatalogService {
 
-    static final PropertyDescriptor METASTORE_URI = new 
PropertyDescriptor.Builder()
+    public static final PropertyDescriptor METASTORE_URI = new 
PropertyDescriptor.Builder()
             .name("hive-metastore-uri")
             .displayName("Hive Metastore URI")
             .description("The URI location(s) for the Hive metastore; note 
that this is not the location of the Hive Server. The default port for the Hive 
metastore is 9043.")
@@ -47,7 +45,7 @@ public class HiveCatalogService extends 
AbstractCatalogService {
             .addValidator(StandardValidators.URI_LIST_VALIDATOR)
             .build();
 
-    static final PropertyDescriptor WAREHOUSE_LOCATION = new 
PropertyDescriptor.Builder()
+    public static final PropertyDescriptor WAREHOUSE_LOCATION = new 
PropertyDescriptor.Builder()
             .name("warehouse-location")
             .displayName("Default Warehouse Location")
             .description("Location of default database for the warehouse. This 
field sets or overrides the 'hive.metastore.warehouse.dir' configuration 
property.")
@@ -55,11 +53,7 @@ public class HiveCatalogService extends 
AbstractCatalogService {
             .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
             .build();
 
-    private static final List<PropertyDescriptor> PROPERTIES = 
Collections.unmodifiableList(Arrays.asList(
-            METASTORE_URI,
-            WAREHOUSE_LOCATION,
-            HADOOP_CONFIGURATION_RESOURCES
-    ));
+    private static final List<PropertyDescriptor> PROPERTIES = 
List.of(METASTORE_URI, WAREHOUSE_LOCATION, HADOOP_CONFIGURATION_RESOURCES);
 
     @Override
     protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/JdbcCatalogService.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/JdbcCatalogService.java
new file mode 100644
index 0000000000..9a596aa06f
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/java/org/apache/nifi/services/iceberg/JdbcCatalogService.java
@@ -0,0 +1,90 @@
+/*
+ * 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.nifi.services.iceberg;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnEnabled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.dbcp.DBCPService;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.util.List;
+
+import static org.apache.nifi.services.iceberg.FileIOImplementation.HADOOP;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.FILE_IO_IMPLEMENTATION;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.CLIENT_POOL_SERVICE;
+import static 
org.apache.nifi.services.iceberg.IcebergCatalogProperty.WAREHOUSE_LOCATION;
+
+@Tags({"iceberg", "catalog", "service", "jdbc"})
+@CapabilityDescription("Catalog service using relational database to manage 
Iceberg tables through JDBC.")
+public class JdbcCatalogService extends AbstractCatalogService {
+
+    public static final PropertyDescriptor CATALOG_NAME = new 
PropertyDescriptor.Builder()
+            .name("Catalog Name")
+            .description("Name of the Iceberg catalog.")
+            .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
+            .defaultValue("jdbc-catalog")
+            .required(true)
+            .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
+            .build();
+
+    public static final PropertyDescriptor CONNECTION_POOL = new 
PropertyDescriptor.Builder()
+            .name("Connection Pool")
+            .description("Specifies the JDBC Connection Pool to use in order 
to communicate with the Iceberg catalog.")
+            .identifiesControllerService(DBCPService.class)
+            .required(true)
+            .build();
+
+    public static final PropertyDescriptor FILE_IO_IMPL = new 
PropertyDescriptor.Builder()
+            .name("File IO Implementation")
+            .description("Specifies the implementation of FileIO interface to 
be used. " +
+                    "The provided implementation have to include the class and 
full package name.")
+            .required(true)
+            .defaultValue(HADOOP.getValue())
+            .allowableValues(FileIOImplementation.class)
+            .build();
+
+    private static final List<PropertyDescriptor> PROPERTIES = List.of(
+            CATALOG_NAME, CONNECTION_POOL, FILE_IO_IMPL, WAREHOUSE_PATH, 
HADOOP_CONFIGURATION_RESOURCES);
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTIES;
+    }
+
+    @OnEnabled
+    public void onEnabled(final ConfigurationContext context) {
+        if (context.getProperty(HADOOP_CONFIGURATION_RESOURCES).isSet()) {
+            configFilePaths = 
createFilePathList(context.getProperty(HADOOP_CONFIGURATION_RESOURCES).evaluateAttributeExpressions().getValue());
+        }
+
+        catalogProperties.put(IcebergCatalogProperty.CATALOG_NAME, 
context.getProperty(CATALOG_NAME).evaluateAttributeExpressions().getValue());
+        catalogProperties.put(CLIENT_POOL_SERVICE, 
context.getProperty(CONNECTION_POOL).asControllerService(DBCPService.class));
+        catalogProperties.put(FILE_IO_IMPLEMENTATION, 
context.getProperty(FILE_IO_IMPL).evaluateAttributeExpressions().getValue());
+        catalogProperties.put(WAREHOUSE_LOCATION, 
context.getProperty(WAREHOUSE_PATH).evaluateAttributeExpressions().getValue());
+    }
+
+    @Override
+    public IcebergCatalogType getCatalogType() {
+        return IcebergCatalogType.JDBC;
+    }
+
+}
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
index 4c042964b2..44a8e957cd 100755
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
@@ -14,4 +14,5 @@
 # limitations under the License.
 
 org.apache.nifi.services.iceberg.HiveCatalogService
-org.apache.nifi.services.iceberg.HadoopCatalogService
\ No newline at end of file
+org.apache.nifi.services.iceberg.HadoopCatalogService
+org.apache.nifi.services.iceberg.JdbcCatalogService
\ No newline at end of file
diff --git 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-test-utils/src/main/java/org/apache/nifi/hive/metastore/MetastoreCore.java
 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-test-utils/src/main/java/org/apache/nifi/hive/metastore/MetastoreCore.java
index 7d4636ecd8..3d2908b3d1 100644
--- 
a/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-test-utils/src/main/java/org/apache/nifi/hive/metastore/MetastoreCore.java
+++ 
b/nifi-extension-bundles/nifi-iceberg-bundle/nifi-iceberg-test-utils/src/main/java/org/apache/nifi/hive/metastore/MetastoreCore.java
@@ -73,7 +73,7 @@ import static 
org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars.WAREH
  */
 class MetastoreCore {
 
-    private final String DATABASE_NAME = "test_metastore";
+    private final String DATABASE_NAME = "iceberg_test";
 
     private String thriftConnectionUri;
     private Configuration hiveConf;


Reply via email to