View | Details | Raw Unified | Return to bug 54693
Collapse All | Expand All

(-)src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java (+13 lines)
Lines 524-529 Link Here
524
    public void setValidationQuery(String validationQuery);
524
    public void setValidationQuery(String validationQuery);
525
525
526
    /**
526
    /**
527
     * The timeout in seconds before a connection validation queries fail.
528
     * A value less than or equal to zero will disable this feature.  Defaults to -1. 
529
     * @return the timeout value in seconds 
530
     */
531
    public int getValidationQueryTimeout();
532
    
533
    /**
534
     * The timeout in seconds before a connection validation queries fail.
535
     * A value less than or equal to zero will disable this feature.  Defaults to -1. 
536
     */
537
    public void setValidationQueryTimeout(int validationQueryTimeout);
538
    
539
    /**
527
     * Return the name of the optional validator class - may be null.
540
     * Return the name of the optional validator class - may be null.
528
     *
541
     *
529
     * @return the name of the optional validator class - may be null
542
     * @return the name of the optional validator class - may be null
(-)src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java (+6 lines)
Lines 451-456 Link Here
451
        Statement stmt = null;
451
        Statement stmt = null;
452
        try {
452
        try {
453
            stmt = connection.createStatement();
453
            stmt = connection.createStatement();
454
            
455
            int validationQueryTimeout = poolProperties.getValidationQueryTimeout();
456
            if (validationQueryTimeout > 0) {
457
            	stmt.setQueryTimeout(validationQueryTimeout);
458
            }
459
            
454
            stmt.execute(query);
460
            stmt.execute(query);
455
            stmt.close();
461
            stmt.close();
456
            this.lastValidated = now;
462
            this.lastValidated = now;
(-)src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java (+10 lines)
Lines 310-315 Link Here
310
    public String getValidationQuery() {
310
    public String getValidationQuery() {
311
        return getPoolProperties().getValidationQuery();
311
        return getPoolProperties().getValidationQuery();
312
    }
312
    }
313
    
314
    @Override
315
    public int getValidationQueryTimeout() {
316
    	return getPoolProperties().getValidationQueryTimeout();
317
    }
313
318
314
    /**
319
    /**
315
     * {@inheritDoc}
320
     * {@inheritDoc}
Lines 664-669 Link Here
664
        getPoolProperties().setValidationQuery(validationQuery);
669
        getPoolProperties().setValidationQuery(validationQuery);
665
    }
670
    }
666
671
672
    @Override
673
    public void setValidationQueryTimeout(int validationQueryTimeout) {
674
    	getPoolProperties().setValidationQueryTimeout(validationQueryTimeout);
675
    }
676
    
667
    /**
677
    /**
668
     * {@inheritDoc}
678
     * {@inheritDoc}
669
     */
679
     */
(-)src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java (+7 lines)
Lines 80-85 Link Here
80
    protected static final String PROP_TESTWHILEIDLE = "testWhileIdle";
80
    protected static final String PROP_TESTWHILEIDLE = "testWhileIdle";
81
    protected static final String PROP_TESTONCONNECT = "testOnConnect";
81
    protected static final String PROP_TESTONCONNECT = "testOnConnect";
82
    protected static final String PROP_VALIDATIONQUERY = "validationQuery";
82
    protected static final String PROP_VALIDATIONQUERY = "validationQuery";
83
    protected static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
83
    protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName";
84
    protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName";
84
85
85
    protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
86
    protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
Lines 149-154 Link Here
149
        PROP_URL,
150
        PROP_URL,
150
        PROP_USERNAME,
151
        PROP_USERNAME,
151
        PROP_VALIDATIONQUERY,
152
        PROP_VALIDATIONQUERY,
153
        PROP_VALIDATIONQUERY_TIMEOUT,
152
        PROP_VALIDATOR_CLASS_NAME,
154
        PROP_VALIDATOR_CLASS_NAME,
153
        PROP_VALIDATIONINTERVAL,
155
        PROP_VALIDATIONINTERVAL,
154
        PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
156
        PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
Lines 366-371 Link Here
366
        if (value != null) {
368
        if (value != null) {
367
            poolProperties.setValidationQuery(value);
369
            poolProperties.setValidationQuery(value);
368
        }
370
        }
371
        
372
        value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
373
        if (value != null) {
374
        	poolProperties.setValidationQueryTimeout(Integer.parseInt(value));
375
        }
369
376
370
        value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME);
377
        value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME);
371
        if (value != null) {
378
        if (value != null) {
(-)src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java (-2 / +1 lines)
Lines 713-719 Link Here
713
            } else {
713
            } else {
714
                //validation failed, make sure we disconnect
714
                //validation failed, make sure we disconnect
715
                //and clean up
715
                //and clean up
716
                error =true;
716
            	throw new SQLException("Validation Query Failed, enable logValidationErrors for more details.");
717
            } //end if
717
            } //end if
718
        } catch (Exception e) {
718
        } catch (Exception e) {
719
            error = true;
719
            error = true;
Lines 733-739 Link Here
733
            }
733
            }
734
            con.unlock();
734
            con.unlock();
735
        }//catch
735
        }//catch
736
        return null;
737
    }
736
    }
738
737
739
    /**
738
    /**
(-)src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java (+17 lines)
Lines 57-62 Link Here
57
    private volatile int minIdle = initialSize;
57
    private volatile int minIdle = initialSize;
58
    private volatile int maxWait = 30000;
58
    private volatile int maxWait = 30000;
59
    private volatile String validationQuery;
59
    private volatile String validationQuery;
60
    private volatile int validationQueryTimeout = -1;
60
    private volatile String validatorClassName;
61
    private volatile String validatorClassName;
61
    private volatile Validator validator;
62
    private volatile Validator validator;
62
    private volatile boolean testOnBorrow = false;
63
    private volatile boolean testOnBorrow = false;
Lines 382-387 Link Here
382
    /**
383
    /**
383
     * {@inheritDoc}
384
     * {@inheritDoc}
384
     */
385
     */
386
    @Override
387
    public int getValidationQueryTimeout() {
388
    	return validationQueryTimeout;
389
    }
390
    
391
    /**
392
     * {@inheritDoc}
393
     */
394
    @Override
395
    public void setValidationQueryTimeout(int validationQueryTimeout) {
396
    	this.validationQueryTimeout = validationQueryTimeout;
397
    }
398
    
399
    /**
400
     * {@inheritDoc}
401
     */
385
402
386
    @Override
403
    @Override
387
    public String getValidatorClassName() {
404
    public String getValidatorClassName() {
(-)src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java (+18 lines)
Lines 452-457 Link Here
452
    public void setValidatorClassName(String className) {
452
    public void setValidatorClassName(String className) {
453
        this.poolProperties.setValidatorClassName(className);
453
        this.poolProperties.setValidatorClassName(className);
454
    }
454
    }
455
    
456
    /**
457
     * {@inheritDoc}
458
     */
459
    
460
    @Override
461
    public void setValidationQueryTimeout(int validationQueryTimeout) {
462
    	this.poolProperties.setValidationQueryTimeout(validationQueryTimeout);
463
    }
455
464
456
    /**
465
    /**
457
     * {@inheritDoc}
466
     * {@inheritDoc}
Lines 928-933 Link Here
928
    /**
937
    /**
929
     * {@inheritDoc}
938
     * {@inheritDoc}
930
     */
939
     */
940
    
941
    @Override
942
    public int getValidationQueryTimeout() {
943
    	return getPoolProperties().getValidationQueryTimeout();
944
    }
945
    
946
    /**
947
     * {@inheritDoc}
948
     */
931
949
932
    @Override
950
    @Override
933
    public String getValidatorClassName() {
951
    public String getValidatorClassName() {
(-)src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml (+5 lines)
Lines 125-130 Link Here
125
           description="The query to run during validation"
125
           description="The query to run during validation"
126
                  type="java.lang.String"
126
                  type="java.lang.String"
127
             writeable="false"/>
127
             writeable="false"/>
128
             
129
    <attribute    name="validationQueryTimeout"
130
           description="The timeout in seconds before a connection validation queries fail"
131
                  type="java.lang.Integer"
132
             writeable="false" />
128
133
129
    <attribute    name="testOnBorrow"
134
    <attribute    name="testOnBorrow"
130
           description="True if validation happens when a connection is requested"
135
           description="True if validation happens when a connection is requested"
(-)doc/jdbc-pool.xml (+9 lines)
Lines 277-282 Link Here
277
         Example values are <code>SELECT 1</code>(mysql), <code>select 1 from dual</code>(oracle), <code>SELECT 1</code>(MS Sql Server)
277
         Example values are <code>SELECT 1</code>(mysql), <code>select 1 from dual</code>(oracle), <code>SELECT 1</code>(MS Sql Server)
278
      </p>
278
      </p>
279
    </attribute>
279
    </attribute>
280
    
281
    <attribute name="validationQueryTimeout" required="false">
282
      <p>(int) The timeout in seconds before a connection validation queries fail.  This works by calling 
283
         <code>java.sql.Statement.setQueryTimeout(seconds)</code> on the statement that executes the <code>validationQuery</code>.
284
         The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. 
285
         A value less than or equal to zero will disable this feature. 
286
         The default value is <code>-1</code>.
287
      </p>
288
    </attribute>
280
289
281
    <attribute name="validatorClassName" required="false">
290
    <attribute name="validatorClassName" required="false">
282
      <p>(String) The name of a class which implements the
291
      <p>(String) The name of a class which implements the
(-)src/test/java/org/apache/tomcat/jdbc/test/TestValidationQueryTimeout.java (+250 lines)
Line 0 Link Here
1
package org.apache.tomcat.jdbc.test;
2
3
import java.sql.Connection;
4
import java.sql.DriverManager;
5
import java.sql.DriverPropertyInfo;
6
import java.sql.ResultSet;
7
import java.sql.SQLException;
8
import java.sql.SQLFeatureNotSupportedException;
9
import java.sql.Statement;
10
import java.util.Properties;
11
import java.util.logging.Logger;
12
13
import junit.framework.Assert;
14
15
import org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor;
16
import org.junit.After;
17
import org.junit.Before;
18
import org.junit.Test;
19
20
public class TestValidationQueryTimeout extends DefaultTestCase {
21
	
22
	private static int TIMEOUT = 10;
23
	private static boolean isTimeoutSet;
24
	private static final String longQuery = "select * from test as A, test as B, test as C, test as D, test as E";
25
	
26
	@Before
27
	public void setUp() throws SQLException {
28
		DriverManager.registerDriver(new MockDriver());
29
		
30
		// use our mock driver
31
		this.datasource.setDriverClassName(MockDriver.class.getName());
32
        this.datasource.setUrl(MockDriver.url);
33
        
34
        // Required to trigger validation query's execution 
35
        this.datasource.setInitialSize(1);
36
        this.datasource.setTestOnBorrow(true);
37
        this.datasource.setValidationInterval(-1);
38
        this.datasource.setValidationQuery("SELECT 1");
39
        this.datasource.setValidationQueryTimeout(TIMEOUT);
40
        
41
		TIMEOUT = 10;
42
		isTimeoutSet = false;
43
	}
44
	
45
	@After
46
	public void tearDown() throws SQLException {
47
		DriverManager.deregisterDriver(new MockDriver());
48
	}
49
	
50
	@Test
51
	public void testValidationQueryTimeoutEnabled() throws Exception {
52
    	// because testOnBorrow is true, this triggers the validation query
53
    	this.datasource.getConnection();
54
    	Assert.assertTrue(isTimeoutSet);
55
	}
56
	
57
	@Test
58
	public void testValidationQueryTimeoutDisabled() throws Exception {
59
		this.datasource.setValidationQueryTimeout(-1);
60
		
61
    	// because testOnBorrow is true, this triggers the validation query
62
    	this.datasource.getConnection();
63
    	Assert.assertFalse(isTimeoutSet);
64
	}
65
	
66
	@Test
67
	public void testValidationQueryTimeoutWithQueryTimeoutInterceptor() throws Exception {
68
		int interceptorTimeout = 30;
69
		this.datasource.setJdbcInterceptors(
70
							QueryTimeoutInterceptor.class.getName()+
71
							"(queryTimeout="+ interceptorTimeout +")");
72
		
73
		// because testOnBorrow is true, this triggers the validation query
74
    	Connection con = this.datasource.getConnection();
75
    	Assert.assertTrue(isTimeoutSet);
76
    	
77
    	// increase the expected timeout to 30, which is what we set for the interceptor
78
    	TIMEOUT = 30;
79
    	
80
    	// now create a statement, make sure the query timeout is set by the interceptor
81
    	Statement st = con.createStatement();
82
        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
83
        st.close();
84
        st = con.prepareStatement("");
85
        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
86
        st.close();
87
        st = con.prepareCall("");
88
        Assert.assertEquals(interceptorTimeout, st.getQueryTimeout());
89
        st.close();
90
        con.close();
91
        
92
        // pull another connection and check it
93
        TIMEOUT = 10;
94
        isTimeoutSet = false;
95
        this.datasource.getConnection();
96
        Assert.assertTrue(isTimeoutSet);
97
	}
98
	
99
	// this test depends on the execution time of the validation query
100
	//   specifically, it needs to run for longer than 1 second to pass
101
	//   if this fails
102
	@Test(expected=SQLException.class) 
103
	public void testValidationQueryTimeoutOnConnection() throws Exception {
104
		// use our mock driver
105
		this.datasource.setDriverClassName("org.h2.Driver");
106
        this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
107
        
108
        // Required to trigger validation query's execution 
109
        this.datasource.setTestOnConnect(true);
110
        this.datasource.setValidationInterval(-1);
111
        this.datasource.setValidationQuery(longQuery);
112
        this.datasource.setValidationQueryTimeout(1);
113
        
114
        this.datasource.getConnection();
115
	}
116
	
117
	@Test(expected=SQLException.class)
118
	public void testValidationInvalidOnConnection() throws Exception {
119
		// use our mock driver
120
		this.datasource.setDriverClassName("org.h2.Driver");
121
        this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
122
        
123
        // Required to trigger validation query's execution 
124
        this.datasource.setTestOnBorrow(true);
125
        this.datasource.setInitialSize(1);
126
        this.datasource.setTestOnConnect(true);
127
        this.datasource.setValidationInterval(-1);
128
        this.datasource.setValidationQuery("SELECT");
129
        this.datasource.setValidationQueryTimeout(1);
130
        
131
        this.datasource.getConnection();
132
	}
133
	
134
	@Test
135
	public void testLongValidationQueryTime() throws Exception {
136
		// use our mock driver
137
		this.datasource.setDriverClassName("org.h2.Driver");
138
        this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
139
        Connection con = this.datasource.getConnection();
140
        Statement stmt = null;
141
        long start = 0, end = 0;
142
        try {
143
        	stmt = con.createStatement();
144
        	// set the query timeout to 2 sec
145
            //  this keeps this test from slowing things down too much
146
            stmt.setQueryTimeout(2);
147
            // assert that our long query takes longer than one second to run
148
            //  this is a requirement for other tests to run properly
149
            start = System.currentTimeMillis();
150
        	stmt.execute(longQuery);
151
        } catch (SQLException ex) {}
152
        finally {
153
        	end = System.currentTimeMillis();
154
        
155
        	if (stmt != null) { stmt.close(); }
156
        	if (con != null) { con.close(); }
157
        	
158
        	Assert.assertTrue(start != 0 && end != 0);
159
        	Assert.assertTrue((end - start) > 1000);
160
        }
161
	}
162
	
163
	@Test
164
	public void testValidationQueryTimeoutOnBorrow() throws Exception {
165
		// use our mock driver
166
		this.datasource.setDriverClassName("org.h2.Driver");
167
        this.datasource.setUrl("jdbc:h2:~/.h2/test;QUERY_TIMEOUT=0;DB_CLOSE_ON_EXIT=FALSE");
168
        
169
        // Required to trigger validation query's execution 
170
        this.datasource.setTestOnBorrow(true);
171
        this.datasource.setValidationInterval(-1);
172
        this.datasource.setValidationQuery(longQuery);
173
        this.datasource.setValidationQueryTimeout(1);
174
        
175
        // assert that even though the validation query times out, we still get a connection
176
        Connection con = this.datasource.getConnection();
177
        Assert.assertNotNull(con);
178
        Statement st = con.createStatement();
179
        ResultSet rs = st.executeQuery("SELECT 1");
180
        rs.close();
181
        st.close();
182
        con.close();
183
	}
184
	
185
	/**
186
	 * Mock Driver, Connection and Statement implementations use to verify setQueryTimeout was called.
187
	 */
188
    public static class MockDriver implements java.sql.Driver {
189
    	public static final String url = "jdbc:tomcat:mock";
190
191
        public MockDriver() {
192
        }
193
194
        @Override
195
        public boolean acceptsURL(String url) throws SQLException {
196
            return url!=null && url.equals(MockDriver.url);
197
        }
198
199
        @Override
200
        public Connection connect(String url, Properties info) throws SQLException {
201
            return new MockConnection(info);
202
        }
203
204
        @Override
205
        public int getMajorVersion() {
206
            return 0;
207
        }
208
209
        @Override
210
        public int getMinorVersion() {
211
            return 0;
212
        }
213
214
        @Override
215
        public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
216
            return null;
217
        }
218
219
        @Override
220
        public boolean jdbcCompliant() {
221
            return false;
222
        }
223
224
        @Override
225
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
226
            return null;
227
        }
228
    }
229
    
230
    public static class MockConnection extends org.apache.tomcat.jdbc.test.driver.Connection {
231
        public MockConnection(Properties info) {
232
			super(info);
233
		}
234
235
		@Override
236
        public Statement createStatement() throws SQLException {
237
            return new MockStatement();
238
        }
239
    }
240
    
241
    public static class MockStatement extends org.apache.tomcat.jdbc.test.driver.Statement {
242
    	@Override
243
    	public void setQueryTimeout(int seconds) throws SQLException {
244
    		super.setQueryTimeout(seconds);
245
    		Assert.assertEquals(TIMEOUT, seconds);
246
    		isTimeoutSet = true;
247
    	}
248
    }
249
    
250
}

Return to bug 54693