@Test
  public void unwrapForSupportedInterface() throws Exception {

    DataSourceStub ds = new DataSourceStub();
    ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(ds);

    assertTrue(tested.isWrapperFor(Supported.class));
    assertEquals(ds, tested.unwrap(Supported.class));
  }
  @Test
  public void testOperationWithDirectCloseCall() throws SQLException {
    Connection con = createMock(Connection.class);
    DataSource ds = createMock(DataSource.class);

    expect(ds.getConnection()).andReturn(con); // con1
    con.close();
    expect(ds.getConnection()).andReturn(con); // con2
    con.close();

    replay(ds);
    replay(con);

    final ExtendedConnectionDataSourceProxy csds = new ExtendedConnectionDataSourceProxy(ds);

    Connection con1 = csds.getConnection();
    csds.startCloseSuppression(con1);
    Connection con1_1 = csds.getConnection();
    assertSame("should be same connection", con1_1, con1);
    con1_1.close(); // no mock call for this - should be suppressed
    Connection con1_2 = csds.getConnection();
    assertSame("should be same connection", con1_2, con1);
    Connection con2 = csds.getConnection();
    assertNotSame("shouldn't be same connection", con2, con1);
    csds.stopCloseSuppression(con1);
    assertTrue("should be able to close connection", csds.shouldClose(con1));
    con1_1 = null;
    con1_2 = null;
    con1.close();
    assertTrue("should be able to close connection", csds.shouldClose(con2));
    con2.close();

    verify(ds);
    verify(con);
  }
  @Test
  public void unwrapForSmartDataSource() throws Exception {

    ExtendedConnectionDataSourceProxy tested =
        new ExtendedConnectionDataSourceProxy(new DataSourceStub());

    assertTrue(tested.isWrapperFor(DataSource.class));
    assertEquals(tested, tested.unwrap(DataSource.class));

    assertTrue(tested.isWrapperFor(SmartDataSource.class));
    assertEquals(tested, tested.unwrap(SmartDataSource.class));
  }
  @Test
  public void unwrapForUnsupportedInterface() throws Exception {

    ExtendedConnectionDataSourceProxy tested =
        new ExtendedConnectionDataSourceProxy(new DataSourceStub());

    assertFalse(tested.isWrapperFor(Unsupported.class));

    try {
      tested.unwrap(Unsupported.class);
      fail();
    } catch (SQLException expected) {
      //			this would be the correct behavior in a Java6-only recursive implementation
      //			assertEquals(DataSourceStub.UNWRAP_ERROR_MESSAGE, expected.getMessage());
      assertEquals("Unsupported class " + Unsupported.class.getSimpleName(), expected.getMessage());
    }
  }
  @Test(expected = IllegalArgumentException.class)
  public void delegateIsRequired() throws Exception {

    ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(null);
    tested.afterPropertiesSet();
  }
  @Test
  public void testSupressOfCloseWithJdbcTemplate() throws Exception {

    Connection con = createMock(Connection.class);
    DataSource ds = createMock(DataSource.class);
    Statement stmt = createMock(Statement.class);
    ResultSet rs = createMock(ResultSet.class);

    // open and start suppressing close
    expect(ds.getConnection()).andReturn(con);

    // transaction 1
    expect(con.getAutoCommit()).andReturn(false);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select baz from bar")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select foo from bar")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    con.commit();

    // transaction 2
    expect(con.getAutoCommit()).andReturn(false);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select ham from foo")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    // REQUIRES_NEW transaction
    expect(ds.getConnection()).andReturn(con);
    expect(con.getAutoCommit()).andReturn(false);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select 1 from eggs")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    con.commit();
    con.close();
    // resume transaction 2
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select more, ham from foo")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    con.commit();

    // transaction 3
    expect(con.getAutoCommit()).andReturn(false);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select spam from ham")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    con.commit();

    // stop suppressing close and close
    con.close();

    // standalone query
    expect(ds.getConnection()).andReturn(con);
    expect(con.createStatement()).andReturn(stmt);
    expect(stmt.executeQuery("select egg from bar")).andReturn(rs);
    expect(rs.next()).andReturn(false);
    con.close();

    replay(rs);
    replay(stmt);
    replay(con);
    replay(ds);

    final ExtendedConnectionDataSourceProxy csds = new ExtendedConnectionDataSourceProxy();
    csds.setDataSource(ds);
    PlatformTransactionManager tm = new DataSourceTransactionManager(csds);
    TransactionTemplate tt = new TransactionTemplate(tm);
    final TransactionTemplate tt2 = new TransactionTemplate(tm);
    tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    final JdbcTemplate template = new JdbcTemplate(csds);

    Connection connection = DataSourceUtils.getConnection(csds);
    csds.startCloseSuppression(connection);
    tt.execute(
        new TransactionCallback() {
          @Override
          public Object doInTransaction(TransactionStatus status) {
            template.queryForList("select baz from bar");
            template.queryForList("select foo from bar");
            return null;
          }
        });
    tt.execute(
        new TransactionCallback() {
          @Override
          public Object doInTransaction(TransactionStatus status) {
            template.queryForList("select ham from foo");
            tt2.execute(
                new TransactionCallback() {
                  @Override
                  public Object doInTransaction(TransactionStatus status) {
                    template.queryForList("select 1 from eggs");
                    return null;
                  }
                });
            template.queryForList("select more, ham from foo");
            return null;
          }
        });
    tt.execute(
        new TransactionCallback() {
          @Override
          public Object doInTransaction(TransactionStatus status) {
            template.queryForList("select spam from ham");
            return null;
          }
        });
    csds.stopCloseSuppression(connection);
    DataSourceUtils.releaseConnection(connection, csds);
    template.queryForList("select egg from bar");

    verify(rs);
    verify(stmt);
    verify(con);
    verify(ds);
  }