1
1
package org .tarantool .jdbc ;
2
2
3
+ import java .io .IOException ;
4
+ import java .net .InetSocketAddress ;
5
+ import java .net .Socket ;
6
+ import java .net .SocketException ;
3
7
import java .sql .Array ;
4
8
import java .sql .Blob ;
5
9
import java .sql .CallableStatement ;
8
12
import java .sql .DatabaseMetaData ;
9
13
import java .sql .NClob ;
10
14
import java .sql .PreparedStatement ;
15
+ import java .sql .ResultSet ;
11
16
import java .sql .SQLClientInfoException ;
12
17
import java .sql .SQLException ;
13
18
import java .sql .SQLFeatureNotSupportedException ;
16
21
import java .sql .Savepoint ;
17
22
import java .sql .Statement ;
18
23
import java .sql .Struct ;
24
+ import java .util .Arrays ;
25
+ import java .util .List ;
19
26
import java .util .Map ;
20
27
import java .util .Properties ;
21
28
import java .util .concurrent .Executor ;
22
29
30
+ import org .tarantool .CommunicationException ;
31
+ import org .tarantool .JDBCBridge ;
23
32
import org .tarantool .TarantoolConnection ;
24
33
34
+ import static org .tarantool .jdbc .SQLDriver .PROP_HOST ;
35
+ import static org .tarantool .jdbc .SQLDriver .PROP_PASSWORD ;
36
+ import static org .tarantool .jdbc .SQLDriver .PROP_PORT ;
37
+ import static org .tarantool .jdbc .SQLDriver .PROP_SOCKET_TIMEOUT ;
38
+ import static org .tarantool .jdbc .SQLDriver .PROP_USER ;
39
+
25
40
@ SuppressWarnings ("Since15" )
26
41
public class SQLConnection implements Connection {
27
- final TarantoolConnection connection ;
42
+ private final TarantoolConnection connection ;
28
43
final String url ;
29
44
final Properties properties ;
30
45
31
- public SQLConnection (TarantoolConnection connection , String url , Properties properties ) {
32
- this .connection = connection ;
46
+ SQLConnection (String url , Properties properties ) throws SQLException {
33
47
this .url = url ;
34
48
this .properties = properties ;
49
+
50
+ String user = properties .getProperty (PROP_USER );
51
+ String pass = properties .getProperty (PROP_PASSWORD );
52
+ Socket socket = null ;
53
+ try {
54
+ socket = getConnectedSocket ();
55
+ this .connection = makeConnection (user , pass , socket );
56
+ } catch (Exception e ) {
57
+ if (socket != null ) {
58
+ try {
59
+ socket .close ();
60
+ } catch (IOException ignored ) {
61
+ // No-op.
62
+ }
63
+ }
64
+ if (e instanceof SQLException )
65
+ throw (SQLException )e ;
66
+ throw new SQLException ("Couldn't initiate connection using " + SQLDriver .diagProperties (properties ), e );
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Provides a connected socket to be used to initialize a native tarantool
72
+ * connection.
73
+ *
74
+ * The implementation assumes that {@link #properties} contains all the
75
+ * necessary info extracted from both the URI and connection properties
76
+ * provided by the user. However, the overrides are free to also use the
77
+ * {@link #url} if required.
78
+ *
79
+ * A connect is guarded with user provided timeout. Socket is configured
80
+ * to honor this timeout for the following read/write operations as well.
81
+ *
82
+ * @return Connected socket.
83
+ * @throws SQLException if failed.
84
+ */
85
+ protected Socket getConnectedSocket () throws SQLException {
86
+ Socket socket = makeSocket ();
87
+ int timeout = Integer .parseInt (properties .getProperty (PROP_SOCKET_TIMEOUT ));
88
+ String host = properties .getProperty (PROP_HOST );
89
+ int port = Integer .parseInt (properties .getProperty (PROP_PORT ));
90
+ try {
91
+ socket .connect (new InetSocketAddress (host , port ), timeout );
92
+ } catch (IOException e ) {
93
+ throw new SQLException ("Couldn't connect to " + host + ":" + port , e );
94
+ }
95
+ // Setup socket further.
96
+ if (timeout > 0 ) {
97
+ try {
98
+ socket .setSoTimeout (timeout );
99
+ } catch (SocketException e ) {
100
+ try {
101
+ socket .close ();
102
+ } catch (IOException ignored ) {
103
+ // No-op.
104
+ }
105
+ throw new SQLException ("Couldn't set socket timeout. timeout=" + timeout , e );
106
+ }
107
+ }
108
+ return socket ;
109
+ }
110
+
111
+ /**
112
+ * Provides a newly connected socket instance. The method is intended to be
113
+ * overridden to enable unit testing of the class.
114
+ *
115
+ * Not supposed to contain any logic other than a call to constructor.
116
+ *
117
+ * @return socket.
118
+ */
119
+ protected Socket makeSocket () {
120
+ return new Socket ();
121
+ }
122
+
123
+ /**
124
+ * Provides a native tarantool connection instance. The method is intended
125
+ * to be overridden to enable unit testing of the class.
126
+ *
127
+ * Not supposed to contain any logic other than a call to constructor.
128
+ *
129
+ * @param user User name.
130
+ * @param pass Password.
131
+ * @param socket Connected socket.
132
+ * @return Native tarantool connection.
133
+ * @throws IOException if failed.
134
+ */
135
+ protected TarantoolConnection makeConnection (String user , String pass , Socket socket ) throws IOException {
136
+ return new TarantoolConnection (user , pass , socket ) {{
137
+ msgPackLite = SQLMsgPackLite .INSTANCE ;
138
+ }};
35
139
}
36
140
37
141
@ Override
38
142
public Statement createStatement () throws SQLException {
39
- return new SQLStatement (connection , this );
143
+ checkNotClosed ();
144
+ return new SQLStatement (this );
40
145
}
41
146
42
147
@ Override
43
148
public PreparedStatement prepareStatement (String sql ) throws SQLException {
44
- return new SQLPreparedStatement (connection , this , sql );
149
+ checkNotClosed ();
150
+ return new SQLPreparedStatement (this , sql );
45
151
}
46
152
47
153
@ Override
@@ -89,6 +195,7 @@ public boolean isClosed() throws SQLException {
89
195
90
196
@ Override
91
197
public DatabaseMetaData getMetaData () throws SQLException {
198
+ checkNotClosed ();
92
199
return new SQLDatabaseMetadata (this );
93
200
}
94
201
@@ -293,15 +400,28 @@ public void abort(Executor executor) throws SQLException {
293
400
294
401
@ Override
295
402
public void setNetworkTimeout (Executor executor , int milliseconds ) throws SQLException {
296
- throw new SQLFeatureNotSupportedException ();
403
+ checkNotClosed ();
404
+
405
+ if (milliseconds < 0 )
406
+ throw new SQLException ("Network timeout cannot be negative." );
407
+
408
+ try {
409
+ connection .setSocketTimeout (milliseconds );
410
+ } catch (SocketException e ) {
411
+ throw new SQLException ("Failed to set socket timeout: timeout=" + milliseconds , e );
412
+ }
297
413
}
298
414
299
415
@ Override
300
416
public int getNetworkTimeout () throws SQLException {
301
- throw new SQLFeatureNotSupportedException ();
417
+ checkNotClosed ();
418
+ try {
419
+ return connection .getSocketTimeout ();
420
+ } catch (SocketException e ) {
421
+ throw new SQLException ("Failed to retrieve socket timeout" , e );
422
+ }
302
423
}
303
424
304
-
305
425
@ Override
306
426
public <T > T unwrap (Class <T > iface ) throws SQLException {
307
427
throw new SQLFeatureNotSupportedException ();
@@ -311,4 +431,84 @@ public <T> T unwrap(Class<T> iface) throws SQLException {
311
431
public boolean isWrapperFor (Class <?> iface ) throws SQLException {
312
432
throw new SQLFeatureNotSupportedException ();
313
433
}
434
+
435
+ protected Object execute (String sql , Object ... args ) throws SQLException {
436
+ checkNotClosed ();
437
+ try {
438
+ return JDBCBridge .execute (connection , sql , args );
439
+ } catch (Exception e ) {
440
+ handleException (e );
441
+ throw new SQLException (formatError (sql , args ), e );
442
+ }
443
+ }
444
+
445
+ protected ResultSet executeQuery (String sql , Object ... args ) throws SQLException {
446
+ checkNotClosed ();
447
+ try {
448
+ return new SQLResultSet (JDBCBridge .query (connection , sql , args ));
449
+ } catch (Exception e ) {
450
+ handleException (e );
451
+ throw new SQLException (formatError (sql , args ), e );
452
+ }
453
+ }
454
+
455
+ protected int executeUpdate (String sql , Object ... args ) throws SQLException {
456
+ checkNotClosed ();
457
+ try {
458
+ return JDBCBridge .update (connection , sql , args );
459
+ } catch (Exception e ) {
460
+ handleException (e );
461
+ throw new SQLException (formatError (sql , args ), e );
462
+ }
463
+ }
464
+
465
+ protected List <?> nativeSelect (Integer space , Integer index , List <?> key , int offset , int limit , int iterator )
466
+ throws SQLException {
467
+ checkNotClosed ();
468
+ try {
469
+ return connection .select (space , index , key , offset , limit , iterator );
470
+ } catch (Exception e ) {
471
+ handleException (e );
472
+ throw new SQLException (e );
473
+ }
474
+ }
475
+
476
+ protected String getServerVersion () {
477
+ return connection .getServerVersion ();
478
+ }
479
+
480
+ /**
481
+ * @throws SQLException If connection is closed.
482
+ */
483
+ protected void checkNotClosed () throws SQLException {
484
+ if (isClosed ())
485
+ throw new SQLException ("Connection is closed." );
486
+ }
487
+
488
+ /**
489
+ * Inspects passed exception and closes the connection if appropriate.
490
+ *
491
+ * @param e Exception to process.
492
+ */
493
+ private void handleException (Exception e ) {
494
+ if (CommunicationException .class .isAssignableFrom (e .getClass ()) ||
495
+ IOException .class .isAssignableFrom (e .getClass ())) {
496
+ try {
497
+ close ();
498
+ } catch (SQLException ignored ) {
499
+ // No-op.
500
+ }
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Provides error message that contains parameters of failed SQL statement.
506
+ *
507
+ * @param sql SQL Text.
508
+ * @param params Parameters of the SQL statement.
509
+ * @return Formatted error message.
510
+ */
511
+ private static String formatError (String sql , Object ... params ) {
512
+ return "Failed to execute SQL: " + sql + ", params: " + Arrays .deepToString (params );
513
+ }
314
514
}
0 commit comments