From 05429c2cdf393730f17fb8fa51ee4b9e58e0a88f Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 17 Aug 2011 13:30:40 -0400 Subject: [PATCH] Renamed logger. Improved test suite. Added Apache Commons and DBCP. --- .classpath | 6 + .gitignore | 1 + NOTICE | 23 +- README.markdown | 1 + api/v6.xml | 5874 +++++++++++++++++ build.xml | 3 + docs/00_index.mkd | 1 + docs/02_usage.mkd | 20 +- docs/03_performance.mkd | 6 +- docs/05_building.mkd | 4 + docs/05_releases.mkd | 7 + src/com/iciql/Constants.java | 6 +- src/com/iciql/Db.java | 4 +- src/com/iciql/IciqlException.java | 3 + src/com/iciql/Query.java | 8 +- src/com/iciql/SQLDialectDefault.java | 3 + src/com/iciql/TableDefinition.java | 14 +- src/com/iciql/build/Build.java | 18 +- ...{StatementLogger.java => IciqlLogger.java} | 41 +- ...tListener.java => Slf4jIciqlListener.java} | 16 +- tests/com/iciql/test/AnnotationsTest.java | 3 + tests/com/iciql/test/IciqlSuite.java | 201 +- 22 files changed, 6141 insertions(+), 122 deletions(-) create mode 100644 api/v6.xml rename src/com/iciql/util/{StatementLogger.java => IciqlLogger.java} (80%) rename src/com/iciql/util/{Slf4jStatementListener.java => Slf4jIciqlListener.java} (77%) diff --git a/.classpath b/.classpath index a842b70..e985d8c 100644 --- a/.classpath +++ b/.classpath @@ -33,5 +33,11 @@ + + + + + + diff --git a/.gitignore b/.gitignore index ecb1ae2..2808169 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /performance.txt /performance_db.txt /performance_sql.txt +/testdbs diff --git a/NOTICE b/NOTICE index 61f70f6..f39f8f6 100644 --- a/NOTICE +++ b/NOTICE @@ -46,9 +46,9 @@ HSQL Database http://hsqldb.org --------------------------------------------------------------------------- -Derby Database +Apache Derby Database --------------------------------------------------------------------------- - Derby, released under the + Apache Derby, released under the Apache Software License, Version 2.0. http://db.apache.org/derby @@ -75,4 +75,21 @@ SLF4J SLF4J, released under the MIT/X11 License. - http://www.slf4j.org \ No newline at end of file + http://www.slf4j.org + +--------------------------------------------------------------------------- +Apache Commons Pool +--------------------------------------------------------------------------- + Apache Commons Pool, released under the + Apache Software License, Version 2.0. + + http://commons.apache.org/pool + +--------------------------------------------------------------------------- +Apache Commons DBCP +--------------------------------------------------------------------------- + Apache Commons DBCP, released under the + Apache Software License, Version 2.0. + + http://commons.apache.org/dbcp + \ No newline at end of file diff --git a/README.markdown b/README.markdown index 28aa424..e5894e2 100644 --- a/README.markdown +++ b/README.markdown @@ -21,6 +21,7 @@ Supported Databases (Unit-Tested) - [HSQLDB](http://hsqldb.org) 2.2.4 - [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2 - [MySQL](http://mysql.com) 5.0.51b +- [PostgreSQL](http://postgresql.org) 9.0 Support for others is possible and may only require creating a simple "dialect" class. diff --git a/api/v6.xml b/api/v6.xml new file mode 100644 index 0000000..d3e8257 --- /dev/null +++ b/api/v6.xml @@ -0,0 +1,5874 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.xml b/build.xml index 5ac4352..72c4960 100644 --- a/build.xml +++ b/build.xml @@ -108,6 +108,7 @@ Executing iciql ${iq.version} test suite + This will take a few minutes... @@ -139,6 +140,8 @@ + + diff --git a/docs/00_index.mkd b/docs/00_index.mkd index daf9c42..ea5df61 100644 --- a/docs/00_index.mkd +++ b/docs/00_index.mkd @@ -40,6 +40,7 @@ select * from products - [HSQLDB](http://hsqldb.org) 2.2.4 - [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2 - [MySQL](http://mysql.com) 5.0.51b +- [PostgreSQL](http://postgresql.org) 9.0 Support for others is possible and may only require creating a simple "dialect" class. diff --git a/docs/02_usage.mkd b/docs/02_usage.mkd index 31dbfb6..14619f2 100644 --- a/docs/02_usage.mkd +++ b/docs/02_usage.mkd @@ -97,33 +97,33 @@ Iciql does not throw any [checked exceptions](http://en.wikipedia.org/wiki/Excep ### Statement Logging -Iciql provides a mechanism to log generated statements to the console, to SLF4J, or to your own logging framework. Exceptions are not logged using this mechanism; exceptions are wrapped and rethrown as `IciqlException`, which is a RuntimeException. +Iciql provides a mechanism to log generated statements and warnings to the console, to SLF4J, or to your own logging framework. Exceptions are not logged using this mechanism; exceptions are wrapped and rethrown as `IciqlException`, which is a RuntimeException. #### Console Logging %BEGINCODE% -StatmentLogger.activeConsoleLogger(); -StatmentLogger.deactiveConsoleLogger(); +IciqlLogger.activeConsoleLogger(); +IciqlLogger.deactiveConsoleLogger(); %ENDCODE% #### SLF4J Logging %BEGINCODE% -Slf4jStatementListener slf4j = new Slf4jStatementListener(); +Slf4jIciqlListener slf4j = new Slf4jIciqlListener(); slf4j.setLevel(StatementType.CREATE, Level.WARN); slf4j.setLevel(StatementType.DELETE, Level.WARN); slf4j.setLevel(StatementType.MERGE, Level.OFF); -StatmentLogger.registerListener(slf4j); -StatmentLogger.unregisterListener(slf4j); +IciqlLogger.registerListener(slf4j); +IciqlLogger.unregisterListener(slf4j); %ENDCODE% #### Custom Logging %BEGINCODE% -StatementListener custom = new StatementListener() { - public void logStatement(StatementType type, String statement) { +IciqlListener custom = new IciqlListener() { + public void logIciql(StatementType type, String statement) { // do log } }; -StatmentLogger.registerListener(custom); -StatmentLogger.unregisterListener(custom); +IciqlLogger.registerListener(custom); +IciqlLogger.unregisterListener(custom); %ENDCODE% ## Understanding Aliases and Model Classes diff --git a/docs/03_performance.mkd b/docs/03_performance.mkd index 7df050a..12c07a1 100644 --- a/docs/03_performance.mkd +++ b/docs/03_performance.mkd @@ -9,7 +9,11 @@ Performance of iciql statement generation is not currently benchmarked. ### iciql+database performance comparison -The following data was generated by running the iciql test suite. The suite is almost completely single-threaded. All Java databases are run in embedded *memory-only* mode through a JDBC connection. +The following data was generated by running the *single-threaded* iciql test suite. All database connections are pooled and re-used within each execution of the test suite using [Apache Commons DBCP](http://commons.apache.org/dbcp). + +Connections are pooled to normalize embedded database performance with out-of-process database performance. Some of the Java embedded database configurations have a very high startup-time penalty. Notably, H2 is slow to open a database and its performance is substantially affected if connection pooling is not enabled to keep the embedded database open. + +All performance numbers include the combined overhead of iciql statement generation and JUnit 4 test framework execution so they are not bare-metal database metrics.
 %DBPERFORMANCE%
diff --git a/docs/05_building.mkd b/docs/05_building.mkd
index 8a4f45b..0025279 100644
--- a/docs/05_building.mkd
+++ b/docs/05_building.mkd
@@ -11,12 +11,16 @@ Additionally, [eclipse-cs](http://eclipse-cs.sourceforge.net), [FindBugs](http:/
 - [H2 Database](http://h2database.com) (Eclipse Public License 1.0)
 - [HSQL Database Engine](http://hsqldb.org) (BSD)
 - [Apache Derby Database](http://db.apache.org/derby) (Apache 2.0)
+- [MySQL Connector/J](http://dev.mysql.com/downloads/connector/j) (GPL)
+- [PostgreSQL JDBC Connector](http://jdbc.postgresql.org) (BSD)
 - [JUnit](http://junit.org) (Common Public License)
 - [commons-net](http://commons.apache.org/net) (Apache 2.0)
 - [ant-googlecode](http://code.google.com/p/ant-googlecode) (New BSD)
 - [MarkdownPapers](http://markdown.tautua.org) (Apache 2.0)
 - [doclava](http://code.google.com/p/doclava) (Apache 2.0)
 - [SLF4J](http://www.slf4j.org) (MIT/X11)
+- [Apache Commons Pool](http://commons.apache.org/pool) (Apache 2.0)
+- [Apache Commons DBCP](http://commons.apache.org/dbcp) (Apache 2.0)
 
 ### Instructions
 1. Clone the git repository from [GoogleCode][iciqlsrc].
diff --git a/docs/05_releases.mkd b/docs/05_releases.mkd
index 8e0dfd6..bd3c853 100644
--- a/docs/05_releases.mkd
+++ b/docs/05_releases.mkd
@@ -6,13 +6,20 @@
 
 **%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%))   *released %BUILDDATE%*
 
+- api change release (API v6)
 - Finished MySQL dialect implementation.  MySQL 5.0.51b passes 100% of tests.
+- Added PostgreSQL dialect.  PostgreSQL 9.0 passes all but the boolean-as-int tests.
 - Added Db.dropTable(T) method
+- Overhauled test suite and included more database configurations.
+- Renamed StatementLogger to IciqlLogger
+- Added IciqlLogger.warn method
+- Added IciqlLogger.drop method
 
 ### Older Releases
 
 **0.6.6**   *released 2011-08-15*
 
+- api change release (API v5)
 - Disabled two concurrency unit tests since I believe they are flawed and do not yield reproducible results
 - Added Derby database dialect.  Derby 10.7.1.1 and 10.8.1.2 pass 100% of tests.
 - Implemented HSQL MERGE syntax.  HSQL 2.2.4 fails 1 test which is a known [bug in HSQL](https://sourceforge.net/tracker/?func=detail&aid=3390047&group_id=23316&atid=378131)
diff --git a/src/com/iciql/Constants.java b/src/com/iciql/Constants.java
index bf6a5d0..b8d38dd 100644
--- a/src/com/iciql/Constants.java
+++ b/src/com/iciql/Constants.java
@@ -25,14 +25,14 @@ public class Constants {
 
 	// The build script extracts this exact line so be careful editing it
 	// and only use A-Z a-z 0-9 .-_ in the string.
-	public static final String VERSION = "0.6.6";
+	public static final String VERSION = "0.7.0";
 
 	// The build script extracts this exact line so be careful editing it
 	// and only use A-Z a-z 0-9 .-_ in the string.
-	public static final String VERSION_DATE = "2011-08-15";
+	public static final String VERSION_DATE = "2011-08-17";
 
 	// The build script extracts this exact line so be careful editing it
 	// and only use A-Z a-z 0-9 .-_ in the string.
-	public static final String API_CURRENT = "5";
+	public static final String API_CURRENT = "6";
 
 }
diff --git a/src/com/iciql/Db.java b/src/com/iciql/Db.java
index da0a7ac..af3c139 100644
--- a/src/com/iciql/Db.java
+++ b/src/com/iciql/Db.java
@@ -38,7 +38,7 @@ import com.iciql.DbUpgrader.DefaultDbUpgrader;
 import com.iciql.Iciql.IQTable;
 import com.iciql.Iciql.IQVersion;
 import com.iciql.util.JdbcUtils;
-import com.iciql.util.StatementLogger;
+import com.iciql.util.IciqlLogger;
 import com.iciql.util.StringUtils;
 import com.iciql.util.Utils;
 import com.iciql.util.WeakIdentityHashMap;
@@ -245,7 +245,7 @@ public class Db {
 		TableDefinition def = (TableDefinition) define(modelClass);
 		SQLStatement stat = new SQLStatement(this);
 		getDialect().prepareDropTable(stat, def);
-		StatementLogger.drop(stat.getSQL());
+		IciqlLogger.drop(stat.getSQL());
 		int rc = 0;
 		try {
 			rc = stat.executeUpdate();
diff --git a/src/com/iciql/IciqlException.java b/src/com/iciql/IciqlException.java
index 4670418..7e7021e 100644
--- a/src/com/iciql/IciqlException.java
+++ b/src/com/iciql/IciqlException.java
@@ -109,6 +109,9 @@ public class IciqlException extends RuntimeException {
 			} else if ("42X05".equals(state)) {
 				// Derby table not found
 				iciqlCode = CODE_OBJECT_NOT_FOUND;
+			} else if ("42Y55".equals(state)) {
+				// Derby table not found
+				iciqlCode = CODE_OBJECT_NOT_FOUND;
 			} else if ("42S02".equals(state)) {
 				// H2 table not found
 				iciqlCode = CODE_OBJECT_NOT_FOUND;
diff --git a/src/com/iciql/Query.java b/src/com/iciql/Query.java
index 8f34761..b2d397a 100644
--- a/src/com/iciql/Query.java
+++ b/src/com/iciql/Query.java
@@ -31,7 +31,7 @@ import java.util.List;
 import com.iciql.Iciql.EnumType;
 import com.iciql.bytecode.ClassReader;
 import com.iciql.util.JdbcUtils;
-import com.iciql.util.StatementLogger;
+import com.iciql.util.IciqlLogger;
 import com.iciql.util.Utils;
 
 /**
@@ -141,7 +141,7 @@ public class Query {
 		stat.appendSQL("DELETE FROM ");
 		from.appendSQL(stat);
 		appendWhere(stat);
-		StatementLogger.delete(stat.getSQL());
+		IciqlLogger.delete(stat.getSQL());
 		return stat.executeUpdate();
 	}
 
@@ -239,7 +239,7 @@ public class Query {
 			declaration.appendSQL(stat);
 		}
 		appendWhere(stat);
-		StatementLogger.update(stat.getSQL());
+		IciqlLogger.update(stat.getSQL());
 		return stat.executeUpdate();
 	}
 
@@ -713,7 +713,7 @@ public class Query {
 			}
 		}
 		db.getDialect().appendLimitOffset(stat, limit, offset);
-		StatementLogger.select(stat.getSQL());
+		IciqlLogger.select(stat.getSQL());
 	}
 
 	/**
diff --git a/src/com/iciql/SQLDialectDefault.java b/src/com/iciql/SQLDialectDefault.java
index 7ef507a..4814fdd 100644
--- a/src/com/iciql/SQLDialectDefault.java
+++ b/src/com/iciql/SQLDialectDefault.java
@@ -23,6 +23,7 @@ import java.text.MessageFormat;
 
 import com.iciql.TableDefinition.FieldDefinition;
 import com.iciql.TableDefinition.IndexDefinition;
+import com.iciql.util.IciqlLogger;
 import com.iciql.util.StatementBuilder;
 import com.iciql.util.StringUtils;
 
@@ -217,6 +218,8 @@ public class SQLDialectDefault implements SQLDialect {
 		case UNIQUE_HASH:
 			buff.append("UNIQUE ");
 			break;
+		default:
+			IciqlLogger.warn("{0} does not support hash indexes", getClass().getSimpleName());
 		}
 		buff.append("INDEX ");
 		buff.append(index.indexName);
diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java
index 6f201ad..9b3666c 100644
--- a/src/com/iciql/TableDefinition.java
+++ b/src/com/iciql/TableDefinition.java
@@ -38,7 +38,7 @@ import com.iciql.Iciql.IQTable;
 import com.iciql.Iciql.IQVersion;
 import com.iciql.Iciql.IndexType;
 import com.iciql.util.StatementBuilder;
-import com.iciql.util.StatementLogger;
+import com.iciql.util.IciqlLogger;
 import com.iciql.util.StringUtils;
 import com.iciql.util.Utils;
 
@@ -431,7 +431,7 @@ public class TableDefinition {
 		}
 		buff.append(')');
 		stat.setSQL(buff.toString());
-		StatementLogger.insert(stat.getSQL());
+		IciqlLogger.insert(stat.getSQL());
 		if (returnKey) {
 			return stat.executeInsert();
 		}
@@ -461,7 +461,7 @@ public class TableDefinition {
 		}
 		SQLStatement stat = new SQLStatement(db);
 		db.getDialect().prepareMerge(stat, schemaName, tableName, this, obj);
-		StatementLogger.merge(stat.getSQL());
+		IciqlLogger.merge(stat.getSQL());
 		return stat.executeUpdate();
 	}
 
@@ -503,7 +503,7 @@ public class TableDefinition {
 		}
 		stat.setSQL(buff.toString());
 		query.appendWhere(stat);
-		StatementLogger.update(stat.getSQL());
+		IciqlLogger.update(stat.getSQL());
 		return stat.executeUpdate();
 	}
 
@@ -535,7 +535,7 @@ public class TableDefinition {
 		}
 		stat.setSQL(buff.toString());
 		query.appendWhere(stat);
-		StatementLogger.delete(stat.getSQL());
+		IciqlLogger.delete(stat.getSQL());
 		return stat.executeUpdate();
 	}
 
@@ -548,7 +548,7 @@ public class TableDefinition {
 		}
 		SQLStatement stat = new SQLStatement(db);
 		db.getDialect().prepareCreateTable(stat, this);
-		StatementLogger.create(stat.getSQL());
+		IciqlLogger.create(stat.getSQL());
 		try {
 			stat.executeUpdate();
 		} catch (IciqlException e) {
@@ -561,7 +561,7 @@ public class TableDefinition {
 		for (IndexDefinition index : indexes) {
 			stat = new SQLStatement(db);
 			db.getDialect().prepareCreateIndex(stat, schemaName, tableName, index);
-			StatementLogger.create(stat.getSQL());
+			IciqlLogger.create(stat.getSQL());
 			try {
 				stat.executeUpdate();
 			} catch (IciqlException e) {
diff --git a/src/com/iciql/build/Build.java b/src/com/iciql/build/Build.java
index 75f6483..e8b9385 100644
--- a/src/com/iciql/build/Build.java
+++ b/src/com/iciql/build/Build.java
@@ -69,6 +69,10 @@ public class Build {
 		downloadFromApache(MavenObject.DOCLAVA, BuildType.COMPILETIME);
 		downloadFromApache(MavenObject.SLF4JAPI, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.SLF4JAPI, BuildType.COMPILETIME);
+		downloadFromApache(MavenObject.COMMONSPOOL, BuildType.RUNTIME);
+		downloadFromApache(MavenObject.COMMONSPOOL, BuildType.COMPILETIME);
+		downloadFromApache(MavenObject.COMMONSDBCP, BuildType.RUNTIME);
+		downloadFromApache(MavenObject.COMMONSDBCP, BuildType.COMPILETIME);
 
 		// needed for site publishing
 		downloadFromApache(MavenObject.COMMONSNET, BuildType.RUNTIME);
@@ -172,9 +176,9 @@ public class Build {
 				"219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d",
 				"c7adc475ca40c288c93054e0f4fe58f3a98c0cb5");
 
-		public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.158",
-				"4bac13427caeb32ef6e93b70101e61f370c7b5e2", "6bb165156a0831879fa7797df6e18bdcd4421f2d",
-				"446d3f58c44992534cb54f67134532d95961904a");
+		public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.159",
+				"dd89f939661eb5593909584e1c243db0c25de130", "4d953bf765e8a13c7e06ca51165438338966c698",
+				"4c79ed03f994820a1a873150c8a9f13c667784d3");
 
 		public static final MavenObject HSQLDB = new MavenObject("org/hsqldb", "hsqldb", "2.2.4",
 				"6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "", "");
@@ -206,6 +210,14 @@ public class Build {
 				"6f3b8a24bf970f17289b234284c94f43eb42f0e4", "46a386136c901748e6a3af67ebde6c22bc6b4524",
 				"e223571d77769cdafde59040da235842f3326453");
 
+		public static final MavenObject COMMONSPOOL = new MavenObject("commons-pool", "commons-pool", "1.5.6",
+				"16390e2d74df4ab08c06a85d42a74a951dc93ad7", "bbfb73ed3c341d9738c64da8157910b967f878d6",
+				"d72204023b30cd9fecb64829586472f3c6806005");
+
+		public static final MavenObject COMMONSDBCP = new MavenObject("commons-dbcp", "commons-dbcp", "1.4",
+				"30be73c965cc990b153a100aaaaafcf239f82d39", "9b076ff231434d5403be6599a1347019b12c0def",
+				"098bf7c8d5b026f6e3969259a36e813ac37432b3");
+
 		public final String group;
 		public final String artifact;
 		public final String version;
diff --git a/src/com/iciql/util/StatementLogger.java b/src/com/iciql/util/IciqlLogger.java
similarity index 80%
rename from src/com/iciql/util/StatementLogger.java
rename to src/com/iciql/util/IciqlLogger.java
index b11a5f9..d8005bb 100644
--- a/src/com/iciql/util/StatementLogger.java
+++ b/src/com/iciql/util/IciqlLogger.java
@@ -17,6 +17,7 @@
 package com.iciql.util;
 
 import java.text.DecimalFormat;
+import java.text.MessageFormat;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -25,34 +26,34 @@ import java.util.concurrent.atomic.AtomicLong;
 import com.iciql.IciqlException;
 
 /**
- * Utility class to optionally log generated statements to StatementListeners.
+ * Utility class to optionally log generated statements to IciqlListeners.
* Statement logging is disabled by default. *

* This class also tracks the counts for generated statements by major type. * */ -public class StatementLogger { +public class IciqlLogger { /** * Enumeration of the different statement types that are logged. */ public enum StatementType { - STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT, DROP; + STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT, DROP, WARN; } /** - * Interface that defines a statement listener. + * Interface that defines an iciql listener. */ - public interface StatementListener { - void logStatement(StatementType type, String statement); + public interface IciqlListener { + void logIciql(StatementType type, String statement); } private static final ExecutorService EXEC = Executors.newSingleThreadExecutor(); - private static final Set LISTENERS = Utils.newHashSet(); - private static final StatementListener CONSOLE = new StatementListener() { + private static final Set LISTENERS = Utils.newHashSet(); + private static final IciqlListener CONSOLE = new IciqlListener() { @Override - public void logStatement(StatementType type, String message) { + public void logIciql(StatementType type, String message) { System.out.println(message); } }; @@ -64,6 +65,7 @@ public class StatementLogger { private static final AtomicLong MERGE_COUNT = new AtomicLong(); private static final AtomicLong DELETE_COUNT = new AtomicLong(); private static final AtomicLong DROP_COUNT = new AtomicLong(); + private static final AtomicLong WARN_COUNT = new AtomicLong(); /** * Activates the Console Logger. @@ -84,7 +86,7 @@ public class StatementLogger { * * @param listener */ - public static void registerListener(StatementListener listener) { + public static void registerListener(IciqlListener listener) { LISTENERS.add(listener); } @@ -93,9 +95,9 @@ public class StatementLogger { * * @param listener */ - public static void unregisterListener(StatementListener listener) { + public static void unregisterListener(IciqlListener listener) { if (!LISTENERS.remove(listener)) { - throw new IciqlException("Failed to remove statement listener {0}", listener); + throw new IciqlException("Failed to remove iciql listener {0}", listener); } } @@ -134,11 +136,16 @@ public class StatementLogger { logStatement(StatementType.DROP, statement); } + public static void warn(String message, Object... args) { + WARN_COUNT.incrementAndGet(); + logStatement(StatementType.WARN, args.length > 0 ? MessageFormat.format(message, args) : message); + } + private static void logStatement(final StatementType type, final String statement) { - for (final StatementListener listener : LISTENERS) { + for (final IciqlListener listener : LISTENERS) { EXEC.execute(new Runnable() { public void run() { - listener.logStatement(type, statement); + listener.logIciql(type, statement); } }); } @@ -172,6 +179,10 @@ public class StatementLogger { return DROP_COUNT.longValue(); } + public static long getWarnCount() { + return WARN_COUNT.longValue(); + } + public static long getTotalCount() { return getCreateCount() + getInsertCount() + getUpdateCount() + getDeleteCount() + getMergeCount() + getSelectCount() + getDropCount(); @@ -180,6 +191,8 @@ public class StatementLogger { public static void logStats() { logStatement(StatementType.STAT, "iciql Runtime Statistics"); logStatement(StatementType.STAT, "========================"); + logStat(StatementType.WARN, getWarnCount()); + logStatement(StatementType.STAT, "========================"); logStat(StatementType.CREATE, getCreateCount()); logStat(StatementType.INSERT, getInsertCount()); logStat(StatementType.UPDATE, getUpdateCount()); diff --git a/src/com/iciql/util/Slf4jStatementListener.java b/src/com/iciql/util/Slf4jIciqlListener.java similarity index 77% rename from src/com/iciql/util/Slf4jStatementListener.java rename to src/com/iciql/util/Slf4jIciqlListener.java index f46099c..ded393f 100644 --- a/src/com/iciql/util/Slf4jStatementListener.java +++ b/src/com/iciql/util/Slf4jIciqlListener.java @@ -23,15 +23,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.iciql.Iciql; -import com.iciql.util.StatementLogger.StatementListener; -import com.iciql.util.StatementLogger.StatementType; +import com.iciql.util.IciqlLogger.IciqlListener; +import com.iciql.util.IciqlLogger.StatementType; /** - * Slf4jStatementListener interfaces the iciql statement logger to the SLF4J - * logging architecture. - * + * Slf4jIciqlListener interfaces the IciqlLogger to the SLF4J logging framework. */ -public class Slf4jStatementListener implements StatementListener { +public class Slf4jIciqlListener implements IciqlListener { private Logger logger = LoggerFactory.getLogger(Iciql.class); @@ -46,11 +44,11 @@ public class Slf4jStatementListener implements StatementListener { private final Map levels; - public Slf4jStatementListener() { + public Slf4jIciqlListener() { this(Level.TRACE); } - public Slf4jStatementListener(Level defaultLevel) { + public Slf4jIciqlListener(Level defaultLevel) { this.defaultLevel = defaultLevel; levels = new HashMap(); for (StatementType type : StatementType.values()) { @@ -69,7 +67,7 @@ public class Slf4jStatementListener implements StatementListener { } @Override - public void logStatement(StatementType type, String statement) { + public void logIciql(StatementType type, String statement) { Level level = levels.get(type); switch (level) { case ERROR: diff --git a/tests/com/iciql/test/AnnotationsTest.java b/tests/com/iciql/test/AnnotationsTest.java index 2fdbcf4..30e46bb 100644 --- a/tests/com/iciql/test/AnnotationsTest.java +++ b/tests/com/iciql/test/AnnotationsTest.java @@ -85,6 +85,9 @@ public class AnnotationsTest { } private String prepName(String name, boolean upper, boolean lower) { + if (name == null) { + return null; + } if (upper) { return name.toUpperCase(); } else if (lower) { diff --git a/tests/com/iciql/test/IciqlSuite.java b/tests/com/iciql/test/IciqlSuite.java index d79f277..d68e146 100644 --- a/tests/com/iciql/test/IciqlSuite.java +++ b/tests/com/iciql/test/IciqlSuite.java @@ -15,17 +15,23 @@ */ package com.iciql.test; +import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.sql.SQLException; +import java.text.DecimalFormat; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Map; +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDataSource; +import org.apache.commons.pool.impl.GenericObjectPool; import org.junit.Assert; import org.junit.runner.JUnitCore; import org.junit.runner.Result; @@ -54,10 +60,11 @@ import com.iciql.test.models.ProductAnnotationOnly; import com.iciql.test.models.ProductInheritedAnnotation; import com.iciql.test.models.ProductMixedAnnotation; import com.iciql.test.models.SupportedTypes; -import com.iciql.util.StatementLogger; -import com.iciql.util.StatementLogger.StatementListener; -import com.iciql.util.StatementLogger.StatementType; +import com.iciql.util.IciqlLogger; +import com.iciql.util.IciqlLogger.IciqlListener; +import com.iciql.util.IciqlLogger.StatementType; import com.iciql.util.StringUtils; +import com.iciql.util.Utils; /** * JUnit 4 iciql test suite. @@ -66,7 +73,11 @@ import com.iciql.util.StringUtils; * this by switching the DEFAULT_TEST_DB value. *

* Alternatively, you can run this class an application which will run all tests - * for all tested databases. + * for all tested database configurations. + *

+ * NOTE: If you want to test against MySQL or PostgreSQL you must create an + * "iciql" database and allow user "sa" password "sa" complete control of that + * database. * */ @RunWith(Suite.class) @@ -75,29 +86,35 @@ import com.iciql.util.StringUtils; RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, UUIDTest.class }) public class IciqlSuite { - private static final TestDb[] TEST_DBS = { new TestDb("H2 (embedded)", "jdbc:h2:mem:db{0,number,000}"), - new TestDb("HSQL (embedded)", "jdbc:hsqldb:mem:db{0,number,000}"), - new TestDb("Derby (embedded)", "jdbc:derby:memory:db{0,number,000};create=true"), - new TestDb("MySQL (tcp/myisam)", "jdbc:mysql://localhost:3306/iciql"), - new TestDb("PostgreSQL (tcp)", "jdbc:postgresql://localhost:5432/iciql")}; + private static final TestDb[] TEST_DBS = { + new TestDb("H2", true, true, "jdbc:h2:mem:iciql"), + new TestDb("H2", true, false, "jdbc:h2:file:testdbs/h2/iciql"), + new TestDb("HSQL", true, true, "jdbc:hsqldb:mem:iciql"), + new TestDb("HSQL", true, false, "jdbc:hsqldb:file:testdbs/hsql/iciql"), + new TestDb("Derby", true, true, "jdbc:derby:memory:iciql;create=true"), + new TestDb("Derby", true, false, "jdbc:derby:directory:testdbs/derby/iciql;create=true"), + new TestDb("MySQL", false, false, "jdbc:mysql://localhost:3306/iciql"), + new TestDb("PostgreSQL", false, false, "jdbc:postgresql://localhost:5432/iciql") }; - private static final TestDb DEFAULT_TEST_DB = TEST_DBS[4]; + private static final TestDb DEFAULT_TEST_DB = TEST_DBS[0]; private static final PrintStream ERR = System.err; - private static AtomicInteger openCount = new AtomicInteger(0); - private static String username = "sa"; private static String password = "sa"; private static PrintStream out = System.out; + private static Map connectionFactories = Utils.newSynchronizedHashMap(); + + private static Map dataSources = Utils.newSynchronizedHashMap(); + public static void assertStartsWith(String value, String startsWith) { Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", startsWith, value), value.startsWith(startsWith)); } - + public static void assertEqualsIgnoreCase(String expected, String actual) { Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", expected, actual), expected.equalsIgnoreCase(actual)); @@ -111,17 +128,30 @@ public class IciqlSuite { } /** - * Increment the database counter, open and create a new database. + * Open a new Db object. All connections are cached and re-used to eliminate + * embedded database startup costs. * - * @return a fresh database + * @return a fresh Db object */ public static Db openNewDb() { String testUrl = System.getProperty("iciql.url"); if (testUrl == null) { testUrl = DEFAULT_TEST_DB.url; } - testUrl = MessageFormat.format(testUrl, openCount.incrementAndGet()); - Db db = Db.open(testUrl, username, password); + Db db = null; + PoolingDataSource dataSource = dataSources.get(testUrl); + if (dataSource == null) { + ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(testUrl, username, + password); + GenericObjectPool pool = new GenericObjectPool(); + pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW); + PoolableConnectionFactory factory = new PoolableConnectionFactory(connectionFactory, pool, null, + null, false, true); + dataSource = new PoolingDataSource(pool); + dataSources.put(testUrl, dataSource); + connectionFactories.put(testUrl, factory); + } + db = Db.open(dataSource); // drop tables db.dropTable(BooleanModel.class); @@ -152,19 +182,9 @@ public class IciqlSuite { if (testUrl == null) { testUrl = DEFAULT_TEST_DB.url; } - testUrl = MessageFormat.format(testUrl, openCount.get()); return Db.open(testUrl, username, password); } - /** - * Drops all tables from the current database. - * - * @return the current database - */ - public static void dropAllTables(Db db) { - - } - /** * Returns the name of the underlying database engine for the Db object. * @@ -250,6 +270,8 @@ public class IciqlSuite { System.setErr(out); } + deleteRecursively(new File("testdbs")); + // Statement logging final FileWriter statementWriter; if (StringUtils.isNullOrEmpty(params.sqlStatementsFile)) { @@ -257,9 +279,9 @@ public class IciqlSuite { } else { statementWriter = new FileWriter(params.sqlStatementsFile); } - StatementListener statementListener = new StatementListener() { + IciqlListener statementListener = new IciqlListener() { @Override - public void logStatement(StatementType type, String statement) { + public void logIciql(StatementType type, String statement) { if (statementWriter == null) { return; } @@ -271,7 +293,7 @@ public class IciqlSuite { } } }; - StatementLogger.registerListener(statementListener); + IciqlLogger.registerListener(statementListener); SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class); long quickestDatabase = Long.MAX_VALUE; @@ -280,7 +302,7 @@ public class IciqlSuite { // Header out.println(dividerMajor); - out.println(MessageFormat.format("{0} {1} ({2}) testing {3} databases", Constants.NAME, + out.println(MessageFormat.format("{0} {1} ({2}) testing {3} database configurations", Constants.NAME, Constants.VERSION, Constants.VERSION_DATE, TEST_DBS.length)); out.println(dividerMajor); out.println(); @@ -302,15 +324,15 @@ public class IciqlSuite { long lastCount = 0; for (TestDb testDb : TEST_DBS) { out.println(dividerMinor); - out.println("Testing " + testDb.name + " " + testDb.getVersion()); + out.println("Testing " + testDb.describeDatabase()); + out.println(" " + testDb.url); out.println(dividerMinor); // inject a database section delimiter in the statement log if (statementWriter != null) { statementWriter.append("\n\n"); statementWriter.append("# ").append(dividerMinor).append('\n'); - statementWriter.append("# ").append("Testing " + testDb.name + " " + testDb.getVersion()) - .append('\n'); + statementWriter.append("# ").append("Testing " + testDb.describeDatabase()).append('\n'); statementWriter.append("# ").append(dividerMinor).append('\n'); statementWriter.append("\n\n"); } @@ -318,6 +340,7 @@ public class IciqlSuite { if (testDb.getVersion().equals("OFFLINE")) { // Database not available out.println("Skipping. Could not find " + testDb.url); + out.println(); } else { // Test database System.setProperty("iciql.url", testDb.url); @@ -326,9 +349,9 @@ public class IciqlSuite { if (testDb.runtime < quickestDatabase) { quickestDatabase = testDb.runtime; } - testDb.statements = StatementLogger.getTotalCount() - lastCount; + testDb.statements = IciqlLogger.getTotalCount() - lastCount; // reset total count for next database - lastCount = StatementLogger.getTotalCount(); + lastCount = IciqlLogger.getTotalCount(); out.println(MessageFormat.format( "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs", @@ -356,36 +379,32 @@ public class IciqlSuite { Constants.VERSION, Constants.VERSION_DATE)); out.println(dividerMajor); List dbs = Arrays.asList(TEST_DBS); - Collections.sort(dbs, new Comparator() { + Collections.sort(dbs); - @Override - public int compare(TestDb o1, TestDb o2) { - if (o1.runtime == 0) { - return 1; - } - if (o2.runtime == 0) { - return -1; - } - if (o1.runtime == o2.runtime) { - return 0; - } - if (o1.runtime > o2.runtime) { - return 1; - } - return -1; - } - }); + out.println(MessageFormat.format("{0} {1} {2} {3} {4}", StringUtils.pad("Name", 11, " ", true), + StringUtils.pad("Type", 5, " ", true), StringUtils.pad("Version", 23, " ", true), + StringUtils.pad("Stats/Sec", 10, " ", true), "Runtime")); + out.println(dividerMinor); for (TestDb testDb : dbs) { - out.println(MessageFormat.format( - "{0} {1} {2,number,0} stats/sec {3,number,0.000} secs ({4,number,0.0}x)", - StringUtils.pad(testDb.name, 20, " ", true), - StringUtils.pad(testDb.getVersion(), 22, " ", true), ((double) testDb.statements) - / (testDb.runtime / 1000d), testDb.runtime / 1000f, ((double) testDb.runtime) + DecimalFormat df = new DecimalFormat("0.0"); + out.println(MessageFormat.format("{0} {1} {2} {3} {4} {5}s ({6,number,0.0}x)", + StringUtils.pad(testDb.name, 11, " ", true), testDb.isEmbedded ? "E" : "T", + testDb.isMemory ? "M" : "F", StringUtils.pad(testDb.getVersion(), 21, " ", true), + StringUtils.pad("" + testDb.getStatementRate(), 10, " ", false), + StringUtils.pad(df.format(testDb.getRuntime()), 8, " ", false), ((double) testDb.runtime) / quickestDatabase)); } - - // close PrintStream and restore System.err - StatementLogger.unregisterListener(statementListener); + out.println(dividerMinor); + out.println(" E = embedded connection"); + out.println(" T = tcp/ip connection"); + out.println(" M = memory database"); + out.println(" F = file/persistent database"); + + // cleanup + for (PoolableConnectionFactory factory : connectionFactories.values()) { + factory.getPool().close(); + } + IciqlLogger.unregisterListener(statementListener); out.close(); System.setErr(ERR); if (statementWriter != null) { @@ -425,21 +444,52 @@ public class IciqlSuite { return sb.toString(); } + private static void deleteRecursively(File f) { + if (f.isDirectory()) { + for (File file : f.listFiles()) { + if (file.isDirectory()) { + deleteRecursively(file); + } + file.delete(); + } + } + f.delete(); + } + /** * Represents a test database url. */ - private static class TestDb { + private static class TestDb implements Comparable { final String name; + boolean isEmbedded; + boolean isMemory; final String url; String version; long runtime; long statements; - TestDb(String name, String url) { + TestDb(String name, boolean isEmbedded, boolean isMemory, String url) { this.name = name; + this.isEmbedded = isEmbedded; + this.isMemory = isMemory; this.url = url; } + double getRuntime() { + return runtime / 1000d; + } + + int getStatementRate() { + return Double.valueOf(((double) statements) / (runtime / 1000d)).intValue(); + } + + String describeDatabase() { + StringBuilder sb = new StringBuilder(name); + sb.append(" "); + sb.append(getVersion()); + return sb.toString(); + } + String getVersion() { if (version == null) { try { @@ -453,6 +503,25 @@ public class IciqlSuite { } return version; } + + @Override + public int compareTo(TestDb o) { + if (runtime == 0) { + return 1; + } + if (o.runtime == 0) { + return -1; + } + int r1 = getStatementRate(); + int r2 = o.getStatementRate(); + if (r1 == r2) { + return 0; + } + if (r1 < r2) { + return 1; + } + return -1; + } } /** -- 2.39.5