You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2019/01/23 11:32:22 UTC

[ignite] branch master updated: IGNITE-5234: JDBC Thin Driver: implemented Connection.setNetworkTimeout method. This closes #5819.

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

vozerov 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 35882c5  IGNITE-5234: JDBC Thin Driver: implemented Connection.setNetworkTimeout method. This closes #5819.
35882c5 is described below

commit 35882c5d02d32772e31f8af8939bedb055026ef0
Author: sanpwc <la...@gmail.com>
AuthorDate: Wed Jan 23 14:32:12 2019 +0300

    IGNITE-5234: JDBC Thin Driver: implemented Connection.setNetworkTimeout method. This closes #5819.
---
 .../jdbc/suite/IgniteJdbcDriverTestSuite.java      |   2 +
 .../jdbc/thin/JdbcThinConnectionSelfTest.java      |  13 -
 .../thin/JdbcThinConnectionTimeoutSelfTest.java    | 284 +++++++++++++++++++++
 .../internal/jdbc/thin/JdbcThinConnection.java     |  38 +--
 .../ignite/internal/jdbc/thin/JdbcThinTcpIo.java   |  35 +++
 5 files changed, 343 insertions(+), 29 deletions(-)

diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
index b959242..6f8da20 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
@@ -50,6 +50,7 @@ import org.apache.ignite.jdbc.thin.JdbcThinConnectionMultipleAddressesTest;
 import org.apache.ignite.jdbc.thin.JdbcThinConnectionMvccEnabledSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinConnectionSSLTest;
 import org.apache.ignite.jdbc.thin.JdbcThinConnectionSelfTest;
+import org.apache.ignite.jdbc.thin.JdbcThinConnectionTimeoutSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinDataSourceSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinDeleteStatementSelfTest;
 import org.apache.ignite.jdbc.thin.JdbcThinDynamicIndexAtomicPartitionedNearSelfTest;
@@ -167,6 +168,7 @@ import org.junit.runners.Suite;
     JdbcThinErrorsSelfTest.class,
     JdbcThinStatementCancelSelfTest.class,
     JdbcThinStatementTimeoutSelfTest.class,
+    JdbcThinConnectionTimeoutSelfTest.class,
 
     JdbcThinInsertStatementSelfTest.class,
     JdbcThinUpdateStatementSelfTest.class,
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index cc2b235..76bff4f 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -1917,19 +1917,6 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
 
             final int timeout = 1000;
 
-            //Invalid executor
-            GridTestUtils.assertThrows(log,
-                new Callable<Object>() {
-                    @Override public Object call() throws Exception {
-                        conn.setNetworkTimeout(null, timeout);
-
-                        return null;
-                    }
-                },
-                SQLException.class,
-                "Executor cannot be null"
-            );
-
             //Invalid timeout
             GridTestUtils.assertThrows(log,
                 new Callable<Object>() {
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java
new file mode 100644
index 0000000..bfe3519
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionTimeoutSelfTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.jdbc.thin;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.SQLTimeoutException;
+import java.sql.Statement;
+import java.util.concurrent.Executor;
+import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Jdbc Thin Connection timeout tests.
+ */
+@RunWith(JUnit4.class)
+@SuppressWarnings("ThrowableNotThrown")
+public class JdbcThinConnectionTimeoutSelfTest extends JdbcThinAbstractSelfTest {
+
+    /** IP finder. */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** URL. */
+    private static final String URL = "jdbc:ignite:thin://127.0.0.1/";
+
+    /** Server thread pull size. */
+    private static final int SERVER_THREAD_POOL_SIZE = 4;
+
+    /** Nodes count. */
+    private static final byte NODES_COUNT = 3;
+
+    /** Max table rows. */
+    private static final int MAX_ROWS = 10000;
+
+    /** Executor stub */
+    private static final Executor EXECUTOR_STUB = (Runnable command) -> {};
+
+    /** Connection. */
+    private Connection conn;
+
+    /** Statement. */
+    private Statement stmt;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration<?, ?> cache = defaultCacheConfiguration();
+
+        cache.setCacheMode(PARTITIONED);
+        cache.setBackups(1);
+        cache.setWriteSynchronizationMode(FULL_SYNC);
+        cache.setSqlFunctionClasses(JdbcThinConnectionTimeoutSelfTest.class);
+        cache.setIndexedTypes(Integer.class, Integer.class);
+
+        cfg.setCacheConfiguration(cache);
+
+        TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+        disco.setIpFinder(IP_FINDER);
+
+        cfg.setDiscoverySpi(disco);
+
+        cfg.setClientConnectorConfiguration(new ClientConnectorConfiguration().setThreadPoolSize(SERVER_THREAD_POOL_SIZE));
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        startGridsMultiThreaded(NODES_COUNT);
+
+        for (int i = 0; i < MAX_ROWS; ++i)
+            grid(0).cache(DEFAULT_CACHE_NAME).put(i, i);
+    }
+
+    /**
+     * Called before execution of every test method in class.
+     *
+     * @throws Exception If failed.
+     */
+    @Before
+    public void before() throws Exception {
+        conn = DriverManager.getConnection(URL);
+
+        conn.setSchema('"' + DEFAULT_CACHE_NAME + '"');
+
+        stmt = conn.createStatement();
+
+        assert stmt != null;
+        assert !stmt.isClosed();
+    }
+
+    /**
+     * Called after execution of every test method in class.
+     *
+     * @throws Exception If failed.
+     */
+    @After
+    public void after() throws Exception {
+        if (stmt != null && !stmt.isClosed()) {
+            stmt.close();
+
+            assert stmt.isClosed();
+        }
+
+        conn.close();
+
+        assert stmt.isClosed();
+        assert conn.isClosed();
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testSettingNegativeConnectionTimeout() {
+
+        GridTestUtils.assertThrows(log,
+            () -> {
+                conn.setNetworkTimeout(EXECUTOR_STUB, -1);
+                return null;
+            },
+            SQLException.class, "Network timeout cannot be negative.");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testConnectionTimeoutRetrieval() throws Exception {
+        conn.setNetworkTimeout(EXECUTOR_STUB, 2000);
+        assertEquals(2000, conn.getNetworkTimeout());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testConnectionTimeout() throws Exception {
+
+        conn.setNetworkTimeout(EXECUTOR_STUB, 1000);
+
+        GridTestUtils.assertThrows(log,
+            () -> {
+                stmt.execute("select sleep_func(2000)");
+                return null;
+            },
+            SQLException.class, "Connection timed out.");
+
+        GridTestUtils.assertThrows(log,
+            () -> {
+                stmt.execute("select 1");
+                return null;
+            },
+            SQLException.class, "Statement is closed.");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testQueryTimeoutOccursBeforeConnectionTimeout() throws Exception {
+        conn.setNetworkTimeout(EXECUTOR_STUB, 10_000);
+
+        stmt.setQueryTimeout(1);
+
+        GridTestUtils.assertThrows(log, () -> {
+            stmt.executeQuery("select sleep_func(10) from Integer;");
+
+            return null;
+        }, SQLTimeoutException.class, "The query was cancelled while executing.");
+
+        stmt.execute("select 1");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testConnectionTimeoutUpdate() throws Exception {
+        conn.setNetworkTimeout(EXECUTOR_STUB, 5000);
+
+        stmt.execute("select sleep_func(1000)");
+
+        conn.setNetworkTimeout(EXECUTOR_STUB, 500);
+
+        GridTestUtils.assertThrows(log, () -> {
+            stmt.execute("select sleep_func(1000)");
+            return null;
+        }, SQLException.class, "Connection timed out.");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testCancelingTimedOutStatement() throws Exception {
+        conn.setNetworkTimeout(EXECUTOR_STUB, 1);
+
+        GridTestUtils.runAsync(
+            () -> {
+                try {
+                    Thread.sleep(1000);
+
+                    GridTestUtils.assertThrows(log,
+                        () -> {
+                            stmt.cancel();
+                            return null;
+                        },
+                        SQLException.class, "Statement is closed.");
+                }
+                catch (Exception e) {
+                    log.error("Unexpected exception.", e);
+
+                    fail("Unexpected exception");
+                }
+            });
+
+        GridTestUtils.runAsync(() -> {
+            try {
+                GridTestUtils.assertThrows(log,
+                    () -> {
+                        stmt.execute("select sleep_func(1000)");
+                        return null;
+                    },
+                    SQLException.class, "Connection timed out.");
+            }
+            catch (Exception e) {
+                log.error("Unexpected exception.", e);
+
+                fail("Unexpected exception");
+            }
+        });
+    }
+
+    /**
+     * @param v amount of milliseconds to sleep
+     * @return amount of milliseconds to sleep
+     */
+    @SuppressWarnings("unused")
+    @QuerySqlFunction
+    public static int sleep_func(int v) {
+        try {
+            Thread.sleep(v);
+        }
+        catch (InterruptedException ignored) {
+            // No-op
+        }
+        return v;
+    }
+}
\ No newline at end of file
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index fb3064e..eb8a328 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.jdbc.thin;
 
+import java.net.SocketTimeoutException;
 import java.sql.Array;
 import java.sql.BatchUpdateException;
 import java.sql.Blob;
@@ -29,6 +30,7 @@ import java.sql.PreparedStatement;
 import java.sql.SQLClientInfoException;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLPermission;
 import java.sql.SQLTimeoutException;
 import java.sql.SQLWarning;
 import java.sql.SQLXML;
@@ -85,6 +87,9 @@ public class JdbcThinConnection implements Connection {
     /** Request timeout period. */
     private static final int REQUEST_TIMEOUT_PERIOD = 1_000;
 
+    /** Network timeout permission */
+    private static final String SET_NETWORK_TIMEOUT_PERM = "setNetworkTimeout";
+
     /** Zero timeout as query timeout means no timeout. */
     static final int NO_TIMEOUT = 0;
 
@@ -112,9 +117,6 @@ public class JdbcThinConnection implements Connection {
     /** Current transaction holdability. */
     private int holdability;
 
-    /** Timeout. */
-    private int timeout;
-
     /** Ignite endpoint. */
     private JdbcThinTcpIo cliIo;
 
@@ -259,9 +261,6 @@ public class JdbcThinConnection implements Connection {
 
         JdbcThinStatement stmt  = new JdbcThinStatement(this, resSetHoldability, schema);
 
-        if (timeout > 0)
-            stmt.timeout(timeout);
-
         synchronized (stmtsMux) {
             stmts.add(stmt);
         }
@@ -292,9 +291,6 @@ public class JdbcThinConnection implements Connection {
 
         JdbcThinPreparedStatement stmt = new JdbcThinPreparedStatement(this, sql, resSetHoldability, schema);
 
-        if (timeout > 0)
-            stmt.timeout(timeout);
-
         synchronized (stmtsMux) {
             stmts.add(stmt);
         }
@@ -715,20 +711,22 @@ public class JdbcThinConnection implements Connection {
     @Override public void setNetworkTimeout(Executor executor, int ms) throws SQLException {
         ensureNotClosed();
 
-        if (executor == null)
-            throw new SQLException("Executor cannot be null.");
-
         if (ms < 0)
             throw new SQLException("Network timeout cannot be negative.");
 
-        timeout = ms;
+        SecurityManager secMgr = System.getSecurityManager();
+
+        if (secMgr != null)
+            secMgr.checkPermission(new SQLPermission(SET_NETWORK_TIMEOUT_PERM));
+
+        cliIo.timeout(ms);
     }
 
     /** {@inheritDoc} */
     @Override public int getNetworkTimeout() throws SQLException {
         ensureNotClosed();
 
-        return timeout;
+        return cliIo.timeout();
     }
 
     /**
@@ -805,7 +803,10 @@ public class JdbcThinConnection implements Connection {
         catch (Exception e) {
             onDisconnect();
 
-            throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
+            if (e instanceof SocketTimeoutException)
+                throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, e);
+            else
+                throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
         }
         finally {
             if (stmt != null && stmt.requestTimeout() != NO_TIMEOUT && reqTimeoutTimerTask != null)
@@ -848,7 +849,10 @@ public class JdbcThinConnection implements Connection {
         catch (Exception e) {
             onDisconnect();
 
-            throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
+            if (e instanceof SocketTimeoutException)
+                throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, e);
+            else
+                throw new SQLException("Failed to communicate with Ignite cluster.", SqlStateCode.CONNECTION_FAILURE, e);
         }
     }
 
@@ -1037,6 +1041,8 @@ public class JdbcThinConnection implements Connection {
                 else {
                     onDisconnect();
 
+                    if (err0 instanceof SocketTimeoutException)
+                        throw new SQLException("Connection timed out.", SqlStateCode.CONNECTION_FAILURE, err0);
                     throw new SQLException("Failed to communicate with Ignite cluster on JDBC streaming.",
                         SqlStateCode.CONNECTION_FAILURE, err0);
                 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
index 11dc221..206e62c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinTcpIo.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -137,6 +138,9 @@ public class JdbcThinTcpIo {
     /** Server index. */
     private volatile int srvIdx;
 
+    /** Socket. */
+    private Socket sock;
+
     /**
      * Constructor.
      *
@@ -258,6 +262,8 @@ public class JdbcThinTcpIo {
 
                 try {
                     sock.connect(addr, timeout);
+
+                    this.sock = sock;
                 }
                 catch (IOException e) {
                     throw new SQLException("Failed to connect to server [host=" + addr.getHostName() +
@@ -728,4 +734,33 @@ public class JdbcThinTcpIo {
             return (int)(nextIdx % len);
         }
     }
+
+    /**
+     * Enable/disable socket timeout with specified timeout.
+     *
+     * @param ms the specified timeout, in milliseconds.
+     * @throws SQLException if there is an error in the underlying protocol.
+     */
+    public void timeout(int ms) throws SQLException {
+        try {
+            sock.setSoTimeout(ms);
+        }
+        catch (SocketException e) {
+            throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+        }
+    }
+
+    /**
+     * Returns socket timeout.
+     *
+     * @throws SQLException if there is an error in the underlying protocol.
+     */
+    public int timeout() throws SQLException {
+        try {
+            return sock.getSoTimeout();
+        }
+        catch (SocketException e) {
+            throw new SQLException("Failed to set connection timeout.", SqlStateCode.INTERNAL_ERROR, e);
+        }
+    }
 }