Skip to content

Commit 183005a

Browse files
committed
Redis Cluster: verify that mode specified for a connection in the URL scheme matches the server mode
1 parent 2653379 commit 183005a

File tree

9 files changed

+99
-11
lines changed

9 files changed

+99
-11
lines changed

driver/src/main/java/jdbc/RedisConnection.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package jdbc;
22

33
import jdbc.client.RedisClient;
4+
import jdbc.client.RedisMode;
5+
import org.jetbrains.annotations.NotNull;
46

57
import java.sql.*;
68
import java.util.Map;
@@ -20,6 +22,24 @@ public RedisConnection(RedisDriver driver, RedisClient client) {
2022
this.client = client;
2123
}
2224

25+
private static final String UNKNOWN_SERVER_MODE_MESSAGE = "Unable to get the server mode" +
26+
" using the \"INFO server\" command to check if the connection mode matches the server mode.";
27+
28+
public void checkConnectionMode() throws SQLException {
29+
RedisMode serverMode;
30+
try {
31+
serverMode = getMetaData().getDatabaseProductMode();
32+
} catch (SQLException e) {
33+
throw new SQLException(UNKNOWN_SERVER_MODE_MESSAGE, e);
34+
}
35+
if (serverMode == null)
36+
throw new SQLException(UNKNOWN_SERVER_MODE_MESSAGE);
37+
RedisMode clientMode = client.getMode();
38+
if (clientMode != serverMode)
39+
throw new SQLException(String.format("The connection mode \"%s\" does not match the server mode \"%s\".",
40+
clientMode.name(), serverMode.name()));
41+
}
42+
2343
private void checkClosed() throws SQLException {
2444
if (isClosed) throw new SQLException("Connection was previously closed.");
2545
}
@@ -85,7 +105,7 @@ public boolean isClosed() throws SQLException {
85105
}
86106

87107
@Override
88-
public DatabaseMetaData getMetaData() throws SQLException {
108+
public @NotNull RedisDatabaseMetaData getMetaData() throws SQLException {
89109
checkClosed();
90110
return new RedisDatabaseMetaData(this, driver);
91111
}

driver/src/main/java/jdbc/RedisDatabaseMetaData.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
package jdbc;
22

3+
import jdbc.client.RedisMode;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
6+
37
import java.sql.*;
8+
import java.util.Arrays;
49
import java.util.regex.Matcher;
510
import java.util.regex.Pattern;
611

12+
import static jdbc.utils.Utils.toCapitalized;
13+
714
public class RedisDatabaseMetaData implements DatabaseMetaData {
815

916
private final RedisConnection connection;
@@ -61,15 +68,28 @@ public boolean nullsAreSortedAtEnd() throws SQLException {
6168

6269
@Override
6370
public String getDatabaseProductName() throws SQLException {
64-
return "Redis";
71+
RedisMode mode = getDatabaseProductMode();
72+
return mode != null ? String.format("Redis %s", toCapitalized(mode.name())) : "Redis";
73+
}
74+
75+
@Nullable
76+
public RedisMode getDatabaseProductMode() throws SQLException {
77+
String modeName = getDatabaseProductInfo("redis_mode:(.+)");
78+
if (modeName == null) return null;
79+
return Arrays.stream(RedisMode.values())
80+
.filter(v -> modeName.equalsIgnoreCase(v.name())).findFirst().orElse(null);
6581
}
6682

6783
@Override
6884
public String getDatabaseProductVersion() throws SQLException {
85+
return getDatabaseProductInfo("redis_version:(.+)");
86+
}
87+
88+
private String getDatabaseProductInfo(@NotNull String fieldPattern) throws SQLException {
6989
Statement statement = connection.createStatement();
7090
ResultSet result = statement.executeQuery("INFO server");
7191
String serverInfo = result.next() ? result.getString(1) : null;
72-
Matcher matcher = serverInfo != null ? Pattern.compile("redis_version:(.+)").matcher(serverInfo) : null;
92+
Matcher matcher = serverInfo != null ? Pattern.compile(fieldPattern).matcher(serverInfo) : null;
7393
return matcher != null && matcher.find() ? matcher.group(1) : null;
7494
}
7595

driver/src/main/java/jdbc/RedisDriver.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
import jdbc.client.RedisClient;
44
import jdbc.client.RedisClientFactory;
55
import jdbc.properties.RedisDriverPropertyInfoHelper;
6+
import org.jetbrains.annotations.NotNull;
67

78
import java.sql.*;
89
import java.util.Properties;
910
import java.util.logging.Logger;
1011

12+
import static jdbc.properties.RedisDriverPropertyInfoHelper.VERIFY_CONNECTION_MODE;
13+
import static jdbc.properties.RedisDriverPropertyInfoHelper.VERIFY_CONNECTION_MODE_DEFAULT;
14+
import static jdbc.utils.Utils.getBoolean;
15+
1116
public class RedisDriver implements Driver {
1217

1318
static {
@@ -22,7 +27,19 @@ public class RedisDriver implements Driver {
2227
public Connection connect(String url, Properties info) throws SQLException {
2328
RedisClient client = RedisClientFactory.create(url, info);
2429
if (client == null) return null;
25-
return new RedisConnection(this, client);
30+
RedisConnection connection = new RedisConnection(this, client);
31+
try {
32+
checkConnectionModeIfNeeded(info, connection);
33+
} catch (SQLException e) {
34+
connection.close();
35+
throw e;
36+
}
37+
return connection;
38+
}
39+
40+
private static void checkConnectionModeIfNeeded(Properties info, @NotNull RedisConnection connection) throws SQLException {
41+
if (!getBoolean(info, VERIFY_CONNECTION_MODE, VERIFY_CONNECTION_MODE_DEFAULT)) return;
42+
connection.checkConnectionMode();
2643
}
2744

2845
@Override

driver/src/main/java/jdbc/client/RedisClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jdbc.client;
22

33
import jdbc.client.structures.result.RedisResult;
4+
import org.jetbrains.annotations.NotNull;
45

56
import java.sql.SQLException;
67

@@ -13,4 +14,6 @@ public interface RedisClient extends AutoCloseable {
1314
String getDatabase();
1415

1516
void close() throws SQLException;
17+
18+
@NotNull RedisMode getMode();
1619
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package jdbc.client;
2+
3+
public enum RedisMode {
4+
STANDALONE, CLUSTER, SENTINEL
5+
}

driver/src/main/java/jdbc/client/impl/cluster/RedisJedisClusterClient.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package jdbc.client.impl.cluster;
22

3+
import jdbc.client.RedisMode;
34
import jdbc.client.impl.RedisClientBase;
45
import jdbc.client.impl.standalone.RedisJedisClient;
56
import jdbc.client.structures.query.NodeHint;
@@ -19,10 +20,6 @@
1920

2021
public class RedisJedisClusterClient extends RedisClientBase {
2122

22-
private static final String UNSUPPORTED_COMMAND_MESSAGE = "Cluster mode does not support %s command";
23-
private static final String UNSUPPORTED_KEYS_PATTERN_COMMAND_MESSAGE = "Cluster mode only supports %s command"
24-
+ " with pattern containing hash-tag ( curly-brackets enclosed string )";
25-
2623
private static final Set<Command> UNSUPPORTED_COMMANDS = Set.of(Command.DBSIZE, Command.WAIT);
2724

2825

@@ -71,7 +68,8 @@ protected Object executeImpl(@NotNull RedisKeysPatternQuery query) throws SQLExc
7168
private static void checkSupportInClusterMode(@NotNull RedisKeysPatternQuery query) throws SQLException {
7269
String keysPattern = query.getKeysPattern();
7370
if (keysPattern == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(keysPattern))
74-
throw new SQLException(String.format(UNSUPPORTED_KEYS_PATTERN_COMMAND_MESSAGE, query.getCommand()));
71+
throw new SQLException(String.format("Cluster mode only supports %s command"
72+
+ " with pattern containing hash-tag ( curly-brackets enclosed string )", query.getCommand()));
7573
}
7674

7775

@@ -95,7 +93,7 @@ protected synchronized Object executeImpl(@NotNull RedisQuery query) throws SQLE
9593
private static void checkSupportInClusterMode(@NotNull RedisQuery query) throws SQLException {
9694
Command command = query.getCommand();
9795
if (UNSUPPORTED_COMMANDS.contains(command))
98-
throw new SQLException(String.format(UNSUPPORTED_COMMAND_MESSAGE, command));
96+
throw new SQLException(String.format("Cluster mode does not support %s command", command));
9997
}
10098

10199

@@ -116,4 +114,10 @@ public String getDatabase() {
116114
public synchronized void doClose() {
117115
jedisCluster.close();
118116
}
117+
118+
119+
@Override
120+
public @NotNull RedisMode getMode() {
121+
return RedisMode.CLUSTER;
122+
}
119123
}

driver/src/main/java/jdbc/client/impl/standalone/RedisJedisClient.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package jdbc.client.impl.standalone;
22

3+
import jdbc.client.RedisMode;
34
import jdbc.client.impl.RedisClientBase;
45
import jdbc.client.structures.query.RedisQuery;
56
import org.jetbrains.annotations.NotNull;
@@ -57,4 +58,11 @@ public synchronized String getDatabase() {
5758
protected synchronized void doClose() {
5859
jedis.close();
5960
}
61+
62+
63+
64+
@Override
65+
public @NotNull RedisMode getMode() {
66+
return RedisMode.STANDALONE;
67+
}
6068
}

driver/src/main/java/jdbc/properties/RedisDriverPropertyInfoHelper.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ public class RedisDriverPropertyInfoHelper {
1717
public static final String MAX_ATTEMPTS = "maxAttempts";
1818
public static final String SSL = "ssl";
1919
public static final String VERIFY_SERVER_CERTIFICATE = "verifyServerCertificate";
20+
2021
public static final String HOST_AND_PORT_MAPPING = "hostAndPortMapping";
22+
public static final String HOST_AND_PORT_MAPPING_DEFAULT = null;
23+
24+
public static final String VERIFY_CONNECTION_MODE = "verifyConnectionMode";
25+
public static final boolean VERIFY_CONNECTION_MODE_DEFAULT = true;
2126

2227
private static final String[] booleanChoices = new String[]{Boolean.TRUE.toString(), Boolean.FALSE.toString()};
2328

@@ -38,7 +43,9 @@ public static DriverPropertyInfo[] getPropertyInfo() {
3843
addPropInfo(propInfos, SSL, String.valueOf(CONFIG.isSsl()), "Enable SSL.", booleanChoices);
3944
addPropInfo(propInfos, VERIFY_SERVER_CERTIFICATE, String.valueOf(CONFIG.isVerifyServerCertificate()),
4045
"Configure a connection that uses SSL but does not verify the identity of the server.", booleanChoices);
41-
addPropInfo(propInfos, HOST_AND_PORT_MAPPING, null, "Host and port mapping.");
46+
addPropInfo(propInfos, HOST_AND_PORT_MAPPING, HOST_AND_PORT_MAPPING_DEFAULT, "Host and port mapping.");
47+
addPropInfo(propInfos, VERIFY_CONNECTION_MODE, String.valueOf(VERIFY_CONNECTION_MODE_DEFAULT),
48+
"Verify that mode specified for a connection in the URL scheme matches the server mode.", booleanChoices);
4249
return propInfos.toArray(new DriverPropertyInfo[0]);
4350
}
4451

driver/src/main/java/jdbc/utils/Utils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public static boolean isNullOrEmpty(@Nullable String value) {
3535
return value.toUpperCase(Locale.ENGLISH);
3636
}
3737

38+
public static @NotNull String toCapitalized(@NotNull String value) {
39+
return value.isEmpty() ? value : toUpperCase(value.substring(0, 1)) + toLowerCase(value.substring(1));
40+
}
41+
3842

3943
public static @Nullable String getFirst(@NotNull String[] elements) {
4044
return elements.length > 0 ? elements[0] : null;

0 commit comments

Comments
 (0)