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

ndipiazza pushed a commit to branch TIKA-4606-ignite-3x-upgrade
in repository https://gitbox.apache.org/repos/asf/tika.git

commit c752e37c93a42423df17e98f7bbcfd3977be478f
Author: Nicholas DiPiazza <[email protected]>
AuthorDate: Mon Dec 29 09:16:15 2025 -0600

    TIKA-4606: Complete Ignite 3.x code refactoring with Calcite SQL engine
    
    ✅ COMPILATION SUCCESS - All code refactored for Ignite 3.x API
    
    Changes:
    1. IgniteConfigStoreConfig.java:
       - Replaced CacheMode enum with replicas/partitions
       - tableName replaces cacheName (Ignite 3.x uses tables not caches)
       - Added partitions configuration
       - Removed getCacheModeEnum() method
    
    2. IgniteConfigStore.java:
       - Complete rewrite for Ignite 3.x client-server architecture
       - Uses IgniteClient.builder() to connect to cluster
       - KeyValueView<K,V> replaces IgniteCache<K,V>
       - Table-based storage instead of cache-based
       - Client-server model (connects to IgniteStoreServer)
    
    3. IgniteStoreServer.java:
       - Uses IgniteServer for embedded server
       - Creates tables and distribution zones via SQL
       - Simplified initialization (no complex config needed)
       - Uses Ignite 3.x Table API
    
    4. IgniteConfigStoreTest.java:
       - Updated to use BeforeAll/AfterAll for server lifecycle
       - Starts IgniteStoreServer once for all tests
       - Clients connect to server instance
    
    Technical Details:
    - Client connects via port 10800 (default)
    - Distribution zones configure replication
    - SQL: CREATE ZONE, CREATE TABLE
    - KeyValueView for simple get/put operations
    - SQL queries for keySet() and size()
    
    Status:
    ✅ Code compiles successfully
    ✅ No dependency issues
    ✅ Checkstyle passes
    ✅ Spotless passes
    ⚠️ Tests need server initialization fix (Ignite 3.x embedded startup)
    
    Next: Fix embedded Ignite 3.x server startup in tests
---
 tika-pipes/tika-pipes-config-store-ignite/pom.xml  |   5 +
 .../tika/pipes/ignite/IgniteConfigStore.java       | 183 ++++++++++++++-------
 .../ignite/config/IgniteConfigStoreConfig.java     |  57 +++----
 .../pipes/ignite/server/IgniteStoreServer.java     | 151 +++++++++++------
 .../tika/pipes/ignite/IgniteConfigStoreTest.java   |  34 +++-
 5 files changed, 282 insertions(+), 148 deletions(-)

diff --git a/tika-pipes/tika-pipes-config-store-ignite/pom.xml 
b/tika-pipes/tika-pipes-config-store-ignite/pom.xml
index e8771de65..b54cee9cb 100644
--- a/tika-pipes/tika-pipes-config-store-ignite/pom.xml
+++ b/tika-pipes/tika-pipes-config-store-ignite/pom.xml
@@ -83,6 +83,11 @@
       <artifactId>ignite-api</artifactId>
       <version>${ignite.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.ignite</groupId>
+      <artifactId>ignite-client</artifactId>
+      <version>${ignite.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.ignite</groupId>
       <artifactId>ignite-runner</artifactId>
diff --git 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/IgniteConfigStore.java
 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/IgniteConfigStore.java
index 05becb62b..b3790e579 100644
--- 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/IgniteConfigStore.java
+++ 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/IgniteConfigStore.java
@@ -16,14 +16,14 @@
  */
 package org.apache.tika.pipes.ignite;
 
+import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.Ignition;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.table.KeyValueView;
+import org.apache.ignite.table.Table;
+import org.apache.ignite.table.Tuple;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,29 +33,33 @@ import 
org.apache.tika.pipes.ignite.config.IgniteConfigStoreConfig;
 import org.apache.tika.plugins.ExtensionConfig;
 
 /**
- * Apache Ignite-based implementation of {@link ConfigStore}.
- * Provides distributed configuration storage for Tika Pipes clustering.
+ * Apache Ignite 3.x-based implementation of {@link ConfigStore}.
+ * Provides distributed configuration storage for Tika Pipes clustering using 
Calcite SQL engine.
  * <p>
  * This implementation is thread-safe and suitable for multi-instance 
deployments
  * where configurations need to be shared across multiple servers.
  * <p>
  * Configuration options:
  * <ul>
- *   <li>cacheName - Name of the Ignite cache (default: 
"tika-config-store")</li>
- *   <li>cacheMode - Cache replication mode: PARTITIONED or REPLICATED 
(default: REPLICATED)</li>
+ *   <li>tableName - Name of the Ignite table (default: 
"tika_config_store")</li>
+ *   <li>replicas - Replication factor (default: 2)</li>
+ *   <li>partitions - Number of partitions (default: 10)</li>
  *   <li>igniteInstanceName - Name of the Ignite instance (default: 
"TikaIgniteConfigStore")</li>
  * </ul>
+ * 
+ * Note: This uses Ignite 3.x with built-in Apache Calcite SQL engine (no H2 
dependency).
  */
 public class IgniteConfigStore implements ConfigStore {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(IgniteConfigStore.class);
-    private static final String DEFAULT_CACHE_NAME = "tika-config-store";
+    private static final String DEFAULT_TABLE_NAME = "tika_config_store";
     private static final String DEFAULT_INSTANCE_NAME = 
"TikaIgniteConfigStore";
 
     private Ignite ignite;
-    private IgniteCache<String, ExtensionConfigDTO> cache;
-    private String cacheName = DEFAULT_CACHE_NAME;
-    private CacheMode cacheMode = CacheMode.REPLICATED;
+    private KeyValueView<String, ExtensionConfigDTO> kvView;
+    private String tableName = DEFAULT_TABLE_NAME;
+    private int replicas = 2;
+    private int partitions = 10;
     private String igniteInstanceName = DEFAULT_INSTANCE_NAME;
     private boolean autoClose = true;
     private ExtensionConfig extensionConfig;
@@ -69,14 +73,15 @@ public class IgniteConfigStore implements ConfigStore {
         this.extensionConfig = extensionConfig;
         
         IgniteConfigStoreConfig config = 
IgniteConfigStoreConfig.load(extensionConfig.json());
-        this.cacheName = config.getCacheName();
-        this.cacheMode = config.getCacheModeEnum();
+        this.tableName = config.getTableName();
+        this.replicas = config.getReplicas();
+        this.partitions = config.getPartitions();
         this.igniteInstanceName = config.getIgniteInstanceName();
         this.autoClose = config.isAutoClose();
     }
 
-    public IgniteConfigStore(String cacheName) {
-        this.cacheName = cacheName;
+    public IgniteConfigStore(String tableName) {
+        this.tableName = tableName;
     }
 
     @Override
@@ -94,105 +99,157 @@ public class IgniteConfigStore implements ConfigStore {
             return;
         }
 
-        LOG.info("Initializing IgniteConfigStore with cache: {}, mode: {}, 
instance: {}",
-                cacheName, cacheMode, igniteInstanceName);
+        LOG.info("Initializing IgniteConfigStore with table: {}, replicas: {}, 
partitions: {}, instance: {}",
+                tableName, replicas, partitions, igniteInstanceName);
 
-        // Disable Ignite's Object Input Filter autoconfiguration to avoid 
conflicts
-        
System.setProperty("IGNITE_ENABLE_OBJECT_INPUT_FILTER_AUTOCONFIGURATION", 
"false");
+        // In Ignite 3.x, we connect as a client to an existing cluster
+        // The server must be started separately via IgniteStoreServer
+        try {
+            // Connect to Ignite cluster as client
+            ignite = IgniteClient.builder()
+                    .addresses("127.0.0.1:10800")  // Default client port
+                    .build();
 
-        IgniteConfiguration cfg = new IgniteConfiguration();
-        cfg.setIgniteInstanceName(igniteInstanceName + (clientMode ? "-Client" 
: ""));
-        cfg.setClientMode(clientMode);
-        cfg.setPeerClassLoadingEnabled(false);  // Disable to avoid 
classloader conflicts
-        
-        // Set work directory to /var/cache/tika to match Tika's cache location
-        cfg.setWorkDirectory(System.getProperty("ignite.work.dir", 
"/var/cache/tika/ignite-work"));
+            LOG.info("Connected to Ignite cluster as client");
 
-        ignite = Ignition.start(cfg);
+            // Get or create table
+            Table table = ignite.tables().table(tableName);
+            if (table == null) {
+                LOG.warn("Table {} not found on server. It should be created 
by IgniteStoreServer.", tableName);
+                throw new IllegalStateException("Table " + tableName + " not 
found. Ensure IgniteStoreServer is running.");
+            }
 
-        // Get cache (it should already exist on the server)
-        cache = ignite.cache(cacheName);
-        if (cache == null) {
-            // If not found, create it (shouldn't happen if server started 
first)
-            LOG.warn("Cache {} not found on server, creating it", cacheName);
-            CacheConfiguration<String, ExtensionConfigDTO> cacheCfg = new 
CacheConfiguration<>(cacheName);
-            cacheCfg.setCacheMode(cacheMode);
-            cacheCfg.setBackups(cacheMode == CacheMode.PARTITIONED ? 1 : 0);
-            cache = ignite.getOrCreateCache(cacheCfg);
+            // Get key-value view for simple get/put operations
+            kvView = table.keyValueView(String.class, 
ExtensionConfigDTO.class);
+            
+            LOG.info("IgniteConfigStore initialized successfully as client");
+        } catch (Exception e) {
+            LOG.error("Failed to initialize IgniteConfigStore", e);
+            throw new TikaConfigException("Failed to connect to Ignite 
cluster. Ensure IgniteStoreServer is running.", e);
         }
-        LOG.info("IgniteConfigStore initialized successfully as client");
     }
 
     @Override
     public void put(String id, ExtensionConfig config) {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        cache.put(id, new ExtensionConfigDTO(config));
+        try {
+            kvView.put(null, id, new ExtensionConfigDTO(config));
+        } catch (Exception e) {
+            LOG.error("Failed to put config with id: {}", id, e);
+            throw new RuntimeException("Failed to put config", e);
+        }
     }
 
     @Override
     public ExtensionConfig get(String id) {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        ExtensionConfigDTO dto = cache.get(id);
-        return dto != null ? dto.toExtensionConfig() : null;
+        try {
+            ExtensionConfigDTO dto = kvView.get(null, id);
+            return dto != null ? dto.toExtensionConfig() : null;
+        } catch (Exception e) {
+            LOG.error("Failed to get config with id: {}", id, e);
+            throw new RuntimeException("Failed to get config", e);
+        }
     }
 
     @Override
     public boolean containsKey(String id) {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        return cache.containsKey(id);
+        try {
+            return kvView.get(null, id) != null;
+        } catch (Exception e) {
+            LOG.error("Failed to check if key exists: {}", id, e);
+            throw new RuntimeException("Failed to check key", e);
+        }
     }
 
     @Override
     public Set<String> keySet() {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        return Set.copyOf(cache.query(new 
org.apache.ignite.cache.query.ScanQuery<String, ExtensionConfigDTO>())
-                .getAll()
-                .stream()
-                .map(javax.cache.Cache.Entry::getKey)
-                .toList());
+        try {
+            // Use SQL query to get all keys
+            var sql = ignite.sql();
+            var resultSet = sql.execute(null, "SELECT id FROM " + tableName);
+            
+            Set<String> keys = new HashSet<>();
+            while (resultSet.hasNext()) {
+                Tuple tuple = resultSet.next();
+                keys.add(tuple.stringValue("id"));
+            }
+            return keys;
+        } catch (Exception e) {
+            LOG.error("Failed to get keySet", e);
+            throw new RuntimeException("Failed to get keySet", e);
+        }
     }
 
     @Override
     public int size() {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        return cache.size();
+        try {
+            // Use SQL query to count rows
+            var sql = ignite.sql();
+            var resultSet = sql.execute(null, "SELECT COUNT(*) as cnt FROM " + 
tableName);
+            
+            if (resultSet.hasNext()) {
+                Tuple tuple = resultSet.next();
+                return tuple.intValue("cnt");
+            }
+            return 0;
+        } catch (Exception e) {
+            LOG.error("Failed to get size", e);
+            throw new RuntimeException("Failed to get size", e);
+        }
     }
 
     @Override
     public ExtensionConfig remove(String id) {
-        if (cache == null) {
+        if (kvView == null) {
             throw new IllegalStateException("IgniteConfigStore not 
initialized. Call init() first.");
         }
-        ExtensionConfigDTO removed = cache.getAndRemove(id);
-        return removed != null ? removed.toExtensionConfig() : null;
+        try {
+            ExtensionConfigDTO removed = kvView.getAndRemove(null, id);
+            return removed != null ? removed.toExtensionConfig() : null;
+        } catch (Exception e) {
+            LOG.error("Failed to remove config with id: {}", id, e);
+            throw new RuntimeException("Failed to remove config", e);
+        }
     }
 
     public void close() {
         if (ignite != null && autoClose) {
             LOG.info("Closing IgniteConfigStore");
-            ignite.close();
+            try {
+                ((AutoCloseable) ignite).close();
+            } catch (Exception e) {
+                LOG.error("Error closing Ignite", e);
+            }
             ignite = null;
-            cache = null;
+            kvView = null;
             closed = true;
         }
     }
 
-    public void setCacheName(String cacheName) {
-        this.cacheName = cacheName;
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public void setReplicas(int replicas) {
+        this.replicas = replicas;
     }
 
-    public void setCacheMode(CacheMode cacheMode) {
-        this.cacheMode = cacheMode;
+    public void setPartitions(int partitions) {
+        this.partitions = partitions;
     }
 
     public void setIgniteInstanceName(String igniteInstanceName) {
diff --git 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/config/IgniteConfigStoreConfig.java
 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/config/IgniteConfigStoreConfig.java
index 6ec7699ec..1988c46ee 100644
--- 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/config/IgniteConfigStoreConfig.java
+++ 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/config/IgniteConfigStoreConfig.java
@@ -18,7 +18,6 @@ package org.apache.tika.pipes.ignite.config;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.ignite.cache.CacheMode;
 
 import org.apache.tika.exception.TikaConfigException;
 
@@ -28,12 +27,16 @@ import org.apache.tika.exception.TikaConfigException;
  * Example JSON configuration:
  * <pre>
  * {
- *   "cacheName": "my-tika-cache",
- *   "cacheMode": "REPLICATED",
+ *   "tableName": "my-tika-table",
+ *   "replicas": 2,
  *   "igniteInstanceName": "MyTikaCluster",
  *   "autoClose": true
  * }
  * </pre>
+ * 
+ * Note: In Ignite 3.x, "cacheMode" is replaced by "replicas" (replication 
factor).
+ * - replicas=1 is similar to PARTITIONED (each partition on one node)
+ * - replicas=N where N>=cluster size is similar to REPLICATED
  */
 public class IgniteConfigStoreConfig {
 
@@ -50,47 +53,30 @@ public class IgniteConfigStoreConfig {
         }
     }
 
-    private String cacheName = "tika-config-store";
-    private String cacheMode = "REPLICATED";
+    private String tableName = "tika_config_store";
+    private int replicas = 2;  // Replication factor
     private String igniteInstanceName = "TikaIgniteConfigStore";
     private boolean autoClose = true;
+    private int partitions = 10;  // Number of partitions
 
-    public String getCacheName() {
-        return cacheName;
+    public String getTableName() {
+        return tableName;
     }
 
-    public IgniteConfigStoreConfig setCacheName(String cacheName) {
-        this.cacheName = cacheName;
+    public IgniteConfigStoreConfig setTableName(String tableName) {
+        this.tableName = tableName;
         return this;
     }
 
-    public String getCacheMode() {
-        return cacheMode;
+    public int getReplicas() {
+        return replicas;
     }
 
-    public IgniteConfigStoreConfig setCacheMode(String cacheMode) {
-        this.cacheMode = cacheMode;
+    public IgniteConfigStoreConfig setReplicas(int replicas) {
+        this.replicas = replicas;
         return this;
     }
 
-    public CacheMode getCacheModeEnum() {
-        if (cacheMode == null || cacheMode.trim().isEmpty()) {
-            return CacheMode.REPLICATED;
-        }
-        
-        if ("PARTITIONED".equalsIgnoreCase(cacheMode)) {
-            return CacheMode.PARTITIONED;
-        }
-        
-        if ("REPLICATED".equalsIgnoreCase(cacheMode)) {
-            return CacheMode.REPLICATED;
-        }
-        
-        throw new IllegalArgumentException(
-                "Unsupported cacheMode: '" + cacheMode
-                        + "'. Supported values are PARTITIONED and 
REPLICATED.");
-    }
-
     public String getIgniteInstanceName() {
         return igniteInstanceName;
     }
@@ -108,4 +94,13 @@ public class IgniteConfigStoreConfig {
         this.autoClose = autoClose;
         return this;
     }
+
+    public int getPartitions() {
+        return partitions;
+    }
+
+    public IgniteConfigStoreConfig setPartitions(int partitions) {
+        this.partitions = partitions;
+        return this;
+    }
 }
diff --git 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/server/IgniteStoreServer.java
 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/server/IgniteStoreServer.java
index 8d8759ac8..3fdfd3a7f 100644
--- 
a/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/server/IgniteStoreServer.java
+++ 
b/tika-pipes/tika-pipes-config-store-ignite/src/main/java/org/apache/tika/pipes/ignite/server/IgniteStoreServer.java
@@ -16,41 +16,45 @@
  */
 package org.apache.tika.pipes.ignite.server;
 
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
-import org.apache.ignite.Ignition;
-import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.ignite.IgniteServer;
+import org.apache.ignite.table.Table;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.tika.pipes.ignite.ExtensionConfigDTO;
-
 /**
- * Embedded Ignite server that hosts the distributed cache.
+ * Embedded Ignite 3.x server that hosts the distributed table.
  * This runs as a background thread within the tika-grpc process.
  * Tika gRPC and forked PipesServer instances connect as clients.
+ * 
+ * Note: Uses Ignite 3.x with Calcite SQL engine (no H2).
  */
 public class IgniteStoreServer implements AutoCloseable {
     
     private static final Logger LOG = 
LoggerFactory.getLogger(IgniteStoreServer.class);
-    private static final String DEFAULT_CACHE_NAME = "tika-config-store";
-    private static final String DEFAULT_INSTANCE_NAME = "TikaIgniteServer";
+    private static final String DEFAULT_TABLE_NAME = "tika_config_store";
+    private static final String DEFAULT_NODE_NAME = "TikaIgniteServer";
     
-    private Ignite ignite;
-    private final String cacheName;
-    private final CacheMode cacheMode;
-    private final String instanceName;
+    private IgniteServer ignite;
+    private final String tableName;
+    private final int replicas;
+    private final int partitions;
+    private final String nodeName;
+    private final Path workDir;
     
     public IgniteStoreServer() {
-        this(DEFAULT_CACHE_NAME, CacheMode.REPLICATED, DEFAULT_INSTANCE_NAME);
+        this(DEFAULT_TABLE_NAME, 2, 10, DEFAULT_NODE_NAME);
     }
     
-    public IgniteStoreServer(String cacheName, CacheMode cacheMode, String 
instanceName) {
-        this.cacheName = cacheName;
-        this.cacheMode = cacheMode;
-        this.instanceName = instanceName;
+    public IgniteStoreServer(String tableName, int replicas, int partitions, 
String nodeName) {
+        this.tableName = tableName;
+        this.replicas = replicas;
+        this.partitions = partitions;
+        this.nodeName = nodeName;
+        this.workDir = Paths.get(System.getProperty("ignite.work.dir", 
"/var/cache/tika/ignite-work"));
     }
     
     /**
@@ -69,38 +73,87 @@ public class IgniteStoreServer implements AutoCloseable {
         
         // Wait for server to initialize
         try {
-            Thread.sleep(3000);
+            Thread.sleep(5000);  // Give it more time for Ignite 3.x 
initialization
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
         }
     }
     
     private void start() throws Exception {
-        LOG.info("Starting Ignite server: instance={}, cache={}, mode={}", 
-            instanceName, cacheName, cacheMode);
-        
-        // Disable Ignite's Object Input Filter autoconfiguration to avoid 
conflicts
-        
System.setProperty("IGNITE_ENABLE_OBJECT_INPUT_FILTER_AUTOCONFIGURATION", 
"false");
-        
-        IgniteConfiguration cfg = new IgniteConfiguration();
-        cfg.setIgniteInstanceName(instanceName);
-        cfg.setClientMode(false); // Server mode
-        cfg.setPeerClassLoadingEnabled(false); // Disable to avoid classloader 
conflicts
-        
-        // Set work directory to /var/cache/tika to match Tika's cache location
-        cfg.setWorkDirectory(System.getProperty("ignite.work.dir", 
"/var/cache/tika/ignite-work"));
+        LOG.info("Starting Ignite 3.x server: node={}, table={}, replicas={}, 
partitions={}", 
+            nodeName, tableName, replicas, partitions);
         
-        ignite = Ignition.start(cfg);
-        
-        CacheConfiguration<String, ExtensionConfigDTO> cacheCfg = 
-            new CacheConfiguration<>(cacheName);
-        cacheCfg.setCacheMode(cacheMode);
-        cacheCfg.setBackups(cacheMode == CacheMode.PARTITIONED ? 1 : 0);
-        
-        IgniteCache<String, ExtensionConfigDTO> cache = 
ignite.getOrCreateCache(cacheCfg);
-        
-        LOG.info("Ignite server started successfully with cache: {}", 
cache.getName());
-        LOG.info("Ignite topology: {} nodes", ignite.cluster().nodes().size());
+        try {
+            // Ensure work directory exists
+            if (!Files.exists(workDir)) {
+                Files.createDirectories(workDir);
+                LOG.info("Created work directory: {}", workDir);
+            }
+            
+            // Start the server node directly
+            // Note: In Ignite 3.x embedded mode, the server manages its own 
initialization
+            LOG.info("Starting Ignite node: {} at {}", nodeName, workDir);
+            ignite = IgniteServer.start(nodeName, workDir, null);
+            LOG.info("Ignite server started successfully");
+            
+            // Wait a bit for the cluster to be ready
+            Thread.sleep(3000);
+            
+            // Create table if it doesn't exist
+            createTableIfNeeded();
+            
+            LOG.info("Ignite server is ready");
+        } catch (Exception e) {
+            LOG.error("Failed to start Ignite server", e);
+            throw e;
+        }
+    }
+    
+    private void createTableIfNeeded() {
+        try {
+            // Get the API interface from the server
+            org.apache.ignite.Ignite api = ignite.api();
+            
+            Table existingTable = api.tables().table(tableName);
+            if (existingTable != null) {
+                LOG.info("Table {} already exists", tableName);
+                return;
+            }
+            
+            LOG.info("Creating table: {} with replicas={}, partitions={}", 
tableName, replicas, partitions);
+            
+            // Create table using SQL
+            String createTableSql = String.format(
+                "CREATE TABLE IF NOT EXISTS %s (" +
+                "  id VARCHAR PRIMARY KEY," +
+                "  contextKey VARCHAR," +
+                "  entityType VARCHAR," +
+                "  factoryName VARCHAR," +
+                "  json VARCHAR(10000)" +
+                ") WITH PRIMARY_ZONE='%s_ZONE'",
+                tableName, tableName.toUpperCase()
+            );
+            
+            // First create a distribution zone
+            String createZoneSql = String.format(
+                "CREATE ZONE IF NOT EXISTS %s_ZONE WITH " +
+                "REPLICAS=%d, " +
+                "PARTITIONS=%d, " +
+                "STORAGE_PROFILES='default'",
+                tableName.toUpperCase(), replicas, partitions
+            );
+            
+            LOG.info("Creating distribution zone with SQL: {}", createZoneSql);
+            api.sql().execute(null, createZoneSql);
+            
+            LOG.info("Creating table with SQL: {}", createTableSql);
+            api.sql().execute(null, createTableSql);
+            
+            LOG.info("Table {} created successfully", tableName);
+        } catch (Exception e) {
+            LOG.error("Failed to create table: {}", tableName, e);
+            throw new RuntimeException("Failed to create table", e);
+        }
     }
     
     public boolean isRunning() {
@@ -110,8 +163,12 @@ public class IgniteStoreServer implements AutoCloseable {
     @Override
     public void close() {
         if (ignite != null) {
-            LOG.info("Stopping Ignite server: {}", instanceName);
-            ignite.close();
+            LOG.info("Stopping Ignite server: {}", nodeName);
+            try {
+                ((AutoCloseable) ignite).close();
+            } catch (Exception e) {
+                LOG.error("Error stopping Ignite server", e);
+            }
             ignite = null;
         }
     }
diff --git 
a/tika-pipes/tika-pipes-config-store-ignite/src/test/java/org/apache/tika/pipes/ignite/IgniteConfigStoreTest.java
 
b/tika-pipes/tika-pipes-config-store-ignite/src/test/java/org/apache/tika/pipes/ignite/IgniteConfigStoreTest.java
index 41a520e1f..f96aaaec5 100644
--- 
a/tika-pipes/tika-pipes-config-store-ignite/src/test/java/org/apache/tika/pipes/ignite/IgniteConfigStoreTest.java
+++ 
b/tika-pipes/tika-pipes-config-store-ignite/src/test/java/org/apache/tika/pipes/ignite/IgniteConfigStoreTest.java
@@ -25,28 +25,48 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.nio.file.Path;
 
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
+import org.apache.tika.pipes.ignite.server.IgniteStoreServer;
 import org.apache.tika.plugins.ExtensionConfig;
 
 public class IgniteConfigStoreTest {
 
     @TempDir
-    private Path tempDir;
+    private static Path tempDir;
     
+    private static IgniteStoreServer server;
     private IgniteConfigStore store;
 
-    @BeforeEach
-    public void setUp() throws Exception {
+    @BeforeAll
+    public static void setUpServer() throws Exception {
         // Set the work directory for Ignite to use the temp directory
         System.setProperty("ignite.work.dir", tempDir.toString());
         
+        // Start the Ignite server once for all tests
+        server = new IgniteStoreServer();
+        server.startAsync();
+        
+        // Give server more time to fully start
+        Thread.sleep(10000);
+    }
+    
+    @AfterAll
+    public static void tearDownServer() {
+        if (server != null) {
+            server.close();
+        }
+    }
+
+    @BeforeEach
+    public void setUp() throws Exception {
         store = new IgniteConfigStore();
-        store.setIgniteInstanceName("TestIgniteInstance-" + 
System.currentTimeMillis());
-        store.setClientMode(false);  // Run as server for tests
+        store.setClientMode(true);  // Connect as client to the server
         store.init();
     }
 
@@ -199,8 +219,8 @@ public class IgniteConfigStoreTest {
 
     @Test
     public void testCustomCacheName() throws Exception {
-        IgniteConfigStore customStore = new IgniteConfigStore("custom-cache");
-        customStore.setIgniteInstanceName("CustomInstance-" + 
System.currentTimeMillis());
+        IgniteConfigStore customStore = new IgniteConfigStore("custom_table");
+        customStore.setClientMode(true);
         
         try {
             customStore.init();

Reply via email to