Skip to content

Commit 577b264

Browse files
committed
Fix #78: avoid NullPointerException when checking if connected to AWS Keyspaces
also improve the check performed by method CassandraConnection.isNotConnectedToAmazonKeyspaces()
1 parent 2ea6432 commit 577b264

File tree

5 files changed

+79
-7
lines changed

5 files changed

+79
-7
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2222
- Add support for collections of `timestamp`, `date` and `time` values (see issue
2323
[#77](https://github.com/ing-bank/cassandra-jdbc-wrapper/issues/77)) by adding specific codecs for
2424
`java.sql.Timestamp`, `java.sql.Date` and `java.sql.Time` types.
25+
- Fix `NullPointerException` when checking if the connection using a pre-existing CQL session is done to an Amazon
26+
Keyspaces instance (see issue [#78](https://github.com/ing-bank/cassandra-jdbc-wrapper/issues/78)) and also improve
27+
the implementation of the method `CassandraConnection.isNotConnectedToAmazonKeyspaces()`.
2528

2629
## [4.14.0] - 2024-12-24
2730
### Added

Diff for: src/main/java/com/ing/data/cassandra/jdbc/CassandraConnection.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.sql.SQLWarning;
3939
import java.sql.Statement;
4040
import java.util.HashMap;
41+
import java.util.HashSet;
4142
import java.util.Map;
4243
import java.util.Objects;
4344
import java.util.Properties;
@@ -51,11 +52,12 @@
5152
import java.util.concurrent.Executors;
5253
import java.util.concurrent.Future;
5354
import java.util.concurrent.TimeUnit;
55+
import java.util.stream.Collectors;
5456

5557
import static com.ing.data.cassandra.jdbc.CassandraResultSet.DEFAULT_CONCURRENCY;
5658
import static com.ing.data.cassandra.jdbc.CassandraResultSet.DEFAULT_HOLDABILITY;
5759
import static com.ing.data.cassandra.jdbc.CassandraResultSet.DEFAULT_TYPE;
58-
import static com.ing.data.cassandra.jdbc.utils.AwsUtil.AWS_KEYSPACES_HOSTS_REGEX;
60+
import static com.ing.data.cassandra.jdbc.utils.AwsUtil.AWS_KEYSPACES_VALID_HOSTS;
5961
import static com.ing.data.cassandra.jdbc.utils.DriverUtil.PRECONFIGURED_CODECS;
6062
import static com.ing.data.cassandra.jdbc.utils.DriverUtil.safelyRegisterCodecs;
6163
import static com.ing.data.cassandra.jdbc.utils.DriverUtil.toStringWithoutSensitiveValues;
@@ -253,7 +255,21 @@ public boolean isNotConnectedToAmazonKeyspaces() {
253255
this.connectedToAmazonKeyspaces = false;
254256
// Valid Amazon Keyspaces endpoints are available here:
255257
// https://docs.aws.amazon.com/keyspaces/latest/devguide/programmatic.endpoints.html
256-
if (this.url.matches(AWS_KEYSPACES_HOSTS_REGEX)) {
258+
Set<String> connectionUrlOrEndpoints = new HashSet<>();
259+
if (this.url != null) {
260+
connectionUrlOrEndpoints.add(this.url);
261+
} else {
262+
try {
263+
connectionUrlOrEndpoints = this.metadata.getNodes().values().stream()
264+
.map(node -> node.getEndPoint().resolve().toString())
265+
.collect(Collectors.toSet());
266+
} catch (final Exception e) {
267+
LOG.debug("Failed to retrieve the contact points of the connection to determine if the connection "
268+
+ "is bound to AWS: {}", e.getMessage());
269+
}
270+
}
271+
if (connectionUrlOrEndpoints.stream().anyMatch(
272+
endpoint -> AWS_KEYSPACES_VALID_HOSTS.stream().anyMatch(endpoint::contains))) {
257273
this.connectedToAmazonKeyspaces = true;
258274
} else {
259275
// Check for the existence of the keyspace 'system_schema_mcs' (specific to Amazon Keyspaces).

Diff for: src/main/java/com/ing/data/cassandra/jdbc/utils/AwsUtil.java

+36-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
import java.net.URI;
2828
import java.sql.SQLTransientException;
29+
import java.util.HashSet;
30+
import java.util.Set;
2931

3032
import static com.ing.data.cassandra.jdbc.utils.ErrorConstants.AWS_SECRET_RETRIEVAL_FAILED;
3133

@@ -35,11 +37,41 @@
3537
public final class AwsUtil {
3638

3739
/**
38-
* Regular expression matching the valid hosts for Amazon Keyspaces.
40+
* Set of valid hosts for Amazon Keyspaces.
41+
* <p>
42+
* See: <a href="https://docs.aws.amazon.com/keyspaces/latest/devguide/programmatic.endpoints.html">
43+
* List of Amazon Keyspaces endpoints</a> (last update: March 2025).
44+
* </p>
3945
*/
40-
public static final String AWS_KEYSPACES_HOSTS_REGEX =
41-
"cassandra(-fips)?\\.(us(-gov)?|ap|eu|ca|me|sa|cn)-central|(north|south)?(east|west)?-[0-9]"
42-
+ "\\.amazonaws\\.com(\\.cn)?";
46+
public static final Set<String> AWS_KEYSPACES_VALID_HOSTS = new HashSet<String>() {
47+
{
48+
add("cassandra.us-east-1.amazonaws.com");
49+
add("cassandra-fips.us-east-1.amazonaws.com");
50+
add("cassandra.us-east-2.amazonaws.com");
51+
add("cassandra.us-west-1.amazonaws.com");
52+
add("cassandra.us-west-2.amazonaws.com");
53+
add("cassandra-fips.us-west-2.amazonaws.com");
54+
add("cassandra.af-south-1.amazonaws.com");
55+
add("cassandra.ap-east-1.amazonaws.com");
56+
add("cassandra.ap-south-1.amazonaws.com");
57+
add("cassandra.ap-northeast-1.amazonaws.com");
58+
add("cassandra.ap-northeast-2.amazonaws.com");
59+
add("cassandra.ap-southeast-1.amazonaws.com");
60+
add("cassandra.ap-southeast-2.amazonaws.com");
61+
add("cassandra.ca-central-1.amazonaws.com");
62+
add("cassandra.eu-central-1.amazonaws.com");
63+
add("cassandra.eu-west-1.amazonaws.com");
64+
add("cassandra.eu-west-2.amazonaws.com");
65+
add("cassandra.eu-west-3.amazonaws.com");
66+
add("cassandra.eu-north-1.amazonaws.com");
67+
add("cassandra.me-south-1.amazonaws.com");
68+
add("cassandra.sa-east-1.amazonaws.com");
69+
add("cassandra.us-gov-east-1.amazonaws.com");
70+
add("cassandra.us-gov-west-1.amazonaws.com");
71+
add("cassandra.cn-north-1.amazonaws.com.cn");
72+
add("cassandra.cn-northwest-1.amazonaws.com.cn");
73+
}
74+
};
4375

4476
/**
4577
* Name of the system property used to override the default endpoint of the Amazon Secrets manager.

Diff for: src/test/java/com/ing/data/cassandra/jdbc/AmazonKeyspacesIntegrationTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ static void setupAwsKeyspaces() {
109109
* cassandra.us-west-1.amazonaws.com
110110
* cassandra.us-west-2.amazonaws.com
111111
* cassandra-fips.us-west-2.amazonaws.com
112+
* cassandra.af-south-1.amazonaws.com
112113
* cassandra.ap-east-1.amazonaws.com
113114
* cassandra.ap-south-1.amazonaws.com
114115
* cassandra.ap-northeast-1.amazonaws.com
@@ -132,7 +133,7 @@ static void setupAwsKeyspaces() {
132133
* cassandra.cn-north-1.amazonaws.com.cn
133134
* cassandra.cn-northwest-1.amazonaws.com.cn
134135
*
135-
* See: https://docs.aws.amazon.com/keyspaces/latest/devguide/programmatic.endpoints.html (Oct. 2024)
136+
* See: https://docs.aws.amazon.com/keyspaces/latest/devguide/programmatic.endpoints.html (Mar. 2025)
136137
*/
137138

138139
if (canRunTests()) {

Diff for: src/test/java/com/ing/data/cassandra/jdbc/JdbcRegressionUnitTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@
1414
package com.ing.data.cassandra.jdbc;
1515

1616
import com.datastax.oss.driver.api.core.ConsistencyLevel;
17+
import com.datastax.oss.driver.api.core.CqlSession;
1718
import com.datastax.oss.driver.api.core.data.CqlDuration;
1819
import com.datastax.oss.driver.api.core.data.TupleValue;
1920
import com.datastax.oss.driver.api.core.data.UdtValue;
21+
import com.ing.data.cassandra.jdbc.optionset.Default;
2022
import org.junit.jupiter.api.BeforeAll;
2123
import org.junit.jupiter.api.Test;
2224

2325
import java.io.ByteArrayInputStream;
2426
import java.math.BigDecimal;
2527
import java.net.InetAddress;
28+
import java.net.InetSocketAddress;
2629
import java.net.URL;
2730
import java.nio.charset.StandardCharsets;
2831
import java.sql.Blob;
@@ -53,6 +56,7 @@
5356
import java.util.Set;
5457
import java.util.UUID;
5558

59+
import static com.datastax.oss.driver.api.core.config.DriverExecutionProfile.DEFAULT_NAME;
5660
import static org.hamcrest.MatcherAssert.assertThat;
5761
import static org.hamcrest.Matchers.hasItem;
5862
import static org.hamcrest.Matchers.hasItems;
@@ -1157,4 +1161,20 @@ void testIngIssue77() throws Exception {
11571161
resultSet.close();
11581162
stmt2.close();
11591163
}
1164+
1165+
@Test
1166+
void testIngIssue78() throws SQLException {
1167+
final CqlSession cqlSession = CqlSession.builder()
1168+
.addContactPoint(new InetSocketAddress(
1169+
cassandraContainer.getContactPoint().getHostName(), cassandraContainer.getContactPoint().getPort())
1170+
)
1171+
.withLocalDatacenter("datacenter1")
1172+
.build();
1173+
final Connection cassandraConnection = new CassandraConnection(
1174+
cqlSession, KEYSPACE, ConsistencyLevel.LOCAL_ONE, false, new Default(), DEFAULT_NAME
1175+
);
1176+
// The following method call checking if the database is an Amazon Keyspaces instance should not throw
1177+
// an exception when the connection is created from a pre-existing session.
1178+
assertNotNull(cassandraConnection.getMetaData().getSQLKeywords());
1179+
}
11601180
}

0 commit comments

Comments
 (0)