mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
14 hours ago 3fe8fc5280bcb0ced976a698fcae2dc977450ce6
opendj-server-legacy/src/test/java/org/opends/server/backends/jdbc/CachedConnectionTestCase.java
@@ -39,8 +39,19 @@
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.PreparedStatement;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLInvalidAuthorizationSpecException;
import java.sql.SQLNonTransientConnectionException;
import java.sql.SQLNonTransientException;
import java.sql.SQLRecoverableException;
import java.sql.SQLSyntaxErrorException;
import java.sql.SQLTimeoutException;
import java.sql.SQLTransactionRollbackException;
import java.sql.SQLTransientConnectionException;
import java.sql.SQLTransientException;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
@@ -942,6 +953,157 @@
   }
   /**
    * A link that says the connection is gone by its type alone still says so once it is rebuilt
    * (#1074). The type is what the JDBC contract gives a driver to say it - write() asks it before
    * the SQLState - so a copy made as a plain SQLException reads as a failure that says nothing of
    * the connection, and only at the deployment whose url has a password in it.
    */
   @Test(timeOut = 60000)
   public void testALinkThatSaysTheConnectionIsGoneByItsTypeKeepsItThroughRedaction() throws Exception {
      final String url = "jdbc:postgresql://opendj:S3cretOfTheBackend@127.0.0.1:5432/opendj";
      for (final SQLException gone : new SQLException[] { new SQLRecoverableException("io error"),
            new SQLNonTransientConnectionException("closed"), new SQLTransientConnectionException("reset") }) {
         final SQLException failure = new SQLException("login to " + url + " failed", "S0001", 18456);
         failure.setNextException(gone);
         final SQLException reported = CachedConnection.reported(failure, url);
         assertNoCredentials(reported);
         assertTrue(JDBCStorage.isConnectionFailure(reported),
            "a rebuilt " + gone.getClass().getSimpleName() + " no longer says the connection is gone");
      }
   }
   /**
    * ... and so does one standing past the budget of the rebuild, whatever it says it by: the link
    * that stands for the rest of the chain there is all that the classification of write() gets to
    * read of it. The url has no password, and the chain is rebuilt all the same - it is longer than
    * the walk looking for credentials, which answers "yes" past it.
    */
   @Test(timeOut = 60000)
   public void testALinkThatSaysTheConnectionIsGonePastTheBudgetOfARebuildStillSaysSo() throws Exception {
      final String url = "jdbc:postgresql://127.0.0.1:5432/opendj";
      for (final SQLException gone : new SQLException[] { new SQLException("connection reset", "08S01", 10054),
            new SQLRecoverableException("io error") }) {
         final SQLException failure = plainChain(40);
         failure.setNextException(gone);
         final SQLException reported = CachedConnection.reported(failure, url);
         assertTrue(reported != failure, "a chain this long is expected to be rebuilt");
         assertTrue(JDBCStorage.isConnectionFailure(reported),
            gone + " past the budget of the rebuild no longer says the connection is gone");
      }
   }
   /**
    * ... and so does one past the budget on any other edge the rebuild cuts: the cause of a link, what
    * was suppressed on it - where establish() puts the close that failed (#929) - and the cause of a
    * link that is no SQLException, which is rebuilt on a road of its own. Each chain spends the budget
    * before the rebuild reaches the edge, so the link that says the connection is gone is behind the
    * one that stands for the rest; the last one has it on what was suppressed on a link that is cut,
    * which the rest is read for as well.
    */
   @Test(timeOut = 60000)
   public void testALinkThatSaysTheConnectionIsGoneOnACauseOrASuppressedEdgePastTheBudgetStillSaysSo()
         throws Exception {
      final String url = "jdbc:postgresql://127.0.0.1:5432/opendj";
      final SQLException byCause = plainChain(40);
      byCause.initCause(new IOException("socket closed", new SQLException("connection reset", "08S01", 10054)));
      final SQLException bySuppressed = plainChain(40);
      bySuppressed.addSuppressed(new SQLException("the connection is closed", "08003"));
      final SQLException byTheCauseOfALinkThatIsNoSQLException = new SQLException("login failed", "S0001", 18456);
      Throwable wrapper = new SQLException("connection reset", "08S01", 10054);
      for (int i = 0; i < 40; i++) {
         wrapper = new IOException("wrapper " + i, wrapper);
      }
      byTheCauseOfALinkThatIsNoSQLException.initCause(wrapper);
      final SQLException byTheSuppressedOfALinkCut = plainChain(40);
      lastOf(byTheSuppressedOfALinkCut).addSuppressed(new SQLException("the connection is closed", "08003"));
      final Object[][] cases = {
         { "the cause", byCause },
         { "the suppressed", bySuppressed },
         { "the cause of a link that is no SQLException", byTheCauseOfALinkThatIsNoSQLException },
         { "what was suppressed on a link cut", byTheSuppressedOfALinkCut } };
      for (final Object[] edge : cases) {
         final SQLException failure = (SQLException) edge[1];
         assertTrue(JDBCStorage.isConnectionFailure(failure), edge[0] + ": the failure itself says the connection is gone");
         final SQLException reported = CachedConnection.reported(failure, url);
         assertNotSame(reported, failure, edge[0] + ": a chain this long is expected to be rebuilt");
         assertTrue(JDBCStorage.isConnectionFailure(reported),
            edge[0] + " cut past the budget no longer says the connection is gone");
      }
   }
   /**
    * Every standard type of JDBC a link can be of survives its rebuild, not only the three that say
    * the connection is gone: each is read somewhere - a SQLTimeoutException is what sends a borrow of
    * an import to the debug log rather than the warn one (borrowedOrShared()) - and a rebuild that
    * turned one into its supertype would classify the same failure one way where the url of the
    * backend has no password and the other where it does. Each type is asked for before its
    * supertype, so a check out of order fails here as well.
    */
   @Test(timeOut = 60000)
   public void testEveryStandardTypeOfALinkIsKeptThroughRedaction() throws Exception {
      final String url = "jdbc:postgresql://opendj:S3cretOfTheBackend@127.0.0.1:5432/opendj";
      final String message = "login to " + url + " failed";
      for (final SQLException original : new SQLException[] {
            new SQLRecoverableException(message, "08006", 17002),
            new SQLNonTransientConnectionException(message, "08001", 0),
            new SQLTransientConnectionException(message, "08001", 0),
            new SQLTimeoutException(message, "HYT00", 0),
            new SQLTransactionRollbackException(message, "40001", 1205),
            new SQLFeatureNotSupportedException(message, "0A000", 0),
            new SQLIntegrityConstraintViolationException(message, "23000", 2627),
            new SQLInvalidAuthorizationSpecException(message, "28000", 18456),
            new SQLSyntaxErrorException(message, "42000", 0),
            new SQLDataException(message, "22000", 0),
            new SQLTransientException(message, "S1000", 0),
            new SQLNonTransientException(message, "S1000", 0),
            new SQLException(message, "S1000", 0) }) {
         final SQLException reported = CachedConnection.reported(original, url);
         assertNoCredentials(reported);
         assertEquals(reported.getClass(), original.getClass(), "the rebuild changed the type of the link");
         assertEquals(reported.getSQLState(), original.getSQLState(), "the rebuild changed the SQLState of the link");
         assertEquals(reported.getErrorCode(), original.getErrorCode(), "the rebuild changed the vendor code of the link");
      }
   }
   /**
    * The link standing for the rest says only what the rest says: a chain that says nothing of the
    * connection is not made to say it is gone by being cut. That would replay the write and distrust
    * the pool over a database that refused a connection for a reason of its own.
    */
   @Test(timeOut = 60000)
   public void testALongChainThatSaysNothingOfTheConnectionIsNotMadeToSayItByTheRebuild() throws Exception {
      final SQLException reported = CachedConnection.reported(plainChain(40), "jdbc:postgresql://127.0.0.1:5432/opendj");
      assertFalse(JDBCStorage.isConnectionFailure(reported),
         "the rebuild made a failure that says nothing of the connection say it is gone");
   }
   /** That many plain links of the next exception chain, none of which says the connection is gone. */
   private static SQLException plainChain(int links) {
      final SQLException head = new SQLException("error 0", "S0001", 1);
      for (int i = 1; i < links; i++) {
         head.setNextException(new SQLException("error " + i, "S0001", 1));
      }
      return head;
   }
   /** The last link of the next exception chain of a failure. */
   private static SQLException lastOf(SQLException failure) {
      SQLException last = failure;
      while (last.getNextException() != null) {
         last = last.getNextException();
      }
      return last;
   }
   /**
    * A credential named by a suppressed link alone is redacted like any other. The close of a
    * connection whose set-up failed rides there (establish(), #929) and an interrupt that ended a
    * wait for a catalog connect does, and a driver names the url it could not close as readily as