From: <mro...@us...> - 2012-12-02 13:45:12
|
Revision: 57438 http://firebird.svn.sourceforge.net/firebird/?rev=57438&view=rev Author: mrotteveel Date: 2012-12-02 13:45:01 +0000 (Sun, 02 Dec 2012) Log Message: ----------- JDBC-279 Add connect timeout Modified Paths: -------------- client-java/trunk/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java client-java/trunk/src/main/org/firebirdsql/ds/FBSimpleDataSource.java client-java/trunk/src/main/org/firebirdsql/gds/impl/jni/BaseGDSImpl.java client-java/trunk/src/main/org/firebirdsql/gds/impl/wire/AbstractJavaGDSImpl.java client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnection.java client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnectionFactory.java client-java/trunk/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java client-java/trunk/src/main/org/firebirdsql/jdbc/FirebirdConnectionProperties.java client-java/trunk/src/resources/driver_property_info.properties client-java/trunk/src/resources/isc_dpb_types.properties client-java/trunk/src/test/org/firebirdsql/common/FBTestProperties.java Added Paths: ----------- client-java/trunk/src/test/org/firebirdsql/jdbc/TestFBConnectionTimeout.java Modified: client-java/trunk/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/ds/FBAbstractCommonDataSource.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -199,14 +199,34 @@ } } + /** + * {@inheritDoc} + * <p> + * This property is an alias for the connectTimeout property. + * </p> + */ public int getLoginTimeout() throws SQLException { - return connectionProperties.getSoTimeout() / 1000; + return getConnectTimeout(); } + /** + * {@inheritDoc} + * <p> + * This property is an alias for the connectTimeout property. + * </p> + */ public void setLoginTimeout(int seconds) throws SQLException { - connectionProperties.setSoTimeout(seconds * 1000); + setConnectTimeout(seconds); } + public int getConnectTimeout() { + return connectionProperties.getConnectTimeout(); + } + + public void setConnectTimeout(int connectTimeout) { + connectionProperties.setConnectTimeout(connectTimeout); + } + @Deprecated public String getDatabase() { synchronized(lock) { Modified: client-java/trunk/src/main/org/firebirdsql/ds/FBSimpleDataSource.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/ds/FBSimpleDataSource.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/ds/FBSimpleDataSource.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -59,7 +59,6 @@ protected Reference jndiReference; protected String description; - protected int loginTimeout; /** * Create instance of this class. @@ -380,8 +379,14 @@ mcf.setSoTimeout(soTimeout); } - + public int getConnectTimeout() { + return mcf.getConnectTimeout(); + } + public void setConnectTimeout(int connectTimeout) { + mcf.setConnectTimeout(connectTimeout); + } + /* * INTERFACES IMPLEMENTATION */ @@ -433,24 +438,23 @@ } /** - * Get login timeout specified for this datasource. - * - * @return login timeout of this datasource in seconds. - * - * @throws SQLException if something went wrong. + * {@inheritDoc} + * <p> + * This property is an alias for the connectTimeout property. + * </p> */ public int getLoginTimeout() throws SQLException { - return loginTimeout; + return getConnectTimeout(); } /** - * Set login timeout for this datasource. - * - * @param loginTimeout login timeout in seconds. - * @throws SQLException + * {@inheritDoc} + * <p> + * This property is an alias for the connectTimeout property. + * </p> */ public void setLoginTimeout(int loginTimeout) throws SQLException { - this.loginTimeout = loginTimeout; + setConnectTimeout(loginTimeout); } /** Modified: client-java/trunk/src/main/org/firebirdsql/gds/impl/jni/BaseGDSImpl.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/gds/impl/jni/BaseGDSImpl.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/gds/impl/jni/BaseGDSImpl.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -36,6 +36,10 @@ // TODO Checking for validity of dbhandle is inconsistent (sometimes only null check, sometimes also .isValid()) private static Logger log = LoggerFactory.getLogger(BaseGDSImpl.class, false); + + private static final String WARNING_CONNECT_TIMEOUT_NATIVE = + "WARNING: The native driver does not apply connectTimeout for establishing the socket connection (only for protocol negotiation with the Firebird server), " + + "it will not detect unreachable hosts within the specified timeout"; protected static final byte[] DESCRIBE_DATABASE_INFO_BLOCK = new byte[] { ISCConstants.isc_info_db_sql_dialect, @@ -118,6 +122,15 @@ final String filenameCharset; if (databaseParameterBuffer != null) { DatabaseParameterBuffer cleanDPB = ((DatabaseParameterBufferExtension)databaseParameterBuffer).removeExtensionParams(); + if (cleanDPB.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT)) { + // For the native driver isc_dpb_connect_timeout is not a socket connect timeout + // It only applies to the steps for op_accept (negotiating protocol, etc) + if (log != null) { + log.warn(WARNING_CONNECT_TIMEOUT_NATIVE); + } + db_handle.addWarning(new GDSWarning(WARNING_CONNECT_TIMEOUT_NATIVE)); + } + dpbBytes = ((DatabaseParameterBufferImp) cleanDPB).getBytesForNativeCode(); filenameCharset = databaseParameterBuffer.getArgumentAsString(DatabaseParameterBufferExtension.FILENAME_CHARSET); } else { Modified: client-java/trunk/src/main/org/firebirdsql/gds/impl/wire/AbstractJavaGDSImpl.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/gds/impl/wire/AbstractJavaGDSImpl.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/gds/impl/wire/AbstractJavaGDSImpl.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -1887,17 +1887,29 @@ boolean debug = log != null && log.isDebugEnabled(); - int socketBufferSize = -1; - int soTimeout = -1; - - if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SOCKET_BUFFER_SIZE)) + final int socketBufferSize; + if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SOCKET_BUFFER_SIZE)) { socketBufferSize = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBufferExtension.SOCKET_BUFFER_SIZE); + } else { + socketBufferSize = -1; + } - if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SO_TIMEOUT)) + final int soTimeout; + if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SO_TIMEOUT)) { soTimeout = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBufferExtension.SO_TIMEOUT); + } else { + soTimeout = -1; + } + + final int connectTimeout; + if (databaseParameterBuffer.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT)) { + connectTimeout = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBuffer.CONNECT_TIMEOUT) * 1000; + } else { + connectTimeout = 0; + } try { - openSocket(db, dbai, debug, socketBufferSize, soTimeout); + openSocket(db, dbai, debug, socketBufferSize, soTimeout, connectTimeout); XdrOutputStream out = db.out; XdrInputStream in = db.in; @@ -1987,7 +1999,6 @@ out.writeInt(op_attach); out.writeInt(2); // CONNECT_VERSION2 out.writeInt(1); // arch_generic - // db.out.writeString(file_name); // p_cnct_file out.writeString(fileName); // p_cnct_file out.writeInt(1); // p_cnct_count out.writeBuffer(user_id); // p_cnct_user_id @@ -2009,7 +2020,7 @@ } protected void openSocket(isc_db_handle_impl db, DbAttachInfo dbai, - boolean debug, int socketBufferSize, int soTimeout) throws IOException, + boolean debug, int socketBufferSize, int soTimeout, int connectTimeout) throws IOException, SocketException, GDSException { try { db.socket = new Socket(); @@ -2024,8 +2035,7 @@ db.socket.setSendBufferSize(socketBufferSize); } // TODO : introduce keep alive - // TODO : introduce connection timeout - db.socket.connect(new InetSocketAddress(dbai.getServer(), dbai.getPort())); + db.socket.connect(new InetSocketAddress(dbai.getServer(), dbai.getPort()), connectTimeout); if (debug) log.debug("Got socket"); Modified: client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnection.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnection.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnection.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -101,6 +101,10 @@ dbHandle.addWarning(new GDSWarning(WARNING_NO_CHARSET)); } + if (!dpb.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT) && DriverManager.getLoginTimeout() > 0) { + dpb.addArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT, DriverManager.getLoginTimeout()); + } + gds.iscAttachDatabase(mcf.getDatabase(), dbHandle, dpb); gdsHelper = new GDSHelper(gds, dpb, dbHandle, this); Modified: client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnectionFactory.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnectionFactory.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/jca/FBManagedConnectionFactory.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -374,6 +374,14 @@ public void setSoTimeout(int soTimeout) { connectionProperties.setSoTimeout(soTimeout); } + + public int getConnectTimeout() { + return connectionProperties.getConnectTimeout(); + } + + public void setConnectTimeout(int connectTimeout) { + connectionProperties.setConnectTimeout(connectTimeout); + } public int hashCode() { if (hashCode != 0) Modified: client-java/trunk/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/jdbc/FBConnectionProperties.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -57,6 +57,7 @@ public static final String BUFFERS_NUMBER_PROPERTY = "buffersNumber"; public static final String DEFAULT_HOLDABLE_RS_PROPERTY = "defaultHoldable"; public static final String SO_TIMEOUT = "soTimeout"; + public static final String CONNECT_TIMEOUT = "connectTimeout"; private Map<String, Object> properties = new HashMap<String, Object>(); private String type; @@ -355,7 +356,17 @@ public void setSoTimeout(int soTimeout) { setIntProperty(SO_TIMEOUT, soTimeout); } + + @Override + public int getConnectTimeout() { + return getIntProperty(CONNECT_TIMEOUT); + } + @Override + public void setConnectTimeout(int connectTimeout) { + setIntProperty(CONNECT_TIMEOUT, connectTimeout); + } + public void setNonStandardProperty(String propertyMapping) { char[] chars = propertyMapping.toCharArray(); StringBuilder key = new StringBuilder(); @@ -497,5 +508,4 @@ throw new IllegalArgumentException("Unknown GDS type " + type); return GDSFactory.getGDSForType(gdsType); } - } Modified: client-java/trunk/src/main/org/firebirdsql/jdbc/FirebirdConnectionProperties.java =================================================================== --- client-java/trunk/src/main/org/firebirdsql/jdbc/FirebirdConnectionProperties.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/main/org/firebirdsql/jdbc/FirebirdConnectionProperties.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -366,11 +366,45 @@ */ void setTransactionParameters(int isolation, TransactionParameterBuffer tpb); + /** + * Get the default ResultSet holdability. + * + * @return <code>true</code> when ResultSets are holdable by default, <code>false</code> not holdable. + */ boolean isDefaultResultSetHoldable(); + /** + * Sets the default ResultSet holdability. + * + * @param isHoldable <code>true</code> when ResultSets are holdable by default, <code>false</code> not holdable. + */ void setDefaultResultSetHoldable(boolean isHoldable); + /** + * Get the current Socket blocking timeout (SoTimeout). + * + * @return The socket blocking timeout in milliseconds (0 is 'infinite') + */ int getSoTimeout(); + /** + * Set the Socket blocking timeout (SoTimeout). + * + * @param soTimeout Timeout in milliseconds (0 is 'infinite') + */ void setSoTimeout(int soTimeout); + + /** + * Get the current connect timeout. + * + * @return Connect timeout in seconds (0 is 'infinite', or better: OS specific timeout) + */ + int getConnectTimeout(); + + /** + * Set the connect timeout. + * + * @param connectTimout Connect timeout in seconds (0 is 'infinite', or better: OS specific timeout) + */ + void setConnectTimeout(int connectTimeout); } Modified: client-java/trunk/src/resources/driver_property_info.properties =================================================================== --- client-java/trunk/src/resources/driver_property_info.properties 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/resources/driver_property_info.properties 2012-12-02 13:45:01 UTC (rev 57438) @@ -34,5 +34,6 @@ noResultSetTracking isc_dpb_no_result_set_tracking octetsAsBytes isc_dpb_octets_as_bytes -soTimeout isc_dpb_so_timeout +soTimeout isc_dpb_so_timeout # Socket blocking timeout (in milliseconds) +connectTimeout isc_dpb_connect_timeout # Connect timeout (in seconds) columnLabelForName isc_dpb_column_label_for_name # If enabled, the columnLabel (AS clause) is used for the columnName in the metadata \ No newline at end of file Modified: client-java/trunk/src/resources/isc_dpb_types.properties =================================================================== --- client-java/trunk/src/resources/isc_dpb_types.properties 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/resources/isc_dpb_types.properties 2012-12-02 13:45:01 UTC (rev 57438) @@ -15,6 +15,7 @@ isc_dpb_password_enc string isc_dpb_sys_user_name string isc_dpb_user_name string +isc_dpb_connect_timeout int # connect timeout (in seconds) # following properties are extensions from JayBird isc_dpb_socket_buffer_size int Modified: client-java/trunk/src/test/org/firebirdsql/common/FBTestProperties.java =================================================================== --- client-java/trunk/src/test/org/firebirdsql/common/FBTestProperties.java 2012-12-02 13:25:26 UTC (rev 57437) +++ client-java/trunk/src/test/org/firebirdsql/common/FBTestProperties.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -41,6 +41,16 @@ * Helper class for test properties (database user, password, paths etc) */ public final class FBTestProperties { + + static { + // TODO: Technically not needed with JDBC 4.0 autoloading + try { + Class.forName(FBDriver.class.getName()); + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError("No suitable driver."); + } + } + private static ResourceBundle testDefaults = ResourceBundle.getBundle("unit_test_defaults"); public static String getProperty(String property) { @@ -156,12 +166,6 @@ } public static FirebirdConnection getConnectionViaDriverManager() throws SQLException { - try { - Class.forName(FBDriver.class.getName()); - } catch (ClassNotFoundException ex) { - throw new SQLException("No suitable driver."); - } - return (FirebirdConnection) DriverManager.getConnection(getUrl(), getDefaultPropertiesForConnection()); } @@ -172,7 +176,7 @@ * @return Configured FBManager instance used for creation of the database * @throws Exception */ - protected static FBManager defaultDatabaseSetUp() throws Exception { + public static FBManager defaultDatabaseSetUp() throws Exception { FBManager fbManager = createFBManager(); if (getGdsType() == GDSType.getType("PURE_JAVA") @@ -195,7 +199,7 @@ * FBManager instance * @throws Exception */ - protected static void defaultDatabaseTearDown(FBManager fbManager) throws Exception { + public static void defaultDatabaseTearDown(FBManager fbManager) throws Exception { fbManager.dropDatabase(getDatabasePath(), DB_USER, DB_PASSWORD); fbManager.stop(); } Added: client-java/trunk/src/test/org/firebirdsql/jdbc/TestFBConnectionTimeout.java =================================================================== --- client-java/trunk/src/test/org/firebirdsql/jdbc/TestFBConnectionTimeout.java (rev 0) +++ client-java/trunk/src/test/org/firebirdsql/jdbc/TestFBConnectionTimeout.java 2012-12-02 13:45:01 UTC (rev 57438) @@ -0,0 +1,175 @@ +/* + * Firebird Open Source J2ee connector - jdbc driver + * + * Distributable under LGPL license. + * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * LGPL License for more details. + * + * This file was created by members of the firebird development team. + * All individual contributions remain the Copyright (C) of those + * individuals. Contributors to this file are either listed here or + * can be obtained from a CVS history command. + * + * All rights reserved. + */ +package org.firebirdsql.jdbc; + +import static org.junit.Assert.*; +import static org.junit.Assume.*; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +import org.firebirdsql.common.FBTestProperties; +import org.firebirdsql.gds.GDSException; +import org.firebirdsql.gds.impl.GDSFactory; +import org.firebirdsql.gds.impl.GDSType; +import org.firebirdsql.gds.impl.jni.EmbeddedGDSImpl; +import org.firebirdsql.gds.impl.jni.NativeGDSImpl; +import org.firebirdsql.management.FBManager; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Tests for connection timeouts. + * + * @author <a href="mailto:mro...@us...">Mark Rotteveel</a> + * @since 2.3 + */ +public class TestFBConnectionTimeout { + // This test does not extend FBJUnit4TestBase as a lot of these tests don't need an actual database + + /** + * IP address which does not exist (we simply assume that this site local address does not exist in + * the network when running the test). This assumption is a lot cheaper than testing various addresses + * or applying some heuristic based on the local interface address. + */ + private static final String NON_EXISTENT_IP = "10.253.253.253"; + /** + * Delta for timeout, it is about 100-120 on my machine + */ + private static final double TIMEOUT_DELTA_MS = 200; + + @BeforeClass + public static void verifyTestType() { + // Test won't work for embedded + assumeTrue(!FBTestProperties.getGdsType().toString().equals(EmbeddedGDSImpl.EMBEDDED_TYPE_NAME)); + // Test won't for for native + assumeTrue(!FBTestProperties.getGdsType().toString().equals(NativeGDSImpl.NATIVE_TYPE_NAME)); + } + + /** + * Test for default connect timeout. + * <p> + * This test is ignored by default, as it is OS dependent (eg on Windows 8 it is 20 seconds). + * </p> + */ + @Test + @Ignore + public void defaultConnectTimeout() { + // Ensure default timeout is used + DriverManager.setLoginTimeout(0); + long startTime = System.currentTimeMillis(); + try { + DriverManager.getConnection("jdbc:firebirdsql://" + NON_EXISTENT_IP + "/db", "sysdba", "masterkey"); + } catch (SQLException e) { + long endTime = System.currentTimeMillis(); + long difference = endTime - startTime; + assertEquals("Expected error code for \"Unable to complete network request\"", 335544721, e.getErrorCode()); + System.out.printf("Timeout: %f%n", difference / 1000.0); + } + } + + /** + * Test for connect timeout specified through {@link java.sql.DriverManager#setLoginTimeout(int)} + */ + @Test + public void connectTimeoutFromDriverManager() { + // Timeout set through DriverManager + DriverManager.setLoginTimeout(2); + long startTime = System.currentTimeMillis(); + try { + DriverManager.getConnection(buildTestURL(), "sysdba", "masterkey"); + } catch (SQLException e) { + long endTime = System.currentTimeMillis(); + long difference = endTime - startTime; + assertEquals("Expected error code for \"Unable to complete network request\"", 335544721, e.getErrorCode()); + assertEquals("Unexpected timeout duration (in ms)", 2000, difference, TIMEOUT_DELTA_MS); + } + } + + /** + * Test for connect timeout specified through connection property (in the url) + */ + @Test + public void connectTimeoutFromProperty() { + // Reset DriverManager timeout + DriverManager.setLoginTimeout(0); + long startTime = System.currentTimeMillis(); + try { + DriverManager.getConnection(buildTestURL() + "?connectTimeout=1", "sysdba", "masterkey"); + } catch (SQLException e) { + long endTime = System.currentTimeMillis(); + long difference = endTime - startTime; + assertEquals("Expected error code for \"Unable to complete network request\"", 335544721, e.getErrorCode()); + assertEquals("Unexpected timeout duration (in ms)", 1000, difference, TIMEOUT_DELTA_MS); + } + } + + /** + * Test if a normal connection will work when the timeout is specified in the connection properties. + */ + @Test + public void normalConnectionWithTimeoutFromProperty() throws Exception { + // Reset DriverManager timeout + DriverManager.setLoginTimeout(0); + FBManager fbManager = FBTestProperties.defaultDatabaseSetUp(); + try { + Properties properties = FBTestProperties.getDefaultPropertiesForConnection(); + properties.setProperty("connectTimeout", "1"); + Connection connection = DriverManager.getConnection(FBTestProperties.getUrl(), properties); + connection.close(); + } finally { + FBTestProperties.defaultDatabaseTearDown(fbManager); + } + } + + /** + * Test if a normal connection will work when the timeout is specified through DriverManager + */ + @Test + public void normalConnectionWithTimeoutFromDriverManager() throws Exception { + // Reset DriverManager timeout + DriverManager.setLoginTimeout(2); + FBManager fbManager = FBTestProperties.defaultDatabaseSetUp(); + try { + Properties properties = FBTestProperties.getDefaultPropertiesForConnection(); + Connection connection = DriverManager.getConnection(FBTestProperties.getUrl(), properties); + connection.close(); + } finally { + FBTestProperties.defaultDatabaseTearDown(fbManager); + } + } + + /** + * Builds the test URL (to a non-existent IP) for the current GDS testtype. + * + * @return Test URL to a non-existent IP + */ + private static String buildTestURL() { + GDSType gdsType = FBTestProperties.getGdsType(); + try { + return GDSFactory.getJdbcUrl(gdsType, GDSFactory.getDatabasePath(gdsType, NON_EXISTENT_IP, null, "db")); + } catch (GDSException e) { + fail("Unable to generate testURL"); + } + return null; + } +} Property changes on: client-java/trunk/src/test/org/firebirdsql/jdbc/TestFBConnectionTimeout.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |