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

nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 9186d7576e3 IGNITE-23113 [ducktests] Add Thin JDBC driver support 
(#11527)
9186d7576e3 is described below

commit 9186d7576e33303c4857eb06115e9f993e58be59
Author: Sergey Korotkov <[email protected]>
AuthorDate: Wed Sep 11 21:41:40 2024 +0700

    IGNITE-23113 [ducktests] Add Thin JDBC driver support (#11527)
---
 .../tests/jdbc/JdbcThinSelfTestApplication.java    | 124 +++++++++++++++++++++
 .../ducktest/utils/IgniteAwareApplication.java     |   4 +
 .../utils/IgniteAwareApplicationService.java       |  12 +-
 .../tests/ignitetest/services/utils/__init__.py    |   1 +
 .../ignitetest/services/utils/config_template.py   |   9 ++
 .../ignitetest/services/utils/ignite_aware.py      |   3 +-
 .../utils/ignite_configuration/__init__.py         |  32 ++++++
 .../tests/ignitetest/services/utils/ignite_spec.py |  13 ++-
 .../tests/ignitetest/services/utils/path.py        |   9 ++
 .../utils/templates/thin_jdbc_config.xml.j2        |  46 ++++++++
 .../tests/ignitetest/tests/jdbc_thin_test.py       |  66 +++++++++++
 11 files changed, 314 insertions(+), 5 deletions(-)

diff --git 
a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/jdbc/JdbcThinSelfTestApplication.java
 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/jdbc/JdbcThinSelfTestApplication.java
new file mode 100644
index 00000000000..98ca821f874
--- /dev/null
+++ 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/jdbc/JdbcThinSelfTestApplication.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ignite.internal.ducktest.tests.jdbc;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.ignite.internal.ducktest.utils.IgniteAwareApplication;
+
+/**
+ * Simple application working via the Thin JDBC driver.
+ */
+public class JdbcThinSelfTestApplication extends IgniteAwareApplication {
+    /** */
+    private static final BigDecimal DECIMAL_VALUE1 = BigDecimal.valueOf(762, 
2);
+
+    /** */
+    private static final BigDecimal DECIMAL_VALUE2 = BigDecimal.valueOf(545, 
2);
+
+    /** */
+    private static final String VARCHAR_VALUE = "ak";
+
+    /** {@inheritDoc} */
+    @Override public void run(JsonNode jsonNode) throws SQLException {
+        markInitialized();
+
+        try (Connection conn = thinJdbcDataSource.getConnection()) {
+            createTable(conn);
+
+            insertRow(conn);
+
+            selectRow(conn);
+
+            updateRow(conn);
+
+            deleteRow(conn);
+
+            dropTable(conn);
+
+            markFinished();
+        }
+    }
+
+    /** */
+    private void createTable(Connection conn) throws SQLException {
+        conn.createStatement().execute("CREATE TABLE IF NOT EXISTS table(id 
INT, strVal VARCHAR, decVal DECIMAL, PRIMARY KEY(id)) ");
+    }
+
+    /** */
+    private void insertRow(Connection conn) throws SQLException {
+        PreparedStatement insertStatement = conn.prepareStatement("INSERT INTO 
table(id, strVal, decVal) VALUES(?, ?, ?)");
+
+        insertStatement.setInt(1, 1);
+        insertStatement.setString(2, VARCHAR_VALUE);
+        insertStatement.setBigDecimal(3, DECIMAL_VALUE1);
+
+        insertStatement.execute();
+    }
+
+    /** */
+    private void selectRow(Connection conn) throws SQLException {
+        PreparedStatement selectStatement = conn.prepareStatement("SELECT * 
FROM table WHERE id = ?");
+
+        selectStatement.setInt(1, 1);
+
+        ResultSet resultSet = selectStatement.executeQuery();
+
+        if (!resultSet.next())
+            markBroken(new RuntimeException("ResultSet is empty."));
+
+        int resultId = resultSet.getInt("id");
+        String resultStr = resultSet.getString("strVal");
+        BigDecimal resultDecimal = resultSet.getBigDecimal("decVal");
+
+        if (1 != resultId || !VARCHAR_VALUE.equals(resultStr) || 
DECIMAL_VALUE1.compareTo(resultDecimal) != 0)
+            throw new RuntimeException("Wrong row selected: " +
+                    "expected [id=1, strVal=" + VARCHAR_VALUE + ", decVal=" + 
DECIMAL_VALUE1 + "], " +
+                    "actual [id=" + resultId + ", strVal=" + resultStr + ", 
decVal=" + resultDecimal + "].");
+    }
+
+    /** */
+    private void updateRow(Connection conn) throws SQLException {
+        PreparedStatement selectStatement = conn.prepareStatement("UPDATE 
table SET decVal = ? WHERE id = ?");
+
+        selectStatement.setBigDecimal(1, DECIMAL_VALUE2);
+        selectStatement.setInt(2, 1);
+
+        if (1 != selectStatement.executeUpdate())
+            throw new RuntimeException("Row wasn't updated.");
+    }
+
+    /** */
+    private void deleteRow(Connection conn) throws SQLException {
+        PreparedStatement selectStatement = conn.prepareStatement("DELETE FROM 
table WHERE id = ?");
+
+        selectStatement.setInt(1, 1);
+
+        if (1 != selectStatement.executeUpdate())
+            throw new RuntimeException("Row wasn't deleted.");
+    }
+
+    /** */
+    private void dropTable(Connection conn) throws SQLException {
+        conn.createStatement().execute("DROP TABLE IF EXISTS table");
+    }
+}
diff --git 
a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplication.java
 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplication.java
index 4d6c0ef086b..6157c94b0ac 100644
--- 
a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplication.java
+++ 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplication.java
@@ -21,6 +21,7 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import com.fasterxml.jackson.databind.JsonNode;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteJdbcThinDataSource;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.internal.IgniteEx;
@@ -71,6 +72,9 @@ public abstract class IgniteAwareApplication {
     /** Client. */
     protected IgniteClient client;
 
+    /** Thin JDBC DataSource. */
+    protected IgniteJdbcThinDataSource thinJdbcDataSource;
+
     /** Cfg path. */
     protected String cfgPath;
 
diff --git 
a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplicationService.java
 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplicationService.java
index 19b13a151b2..ddb0ef191c5 100644
--- 
a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplicationService.java
+++ 
b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/utils/IgniteAwareApplicationService.java
@@ -47,7 +47,10 @@ public class IgniteAwareApplicationService {
         THIN_CLIENT,
 
         /** Run application without precreated connections. */
-        NONE
+        NONE,
+
+        /** Run application with the precreated thin JDBC data source. */
+        THIN_JDBC
     }
 
     /**
@@ -106,6 +109,13 @@ public class IgniteAwareApplicationService {
         }
         else if (svcType == IgniteServiceType.NONE)
             app.start(jsonNode);
+        else if (svcType == IgniteServiceType.THIN_JDBC) {
+            log.info("Create thin jdbc ignite data source...");
+
+            app.thinJdbcDataSource = IgnitionEx.loadSpringBean(cfgPath, 
"thin.jdbc.cfg");
+
+            app.start(jsonNode);
+        }
         else
             throw new IllegalArgumentException("Unknown service type " + 
svcType);
     }
diff --git a/modules/ducktests/tests/ignitetest/services/utils/__init__.py 
b/modules/ducktests/tests/ignitetest/services/utils/__init__.py
index c2830bce18a..efe71e8ad11 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/__init__.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/__init__.py
@@ -29,3 +29,4 @@ class IgniteServiceType(IntEnum):
     NODE = 0
     THIN_CLIENT = 1
     NONE = 2
+    THIN_JDBC = 3
diff --git 
a/modules/ducktests/tests/ignitetest/services/utils/config_template.py 
b/modules/ducktests/tests/ignitetest/services/utils/config_template.py
index 0af5f7cd77b..16a6a9a3533 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/config_template.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/config_template.py
@@ -24,6 +24,7 @@ IGNITE_TEMPLATE_PATH = 
os.path.join(os.path.dirname(os.path.abspath(__file__)),
 ZK_TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 
"..", "zk", "templates")
 DEFAULT_IGNITE_CONF = "ignite.xml.j2"
 DEFAULT_THIN_CLIENT_CONF = "thin_client_config.xml.j2"
+DEFAULT_THIN_JDBC_CONF = "thin_jdbc_config.xml.j2"
 DEFAULT_LOG4J2_CONF = "log4j2.xml.j2"
 
 TEMPLATE_PATHES = [IGNITE_TEMPLATE_PATH, ZK_TEMPLATE_PATH]
@@ -85,6 +86,14 @@ class IgniteThinClientConfigTemplate(ConfigTemplate):
         super().__init__(path)
 
 
+class IgniteThinJdbcConfigTemplate(ConfigTemplate):
+    """
+    Ignite client node configuration.
+    """
+    def __init__(self, path=DEFAULT_THIN_JDBC_CONF):
+        super().__init__(path)
+
+
 class IgniteLoggerConfigTemplate(ConfigTemplate):
     """
     Ignite logger configuration.
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py 
b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
index 0a0b581f301..b6e2fe05e14 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_aware.py
@@ -101,7 +101,8 @@ class IgniteAwareService(BackgroundThreadService, 
IgnitePathAware, JvmProcessMix
         """
         Awaits start finished.
         """
-        if self.config.service_type in (IgniteServiceType.NONE, 
IgniteServiceType.THIN_CLIENT):
+        if self.config.service_type in (IgniteServiceType.NONE, 
IgniteServiceType.THIN_CLIENT,
+                                        IgniteServiceType.THIN_JDBC):
             return
 
         self.logger.info("Waiting for IgniteAware(s) to start ...")
diff --git 
a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py
 
b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py
index 6f83f603e04..1fdec71e3b4 100644
--- 
a/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py
+++ 
b/modules/ducktests/tests/ignitetest/services/utils/ignite_configuration/__init__.py
@@ -166,3 +166,35 @@ class IgniteThinClientConfiguration(NamedTuple):
         Application mode.
         """
         return IgniteServiceType.THIN_CLIENT
+
+
+class IgniteThinJdbcConfiguration(NamedTuple):
+    addresses: list = []
+    version: IgniteVersion = DEV_BRANCH
+    ssl_params: SslParams = None
+    username: str = None
+    password: str = None
+
+    def prepare_ssl(self, test_globals, shared_root):
+        """
+        Updates ssl configuration from globals.
+        """
+        ssl_params = None
+        if self.ssl_params is None and is_ssl_enabled(test_globals):
+            ssl_params = get_ssl_params(test_globals, shared_root, 
IGNITE_CLIENT_ALIAS)
+        if ssl_params:
+            return self._replace(ssl_params=ssl_params)
+        return self
+
+    def prepare_for_env(self, cluster, node):
+        """
+        Updates configuration based on current environment.
+        """
+        return self
+
+    @property
+    def service_type(self):
+        """
+        Application mode.
+        """
+        return IgniteServiceType.THIN_JDBC
diff --git a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py 
b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
index e8c2c036b8b..7cb60371a04 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/ignite_spec.py
@@ -29,7 +29,7 @@ from itertools import chain
 
 from ignitetest.services.utils import IgniteServiceType
 from ignitetest.services.utils.config_template import 
IgniteClientConfigTemplate, IgniteServerConfigTemplate, \
-    IgniteLoggerConfigTemplate, IgniteThinClientConfigTemplate
+    IgniteLoggerConfigTemplate, IgniteThinClientConfigTemplate, 
IgniteThinJdbcConfigTemplate
 from ignitetest.services.utils.jvm_utils import create_jvm_settings, 
merge_jvm_settings
 from ignitetest.services.utils.path import get_home_dir, IgnitePathAware
 from ignitetest.services.utils.ssl.ssl_params import is_ssl_enabled
@@ -142,6 +142,9 @@ class IgniteSpec(metaclass=ABCMeta):
         if self.service.config.service_type == IgniteServiceType.THIN_CLIENT:
             
config_templates.append((IgnitePathAware.IGNITE_THIN_CLIENT_CONFIG_NAME, 
IgniteThinClientConfigTemplate()))
 
+        if self.service.config.service_type == IgniteServiceType.THIN_JDBC:
+            
config_templates.append((IgnitePathAware.IGNITE_THIN_JDBC_CONFIG_NAME, 
IgniteThinJdbcConfigTemplate()))
+
         return config_templates
 
     def extend_config(self, config):
@@ -355,5 +358,9 @@ class IgniteApplicationSpec(IgniteSpec):
         return cmd
 
     def config_file_path(self):
-        return self.service.config_file if self.service.config.service_type == 
IgniteServiceType.NODE \
-            else self.service.thin_client_config_file
+        if self.service.config.service_type == IgniteServiceType.NODE:
+            return self.service.config_file
+        elif self.service.config.service_type == IgniteServiceType.THIN_CLIENT:
+            return self.service.thin_client_config_file
+        else:
+            return self.service.thin_jdbc_config_file
diff --git a/modules/ducktests/tests/ignitetest/services/utils/path.py 
b/modules/ducktests/tests/ignitetest/services/utils/path.py
index a1676d5887a..3bc02b32c80 100644
--- a/modules/ducktests/tests/ignitetest/services/utils/path.py
+++ b/modules/ducktests/tests/ignitetest/services/utils/path.py
@@ -173,6 +173,8 @@ class IgnitePathAware(PathAware, metaclass=ABCMeta):
 
     IGNITE_THIN_CLIENT_CONFIG_NAME = "ignite-thin-config.xml"
 
+    IGNITE_THIN_JDBC_CONFIG_NAME = "ignite-thin-jdbc-config.xml"
+
     IGNITE_LOG_CONFIG_NAME = "ignite-ducktape-log4j2.xml"
 
     @property
@@ -189,6 +191,13 @@ class IgnitePathAware(PathAware, metaclass=ABCMeta):
         """
         return os.path.join(self.config_dir, 
IgnitePathAware.IGNITE_THIN_CLIENT_CONFIG_NAME)
 
+    @property
+    def thin_jdbc_config_file(self):
+        """
+        :return: path to thin JDBC driver config file
+        """
+        return os.path.join(self.config_dir, 
IgnitePathAware.IGNITE_THIN_JDBC_CONFIG_NAME)
+
     @property
     def log_config_file(self):
         """
diff --git 
a/modules/ducktests/tests/ignitetest/services/utils/templates/thin_jdbc_config.xml.j2
 
b/modules/ducktests/tests/ignitetest/services/utils/templates/thin_jdbc_config.xml.j2
new file mode 100644
index 00000000000..0c72cddc362
--- /dev/null
+++ 
b/modules/ducktests/tests/ignitetest/services/utils/templates/thin_jdbc_config.xml.j2
@@ -0,0 +1,46 @@
+{#
+ 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.
+#}
+
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:util="http://www.springframework.org/schema/util"; 
xsi:schemaLocation="
+       http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util.xsd";>
+    <bean class="org.apache.ignite.IgniteJdbcThinDataSource" 
id="thin.jdbc.cfg">
+        <property name="addresses">
+            <list>
+                {% for address in config.addresses %}
+                    <value>{{ address }}</value>
+                {% endfor %}
+            </list>
+        </property>
+
+        {% if config.username %}
+            <property name="username" value="{{ config.username }}"/>
+            <property name="password" value="{{ config.password }}"/>
+        {% endif %}
+
+        {% if config.ssl_params %}
+            <property name="sslMode" value="require"/>
+            <property name="sslClientCertificateKeyStoreUrl" value="{{ 
config.ssl_params.key_store_path }}"/>
+            <property name="sslClientCertificateKeyStorePassword" value="{{ 
config.ssl_params.key_store_password }}"/>
+            <property name="sslTrustCertificateKeyStoreUrl" value="{{ 
config.ssl_params.trust_store_path }}"/>
+            <property name="sslTrustCertificateKeyStorePassword" value="{{ 
config.ssl_params.trust_store_password }}"/>
+        {% endif %}
+    </bean>
+</beans>
diff --git a/modules/ducktests/tests/ignitetest/tests/jdbc_thin_test.py 
b/modules/ducktests/tests/ignitetest/tests/jdbc_thin_test.py
new file mode 100644
index 00000000000..766ca010261
--- /dev/null
+++ b/modules/ducktests/tests/ignitetest/tests/jdbc_thin_test.py
@@ -0,0 +1,66 @@
+# 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.
+
+"""
+This module contains Thin JDBC driver tests.
+"""
+
+from ignitetest.services.ignite import IgniteService
+from ignitetest.services.ignite_app import IgniteApplicationService
+from ignitetest.services.utils.control_utility import ControlUtility
+from ignitetest.services.utils.ignite_configuration import 
IgniteConfiguration, IgniteThinJdbcConfiguration
+from ignitetest.services.utils.ssl.client_connector_configuration import 
ClientConnectorConfiguration
+from ignitetest.utils import cluster, ignite_versions
+from ignitetest.utils.ignite_test import IgniteTest
+from ignitetest.utils.version import DEV_BRANCH, LATEST, IgniteVersion
+
+
+class JdbcThinTest(IgniteTest):
+    """
+    Thin JDBC driver test.
+    """
+    @cluster(num_nodes=2)
+    @ignite_versions(str(DEV_BRANCH), str(LATEST), 
version_prefix="server_version")
+    @ignite_versions(str(DEV_BRANCH), str(LATEST), 
version_prefix="thin_jdbc_version")
+    def test_simple_insert_select(self, server_version, thin_jdbc_version):
+        """
+        Smoke test ensuring the Thin JDBC driver just works doing simple SQL 
queries
+        and that the compationility between Ignite versions is preserved.
+        """
+        server_config = IgniteConfiguration(
+            version=IgniteVersion(server_version),
+            client_connector_configuration=ClientConnectorConfiguration())
+
+        ignite = IgniteService(self.test_context, server_config, 1)
+
+        ignite.start()
+
+        ControlUtility(ignite).activate()
+
+        address = ignite.nodes[0].account.hostname + ":" + 
str(server_config.client_connector_configuration.port)
+
+        app = IgniteApplicationService(
+            self.test_context,
+            IgniteThinJdbcConfiguration(
+                version=IgniteVersion(thin_jdbc_version),
+                addresses=[address]
+            ),
+            
java_class_name="org.apache.ignite.internal.ducktest.tests.jdbc.JdbcThinSelfTestApplication",
+            num_nodes=1)
+
+        app.start()
+        app.stop()
+
+        ignite.stop()

Reply via email to