You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/09/14 11:11:31 UTC
[22/28] ignite git commit: ignite-1250 JDBC driver: migration to
embedded Ignite client node
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementSelfTest.java
new file mode 100644
index 0000000..7898bc8
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcStatementSelfTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.jdbc2;
+
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.F;
+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.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.IgniteJdbcDriver.CFG_URL_PREFIX;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ * Statement test.
+ */
+public class JdbcStatementSelfTest extends GridCommonAbstractTest {
+ /** IP finder. */
+ private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** JDBC URL. */
+ private static final String BASE_URL = CFG_URL_PREFIX + "modules/clients/src/test/config/jdbc-config.xml";
+
+ /** SQL query. */
+ private static final String SQL = "select * from Person where age > 30";
+
+ /** Connection. */
+ private Connection conn;
+
+ /** Statement. */
+ private Statement stmt;
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ CacheConfiguration<?,?> cache = defaultCacheConfiguration();
+
+ cache.setCacheMode(PARTITIONED);
+ cache.setBackups(1);
+ cache.setWriteSynchronizationMode(FULL_SYNC);
+ cache.setIndexedTypes(
+ String.class, Person.class
+ );
+
+ cfg.setCacheConfiguration(cache);
+
+ TcpDiscoverySpi disco = new TcpDiscoverySpi();
+
+ disco.setIpFinder(IP_FINDER);
+
+ cfg.setDiscoverySpi(disco);
+
+ cfg.setConnectorConfiguration(new ConnectorConfiguration());
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ startGridsMultiThreaded(3);
+
+ IgniteCache<String, Person> cache = grid(0).cache(null);
+
+ assert cache != null;
+
+ cache.put("p1", new Person(1, "John", "White", 25));
+ cache.put("p2", new Person(2, "Joe", "Black", 35));
+ cache.put("p3", new Person(3, "Mike", "Green", 40));
+
+ Class.forName("org.apache.ignite.IgniteJdbcDriver");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTestsStopped() throws Exception {
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ conn = DriverManager.getConnection(BASE_URL);
+ stmt = conn.createStatement();
+
+ assertNotNull(stmt);
+ assertFalse(stmt.isClosed());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ if (stmt != null && !stmt.isClosed())
+ stmt.close();
+
+ conn.close();
+
+ assertTrue(stmt.isClosed());
+ assertTrue(conn.isClosed());
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testExecuteQuery() throws Exception {
+ ResultSet rs = stmt.executeQuery(SQL);
+
+ assert rs != null;
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ if (id == 2) {
+ assert "Joe".equals(rs.getString("firstName"));
+ assert "Black".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 35;
+ }
+ else if (id == 3) {
+ assert "Mike".equals(rs.getString("firstName"));
+ assert "Green".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 40;
+ }
+ else
+ assert false : "Wrong ID: " + id;
+
+ cnt++;
+ }
+
+ assert cnt == 2;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testExecute() throws Exception {
+ assert stmt.execute(SQL);
+
+ ResultSet rs = stmt.getResultSet();
+
+ assert rs != null;
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ if (id == 2) {
+ assert "Joe".equals(rs.getString("firstName"));
+ assert "Black".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 35;
+ }
+ else if (id == 3) {
+ assert "Mike".equals(rs.getString("firstName"));
+ assert "Green".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 40;
+ }
+ else
+ assert false : "Wrong ID: " + id;
+
+ cnt++;
+ }
+
+ assert cnt == 2;
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testMaxRows() throws Exception {
+ stmt.setMaxRows(1);
+
+ ResultSet rs = stmt.executeQuery(SQL);
+
+ assert rs != null;
+
+ int cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ if (id == 2) {
+ assert "Joe".equals(rs.getString("firstName"));
+ assert "Black".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 35;
+ }
+ else if (id == 3) {
+ assert "Mike".equals(rs.getString("firstName"));
+ assert "Green".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 40;
+ }
+ else
+ assert false : "Wrong ID: " + id;
+
+ cnt++;
+ }
+
+ assert cnt == 1;
+
+ stmt.setMaxRows(0);
+
+ rs = stmt.executeQuery(SQL);
+
+ assert rs != null;
+
+ cnt = 0;
+
+ while (rs.next()) {
+ int id = rs.getInt("id");
+
+ if (id == 2) {
+ assert "Joe".equals(rs.getString("firstName"));
+ assert "Black".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 35;
+ }
+ else if (id == 3) {
+ assert "Mike".equals(rs.getString("firstName"));
+ assert "Green".equals(rs.getString("lastName"));
+ assert rs.getInt("age") == 40;
+ }
+ else
+ assert false : "Wrong ID: " + id;
+
+ cnt++;
+ }
+
+ assert cnt == 2;
+ }
+
+ /**
+ * Person.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ private static class Person implements Serializable {
+ /** ID. */
+ @QuerySqlField
+ private final int id;
+
+ /** First name. */
+ @QuerySqlField(index = false)
+ private final String firstName;
+
+ /** Last name. */
+ @QuerySqlField(index = false)
+ private final String lastName;
+
+ /** Age. */
+ @QuerySqlField
+ private final int age;
+
+ /**
+ * @param id ID.
+ * @param firstName First name.
+ * @param lastName Last name.
+ * @param age Age.
+ */
+ private Person(int id, String firstName, String lastName, int age) {
+ assert !F.isEmpty(firstName);
+ assert !F.isEmpty(lastName);
+ assert age > 0;
+
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.age = age;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java
----------------------------------------------------------------------
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 b0c0c58..bac2f60 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
@@ -38,6 +38,7 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
public static TestSuite suite() throws Exception {
TestSuite suite = new TestSuite("Ignite JDBC Driver Test Suite");
+ // Thin client based driver tests
suite.addTest(new TestSuite(JdbcConnectionSelfTest.class));
suite.addTest(new TestSuite(JdbcStatementSelfTest.class));
suite.addTest(new TestSuite(JdbcPreparedStatementSelfTest.class));
@@ -47,6 +48,16 @@ public class IgniteJdbcDriverTestSuite extends TestSuite {
suite.addTest(new TestSuite(JdbcEmptyCacheSelfTest.class));
suite.addTest(new TestSuite(JdbcLocalCachesSelfTest.class));
+ // Ignite client node based driver tests
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcConnectionSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcStatementSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcPreparedStatementSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcResultSetSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcComplexQuerySelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcMetadataSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcEmptyCacheSelfTest.class));
+ suite.addTest(new TestSuite(org.apache.ignite.internal.jdbc2.JdbcLocalCachesSelfTest.class));
+
return suite;
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
index 6ba362e..7f8b523 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteJdbcDriver.java
@@ -17,17 +17,19 @@
package org.apache.ignite;
-
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
+import java.util.Arrays;
+import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
import org.apache.ignite.cache.affinity.AffinityKey;
import org.apache.ignite.internal.jdbc.JdbcConnection;
+import org.apache.ignite.logger.java.JavaLogger;
/**
* JDBC driver implementation for In-Memory Data Grid.
@@ -66,8 +68,47 @@ import org.apache.ignite.internal.jdbc.JdbcConnection;
* {@code IGNITE_HOME/libs} folder. So if you are using JDBC driver in any external tool,
* you have to add main Ignite JAR will all dependencies to its classpath.
* <h1 class="header">Configuration</h1>
- * Internally JDBC driver <b>is based on Ignite Java client</b>. Therefore, all client
- * configuration properties can be applied to JDBC connection.
+ *
+ * JDBC driver can return two different types of connection: Ignite Java client based connection and
+ * Ignite client node based connection. Java client best connection is deprecated and left only for
+ * compatibility with previous version, so you should always use Ignite client node based mode.
+ * It is also preferable because it has much better performance.
+ *
+ * The type of returned connection depends on provided JDBC connection URL.
+ *
+ * <h2 class="header">Configuration of Ignite client node based connection</h2>
+ *
+ * JDBC connection URL has the following pattern: {@code jdbc:ignite:cfg://[<params>@]<config_url>}.<br>
+ *
+ * {@code <config_url>} represents any valid URL which points to Ignite configuration file. It is required.<br>
+ *
+ * {@code <params>} are optional and have the following format: {@code param1=value1:param2=value2:...:paramN=valueN}.<br>
+ *
+ * The following parameters are supported:
+ * <ul>
+ * <li>{@code cache} - cache name. If it is not defined than default cache will be used.</li>
+ * <li>
+ * {@code nodeId} - ID of node where query will be executed.
+ * It can be useful for querying through local caches.
+ * If node with provided ID doesn't exist, exception is thrown.
+ * </li>
+ * <li>
+ * {@code local} - query will be executed only on local node. Use this parameter with {@code nodeId} parameter.
+ * Default value is {@code false}.
+ * </li>
+ * <li>
+ * {@code collocated} - flag that used for optimization purposes. Whenever Ignite executes
+ * a distributed query, it sends sub-queries to individual cluster members.
+ * If you know in advance that the elements of your query selection are collocated
+ * together on the same node, usually based on some <b>affinity-key</b>, Ignite
+ * can make significant performance and network optimizations.
+ * Default value is {@code false}.
+ * </li>
+ * </ul>
+ *
+ * <h2 class="header">Configuration of Ignite Java client based connection</h2>
+ *
+ * All Ignite Java client configuration properties can be applied to JDBC connection of this type.
* <p>
* JDBC connection URL has the following pattern:
* {@code jdbc:ignite://<hostname>:<port>/<cache_name>?nodeId=<UUID>}<br>
@@ -197,10 +238,10 @@ import org.apache.ignite.internal.jdbc.JdbcConnection;
* <h1 class="header">Example</h1>
* <pre name="code" class="java">
* // Register JDBC driver.
- * Class.forName("org.apache.ignite.jdbc.IgniteJdbcDriver");
+ * Class.forName("org.apache.ignite.IgniteJdbcDriver");
*
* // Open JDBC connection.
- * Connection conn = DriverManager.getConnection("jdbc:ignite://localhost/cache");
+ * Connection conn = DriverManager.getConnection("jdbc:ignite:cfg//cache=persons@file:///etc/configs/ignite-jdbc.xml");
*
* // Query persons' names
* ResultSet rs = conn.createStatement().executeQuery("select name from Person");
@@ -231,6 +272,18 @@ public class IgniteJdbcDriver implements Driver {
/** Prefix for property names. */
private static final String PROP_PREFIX = "ignite.jdbc.";
+ /** Node ID parameter name. */
+ private static final String PARAM_NODE_ID = "nodeId";
+
+ /** Cache parameter name. */
+ private static final String PARAM_CACHE = "cache";
+
+ /** Local parameter name. */
+ private static final String PARAM_LOCAL = "local";
+
+ /** Collocated parameter name. */
+ private static final String PARAM_COLLOCATED = "collocated";
+
/** Hostname property name. */
public static final String PROP_HOST = PROP_PREFIX + "host";
@@ -238,14 +291,26 @@ public class IgniteJdbcDriver implements Driver {
public static final String PROP_PORT = PROP_PREFIX + "port";
/** Cache name property name. */
- public static final String PROP_CACHE = PROP_PREFIX + "cache";
+ public static final String PROP_CACHE = PROP_PREFIX + PARAM_CACHE;
/** Node ID property name. */
- public static final String PROP_NODE_ID = PROP_PREFIX + "nodeId";
+ public static final String PROP_NODE_ID = PROP_PREFIX + PARAM_NODE_ID;
+
+ /** Local property name. */
+ public static final String PROP_LOCAL = PROP_PREFIX + PARAM_LOCAL;
+
+ /** Collocated property name. */
+ public static final String PROP_COLLOCATED = PROP_PREFIX + PARAM_COLLOCATED;
+
+ /** Cache name property name. */
+ public static final String PROP_CFG = PROP_PREFIX + "cfg";
/** URL prefix. */
public static final String URL_PREFIX = "jdbc:ignite://";
+ /** Config URL prefix. */
+ public static final String CFG_URL_PREFIX = "jdbc:ignite:cfg://";
+
/** Default port. */
public static final int DFLT_PORT = 11211;
@@ -255,6 +320,9 @@ public class IgniteJdbcDriver implements Driver {
/** Minor version. */
private static final int MINOR_VER = 0;
+ /** Logger. */
+ private static final IgniteLogger LOG = new JavaLogger();
+
/**
* Register driver.
*/
@@ -272,12 +340,19 @@ public class IgniteJdbcDriver implements Driver {
if (!parseUrl(url, props))
throw new SQLException("URL is invalid: " + url);
- return new JdbcConnection(url, props);
+ if (url.startsWith(URL_PREFIX)) {
+ if (props.getProperty(PROP_CFG) != null)
+ LOG.warning(PROP_CFG + " property is not applicable for this URL.");
+
+ return new JdbcConnection(url, props);
+ }
+ else
+ return new org.apache.ignite.internal.jdbc2.JdbcConnection(url, props);
}
/** {@inheritDoc} */
@Override public boolean acceptsURL(String url) throws SQLException {
- return url.startsWith(URL_PREFIX);
+ return url.startsWith(URL_PREFIX) || url.startsWith(CFG_URL_PREFIX);
}
/** {@inheritDoc} */
@@ -285,49 +360,72 @@ public class IgniteJdbcDriver implements Driver {
if (!parseUrl(url, info))
throw new SQLException("URL is invalid: " + url);
- DriverPropertyInfo[] props = new DriverPropertyInfo[20];
-
- props[0] = new PropertyInfo("Hostname", info.getProperty(PROP_HOST), true);
- props[1] = new PropertyInfo("Port number", info.getProperty(PROP_PORT), "");
- props[2] = new PropertyInfo("Cache name", info.getProperty(PROP_CACHE), "");
- props[3] = new PropertyInfo("Node ID", info.getProperty(PROP_NODE_ID, ""));
- props[4] = new PropertyInfo("ignite.client.protocol", info.getProperty("ignite.client.protocol", "TCP"),
- "Communication protocol (TCP or HTTP).");
- props[5] = new PropertyInfo("ignite.client.connectTimeout", info.getProperty("ignite.client.connectTimeout", "0"),
- "Socket connection timeout.");
- props[6] = new PropertyInfo("ignite.client.tcp.noDelay", info.getProperty("ignite.client.tcp.noDelay", "true"),
- "Flag indicating whether TCP_NODELAY flag should be enabled for outgoing connections.");
- props[7] = new PropertyInfo("ignite.client.ssl.enabled", info.getProperty("ignite.client.ssl.enabled", "false"),
- "Flag indicating that SSL is needed for connection.");
- props[8] = new PropertyInfo("ignite.client.ssl.protocol", info.getProperty("ignite.client.ssl.protocol", "TLS"),
- "SSL protocol.");
- props[9] = new PropertyInfo("ignite.client.ssl.key.algorithm", info.getProperty("ignite.client.ssl.key.algorithm",
- "SunX509"), "Key manager algorithm.");
- props[10] = new PropertyInfo("ignite.client.ssl.keystore.location",
- info.getProperty("ignite.client.ssl.keystore.location", ""),
- "Key store to be used by client to connect with Ignite topology.");
- props[11] = new PropertyInfo("ignite.client.ssl.keystore.password",
- info.getProperty("ignite.client.ssl.keystore.password", ""), "Key store password.");
- props[12] = new PropertyInfo("ignite.client.ssl.keystore.type", info.getProperty("ignite.client.ssl.keystore.type",
- "jks"), "Key store type.");
- props[13] = new PropertyInfo("ignite.client.ssl.truststore.location",
- info.getProperty("ignite.client.ssl.truststore.location", ""),
- "Trust store to be used by client to connect with Ignite topology.");
- props[14] = new PropertyInfo("ignite.client.ssl.keystore.password",
- info.getProperty("ignite.client.ssl.truststore.password", ""), "Trust store password.");
- props[15] = new PropertyInfo("ignite.client.ssl.truststore.type", info.getProperty("ignite.client.ssl.truststore.type",
- "jks"), "Trust store type.");
- props[16] = new PropertyInfo("ignite.client.credentials", info.getProperty("ignite.client.credentials", ""),
- "Client credentials used in authentication process.");
- props[17] = new PropertyInfo("ignite.client.cache.top", info.getProperty("ignite.client.cache.top", "false"),
- "Flag indicating that topology is cached internally. Cache will be refreshed in the background with " +
- "interval defined by topologyRefreshFrequency property (see below).");
- props[18] = new PropertyInfo("ignite.client.topology.refresh", info.getProperty("ignite.client.topology.refresh",
- "2000"), "Topology cache refresh frequency (ms).");
- props[19] = new PropertyInfo("ignite.client.idleTimeout", info.getProperty("ignite.client.idleTimeout", "30000"),
- "Maximum amount of time that connection can be idle before it is closed (ms).");
-
- return props;
+ List<DriverPropertyInfo> props = Arrays.<DriverPropertyInfo>asList(
+ new PropertyInfo("Hostname", info.getProperty(PROP_HOST), ""),
+ new PropertyInfo("Port number", info.getProperty(PROP_PORT), ""),
+ new PropertyInfo("Cache name", info.getProperty(PROP_CACHE), ""),
+ new PropertyInfo("Node ID", info.getProperty(PROP_NODE_ID), ""),
+ new PropertyInfo("Local", info.getProperty(PROP_LOCAL), ""),
+ new PropertyInfo("Collocated", info.getProperty(PROP_COLLOCATED), "")
+ );
+
+ if (info.getProperty(PROP_CFG) != null)
+ props.add(new PropertyInfo("Configuration path", info.getProperty(PROP_CFG), ""));
+ else
+ props.addAll(Arrays.<DriverPropertyInfo>asList(
+ new PropertyInfo("ignite.client.protocol",
+ info.getProperty("ignite.client.protocol", "TCP"),
+ "Communication protocol (TCP or HTTP)."),
+ new PropertyInfo("ignite.client.connectTimeout",
+ info.getProperty("ignite.client.connectTimeout", "0"),
+ "Socket connection timeout."),
+ new PropertyInfo("ignite.client.tcp.noDelay",
+ info.getProperty("ignite.client.tcp.noDelay", "true"),
+ "Flag indicating whether TCP_NODELAY flag should be enabled for outgoing connections."),
+ new PropertyInfo("ignite.client.ssl.enabled",
+ info.getProperty("ignite.client.ssl.enabled", "false"),
+ "Flag indicating that SSL is needed for connection."),
+ new PropertyInfo("ignite.client.ssl.protocol",
+ info.getProperty("ignite.client.ssl.protocol", "TLS"),
+ "SSL protocol."),
+ new PropertyInfo("ignite.client.ssl.key.algorithm",
+ info.getProperty("ignite.client.ssl.key.algorithm", "SunX509"),
+ "Key manager algorithm."),
+ new PropertyInfo("ignite.client.ssl.keystore.location",
+ info.getProperty("ignite.client.ssl.keystore.location", ""),
+ "Key store to be used by client to connect with Ignite topology."),
+ new PropertyInfo("ignite.client.ssl.keystore.password",
+ info.getProperty("ignite.client.ssl.keystore.password", ""),
+ "Key store password."),
+ new PropertyInfo("ignite.client.ssl.keystore.type",
+ info.getProperty("ignite.client.ssl.keystore.type", "jks"),
+ "Key store type."),
+ new PropertyInfo("ignite.client.ssl.truststore.location",
+ info.getProperty("ignite.client.ssl.truststore.location", ""),
+ "Trust store to be used by client to connect with Ignite topology."),
+ new PropertyInfo("ignite.client.ssl.keystore.password",
+ info.getProperty("ignite.client.ssl.truststore.password", ""),
+ "Trust store password."),
+ new PropertyInfo("ignite.client.ssl.truststore.type",
+ info.getProperty("ignite.client.ssl.truststore.type", "jks"),
+ "Trust store type."),
+ new PropertyInfo("ignite.client.credentials",
+ info.getProperty("ignite.client.credentials", ""),
+ "Client credentials used in authentication process."),
+ new PropertyInfo("ignite.client.cache.top",
+ info.getProperty("ignite.client.cache.top", "false"),
+ "Flag indicating that topology is cached internally. Cache will be refreshed in the " +
+ "background with interval defined by topologyRefreshFrequency property (see below)."),
+ new PropertyInfo("ignite.client.topology.refresh",
+ info.getProperty("ignite.client.topology.refresh", "2000"),
+ "Topology cache refresh frequency (ms)."),
+ new PropertyInfo("ignite.client.idleTimeout",
+ info.getProperty("ignite.client.idleTimeout", "30000"),
+ "Maximum amount of time that connection can be idle before it is closed (ms).")
+ )
+ );
+
+ return props.toArray(new DriverPropertyInfo[0]);
}
/** {@inheritDoc} */
@@ -358,9 +456,44 @@ public class IgniteJdbcDriver implements Driver {
* @return Whether URL is valid.
*/
private boolean parseUrl(String url, Properties props) {
- if (url == null || !url.startsWith(URL_PREFIX) || url.length() == URL_PREFIX.length())
+ if (url == null)
+ return false;
+
+ if (url.startsWith(URL_PREFIX) && url.length() > URL_PREFIX.length())
+ return parseJdbcUrl(url, props);
+ else if (url.startsWith(CFG_URL_PREFIX) && url.length() > CFG_URL_PREFIX.length())
+ return parseJdbcConfigUrl(url, props);
+
+ return false;
+ }
+
+ /**
+ * @param url Url.
+ * @param props Properties.
+ */
+ private boolean parseJdbcConfigUrl(String url, Properties props) {
+ url = url.substring(CFG_URL_PREFIX.length());
+
+ String[] parts = url.split("@");
+
+ if (parts.length > 2)
return false;
+ if (parts.length == 2) {
+ if (!parseParameters(parts[0], ":", props))
+ return false;
+ }
+
+ props.setProperty(PROP_CFG, parts[parts.length - 1]);
+
+ return true;
+ }
+
+ /**
+ * @param url Url.
+ * @param props Properties.
+ */
+ private boolean parseJdbcUrl(String url, Properties props) {
url = url.substring(URL_PREFIX.length());
String[] parts = url.split("\\?");
@@ -369,7 +502,7 @@ public class IgniteJdbcDriver implements Driver {
return false;
if (parts.length == 2)
- if (!parseUrlParameters(parts[1], props))
+ if (!parseParameters(parts[1], "&", props))
return false;
parts = parts[0].split("/");
@@ -406,12 +539,13 @@ public class IgniteJdbcDriver implements Driver {
/**
* Validates and parses URL parameters.
*
- * @param urlParams URL parameters string.
+ * @param val Parameters string.
+ * @param delim Delimiter.
* @param props Properties.
* @return Whether URL parameters string is valid.
*/
- private boolean parseUrlParameters(String urlParams, Properties props) {
- String[] params = urlParams.split("&");
+ private boolean parseParameters(String val, String delim, Properties props) {
+ String[] params = val.split(delim);
for (String param : params) {
String[] pair = param.split("=");
@@ -430,13 +564,6 @@ public class IgniteJdbcDriver implements Driver {
* convenient constructors.
*/
private static class PropertyInfo extends DriverPropertyInfo {
- /**
- * @param name Name.
- * @param val Value.
- */
- private PropertyInfo(String name, String val) {
- super(name, val);
- }
/**
* @param name Name.
@@ -448,29 +575,5 @@ public class IgniteJdbcDriver implements Driver {
description = desc;
}
-
- /**
- * @param name Name.
- * @param val Value.
- * @param required Required flag.
- */
- private PropertyInfo(String name, String val, boolean required) {
- super(name, val);
-
- this.required = required;
- }
-
- /**
- * @param name Name.
- * @param val Value.
- * @param desc Description.
- * @param required Required flag.
- */
- private PropertyInfo(String name, String val, String desc, boolean required) {
- super(name, val);
-
- description = desc;
- this.required = required;
- }
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 546a33d..1e4c8b7 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -356,6 +356,9 @@ public final class IgniteSystemProperties {
/** Number of times pending cache objects will be dumped to the log in case of partition exchange timeout. */
public static final String IGNITE_DUMP_PENDING_OBJECTS_THRESHOLD = "IGNITE_DUMP_PENDING_OBJECTS_THRESHOLD";
+ /** JDBC driver cursor remove delay. */
+ public static final String IGNITE_JDBC_DRIVER_CURSOR_REMOVE_DELAY = "IGNITE_JDBC_DRIVER_CURSOR_RMV_DELAY";
+
/**
* Enforces singleton.
*/
@@ -517,4 +520,4 @@ public final class IgniteSystemProperties {
public static Properties snapshot() {
return (Properties)System.getProperties().clone();
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
index 0116ace..a4be6f5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
@@ -56,7 +56,11 @@ import static org.apache.ignite.IgniteJdbcDriver.PROP_PORT;
/**
* JDBC connection implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
public class JdbcConnection implements Connection {
/** Validation task name. */
private static final String VALID_TASK_NAME =
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnectionInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnectionInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnectionInfo.java
deleted file mode 100644
index 36fa0aa..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnectionInfo.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.ignite.internal.jdbc;
-
-/**
- * Connection properties.
- */
-public class JdbcConnectionInfo {
- /** URL. */
- private final String url;
-
- /** Hostname. */
- private String hostname;
-
- /** Port number. */
- private int port;
-
- /** Cache name. */
- private String cacheName;
-
- /**
- * @param url URL.
- */
- JdbcConnectionInfo(String url) {
- this.url = url;
- }
-
- /**
- * @return URL.
- */
- public String url() {
- return url;
- }
-
- /**
- * @return Hostname.
- */
- public String hostname() {
- return hostname;
- }
-
- /**
- * @param hostname Hostname.
- */
- public void hostname(String hostname) {
- this.hostname = hostname;
- }
-
- /**
- * @return Port number.
- */
- public int port() {
- return port;
- }
-
- /**
- * @param port Port number.
- */
- public void port(int port) {
- this.port = port;
- }
-
- /**
- * @return Cache name.
- */
- public String cacheName() {
- return cacheName;
- }
-
- /**
- * @param cacheName Cache name.
- */
- public void cacheName(String cacheName) {
- this.cacheName = cacheName;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
index df26412..e2fbe05 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcDatabaseMetadata.java
@@ -39,8 +39,12 @@ import static java.sql.RowIdLifetime.ROWID_UNSUPPORTED;
/**
* JDBC database metadata implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
@SuppressWarnings("RedundantCast")
+@Deprecated
public class JdbcDatabaseMetadata implements DatabaseMetaData {
/** Task name. */
private static final String TASK_NAME =
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
index 6dfaa18..7e5358b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcPreparedStatement.java
@@ -41,7 +41,11 @@ import java.util.Calendar;
/**
* JDBC prepared statement implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
public class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement {
/** SQL query. */
private final String sql;
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
index 1566006..5961279 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSet.java
@@ -49,7 +49,11 @@ import org.apache.ignite.internal.util.typedef.internal.U;
/**
* JDBC result set implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
public class JdbcResultSet implements ResultSet {
/** Task name. */
private static final String TASK_NAME =
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
index afe1d28..75fe522 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcResultSetMetadata.java
@@ -23,7 +23,11 @@ import java.util.List;
/**
* JDBC result set metadata implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
public class JdbcResultSetMetadata implements ResultSetMetaData {
/** Column width. */
private static final int COL_WIDTH = 30;
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
index caa8495..0f4e08c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcStatement.java
@@ -36,7 +36,11 @@ import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
/**
* JDBC statement implementation.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
public class JdbcStatement implements Statement {
/** Task name. */
private static final String TASK_NAME =
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcUtils.java
index 46e3cfa..ecea21f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/JdbcUtils.java
@@ -47,7 +47,11 @@ import static java.sql.Types.VARCHAR;
/**
* Utility methods for JDBC driver.
+ *
+ * @deprecated Using Ignite client node based JDBC driver is preferable.
+ * See documentation of {@link org.apache.ignite.IgniteJdbcDriver} for details.
*/
+@Deprecated
class JdbcUtils {
/** Marshaller. */
private static final Marshaller MARSHALLER = new JdkMarshaller();
http://git-wip-us.apache.org/repos/asf/ignite/blob/ebb9e2e9/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
new file mode 100644
index 0000000..00eb6b5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcConnection.java
@@ -0,0 +1,777 @@
+/*
+ * 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.jdbc2;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteClientDisconnectedException;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteJdbcDriver;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cluster.ClusterGroup;
+import org.apache.ignite.compute.ComputeTaskTimeoutException;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
+import org.apache.ignite.internal.util.future.GridFutureAdapter;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.resources.IgniteInstanceResource;
+
+import static java.sql.ResultSet.CONCUR_READ_ONLY;
+import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
+import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_CACHE;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_CFG;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_COLLOCATED;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_LOCAL;
+import static org.apache.ignite.IgniteJdbcDriver.PROP_NODE_ID;
+
+/**
+ * JDBC connection implementation.
+ */
+public class JdbcConnection implements Connection {
+ /**
+ * Ignite nodes cache.
+ *
+ * The key is result of concatenation of the following properties:
+ * <ol>
+ * <li>{@link IgniteJdbcDriver#PROP_CFG}</li>
+ * </ol>
+ */
+ private static final ConcurrentMap<String, IgniteNodeFuture> NODES = new ConcurrentHashMap<>();
+
+ /** Ignite ignite. */
+ private final Ignite ignite;
+
+ /** Node key. */
+ private final String cfg;
+
+ /** Cache name. */
+ private String cacheName;
+
+ /** Closed flag. */
+ private boolean closed;
+
+ /** URL. */
+ private String url;
+
+ /** Node ID. */
+ private UUID nodeId;
+
+ /** Local query flag. */
+ private boolean locQry;
+
+ /** Collocated query flag. */
+ private boolean collocatedQry;
+
+ /** Statements. */
+ final Set<JdbcStatement> statements = new HashSet<>();
+
+ /**
+ * Creates new connection.
+ *
+ * @param url Connection URL.
+ * @param props Additional properties.
+ * @throws SQLException In case Ignite node failed to start.
+ */
+ public JdbcConnection(String url, Properties props) throws SQLException {
+ assert url != null;
+ assert props != null;
+
+ this.url = url;
+
+ this.cacheName = props.getProperty(PROP_CACHE);
+ this.locQry = Boolean.parseBoolean(props.getProperty(PROP_LOCAL));
+ this.collocatedQry = Boolean.parseBoolean(props.getProperty(PROP_COLLOCATED));
+
+ String nodeIdProp = props.getProperty(PROP_NODE_ID);
+
+ if (nodeIdProp != null)
+ this.nodeId = UUID.fromString(nodeIdProp);
+
+ try {
+ cfg = props.getProperty(PROP_CFG);
+
+ ignite = getIgnite(cfg);
+
+ if (!isValid(2))
+ throw new SQLException("Client is invalid. Probably cache name is wrong.");
+ }
+ catch (Exception e) {
+ close();
+
+ if (e instanceof SQLException)
+ throw (SQLException)e;
+ else
+ throw new SQLException("Failed to start Ignite node.", e);
+ }
+ }
+
+ /**
+ * @param cfgUrl Config url.
+ */
+ private Ignite getIgnite(String cfgUrl) throws IgniteCheckedException {
+ while (true) {
+ IgniteNodeFuture fut = NODES.get(cfg);
+
+ if (fut == null) {
+ fut = new IgniteNodeFuture();
+
+ IgniteNodeFuture old = NODES.putIfAbsent(cfg, fut);
+
+ if (old != null)
+ fut = old;
+ else {
+ try {
+ Ignite ignite = Ignition.start(loadConfiguration(cfgUrl));
+
+ fut.onDone(ignite);
+ }
+ catch (IgniteException e) {
+ fut.onDone(e);
+ }
+
+ return fut.get();
+ }
+ }
+
+ if (fut.acquire())
+ return fut.get();
+ else
+ NODES.remove(cfg, fut);
+ }
+ }
+
+ /**
+ * @param cfgUrl Config URL.
+ */
+ private IgniteConfiguration loadConfiguration(String cfgUrl) {
+ try {
+ IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> cfgMap =
+ IgnitionEx.loadConfigurations(cfgUrl);
+
+ IgniteConfiguration cfg = F.first(cfgMap.get1());
+
+ if (cfg.getGridName() == null)
+ cfg.setGridName("ignite-jdbc-driver-" + UUID.randomUUID().toString());
+
+ return cfg;
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Statement createStatement() throws SQLException {
+ return createStatement(TYPE_FORWARD_ONLY, CONCUR_READ_ONLY, HOLD_CURSORS_OVER_COMMIT);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql) throws SQLException {
+ ensureNotClosed();
+
+ return prepareStatement(sql, TYPE_FORWARD_ONLY, CONCUR_READ_ONLY, HOLD_CURSORS_OVER_COMMIT);
+ }
+
+ /** {@inheritDoc} */
+ @Override public CallableStatement prepareCall(String sql) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Callable functions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String nativeSQL(String sql) throws SQLException {
+ ensureNotClosed();
+
+ return sql;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setAutoCommit(boolean autoCommit) throws SQLException {
+ ensureNotClosed();
+
+ if (!autoCommit)
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean getAutoCommit() throws SQLException {
+ ensureNotClosed();
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void commit() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void rollback() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void close() throws SQLException {
+ if (closed)
+ return;
+
+ closed = true;
+
+ IgniteNodeFuture fut = NODES.get(cfg);
+
+ if (fut != null && fut.release()) {
+ NODES.remove(cfg);
+
+ if (ignite != null)
+ ignite.close();
+ }
+
+ for (Iterator<JdbcStatement> it = statements.iterator(); it.hasNext();) {
+ JdbcStatement stmt = it.next();
+
+ stmt.closeInternal();
+
+ it.remove();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isClosed() throws SQLException {
+ return closed;
+ }
+
+ /** {@inheritDoc} */
+ @Override public DatabaseMetaData getMetaData() throws SQLException {
+ ensureNotClosed();
+
+ return new JdbcDatabaseMetadata(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setReadOnly(boolean readOnly) throws SQLException {
+ ensureNotClosed();
+
+ if (!readOnly)
+ throw new SQLFeatureNotSupportedException("Updates are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isReadOnly() throws SQLException {
+ ensureNotClosed();
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setCatalog(String catalog) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Catalogs are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getCatalog() throws SQLException {
+ ensureNotClosed();
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTransactionIsolation(int level) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getTransactionIsolation() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public SQLWarning getWarnings() throws SQLException {
+ ensureNotClosed();
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clearWarnings() throws SQLException {
+ ensureNotClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Statement createStatement(int resSetType, int resSetConcurrency) throws SQLException {
+ return createStatement(resSetType, resSetConcurrency, HOLD_CURSORS_OVER_COMMIT);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql, int resSetType,
+ int resSetConcurrency) throws SQLException {
+ ensureNotClosed();
+
+ return prepareStatement(sql, resSetType, resSetConcurrency, HOLD_CURSORS_OVER_COMMIT);
+ }
+
+ /** {@inheritDoc} */
+ @Override public CallableStatement prepareCall(String sql, int resSetType,
+ int resSetConcurrency) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Callable functions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Map<String, Class<?>> getTypeMap() throws SQLException {
+ throw new SQLFeatureNotSupportedException("Types mapping is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Types mapping is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setHoldability(int holdability) throws SQLException {
+ ensureNotClosed();
+
+ if (holdability != HOLD_CURSORS_OVER_COMMIT)
+ throw new SQLFeatureNotSupportedException("Invalid holdability (transactions are not supported).");
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getHoldability() throws SQLException {
+ ensureNotClosed();
+
+ return HOLD_CURSORS_OVER_COMMIT;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Savepoint setSavepoint() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Savepoint setSavepoint(String name) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void rollback(Savepoint savepoint) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Transactions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Statement createStatement(int resSetType, int resSetConcurrency,
+ int resSetHoldability) throws SQLException {
+ ensureNotClosed();
+
+ if (resSetType != TYPE_FORWARD_ONLY)
+ throw new SQLFeatureNotSupportedException("Invalid result set type (only forward is supported.)");
+
+ if (resSetConcurrency != CONCUR_READ_ONLY)
+ throw new SQLFeatureNotSupportedException("Invalid concurrency (updates are not supported).");
+
+ if (resSetHoldability != HOLD_CURSORS_OVER_COMMIT)
+ throw new SQLFeatureNotSupportedException("Invalid holdability (transactions are not supported).");
+
+ JdbcStatement stmt = new JdbcStatement(this);
+
+ statements.add(stmt);
+
+ return stmt;
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql, int resSetType, int resSetConcurrency,
+ int resSetHoldability) throws SQLException {
+ ensureNotClosed();
+
+ if (resSetType != TYPE_FORWARD_ONLY)
+ throw new SQLFeatureNotSupportedException("Invalid result set type (only forward is supported.)");
+
+ if (resSetConcurrency != CONCUR_READ_ONLY)
+ throw new SQLFeatureNotSupportedException("Invalid concurrency (updates are not supported).");
+
+ if (resSetHoldability != HOLD_CURSORS_OVER_COMMIT)
+ throw new SQLFeatureNotSupportedException("Invalid holdability (transactions are not supported).");
+
+ JdbcPreparedStatement stmt = new JdbcPreparedStatement(this, sql);
+
+ statements.add(stmt);
+
+ return stmt;
+ }
+
+ /** {@inheritDoc} */
+ @Override public CallableStatement prepareCall(String sql, int resSetType, int resSetConcurrency,
+ int resSetHoldability) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Callable functions are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Updates are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql, int[] colIndexes) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Updates are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public PreparedStatement prepareStatement(String sql, String[] colNames) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("Updates are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Clob createClob() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Blob createBlob() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public NClob createNClob() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public SQLXML createSQLXML() throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isValid(int timeout) throws SQLException {
+ ensureNotClosed();
+
+ if (timeout < 0)
+ throw new SQLException("Invalid timeout: " + timeout);
+
+ try {
+ JdbcConnectionValidationTask task = new JdbcConnectionValidationTask(cacheName,
+ nodeId == null ? ignite : null);
+
+ if (nodeId != null) {
+ ClusterGroup grp = ignite.cluster().forServers().forNodeId(nodeId);
+
+ if (grp.nodes().isEmpty())
+ throw new SQLException("Failed to establish connection with node (is it a server node?): " +
+ nodeId);
+
+ assert grp.nodes().size() == 1;
+
+ if (grp.node().isDaemon())
+ throw new SQLException("Failed to establish connection with node (is it a server node?): " +
+ nodeId);
+
+ IgniteCompute compute = ignite.compute(grp).withAsync();
+
+ compute.call(task);
+
+ return compute.<Boolean>future().get(timeout, SECONDS);
+ }
+ else
+ return task.call();
+ }
+ catch (IgniteClientDisconnectedException | ComputeTaskTimeoutException e) {
+ throw new SQLException("Failed to establish connection.", e);
+ }
+ catch (IgniteException ignored) {
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setClientInfo(String name, String val) throws SQLClientInfoException {
+ throw new UnsupportedOperationException("Client info is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setClientInfo(Properties props) throws SQLClientInfoException {
+ throw new UnsupportedOperationException("Client info is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getClientInfo(String name) throws SQLException {
+ ensureNotClosed();
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Properties getClientInfo() throws SQLException {
+ ensureNotClosed();
+
+ return new Properties();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public Struct createStruct(String typeName, Object[] attrs) throws SQLException {
+ ensureNotClosed();
+
+ throw new SQLFeatureNotSupportedException("SQL-specific types are not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public <T> T unwrap(Class<T> iface) throws SQLException {
+ if (!isWrapperFor(iface))
+ throw new SQLException("Connection is not a wrapper for " + iface.getName());
+
+ return (T)this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ return iface != null && iface == Connection.class;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setSchema(String schema) throws SQLException {
+ cacheName = schema;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getSchema() throws SQLException {
+ return cacheName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void abort(Executor executor) throws SQLException {
+ close();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setNetworkTimeout(Executor executor, int ms) throws SQLException {
+ throw new SQLFeatureNotSupportedException("Network timeout is not supported.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getNetworkTimeout() throws SQLException {
+ throw new SQLFeatureNotSupportedException("Network timeout is not supported.");
+ }
+
+ /**
+ * @return Ignite node.
+ */
+ Ignite ignite() {
+ return ignite;
+ }
+
+ /**
+ * @return Cache name.
+ */
+ String cacheName() {
+ return cacheName;
+ }
+
+ /**
+ * @return URL.
+ */
+ String url() {
+ return url;
+ }
+
+ /**
+ * @return Node ID.
+ */
+ UUID nodeId() {
+ return nodeId;
+ }
+
+ /**
+ * @return Local query flag.
+ */
+ boolean isLocalQuery() {
+ return locQry;
+ }
+
+ /**
+ * @return Collocated query flag.
+ */
+ boolean isCollocatedQuery() {
+ return collocatedQry;
+ }
+
+ /**
+ * Ensures that connection is not closed.
+ *
+ * @throws SQLException If connection is closed.
+ */
+ private void ensureNotClosed() throws SQLException {
+ if (closed)
+ throw new SQLException("Connection is closed.");
+ }
+
+ /**
+ * @return Internal statement.
+ * @throws SQLException In case of error.
+ */
+ JdbcStatement createStatement0() throws SQLException {
+ return (JdbcStatement)createStatement();
+ }
+
+ /**
+ * JDBC connection validation task.
+ */
+ private static class JdbcConnectionValidationTask implements IgniteCallable<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** Cache name. */
+ private final String cacheName;
+
+ /** Ignite. */
+ @IgniteInstanceResource
+ private Ignite ignite;
+
+ /**
+ * @param cacheName Cache name.
+ * @param ignite Ignite instance.
+ */
+ public JdbcConnectionValidationTask(String cacheName, Ignite ignite) {
+ this.cacheName = cacheName;
+ this.ignite = ignite;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Boolean call() {
+ return ignite.cache(cacheName) != null;
+ }
+ }
+
+ /**
+ *
+ */
+ private static class IgniteNodeFuture extends GridFutureAdapter<Ignite> {
+ /** Reference count. */
+ private final AtomicInteger refCnt = new AtomicInteger(1);
+
+ /**
+ *
+ */
+ public boolean acquire() {
+ while (true) {
+ int cur = refCnt.get();
+
+ if (cur == 0)
+ return false;
+
+ if (refCnt.compareAndSet(cur, cur + 1))
+ return true;
+ }
+ }
+
+ /**
+ *
+ */
+ public boolean release() {
+ while (true) {
+ int cur = refCnt.get();
+
+ assert cur > 0;
+
+ if (refCnt.compareAndSet(cur, cur - 1))
+ // CASed to 0.
+ return cur == 1;
+ }
+ }
+ }
+}