/**
   * Closing a connection handle should release that connection back in the pool and mark it as
   * closed.
   *
   * @throws SecurityException
   * @throws NoSuchFieldException
   * @throws IllegalArgumentException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   * @throws NoSuchMethodException
   * @throws SQLException
   */
  @SuppressWarnings("unchecked")
  @Test
  public void testInternalClose()
      throws SecurityException, NoSuchFieldException, IllegalArgumentException,
          IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLException {
    ConcurrentLinkedQueue<Statement> mockStatementHandles =
        createNiceMock(ConcurrentLinkedQueue.class);
    StatementHandle mockStatement = createNiceMock(StatementHandle.class);

    mockConnection.close();
    expectLastCall().once().andThrow(new SQLException()).once();

    Map<Connection, Reference<ConnectionHandle>> refs =
        new HashMap<Connection, Reference<ConnectionHandle>>();
    expect(mockPool.getFinalizableRefs()).andReturn(refs).anyTimes();
    FinalizableReferenceQueue finalizableRefQueue = new FinalizableReferenceQueue();

    expect(mockPool.getFinalizableRefQueue()).andReturn(finalizableRefQueue).anyTimes();
    expect(mockConnection.getPool()).andReturn(mockPool).anyTimes();

    replay(mockStatement, mockConnection, mockStatementHandles, mockPool);
    testClass.internalClose();
    try {
      testClass.internalClose(); // 2nd time should throw exception
      fail("Should have thrown an exception");
    } catch (Throwable t) {
      // do nothing.
    }

    verify(mockStatement, mockConnection, mockStatementHandles);
  }
  /**
   * Tests that a partition with expired connections should those connections killed off.
   *
   * @throws SQLException
   */
  @Test
  @SuppressWarnings({"unchecked", "rawtypes"})
  public void testConnectionExpired() throws SQLException {

    TransferQueue<ConnectionHandle> mockQueue = createNiceMock(TransferQueue.class);
    expect(mockConnectionPartition.getAvailableConnections()).andReturn(1);
    expect(mockConnectionPartition.getFreeConnections()).andReturn(mockQueue).anyTimes();
    ConnectionHandle mockConnectionExpired = createNiceMock(ConnectionHandle.class);
    ConnectionHandle mockConnection = createNiceMock(ConnectionHandle.class);
    expect(mockQueue.poll()).andReturn(mockConnectionExpired).once();

    expect(mockConnectionExpired.isExpired(anyLong())).andReturn(true).once();

    expect(mockExecutor.isShutdown()).andReturn(false).once();

    mockConnectionExpired.internalClose();
    expectLastCall().once();

    mockPool.postDestroyConnection(mockConnectionExpired);
    expectLastCall().once();

    expect(mockExecutor.schedule((Callable) anyObject(), anyLong(), (TimeUnit) anyObject()))
        .andReturn(null)
        .once();
    replay(
        mockQueue,
        mockExecutor,
        mockConnectionPartition,
        mockConnection,
        mockPool,
        mockConnectionExpired);
    testClass.run();
    verify(mockConnectionExpired);
  }
  /**
   * Tests that a partition with expired connections should those connections killed off.
   *
   * @throws SQLException
   */
  @Test
  @SuppressWarnings("unchecked")
  public void testConnectionNotExpiredLifoMode() throws SQLException {

    LIFOQueue<ConnectionHandle> mockQueue = createNiceMock(LIFOQueue.class);
    expect(mockConnectionPartition.getAvailableConnections()).andReturn(1);
    expect(mockConnectionPartition.getFreeConnections()).andReturn(mockQueue).anyTimes();
    ConnectionHandle mockConnection = createNiceMock(ConnectionHandle.class);
    expect(mockQueue.poll()).andReturn(mockConnection).once();

    expect(mockConnection.isExpired(anyLong())).andReturn(false).once();

    expect(mockExecutor.isShutdown()).andReturn(false).once();

    expect(mockConnection.getOriginatingPartition()).andReturn(mockConnectionPartition).anyTimes();
    expect(mockQueue.offerLast(mockConnection)).andReturn(false).anyTimes();
    mockConnection.internalClose();

    replay(mockQueue, mockExecutor, mockConnectionPartition, mockConnection, mockPool);
    ConnectionMaxAgeThread testClass2 =
        new ConnectionMaxAgeThread(mockConnectionPartition, mockExecutor, mockPool, 5000, true);
    testClass2.run();

    verify(mockConnection, mockPool);
  }
  /**
   * Tests fake exceptions, connection should be shutdown if the scheduler was marked as going down.
   * Same test except just used to check for a spurious interrupted exception (should be logged).
   *
   * @throws SQLException
   * @throws InterruptedException
   * @throws NoSuchFieldException
   * @throws SecurityException
   * @throws IllegalAccessException
   * @throws IllegalArgumentException
   */
  @Test
  public void testExceptionOnCloseConnection()
      throws SQLException, InterruptedException, SecurityException, NoSuchFieldException,
          IllegalArgumentException, IllegalAccessException {
    ArrayBlockingQueue<ConnectionHandle> fakeFreeConnections =
        new ArrayBlockingQueue<ConnectionHandle>(1);
    fakeFreeConnections.add(mockConnection);

    config.setIdleConnectionTestPeriod(1);
    expect(mockPool.getConfig()).andReturn(config).anyTimes();
    expect(mockConnectionPartition.getFreeConnections()).andReturn(fakeFreeConnections).anyTimes();
    expect(mockConnectionPartition.getMinConnections()).andReturn(10).once();
    expect(mockConnection.isPossiblyBroken()).andReturn(false);
    expect(mockConnection.getConnectionLastUsed()).andReturn(0L);
    expect(mockPool.isConnectionHandleAlive((ConnectionHandle) anyObject()))
        .andReturn(false)
        .anyTimes();

    // connection should be closed
    mockConnection.internalClose();
    expectLastCall().andThrow(new SQLException());

    replay(mockPool, mockConnection, mockConnectionPartition, mockExecutor, mockLogger);
    this.testClass = new ConnectionTesterThread(mockConnectionPartition, mockExecutor, mockPool);
    Field loggerField = this.testClass.getClass().getDeclaredField("logger");
    loggerField.setAccessible(true);
    loggerField.set(this.testClass, mockLogger);
    this.testClass.run();
    verify(mockPool, mockConnectionPartition, mockExecutor, mockConnection, mockLogger);
  }
  /**
   * Tests fake exceptions, connection should be shutdown if the scheduler was marked as going down.
   * Mostly for code coverage.
   *
   * @throws SQLException
   * @throws InterruptedException
   */
  @Test
  public void testInterruptedException() throws SQLException, InterruptedException {
    ArrayBlockingQueue<ConnectionHandle> fakeFreeConnections =
        new ArrayBlockingQueue<ConnectionHandle>(1);
    fakeFreeConnections.add(mockConnection);

    config.setIdleConnectionTestPeriod(1);
    expect(mockPool.getConfig()).andReturn(config).anyTimes();
    expect(mockConnectionPartition.getFreeConnections()).andReturn(fakeFreeConnections).anyTimes();
    expect(mockConnectionPartition.getMinConnections()).andReturn(10).once();
    expect(mockConnection.isPossiblyBroken()).andReturn(false);
    expect(mockConnection.getConnectionLastUsed()).andReturn(0L);
    expect(mockPool.isConnectionHandleAlive((ConnectionHandle) anyObject()))
        .andReturn(true)
        .anyTimes();
    expect(mockExecutor.isShutdown()).andReturn(true);
    mockPool.releaseInAnyFreePartition(
        (ConnectionHandle) anyObject(), (ConnectionPartition) anyObject());
    expectLastCall().andThrow(new InterruptedException());
    // connection should be closed
    mockConnection.internalClose();
    mockPool.postDestroyConnection(mockConnection);
    expectLastCall().once();

    replay(mockPool, mockConnection, mockConnectionPartition, mockExecutor);
    this.testClass = new ConnectionTesterThread(mockConnectionPartition, mockExecutor, mockPool);
    this.testClass.run();
    verify(mockPool, mockConnectionPartition, mockExecutor, mockConnection);
  }
  /** @throws SQLException */
  @Test
  public void testCloseConnectionWithException() throws SQLException {
    ConnectionHandle mockConnection = createNiceMock(ConnectionHandle.class);
    mockPool.postDestroyConnection(mockConnection);
    expectLastCall().once();

    mockConnection.internalClose();
    expectLastCall().andThrow(new SQLException());

    replay(mockConnection, mockPool);
    testClass.closeConnection(mockConnection);
    verify(mockConnection, mockPool);
  }
  /** @throws SQLException */
  @Test
  public void testCloseConnectionWithExceptionCoverage() throws SQLException {
    ConnectionHandle mockConnection = createNiceMock(ConnectionHandle.class);
    mockPool.postDestroyConnection(mockConnection);
    expectLastCall().once();
    ConnectionMaxAgeThread.logger = null; // make it break.
    mockConnection.internalClose();
    expectLastCall().andThrow(new SQLException());

    replay(mockConnection, mockPool);
    try {
      testClass.closeConnection(mockConnection);
    } catch (Exception e) {
      // do nothing
    }
    verify(mockConnection, mockPool);
  }
  /**
   * Tests that a connection that is marked broken is closed internally and that the partition is
   * marked as being able to create new connections.
   *
   * @throws SQLException
   */
  @Test
  public void testConnectionMarkedBroken() throws SQLException {
    ArrayBlockingQueue<ConnectionHandle> fakeFreeConnections =
        new ArrayBlockingQueue<ConnectionHandle>(1);
    fakeFreeConnections.add(mockConnection);

    expect(mockPool.getConfig()).andReturn(config).anyTimes();
    expect(mockConnectionPartition.getFreeConnections()).andReturn(fakeFreeConnections).anyTimes();
    expect(mockConnection.isPossiblyBroken()).andReturn(true);

    // connection should be closed
    mockConnection.internalClose();
    mockPool.postDestroyConnection(mockConnection);
    expectLastCall().once();
    replay(mockPool, mockConnection, mockConnectionPartition, mockExecutor);
    this.testClass = new ConnectionTesterThread(mockConnectionPartition, mockExecutor, mockPool);
    this.testClass.run();
    verify(mockPool, mockConnectionPartition, mockExecutor, mockConnection);
  }