Skip to content

Commit fd59ac7

Browse files
committed
Fix nullability in ConnectionInfo
Mark `getConnectionId()` and `getOriginalConnection()` methods with `@Nullable` and updated their javadoc. Fixes #151
1 parent 080ddf4 commit fd59ac7

File tree

5 files changed

+79
-11
lines changed

5 files changed

+79
-11
lines changed

src/main/java/io/r2dbc/proxy/callback/ConnectionFactoryCallbackHandler.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,11 @@ public Object invoke(Object proxy, Method method, @Nullable Object[] args) throw
6161
executionInfo.setMethodArgs(args);
6262
executionInfo.setTarget(target);
6363

64-
// The actual connection is not yet created, but in order to populate connection level context-view,
65-
// create an empty connection info here and set it to the execution info
64+
// The actual connection has not been created yet, but to populate the connection-level context view (mainly for ValueStore),
65+
// an empty ConnectionInfo is created here and set into the execution info.
66+
// NOTE: Until it is associated with an actual connection, retrieving the connection (`getOriginalConnection`) and connection ID (`getConnectionId`)
67+
// will return null. For example, during `beforeMethod` of `ConnectionFactory#create()`.
68+
// This is a design decision to always provide a connection-level ValueStore.
6669
DefaultConnectionInfo connectionInfo = new DefaultConnectionInfo();
6770
executionInfo.setConnectionInfo(connectionInfo);
6871

src/main/java/io/r2dbc/proxy/callback/DefaultConnectionInfo.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.r2dbc.proxy.core.ValueStore;
2222
import io.r2dbc.proxy.util.Assert;
2323
import io.r2dbc.spi.Connection;
24+
import reactor.util.annotation.Nullable;
2425

2526
import java.util.concurrent.atomic.AtomicBoolean;
2627
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@@ -91,11 +92,13 @@ public void setClosed(boolean closed) {
9192
}
9293

9394
@Override
95+
@Nullable
9496
public Connection getOriginalConnection() {
9597
return this.originalConnection;
9698
}
9799

98100
@Override
101+
@Nullable
99102
public String getConnectionId() {
100103
return this.connectionId;
101104
}

src/main/java/io/r2dbc/proxy/callback/StatementCallbackHandler.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
7373
return handleCommonMethod(methodName, this.statement, args, this.connectionInfo.getOriginalConnection());
7474
}
7575

76-
// if ("unwrap".equals(methodName)) {
77-
// return this.statement;
78-
// } else if ("unwrapConnection".equals(methodName)) {
79-
// return this.connectionInfo.getOriginalConnection();
80-
// }
81-
8276
if ("bind".equals(methodName) || "bindNull".equals(methodName)) {
8377

8478
BoundValue boundValue;

src/main/java/io/r2dbc/proxy/core/ConnectionInfo.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,38 @@
1717
package io.r2dbc.proxy.core;
1818

1919

20+
import io.r2dbc.proxy.listener.ProxyExecutionListener;
2021
import io.r2dbc.spi.Connection;
22+
import io.r2dbc.spi.ConnectionFactory;
23+
import reactor.util.annotation.Nullable;
2124

2225
/**
2326
* Hold {@link Connection} related information.
2427
*
2528
* @author Tadaya Tsuyukubo
2629
*/
2730
public interface ConnectionInfo {
28-
2931
/**
3032
* Retrieve original {@link Connection}.
3133
*
32-
* @return connection
34+
* @return connection; {@code null} is returned when {@link ConnectionInfo} is evaluated
35+
* before it is associated with an actual connection, for example, during
36+
* {@link ProxyExecutionListener#beforeMethod(MethodExecutionInfo)} for
37+
* {@link ConnectionFactory#create()}.
3338
*/
39+
@Nullable
3440
Connection getOriginalConnection();
3541

3642
/**
3743
* Get ID for the connection.
3844
*
39-
* @return connection ID
45+
* @return connection ID; {@code null} is returned when {@link ConnectionInfo} is evaluated
46+
* before it is associated with an actual connection, for example, during
47+
* {@link ProxyExecutionListener#beforeMethod(MethodExecutionInfo)} for
48+
* {@link ConnectionFactory#create()}.
4049
* @see io.r2dbc.proxy.callback.ConnectionIdManager
4150
*/
51+
@Nullable
4252
String getConnectionId();
4353

4454
/**

src/test/java/io/r2dbc/proxy/callback/ConnectionFactoryCallbackHandlerTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.r2dbc.proxy.core.ConnectionInfo;
2020
import io.r2dbc.proxy.core.MethodExecutionInfo;
2121
import io.r2dbc.proxy.listener.LastExecutionAwareListener;
22+
import io.r2dbc.proxy.listener.ProxyExecutionListener;
2223
import io.r2dbc.proxy.listener.ProxyMethodExecutionListener;
2324
import io.r2dbc.spi.Connection;
2425
import io.r2dbc.spi.ConnectionFactory;
@@ -114,6 +115,63 @@ void createConnection() throws Throwable {
114115
assertThat(afterMethod.getResult()).isSameAs(originalConnection);
115116
}
116117

118+
@Test
119+
void connectionInfoInBeforeAndAfterMethod() throws Throwable {
120+
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
121+
Connection originalConnection = mock(Connection.class);
122+
ConnectionIdManager idManager = mock(ConnectionIdManager.class);
123+
124+
// mock call to original ConnectionFactory#create()
125+
doReturn(Mono.just(originalConnection)).when(connectionFactory).create();
126+
127+
String connectionId = "100";
128+
when(idManager.getId(originalConnection)).thenReturn(connectionId);
129+
130+
AtomicReference<Connection> connectionBeforeMethod = new AtomicReference<>();
131+
AtomicReference<Connection> connectionAfterMethod = new AtomicReference<>();
132+
AtomicReference<String> connectionIdBeforeMethod = new AtomicReference<>();
133+
AtomicReference<String> connectionIdAfterMethod = new AtomicReference<>();
134+
ProxyExecutionListener listener = new ProxyExecutionListener() {
135+
136+
@Override
137+
public void beforeMethod(MethodExecutionInfo executionInfo) {
138+
ConnectionInfo connectionInfo = executionInfo.getConnectionInfo();
139+
assertThat(connectionInfo).isNotNull();
140+
connectionBeforeMethod.set(connectionInfo.getOriginalConnection());
141+
connectionIdBeforeMethod.set(connectionInfo.getConnectionId());
142+
}
143+
144+
@Override
145+
public void afterMethod(MethodExecutionInfo executionInfo) {
146+
ConnectionInfo connectionInfo = executionInfo.getConnectionInfo();
147+
assertThat(connectionInfo).isNotNull();
148+
connectionAfterMethod.set(connectionInfo.getOriginalConnection());
149+
connectionIdAfterMethod.set(connectionInfo.getConnectionId());
150+
}
151+
};
152+
153+
ProxyConfig proxyConfig = ProxyConfig.builder()
154+
.connectionIdManager(idManager)
155+
.listener(listener)
156+
.build();
157+
158+
ConnectionFactoryCallbackHandler handler = new ConnectionFactoryCallbackHandler(connectionFactory, proxyConfig);
159+
160+
Object result = handler.invoke(connectionFactory, CREATE_METHOD, null);
161+
162+
assertThat(result).isInstanceOf(Publisher.class);
163+
164+
StepVerifier.create((Publisher<?>) result)
165+
.expectSubscription()
166+
.expectNextCount(1)
167+
.verifyComplete();
168+
169+
assertThat(connectionBeforeMethod).hasValue(null);
170+
assertThat(connectionIdBeforeMethod).hasValue(null);
171+
assertThat(connectionAfterMethod).hasValue(originalConnection);
172+
assertThat(connectionIdAfterMethod).hasValue("100");
173+
}
174+
117175
@Test
118176
void getMetadata() throws Throwable {
119177

0 commit comments

Comments
 (0)