/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.jboss.bqt.framework; import java.io.File; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.apache.commons.lang.StringUtils; import org.jboss.bqt.core.exception.FrameworkException; import org.jboss.bqt.core.exception.FrameworkRuntimeException; import org.jboss.bqt.core.exception.QueryTestFailedException; import org.jboss.bqt.core.exception.TransactionRuntimeException; import org.jboss.bqt.core.util.ArgCheck; import org.jboss.bqt.framework.connection.ConnectionStrategy; import org.jboss.bqt.framework.connection.ConnectionStrategyFactory; import org.jboss.bqt.framework.util.AssertResults; import org.jboss.bqt.framework.util.PrintResults; /** * This class the base class that provides the core logic around using * the jdbc connection and its results. * * The connection is not closed by this class, only lent to this class for * query processing. */ public abstract class AbstractQuery implements TransactionAPI { // NOTE not all tests will pass with this set to true, only those with // scrollable resultsets static boolean WRITE_ACTUAL = false; protected ConnectionStrategy connStrategy; protected Connection internalConnection = null; protected ResultSet internalResultSet = null; protected Statement internalStatement = null; private SQLException internalException = null; private Throwable applicationException = null; private String testClassName = "n/a"; private TestCase testCase = null; public AbstractQuery() { this.testClassName = StringUtils.substringAfterLast(this.getClass().getName(), "."); connStrategy = ConnectionStrategyFactory .createConnectionStrategy(); } /************************* * LifeCycle Methods *************************/ /** * @param testCase * */ public void before(TestCase testCase) { this.testCase = testCase; this.applicationException = null; this.internalException = null; try { this.setConnection(this.connStrategy.getConnection()); } catch (FrameworkException e) { throw new FrameworkRuntimeException(e.getMessage()); } } public void after() { testCase.getTestResult().setException(this.getException()); if (testCase.getTestResult().getException()!= null) { this.testCase.getTestResult().setStatus(TestResult.RESULT_STATE.TEST_EXCEPTION); } } /** * Call after each query has completed and fully processed. Meaning all the * rows have been read, that need to be, and any commit or rollback has been * performed. */ public void cleanup() { closeStatement(); // don't clear connection, it will be reused, // just clear it out this.internalConnection = null; this.internalException = null; this.applicationException = null; } public ConnectionStrategy getConnectionStrategy() { return this.connStrategy; } /* ********** End Of LifeCycle Methods ************ */ public void setConnection(Connection c) { this.internalConnection = c; } public Connection getConnection() { return this.internalConnection; } public Statement getStatement() { return this.internalStatement; } public TestCase getTestCase() { return this.testCase; } public ResultSet getResultSet() { return this.internalResultSet; } /** * @param sql * @return boolean * @throws QueryTestFailedException * */ public boolean execute(String sql) throws QueryTestFailedException { return execute(sql, new Object[] {}); } public boolean execute(String sql, Object[] params) throws QueryTestFailedException{ return execute(sql, params, null); } public boolean execute(String sql, Object[] params, Serializable payload) throws QueryTestFailedException { closeStatement(); long endTS = 0; long beginTS = 0; boolean result = false; try { ArgCheck.isNotNull(this.internalConnection, "Unable to execute, connection is null"); ArgCheck.isTrue(!this.internalConnection.isClosed(), "Connection is closed"); if (params != null && params.length > 0) { if (sql.toLowerCase().startsWith("exec ")) { //$NON-NLS-1$ sql = sql.substring(5); this.internalStatement = createPrepareCallStatement(sql); } else { this.internalStatement = createPrepareStatement(sql); } setParameters((PreparedStatement) this.internalStatement, params); assignExecutionProperties(this.internalStatement); this.setPayload(this.internalStatement, payload); beginTS = System.currentTimeMillis(); result = ((PreparedStatement) this.internalStatement).execute(); endTS = System.currentTimeMillis(); } else { this.internalStatement = createStatement(); assignExecutionProperties(this.internalStatement); // this.setPayload(this.internalStatement, payload); beginTS = System.currentTimeMillis(); result = this.internalStatement.execute(sql); endTS = System.currentTimeMillis(); } if (result) { this.internalResultSet = this.internalStatement.getResultSet(); } else { this.testCase.getTestResult().setRowCount(0); this.testCase.getTestResult().setUpdateCount( this.internalStatement.getUpdateCount() ); } } catch (SQLException e) { endTS = -1; beginTS = -1; this.internalException = e; this.connStrategy.shutdown(); throw new QueryTestFailedException(e); } this.testCase.getTestResult().setBeginTS(beginTS); this.testCase.getTestResult().setEndTS(endTS); return result; } protected Statement createPrepareCallStatement(String sql) throws SQLException { return this.internalConnection.prepareCall("{?=call " + sql + "}"); //$NON-NLS-1$ //$NON-NLS-2$ } protected Statement createPrepareStatement(String sql) throws SQLException { return this.internalConnection.prepareStatement(sql); } // protected Statement createStatement() throws SQLException { // return this.internalConnection.createStatement(); // } // // @Override protected Statement createStatement() throws SQLException { return this.internalConnection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); } private void setParameters(PreparedStatement stmt, Object[] params) throws SQLException { for (int i = 0; i < params.length; i++) { stmt.setObject(i + 1, params[i]); } } private void setPayload(Object stmt, Serializable payload) { if (payload == null) return; // if (stmt instanceof org.teiid.jdbc.TeiidStatement) { // ((TeiidStatement) stmt).setPayload(payload); // } } public int[] executeBatch(String[] sql) throws QueryTestFailedException { return executeBatch(sql, -1); } public int[] executeBatch(String[] sql, int timeout) throws QueryTestFailedException { closeStatement(); try { ArgCheck.isNotNull(this.internalConnection, "Unable to execute batch, connection is null"); ArgCheck.isTrue(!this.internalConnection.isClosed()); for (int i = 0; i < sql.length; i++) { if (sql[i].indexOf("?") != -1) { //$NON-NLS-1$ throw new QueryTestFailedException(FrameworkPlugin.Util.getString("AbstractQueryTest.invalidPreparedStatementInBatch")); } } this.internalStatement = createStatement(); assignExecutionProperties(this.internalStatement); if (timeout != -1) { this.internalStatement.setQueryTimeout(timeout); } for (int i = 0; i < sql.length; i++) { this.internalStatement.addBatch(sql[i]); } return this.internalStatement.executeBatch(); } catch (SQLException e) { this.internalException = e; // if (!exceptionExpected()) { throw new QueryTestFailedException(e); // } } // return null; } /** * Override when you need to set an execution property on the statement * before execution. * * <p> * Example: * <code>if (this.executionProperties.getProperty(ExecutionProperties.PROP_FETCH_SIZE) != null) { * statement.setExecutionProperty(ExecutionProperties.PROP_FETCH_SIZE, this.executionProperties.getProperty(ExecutionProperties.PROP_FETCH_SIZE)); * } * </code> * </p> * * @param stmt * * @since */ protected void assignExecutionProperties(Statement stmt) { } public boolean exceptionOccurred() { return this.internalException != null; } /** * Called by the testing process to capture the exception to be exposed * when {@link #getException()} is called. * @param t */ public void setApplicationException(Throwable t) { // keep the first one set, which is most like the exception closest // to the problem that we need to know about if (this.applicationException == null) this.applicationException = t; } private Throwable getException() { if (this.internalException != null) return this.internalException; if (this.applicationException != null) { if (this.applicationException instanceof SQLException) { return this.applicationException; } SQLException mm = new SQLException( this.applicationException.getMessage()); return mm; } return null; } public void assertResultsSetEquals(Object expected) throws QueryTestFailedException { if (expected instanceof File) { AssertResults.assertResultsSetEquals(this.internalResultSet, (File) expected, this.compareCaseSensitive()); } else if (expected instanceof String) { AssertResults.assertResultsSetEquals(this.internalResultSet, (String) expected, this.compareCaseSensitive()); } else { AssertResults.assertResultsSetEquals(this.internalResultSet, (String[]) expected, this.compareCaseSensitive()); } } protected boolean compareCaseSensitive() { return true; } public void printResults() { PrintResults.printResults(this.internalResultSet); } public void printResults(boolean comparePrint) { ArgCheck.isNotNull(this.internalResultSet, "Unable to printResults, ResultSet it null"); PrintResults.printResults(this.internalResultSet, comparePrint); } public void walkResults() throws QueryTestFailedException { ArgCheck.isNotNull(this.internalResultSet, "Unable to walk results, ResultSet is null"); try { int columnCount = this.internalResultSet.getMetaData() .getColumnCount(); while (this.internalResultSet.next()) { for (int col = 1; col <= columnCount; col++) { this.internalResultSet.getObject(col); } } closeResultSet(); } catch (SQLException e) { throw new QueryTestFailedException(e); } } public int getRowCount() throws QueryTestFailedException { if (this.internalResultSet == null) return 0; // Count all try { int count = 0; while (this.internalResultSet.next()) { count++; } return count; } catch (SQLException e) { throw new QueryTestFailedException(e); } } public void cancelQuery() throws SQLException { ArgCheck.isNotNull(this.internalConnection, "Unable to cancel query, result set is null"); ArgCheck.isTrue(!this.internalConnection.isClosed()); ArgCheck.isNotNull(this.internalStatement, "Unable to close statement, its null"); this.internalStatement.cancel(); } protected void executeAndAssertResults(String query, String[] expected) throws QueryTestFailedException { execute(query); if (expected != null) { assertResultsSetEquals(expected); } else { printResults(true); } } private void closeStatement() { closeResultSet(); if (this.internalStatement != null) { try { this.internalStatement.close(); } catch (SQLException e) { throw new TransactionRuntimeException(e); } finally { this.internalStatement = null; } } } private void closeResultSet() { if (this.internalResultSet != null) { try { this.internalResultSet.close(); } catch (SQLException e) { // ignore } finally { this.internalResultSet = null; } } } protected void debug(String message) { FrameworkPlugin.LOGGER.debug("[" + this.testClassName + "] " + message); } protected void detail(String message) { FrameworkPlugin.LOGGER.info("[" + this.testClassName + "] " + message); } }