This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit f99f34a5fcbbf45f26f704d4b619d4c452feefa7 Merge: ae8680275a 2d9abd701b Author: remm <[email protected]> AuthorDate: Wed Oct 28 23:44:49 2026 +0100 Merge branch 'main' of [email protected]:apache/tomcat.git into main .../org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java | 2 +- .../interceptor/TestResetAbandonedTimerStreaming.java | 1 + .../jdbc/pool/interceptor/TestSlowQueryEviction.java | 2 +- .../pool/interceptor/TestSlowQueryJmxMBeanSurface.java | 6 +++--- .../pool/interceptor/TestSlowQueryJmxNotifications.java | 10 +++++----- .../interceptor/TestStatementCacheJmxCacheSize.java | 4 ++-- .../pool/interceptor/TestStatementCacheReconnect.java | 17 +++++++---------- .../TestStatementCacheStaleEntryResurrection.java | 7 +++---- .../interceptor/TestStatementDecoratorIdentity.java | 6 +++++- .../jdbc/pool/jmx/TestJmxNotificationTypeContract.java | 2 +- 10 files changed, 29 insertions(+), 28 deletions(-) diff --cc modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestResetAbandonedTimerStreaming.java index 6945021cf6,b21c2116e0..21601ef56f --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestResetAbandonedTimerStreaming.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestResetAbandonedTimerStreaming.java @@@ -248,11 -247,13 +248,12 @@@ class TestResetAbandonedTimerStreaming @Test void resultSetsOnEveryAccessPathStayUsableAndHandleStable() throws Exception { awaitSessions(0); - assertResultSetHandlesCompose(true); - assertResultSetHandlesCompose(false); + assertResultSetHandlesCompose(); } + @SuppressWarnings("unlikely-arg-type") // Use of unlikely arg-type is deliberate - private void assertResultSetHandlesCompose(boolean useStatementFacade) throws SQLException { - DataSource ds = streamingPool(useStatementFacade); + private void assertResultSetHandlesCompose() throws SQLException { + DataSource ds = streamingPool(); Connection con = ds.getConnection(); String table = uniqueTable(); try (Statement st = con.createStatement()) { diff --cc modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestStatementDecoratorIdentity.java index f02daedea9,dcf90537cb..553bcd81d0 --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestStatementDecoratorIdentity.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestStatementDecoratorIdentity.java @@@ -61,153 -59,105 +61,156 @@@ class TestStatementDecoratorIdentity ex return ds; } + /* The interceptor's own statement handle behind the facade's. */ + private static Statement statementBehindFacade(Statement facadeHandle) + throws ReflectiveOperationException { + return (Statement) proxiedObject(facadeHandle); + } + + /* The interceptor's own result set handle behind the facade's. */ + private static ResultSet resultSetBehindFacade(ResultSet facadeHandle) + throws ReflectiveOperationException { + return (ResultSet) proxiedObject(facadeHandle); + } + + /* The contract of an open statement handle, for one layer. */ + private static void assertOpenStatementHandle(Statement handle, + Statement other, Connection pooled, String layer) { + Assertions.assertTrue(handle.equals(handle), + layer + ": a statement handle must be equal to itself"); + Assertions.assertFalse(handle.equals(null), + layer + ": a statement handle is not equal to null"); + Assertions.assertFalse(handle.equals(pooled), + layer + ": a statement handle is not equal to its connection"); + Assertions.assertFalse(handle.equals(other), + layer + ": two statements are two handles"); + Assertions.assertFalse(other.equals(handle), + layer + ": and not in the other direction either"); + int hash = handle.hashCode(); + Assertions.assertEquals(hash, handle.hashCode(), + layer + ": hashCode must be a stable value of the handle itself"); + Set<Statement> seen = new HashSet<>(); + seen.add(handle); + Assertions.assertTrue(seen.contains(handle), + layer + ": hashCode must agree with equals"); + } + + /* The contract of a closed statement handle, for one layer. */ + private static void assertClosedStatementHandle(Statement handle, + int hashBeforeClose, Connection pooled, String layer) { + // Neither method declares a checked exception, so a throwing handler + // cannot reach the application as a SQLException - the proxy layer + // wraps it. Both answers must simply be values. + Assertions.assertDoesNotThrow(handle::hashCode, + layer + ": hashCode on a closed handle must return, not throw"); + Assertions.assertEquals(hashBeforeClose, handle.hashCode(), + layer + ": closing must not change the identity of a handle"); + Assertions.assertTrue(handle.equals(handle), + layer + ": closing must not break reflexivity of a handle"); + Assertions.assertFalse(handle.equals(pooled), + layer + ": a closed handle is still equal to nothing but itself"); + try { + handle.hashCode(); + } catch (UndeclaredThrowableException e) { + Assertions.fail(layer + ": hashCode on a closed handle must not " + + "surface as UndeclaredThrowableException: " + e.getCause()); + } + } + + @SuppressWarnings("unlikely-arg-type") // Use of unlikely arg-type is deliberate @Test void aStatementHandleIsEqualOnlyToItselfWhileOpen() throws Exception { - DataSource ds = decoratorPool(false); + DataSource ds = decoratorPool(); try (Connection pooled = ds.getConnection()) { - try (Statement st = pooled.createStatement()) { - Assertions.assertTrue(st.equals(st), - "a statement handle must be equal to itself"); - Assertions.assertFalse(st.equals(null), - "a statement handle is not equal to null"); - Assertions.assertFalse(st.equals(pooled), - "a statement handle is not equal to its connection"); - try (Statement other = pooled.createStatement()) { - Assertions.assertFalse(st.equals(other), - "two statements are two handles"); - Assertions.assertFalse(other.equals(st)); - } - int hash = st.hashCode(); - Assertions.assertEquals(hash, st.hashCode(), - "hashCode must be a stable value of the handle itself"); - Set<Statement> seen = new HashSet<>(); - seen.add(st); - Assertions.assertTrue(seen.contains(st), - "hashCode must agree with equals"); + try (Statement st = pooled.createStatement(); + Statement other = pooled.createStatement()) { + assertOpenStatementHandle(st, other, pooled, "the statement facade"); + assertOpenStatementHandle(statementBehindFacade(st), + statementBehindFacade(other), pooled, + "the decorator's own proxy"); } } } + @SuppressWarnings("unlikely-arg-type") // Use of unlikely arg-type is deliberate @Test void aClosedStatementHandleKeepsAnsweringWithItsOwnIdentity() throws Exception { - DataSource ds = decoratorPool(false); - Connection pooled = ds.getConnection(); - try { + DataSource ds = decoratorPool(); + try (Connection pooled = ds.getConnection()) { Statement st = pooled.createStatement(); - int hash = st.hashCode(); + // The decorator's proxy is reached while the handle is still + // open; it is the layer this test was written for. + Statement decoratorHandle = statementBehindFacade(st); + int facadeHash = st.hashCode(); + int decoratorHash = decoratorHandle.hashCode(); st.close(); - // Neither method declares a checked exception, so a throwing - // handler cannot reach the application as a SQLException - the - // proxy layer wraps it. Both answers must simply be values. - Assertions.assertDoesNotThrow(st::hashCode, - "hashCode on a closed handle must return, not throw"); - Assertions.assertEquals(hash, st.hashCode(), - "closing must not change the identity of a handle"); - Assertions.assertTrue(st.equals(st), - "closing must not break reflexivity of a handle"); - Assertions.assertFalse(st.equals(pooled), - "a closed handle is still equal to nothing but itself"); - try { - st.hashCode(); - } catch (UndeclaredThrowableException e) { - Assertions.fail("hashCode on a closed handle must not surface as " - + "UndeclaredThrowableException: " + e.getCause()); - } - } finally { - pooled.close(); + assertClosedStatementHandle(st, facadeHash, pooled, "the statement facade"); + assertClosedStatementHandle(decoratorHandle, decoratorHash, pooled, + "the decorator's proxy"); } } + @SuppressWarnings("unlikely-arg-type") // Use of unlikely arg-type is deliberate @Test void aResultSetHandleIsAnHonestHandleOpenAndClosed() throws Exception { - DataSource ds = decoratorPool(false); + DataSource ds = decoratorPool(); try (Connection pooled = ds.getConnection()) { - try (Statement st = pooled.createStatement()) { - ResultSet rs = st.executeQuery("SELECT 1"); - Assertions.assertTrue(rs.next()); + Statement st = pooled.createStatement(); + ResultSet rs = st.executeQuery("SELECT 1"); + Assertions.assertTrue(rs.next()); + Statement decoratorSt = statementBehindFacade(st); + ResultSet decoratorRs = resultSetBehindFacade(rs); - Assertions.assertTrue(rs.equals(rs), - "a result set handle must be equal to itself"); - Assertions.assertFalse(rs.equals(null), - "a result set handle is not equal to null"); - Assertions.assertFalse(rs.equals(st), - "a result set handle is not equal to its statement"); - Assertions.assertFalse(rs.equals(new Object()), - "a result set handle is not equal to a foreign object"); - int hash = rs.hashCode(); - Assertions.assertEquals(hash, rs.hashCode(), - "hashCode must be a stable value of the handle itself"); + ResultSet facadeRs = rs; + for (int layer = 0; layer < 2; layer++) { + String name = layer == 0 ? "the statement facade" + : "the decorator's own proxies"; + Statement stHandle = layer == 0 ? st : decoratorSt; + ResultSet rsHandle = layer == 0 ? facadeRs : decoratorRs; + + Assertions.assertTrue(rsHandle.equals(rsHandle), + name + ": a result set handle must be equal to itself"); + Assertions.assertFalse(rsHandle.equals(null), + name + ": a result set handle is not equal to null"); + Assertions.assertFalse(rsHandle.equals(stHandle), + name + ": a result set handle is not equal to its statement"); + Assertions.assertFalse(rsHandle.equals(new Object()), + name + ": a result set handle is not equal to a foreign object"); + int hash = rsHandle.hashCode(); + Assertions.assertEquals(hash, rsHandle.hashCode(), + name + ": hashCode must be a stable value of the handle itself"); // the handle a result set reports must answer to the - // statement handle the application holds - Assertions.assertSame(st, rs.getStatement()); - Assertions.assertTrue(rs.getStatement().equals(st), - "the reported statement must be equal to the held handle"); + // statement handle of the same layer + Assertions.assertSame(stHandle, rsHandle.getStatement(), + name + ": the statement behind the result set must be " + + "the handle of the same layer"); + Assertions.assertTrue(rsHandle.getStatement().equals(stHandle), + name + ": the reported statement must be equal to the held handle"); // toString is answered by the proxy, not forwarded to the // driver's cursor - Assertions.assertTrue(rs.toString().contains("ResultSetProxy"), - "the handle must describe itself, was: " + rs); - - rs.close(); - st.close(); - - Assertions.assertTrue(rs.equals(rs), - "a closed result set handle must still be equal to itself"); - Assertions.assertEquals(hash, rs.hashCode(), - "closing must not change the identity of a handle"); - Assertions.assertDoesNotThrow(() -> Boolean.valueOf(rs.equals(rs)), - "equals on a closed handle must return, not throw"); + Assertions.assertTrue(rsHandle.toString().contains("ResultSetProxy"), + name + ": the handle must describe itself, was: " + rsHandle); + } + + int facadeRsHash = facadeRs.hashCode(); + int decoratorRsHash = decoratorRs.hashCode(); + // Close through the application's handles; both layers see it. + facadeRs.close(); + st.close(); + + for (int layer = 0; layer < 2; layer++) { + String name = layer == 0 ? "the statement facade" + : "the decorator's proxy"; + ResultSet rsHandle = layer == 0 ? facadeRs : decoratorRs; + int hash = layer == 0 ? facadeRsHash : decoratorRsHash; + + Assertions.assertTrue(rsHandle.equals(rsHandle), + name + ": a closed result set handle must still be equal to itself"); + Assertions.assertEquals(hash, rsHandle.hashCode(), + name + ": closing must not change the identity of a handle"); - Assertions.assertDoesNotThrow(() -> rsHandle.equals(rsHandle), ++ Assertions.assertDoesNotThrow(() -> Boolean.valueOf(rsHandle.equals(rsHandle)), + name + ": equals on a closed handle must return, not throw"); } } } @@@ -263,60 -200,47 +266,61 @@@ } /* - * The facade absorbs the Object methods from outside the decorator, so - * the configuration switch must not change any answer the application - * gets. + * The facade answers the Object methods from outside the decorator, and + * since it is always there the application can never be routed around + * it - but the decorator's proxies stay live underneath, and the answers + * both layers give must obey the same contract. */ + @SuppressWarnings("unlikely-arg-type") // Use of unlikely arg-type is deliberate @Test - void bothConfigurationsAnswerTheSameWay() throws Exception { - for (boolean facade : new boolean[] { false, true }) { - String config = facade ? "with" : "without"; - DataSource ds = decoratorPool(facade); - try (Connection pooled = ds.getConnection()) { - Statement st = pooled.createStatement(); - ResultSet rs = st.executeQuery("SELECT 1"); - Assertions.assertTrue(rs.next(), config + " the facade"); - - Assertions.assertTrue(st.equals(st), - config + " the facade a statement handle is equal to itself"); - Assertions.assertFalse(st.equals(null), - config + " the facade equals(null) is false"); - Assertions.assertFalse(st.equals(rs), - config + " the facade a statement is not its result set"); - Assertions.assertTrue(rs.equals(rs), - config + " the facade a result set handle is equal to itself"); - Assertions.assertFalse(rs.equals(st), - config + " the facade a result set is not its statement"); - Assertions.assertTrue(rs.getStatement().equals(st), - config + " the facade the reported statement answers to the held handle"); - - int stHash = st.hashCode(); - int rsHash = rs.hashCode(); - rs.close(); - st.close(); - - Assertions.assertTrue(st.equals(st), - config + " the facade a closed statement stays equal to itself"); - Assertions.assertTrue(rs.equals(rs), - config + " the facade a closed result set stays equal to itself"); - Assertions.assertEquals(stHash, st.hashCode(), - config + " the facade the statement hash survives the close"); - Assertions.assertEquals(rsHash, rs.hashCode(), - config + " the facade the result set hash survives the close"); + void facadeHandleAndDecoratorHandleObeyTheSameContract() throws Exception { + DataSource ds = decoratorPool(); + try (Connection pooled = ds.getConnection()) { + Statement st = pooled.createStatement(); + ResultSet rs = st.executeQuery("SELECT 1"); + Assertions.assertTrue(rs.next()); + Statement[] statements = { st, statementBehindFacade(st) }; + ResultSet[] resultSets = { rs, resultSetBehindFacade(rs) }; + + for (int layer = 0; layer < 2; layer++) { + String name = layer == 0 ? "the statement facade" + : "the decorator's own proxies"; + Statement s = statements[layer]; + ResultSet r = resultSets[layer]; + + Assertions.assertTrue(s.equals(s), + name + ": a statement handle is equal to itself"); + Assertions.assertFalse(s.equals(null), + name + ": equals(null) is false"); + Assertions.assertFalse(s.equals(r), + name + ": a statement is not its result set"); + Assertions.assertTrue(r.equals(r), + name + ": a result set handle is equal to itself"); + Assertions.assertFalse(r.equals(s), + name + ": a result set is not its statement"); + Assertions.assertTrue(r.getStatement().equals(s), + name + ": the reported statement answers to the held handle"); + } + + int[] stHashes = { statements[0].hashCode(), statements[1].hashCode() }; + int[] rsHashes = { resultSets[0].hashCode(), resultSets[1].hashCode() }; + // Close through the application's handles; both layers then face + // the same closed state. + resultSets[0].close(); + statements[0].close(); + + for (int layer = 0; layer < 2; layer++) { + String name = layer == 0 ? "the statement facade" + : "the decorator's own proxies"; + + Assertions.assertTrue(statements[layer].equals(statements[layer]), + name + ": a closed statement stays equal to itself"); + Assertions.assertTrue(resultSets[layer].equals(resultSets[layer]), + name + ": a closed result set stays equal to itself"); + Assertions.assertEquals(stHashes[layer], statements[layer].hashCode(), + name + ": the statement hash survives the close"); + Assertions.assertEquals(rsHashes[layer], resultSets[layer].hashCode(), + name + ": the result set hash survives the close"); } } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
