View Javadoc
1   /**
2    *    Copyright 2009-2019 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.jdbc;
17  
18  import static org.junit.jupiter.api.Assertions.*;
19  
20  import java.sql.Connection;
21  import java.sql.PreparedStatement;
22  import java.sql.ResultSet;
23  import java.sql.SQLException;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.Properties;
27  import java.util.concurrent.TimeUnit;
28  
29  import org.apache.ibatis.BaseDataTest;
30  import org.apache.ibatis.datasource.pooled.PooledDataSource;
31  import org.hsqldb.jdbc.JDBCConnection;
32  import org.junit.jupiter.api.Disabled;
33  import org.junit.jupiter.api.Test;
34  
35  class PooledDataSourceTest extends BaseDataTest {
36  
37    @Test
38    void shouldProperlyMaintainPoolOf3ActiveAnd2IdleConnections() throws Exception {
39      PooledDataSource ds = createPooledDataSource(JPETSTORE_PROPERTIES);
40      try {
41        runScript(ds, JPETSTORE_DDL);
42        ds.setDefaultAutoCommit(false);
43        ds.setDriverProperties(new Properties() {
44          {
45            setProperty("username", "sa");
46            setProperty("password", "");
47          }
48        });
49        ds.setPoolMaximumActiveConnections(3);
50        ds.setPoolMaximumIdleConnections(2);
51        ds.setPoolMaximumCheckoutTime(10000);
52        ds.setPoolPingConnectionsNotUsedFor(1);
53        ds.setPoolPingEnabled(true);
54        ds.setPoolPingQuery("SELECT * FROM PRODUCT");
55        ds.setPoolTimeToWait(10000);
56        ds.setLogWriter(null);
57        List<Connection> connections = new ArrayList<>();
58        for (int i = 0; i < 3; i++) {
59          connections.add(ds.getConnection());
60        }
61        assertEquals(3, ds.getPoolState().getActiveConnectionCount());
62        for (Connection c : connections) {
63          c.close();
64        }
65        assertEquals(2, ds.getPoolState().getIdleConnectionCount());
66        assertEquals(4, ds.getPoolState().getRequestCount());
67        assertEquals(0, ds.getPoolState().getBadConnectionCount());
68        assertEquals(0, ds.getPoolState().getHadToWaitCount());
69        assertEquals(0, ds.getPoolState().getAverageOverdueCheckoutTime());
70        assertEquals(0, ds.getPoolState().getClaimedOverdueConnectionCount());
71        assertEquals(0, ds.getPoolState().getAverageWaitTime());
72        assertNotNull(ds.getPoolState().toString());
73      } finally {
74        ds.forceCloseAll();
75      }
76    }
77  
78    @Test
79    void shouldNotFailCallingToStringOverAnInvalidConnection() throws Exception {
80      PooledDataSource ds = createPooledDataSource(JPETSTORE_PROPERTIES);
81      Connection c = ds.getConnection();
82      c.close();
83      c.toString();
84    }
85  
86    @Test
87    void ShouldReturnRealConnection() throws Exception {
88      PooledDataSource ds = createPooledDataSource(JPETSTORE_PROPERTIES);
89      Connection c = ds.getConnection();
90      JDBCConnection realConnection = (JDBCConnection) PooledDataSource.unwrapConnection(c);
91      c.close();
92    }
93  
94    @Disabled("See the comments")
95    @Test
96    void shouldReconnectWhenServerKilledLeakedConnection() throws Exception {
97      // See #748
98      // Requirements:
99      // 1. MySQL JDBC driver dependency.
100     // 2. MySQL server instance with the following.
101     //  - CREATE DATABASE `test`;
102     //  - SET GLOBAL wait_timeout=3;
103     // 3. Tweak the connection info below.
104     final String URL = "jdbc:mysql://localhost:3306/test";
105     final String USERNAME = "admin";
106     final String PASSWORD = "";
107 
108     PooledDataSource ds = new PooledDataSource();
109     ds.setDriver("com.mysql.jdbc.Driver");
110     ds.setUrl(URL);
111     ds.setUsername(USERNAME);
112     ds.setPassword(PASSWORD);
113     ds.setPoolMaximumActiveConnections(1);
114     ds.setPoolMaximumIdleConnections(1);
115     ds.setPoolTimeToWait(1000);
116     ds.setPoolMaximumCheckoutTime(2000);
117     ds.setPoolPingEnabled(true);
118     ds.setPoolPingQuery("select 1");
119     ds.setDefaultAutoCommit(true);
120     // MySQL wait_timeout * 1000 or less. (unit:ms)
121     ds.setPoolPingConnectionsNotUsedFor(1000);
122 
123     Connection con = ds.getConnection();
124     exexuteQuery(con);
125     // Simulate connection leak by not closing.
126     // con.close();
127 
128     // Wait for disconnected from mysql...
129     Thread.sleep(TimeUnit.SECONDS.toMillis(3));
130 
131     con.close();
132 
133     // Should return usable connection.
134     con = ds.getConnection();
135     exexuteQuery(con);
136     con.close();
137   }
138 
139   private void exexuteQuery(Connection con) throws SQLException {
140     try (PreparedStatement st = con.prepareStatement("select 1");
141          ResultSet rs = st.executeQuery()) {
142       while (rs.next()) {
143         assertEquals(1, rs.getInt(1));
144       }
145     }
146   }
147 }