Package testsuite.regression

Source Code of testsuite.regression.PooledConnectionRegressionTest$ConnectionListener

/*
Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.

  The MySQL Connector/J is licensed under the terms of the GPLv2
  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  this software, see the FLOSS License Exception
  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.

  This program is free software; you can redistribute it and/or modify it under the terms
  of the GNU General Public License as published by the Free Software Foundation; version 2
  of the License.

  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 GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with this
  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  USA


*/
package testsuite.regression;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;

import junit.framework.Test;
import junit.framework.TestSuite;
import testsuite.BaseTestCase;

import com.mysql.jdbc.PacketTooBigException;
import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper;
import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;

/**
* Tests a PooledConnection implementation provided by a JDBC driver. Test case
* provided by Johnny Macchione from bug database record BUG#884. According to
* the JDBC 2.0 specification:
*
* <p>
* "Each call to PooledConnection.getConnection() must return a newly
* constructed Connection object that exhibits the default Connection behavior.
* Only the most recent Connection object produced from a particular
* PooledConnection is open. An existing Connection object is automatically
* closed, if the getConnection() method of its associated Pooled-Connection is
* called again, before it has been explicitly closed by the application. This
* gives the application server a way to �take away� a Connection from the
* application if it wishes, and give it out to someone else. This capability
* will not likely be used frequently in practice."
* </p>
*
* <p>
* "When the application calls Connection.close(), an event is triggered that
* tells the connection pool it can recycle the physical database connection. In
* other words, the event signals the connection pool that the PooledConnection
* object which originally produced the Connection object generating the event
* can be put back in the connection pool."
* </p>
*
* <p>
* "A Connection-EventListener will also be notified when a fatal error occurs,
* so that it can make a note not to put a bad PooledConnection object back in
* the cache when the application finishes using it. When an error occurs, the
* ConnectionEventListener is notified by the JDBC driver, just before the
* driver throws an SQLException to the application to notify it of the same
* error. Note that automatic closing of a Connection object as discussed in the
* previous section does not generate a connection close event."
* </p>
* The JDBC 3.0 specification states the same in other words:
*
* <p>
* "The Connection.close method closes the logical handle, but the physical
* connection is maintained. The connection pool manager is notified that the
* underlying PooledConnection object is now available for reuse. If the
* application attempts to reuse the logical handle, the Connection
* implementation throws an SQLException."
* </p>
*
* <p>
* "For a given PooledConnection object, only the most recently produced logical
* Connection object will be valid. Any previously existing Connection object is
* automatically closed when the associated PooledConnection.getConnection
* method is called. Listeners (connection pool managers) are not notified in
* this case. This gives the application server a way to take a connection away
* from a client. This is an unlikely scenario but may be useful if the
* application server is trying to force an orderly shutdown."
* </p>
*
* <p>
* "A connection pool manager shuts down a physical connection by calling the
* method PooledConnection.close. This method is typically called only in
* certain circumstances: when the application server is undergoing an orderly
* shutdown, when the connection cache is being reinitialized, or when the
* application server receives an event indicating that an unrecoverable error
* has occurred on the connection."
* </p>
* Even though the specification isn't clear about it, I think it is no use
* generating a close event when calling the method PooledConnection.close(),
* even if a logical Connection is open for this PooledConnection, bc the
* PooledConnection will obviously not be returned to the pool.
*
* @author fcr
*/
public final class PooledConnectionRegressionTest extends BaseTestCase {
  private ConnectionPoolDataSource cpds;

  // Count nb of closeEvent.
  protected int closeEventCount;

  // Count nb of connectionErrorEvent
  protected int connectionErrorEventCount;

  /**
   * Creates a new instance of ProgressPooledConnectionTest
   *
   * @param testname
   *            DOCUMENT ME!
   */
  public PooledConnectionRegressionTest(String testname) {
    super(testname);
  }

  /**
   * Set up test case before a test is run.
   *
   * @throws Exception
   *             DOCUMENT ME!
   */
  public void setUp() throws Exception {
    super.setUp();

    // Reset event count.
    this.closeEventCount = 0;
    this.connectionErrorEventCount = 0;

    MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();

    ds.setURL(BaseTestCase.dbUrl);

    this.cpds = ds;
  }

  /**
   * Runs all test cases in this test suite
   *
   * @param args
   */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(PooledConnectionRegressionTest.class);
  }

  /**
   * DOCUMENT ME!
   *
   * @return a test suite composed of this test case.
   */
  public static Test suite() {
    TestSuite suite = new TestSuite(PooledConnectionRegressionTest.class);

    return suite;
  }

  /**
   * After the test is run.
   */
  public void tearDown() throws Exception {
    this.cpds = null;
    super.tearDown();
  }

  /**
   * Tests fix for BUG#7136 ... Statement.getConnection() returning physical
   * connection instead of logical connection.
   */
  public void testBug7136() {
    final ConnectionEventListener conListener = new ConnectionListener();
    PooledConnection pc = null;
    this.closeEventCount = 0;

    try {
      pc = this.cpds.getPooledConnection();

      pc.addConnectionEventListener(conListener);

      Connection _conn = pc.getConnection();

      Connection connFromStatement = _conn.createStatement()
          .getConnection();

      // This should generate a close event.

      connFromStatement.close();

      assertEquals("One close event should've been registered", 1,
          this.closeEventCount);

      this.closeEventCount = 0;

      _conn = pc.getConnection();

      Connection connFromPreparedStatement = _conn.prepareStatement(
          "SELECT 1").getConnection();

      // This should generate a close event.

      connFromPreparedStatement.close();

      assertEquals("One close event should've been registered", 1,
          this.closeEventCount);

    } catch (SQLException ex) {
      fail(ex.toString());
    } finally {
      if (pc != null) {
        try {
          pc.close();
        } catch (SQLException ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  /**
   * Test the nb of closeEvents generated when a Connection is reclaimed. No
   * event should be generated in that case.
   */
  public void testConnectionReclaim() {
    final ConnectionEventListener conListener = new ConnectionListener();
    PooledConnection pc = null;
    final int NB_TESTS = 5;

    try {
      pc = this.cpds.getPooledConnection();

      pc.addConnectionEventListener(conListener);

      for (int i = 0; i < NB_TESTS; i++) {
        Connection _conn = pc.getConnection();

        try {
          // Try to reclaim connection.
          System.out.println("Before connection reclaim.");

          _conn = pc.getConnection();

          System.out.println("After connection reclaim.");
        } finally {
          if (_conn != null) {
            System.out.println("Before connection.close().");

            // This should generate a close event.
            _conn.close();

            System.out.println("After connection.close().");
          }
        }
      }
    } catch (SQLException ex) {
      ex.printStackTrace();
      fail(ex.toString());
    } finally {
      if (pc != null) {
        try {
          System.out.println("Before pooledConnection.close().");

          // This should not generate a close event.
          pc.close();

          System.out.println("After pooledConnection.close().");
        } catch (SQLException ex) {
          ex.printStackTrace();
          fail(ex.toString());
        }
      }
    }

    assertEquals("Wrong nb of CloseEvents: ", NB_TESTS,
        this.closeEventCount);
  }

  /**
   * Tests that PacketTooLargeException doesn't clober the connection.
   *
   * @throws Exception
   *             if the test fails.
   */
  public void testPacketTooLargeException() throws Exception {
    final ConnectionEventListener conListener = new ConnectionListener();
    PooledConnection pc = null;

    pc = this.cpds.getPooledConnection();

    pc.addConnectionEventListener(conListener);

    createTable("testPacketTooLarge", "(field1 LONGBLOB)");

    Connection connFromPool = pc.getConnection();
    PreparedStatement pstmtFromPool = ((ConnectionWrapper) connFromPool)
        .clientPrepare("INSERT INTO testPacketTooLarge VALUES (?)");

    this.rs = this.stmt
        .executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'");
    this.rs.next();

    int maxAllowedPacket = this.rs.getInt(2);

    int numChars = (int) (maxAllowedPacket * 1.2);

    pstmtFromPool.setBinaryStream(
        1,
        new BufferedInputStream(new FileInputStream(newTempBinaryFile(
            "testPacketTooLargeException", numChars))), numChars);

    try {
      pstmtFromPool.executeUpdate();
      fail("Expecting PacketTooLargeException");
    } catch (PacketTooBigException ptbe) {
      // We're expecting this one...
    }

    // This should still work okay, even though the last query on the
    // same
    // connection didn't...
    connFromPool.createStatement().executeQuery("SELECT 1");

    assertTrue(this.connectionErrorEventCount == 0);
    assertTrue(this.closeEventCount == 0);
  }

  /**
   * Test the nb of closeEvents generated by a PooledConnection. A
   * JDBC-compliant driver should only generate 1 closeEvent each time
   * connection.close() is called.
   */
  public void testCloseEvent() {
    final ConnectionEventListener conListener = new ConnectionListener();
    PooledConnection pc = null;
    final int NB_TESTS = 5;

    try {
      pc = this.cpds.getPooledConnection();

      pc.addConnectionEventListener(conListener);

      for (int i = 0; i < NB_TESTS; i++) {
        Connection pConn = pc.getConnection();

        System.out.println("Before connection.close().");

        // This should generate a close event.
        pConn.close();

        System.out.println("After connection.close().");
      }
    } catch (SQLException ex) {
      fail(ex.toString());
    } finally {
      if (pc != null) {
        try {
          System.out.println("Before pooledConnection.close().");

          // This should not generate a close event.
          pc.close();

          System.out.println("After pooledConnection.close().");
        } catch (SQLException ex) {
          ex.printStackTrace();
        }
      }
    }
    assertEquals("Wrong nb of CloseEvents: ", NB_TESTS,
        this.closeEventCount);
  }

  /**
   * Listener for PooledConnection events.
   */
  protected final class ConnectionListener implements ConnectionEventListener {
    /** */
    public void connectionClosed(ConnectionEvent event) {
      PooledConnectionRegressionTest.this.closeEventCount++;
      System.out
          .println(PooledConnectionRegressionTest.this.closeEventCount
              + " - Connection closed.");
    }

    /** */
    public void connectionErrorOccurred(ConnectionEvent event) {
      PooledConnectionRegressionTest.this.connectionErrorEventCount++;
      System.out.println("Connection error: " + event.getSQLException());
    }
  }

  /**
   * Tests fix for BUG#35489 - Prepared statements from pooled connections
   * cause NPE when closed() under JDBC4
   *
   * @throws Exception
   *             if the test fails
   */
  public void testBug35489() throws Exception {
    MysqlConnectionPoolDataSource pds = new MysqlConnectionPoolDataSource();
    pds.setUrl(dbUrl);
    this.pstmt = pds.getPooledConnection().getConnection()
        .prepareStatement("SELECT 1");
    this.pstmt.execute();
    this.pstmt.close();

    MysqlXADataSource xads = new MysqlXADataSource();
    xads.setUrl(dbUrl);
    this.pstmt = xads.getXAConnection().getConnection()
        .prepareStatement("SELECT 1");
    this.pstmt.execute();
    this.pstmt.close();

    xads = new MysqlXADataSource();
    xads.setUrl(dbUrl);
    xads.setPinGlobalTxToPhysicalConnection(true);
    this.pstmt = xads.getXAConnection().getConnection()
        .prepareStatement("SELECT 1");
    this.pstmt.execute();
    this.pstmt.close();
  }
}
TOP

Related Classes of testsuite.regression.PooledConnectionRegressionTest$ConnectionListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.