/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package io.narayana.spi;
import com.arjuna.ats.jta.common.jtaPropertyManager;
import io.narayana.spi.util.DbProps;
import io.narayana.spi.util.*;
import org.junit.Assert;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class DbTester {
private final int NROWS = 3;
private Map<String, Connection> connections;
private Map<String, Integer> counts;
private String fault;
public DbTester(boolean clearTables) throws SQLException, InitializationException {
Map<String, DbProps> dbConfigs = new DbProps().getConfig(DbProps.DB_PROPERTIES_NAME);
connections = new HashMap<String, Connection>();
for (DbProps props : dbConfigs.values())
connections.put(props.getBinding(), getDataSource(props.getBinding()).getConnection());
createTables(clearTables);
fault = System.getProperty("spitest.fault", "");
counts = new HashMap<String, Integer>(connections.size());
for (Map.Entry<String, Connection> entry : connections.entrySet())
counts.put(entry.getKey(), countRows(entry.getValue(), "CEYLONKV"));
}
public DbTester() throws SQLException, InitializationException {
this(true);
}
public void clearCounts() {
for (String db : counts.keySet())
counts.put(db, 0);
}
public void doInserts() throws SQLException, NamingException, RollbackException, SystemException {
/* for testing recovery use a sorted set to ensure that postresql is enlisted into any transaction after h2
* and if fault injection is required then the dummy resource is enlisted before postresql
*/
SortedSet<String> keys = new TreeSet<String>(connections.keySet());
if ("XA_RBROLLBACK".equalsIgnoreCase(fault)) {
// the first participant will throw a rollback exception during the commit phase resulting in a transaction rollback
injectFault(ASFailureType.XARES_COMMIT, ASFailureMode.XAEXCEPTION, "XA_RBROLLBACK");
}
for (String key : keys) {
Connection connection = connections.get(key);
if ("HALT".equalsIgnoreCase(fault) && "postgresql".equals(key)) {
System.out.println("testXADSWithFaults: halting during phase 2");
injectFault(ASFailureType.XARES_COMMIT, ASFailureMode.HALT, "");
}
for (int i = 0; i < NROWS; i++)
insertTable(connection, "k" + i, "v" + i);
PreparedStatement statement = connection.prepareStatement("SELECT * FROM CEYLONKV");
ResultSet rs = statement.executeQuery();
printResultSet(key, rs, "key", "val");
rs.close();
statement.close();
}
}
public String getFault() {
return fault;
}
public void assertRowCounts(boolean committed) throws SQLException {
assertRowCounts(committed ? NROWS : 0);
}
public void assertRowCounts(int extraRows) throws SQLException {
for (Map.Entry<String, Connection> entry : connections.entrySet()) {
Assert.assertEquals(entry.getKey(), counts.get(entry.getKey()) + extraRows, countRows(connections.get(entry.getKey()), "CEYLONKV"));
}
}
public void dropTables() throws SQLException {
dropTables(connections);
}
public void createTables(boolean clearTables) throws SQLException {
String sql = "CREATE TABLE CEYLONKV " +
"(key VARCHAR(255) not NULL, " +
" val VARCHAR(255), " +
" PRIMARY KEY ( key ))";
for (Connection connection : connections.values()) {
Statement statement = connection.createStatement();
try {
statement.executeUpdate(sql);
} catch (SQLException e) {
// ignore
}
if (clearTables)
statement.executeUpdate("delete from CEYLONKV");
statement.close();
}
}
private DataSource getDataSource(String bindingName) throws InitializationException, SQLException {
try {
return (DataSource) new InitialContext().lookup(bindingName);
} catch (NamingException e) {
throw new InitializationException(e.getMessage(), e);
}
}
private int countRows(Connection connection, String tableName) throws SQLException {
int count = 0;
Statement s1 = connection.createStatement();
ResultSet rs = s1.executeQuery("select count(*) from " + tableName);
while (rs.next()){
count = rs.getInt(1);
}
rs.close();
s1.close();
return count;
}
private void injectFault(ASFailureType type, ASFailureMode mode, String modeArg) throws RollbackException, SystemException, NamingException {
ASFailureSpec fault = new ASFailureSpec("fault", mode, modeArg, type);
TransactionManager transactionManager =
(TransactionManager) new InitialContext().lookup(jtaPropertyManager.getJTAEnvironmentBean().getTransactionManagerJNDIContext());
transactionManager.getTransaction().enlistResource(new DummyXAResource(fault));
}
public void dropTables(Map<String, Connection> connections) throws SQLException {
for (Connection connection : connections.values()) {
Statement statement = connection.createStatement();
statement.executeUpdate("DROP TABLE CEYLONKV");
statement.close();
}
}
public void printResultSet(String title, ResultSet rs, String ... colNames) throws SQLException {
System.out.println(title);
while(rs.next()) {
for (String colName : colNames) {
String colVal = rs.getString(colName);
System.out.printf("%s: %s ", colName, colVal);
}
System.out.println();
}
}
public static void insertTable(Connection connection, String key, String val) throws SQLException {
String insert = "INSERT INTO CEYLONKV(key ,val) values (?,?)";
PreparedStatement statement = connection.prepareStatement(insert);
statement.setString(1, key);
statement.setString(2, val);
statement.executeUpdate();
statement.close();
}
}