/*
* Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: TestDbConcurrency.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.database;
import com.uwyn.rife.database.exceptions.DatabaseException;
import com.uwyn.rife.database.queries.CreateTable;
import com.uwyn.rife.database.queries.Delete;
import com.uwyn.rife.database.queries.DropTable;
import com.uwyn.rife.database.queries.Insert;
import com.uwyn.rife.database.queries.Select;
import com.uwyn.rife.tools.exceptions.InnerClassException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import junit.framework.TestCase;
public class TestDbConcurrency extends TestCase
{
public static boolean VERBOSE = false;
public static boolean DEBUG = false;
private Datasource mDatasource = null;
private static final Object sOutputLock = new Object();
private static final int sOutputLimit = 60;
private static int sOutputChars = 0;
private static int sConnectionOverload = 25;
public TestDbConcurrency(Datasource datasource, String datasourceName, String name)
{
super(name);
mDatasource = datasource;
}
private static void display(char display)
{
if (VERBOSE)
{
synchronized (sOutputLock)
{
System.out.print(display);
sOutputChars++;
if (sOutputChars == sOutputLimit)
{
sOutputChars = 0;
System.out.println();
}
}
}
}
public static void displayCommit()
{
display('v');
}
public static void displayError()
{
display('x');
}
public void testConcurrency()
{
Structure structure = new Structure(mDatasource);
try
{
structure.install();
}
catch (DatabaseException e)
{
e.printStackTrace();
}
if (VERBOSE)
{
System.out.println();
}
ArrayList<Concurrency> threads = new ArrayList<Concurrency>();
Integer mainlock = new Integer(0);
Concurrency concurrency = null;
for (int i = 1; i <= mDatasource.getPoolsize()*sConnectionOverload; i++)
{
concurrency = new Concurrency(mDatasource, mainlock);
Thread thread = new Thread(concurrency, "example "+i);
thread.setDaemon(true);
threads.add(concurrency);
thread.start();
}
boolean thread_alive = true;
boolean thread_advanced = false;
synchronized (mainlock)
{
while (thread_alive)
{
try
{
mainlock.wait(10000);
}
catch (InterruptedException e)
{
Thread.yield();
}
thread_alive = false;
thread_advanced = false;
for (Concurrency thread : threads)
{
if (thread.isAlive())
{
thread_alive = true;
if (thread.hasAdvanced())
{
thread_advanced = true;
break;
}
}
}
// if none of the threads has advanced
// throw an error and terminate all threads
if (thread_alive && !thread_advanced)
{
for (Concurrency thread : threads)
{
thread.terminate();
}
thread_alive = false;
System.out.println();
System.out.println("Concurrency deadlock for datasource with driver: '"+mDatasource.getDriver()+"'.");
System.exit(1);
break;
}
}
}
try
{
structure.remove();
}
catch (DatabaseException e)
{
e.printStackTrace();
}
mDatasource.cleanup();
}
}
class Concurrency extends DbQueryManager implements Runnable
{
private Object mMainlock = null;
private int mErrors = 0;
private int mCommits = 0;
private long mLastExecution = -1;
private boolean mAlive = false;
public Concurrency(Datasource datasource, Object mainlock)
{
super(datasource);
mMainlock = mainlock;
}
public void terminate()
{
mAlive = false;
}
public long getLastExecution()
{
return mLastExecution;
}
public boolean hasAdvanced()
{
if (-1 == mLastExecution)
{
return true;
}
if (System.currentTimeMillis() < mLastExecution+(100*1000))
{
return true;
}
return false;
}
public boolean isAlive()
{
if (mErrors >= 10 && mCommits >= 10)
{
return false;
}
return mAlive;
}
public void run()
{
mAlive = true;
while (isAlive())
{
mLastExecution = System.currentTimeMillis();
try
{
doIt();
Thread.yield();
Thread.sleep((int)Math.random()*200);
TestDbConcurrency.displayCommit();
mCommits++;
}
catch (DatabaseException e)
{
TestDbConcurrency.displayError();
mErrors++;
if (TestDbConcurrency.DEBUG)
{
e.printStackTrace();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
mAlive = false;
synchronized (mMainlock)
{
mMainlock.notifyAll();
}
}
public void doIt()
throws DatabaseException
{
if (TestDbConcurrency.DEBUG)
{
System.out.println(Thread.currentThread().getName()+" : begin");
}
inTransaction(new DbTransactionUserWithoutResult() {
public int getTransactionIsolation()
{
if (getDatasource().getAliasedDriver().equals("org.apache.derby.jdbc.EmbeddedDriver"))
{
return Connection.TRANSACTION_READ_UNCOMMITTED;
}
if (getDatasource().getAliasedDriver().equals("in.co.daffodil.db.jdbc.DaffodilDBDriver"))
{
return Connection.TRANSACTION_SERIALIZABLE;
}
return -1;
}
public void useTransactionWithoutResult()
throws InnerClassException
{
Insert insert = new Insert(getDatasource());
insert
.into("example")
.fieldParameter("firstname")
.fieldParameter("lastname");
DbPreparedStatement insert_stmt = getConnection().getPreparedStatement(insert);
try
{
insert_stmt.setString("firstname", "John");
if ((Math.random()*100) <= 30)
{
insert_stmt.setNull("lastname", Types.VARCHAR);
}
else
{
insert_stmt.setString("lastname", "Doe");
}
insert_stmt.executeUpdate();
insert_stmt.clearParameters();
insert_stmt.setString("firstname", "Jane");
insert_stmt.setString("lastname", "TheLane");
insert_stmt.executeUpdate();
}
finally
{
insert_stmt.close();
}
Select select = new Select(getDatasource());
select
.from("example")
.orderBy("firstname");
DbStatement select_stmt = executeQuery(select);
try
{
Processor processor = new Processor();
while (fetch(select_stmt.getResultSet(), processor) &&
processor.wasSuccessful())
{
processor.getFirstname();
processor.getLastname();
}
}
finally
{
select_stmt.close();
}
}
});
if ((Math.random()*100) <= 10)
{
inTransaction(new DbTransactionUserWithoutResult() {
public int getTransactionIsolation()
{
if (getDatasource().getAliasedDriver().equals("org.apache.derby.jdbc.EmbeddedDriver"))
{
return Connection.TRANSACTION_READ_UNCOMMITTED;
}
if (getDatasource().getAliasedDriver().equals("in.co.daffodil.db.jdbc.DaffodilDBDriver"))
{
return Connection.TRANSACTION_SERIALIZABLE;
}
return -1;
}
public void useTransactionWithoutResult()
throws InnerClassException
{
Delete delete = new Delete(getDatasource());
delete
.from("example");
DbPreparedStatement delete_stmt = getConnection().getPreparedStatement(delete);
try
{
delete_stmt.executeUpdate();
}
finally
{
delete_stmt.close();
}
if (TestDbConcurrency.DEBUG)
{
System.out.println(Thread.currentThread().getName()+" : deleted");
}
}
});
}
if (TestDbConcurrency.DEBUG)
{
System.out.println(Thread.currentThread().getName()+" : comitted");
}
}
}
class Processor extends DbRowProcessor
{
private String mFirstname = null;
private String mLastname = null;
public String getFirstname()
{
return mFirstname;
}
public String getLastname()
{
return mLastname;
}
public boolean processRow(ResultSet resultSet)
throws SQLException
{
mFirstname = resultSet.getString("firstname");
mLastname = resultSet.getString("lastname");
return true;
}
}
class Structure extends DbQueryManager
{
public Structure(Datasource datasource)
{
super(datasource);
}
public void install()
throws DatabaseException
{
CreateTable create = new CreateTable(getDatasource());
create
.table("example")
.column("firstname", String.class, 50, CreateTable.NOTNULL)
.column("lastname", String.class, 50, CreateTable.NOTNULL);
executeUpdate(create);
}
public void remove()
throws DatabaseException
{
DropTable drop = new DropTable(getDatasource());
drop.table("example");
executeUpdate(drop);
}
}