Skip to content

Commit eb4dcda

Browse files
adutraolim7t
authored andcommitted
Return covariant future types from session async methods
Motivation: We experienced problems by the past trying to extend classes and override methods returning CompletionStage<SomeInterface>. It is usually better to return CompletionStage<? extends SomeInterface> since this can be overridden by CompletionStage<? extends SomeChildInterface> thanks to type covariance. We modified lots of methods but for some reason we forgot a few ones in CqlSession and DefaultSession. Modifications: - Modify return types of methods in CqlSession and DefaultSession from CompletionStage<T> to CompletionStage<? extends T>. Result: Methods in CqlSession and DefaultSession can now be overriden more easily.
1 parent a4cf1ae commit eb4dcda

File tree

8 files changed

+101
-17
lines changed

8 files changed

+101
-17
lines changed

core/revapi.json

Lines changed: 75 additions & 0 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -522,6 +522,81 @@
522
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
522
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
523
"elementKind": "method",
523
"elementKind": "method",
524
"justification": "Add ability to query specific nodes for virtual tables"
524
"justification": "Add ability to query specific nodes for virtual tables"
525+
},
526+
{
527+
"code": "java.method.returnTypeTypeParametersChanged",
528+
"old": "method java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.AsyncResultSet> com.datastax.oss.driver.api.core.CqlSession::executeAsync(com.datastax.oss.driver.api.core.cql.Statement<?>)",
529+
"new": "method java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.AsyncResultSet> com.datastax.oss.driver.api.core.CqlSession::executeAsync(com.datastax.oss.driver.api.core.cql.Statement<?>)",
530+
"oldType": "java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.AsyncResultSet>",
531+
"newType": "java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.AsyncResultSet>",
532+
"package": "com.datastax.oss.driver.api.core",
533+
"classQualifiedName": "com.datastax.oss.driver.api.core.CqlSession",
534+
"classSimpleName": "CqlSession",
535+
"methodName": "executeAsync",
536+
"oldArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta1",
537+
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
538+
"elementKind": "method",
539+
"justification": "Return covariant future types from session async methods"
540+
},
541+
{
542+
"code": "java.method.returnTypeTypeParametersChanged",
543+
"old": "method java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.AsyncResultSet> com.datastax.oss.driver.api.core.CqlSession::executeAsync(java.lang.String)",
544+
"new": "method java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.AsyncResultSet> com.datastax.oss.driver.api.core.CqlSession::executeAsync(java.lang.String)",
545+
"oldType": "java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.AsyncResultSet>",
546+
"newType": "java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.AsyncResultSet>",
547+
"package": "com.datastax.oss.driver.api.core",
548+
"classQualifiedName": "com.datastax.oss.driver.api.core.CqlSession",
549+
"classSimpleName": "CqlSession",
550+
"methodName": "executeAsync",
551+
"oldArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta1",
552+
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
553+
"elementKind": "method",
554+
"justification": "Return covariant future types from session async methods"
555+
},
556+
{
557+
"code": "java.method.returnTypeTypeParametersChanged",
558+
"old": "method java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.PrepareRequest)",
559+
"new": "method java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.PrepareRequest)",
560+
"oldType": "java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement>",
561+
"newType": "java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement>",
562+
"package": "com.datastax.oss.driver.api.core",
563+
"classQualifiedName": "com.datastax.oss.driver.api.core.CqlSession",
564+
"classSimpleName": "CqlSession",
565+
"methodName": "prepareAsync",
566+
"oldArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta1",
567+
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
568+
"elementKind": "method",
569+
"justification": "Return covariant future types from session async methods"
570+
},
571+
{
572+
"code": "java.method.returnTypeTypeParametersChanged",
573+
"old": "method java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.SimpleStatement)",
574+
"new": "method java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.SimpleStatement)",
575+
"oldType": "java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement>",
576+
"newType": "java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement>",
577+
"package": "com.datastax.oss.driver.api.core",
578+
"classQualifiedName": "com.datastax.oss.driver.api.core.CqlSession",
579+
"classSimpleName": "CqlSession",
580+
"methodName": "prepareAsync",
581+
"oldArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta1",
582+
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
583+
"elementKind": "method",
584+
"justification": "Return covariant future types from session async methods"
585+
},
586+
{
587+
"code": "java.method.returnTypeTypeParametersChanged",
588+
"old": "method java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(java.lang.String)",
589+
"new": "method java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement> com.datastax.oss.driver.api.core.CqlSession::prepareAsync(java.lang.String)",
590+
"oldType": "java.util.concurrent.CompletionStage<com.datastax.oss.driver.api.core.cql.PreparedStatement>",
591+
"newType": "java.util.concurrent.CompletionStage<? extends com.datastax.oss.driver.api.core.cql.PreparedStatement>",
592+
"package": "com.datastax.oss.driver.api.core",
593+
"classQualifiedName": "com.datastax.oss.driver.api.core.CqlSession",
594+
"classSimpleName": "CqlSession",
595+
"methodName": "prepareAsync",
596+
"oldArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta1",
597+
"newArchive": "com.datastax.oss:java-driver-core:jar:4.0.0-beta2-SNAPSHOT",
598+
"elementKind": "method",
599+
"justification": "Return covariant future types from session async methods"
525
}
600
}
526
]
601
]
527
}
602
}

core/src/main/java/com/datastax/oss/driver/api/core/CqlSession.java

Lines changed: 6 additions & 5 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -61,7 +61,7 @@ default ResultSet execute(@NonNull String query) {
61
* generally before the result is available).
61
* generally before the result is available).
62
*/
62
*/
63
@NonNull
63
@NonNull
64-
default CompletionStage<AsyncResultSet> executeAsync(@NonNull Statement<?> statement) {
64+
default CompletionStage<? extends AsyncResultSet> executeAsync(@NonNull Statement<?> statement) {
65
return Objects.requireNonNull(
65
return Objects.requireNonNull(
66
execute(statement, Statement.ASYNC), "The CQL processor should never return a null result");
66
execute(statement, Statement.ASYNC), "The CQL processor should never return a null result");
67
}
67
}
@@ -71,7 +71,7 @@ default CompletionStage<AsyncResultSet> executeAsync(@NonNull Statement<?> state
71
* generally before the result is available).
71
* generally before the result is available).
72
*/
72
*/
73
@NonNull
73
@NonNull
74-
default CompletionStage<AsyncResultSet> executeAsync(@NonNull String query) {
74+
default CompletionStage<? extends AsyncResultSet> executeAsync(@NonNull String query) {
75
return executeAsync(SimpleStatement.newInstance(query));
75
return executeAsync(SimpleStatement.newInstance(query));
76
}
76
}
77

77

@@ -167,7 +167,8 @@ default PreparedStatement prepare(@NonNull PrepareRequest request) {
167
* details.
167
* details.
168
*/
168
*/
169
@NonNull
169
@NonNull
170-
default CompletionStage<PreparedStatement> prepareAsync(@NonNull SimpleStatement statement) {
170+
default CompletionStage<? extends PreparedStatement> prepareAsync(
171+
@NonNull SimpleStatement statement) {
171
return Objects.requireNonNull(
172
return Objects.requireNonNull(
172
execute(new DefaultPrepareRequest(statement), PrepareRequest.ASYNC),
173
execute(new DefaultPrepareRequest(statement), PrepareRequest.ASYNC),
173
"The CQL prepare processor should never return a null result");
174
"The CQL prepare processor should never return a null result");
@@ -178,7 +179,7 @@ default CompletionStage<PreparedStatement> prepareAsync(@NonNull SimpleStatement
178
* sent, generally before the statement is prepared).
179
* sent, generally before the statement is prepared).
179
*/
180
*/
180
@NonNull
181
@NonNull
181-
default CompletionStage<PreparedStatement> prepareAsync(@NonNull String query) {
182+
default CompletionStage<? extends PreparedStatement> prepareAsync(@NonNull String query) {
182
return Objects.requireNonNull(
183
return Objects.requireNonNull(
183
execute(new DefaultPrepareRequest(query), PrepareRequest.ASYNC),
184
execute(new DefaultPrepareRequest(query), PrepareRequest.ASYNC),
184
"The CQL prepare processor should never return a null result");
185
"The CQL prepare processor should never return a null result");
@@ -194,7 +195,7 @@ default CompletionStage<PreparedStatement> prepareAsync(@NonNull String query) {
194
* with {@link PrepareRequest} directly.
195
* with {@link PrepareRequest} directly.
195
*/
196
*/
196
@NonNull
197
@NonNull
197-
default CompletionStage<PreparedStatement> prepareAsync(PrepareRequest request) {
198+
default CompletionStage<? extends PreparedStatement> prepareAsync(PrepareRequest request) {
198
return Objects.requireNonNull(
199
return Objects.requireNonNull(
199
execute(request, PrepareRequest.ASYNC),
200
execute(request, PrepareRequest.ASYNC),
200
"The CQL prepare processor should never return a null result");
201
"The CQL prepare processor should never return a null result");

core/src/main/java/com/datastax/oss/driver/internal/core/cql/DefaultAsyncResultSet.java

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -95,7 +95,7 @@ public boolean hasMorePages() {
95

95

96
@NonNull
96
@NonNull
97
@Override
97
@Override
98-
public CompletionStage<AsyncResultSet> fetchNextPage() throws IllegalStateException {
98+
public CompletionStage<? extends AsyncResultSet> fetchNextPage() throws IllegalStateException {
99
ByteBuffer nextState = executionInfo.getPagingState();
99
ByteBuffer nextState = executionInfo.getPagingState();
100
if (nextState == null) {
100
if (nextState == null) {
101
throw new IllegalStateException(
101
throw new IllegalStateException(

core/src/main/java/com/datastax/oss/driver/internal/core/session/DefaultSession.java

Lines changed: 2 additions & 2 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -128,13 +128,13 @@ public boolean isSchemaMetadataEnabled() {
128

128

129
@NonNull
129
@NonNull
130
@Override
130
@Override
131-
public CompletionStage<Metadata> setSchemaMetadataEnabled(@Nullable Boolean newValue) {
131+
public CompletionStage<? extends Metadata> setSchemaMetadataEnabled(@Nullable Boolean newValue) {
132
return metadataManager.setSchemaEnabled(newValue);
132
return metadataManager.setSchemaEnabled(newValue);
133
}
133
}
134

134

135
@NonNull
135
@NonNull
136
@Override
136
@Override
137-
public CompletionStage<Metadata> refreshSchemaAsync() {
137+
public CompletionStage<? extends Metadata> refreshSchemaAsync() {
138
return metadataManager.refreshSchema(null, true, true);
138
return metadataManager.refreshSchema(null, true, true);
139
}
139
}
140

140

core/src/test/java/com/datastax/oss/driver/internal/core/cql/DefaultAsyncResultSetTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class DefaultAsyncResultSetTest {
55
public void setup() {
55
public void setup() {
56
MockitoAnnotations.initMocks(this);
56
MockitoAnnotations.initMocks(this);
57

57

58-
Mockito.when(executionInfo.getStatement()).thenReturn((Statement) statement);
58+
Mockito.when(executionInfo.getStatement()).thenAnswer(invocation -> statement);
59
Mockito.when(context.getCodecRegistry()).thenReturn(CodecRegistry.DEFAULT);
59
Mockito.when(context.getCodecRegistry()).thenReturn(CodecRegistry.DEFAULT);
60
Mockito.when(context.getProtocolVersion()).thenReturn(DefaultProtocolVersion.DEFAULT);
60
Mockito.when(context.getProtocolVersion()).thenReturn(DefaultProtocolVersion.DEFAULT);
61
}
61
}
@@ -85,14 +85,15 @@ public void should_invoke_session_to_fetch_next_page() {
85
Mockito.when(((Statement) statement).copy(mockPagingState)).thenReturn(mockNextStatement);
85
Mockito.when(((Statement) statement).copy(mockPagingState)).thenReturn(mockNextStatement);
86

86

87
CompletableFuture<AsyncResultSet> mockResultFuture = new CompletableFuture<>();
87
CompletableFuture<AsyncResultSet> mockResultFuture = new CompletableFuture<>();
88-
Mockito.when(session.executeAsync(Mockito.any(Statement.class))).thenReturn(mockResultFuture);
88+
Mockito.when(session.executeAsync(Mockito.any(Statement.class)))
89+
.thenAnswer(invocation -> mockResultFuture);
89

90

90
// When
91
// When
91
DefaultAsyncResultSet resultSet =
92
DefaultAsyncResultSet resultSet =
92
new DefaultAsyncResultSet(
93
new DefaultAsyncResultSet(
93
columnDefinitions, executionInfo, new ArrayDeque<>(), session, context);
94
columnDefinitions, executionInfo, new ArrayDeque<>(), session, context);
94
assertThat(resultSet.hasMorePages()).isTrue();
95
assertThat(resultSet.hasMorePages()).isTrue();
95-
CompletionStage<AsyncResultSet> nextPageFuture = resultSet.fetchNextPage();
96+
CompletionStage<? extends AsyncResultSet> nextPageFuture = resultSet.fetchNextPage();
96

97

97
// Then
98
// Then
98
Mockito.verify(statement).copy(mockPagingState);
99
Mockito.verify(statement).copy(mockPagingState);

core/src/test/java/com/datastax/oss/driver/internal/core/cql/QueryTraceFetcherTest.java

Lines changed: 11 additions & 4 deletions
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -116,7 +116,8 @@ public void should_succeed_when_both_queries_succeed_immediately() {
116
CompletionStage<AsyncResultSet> sessionRow = completeSessionRow();
116
CompletionStage<AsyncResultSet> sessionRow = completeSessionRow();
117
CompletionStage<AsyncResultSet> eventRows = singlePageEventRows();
117
CompletionStage<AsyncResultSet> eventRows = singlePageEventRows();
118
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
118
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
119-
.thenReturn(sessionRow, eventRows);
119+
.thenAnswer(invocation -> sessionRow)
120+
.thenAnswer(invocation -> eventRows);
120

121

121
// When
122
// When
122
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
123
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
@@ -167,7 +168,9 @@ public void should_succeed_when_events_query_is_paged() {
167
CompletionStage<AsyncResultSet> eventRows1 = multiPageEventRows1();
168
CompletionStage<AsyncResultSet> eventRows1 = multiPageEventRows1();
168
CompletionStage<AsyncResultSet> eventRows2 = multiPageEventRows2();
169
CompletionStage<AsyncResultSet> eventRows2 = multiPageEventRows2();
169
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
170
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
170-
.thenReturn(sessionRow, eventRows1, eventRows2);
171+
.thenAnswer(invocation -> sessionRow)
172+
.thenAnswer(invocation -> eventRows1)
173+
.thenAnswer(invocation -> eventRows2);
171

174

172
// When
175
// When
173
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
176
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
@@ -192,7 +195,9 @@ public void should_retry_when_session_row_is_incomplete() {
192
CompletionStage<AsyncResultSet> sessionRow2 = completeSessionRow();
195
CompletionStage<AsyncResultSet> sessionRow2 = completeSessionRow();
193
CompletionStage<AsyncResultSet> eventRows = singlePageEventRows();
196
CompletionStage<AsyncResultSet> eventRows = singlePageEventRows();
194
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
197
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
195-
.thenReturn(sessionRow1, sessionRow2, eventRows);
198+
.thenAnswer(invocation -> sessionRow1)
199+
.thenAnswer(invocation -> sessionRow2)
200+
.thenAnswer(invocation -> eventRows);
196

201

197
// When
202
// When
198
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
203
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
@@ -259,7 +264,9 @@ public void should_fail_when_session_query_still_incomplete_after_max_tries() {
259
CompletionStage<AsyncResultSet> sessionRow2 = incompleteSessionRow();
264
CompletionStage<AsyncResultSet> sessionRow2 = incompleteSessionRow();
260
CompletionStage<AsyncResultSet> sessionRow3 = incompleteSessionRow();
265
CompletionStage<AsyncResultSet> sessionRow3 = incompleteSessionRow();
261
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
266
Mockito.when(session.executeAsync(any(SimpleStatement.class)))
262-
.thenReturn(sessionRow1, sessionRow2, sessionRow3);
267+
.thenAnswer(invocation -> sessionRow1)
268+
.thenAnswer(invocation -> sessionRow2)
269+
.thenAnswer(invocation -> sessionRow3);
263

270

264
// When
271
// When
265
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);
272
QueryTraceFetcher fetcher = new QueryTraceFetcher(TRACING_ID, session, context, config);

integration-tests/src/test/java/com/datastax/oss/driver/api/core/connection/FrameLengthIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -92,7 +92,7 @@ public void should_fail_if_request_exceeds_max_frame_length() {
92

92

93
@Test
93
@Test
94
public void should_fail_if_response_exceeds_max_frame_length() {
94
public void should_fail_if_response_exceeds_max_frame_length() {
95-
CompletionStage<AsyncResultSet> slowResultFuture =
95+
CompletionStage<? extends AsyncResultSet> slowResultFuture =
96
sessionRule.session().executeAsync(SLOW_QUERY);
96
sessionRule.session().executeAsync(SLOW_QUERY);
97
try {
97
try {
98
sessionRule.session().execute(LARGE_QUERY);
98
sessionRule.session().execute(LARGE_QUERY);

integration-tests/src/test/java/com/datastax/oss/driver/api/core/cql/AsyncResultSetIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberOriginal file lineDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static void setupSchema() {
85
public void should_only_iterate_over_rows_in_current_page() throws Exception {
85
public void should_only_iterate_over_rows_in_current_page() throws Exception {
86
// very basic test that just ensures that iterating over an AsyncResultSet only visits the first
86
// very basic test that just ensures that iterating over an AsyncResultSet only visits the first
87
// page.
87
// page.
88-
CompletionStage<AsyncResultSet> result =
88+
CompletionStage<? extends AsyncResultSet> result =
89
sessionRule
89
sessionRule
90
.session()
90
.session()
91
.executeAsync(
91
.executeAsync(

0 commit comments

Comments
 (0)