[c3p0] 02/03: Imported Upstream version 0.9.1.2
Tony Mancill
tmancill at moszumanska.debian.org
Fri Nov 27 04:49:08 UTC 2015
This is an automated email from the git hooks/post-receive script.
tmancill pushed a commit to branch master
in repository c3p0.
commit 81475a188ee1476b15b7902799fd3391d0feeb2c
Author: tony mancill <tmancill at debian.org>
Date: Thu Nov 26 17:07:07 2015 -0800
Imported Upstream version 0.9.1.2
---
.classpath | 6 +
.project | 11 +
README-SRC | 16 +
build.properties | 90 +
build.xml | 772 ++++++
build.xml.bkup | 728 ++++++
dbms/oracle-thin/build.properties | 10 +
dbms/oracle-thin/build.xml | 68 +
.../classes/com/mchange/v2/c3p0/dbms/Debug.java | 42 +
.../com/mchange/v2/c3p0/dbms/OracleUtils.java | 122 +
relproj/build.xml | 26 +
relproj/debuggen/build.xml | 71 +
.../debuggen/src/app-rsrc/META-INF/manifest.src | 2 +
.../src/classes/com/mchange/v1/io/WriterUtils.java | 41 +
.../classes/com/mchange/v1/lang/BooleanUtils.java | 40 +
.../com/mchange/v1/util/ClosableResource.java | 40 +
.../classes/com/mchange/v1/util/IteratorUtils.java | 159 ++
.../src/classes/com/mchange/v1/util/SetUtils.java | 83 +
.../com/mchange/v1/util/StringTokenizerUtils.java | 48 +
.../src/classes/com/mchange/v1/util/UIterator.java | 42 +
.../v2/cmdline/BadCommandLineException.java | 33 +
.../com/mchange/v2/cmdline/CommandLineUtils.java | 86 +
.../mchange/v2/cmdline/MissingSwitchException.java | 38 +
.../com/mchange/v2/cmdline/ParsedCommandLine.java | 34 +
.../mchange/v2/cmdline/ParsedCommandLineImpl.java | 124 +
.../cmdline/UnexpectedSwitchArgumentException.java | 43 +
.../v2/cmdline/UnexpectedSwitchException.java | 38 +
.../com/mchange/v2/debug/DebugConstants.java | 31 +
.../src/classes/com/mchange/v2/debug/DebugGen.java | 371 +++
.../com/mchange/v2/io/DirectoryDescentUtils.java | 130 +
.../classes/com/mchange/v2/io/FileIterator.java | 48 +
relproj/debuggen/version.properties | 3 +
src/classes/com/mchange/lang/ByteUtils.java | 111 +
.../com/mchange/lang/PotentiallySecondary.java | 34 +
.../mchange/lang/PotentiallySecondaryError.java | 74 +
.../lang/PotentiallySecondaryException.java | 91 +
.../lang/PotentiallySecondaryRuntimeException.java | 74 +
src/classes/com/mchange/lang/ThrowableUtils.java | 51 +
src/classes/com/mchange/util/AssertException.java | 33 +
.../com/mchange/v1/db/sql/ConnectionUtils.java | 75 +
.../com/mchange/v1/db/sql/ResultSetUtils.java | 55 +
.../com/mchange/v1/db/sql/StatementUtils.java | 55 +
.../com/mchange/v1/identicator/IdHashKey.java | 41 +
.../com/mchange/v1/identicator/IdHashMap.java | 35 +
src/classes/com/mchange/v1/identicator/IdMap.java | 131 ++
.../com/mchange/v1/identicator/IdWeakHashMap.java | 252 ++
.../com/mchange/v1/identicator/Identicator.java | 34 +
.../mchange/v1/identicator/StrongIdHashKey.java | 52 +
.../com/mchange/v1/identicator/WeakIdHashKey.java | 85 +
.../com/mchange/v1/io/InputStreamUtils.java | 142 ++
.../com/mchange/v1/io/OutputStreamUtils.java | 48 +
.../com/mchange/v1/jvm/InternalNameUtils.java | 211 ++
.../com/mchange/v1/jvm/TypeFormatException.java | 33 +
.../v1/lang/AmbiguousClassNameException.java | 34 +
src/classes/com/mchange/v1/lang/BooleanUtils.java | 40 +
src/classes/com/mchange/v1/lang/ClassUtils.java | 217 ++
src/classes/com/mchange/v1/lang/GentleThread.java | 98 +
src/classes/com/mchange/v1/lang/NullUtils.java | 43 +
.../com/mchange/v1/util/AbstractMapEntry.java | 56 +
src/classes/com/mchange/v1/util/ArrayUtils.java | 300 +++
.../com/mchange/v1/util/ClosableResource.java | 40 +
.../com/mchange/v1/util/ClosableResourceUtils.java | 55 +
src/classes/com/mchange/v1/util/DebugUtils.java | 38 +
.../com/mchange/v1/util/SimpleMapEntry.java | 51 +
.../com/mchange/v1/util/StringTokenizerUtils.java | 48 +
.../com/mchange/v1/util/WrapperIterator.java | 115 +
src/classes/com/mchange/v1/xml/DomParseUtils.java | 179 ++
.../com/mchange/v2/async/AsynchronousRunner.java | 56 +
.../com/mchange/v2/async/CarefulRunnableQueue.java | 235 ++
src/classes/com/mchange/v2/async/Queuable.java | 29 +
.../v2/async/RoundRobinAsynchronousRunner.java | 154 ++
.../com/mchange/v2/async/RunnableQueue.java | 32 +
.../mchange/v2/async/StrandedTaskReporting.java | 42 +
.../v2/async/ThreadPerTaskAsynchronousRunner.java | 261 +++
.../v2/async/ThreadPoolAsynchronousRunner.java | 718 ++++++
...readPerTaskAsynchronousRunnerJUnitTestCase.java | 208 ++
.../ThreadPoolAsynchronousRunnerJUnitTestCase.java | 121 +
src/classes/com/mchange/v2/beans/BeansUtils.java | 493 ++++
src/classes/com/mchange/v2/beans/StateBean.java | 27 +
.../com/mchange/v2/beans/StateBeanExporter.java | 34 +
.../com/mchange/v2/beans/StateBeanImporter.java | 29 +
.../v2/c3p0/AbstractConnectionCustomizer.java | 51 +
.../mchange/v2/c3p0/AbstractConnectionTester.java | 83 +
.../com/mchange/v2/c3p0/C3P0ProxyConnection.java | 83 +
.../com/mchange/v2/c3p0/C3P0ProxyStatement.java | 84 +
src/classes/com/mchange/v2/c3p0/C3P0Registry.java | 339 +++
.../com/mchange/v2/c3p0/ComboPooledDataSource.java | 684 ++++++
.../com/mchange/v2/c3p0/ConnectionCustomizer.java | 89 +
.../com/mchange/v2/c3p0/ConnectionTester.java | 65 +
src/classes/com/mchange/v2/c3p0/DataSources.java | 368 +++
.../mchange/v2/c3p0/DriverManagerDataSource.java | 254 ++
.../v2/c3p0/DriverManagerDataSourceFactory.java | 154 ++
.../mchange/v2/c3p0/FullQueryConnectionTester.java | 32 +
.../v2/c3p0/JndiRefConnectionPoolDataSource.java | 311 +++
.../v2/c3p0/JndiRefForwardingDataSource.java | 169 ++
.../com/mchange/v2/c3p0/PoolBackedDataSource.java | 39 +
.../v2/c3p0/PoolBackedDataSourceFactory.java | 670 ++++++
src/classes/com/mchange/v2/c3p0/PoolConfig.java | 635 +++++
.../com/mchange/v2/c3p0/PooledDataSource.java | 283 +++
.../com/mchange/v2/c3p0/QueryConnectionTester.java | 32 +
src/classes/com/mchange/v2/c3p0/SQLWarnings.java | 48 +
.../mchange/v2/c3p0/UnifiedConnectionTester.java | 69 +
.../v2/c3p0/WrapperConnectionPoolDataSource.java | 286 +++
.../com/mchange/v2/c3p0/cfg/C3P0Config.java | 329 +++
.../com/mchange/v2/c3p0/cfg/C3P0ConfigFinder.java | 31 +
.../com/mchange/v2/c3p0/cfg/C3P0ConfigUtils.java | 160 ++
.../mchange/v2/c3p0/cfg/C3P0ConfigXmlUtils.java | 232 ++
.../v2/c3p0/cfg/DefaultC3P0ConfigFinder.java | 88 +
.../com/mchange/v2/c3p0/cfg/NamedScope.java | 46 +
.../c3p0/codegen/BeangenDataSourceGenerator.java | 230 ++
.../v2/c3p0/codegen/JdbcProxyGenerator.java | 1018 ++++++++
.../mchange/v2/c3p0/filter/FilterDataSource.java | 73 +
.../v2/c3p0/impl/AbstractC3P0PooledConnection.java | 35 +
.../v2/c3p0/impl/AbstractIdentityTokenized.java | 47 +
.../v2/c3p0/impl/AbstractPoolBackedDataSource.java | 500 ++++
.../v2/c3p0/impl/AuthMaskingProperties.java | 67 +
.../com/mchange/v2/c3p0/impl/C3P0Defaults.java | 207 ++
.../com/mchange/v2/c3p0/impl/C3P0ImplUtils.java | 381 +++
.../v2/c3p0/impl/C3P0JavaBeanObjectFactory.java | 57 +
.../mchange/v2/c3p0/impl/C3P0PooledConnection.java | 1179 ++++++++++
.../v2/c3p0/impl/C3P0PooledConnectionPool.java | 921 ++++++++
.../c3p0/impl/C3P0PooledConnectionPoolManager.java | 1091 +++++++++
src/classes/com/mchange/v2/c3p0/impl/DbAuth.java | 98 +
.../v2/c3p0/impl/DefaultConnectionTester.java | 237 ++
.../v2/c3p0/impl/IdentityTokenResolvable.java | 60 +
.../mchange/v2/c3p0/impl/IdentityTokenized.java | 30 +
.../impl/IdentityTokenizedCoalesceChecker.java | 54 +
.../v2/c3p0/impl/InternalPooledConnection.java | 34 +
.../mchange/v2/c3p0/impl/NewPooledConnection.java | 740 ++++++
.../impl/NullStatementSetManagedResultSet.java | 47 +
.../v2/c3p0/impl/SetManagedDatabaseMetaData.java | 136 ++
.../mchange/v2/c3p0/impl/SetManagedResultSet.java | 60 +
.../v2/c3p0/impl/SnatchFromSetResultSet.java | 49 +
.../v2/c3p0/jboss/C3P0PooledDataSource.java | 500 ++++
.../v2/c3p0/jboss/C3P0PooledDataSourceMBean.java | 184 ++
.../management/ActiveManagementCoordinator.java | 151 ++
.../v2/c3p0/management/C3P0RegistryManager.java | 77 +
.../c3p0/management/C3P0RegistryManagerMBean.java | 46 +
.../DynamicPooledDataSourceManagerMBean.java | 619 +++++
.../v2/c3p0/management/ManagementCoordinator.java | 35 +
.../c3p0/management/NullManagementCoordinator.java | 42 +
.../c3p0/management/PooledDataSourceManager.java | 126 +
.../management/PooledDataSourceManagerMBean.java | 61 +
.../v2/c3p0/mbean/C3P0PooledDataSource.java | 433 ++++
.../v2/c3p0/mbean/C3P0PooledDataSourceMBean.java | 190 ++
.../v2/c3p0/stmt/DoubleMaxStatementCache.java | 73 +
.../v2/c3p0/stmt/GlobalMaxOnlyStatementCache.java | 57 +
.../mchange/v2/c3p0/stmt/GooGooStatementCache.java | 801 +++++++
.../stmt/MemoryCoalescedStatementCacheKey.java | 165 ++
.../stmt/PerConnectionMaxOnlyStatementCache.java | 57 +
.../v2/c3p0/stmt/SimpleStatementCacheKey.java | 155 ++
.../com/mchange/v2/c3p0/stmt/StatementCache.java | 49 +
.../v2/c3p0/stmt/StatementCacheBenchmark.java | 175 ++
.../mchange/v2/c3p0/stmt/StatementCacheKey.java | 180 ++
.../c3p0/stmt/ValueIdentityStatementCacheKey.java | 202 ++
.../mchange/v2/c3p0/subst/C3P0Substitutions.java | 35 +
.../v2/c3p0/test/AlwaysFailConnectionTester.java | 64 +
.../com/mchange/v2/c3p0/test/C3P0BenchmarkApp.java | 732 ++++++
.../v2/c3p0/test/ConnectionDispersionTest.java | 233 ++
.../test/FreezableDriverManagerDataSource.java | 283 +++
.../com/mchange/v2/c3p0/test/JavaBeanRefTest.java | 51 +
.../com/mchange/v2/c3p0/test/JndiBindTest.java | 101 +
.../com/mchange/v2/c3p0/test/JndiLookupTest.java | 75 +
.../com/mchange/v2/c3p0/test/ListTablesTest.java | 59 +
.../v2/c3p0/test/LoadPoolBackedDataSource.java | 244 ++
.../test/OneThreadRepeatedInsertOrQueryTest.java | 163 ++
.../v2/c3p0/test/PSLoadPoolBackedDataSource.java | 226 ++
.../mchange/v2/c3p0/test/ProxyWrappersTest.java | 73 +
.../mchange/v2/c3p0/test/RawConnectionOpTest.java | 108 +
.../com/mchange/v2/c3p0/test/StatsTest.java | 112 +
.../v2/c3p0/test/TestConnectionCustomizer.java | 42 +
.../com/mchange/v2/c3p0/test/TestRefSerStuff.java | 185 ++
.../v2/c3p0/test/junit/C3P0JUnitTestCaseBase.java | 62 +
.../ConnectionPropertiesResetJUnitTestCase.java | 106 +
...MarshallUnmarshallDataSourcesJUnitTestCase.java | 111 +
.../c3p0/util/CloseReportingConnectionWrapper.java | 40 +
.../v2/c3p0/util/ConnectionEventSupport.java | 76 +
.../com/mchange/v2/c3p0/util/TestUtils.java | 182 ++
.../mchange/v2/cfg/BasicMultiPropertiesConfig.java | 286 +++
.../v2/cfg/CombinedMultiPropertiesConfig.java | 102 +
.../com/mchange/v2/cfg/MultiPropertiesConfig.java | 132 ++
.../BasicMultiPropertiesConfigJUnitTestCase.java | 56 +
.../v2/coalesce/AbstractStrongCoalescer.java | 54 +
.../mchange/v2/coalesce/AbstractWeakCoalescer.java | 61 +
.../com/mchange/v2/coalesce/CoalesceChecker.java | 40 +
.../mchange/v2/coalesce/CoalesceIdenticator.java | 40 +
src/classes/com/mchange/v2/coalesce/Coalescer.java | 33 +
.../com/mchange/v2/coalesce/CoalescerFactory.java | 98 +
.../com/mchange/v2/coalesce/CoalescerIterator.java | 43 +
.../com/mchange/v2/coalesce/StrongCcCoalescer.java | 37 +
.../mchange/v2/coalesce/StrongEqualsCoalescer.java | 37 +
.../com/mchange/v2/coalesce/SyncedCoalescer.java | 43 +
.../com/mchange/v2/coalesce/WeakCcCoalescer.java | 37 +
.../mchange/v2/coalesce/WeakEqualsCoalescer.java | 36 +
.../com/mchange/v2/codegen/CodegenUtils.java | 175 ++
.../com/mchange/v2/codegen/IndentedWriter.java | 35 +
.../bean/BeanExtractingGeneratorExtension.java | 121 +
.../com/mchange/v2/codegen/bean/BeangenUtils.java | 247 ++
.../com/mchange/v2/codegen/bean/ClassInfo.java | 35 +
.../v2/codegen/bean/CloneableExtension.java | 122 +
.../CompleteConstructorGeneratorExtension.java | 67 +
.../bean/CopyConstructorGeneratorExtension.java | 73 +
...plicitDefaultConstructorGeneratorExtension.java | 48 +
...ExplicitPropsConstructorGeneratorExtension.java | 120 +
.../v2/codegen/bean/GeneratorExtension.java | 42 +
.../bean/IndirectingSerializableExtension.java | 153 ++
.../bean/InnerBeanPropertyBeanGenerator.java | 200 ++
.../codegen/bean/ParsedPropertyBeanDocument.java | 180 ++
.../com/mchange/v2/codegen/bean/Property.java | 40 +
.../v2/codegen/bean/PropertyBeanGenerator.java | 31 +
.../v2/codegen/bean/PropertyComparator.java | 35 +
.../PropertyMapConstructorGeneratorExtension.java | 95 +
.../bean/PropertyReferenceableExtension.java | 112 +
.../bean/PropsToStringGeneratorExtension.java | 89 +
.../mchange/v2/codegen/bean/ResolvedClassInfo.java | 30 +
.../mchange/v2/codegen/bean/ResolvedProperty.java | 29 +
.../v2/codegen/bean/SerializableExtension.java | 201 ++
.../mchange/v2/codegen/bean/SimpleClassInfo.java | 62 +
.../mchange/v2/codegen/bean/SimpleProperty.java | 94 +
.../codegen/bean/SimplePropertyBeanGenerator.java | 610 +++++
.../v2/codegen/bean/SimplePropertyMask.java | 64 +
...pleStateBeanImportExportGeneratorExtension.java | 108 +
.../mchange/v2/codegen/bean/WrapperClassInfo.java | 40 +
.../mchange/v2/codegen/bean/WrapperProperty.java | 67 +
.../v2/codegen/intfc/DelegatorGenerator.java | 259 ++
.../com/mchange/v2/debug/DebugConstants.java | 31 +
.../v2/debug/ThreadNameStackTraceRecorder.java | 135 ++
.../v2/encounter/AbstractEncounterCounter.java | 57 +
.../com/mchange/v2/encounter/EncounterCounter.java | 32 +
.../v2/encounter/EqualityEncounterCounter.java | 33 +
.../ChangeNotifyingSynchronizedIntHolder.java | 98 +
.../mchange/v2/holders/SynchronizedIntHolder.java | 73 +
.../mchange/v2/holders/ThreadSafeIntHolder.java | 30 +
src/classes/com/mchange/v2/io/IndentedWriter.java | 154 ++
src/classes/com/mchange/v2/lang/Coerce.java | 138 ++
src/classes/com/mchange/v2/lang/ObjectUtils.java | 48 +
.../com/mchange/v2/lang/ThreadGroupUtils.java | 42 +
src/classes/com/mchange/v2/lang/ThreadUtils.java | 71 +
src/classes/com/mchange/v2/lang/VersionUtils.java | 182 ++
src/classes/com/mchange/v2/log/FallbackMLog.java | 357 +++
src/classes/com/mchange/v2/log/LogUtils.java | 48 +
src/classes/com/mchange/v2/log/MLevel.java | 155 ++
src/classes/com/mchange/v2/log/MLog.java | 251 ++
src/classes/com/mchange/v2/log/MLogClasses.java | 36 +
src/classes/com/mchange/v2/log/MLogger.java | 78 +
.../com/mchange/v2/log/NameTransformer.java | 41 +
src/classes/com/mchange/v2/log/PackageNames.java | 43 +
.../com/mchange/v2/log/jdk14logging/Jdk14MLog.java | 390 +++
.../com/mchange/v2/log/log4j/Log4jMLog.java | 313 +++
.../com/mchange/v2/management/ManagementUtils.java | 96 +
.../mchange/v2/naming/JavaBeanObjectFactory.java | 159 ++
.../mchange/v2/naming/JavaBeanReferenceMaker.java | 176 ++
.../com/mchange/v2/naming/ReferenceIndirector.java | 117 +
.../com/mchange/v2/naming/ReferenceMaker.java | 33 +
.../com/mchange/v2/naming/ReferenceableUtils.java | 177 ++
.../mchange/v2/resourcepool/BasicResourcePool.java | 2085 +++++++++++++++++
.../v2/resourcepool/BasicResourcePoolFactory.java | 362 +++
.../CannotAcquireResourceException.java | 39 +
.../com/mchange/v2/resourcepool/ResourcePool.java | 141 ++
.../mchange/v2/resourcepool/ResourcePoolEvent.java | 75 +
.../v2/resourcepool/ResourcePoolEventSupport.java | 129 +
.../v2/resourcepool/ResourcePoolException.java | 41 +
.../v2/resourcepool/ResourcePoolFactory.java | 189 ++
.../v2/resourcepool/ResourcePoolListener.java | 37 +
.../mchange/v2/resourcepool/ResourcePoolUtils.java | 48 +
.../mchange/v2/resourcepool/TimeoutException.java | 39 +
src/classes/com/mchange/v2/ser/IndirectPolicy.java | 39 +
.../com/mchange/v2/ser/IndirectlySerialized.java | 33 +
src/classes/com/mchange/v2/ser/Indirector.java | 29 +
.../com/mchange/v2/ser/SerializableUtils.java | 171 ++
.../v2/ser/UnsupportedVersionException.java | 35 +
src/classes/com/mchange/v2/sql/SqlUtils.java | 118 +
.../v2/sql/filter/FilterCallableStatement.java | 523 +++++
.../mchange/v2/sql/filter/FilterConnection.java | 160 ++
.../v2/sql/filter/FilterDatabaseMetaData.java | 542 +++++
.../v2/sql/filter/FilterPreparedStatement.java | 285 +++
.../com/mchange/v2/sql/filter/FilterResultSet.java | 479 ++++
.../com/mchange/v2/sql/filter/FilterStatement.java | 159 ++
.../com/mchange/v2/sql/filter/RecreatePackage.java | 97 +
.../SynchronizedFilterCallableStatement.java | 523 +++++
.../sql/filter/SynchronizedFilterConnection.java | 160 ++
.../filter/SynchronizedFilterDatabaseMetaData.java | 542 +++++
.../SynchronizedFilterPreparedStatement.java | 285 +++
.../v2/sql/filter/SynchronizedFilterResultSet.java | 479 ++++
.../v2/sql/filter/SynchronizedFilterStatement.java | 159 ++
.../v2/sql/junit/SqlUtilsJUnitTestCase.java | 48 +
.../com/mchange/v2/util/DoubleWeakHashMap.java | 577 +++++
.../mchange/v2/util/ResourceClosedException.java | 67 +
.../util/junit/DoubleWeakHashMapJUnitTestCase.java | 108 +
.../impl/DriverManagerDataSourceBase.beangen-xml | 71 +
.../v2/c3p0/impl/JndiRefDataSourceBase.beangen-xml | 53 +
.../c3p0/impl/PoolBackedDataSourceBase.beangen-xml | 56 +
...WrapperConnectionPoolDataSourceBase.beangen-xml | 252 ++
src/dist-static/CHANGELOG | 1179 ++++++++++
src/dist-static/LICENSE | 504 ++++
src/dist-static/README | 20 +
src/dist-static/TODO | 28 +
src/dist-static/examples/JndiBindDataSource.java | 80 +
src/dist-static/examples/UseJndiDataSource.java | 86 +
.../examples/UsePoolBackedDataSource.java | 83 +
.../examples/UseUnpooledDataSource.java | 81 +
src/dist-static/examples/c3p0-service.xml | 48 +
src/doc/arrow_sm.png | Bin 0 -> 375 bytes
src/doc/index.html | 2474 ++++++++++++++++++++
src/docweb/docwebapp/WEB-INF/jboss-web.xml | 9 +
src/docweb/docwebapp/WEB-INF/web.xml | 7 +
src/docweb/docwebear/META-INF/application.xml | 15 +
.../com/mchange/v2/cfg/junit/a.properties | 1 +
.../com/mchange/v2/cfg/junit/b.properties | 1 +
.../com/mchange/v2/cfg/vmConfigResourcePaths.txt | 11 +
.../mchange/v2/log/default-mchange-log.properties | 3 +
test-properties/c3p0-config.xml | 38 +
test-properties/c3p0.properties | 54 +
test-properties/log4j.properties | 17 +
test-properties/logging.properties | 66 +
version.properties | 3 +
316 files changed, 52028 insertions(+)
diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..a9fbbd5
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/classes"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..6bc67db
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>c3p0</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/README-SRC b/README-SRC
new file mode 100644
index 0000000..61828a8
--- /dev/null
+++ b/README-SRC
@@ -0,0 +1,16 @@
+Hi.
+
+Building c3p0 should be as easy as editing build.properties and typing ant.
+Please send any feedback / questions to Steve Waldman <swaldman at mchange.com>
+
+All of the test related properties are very optional. There are various
+test tasks in the build file, but they are ad-hoc and unsupported. Someday
+I plan to get with jUnit and do real testing, but that day has not yet come.
+Until then, you, my dear user, are my primary testing engine. Sorry.
+
+ Steve Waldman <swaldman at mchange.com>
+ Machinery For Change, Inc.
+
+
+
+
diff --git a/build.properties b/build.properties
new file mode 100644
index 0000000..1caa5bc
--- /dev/null
+++ b/build.properties
@@ -0,0 +1,90 @@
+# >> BASICS <<
+
+#
+# You'll need to supply at least one of j2ee.classpath
+# or j2ee.jar.file.base.dir. All jar files under
+# ${j2ee.jar.file.base.dir} or its subdirectories
+# will be added to the effective classpath of the project.
+#
+
+#j2ee.classpath=
+#j2ee.jar.base.dir=
+
+# >> DEBUGGING AND TRACING <<
+
+# Set this to true if you want logging enabled for logging levels below INFO.
+# If debug is not set, logging code for these messages will be eliminated from
+# the compiled code by virtue of "if (false) { ... }" blocks.
+
+#c3p0-build.debug=
+
+# Set trace to an integer between 0 and 10 (inclusive) to control how the level
+# of detail of debug logging messages. Only makes a difference if c3p0.debug is
+# set to true above. Default to 5 if unset.
+
+#c3p0-build.trace=
+
+# NOTE: You must still configure your logging library to log or display these
+# debug level messages if you actually want to see any change!
+
+#----------------------------------------------------------------------------
+
+# >> OPTIONAL LIBRARY SUPPRT <<
+
+#
+# You'll only need this property if you want to
+# build-in optional log4j support.
+#
+
+#log4j.jar.file=
+
+#
+# You'll only need this property if you want to
+# build the jar of utilities specific to the
+# oracle-thin jdbc driver / dbms
+#
+
+#oracle-thin.jdbc.jar.file=
+
+#----------------------------------------------------------------------------
+
+# >> OPTIONAL TEST SUPPORT
+
+#
+# this stuff is only required if you want to run
+# the various tests. very optional
+#
+
+#test.jdbc.driver.jar.file=
+#test.jdbc.drivers=
+#test.jdbc.url=
+#test.jdbc.user=
+#test.jdbc.password=
+
+#
+# required if you want to run junit tests
+#
+
+#junit.jar.file
+
+# >> VERY VERY OPTIONAL DOCS-TO-WEB SUPPORT
+
+#
+# this stuff is only required if you want to deploy
+# an ear file containing c3p0's docs to a J2EE appserver.
+# via scp. Requires an available executable "scp".
+#
+# this is a convenience for c3p0's developer, not
+# really intended for other users. just leave blank
+#
+# note that virtual.host modifies a jboss-web.xml file,
+# will do nothing if you are deploying to some other
+# app server
+#
+
+#docwebapp.context.root=
+#docwebapp.virtual.host=
+#docwebear.deploy.user=
+#docwebear.deploy.host=
+#docwebear.deploy.path=
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..cee7c21
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,772 @@
+<project name="c3p0" default="dist">
+
+ <!-- ignore the CLASSPATH environment variable. force builds to specify classpaths -->
+ <property name="build.sysclasspath" value="ignore" />
+
+ <property file="private/build.properties" />
+ <property file="build.properties" />
+ <property file="version.properties" />
+
+ <property name="c3p0.name" value="c3p0-${c3p0.version}" />
+ <property name="bindist.name" value="${c3p0.name}.bin" />
+ <property name="srcdist.name" value="${c3p0.name}.src" />
+ <property name="src.dir" value="src" />
+ <property name="empty.src.dir" value="${src.dir}/empty" />
+ <property name="java.src.dir" value="${src.dir}/classes" />
+ <property name="rsrc.src.dir" value="${src.dir}/resources" />
+ <property name="codegen.src.dir" value="${src.dir}/codegen" />
+ <property name="doc.src.dir" value="${src.dir}/doc" />
+ <property name="docwebapp.src.dir" value="${src.dir}/docweb/docwebapp" />
+ <property name="docwebear.src.dir" value="${src.dir}/docweb/docwebear" />
+ <property name="static.dist.src" value="${src.dir}/dist-static" />
+ <property name="relproj.dir" value="relproj" />
+ <property name="relproj.dist.dir" value="${relproj.dir}/dist" />
+ <property name="test.props.dir" value="test-properties" />
+ <property name="test.logging.props.file" location="${test.props.dir}/logging.properties" />
+ <property name="build.dir" value="build" />
+ <property name="build.codegen.dir" value="${build.dir}/codegen" />
+ <property name="build.classes.dir" value="${build.dir}/classes" />
+ <property name="build.apidocs.dir" value="${build.dir}/apidocs" />
+ <property name="build.docwebapp.dir" value="${build.dir}/docweb/docwebapp" />
+ <property name="build.docwebear.dir" value="${build.dir}/docweb/docwebear" />
+ <property name="build.testresults.dir" value="${build.dir}/testresults" />
+ <property name="build.jar.file" value="${build.dir}/${c3p0.name}.jar" />
+ <property name="build.jdk13.java.src.dir" value="${build.dir}/jdk13src" />
+ <property name="build.jdk13.build.dir" value="${build.dir}/jdk13build" />
+ <property name="build.jdk13.build.classes.dir" value="${build.jdk13.build.dir}/classes" />
+ <property name="build.jar.file.jdk13" value="${build.dir}/${c3p0.name}-jdk1.3.jar" />
+ <property name="dbms.dir" value="dbms" />
+ <property name="dbms.oracle.thin.antproj.dir" value="${dbms.dir}/oracle-thin" />
+ <property name="dbms.oracle.thin.antproj.dist.dir" value="${dbms.oracle.thin.antproj.dir}/dist" />
+ <property name="test.classes.dir" value="${build.dir}/testclasses" />
+ <property name="dist.dir" value="dist" />
+ <property name="license.header.file" value="src/legal.prepend" />
+ <property name="open.dist" value="${dist.dir}/${bindist.name}" />
+ <property name="open.dist.doc.dir" value="${open.dist}/doc" />
+ <property name="open.dist.lib.dir" value="${open.dist}/lib" />
+ <property name="docwebapp.war.file.name" value="docweb.war" />
+ <property name="docwebapp.war.file" value="${build.docwebear.dir}/${docwebapp.war.file.name}" />
+ <property name="docwebear.file" value="${build.dir}/c3p0-docweb.ear" />
+
+ <!-- these properties should be set externally if desired -->
+ <!-- we set them here only to keep classpaths valid -->
+ <!-- when users do not set the path -->
+ <property name="j2ee.jar.base.dir" value="${empty.src.dir}" />
+ <property name="j2ee.jar.dir" value="${empty.src.dir}" />
+
+ <!--
+ <property name="log4j.jar.file" value="" />
+ <property name="junit.jar.file" value="" />
+ -->
+
+ <!-- these properties should often be preempted in build.properties -->
+ <property name="c3p0-build.debug" value="false" />
+ <property name="c3p0-build.trace" value="5" />
+
+ <property name="c3p0.target.version" value="1.4" />
+
+ <path id="codegen-classpath">
+ <pathelement location="${build.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ </path>
+
+ <property name="codegen.classpath" refid="codegen-classpath" />
+
+ <path id="build-classpath">
+ <pathelement location="${build.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ </path>
+
+ <patternset id="common-excludes">
+ <exclude name="**/old/**" />
+ <exclude name="**/bad/**" />
+ <exclude name="**/off/**" />
+ <exclude name="**/private/**" />
+ </patternset>
+
+ <patternset id="init-codegen-classes">
+ <include name="com/mchange/v2/c3p0/codegen/**/*.java" />
+ <include name="com/mchange/v2/codegen/**/*.java" />
+ <include name="com/mchange/v2/log/*.java" />
+ <include name="com/mchange/v2/cfg/*.java" />
+ <include name="com/mchange/v2/io/IndentedWriter.java" />
+ <include name="com/mchange/v1/util/StringTokenizerUtils.java" />
+ <patternset refid="common-excludes" />
+ </patternset>
+
+ <patternset id="dist-jar-classes">
+ <!-- excludes stuff only used by the code generator and by tests-->
+ <exclude name="com/mchange/v2/codegen/bean/*.java" />
+ <exclude name="com/mchange/v2/codegen/*.java" />
+ <exclude name="com/mchange/v2/debug/DebugGen.java" />
+ <exclude name="com/mchange/v1/lang/GentleThread.java" />
+ <exclude name="com/mchange/v1/lang/NullUtils.java" />
+ <exclude name="com/mchange/v1/lang/Synchronizer.java" />
+ <exclude name="com/mchange/v1/lang/TVLUtils.java" />
+ <exclude name="com/mchange/v1/jvm/**" />
+ <exclude name="com/mchange/v1/lang/holders/**" />
+ <exclude name="com/mchange/v2/c3p0/codegen/**" />
+ <exclude name="com/mchange/v2/c3p0/test/**" />
+ <exclude name="**/junit/**" />
+ <exclude name="**/*JUnitTestCase.*" />
+ <patternset refid="common-excludes" />
+ </patternset>
+
+ <patternset id="test-only-classes">
+ <include name="com/mchange/v2/c3p0/test/**" />
+ <include name="**/junit/**" />
+ <include name="**/*JUnitTestCase.class" />
+ </patternset>
+
+ <path id="test-classpath">
+ <pathelement location="${test.props.dir}" />
+ <pathelement location="${build.jar.file}" />
+ <pathelement location="${test.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ <pathelement location="${test.jdbc.driver.jar.file}" />
+ <pathelement location="${log4j.jar.file}" />
+ </path>
+
+ <target name="init">
+ <tstamp>
+ <!-- <format property="c3p0.timestamp" pattern="dd-MMMM-yyyy HH:mm:ss Z"/> -->
+
+ <!-- jdk 1.3 compatible -->
+ <format property="c3p0.timestamp" pattern="dd-MMMM-yyyy HH:mm:ss"/>
+ </tstamp>
+
+ <mkdir dir="${build.dir}" />
+ <mkdir dir="${build.codegen.dir}" />
+ <mkdir dir="${build.classes.dir}" />
+ <mkdir dir="${build.apidocs.dir}" />
+ <mkdir dir="${dist.dir}" />
+ </target>
+
+ <target name="clean" depends="dbms-oracle-thin-clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${dist.dir}" />
+ </target>
+
+ <target name="relproj" depends="init" unless="up-to-date-relproj">
+ <ant dir="${relproj.dir}" target="dist" inheritAll="false" />
+ </target>
+
+ <target name="init-debuggen" depends="relproj">
+ <uptodate property="up-to-date-debugs"
+ srcfile="build.properties"
+ targetfile="${build.codegen.dir}/com/mchange/Debug.java" />
+ </target>
+
+ <target name="debuggen" depends="init-debuggen" unless="up-to-date-debugs">
+ <java classname="com.mchange.v2.debug.DebugGen" fork="true" dir=".">
+ <sysproperty key="com.mchange.v2.log.MLog" value="com.mchange.v2.log.FallbackMLog" />
+ <classpath>
+ <fileset dir="${relproj.dist.dir}">
+ <include name="*.jar" />
+ </fileset>
+ </classpath>
+ <arg value="--packages=com.mchange" />
+ <arg value="--codebase=src/classes" />
+ <arg value="--outputbase=${build.codegen.dir}" />
+ <arg value="--recursive" />
+ <arg value="--debug=${c3p0-build.debug}" />
+ <arg value="--trace=${c3p0-build.trace}" />
+ </java>
+ </target>
+
+ <target name="subst">
+ <copy todir="${build.codegen.dir}">
+ <fileset dir="${java.src.dir}">
+ <include name="**/subst/**" />
+ </fileset>
+ <filterchain>
+ <replacetokens>
+ <token key="c3p0.version" value="${c3p0.version}"/>
+ <token key="c3p0.debug" value="${c3p0-build.debug}"/>
+ <token key="c3p0.trace" value="${c3p0-build.trace}"/>
+ <token key="c3p0.timestamp" value="${c3p0.timestamp}"/>
+
+ <!-- NO LONGER USED THIS WAY junit test stuff only
+ <token key="test.jdbc.drivers" value="${test.jdbc.drivers}" />
+ <token key="test.jdbc.url" value="${test.jdbc.url}" />
+ <token key="test.jdbc.user" value="${test.jdbc.user}" />
+ <token key="test.jdbc.password" value="${test.jdbc.password}" />
+ -->
+ </replacetokens>
+ </filterchain>
+ </copy>
+ </target>
+
+ <target name="init-codegen" depends="debuggen,subst">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="codegen-classpath"
+ debug="true">
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <patternset refid="init-codegen-classes" />
+ </javac>
+
+ <uptodate property="up-to-date-proxies"
+ srcfile="${java.src.dir}/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java"
+ targetfile="${build.codegen.dir}/com/mchange/v2/c3p0/impl/NewProxyConnection.java" />
+ </target>
+
+
+ <target name="beangen" depends="init-codegen">
+ <echo message="Some warnings are expected here. Don't worry about them." />
+ <apply executable="java" dest="${build.codegen.dir}">
+ <arg value="-Dcom.mchange.v2.log.MLog=com.mchange.v2.log.FallbackMLog" />
+ <arg value="-classpath" />
+ <arg path="${codegen.classpath}" />
+ <arg value="com.mchange.v2.c3p0.codegen.BeangenDataSourceGenerator" />
+ <srcfile />
+ <targetfile />
+ <fileset dir="${codegen.src.dir}" includes="**/*.beangen-xml">
+ <patternset refid="common-excludes" />
+ </fileset>
+ <mapper type="glob" from="*.beangen-xml" to="*.java" />
+ </apply>
+ </target>
+
+ <target name="newproxygen" depends="init-codegen" unless="up-to-date-proxies">
+ <java classname="com.mchange.v2.c3p0.codegen.JdbcProxyGenerator" fork="true" dir=".">
+ <sysproperty key="com.mchange.v2.log.MLog" value="com.mchange.v2.log.FallbackMLog" />
+ <classpath refid="codegen-classpath" />
+ <arg value="${build.codegen.dir}" />
+ </java>
+ </target>
+
+ <target name="codegen" depends="beangen,newproxygen" />
+
+ <target name="compile-common" depends="codegen">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="build-classpath"
+ debug="on">
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <exclude name="**/junit/**" />
+ <exclude name="**/*JUnitTestCase.*" />
+ <exclude name="**/subst/**" />
+ <exclude name="com/mchange/v2/log/log4j/**" />
+ <patternset refid="common-excludes" />
+ </javac>
+ </target>
+
+ <target name="compile-subst" depends="codegen">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="build-classpath"
+ debug="on">
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ </src>
+ <include name="**/subst/**" />
+ </javac>
+ </target>
+
+ <target name="compile-log4j" depends="init" if="log4j.jar.file">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ debug="on">
+ <classpath>
+ <path refid="build-classpath" />
+ <pathelement location="${log4j.jar.file}" />
+ </classpath>
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <include name="com/mchange/v2/log/log4j/**" />
+ </javac>
+ </target>
+
+ <target name="compile-junit" depends="init" if="junit.jar.file">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ debug="on">
+ <classpath>
+ <path refid="build-classpath" />
+ <pathelement path="${junit.jar.file}" />
+ </classpath>
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <include name="**/junit/**" />
+ <include name="**/*JUnitTestCase.*" />
+ </javac>
+ </target>
+
+ <target name="compile" depends="codegen,compile-common,compile-subst,compile-log4j,compile-junit" />
+
+ <target name="jar" depends="compile">
+ <jar destfile="${build.jar.file}">
+ <manifest>
+ <attribute name="Extension-Name" value="com.mchange.v2.c3p0" />
+ <attribute name="Specification-Vendor" value="Machinery For Change, Inc." />
+ <attribute name="Specification-Version" value="1.0" />
+ <attribute name="Implementation-Vendor-Id" value="com.mchange" />
+ <attribute name="Implementation-Vendor" value="Machinery For Change, Inc." />
+ <attribute name="Implementation-Version" value="${c3p0.version}" />
+ </manifest>
+ <fileset dir="${build.classes.dir}">
+ <patternset refid="dist-jar-classes"/>
+ </fileset>
+ <fileset dir="${rsrc.src.dir}" />
+ </jar>
+ </target>
+
+ <target name="dbms-oracle-thin-ant">
+ <ant dir="${dbms.oracle.thin.antproj.dir}" target="${subproject.target}" inheritAll="false">
+ <property name="c3p0.version" value="${c3p0.version}" />
+ <property name="c3p0.jar.file" location="${build.jar.file}" />
+ <property name="oracle-thin.jdbc.jar.file" value="${oracle-thin.jdbc.jar.file}" />
+ </ant>
+ </target>
+
+ <target name="dbms-oracle-thin-clean">
+ <antcall target="dbms-oracle-thin-ant">
+ <param name="subproject.target" value="clean" />
+ </antcall>
+ </target>
+
+ <target name="dbms-oracle-thin" depends="jar" if="oracle-thin.jdbc.jar.file">
+ <echo message="oracle-thin.jdbc.jar.file: ${oracle-thin.jdbc.jar.file}" />
+ <antcall target="dbms-oracle-thin-ant">
+ <param name="subproject.target" value="dist" />
+ </antcall>
+ </target>
+
+ <target name="test-init" depends="jar">
+ <mkdir dir="${test.classes.dir}" />
+ <copy toDir="${test.classes.dir}">
+ <fileset dir="${build.classes.dir}">
+ <patternset refid="test-only-classes"/>
+ </fileset>
+ </copy>
+ <property name="testcp" refid="test-classpath" />
+ <echo message="test-classpath: ${testcp}" />
+ </target>
+
+ <target name="stats-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.StatsTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <!-- <jvmarg value="-Xrunhprof:file=/tmp/java.hprof,doe=y,format=b" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="proxywrapper-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.ProxyWrappersTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <!-- <jvmarg value="-Xrunhprof:file=/tmp/java.hprof,doe=y,format=b" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="benchmark-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.C3P0BenchmarkApp"
+ classpathref="test-classpath"
+ fork="true">
+ <!-- <jvmarg value="-Xrunhprof:cpu=times,file=/tmp/java.hprof,doe=y,format=a" /> -->
+ <!-- <jvmarg value="-server" /> -->
+ <!-- <jvmarg value="-Xprof" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <!-- we don't use cmd line args or -Djdbc.drivers any more. we set up test datasources via config files -->
+<!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+-->
+ </java>
+ </target>
+
+ <target name="rco-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.RawConnectionOpTest"
+ classpathref="test-classpath"
+ fork="true">
+ <!-- <jvmarg value="-Xrunhprof:cpu=times,file=/tmp/java.hprof,doe=y,format=a" /> -->
+ <!-- <jvmarg value="-Xprof" /> -->
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="load-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.LoadPoolBackedDataSource"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+
+ <!-- we don't use cmd line args or -Djdbc.drivers any more. we set up test datasources via config files -->
+ <!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ -->
+ </java>
+ </target>
+
+ <target name="psload-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.PSLoadPoolBackedDataSource"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+
+
+ <!-- we don't use cmd line args or -Djdbc.drivers any more. we set up test datasources via config files -->
+ <!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ -->
+ </java>
+ </target>
+
+ <target name="dispersion-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.ConnectionDispersionTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="onethreadrepeat-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.OneThreadRepeatedInsertOrQueryTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="refser-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.TestRefSerStuff"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <jvmarg value="-ea" />
+
+ <!-- we don't use cmd line args or -Djdbc.drivers any more. we set up test datasources via config files -->
+ <!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ -->
+ </java>
+ </target>
+
+ <target name="junit-tests" depends="test-init" if="junit.jar.file">
+ <mkdir dir="${build.testresults.dir}" />
+ <junit printsummary="true" showoutput="true" haltonfailure="true">
+ <classpath refid="test-classpath" />
+ <formatter type="plain"/>
+
+ <!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="c3p0.test.jdbc.url" value="${test.jdbc.url}" />
+ <sysproperty key="c3p0.test.jdbc.user" value="${test.jdbc.user}" />
+ <sysproperty key="c3p0.test.jdbc.password" value="${test.jdbc.password}" />
+ -->
+
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <batchtest fork="yes" toDir="${build.testresults.dir}">
+ <fileset dir="${build.classes.dir}">
+ <include name="**/*JUnitTestCase.class"/>
+ </fileset>
+ </batchtest>
+ </junit>
+
+ <!--
+ <junit printsummary="withOutAndErr" >
+ <test name="com.mchange.v2.c3p0.test.junit.MiscellaneousTopLevelJUnitTestCase"/>
+ <classpath refid="test-classpath" />
+ <formatter type="plain"/>
+ </junit>
+ -->
+ </target>
+
+ <target name="javadocs" depends="init">
+ <javadoc packagenames="com.mchange.v2.c3p0"
+ sourcepath="${java.src.dir}"
+ destdir="${build.apidocs.dir}"
+ classpathref="build-classpath"
+ windowtitle="${c3p0.name} API Documentation"
+ />
+ </target>
+
+ <target name="jar-1.3" depends="jar">
+ <mkdir dir="${build.jdk13.java.src.dir}" />
+ <mkdir dir="${build.jdk13.build.classes.dir}" />
+
+ <!-- filter away assertion lines -->
+ <!-- source is jdk1.3 compatible except for assertions -->
+ <!-- which (by adopted convention) are always one line beginning with "assert" -->
+ <!-- these lines often use the 1.4 method Thread.holdsLock() -->
+ <!-- for now, this is the only code that requires 1.4 in c3p0 -->
+ <copy toDir="${build.jdk13.java.src.dir}">
+ <fileset dir="${java.src.dir}" includes="**/*.java" />
+ <filterchain>
+ <linecontainsregexp>
+ <regexp pattern="(?m)^(?!\s*assert\b).*$"/>
+ </linecontainsregexp>
+ </filterchain>
+ </copy>
+ <antcall target="jar" inheritAll="false">
+ <param name="java.src.dir" value="${build.jdk13.java.src.dir}" />
+ <param name="build.classes.dir" value="${build.jdk13.build.classes.dir}" />
+ <param name="c3p0.target.version" value="1.3" />
+ <param name="build.jar.file" value="${build.jar.file.jdk13}" />
+ <param name="up-to-date-relproj" value="true" />
+ </antcall>
+ </target>
+
+ <target name="basic-open-dist" depends="jar, javadocs, jar-1.3">
+ <copy toDir="${open.dist}">
+ <fileset dir="${static.dist.src}">
+ <exclude name="*~" />
+ <exclude name="examples/*.class" />
+ <patternset refid="common-excludes" />
+ </fileset>
+ </copy>
+ <mkdir dir="${open.dist.doc.dir}/apidocs" />
+ <copy toDir="${open.dist.doc.dir}">
+ <fileset dir="${doc.src.dir}" excludes="*.png"/>
+ <filterchain>
+ <replacetokens>
+ <token key="c3p0.version" value="${c3p0.version}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ <copy toDir="${open.dist.doc.dir}">
+ <fileset dir="${doc.src.dir}" includes="*.png"/>
+ </copy>
+ <copy toDir="${open.dist.doc.dir}/apidocs">
+ <fileset dir="${build.apidocs.dir}" />
+ </copy>
+ <copy file="${build.jar.file}" toDir="${open.dist.lib.dir}" />
+ <copy file="${build.jar.file.jdk13}" toDir="${open.dist.lib.dir}" />
+ </target>
+
+ <target name="docwebapp" depends="basic-open-dist">
+ <mkdir dir="${build.docwebapp.dir}" />
+ <copy toDir="${build.docwebapp.dir}">
+ <fileset dir="${docwebapp.src.dir}"/>
+ <filterchain>
+ <replacetokens>
+ <token key="virtual.host" value="${docwebapp.virtual.host}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ </target>
+
+ <target name="docwebear" depends="docwebapp">
+ <mkdir dir="${build.docwebear.dir}" />
+ <copy toDir="${build.docwebear.dir}">
+ <fileset dir="${docwebear.src.dir}"/>
+ <filterchain>
+ <replacetokens>
+ <token key="web.uri" value="${docwebapp.war.file.name}"/>
+ <token key="context.root" value="${docwebapp.context.root}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ <jar destfile="${docwebapp.war.file}">
+ <zipfileset dir="${build.docwebapp.dir}" />
+ <zipfileset dir="${open.dist.doc.dir}" />
+ </jar>
+ <jar destfile="${docwebear.file}">
+ <zipfileset dir="${build.docwebear.dir}" />
+ </jar>
+ </target>
+
+ <target name="docwebear-deploy" depends="docwebear">
+ <exec executable="scp">
+ <arg line="${docwebear.file} ${docwebear.deploy.user}@${docwebear.deploy.host}:${docwebear.deploy.path}" />
+ </exec>
+ </target>
+
+ <target name="oracle-thin-open-dist" depends="basic-open-dist, dbms-oracle-thin" if="oracle-thin.jdbc.jar.file">
+ <copy toDir="${open.dist}">
+ <fileset dir="${dbms.oracle.thin.antproj.dist.dir}" />
+ </copy>
+ </target>
+
+ <target name="open-dist" depends="basic-open-dist, oracle-thin-open-dist" />
+
+ <target name="zip-dist" depends="open-dist">
+ <zip destfile="${dist.dir}/${bindist.name}.zip">
+ <zipfileset dir="${open.dist}" prefix="${c3p0.name}"/>
+ </zip>
+ </target>
+
+ <target name="tar-dist" depends="open-dist">
+ <tar destfile="${dist.dir}/${bindist.name}.tar">
+ <tarfileset dir="${open.dist}" prefix="${c3p0.name}"/>
+ </tar>
+ </target>
+
+ <target name="tgz-dist" depends="tar-dist">
+ <gzip zipfile="${dist.dir}/${bindist.name}.tgz" src="${dist.dir}/${bindist.name}.tar" />
+ </target>
+
+ <target name="bindist" depends="tgz-dist, zip-dist" />
+
+ <target name="dist" depends="bindist" />
+
+ <target name="srcdist" depends="init">
+ <zip destfile="${dist.dir}/${srcdist.name}.zip">
+ <zipfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <patternset refid="common-excludes" />
+ </zipfileset>
+ </zip>
+ <tar destfile="${dist.dir}/${srcdist.name}.tar">
+ <tarfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <patternset refid="common-excludes" />
+ </tarfileset>
+ </tar>
+ <gzip zipfile="${dist.dir}/${srcdist.name}.tgz" src="${dist.dir}/${srcdist.name}.tar" />
+ </target>
+
+ <target name="all" depends="dist,srcdist" />
+
+</project>
+
diff --git a/build.xml.bkup b/build.xml.bkup
new file mode 100644
index 0000000..1cf902c
--- /dev/null
+++ b/build.xml.bkup
@@ -0,0 +1,728 @@
+<project name="c3p0" default="dist">
+
+ <!-- ignore the CLASSPATH environment variable. force builds to specify classpaths -->
+ <property name="build.sysclasspath" value="ignore" />
+
+ <property file="private/build.properties" />
+ <property file="build.properties" />
+ <property file="version.properties" />
+
+ <property name="c3p0.name" value="c3p0-${c3p0.version}" />
+ <property name="bindist.name" value="${c3p0.name}.bin" />
+ <property name="srcdist.name" value="${c3p0.name}.src" />
+ <property name="src.dir" value="src" />
+ <property name="empty.src.dir" value="${src.dir}/empty" />
+ <property name="java.src.dir" value="${src.dir}/classes" />
+ <property name="rsrc.src.dir" value="${src.dir}/resources" />
+ <property name="codegen.src.dir" value="${src.dir}/codegen" />
+ <property name="doc.src.dir" value="${src.dir}/doc" />
+ <property name="docwebapp.src.dir" value="${src.dir}/docweb/docwebapp" />
+ <property name="docwebear.src.dir" value="${src.dir}/docweb/docwebear" />
+ <property name="static.dist.src" value="${src.dir}/dist-static" />
+ <property name="relproj.dir" value="relproj" />
+ <property name="relproj.dist.dir" value="${relproj.dir}/dist" />
+ <property name="test.props.dir" value="test-properties" />
+ <property name="test.logging.props.file" location="${test.props.dir}/logging.properties" />
+ <property name="build.dir" value="build" />
+ <property name="build.codegen.dir" value="${build.dir}/codegen" />
+ <property name="build.classes.dir" value="${build.dir}/classes" />
+ <property name="build.apidocs.dir" value="${build.dir}/apidocs" />
+ <property name="build.docwebapp.dir" value="${build.dir}/docweb/docwebapp" />
+ <property name="build.docwebear.dir" value="${build.dir}/docweb/docwebear" />
+ <property name="build.testresults.dir" value="${build.dir}/testresults" />
+ <property name="dbms.dir" value="dbms" />
+ <property name="dbms.oracle.thin.antproj.dir" value="${dbms.dir}/oracle-thin" />
+ <property name="dbms.oracle.thin.antproj.dist.dir" value="${dbms.oracle.thin.antproj.dir}/dist" />
+ <property name="test.classes.dir" value="${build.dir}/testclasses" />
+ <property name="dist.dir" value="dist" />
+ <property name="license.header.file" value="src/legal.prepend" />
+ <property name="build.jar.file" value="build/${c3p0.name}.jar" />
+ <property name="open.dist" value="${dist.dir}/${bindist.name}" />
+ <property name="open.dist.doc.dir" value="${open.dist}/doc" />
+ <property name="open.dist.lib.dir" value="${open.dist}/lib" />
+ <property name="docwebapp.war.file.name" value="docweb.war" />
+ <property name="docwebapp.war.file" value="${build.docwebear.dir}/${docwebapp.war.file.name}" />
+ <property name="docwebear.file" value="${build.dir}/c3p0-docweb.ear" />
+
+ <!-- these properties should be set externally if desired -->
+ <!-- we set them here only to keep classpaths valid -->
+ <!-- when users do not set the path -->
+ <property name="j2ee.jar.base.dir" value="${empty.src.dir}" />
+ <property name="j2ee.jar.dir" value="${empty.src.dir}" />
+
+ <!--
+ <property name="log4j.jar.file" value="" />
+ <property name="junit.jar.file" value="" />
+ -->
+
+ <!-- these properties should often be preempted in build.properties -->
+ <property name="c3p0-build.debug" value="false" />
+ <property name="c3p0-build.trace" value="5" />
+
+ <property name="c3p0.target.version" value="1.3" />
+
+ <path id="codegen-classpath">
+ <pathelement location="${build.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ </path>
+
+ <property name="codegen.classpath" refid="codegen-classpath" />
+
+ <path id="build-classpath">
+ <pathelement location="${build.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ </path>
+
+ <patternset id="init-codegen-classes">
+ <include name="com/mchange/v2/c3p0/codegen/**/*.java" />
+ <include name="com/mchange/v2/codegen/**/*.java" />
+ <include name="com/mchange/v2/log/*.java" />
+ <include name="com/mchange/v2/cfg/*.java" />
+ <include name="com/mchange/v2/io/IndentedWriter.java" />
+ <include name="com/mchange/v1/util/StringTokenizerUtils.java" />
+ <exclude name="**/bad/**" />
+ <exclude name="**/old/**" />
+ </patternset>
+
+ <patternset id="dist-jar-classes">
+ <!-- excludes stuff only used by the code generator and by tests-->
+ <exclude name="com/mchange/v2/codegen/bean/*.java" />
+ <exclude name="com/mchange/v2/codegen/*.java" />
+ <exclude name="com/mchange/v2/debug/DebugGen.java" />
+ <exclude name="com/mchange/v1/lang/ClassUtils.java" />
+ <exclude name="com/mchange/v1/lang/GentleThread.java" />
+ <exclude name="com/mchange/v1/lang/NullUtils.java" />
+ <exclude name="com/mchange/v1/lang/Synchronizer.java" />
+ <exclude name="com/mchange/v1/lang/TVLUtils.java" />
+ <exclude name="com/mchange/v1/jvm/**" />
+ <exclude name="com/mchange/v1/lang/holders/**" />
+ <exclude name="com/mchange/v2/c3p0/codegen/**" />
+ <exclude name="com/mchange/v2/c3p0/test/**" />
+ <exclude name="**/junit/**" />
+ <exclude name="**/*JUnitTestCase.*" />
+ <exclude name="**/bad/**" />
+ <exclude name="**/old/**" />
+ </patternset>
+
+ <patternset id="test-only-classes">
+ <include name="com/mchange/v2/c3p0/test/**" />
+ <include name="**/junit/**" />
+ <include name="**/*JUnitTestCase.class" />
+ </patternset>
+
+ <path id="test-classpath">
+ <pathelement location="${test.props.dir}" />
+ <pathelement location="${build.jar.file}" />
+ <pathelement location="${test.classes.dir}" />
+ <pathelement path="${j2ee.classpath}" />
+ <fileset dir="${j2ee.jar.base.dir}" includes="**/*.jar" />
+ <fileset dir="${j2ee.jar.dir}" includes="*.jar" />
+ <pathelement location="${test.jdbc.driver.jar.file}" />
+ <pathelement location="${log4j.jar.file}" />
+ </path>
+
+ <target name="init">
+ <tstamp>
+ <!-- <format property="c3p0.timestamp" pattern="dd-MMMM-yyyy HH:mm:ss Z"/> -->
+ <format property="c3p0.timestamp" pattern="dd-MMMM-yyyy HH:mm:ss"/>
+ <!-- jdk 1.3 compatible -->
+ </tstamp>
+
+ <mkdir dir="${build.dir}" />
+ <mkdir dir="${build.codegen.dir}" />
+ <mkdir dir="${build.classes.dir}" />
+ <mkdir dir="${build.apidocs.dir}" />
+ <mkdir dir="${dist.dir}" />
+ </target>
+
+ <target name="clean" depends="dbms-oracle-thin-clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${dist.dir}" />
+ </target>
+
+ <target name="relproj" depends="init">
+ <ant dir="${relproj.dir}" target="dist" inheritAll="false" />
+ </target>
+
+ <target name="init-debuggen" depends="relproj">
+ <uptodate property="up-to-date-debugs"
+ srcfile="build.properties"
+ targetfile="${build.codegen.dir}/com/mchange/Debug.java" />
+ </target>
+
+ <target name="debuggen" depends="init-debuggen" unless="up-to-date-debugs">
+ <java classname="com.mchange.v2.debug.DebugGen" fork="true" dir=".">
+ <sysproperty key="com.mchange.v2.log.MLog" value="com.mchange.v2.log.FallbackMLog" />
+ <classpath>
+ <fileset dir="${relproj.dist.dir}">
+ <include name="*.jar" />
+ </fileset>
+ </classpath>
+ <arg value="--packages=com.mchange" />
+ <arg value="--codebase=src/classes" />
+ <arg value="--outputbase=${build.codegen.dir}" />
+ <arg value="--recursive" />
+ <arg value="--debug=${c3p0-build.debug}" />
+ <arg value="--trace=${c3p0-build.trace}" />
+ </java>
+ </target>
+
+ <target name="subst">
+ <copy todir="${build.codegen.dir}">
+ <fileset dir="${java.src.dir}">
+ <include name="**/subst/**" />
+ </fileset>
+ <filterchain>
+ <replacetokens>
+ <token key="c3p0.version" value="${c3p0.version}"/>
+ <token key="c3p0.debug" value="${c3p0-build.debug}"/>
+ <token key="c3p0.trace" value="${c3p0-build.trace}"/>
+ <token key="c3p0.timestamp" value="${c3p0.timestamp}"/>
+
+ <!-- NO LONGER USED THIS WAY junit test stuff only
+ <token key="test.jdbc.drivers" value="${test.jdbc.drivers}" />
+ <token key="test.jdbc.url" value="${test.jdbc.url}" />
+ <token key="test.jdbc.user" value="${test.jdbc.user}" />
+ <token key="test.jdbc.password" value="${test.jdbc.password}" />
+ -->
+ </replacetokens>
+ </filterchain>
+ </copy>
+ </target>
+
+ <target name="init-codegen" depends="debuggen,subst">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="codegen-classpath"
+ debug="true">
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <patternset refid="init-codegen-classes" />
+ </javac>
+
+ <uptodate property="up-to-date-proxies"
+ srcfile="${java.src.dir}/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java"
+ targetfile="${build.codegen.dir}/com/mchange/v2/c3p0/impl/NewProxyConnection.java" />
+ </target>
+
+
+ <target name="beangen" depends="init-codegen">
+ <apply executable="java" dest="${build.codegen.dir}">
+ <arg value="-Dcom.mchange.v2.log.MLog=com.mchange.v2.log.FallbackMLog" />
+ <arg value="-classpath" />
+ <arg path="${codegen.classpath}" />
+ <arg value="com.mchange.v2.c3p0.codegen.BeangenDataSourceGenerator" />
+ <srcfile />
+ <targetfile />
+ <fileset dir="${codegen.src.dir}" includes="**/*.beangen-xml">
+ <exclude name="**/bad/**" />
+ <exclude name="**/old/**" />
+ <exclude name="private/**" />
+ </fileset>
+ <mapper type="glob" from="*.beangen-xml" to="*.java" />
+ </apply>
+ </target>
+
+ <target name="newproxygen" depends="init-codegen" unless="up-to-date-proxies">
+ <java classname="com.mchange.v2.c3p0.codegen.JdbcProxyGenerator" fork="true" dir=".">
+ <sysproperty key="com.mchange.v2.log.MLog" value="com.mchange.v2.log.FallbackMLog" />
+ <classpath refid="codegen-classpath" />
+ <arg value="${build.codegen.dir}" />
+ </java>
+ </target>
+
+ <target name="codegen" depends="beangen,newproxygen" />
+
+ <target name="compile-common" depends="codegen">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="build-classpath"
+ debug="on">
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <exclude name="**/junit/**" />
+ <exclude name="**/*JUnitTestCase.*" />
+ <exclude name="**/subst/**" />
+ <exclude name="com/mchange/v2/log/log4j/**" />
+ </javac>
+ </target>
+
+ <target name="compile-subst" depends="codegen">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ classpathref="build-classpath"
+ debug="on">
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ </src>
+ <include name="**/subst/**" />
+ </javac>
+ </target>
+
+ <target name="compile-log4j" depends="init" if="log4j.jar.file">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ debug="on">
+ <classpath>
+ <path refid="build-classpath" />
+ <pathelement location="${log4j.jar.file}" />
+ </classpath>
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <include name="com/mchange/v2/log/log4j/**" />
+ </javac>
+ </target>
+
+ <target name="compile-junit" depends="init" if="junit.jar.file">
+ <javac destdir="${build.classes.dir}"
+ source="${c3p0.target.version}"
+ target="${c3p0.target.version}"
+ debug="on">
+ <classpath>
+ <path refid="build-classpath" />
+ <pathelement path="${junit.jar.file}" />
+ </classpath>
+ <sourcepath>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </sourcepath>
+ <src>
+ <pathelement location="${build.codegen.dir}" />
+ <pathelement location="${java.src.dir}" />
+ </src>
+ <include name="**/junit/**" />
+ <include name="**/*JUnitTestCase.*" />
+ </javac>
+ </target>
+
+ <target name="compile" depends="codegen,compile-common,compile-subst,compile-log4j,compile-junit">
+ </target>
+
+ <target name="jar" depends="compile">
+ <jar destfile="${build.jar.file}">
+ <manifest>
+ <attribute name="Extension-Name" value="com.mchange.v2.c3p0" />
+ <attribute name="Specification-Vendor" value="Machinery For Change, Inc." />
+ <attribute name="Specification-Version" value="1.0" />
+ <attribute name="Implementation-Vendor-Id" value="com.mchange" />
+ <attribute name="Implementation-Vendor" value="Machinery For Change, Inc." />
+ <attribute name="Implementation-Version" value="${c3p0.version}" />
+ </manifest>
+ <fileset dir="${build.classes.dir}">
+ <patternset refid="dist-jar-classes"/>
+ </fileset>
+ <fileset dir="${rsrc.src.dir}" />
+ </jar>
+ </target>
+
+ <target name="dbms-oracle-thin-ant">
+ <ant dir="${dbms.oracle.thin.antproj.dir}" target="${subproject.target}" inheritAll="false">
+ <property name="c3p0.version" value="${c3p0.version}" />
+ <property name="c3p0.jar.file" location="${build.jar.file}" />
+ <property name="oracle-thin.jdbc.jar.file" value="${oracle-thin.jdbc.jar.file}" />
+ </ant>
+ </target>
+
+ <target name="dbms-oracle-thin-clean">
+ <antcall target="dbms-oracle-thin-ant">
+ <param name="subproject.target" value="clean" />
+ </antcall>
+ </target>
+
+ <target name="dbms-oracle-thin" depends="jar" if="oracle-thin.jdbc.jar.file">
+ <echo message="oracle-thin.jdbc.jar.file: ${oracle-thin.jdbc.jar.file}" />
+ <antcall target="dbms-oracle-thin-ant">
+ <param name="subproject.target" value="dist" />
+ </antcall>
+ </target>
+
+ <target name="test-init" depends="jar">
+ <mkdir dir="${test.classes.dir}" />
+ <copy toDir="${test.classes.dir}">
+ <fileset dir="${build.classes.dir}">
+ <patternset refid="test-only-classes"/>
+ </fileset>
+ </copy>
+ <property name="testcp" refid="test-classpath" />
+ <echo message="test-classpath: ${testcp}" />
+ </target>
+
+ <target name="stats-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.StatsTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <!-- <jvmarg value="-Xrunhprof:file=/tmp/java.hprof,doe=y,format=b" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="proxywrapper-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.ProxyWrappersTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <!-- <jvmarg value="-Xrunhprof:file=/tmp/java.hprof,doe=y,format=b" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="benchmark-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.C3P0BenchmarkApp"
+ classpathref="test-classpath"
+ fork="true">
+ <!-- <jvmarg value="-Xrunhprof:cpu=times,file=/tmp/java.hprof,doe=y,format=a" /> -->
+ <!-- <jvmarg value="-server" /> -->
+ <!-- <jvmarg value="-Xprof" /> -->
+ <!-- <jvmarg value="-verbose:class" /> -->
+
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+<!--
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+-->
+ </java>
+ </target>
+
+ <target name="rco-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.RawConnectionOpTest"
+ classpathref="test-classpath"
+ fork="true">
+ <!-- <jvmarg value="-Xrunhprof:cpu=times,file=/tmp/java.hprof,doe=y,format=a" /> -->
+ <!-- <jvmarg value="-Xprof" /> -->
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="load-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.LoadPoolBackedDataSource"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="psload-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.PSLoadPoolBackedDataSource"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+
+ <!--
+ <jvmarg value="-ea" />
+ <sysproperty key="com.sun.management.jmxremote.port" value="38383" />
+ <sysproperty key="com.sun.management.jmxremote.authenticate" value="false" />
+ <sysproperty key="com.sun.management.jmxremote.ssl" value="false" />
+ -->
+
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="dispersion-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.ConnectionDispersionTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="onethreadrepeat-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.OneThreadRepeatedInsertOrQueryTest"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="refser-test" depends="test-init">
+ <java
+ classname="com.mchange.v2.c3p0.test.TestRefSerStuff"
+ classpathref="test-classpath"
+ fork="true">
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="java.util.logging.config.file" value="${test.logging.props.file}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <arg value="${test.jdbc.url}" />
+ <arg value="${test.jdbc.user}" />
+ <arg value="${test.jdbc.password}" />
+ </java>
+ </target>
+
+ <target name="junit-tests" depends="test-init" if="junit.jar.file">
+ <mkdir dir="${build.testresults.dir}" />
+ <junit printsummary="true" showoutput="true" haltonfailure="true">
+ <classpath refid="test-classpath" />
+ <formatter type="plain"/>
+ <sysproperty key="jdbc.drivers" value="${test.jdbc.drivers}" />
+ <sysproperty key="c3p0.test.jdbc.url" value="${test.jdbc.url}" />
+ <sysproperty key="c3p0.test.jdbc.user" value="${test.jdbc.user}" />
+ <sysproperty key="c3p0.test.jdbc.password" value="${test.jdbc.password}" />
+ <syspropertyset>
+ <propertyref builtin="commandline" />
+ </syspropertyset>
+ <batchtest fork="yes" toDir="${build.testresults.dir}">
+ <fileset dir="${build.classes.dir}">
+ <include name="**/*JUnitTestCase.class"/>
+ </fileset>
+ </batchtest>
+ </junit>
+
+ <!--
+ <junit printsummary="withOutAndErr" >
+ <test name="com.mchange.v2.c3p0.test.junit.MiscellaneousTopLevelJUnitTestCase"/>
+ <classpath refid="test-classpath" />
+ <formatter type="plain"/>
+ </junit>
+ -->
+ </target>
+
+ <target name="javadocs" depends="init">
+ <javadoc packagenames="com.mchange.v2.c3p0"
+ sourcepath="${java.src.dir}"
+ destdir="${build.apidocs.dir}"
+ classpathref="build-classpath"
+ windowtitle="${c3p0.name} API Documentation"
+ />
+ </target>
+
+ <target name="basic-open-dist" depends="jar, javadocs">
+ <copy toDir="${open.dist}">
+ <fileset dir="${static.dist.src}">
+ <exclude name="*~" />
+ <exclude name="examples/*.class" />
+ <exclude name="**/old/**" />
+ <exclude name="**/bad/**" />
+ <exclude name="**/private/**" />
+ </fileset>
+ </copy>
+ <mkdir dir="${open.dist.doc.dir}/apidocs" />
+ <copy toDir="${open.dist.doc.dir}">
+ <fileset dir="${doc.src.dir}" excludes="*.png"/>
+ <filterchain>
+ <replacetokens>
+ <token key="c3p0.version" value="${c3p0.version}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ <copy toDir="${open.dist.doc.dir}">
+ <fileset dir="${doc.src.dir}" includes="*.png"/>
+ </copy>
+ <copy toDir="${open.dist.doc.dir}/apidocs">
+ <fileset dir="${build.apidocs.dir}" />
+ </copy>
+ <copy file="${build.jar.file}" toDir="${open.dist.lib.dir}" />
+ </target>
+
+ <target name="docwebapp" depends="basic-open-dist">
+ <mkdir dir="${build.docwebapp.dir}" />
+ <copy toDir="${build.docwebapp.dir}">
+ <fileset dir="${docwebapp.src.dir}"/>
+ <filterchain>
+ <replacetokens>
+ <token key="virtual.host" value="${docwebapp.virtual.host}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ </target>
+
+ <target name="docwebear" depends="docwebapp">
+ <mkdir dir="${build.docwebear.dir}" />
+ <copy toDir="${build.docwebear.dir}">
+ <fileset dir="${docwebear.src.dir}"/>
+ <filterchain>
+ <replacetokens>
+ <token key="web.uri" value="${docwebapp.war.file.name}"/>
+ <token key="context.root" value="${docwebapp.context.root}"/>
+ </replacetokens>
+ </filterchain>
+ </copy>
+ <jar destfile="${docwebapp.war.file}">
+ <zipfileset dir="${build.docwebapp.dir}" />
+ <zipfileset dir="${open.dist.doc.dir}" />
+ </jar>
+ <jar destfile="${docwebear.file}">
+ <zipfileset dir="${build.docwebear.dir}" />
+ </jar>
+ </target>
+
+ <target name="docwebear-deploy" depends="docwebear">
+ <exec executable="scp">
+ <arg line="${docwebear.file} ${docwebear.deploy.user}@${docwebear.deploy.host}:${docwebear.deploy.path}" />
+ </exec>
+ </target>
+
+ <target name="oracle-thin-open-dist" depends="basic-open-dist, dbms-oracle-thin" if="oracle-thin.jdbc.jar.file">
+ <copy toDir="${open.dist}">
+ <fileset dir="${dbms.oracle.thin.antproj.dist.dir}" />
+ </copy>
+ </target>
+
+ <target name="open-dist" depends="basic-open-dist, oracle-thin-open-dist" />
+
+ <target name="zip-dist" depends="open-dist">
+ <zip destfile="${dist.dir}/${bindist.name}.zip">
+ <zipfileset dir="${open.dist}" prefix="${c3p0.name}"/>
+ </zip>
+ </target>
+
+ <target name="tar-dist" depends="open-dist">
+ <tar destfile="${dist.dir}/${bindist.name}.tar">
+ <tarfileset dir="${open.dist}" prefix="${c3p0.name}"/>
+ </tar>
+ </target>
+
+ <target name="tgz-dist" depends="tar-dist">
+ <gzip zipfile="${dist.dir}/${bindist.name}.tgz" src="${dist.dir}/${bindist.name}.tar" />
+ </target>
+
+ <target name="bindist" depends="tgz-dist, zip-dist" />
+
+ <target name="dist" depends="bindist" />
+
+ <target name="srcdist" depends="init">
+ <zip destfile="${dist.dir}/${srcdist.name}.zip">
+ <zipfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <exclude name="**/old/**"/>
+ <exclude name="**/bad/**"/>
+ <exclude name="**/private/**"/>
+ </zipfileset>
+ </zip>
+ <tar destfile="${dist.dir}/${srcdist.name}.tar">
+ <tarfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <exclude name="**/old/**"/>
+ <exclude name="**/bad/**"/>
+ <exclude name="**/private/**"/>
+ </tarfileset>
+ </tar>
+ <gzip zipfile="${dist.dir}/${srcdist.name}.tgz" src="${dist.dir}/${srcdist.name}.tar" />
+ </target>
+
+ <target name="all" depends="dist,srcdist" />
+
+</project>
+
diff --git a/dbms/oracle-thin/build.properties b/dbms/oracle-thin/build.properties
new file mode 100644
index 0000000..9410c87
--- /dev/null
+++ b/dbms/oracle-thin/build.properties
@@ -0,0 +1,10 @@
+#
+# You'll rarely (never) want to set these properties by hand... they
+# are set by the main c3p0 build script. They are listed here primarily
+# to document the dependencies of the dbms-specific build script.
+#
+
+c3p0-version=
+c3p0.jar.file=
+oracle-thin.jdbc.jar.file=
+
diff --git a/dbms/oracle-thin/build.xml b/dbms/oracle-thin/build.xml
new file mode 100644
index 0000000..0db4d81
--- /dev/null
+++ b/dbms/oracle-thin/build.xml
@@ -0,0 +1,68 @@
+<project name="c3p0-oracle-thin" default="dist">
+
+ <!-- ignore the CLASSPATH environment variable. force builds to specify classpaths -->
+ <property name="build.sysclasspath" value="ignore" />
+
+ <property file="build.properties" />
+
+ <property name="c3p0-oracle-thin.name" value="c3p0-oracle-thin-extras-${c3p0.version}" />
+ <property name="src.dir" value="src" />
+ <property name="java.src.dir" value="${src.dir}/classes" />
+ <property name="build.dir" value="build" />
+ <property name="build.classes.dir" value="${build.dir}/classes" />
+ <property name="build.apidocs.dir" value="${build.dir}/apidocs" />
+ <property name="dist.dir" value="dist" />
+ <property name="dist.lib.dir" value="${dist.dir}/lib" />
+ <property name="dist.apidocs.dir" value="${dist.dir}/doc/apidocs-oracle-thin" />
+ <property name="lib.jar.file" value="${dist.lib.dir}/${c3p0-oracle-thin.name}.jar" />
+
+ <path id="build-class-path">
+ <pathelement location="${c3p0.jar.file}" />
+ <pathelement location="${oracle-thin.jdbc.jar.file}" />
+ </path>
+
+ <target name="init">
+ <mkdir dir="${build.dir}" />
+ <mkdir dir="${dist.lib.dir}" />
+ </target>
+
+ <target name="clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${dist.dir}" />
+ </target>
+
+ <target name="compile" depends="init">
+ <echo message="${c3p0.jar.file}" />
+ <echo message="${oracle-thin.jdbc.jar.file}" />
+ <mkdir dir="${build.classes.dir}" />
+ <javac srcdir="${java.src.dir}"
+ destdir="${build.classes.dir}"
+ classpathref="build-class-path"
+ debug="on" />
+ </target>
+
+ <target name="javadocs" depends="init">
+ <mkdir dir="${build.apidocs.dir}" />
+ <javadoc packagenames="com.mchange.v2.c3p0.dbms"
+ sourcepath="${java.src.dir}"
+ destdir="${build.apidocs.dir}"
+ classpathref="build-class-path"
+ windowtitle="${c3p0-oracle-thin.name} API Documentation"
+ />
+ </target>
+
+ <target name="lib-jar" depends="compile">
+ <jar destfile="${lib.jar.file}">
+ <fileset dir="${build.classes.dir}" />
+ </jar>
+ </target>
+
+ <target name="dist" depends="lib-jar,javadocs">
+ <mkdir dir="${dist.apidocs.dir}" />
+ <copy toDir="${dist.apidocs.dir}">
+ <fileset dir="${build.apidocs.dir}" />
+ </copy>
+ </target>
+
+</project>
+
diff --git a/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/Debug.java b/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/Debug.java
new file mode 100644
index 0000000..f7d48b4
--- /dev/null
+++ b/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/Debug.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+/********************************************************************
+ * This class generated by com.mchange.v2.debug.DebugGen
+ * and will probably be overwritten by the same! Edit at
+ * YOUR PERIL!!! Hahahahaha.
+ ********************************************************************/
+
+package com.mchange.v2.c3p0.dbms;
+
+import com.mchange.v2.debug.DebugConstants;
+
+final class Debug implements DebugConstants
+{
+ final static boolean DEBUG = true;
+ final static int TRACE = TRACE_MED;
+
+ private Debug()
+ {}
+}
+
diff --git a/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/OracleUtils.java b/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/OracleUtils.java
new file mode 100644
index 0000000..397d044
--- /dev/null
+++ b/dbms/oracle-thin/src/classes/com/mchange/v2/c3p0/dbms/OracleUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.dbms;
+
+import java.lang.reflect.*;
+import java.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.sql.SqlUtils;
+import oracle.sql.BLOB;
+import oracle.sql.CLOB;
+import oracle.jdbc.driver.OracleConnection;
+
+/**
+ * A convenience class for OracleUsers who wish to use Oracle-specific Connection API
+ * without working directly with c3p0 raw connection operations.
+ */
+public final class OracleUtils
+{
+ final static Class[] CREATE_TEMP_ARGS = new Class[]{Connection.class, boolean.class, int.class};
+
+ /**
+ * Uses Oracle-specific API on the raw, underlying Connection to create a temporary BLOB.
+ * <b>Users are responsible for calling freeTemporary on the returned BLOB prior to Connection close() / check-in!
+ * c3p0 will <i>not</i> automatically clean up temporary BLOBs.</b>
+ *
+ * @param c3p0ProxyCon may be a c3p0 proxy for an <tt>oracle.jdbc.driver.OracleConnection</tt>, or an
+ * <tt>oracle.jdbc.driver.OracleConnection</tt> directly.
+ */
+ public static BLOB createTemporaryBLOB(Connection c3p0ProxyCon, boolean cache, int duration) throws SQLException
+ {
+ if (c3p0ProxyCon instanceof C3P0ProxyConnection)
+ {
+ try
+ {
+ C3P0ProxyConnection castCon = (C3P0ProxyConnection) c3p0ProxyCon;
+ Method m = BLOB.class.getMethod("createTemporary", CREATE_TEMP_ARGS);
+ Object[] args = new Object[] {C3P0ProxyConnection.RAW_CONNECTION, Boolean.valueOf( cache ), new Integer( duration )};
+ return (BLOB) castCon.rawConnectionOperation(m, null, args);
+ }
+ catch (InvocationTargetException e)
+ {
+ if (Debug.DEBUG)
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e.getTargetException() );
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+ else if (c3p0ProxyCon instanceof OracleConnection)
+ return BLOB.createTemporary( c3p0ProxyCon, cache, duration );
+ else
+ throw new SQLException("Cannot create an oracle BLOB from a Connection that is neither an oracle.jdbc.driver.Connection, " +
+ "nor a C3P0ProxyConnection wrapped around an oracle.jdbc.driver.Connection.");
+ }
+
+ /**
+ * Uses Oracle-specific API on the raw, underlying Connection to create a temporary CLOB.
+ * <b>Users are responsible for calling freeTemporary on the returned BLOB prior to Connection close() / check-in!
+ * c3p0 will <i>not</i> automatically clean up temporary CLOBs.</b>
+ *
+ * @param c3p0ProxyCon may be a c3p0 proxy for an <tt>oracle.jdbc.driver.OracleConnection</tt>, or an
+ * <tt>oracle.jdbc.driver.OracleConnection</tt> directly.
+ */
+ public static CLOB createTemporaryCLOB(Connection c3p0ProxyCon, boolean cache, int duration) throws SQLException
+ {
+ if (c3p0ProxyCon instanceof C3P0ProxyConnection)
+ {
+ try
+ {
+ C3P0ProxyConnection castCon = (C3P0ProxyConnection) c3p0ProxyCon;
+ Method m = CLOB.class.getMethod("createTemporary", CREATE_TEMP_ARGS);
+ Object[] args = new Object[] {C3P0ProxyConnection.RAW_CONNECTION, Boolean.valueOf( cache ), new Integer( duration )};
+ return (CLOB) castCon.rawConnectionOperation(m, null, args);
+ }
+ catch (InvocationTargetException e)
+ {
+ if (Debug.DEBUG)
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e.getTargetException() );
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+ else if (c3p0ProxyCon instanceof OracleConnection)
+ return CLOB.createTemporary( c3p0ProxyCon, cache, duration );
+ else
+ throw new SQLException("Cannot create an oracle CLOB from a Connection that is neither an oracle.jdbc.driver.Connection, " +
+ "nor a C3P0ProxyConnection wrapped around an oracle.jdbc.driver.Connection.");
+ }
+
+ private OracleUtils()
+ {}
+}
diff --git a/relproj/build.xml b/relproj/build.xml
new file mode 100644
index 0000000..6d5ffa6
--- /dev/null
+++ b/relproj/build.xml
@@ -0,0 +1,26 @@
+<project name="c3p0-relproj" default="dist">
+
+ <!-- ignore the CLASSPATH environment variable. force builds to specify classpaths -->
+ <property name="build.sysclasspath" value="ignore" />
+
+ <property name="dist.dir" value="dist" />
+ <property name="debuggen.proj.dir" value="debuggen" />
+
+ <target name="init">
+ <mkdir dir="${dist.dir}" />
+ </target>
+
+ <target name="clean">
+ <delete dir="${dist.dir}" />
+ <ant dir="${debuggen.proj.dir}" target="clean" inheritAll="false" />
+ </target>
+
+ <target name="dist">
+ <ant dir="${debuggen.proj.dir}" target="jar" inheritAll="false" />
+ <copy todir="${dist.dir}">
+ <fileset dir="${debuggen.proj.dir}/dist" />
+ </copy>
+ </target>
+
+</project>
+
diff --git a/relproj/debuggen/build.xml b/relproj/debuggen/build.xml
new file mode 100644
index 0000000..ede948c
--- /dev/null
+++ b/relproj/debuggen/build.xml
@@ -0,0 +1,71 @@
+<project name="debuggen" default="dist">
+
+ <!-- ignore the CLASSPATH environment variable. force builds to specify classpaths -->
+ <property name="build.sysclasspath" value="ignore" />
+
+ <property file="build.properties" />
+ <property file="version.properties" />
+
+ <property name="debuggen.name" value="debuggen-${debuggen.version}" />
+ <property name="src.dir" value="src" />
+ <property name="java.src.dir" value="${src.dir}/classes" />
+ <property name="app-rsrc.src.dir" value="${src.dir}/app-rsrc" />
+ <property name="build.dir" value="build" />
+ <property name="build.classes.dir" value="${build.dir}/classes" />
+ <property name="build.apidocs.dir" value="${build.dir}/apidocs" />
+ <property name="dist.dir" value="dist" />
+ <property name="app.jar.manifest" value="${app-rsrc.src.dir}/META-INF/manifest.src" />
+ <property name="jar.file" value="${dist.dir}/${debuggen.name}.jar" />
+ <property name="srcdist.name" value="${debuggen.name}-src" />
+
+ <target name="init">
+ <mkdir dir="${build.dir}" />
+ <mkdir dir="${dist.dir}" />
+ </target>
+
+ <target name="clean">
+ <delete dir="${build.dir}" />
+ <delete dir="${dist.dir}" />
+ </target>
+
+ <target name="compile" depends="init">
+ <mkdir dir="${build.classes.dir}" />
+ <javac srcdir="${java.src.dir}"
+ destdir="${build.classes.dir}"
+ debug="on" />
+ </target>
+
+ <target name="jar" depends="compile">
+ <jar destfile="${jar.file}" manifest="${app.jar.manifest}">
+ <fileset dir="${build.classes.dir}" />
+ </jar>
+ </target>
+
+ <target name="dist" depends="jar" />
+
+ <target name="srcdist">
+ <zip destfile="${dist.dir}/${srcdist.name}.zip">
+ <zipfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <exclude name="**/old/**"/>
+ <exclude name="**/bad/**"/>
+ </zipfileset>
+ </zip>
+ <tar destfile="${dist.dir}/${srcdist.name}.tar">
+ <tarfileset dir="." prefix="${srcdist.name}">
+ <exclude name="${build.dir}/**"/>
+ <exclude name="${dist.dir}/**"/>
+ <exclude name="**/*.class"/>
+ <exclude name="**/old/**"/>
+ <exclude name="**/bad/**"/>
+ </tarfileset>
+ </tar>
+ <gzip zipfile="${dist.dir}/${srcdist.name}.tgz" src="${dist.dir}/${srcdist.name}.tar" />
+ </target>
+
+ <target name="all" depends="dist,srcdist" />
+
+</project>
+
diff --git a/relproj/debuggen/src/app-rsrc/META-INF/manifest.src b/relproj/debuggen/src/app-rsrc/META-INF/manifest.src
new file mode 100644
index 0000000..bbb1b71
--- /dev/null
+++ b/relproj/debuggen/src/app-rsrc/META-INF/manifest.src
@@ -0,0 +1,2 @@
+Main-Class: com.mchange.v2.debug.DebugGen
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/io/WriterUtils.java b/relproj/debuggen/src/classes/com/mchange/v1/io/WriterUtils.java
new file mode 100644
index 0000000..b92676e
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/io/WriterUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.io;
+
+import java.io.Writer;
+import java.io.IOException;
+
+public final class WriterUtils
+{
+ public static void attemptClose(Writer os)
+ {
+ try
+ {if (os != null) os.close();}
+ catch (IOException e)
+ {e.printStackTrace();}
+ }
+
+ private WriterUtils()
+ {}
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/lang/BooleanUtils.java b/relproj/debuggen/src/classes/com/mchange/v1/lang/BooleanUtils.java
new file mode 100644
index 0000000..105d930
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/lang/BooleanUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+public final class BooleanUtils
+{
+ public static boolean parseBoolean(String str) throws IllegalArgumentException
+ {
+ if (str.equals("true"))
+ return true;
+ else if (str.equals("false"))
+ return false;
+ else
+ throw new IllegalArgumentException("\"str\" is neither \"true\" nor \"false\".");
+ }
+
+ private BooleanUtils()
+ {}
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/util/ClosableResource.java b/relproj/debuggen/src/classes/com/mchange/v1/util/ClosableResource.java
new file mode 100644
index 0000000..a50aa57
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/util/ClosableResource.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+/**
+ * An interface intended to be shared by the many sorts
+ * of objects that offer some kind of close method to
+ * cleanup resources they might be using. (I wish Sun
+ * had defined, and used, such an interface in the standard
+ * APIs.
+ */
+public interface ClosableResource
+{
+ /**
+ * forces the release of any resources that might be
+ * associated with this object.
+ */
+ public void close() throws Exception;
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/util/IteratorUtils.java b/relproj/debuggen/src/classes/com/mchange/v1/util/IteratorUtils.java
new file mode 100644
index 0000000..d78137a
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/util/IteratorUtils.java
@@ -0,0 +1,159 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.*;
+import java.lang.reflect.Array;
+
+public final class IteratorUtils
+{
+ public final static Iterator EMPTY_ITERATOR = new Iterator()
+ {
+ public boolean hasNext()
+ { return false; }
+
+ public Object next()
+ { throw new NoSuchElementException(); }
+
+ public void remove()
+ { throw new IllegalStateException(); }
+ };
+
+ public static Iterator oneElementUnmodifiableIterator(final Object elem)
+ {
+ return new Iterator()
+ {
+ boolean shot = false;
+
+ public boolean hasNext() { return (!shot); }
+
+ public Object next()
+ {
+ if (shot)
+ throw new NoSuchElementException();
+ else
+ {
+ shot = true;
+ return elem;
+ }
+ }
+
+ public void remove()
+ { throw new UnsupportedOperationException("remove() not supported."); }
+ };
+ }
+
+ public static boolean equivalent(Iterator ii, Iterator jj)
+ {
+ while (true)
+ {
+ boolean ii_hasnext = ii.hasNext();
+ boolean jj_hasnext = jj.hasNext();
+ if (ii_hasnext ^ jj_hasnext)
+ return false;
+ else if (ii_hasnext)
+ {
+ Object iiNext = ii.next();
+ Object jjNext = jj.next();
+ if (iiNext == jjNext)
+ continue;
+ else if (iiNext == null)
+ return false;
+ else if (!iiNext.equals(jjNext))
+ return false;
+ }
+ else return true;
+ }
+ }
+
+ public static ArrayList toArrayList(Iterator ii, int initial_capacity)
+ {
+ ArrayList out = new ArrayList(initial_capacity);
+ while (ii.hasNext())
+ out.add(ii.next());
+ return out;
+ }
+
+ /**
+ * Fills an array with the contents of an iterator. If the array is too small,
+ * it will contain the first portion of the iterator. If the array can contain
+ * more elements than the iterator, extra elements are left untouched, unless
+ * null_terminate is set to true, in which case the element immediately following
+ * the last from the iterator is set to null. (This method is intended to make
+ * it easy to implement Collection.toArray(Object[] oo) methods...
+ *
+ * @param null_terminate iff there is extra space in the array, set the element
+ * immediately after the last from the iterator to null.
+ */
+ public static void fillArray(Iterator ii, Object[] fillMe, boolean null_terminate)
+ {
+ int i = 0;
+ int len = fillMe.length;
+ while ( i < len && ii.hasNext() )
+ fillMe[ i++ ] = ii.next();
+ if (null_terminate && i < len)
+ fillMe[i] = null;
+ }
+
+ public static void fillArray(Iterator ii, Object[] fillMe)
+ { fillArray( ii, fillMe, false); }
+
+ /**
+ * @param null_terminate iff there is extra space in the array, set the element
+ * immediately after the last from the iterator to null.
+ */
+ public static Object[] toArray(Iterator ii, int array_size, Class componentClass, boolean null_terminate)
+ {
+ Object[] out = (Object[]) Array.newInstance( componentClass, array_size );
+ fillArray(ii, out, null_terminate);
+ return out;
+ }
+
+ public static Object[] toArray(Iterator ii, int array_size, Class componentClass)
+ { return toArray( ii, array_size, componentClass, false ); }
+
+ /**
+ * Designed to help implement Collection.toArray(Object[] )methods... does
+ * the right thing if you can express an iterator and know the size of your
+ * Collection.
+ */
+ public static Object[] toArray(Iterator ii, int ii_size, Object[] maybeFillMe)
+ {
+ if (maybeFillMe.length >= ii_size)
+ {
+ fillArray( ii, maybeFillMe, true );
+ return maybeFillMe;
+ }
+ else
+ {
+ Class componentType = maybeFillMe.getClass().getComponentType();
+ return toArray( ii, ii_size, componentType );
+ }
+ }
+
+ private IteratorUtils()
+ {}
+}
+
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/util/SetUtils.java b/relproj/debuggen/src/classes/com/mchange/v1/util/SetUtils.java
new file mode 100644
index 0000000..7d7e259
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/util/SetUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.AbstractSet;
+import java.util.HashSet;
+
+public final class SetUtils
+{
+ public static Set oneElementUnmodifiableSet(final Object elem)
+ {
+ return new AbstractSet()
+ {
+ public Iterator iterator()
+ { return IteratorUtils.oneElementUnmodifiableIterator( elem ); }
+
+ public int size() { return 1; }
+
+ public boolean isEmpty()
+ { return false; }
+
+ public boolean contains(Object o)
+ { return o == elem; }
+
+ };
+ }
+
+ public static Set setFromArray(Object[] array)
+ {
+ HashSet out = new HashSet();
+ for (int i = 0, len = array.length; i < len; ++i)
+ out.add( array[i] );
+ return out;
+ }
+
+ public static boolean equivalentDisregardingSort(Set a, Set b)
+ {
+ return
+ a.containsAll( b ) &&
+ b.containsAll( a );
+ }
+
+ /**
+ * finds a hash value which takes into account
+ * the value of all elements, such that two sets
+ * for which equivalentDisregardingSort(a, b) returns
+ * true will hashContentsDisregardingSort() to the same value
+ */
+ public static int hashContentsDisregardingSort(Set s)
+ {
+ int out = 0;
+ for (Iterator ii = s.iterator(); ii.hasNext(); )
+ {
+ Object o = ii.next();
+ if (o != null) out ^= o.hashCode();
+ }
+ return out;
+ }
+}
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/util/StringTokenizerUtils.java b/relproj/debuggen/src/classes/com/mchange/v1/util/StringTokenizerUtils.java
new file mode 100644
index 0000000..5db6b5d
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/util/StringTokenizerUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.StringTokenizer;
+
+public final class StringTokenizerUtils
+{
+ public static String[] tokenizeToArray(String str, String delim, boolean returntokens)
+ {
+ StringTokenizer st = new StringTokenizer(str, delim, returntokens);
+ String[] strings = new String[st.countTokens()];
+ for (int i = 0; st.hasMoreTokens(); ++i)
+ strings[i] = st.nextToken();
+ return strings;
+ }
+
+ public static String[] tokenizeToArray(String str, String delim)
+ {return tokenizeToArray(str, delim, false);}
+
+ public static String[] tokenizeToArray(String str)
+ {return tokenizeToArray(str, " \t\r\n");}
+}
+
+
+
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v1/util/UIterator.java b/relproj/debuggen/src/classes/com/mchange/v1/util/UIterator.java
new file mode 100644
index 0000000..48371b7
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v1/util/UIterator.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+/**
+ * Incomplete parent of "Unreliable Iterator"
+ * This is often bound to a scarce resource! Don't
+ * forget to close it when you are done!!!
+ *
+ * This interface is not intended to be implemented
+ * directly, but to be extended by subinterfaces
+ * that narrow the exceptions reasonably.
+ */
+public interface UIterator extends ClosableResource
+{
+ public boolean hasNext() throws Exception;
+ public Object next() throws Exception;
+ public void remove() throws Exception;
+ public void close() throws Exception;
+}
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/BadCommandLineException.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/BadCommandLineException.java
new file mode 100644
index 0000000..055852e
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/BadCommandLineException.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public class BadCommandLineException extends Exception
+{
+ public BadCommandLineException(String msg)
+ { super(msg); }
+
+ public BadCommandLineException()
+ { super(); }
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/CommandLineUtils.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/CommandLineUtils.java
new file mode 100644
index 0000000..6d74474
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/CommandLineUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public final class CommandLineUtils
+{
+ /**
+ * "Parses" a command line by making use several conventions:
+ * <UL>
+ * <LI> Certain arguments are considered "switches", by virtue
+ * of being prefixed with some string, usually "-", "/", or "--"
+ * <LI> Switches may have arguments associated with them. This implementation
+ * permits only a single argument per switch
+ * <LI> Switch arguments are determined via two conventions:
+ * <OL>
+ * <LI> If a switch is of the form "--switch=value" (where "--" is
+ * set as the switch prefix), value is the switches argument.
+ * <LI> If a switch is not of this form (simply "--switch"), then the
+ * following item on the command line is considered the switch's
+ * argument if and only if
+ * <OL>
+ * <LI> the argSwitches array contains the switch, and
+ * <LI> the next item on the command line is not itself a switch
+ * </OL>
+ * </OL>
+ * </UL>
+ *
+ * @param argv the entire list of arguments, usually the argument to a main function
+ * @param switchPrefix the string which separates "switches" from regular command line args.
+ * Must be non-null
+ * @param validSwitches a list of all the switches permissible for this command line.
+ * If non-null, an UnexpectedSwitchException will be thrown if a switch not
+ * in this list is encountered. Use null to accept any switches.
+ * @param requiredSwitches a list of all the switches required by this command line.
+ * If non-null, an MissingSwitchException will be thrown if a switch
+ * in this list is not present. Use null if no switches should be considered required.
+ * @param argSwitches a list of switches that should have an argument associated with them
+ * If non-null, an MissingSwitchArgumentException will be thrown if a switch
+ * in this list has no argument is not present. Use null if no switches should
+ * be considered to require arguments. However, this parameter is required if
+ * distinct items on a command line should be considered arguments to preceding
+ * items. (For example, "f" must be an argSwitch for "-f myfile.txt" to be parsed
+ * as switch and argument, but argSwitches is not required to parse "--file=myfile.txt"
+ */
+ public static ParsedCommandLine parse(String[] argv,
+ String switchPrefix,
+ String[] validSwitches,
+ String[] requiredSwitches,
+ String[] argSwitches)
+ throws BadCommandLineException
+ {
+ return new ParsedCommandLineImpl( argv,
+ switchPrefix,
+ validSwitches,
+ requiredSwitches,
+ argSwitches );
+ }
+
+ private CommandLineUtils()
+ {}
+}
+
+
+
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/MissingSwitchException.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/MissingSwitchException.java
new file mode 100644
index 0000000..3c591dc
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/MissingSwitchException.java
@@ -0,0 +1,38 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public class MissingSwitchException extends BadCommandLineException
+{
+ String sw;
+
+ MissingSwitchException(String msg, String sw)
+ {
+ super(msg);
+ this.sw = sw;
+ }
+
+ public String getMissingSwitch()
+ { return sw; }
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLine.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLine.java
new file mode 100644
index 0000000..7b9e2b2
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLine.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public interface ParsedCommandLine
+{
+ public String[] getRawArgs();
+
+ public String getSwitchPrefix();
+ public boolean includesSwitch(String sw);
+ public String getSwitchArg(String sw);
+ public String[] getUnswitchedArgs();
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLineImpl.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLineImpl.java
new file mode 100644
index 0000000..9d2637e
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/ParsedCommandLineImpl.java
@@ -0,0 +1,124 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+import java.util.*;
+
+class ParsedCommandLineImpl implements ParsedCommandLine
+{
+ String[] argv;
+ String switchPrefix;
+
+ String[] unswitchedArgs;
+
+ //we are relying upon the fact that
+ //HashMaps are null-accepting collections
+ HashMap foundSwitches = new HashMap();
+
+ ParsedCommandLineImpl(String[] argv,
+ String switchPrefix,
+ String[] validSwitches,
+ String[] requiredSwitches,
+ String[] argSwitches)
+ throws BadCommandLineException
+ {
+ this.argv = argv;
+ this.switchPrefix = switchPrefix;
+
+ List unswitchedArgsList = new LinkedList();
+ int sp_len = switchPrefix.length();
+
+ for (int i = 0; i < argv.length; ++i)
+ {
+ if (argv[i].startsWith(switchPrefix)) //okay, this is a switch
+ {
+ String sw = argv[i].substring( sp_len );
+ String arg = null;
+
+ int eq = sw.indexOf('=');
+ if ( eq >= 0 ) //equals convention
+ {
+ arg = sw.substring( eq + 1 );
+ sw = sw.substring( 0, eq );
+ }
+ else if ( contains( sw, argSwitches ) ) //we expect an argument, next arg convention
+ {
+ if (i < argv.length - 1 && !argv[i + 1].startsWith( switchPrefix) )
+ arg = argv[++i];
+ }
+
+ if (validSwitches != null && ! contains( sw, validSwitches ) )
+ throw new UnexpectedSwitchException("Unexpected Switch: " + sw, sw);
+ if (argSwitches != null && arg != null && ! contains( sw, argSwitches ))
+ throw new UnexpectedSwitchArgumentException("Switch \"" + sw +
+ "\" should not have an " +
+ "argument. Argument \"" +
+ arg + "\" found.", sw, arg);
+ foundSwitches.put( sw, arg );
+ }
+ else
+ unswitchedArgsList.add( argv[i] );
+ }
+
+ if (requiredSwitches != null)
+ {
+ for (int i = 0; i < requiredSwitches.length; ++i)
+ if (! foundSwitches.containsKey( requiredSwitches[i] ))
+ throw new MissingSwitchException("Required switch \"" + requiredSwitches[i] +
+ "\" not found.", requiredSwitches[i]);
+ }
+
+ unswitchedArgs = new String[ unswitchedArgsList.size() ];
+ unswitchedArgsList.toArray( unswitchedArgs );
+ }
+
+ public String getSwitchPrefix()
+ { return switchPrefix; }
+
+ public String[] getRawArgs()
+ { return (String[]) argv.clone(); }
+
+ public boolean includesSwitch(String sw)
+ { return foundSwitches.containsKey( sw ); }
+
+ public String getSwitchArg(String sw)
+ { return (String) foundSwitches.get(sw); }
+
+ public String[] getUnswitchedArgs()
+ { return (String[]) unswitchedArgs.clone(); }
+
+ private static boolean contains(String string, String[] list)
+ {
+ for (int i = list.length; --i >= 0;)
+ if (list[i].equals(string)) return true;
+ return false;
+ }
+
+}
+
+
+
+
+
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchArgumentException.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchArgumentException.java
new file mode 100644
index 0000000..28bf1da
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchArgumentException.java
@@ -0,0 +1,43 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public class UnexpectedSwitchArgumentException extends BadCommandLineException
+{
+ String sw;
+ String arg;
+
+ UnexpectedSwitchArgumentException(String msg, String sw, String arg)
+ {
+ super(msg);
+ this.sw = sw;
+ this.arg = arg;
+ }
+
+ public String getSwitch()
+ { return sw; }
+
+ public String getUnexpectedArgument()
+ { return arg; }
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchException.java b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchException.java
new file mode 100644
index 0000000..0f86bd2
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/cmdline/UnexpectedSwitchException.java
@@ -0,0 +1,38 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cmdline;
+
+public class UnexpectedSwitchException extends BadCommandLineException
+{
+ String sw;
+
+ UnexpectedSwitchException(String msg, String sw)
+ {
+ super(msg);
+ this.sw = sw;
+ }
+
+ public String getUnexpectedSwitch()
+ { return sw; }
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugConstants.java b/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugConstants.java
new file mode 100644
index 0000000..1c3be1b
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.debug;
+
+public interface DebugConstants
+{
+ public final static int TRACE_NONE = 0;
+ public final static int TRACE_MED = 5;
+ public final static int TRACE_MAX = 10;
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugGen.java b/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugGen.java
new file mode 100644
index 0000000..e6159d6
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/debug/DebugGen.java
@@ -0,0 +1,371 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.debug;
+
+import java.io.*;
+import java.util.*;
+import com.mchange.v2.cmdline.*;
+import com.mchange.v2.io.FileIterator;
+import com.mchange.v2.io.DirectoryDescentUtils;
+import com.mchange.v1.io.WriterUtils;
+import com.mchange.v1.lang.BooleanUtils;
+import com.mchange.v1.util.SetUtils;
+import com.mchange.v1.util.StringTokenizerUtils;
+
+public final class DebugGen implements DebugConstants
+{
+ final static String[] VALID = new String[]
+ {
+ "codebase",
+ "packages" ,
+ "trace",
+ "debug",
+ "recursive",
+ "javac",
+ "noclobber",
+ "classname",
+ "skipdirs",
+ "outputbase"
+ };
+
+ final static String[] REQUIRED = new String[]
+ { "codebase", "packages" , "trace", "debug" };
+
+ final static String[] ARGS = new String[]
+ { "codebase", "packages" , "trace", "debug", "classname", "outputbase" };
+
+ final static String EOL;
+
+ static
+ {
+ EOL = System.getProperty("line.separator");
+ }
+
+ static int trace_level;
+ static boolean debug;
+ static boolean recursive;
+ static String classname;
+ static boolean clobber;
+ static Set skipDirs;
+
+ public synchronized static final void main(String[] argv)
+ {
+ String codebase;
+ String outputbase;
+ File[] srcPkgDirs;
+
+ try
+ {
+ ParsedCommandLine pcl = CommandLineUtils.parse( argv, "--", VALID, REQUIRED, ARGS);
+
+ //get and normalize the codebase
+ codebase = pcl.getSwitchArg("codebase");
+ codebase = platify( codebase );
+ if (! codebase.endsWith(File.separator))
+ codebase += File.separator;
+
+ //get and normalize the outputbase
+ outputbase = pcl.getSwitchArg("outputbase");
+ if ( outputbase != null )
+ {
+ outputbase = platify( outputbase );
+ if (! outputbase.endsWith(File.separator))
+ outputbase += File.separator;
+ }
+ else
+ outputbase = codebase;
+
+ File outputBaseDir = new File( outputbase );
+ //System.err.println("outputBaseDir: " + outputBaseDir + "; exists? " + outputBaseDir.exists());
+ if (outputBaseDir.exists())
+ {
+ if (!outputBaseDir.isDirectory())
+ {
+ //System.err.println("Output Base '" + outputBaseDir.getPath() + "' is not a directory!");
+ System.exit(-1);
+ }
+ else if (!outputBaseDir.canWrite())
+ {
+ System.err.println("Output Base '" + outputBaseDir.getPath() + "' is not writable!");
+ System.exit(-1);
+ }
+ }
+ else if (! outputBaseDir.mkdirs() )
+ {
+ System.err.println("Output Base directory '" + outputBaseDir.getPath() + "' does not exist, and could not be created!");
+ System.exit(-1);
+ }
+
+ //find existing package dirs
+ String[] packages = StringTokenizerUtils.tokenizeToArray(pcl.getSwitchArg("packages"),", \t");
+ srcPkgDirs = new File[ packages.length ];
+ for(int i = 0, len = packages.length; i < len; ++i)
+ srcPkgDirs[i] = new File(codebase + sepify(packages[i]));
+
+ //find trace level
+ trace_level = Integer.parseInt( pcl.getSwitchArg("trace") );
+
+ //find debug
+ debug = BooleanUtils.parseBoolean( pcl.getSwitchArg("debug") );
+
+ //find classname, or use default
+ classname = pcl.getSwitchArg("classname");
+ if (classname == null)
+ classname = "Debug";
+
+ //find recursive
+ recursive = pcl.includesSwitch("recursive");
+
+ //find clobber
+ clobber = !pcl.includesSwitch("noclobber");
+
+ //find skipDirs
+ String skipdirStr = pcl.getSwitchArg("skipdirs");
+ if (skipdirStr != null)
+ {
+ String[] skipdirArray = StringTokenizerUtils.tokenizeToArray(skipdirStr, ", \t");
+ skipDirs = SetUtils.setFromArray( skipdirArray );
+ }
+ else
+ {
+ skipDirs = new HashSet();
+ skipDirs.add("CVS");
+ }
+
+ if ( pcl.includesSwitch("javac") )
+ System.err.println("autorecompilation of packages not yet implemented.");
+
+ for (int i = 0, len = srcPkgDirs.length; i < len; ++i)
+ {
+ if (recursive)
+ {
+ if (! recursivePrecheckPackages( codebase, srcPkgDirs[i], outputbase, outputBaseDir ))
+ {
+ System.err.println("One or more of the specifies packages" +
+ " could not be processed. Aborting." +
+ " No files have been modified.");
+ System.exit(-1);
+ }
+ }
+ else
+ {
+ if (! precheckPackage( codebase, srcPkgDirs[i], outputbase, outputBaseDir ))
+ {
+ System.err.println("One or more of the specifies packages" +
+ " could not be processed. Aborting." +
+ " No files have been modified.");
+ System.exit(-1);
+ }
+ }
+ }
+
+ for (int i = 0, len = srcPkgDirs.length; i < len; ++i)
+ {
+ if (recursive)
+ recursiveWriteDebugFiles( codebase, srcPkgDirs[i], outputbase, outputBaseDir );
+ else
+ writeDebugFile( outputbase, srcPkgDirs[i] );
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.err.println();
+ usage();
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " + DebugGen.class.getName() + " \\");
+ System.err.println("\t--codebase=<directory under which packages live> \\ (no default)");
+ System.err.println("\t--packages=<comma separated list of packages> \\ (no default)");
+ System.err.println("\t--debug=<true|false> \\ (no default)");
+ System.err.println("\t--trace=<an int between 0 and 10> \\ (no default)");
+ System.err.println("\t--outputdir=<directory under which to generate> \\ (defaults to same dir as codebase)");
+ System.err.println("\t--recursive \\ (no args)");
+ System.err.println("\t--noclobber \\ (no args)");
+ System.err.println("\t--classname=<class to generate> \\ (defaults to Debug)");
+ System.err.println("\t--skipdirs=<directories that should be skipped> \\ (defaults to CVS)");
+ }
+
+ private static String ify(String str, char fromChar, char toChar)
+ {
+ if ( fromChar == toChar ) return str;
+
+ StringBuffer sb = new StringBuffer(str);
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ if (sb.charAt(i) == fromChar)
+ sb.setCharAt(i, toChar);
+ return sb.toString();
+ }
+
+ private static String platify( String str )
+ {
+ String out;
+ out = ify( str, '/', File.separatorChar );
+ out = ify( out, '\\', File.separatorChar );
+ out = ify( out, ':', File.separatorChar );
+ return out;
+ }
+
+ private static String dottify(String str)
+ { return ify(str, File.separatorChar, '.');}
+
+ private static String sepify(String str)
+ { return ify(str, '.', File.separatorChar);}
+
+ private static boolean recursivePrecheckPackages(String codebase, File srcPkgDir, String outputbase, File outputBaseDir) throws IOException
+ {
+ FileIterator fii = DirectoryDescentUtils.depthFirstEagerDescent( srcPkgDir );
+ while (fii.hasNext())
+ {
+ File pkgDir = fii.nextFile();
+ if (! pkgDir.isDirectory() || skipDirs.contains(pkgDir.getName()))
+ continue;
+
+ File outputDir = outputDir( codebase, pkgDir, outputbase, outputBaseDir );
+ if (! outputDir.exists() && !outputDir.mkdirs() )
+ {
+ System.err.println( "Required output dir: '" + outputDir +
+ "' does not exist, and could not be created.");
+ return false;
+ }
+ if (!precheckOutputPackageDir( outputDir ))
+ return false;
+ }
+ return true;
+ }
+
+ private static File outputDir( String codebase, File srcPkgDir, String outputbase, File outputBaseDir )
+ {
+ //System.err.println("outputDir( " + codebase +", " + srcPkgDir + ", " + outputbase + ", " + outputBaseDir + " )");
+ if (codebase.equals(outputbase))
+ return srcPkgDir;
+
+ String srcPath = srcPkgDir.getPath();
+ if (! srcPath.startsWith( codebase ))
+ {
+ System.err.println(DebugGen.class.getName() + ": program bug. Source package path '" + srcPath +
+ "' does not begin with codebase '" + codebase + "'.");
+ System.exit(-1);
+ }
+ return new File( outputBaseDir, srcPath.substring( codebase.length() ) );
+ }
+
+
+ private static boolean precheckPackage( String codebase, File srcPkgDir, String outputbase, File outputBaseDir ) throws IOException
+ { return precheckOutputPackageDir( outputDir(codebase, srcPkgDir, outputbase, outputBaseDir) ); }
+
+ private static boolean precheckOutputPackageDir(File dir) throws IOException
+ {
+ File outFile = new File( dir, classname + ".java" );
+ if (! dir.canWrite())
+ {
+ System.err.println("File '" + outFile.getPath() + "' is not writable.");
+ return false;
+ }
+ else if (!clobber && outFile.exists())
+ {
+ System.err.println("File '" + outFile.getPath() + "' exists, and we are in noclobber mode.");
+ return false;
+ }
+ else
+ return true;
+ }
+
+ private static void recursiveWriteDebugFiles( String codebase, File srcPkgDir, String outputbase, File outputBaseDir ) throws IOException
+ {
+ FileIterator fii = DirectoryDescentUtils.depthFirstEagerDescent( outputDir( codebase, srcPkgDir, outputbase, outputBaseDir ) );
+ while (fii.hasNext())
+ {
+ File pkgDir = fii.nextFile();
+ //System.err.println("pkgDir: " + pkgDir);
+ if (! pkgDir.isDirectory() || skipDirs.contains(pkgDir.getName()))
+ continue;
+
+ writeDebugFile(outputbase, pkgDir);
+ }
+ }
+
+ private static void writeDebugFile(String outputbase, File pkgDir) throws IOException
+ {
+ //System.err.println("outputbase: " + outputbase + "; pkgDir: " + pkgDir);
+
+ File outFile = new File( pkgDir, classname + ".java" );
+ String pkg = dottify( pkgDir.getPath().substring( outputbase.length() ) );
+ System.err.println("Writing file: " + outFile.getPath());
+ Writer writer = null;
+ try
+ {
+ writer = new OutputStreamWriter(
+ new BufferedOutputStream(
+ new FileOutputStream( outFile ) ), "UTF8" );
+ writer.write("/********************************************************************" + EOL);
+ writer.write(" * This class generated by " + DebugGen.class.getName() + EOL);
+ writer.write(" * and will probably be overwritten by the same! Edit at" + EOL);
+ writer.write(" * YOUR PERIL!!! Hahahahaha." + EOL);
+ writer.write(" ********************************************************************/" + EOL);
+ writer.write(EOL);
+ writer.write("package " + pkg + ';' + EOL);
+ writer.write(EOL);
+ writer.write("import com.mchange.v2.debug.DebugConstants;" + EOL);
+ writer.write(EOL);
+ writer.write("final class " + classname + " implements DebugConstants" + EOL);
+ writer.write("{" + EOL);
+ writer.write("\tfinal static boolean DEBUG = " + debug + ';' + EOL);
+ writer.write("\tfinal static int TRACE = " + traceStr( trace_level ) + ';' + EOL);
+ writer.write(EOL);
+ writer.write("\tprivate " + classname + "()" + EOL);
+ writer.write("\t{}" + EOL);
+ writer.write("}" + EOL);
+ writer.write(EOL);
+ writer.flush();
+ }
+ finally
+ { WriterUtils.attemptClose( writer ); }
+ }
+
+ private static String traceStr( int trace )
+ {
+ if (trace == TRACE_NONE)
+ return "TRACE_NONE";
+ else if (trace == TRACE_MED)
+ return "TRACE_MED";
+ else if (trace == TRACE_MAX)
+ return "TRACE_MAX";
+ else
+ return String.valueOf(trace);
+ }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/io/DirectoryDescentUtils.java b/relproj/debuggen/src/classes/com/mchange/v2/io/DirectoryDescentUtils.java
new file mode 100644
index 0000000..45c3f83
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/io/DirectoryDescentUtils.java
@@ -0,0 +1,130 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.io;
+
+import java.io.*;
+import java.util.*;
+
+public final class DirectoryDescentUtils
+{
+ /**
+ * @return FileIterator over all files and dierctories beneath root
+ */
+ public static FileIterator depthFirstEagerDescent(File root)
+ throws IOException
+ { return depthFirstEagerDescent( root, null, false ); }
+
+ /**
+ * @return FileIterator over all files and directories beneath root that
+ * match filter.
+ *
+ * @param canonical file paths will be canonicalized if true
+ */
+ public static FileIterator depthFirstEagerDescent(File root,
+ FileFilter filter,
+ boolean canonical)
+ throws IOException
+ {
+ List list = new LinkedList();
+ Set seenDirex = new HashSet();
+ depthFirstEagerDescend(root, filter, canonical, list, seenDirex);
+ return new IteratorFileIterator( list.iterator() );
+ }
+
+ public static void addSubtree( File root, FileFilter filter, boolean canonical, Collection addToMe ) throws IOException
+ {
+ Set seenDirex = new HashSet();
+ depthFirstEagerDescend(root, filter, canonical, addToMe, seenDirex);
+ }
+
+ private static void depthFirstEagerDescend(File dir, FileFilter filter, boolean canonical,
+ Collection addToMe, Set seenDirex)
+ throws IOException
+ {
+ String canonicalPath = dir.getCanonicalPath();
+ if (! seenDirex.contains( canonicalPath ) )
+ {
+ if ( filter == null || filter.accept( dir ) )
+ addToMe.add( canonical ? new File( canonicalPath ) : dir );
+ seenDirex.add( canonicalPath );
+ String[] babies = dir.list();
+ for (int i = 0, len = babies.length; i < len; ++i)
+ {
+ File baby = new File(dir, babies[i]);
+ if (baby.isDirectory())
+ depthFirstEagerDescend(baby, filter, canonical, addToMe, seenDirex);
+ else
+ if ( filter == null || filter.accept( baby ) )
+ addToMe.add( canonical ? baby.getCanonicalFile() : baby );
+ }
+ }
+ }
+
+ private static class IteratorFileIterator implements FileIterator
+ {
+ Iterator ii;
+ Object last;
+
+ IteratorFileIterator(Iterator ii)
+ { this.ii = ii; }
+
+ public File nextFile() throws IOException
+ { return (File) next(); }
+
+ public boolean hasNext() throws IOException
+ { return ii.hasNext(); }
+
+ public Object next() throws IOException
+ { return (last = ii.next()); }
+
+ public void remove() throws IOException
+ {
+ if (last != null)
+ {
+ ((File) last).delete();
+ last = null;
+ }
+ else
+ throw new IllegalStateException();
+ }
+
+ public void close() throws IOException
+ {}
+ }
+
+ private DirectoryDescentUtils()
+ {}
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ FileIterator fii = depthFirstEagerDescent( new File(argv[0]) );
+ while (fii.hasNext())
+ System.err.println( fii.nextFile().getPath() );
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+}
diff --git a/relproj/debuggen/src/classes/com/mchange/v2/io/FileIterator.java b/relproj/debuggen/src/classes/com/mchange/v2/io/FileIterator.java
new file mode 100644
index 0000000..aebb117
--- /dev/null
+++ b/relproj/debuggen/src/classes/com/mchange/v2/io/FileIterator.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of debuggen v.0.1.0
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.NoSuchElementException;
+import com.mchange.v1.util.UIterator;
+
+public interface FileIterator extends UIterator
+{
+ public File nextFile() throws IOException;
+
+ public boolean hasNext() throws IOException;
+ public Object next() throws IOException;
+ public void remove() throws IOException;
+ public void close() throws IOException;
+
+ public final static FileIterator EMPTY_FILE_ITERATOR = new FileIterator()
+ {
+ public File nextFile() {throw new NoSuchElementException();}
+ public boolean hasNext() {return false;}
+ public Object next() {throw new NoSuchElementException();}
+ public void remove() {throw new IllegalStateException();}
+ public void close() {}
+ };
+}
diff --git a/relproj/debuggen/version.properties b/relproj/debuggen/version.properties
new file mode 100644
index 0000000..9ffb32d
--- /dev/null
+++ b/relproj/debuggen/version.properties
@@ -0,0 +1,3 @@
+#autogenerated -- do not edit!
+#Mon May 21 15:04:55 EDT 2007
+debuggen.version=0.1.0
diff --git a/src/classes/com/mchange/lang/ByteUtils.java b/src/classes/com/mchange/lang/ByteUtils.java
new file mode 100644
index 0000000..f354fe7
--- /dev/null
+++ b/src/classes/com/mchange/lang/ByteUtils.java
@@ -0,0 +1,111 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.IOException;
+
+public final class ByteUtils
+{
+ public final static short UNSIGNED_MAX_VALUE = (Byte.MAX_VALUE * 2) + 1;
+
+ public static short toUnsigned(byte b)
+ {return (short) (b < 0 ? (UNSIGNED_MAX_VALUE + 1) + b : b);}
+
+ public static String toHexAscii(byte b)
+ {
+ StringWriter sw = new StringWriter(2);
+ addHexAscii(b, sw);
+ return sw.toString();
+ }
+
+ public static String toHexAscii(byte[] bytes)
+ {
+ int len = bytes.length;
+ StringWriter sw = new StringWriter(len * 2);
+ for (int i = 0; i < len; ++i)
+ addHexAscii(bytes[i], sw);
+ return sw.toString();
+ }
+
+ public static byte[] fromHexAscii(String s) throws NumberFormatException
+ {
+ try
+ {
+ int len = s.length();
+ if ((len % 2) != 0)
+ throw new NumberFormatException("Hex ascii must be exactly two digits per byte.");
+
+ int out_len = len / 2;
+ byte[] out = new byte[out_len];
+ int i = 0;
+ StringReader sr = new StringReader(s);
+ while (i < out_len)
+ {
+ int val = (16 * fromHexDigit(sr.read())) + fromHexDigit(sr.read());
+ out[i++] = (byte) val;
+ }
+ return out;
+ }
+ catch (IOException e)
+ {throw new InternalError("IOException reading from StringReader?!?!");}
+ }
+
+ static void addHexAscii(byte b, StringWriter sw)
+ {
+ short ub = toUnsigned(b);
+ int h1 = ub / 16;
+ int h2 = ub % 16;
+ sw.write(toHexDigit(h1));
+ sw.write(toHexDigit(h2));
+ }
+
+ private static int fromHexDigit(int c) throws NumberFormatException
+ {
+ if (c >= 0x30 && c < 0x3A)
+ return c - 0x30;
+ else if (c >= 0x41 && c < 0x47)
+ return c - 0x37;
+ else if (c >= 0x61 && c < 0x67)
+ return c - 0x57;
+ else
+ throw new NumberFormatException('\'' + c + "' is not a valid hexadecimal digit.");
+ }
+
+ /* note: we do no arg. checking, because */
+ /* we only ever call this from addHexAscii() */
+ /* above, and we are sure the args are okay */
+ private static char toHexDigit(int h)
+ {
+ char out;
+ if (h <= 9) out = (char) (h + 0x30);
+ else out = (char) (h + 0x37);
+ //System.err.println(h + ": " + out);
+ return out;
+ }
+
+ private ByteUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/lang/PotentiallySecondary.java b/src/classes/com/mchange/lang/PotentiallySecondary.java
new file mode 100644
index 0000000..e5ed596
--- /dev/null
+++ b/src/classes/com/mchange/lang/PotentiallySecondary.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+/*
+ * An interface to be implemented by Throwable objects
+ * that may indicate a secondary problem, originated by the
+ * more primary, nested, throwable
+ */
+public interface PotentiallySecondary
+{
+ public Throwable getNestedThrowable();
+}
diff --git a/src/classes/com/mchange/lang/PotentiallySecondaryError.java b/src/classes/com/mchange/lang/PotentiallySecondaryError.java
new file mode 100644
index 0000000..71ff8b8
--- /dev/null
+++ b/src/classes/com/mchange/lang/PotentiallySecondaryError.java
@@ -0,0 +1,74 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+import java.io.*;
+
+public class PotentiallySecondaryError extends Error implements PotentiallySecondary
+{
+ final static String NESTED_MSG = ">>>>>>>>>> NESTED THROWABLE >>>>>>>>";
+
+ Throwable nested;
+
+ public PotentiallySecondaryError(String msg, Throwable t)
+ {
+ super(msg);
+ this.nested = t;
+ }
+
+ public PotentiallySecondaryError(Throwable t)
+ {this("", t);}
+
+ public PotentiallySecondaryError(String msg)
+ {this(msg, null);}
+
+ public PotentiallySecondaryError()
+ {this("", null);}
+
+ public Throwable getNestedThrowable()
+ {return nested;}
+
+ public void printStackTrace(PrintWriter pw)
+ {
+ super.printStackTrace(pw);
+ if (nested != null)
+ {
+ pw.println(NESTED_MSG);
+ nested.printStackTrace(pw);
+ }
+ }
+
+ public void printStackTrace(PrintStream ps)
+ {
+ super.printStackTrace(ps);
+ if (nested != null)
+ {
+ ps.println("NESTED_MSG");
+ nested.printStackTrace(ps);
+ }
+ }
+
+ public void printStackTrace()
+ {printStackTrace(System.err);}
+}
diff --git a/src/classes/com/mchange/lang/PotentiallySecondaryException.java b/src/classes/com/mchange/lang/PotentiallySecondaryException.java
new file mode 100644
index 0000000..d86fb65
--- /dev/null
+++ b/src/classes/com/mchange/lang/PotentiallySecondaryException.java
@@ -0,0 +1,91 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+import java.io.*;
+import com.mchange.v2.lang.VersionUtils;
+
+/**
+ * @deprecated jdk 1.4 mow includes this idea as part of the standard
+ * Throwable/Exception classes.
+ */
+public class PotentiallySecondaryException extends Exception implements PotentiallySecondary
+{
+ final static String NESTED_MSG = ">>>>>>>>>> NESTED EXCEPTION >>>>>>>>";
+
+ Throwable nested;
+
+ public PotentiallySecondaryException(String msg, Throwable t)
+ {
+ super(msg);
+ this.nested = t;
+ }
+
+ public PotentiallySecondaryException(Throwable t)
+ {this("", t);}
+
+ public PotentiallySecondaryException(String msg)
+ {this(msg, null);}
+
+ public PotentiallySecondaryException()
+ {this("", null);}
+
+ public Throwable getNestedThrowable()
+ {return nested;}
+
+ private void setNested(Throwable t)
+ {
+ this.nested = t;
+ if ( VersionUtils.isAtLeastJavaVersion14() )
+ this.initCause( t );
+ }
+
+ public void printStackTrace(PrintWriter pw)
+ {
+ super.printStackTrace(pw);
+ if ( !VersionUtils.isAtLeastJavaVersion14() && nested != null)
+ {
+ pw.println(NESTED_MSG);
+ nested.printStackTrace(pw);
+ }
+ }
+
+ public void printStackTrace(PrintStream ps)
+ {
+ super.printStackTrace(ps);
+ if ( !VersionUtils.isAtLeastJavaVersion14() && nested != null)
+ {
+ ps.println("NESTED_MSG");
+ nested.printStackTrace(ps);
+ }
+ }
+
+ public void printStackTrace()
+ {
+ if ( VersionUtils.isAtLeastJavaVersion14() )
+ super.printStackTrace();
+ else
+ this.printStackTrace(System.err);
+ }
+}
diff --git a/src/classes/com/mchange/lang/PotentiallySecondaryRuntimeException.java b/src/classes/com/mchange/lang/PotentiallySecondaryRuntimeException.java
new file mode 100644
index 0000000..bece9d4
--- /dev/null
+++ b/src/classes/com/mchange/lang/PotentiallySecondaryRuntimeException.java
@@ -0,0 +1,74 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+import java.io.*;
+
+public class PotentiallySecondaryRuntimeException extends RuntimeException implements PotentiallySecondary
+{
+ final static String NESTED_MSG = ">>>>>>>>>> NESTED EXCEPTION >>>>>>>>";
+
+ Throwable nested;
+
+ public PotentiallySecondaryRuntimeException(String msg, Throwable t)
+ {
+ super(msg);
+ this.nested = t;
+ }
+
+ public PotentiallySecondaryRuntimeException(Throwable t)
+ {this("", t);}
+
+ public PotentiallySecondaryRuntimeException(String msg)
+ {this(msg, null);}
+
+ public PotentiallySecondaryRuntimeException()
+ {this("", null);}
+
+ public Throwable getNestedThrowable()
+ {return nested;}
+
+ public void printStackTrace(PrintWriter pw)
+ {
+ super.printStackTrace(pw);
+ if (nested != null)
+ {
+ pw.println(NESTED_MSG);
+ nested.printStackTrace(pw);
+ }
+ }
+
+ public void printStackTrace(PrintStream ps)
+ {
+ super.printStackTrace(ps);
+ if (nested != null)
+ {
+ ps.println("NESTED_MSG");
+ nested.printStackTrace(ps);
+ }
+ }
+
+ public void printStackTrace()
+ {printStackTrace(System.err);}
+}
diff --git a/src/classes/com/mchange/lang/ThrowableUtils.java b/src/classes/com/mchange/lang/ThrowableUtils.java
new file mode 100644
index 0000000..7d38d46
--- /dev/null
+++ b/src/classes/com/mchange/lang/ThrowableUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.lang;
+
+import java.io.*;
+
+public final class ThrowableUtils
+{
+ public static String extractStackTrace(Throwable t)
+ {
+ StringWriter me = new StringWriter();
+ PrintWriter pw = new PrintWriter(me);
+ t.printStackTrace(pw);
+ pw.flush();
+ return me.toString();
+ }
+
+ public static boolean isChecked(Throwable t)
+ {
+ return
+ t instanceof Exception &&
+ ! (t instanceof RuntimeException);
+ }
+
+ public static boolean isUnchecked(Throwable t)
+ { return ! isChecked( t ); }
+
+ private ThrowableUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/util/AssertException.java b/src/classes/com/mchange/util/AssertException.java
new file mode 100644
index 0000000..d4ede9f
--- /dev/null
+++ b/src/classes/com/mchange/util/AssertException.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.util;
+
+public class AssertException extends RuntimeException
+{
+ public AssertException(String message)
+ {super(message);}
+
+ public AssertException()
+ {super();}
+}
diff --git a/src/classes/com/mchange/v1/db/sql/ConnectionUtils.java b/src/classes/com/mchange/v1/db/sql/ConnectionUtils.java
new file mode 100644
index 0000000..c616638
--- /dev/null
+++ b/src/classes/com/mchange/v1/db/sql/ConnectionUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.db.sql;
+
+import java.sql.*;
+import com.mchange.v2.log.*;
+
+public final class ConnectionUtils
+{
+ private final static MLogger logger = MLog.getLogger( ConnectionUtils.class );
+
+ /**
+ * @return false iff and Exception occurred while
+ * trying to close this object.
+ */
+ public static boolean attemptClose(Connection con)
+ {
+ try
+ {
+ if (con != null) con.close();
+ //System.err.println("Connection [ " + con + " ] closed.");
+ return true;
+ }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+ //System.err.println("Connection close FAILED.");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Connection close FAILED.", e );
+ return false;
+ }
+ }
+
+ public static boolean attemptRollback(Connection con)
+ {
+ try
+ {
+ if (con != null) con.rollback();
+ return true;
+ }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Rollback FAILED.", e );
+ return false;
+ }
+ }
+
+ private ConnectionUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/db/sql/ResultSetUtils.java b/src/classes/com/mchange/v1/db/sql/ResultSetUtils.java
new file mode 100644
index 0000000..ac07858
--- /dev/null
+++ b/src/classes/com/mchange/v1/db/sql/ResultSetUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.db.sql;
+
+import java.sql.*;
+import com.mchange.v2.log.*;
+
+public final class ResultSetUtils
+{
+ private final static MLogger logger = MLog.getLogger( ResultSetUtils.class );
+
+ /**
+ * @return false iff and Exception occurred while
+ * trying to close this object.
+ */
+ public static boolean attemptClose(ResultSet rs)
+ {
+ try
+ {
+ if (rs != null) rs.close();
+ return true;
+ }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "ResultSet close FAILED.", e );
+ return false;
+ }
+ }
+
+ private ResultSetUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/db/sql/StatementUtils.java b/src/classes/com/mchange/v1/db/sql/StatementUtils.java
new file mode 100644
index 0000000..ee40cf7
--- /dev/null
+++ b/src/classes/com/mchange/v1/db/sql/StatementUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.db.sql;
+
+import java.sql.*;
+import com.mchange.v2.log.*;
+
+public final class StatementUtils
+{
+ private final static MLogger logger = MLog.getLogger( StatementUtils.class );
+
+ /**
+ * @return false iff and Exception occurred while
+ * trying to close this object.
+ */
+ public static boolean attemptClose(Statement stmt)
+ {
+ try
+ {
+ if (stmt != null) stmt.close();
+ return true;
+ }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Statement close FAILED.", e );
+ return false;
+ }
+ }
+
+ private StatementUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/identicator/IdHashKey.java b/src/classes/com/mchange/v1/identicator/IdHashKey.java
new file mode 100644
index 0000000..9fbb0f0
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/IdHashKey.java
@@ -0,0 +1,41 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+abstract class IdHashKey
+{
+ Identicator id;
+
+ public IdHashKey( Identicator id )
+ { this.id = id; }
+
+ public abstract Object getKeyObj();
+
+ public Identicator getIdenticator()
+ { return id; }
+
+ public abstract boolean equals(Object o);
+
+ public abstract int hashCode();
+}
diff --git a/src/classes/com/mchange/v1/identicator/IdHashMap.java b/src/classes/com/mchange/v1/identicator/IdHashMap.java
new file mode 100644
index 0000000..50ffb0b
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/IdHashMap.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+import java.util.*;
+
+public final class IdHashMap extends IdMap implements Map
+{
+ public IdHashMap(Identicator id)
+ { super ( new HashMap(), id ); }
+
+ protected IdHashKey createIdKey(Object o)
+ { return new StrongIdHashKey( o, id ); }
+}
diff --git a/src/classes/com/mchange/v1/identicator/IdMap.java b/src/classes/com/mchange/v1/identicator/IdMap.java
new file mode 100644
index 0000000..54f4de9
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/IdMap.java
@@ -0,0 +1,131 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+import java.util.*;
+import com.mchange.v1.util.*;
+
+/*
+ * Implementation notes: many AbstractMap methods are written in
+ * terms of entrySet(). It is most important to get that right.
+ */
+abstract class IdMap extends AbstractMap implements Map
+{
+ Map inner;
+ Identicator id;
+
+ protected IdMap(Map inner, Identicator id)
+ {
+ this.inner = inner;
+ this.id = id;
+ }
+
+ public Object put(Object key, Object value)
+ { return inner.put( createIdKey( key ), value ); }
+
+ public boolean containsKey(Object key)
+ { return inner.containsKey( createIdKey( key ) ); }
+
+ public Object get(Object key)
+ { return inner.get( createIdKey( key ) ); }
+
+ public Object remove(Object key)
+ { return inner.remove( createIdKey( key ) ); }
+
+ protected Object removeIdHashKey( IdHashKey idhk )
+ { return inner.remove( idhk ); }
+
+ public Set entrySet()
+ { return new UserEntrySet(); }
+
+ protected final Set internalEntrySet()
+ { return inner.entrySet(); }
+
+ protected abstract IdHashKey createIdKey(Object o);
+
+ protected final Entry createIdEntry(Object key, Object val)
+ { return new SimpleMapEntry( createIdKey( key ), val); }
+
+ protected final Entry createIdEntry(Entry entry)
+ { return createIdEntry( entry.getKey(), entry.getValue() ); }
+
+ private final class UserEntrySet extends AbstractSet
+ {
+ Set innerEntries = inner.entrySet();
+
+ public Iterator iterator()
+ {
+ return new WrapperIterator(innerEntries.iterator(), true)
+ {
+ protected Object transformObject(Object o)
+ { return new UserEntry( (Entry) o ); }
+ };
+ }
+
+ public int size()
+ { return innerEntries.size(); }
+
+ public boolean contains(Object o)
+ {
+ if (o instanceof Entry)
+ {
+ Entry entry = (Entry) o;
+ return innerEntries.contains( createIdEntry( entry ) );
+ }
+ else
+ return false;
+ }
+
+ public boolean remove(Object o)
+ {
+ if (o instanceof Entry)
+ {
+ Entry entry = (Entry) o;
+ return innerEntries.remove( createIdEntry( entry ) );
+ }
+ else
+ return false;
+ }
+
+ public void clear()
+ { inner.clear(); }
+ }
+
+ protected static class UserEntry extends AbstractMapEntry
+ {
+ private Entry innerEntry;
+
+ UserEntry(Entry innerEntry)
+ { this.innerEntry = innerEntry; }
+
+ public final Object getKey()
+ { return ((IdHashKey) innerEntry.getKey()).getKeyObj(); }
+
+ public final Object getValue()
+ { return innerEntry.getValue(); }
+
+ public final Object setValue(Object value)
+ { return innerEntry.setValue( value ); }
+ }
+}
diff --git a/src/classes/com/mchange/v1/identicator/IdWeakHashMap.java b/src/classes/com/mchange/v1/identicator/IdWeakHashMap.java
new file mode 100644
index 0000000..d9bdeec
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/IdWeakHashMap.java
@@ -0,0 +1,252 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+import java.lang.ref.*;
+import java.util.*;
+import com.mchange.v1.util.WrapperIterator;
+
+/**
+ * IdWeakHashMap is NOT null-accepting!
+ */
+public final class IdWeakHashMap extends IdMap implements Map
+{
+ ReferenceQueue rq;
+
+ public IdWeakHashMap(Identicator id)
+ {
+ super ( new HashMap(), id );
+ this.rq = new ReferenceQueue();
+ }
+
+ //all methods from Map interface
+ public int size()
+ {
+ // doing cleanCleared() afterwards, as with other methods
+ // would be just as "correct", as weak collections
+ // make no guarantees about when things disappear,
+ // but for size(), it feels a little more accurate
+ // this way.
+ cleanCleared();
+ return super.size();
+ }
+
+ public boolean isEmpty()
+ {
+ try
+ { return super.isEmpty(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public boolean containsKey(Object o)
+ {
+ try
+ { return super.containsKey( o ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public boolean containsValue(Object o)
+ {
+ try
+ { return super.containsValue( o ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public Object get(Object o)
+ {
+ try
+ { return super.get( o ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public Object put(Object k, Object v)
+ {
+ try
+ { return super.put( k , v ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public Object remove(Object o)
+ {
+ try
+ { return super.remove( o ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public void putAll(Map m)
+ {
+ try
+ { super.putAll( m ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public void clear()
+ {
+ try
+ { super.clear(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public Set keySet()
+ {
+ try
+ { return super.keySet(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public Collection values()
+ {
+ try
+ { return super.values(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ /*
+ * entrySet() is the basis of the implementation of the other
+ * Collection returning methods. Get this right and the rest
+ * follow.
+ */
+ public Set entrySet()
+ {
+ try
+ { return new WeakUserEntrySet(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public boolean equals(Object o)
+ {
+ try
+ { return super.equals( o ); }
+ finally
+ { cleanCleared(); }
+ }
+
+ public int hashCode()
+ {
+ try
+ { return super.hashCode(); }
+ finally
+ { cleanCleared(); }
+ }
+
+ //internal methods
+ protected IdHashKey createIdKey(Object o)
+ { return new WeakIdHashKey( o, id, rq ); }
+
+ private void cleanCleared()
+ {
+ WeakIdHashKey.Ref ref;
+ while ((ref = (WeakIdHashKey.Ref) rq.poll()) != null)
+ this.removeIdHashKey( ref.getKey() );
+ }
+
+ private final class WeakUserEntrySet extends AbstractSet
+ {
+ Set innerEntries = internalEntrySet();
+
+ public Iterator iterator()
+ {
+ try
+ {
+ return new WrapperIterator(innerEntries.iterator(), true)
+ {
+ protected Object transformObject(Object o)
+ {
+ Entry innerEntry = (Entry) o;
+ final Object userKey = ((IdHashKey) innerEntry.getKey()).getKeyObj();
+ if (userKey == null)
+ return WrapperIterator.SKIP_TOKEN;
+ else
+ return new UserEntry( innerEntry )
+ { Object preventRefClear = userKey; };
+ }
+ };
+ }
+ finally
+ { cleanCleared(); }
+ }
+
+ public int size()
+ {
+ // doing cleanCleared() afterwards, as with other methods
+ // would be just as "correct", as weak collections
+ // make no guarantees about when things disappear,
+ // but for size(), it feels a little more accurate
+ // this way.
+ cleanCleared();
+ return innerEntries.size();
+ }
+
+ public boolean contains(Object o)
+ {
+ try
+ {
+ if (o instanceof Entry)
+ {
+ Entry entry = (Entry) o;
+ return innerEntries.contains( createIdEntry( entry ) );
+ }
+ else
+ return false;
+ }
+ finally
+ { cleanCleared(); }
+ }
+
+ public boolean remove(Object o)
+ {
+ try
+ {
+ if (o instanceof Entry)
+ {
+ Entry entry = (Entry) o;
+ return innerEntries.remove( createIdEntry( entry ) );
+ }
+ else
+ return false;
+ }
+ finally
+ { cleanCleared(); }
+ }
+
+ public void clear()
+ {
+ try
+ { inner.clear(); }
+ finally
+ { cleanCleared(); }
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v1/identicator/Identicator.java b/src/classes/com/mchange/v1/identicator/Identicator.java
new file mode 100644
index 0000000..771d9ab
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/Identicator.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+/**
+ * Identicators should be immutable (sharable).
+ * Cloned collections share identicators
+ */
+public interface Identicator
+{
+ public boolean identical(Object a, Object b);
+ public int hash(Object o);
+}
diff --git a/src/classes/com/mchange/v1/identicator/StrongIdHashKey.java b/src/classes/com/mchange/v1/identicator/StrongIdHashKey.java
new file mode 100644
index 0000000..e5aa188
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/StrongIdHashKey.java
@@ -0,0 +1,52 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+// revisit equals() if ever made non-final
+
+final class StrongIdHashKey extends IdHashKey
+{
+ Object keyObj;
+
+ public StrongIdHashKey(Object keyObj, Identicator id)
+ {
+ super( id );
+ this.keyObj = keyObj;
+ }
+
+ public Object getKeyObj()
+ { return keyObj; }
+
+ public boolean equals(Object o)
+ {
+ // fast type-exact match for final class
+ if (o instanceof StrongIdHashKey)
+ return id.identical( keyObj, ((StrongIdHashKey) o).keyObj );
+ else
+ return false;
+ }
+
+ public int hashCode()
+ { return id.hash( keyObj ); }
+}
diff --git a/src/classes/com/mchange/v1/identicator/WeakIdHashKey.java b/src/classes/com/mchange/v1/identicator/WeakIdHashKey.java
new file mode 100644
index 0000000..11a62c4
--- /dev/null
+++ b/src/classes/com/mchange/v1/identicator/WeakIdHashKey.java
@@ -0,0 +1,85 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.identicator;
+
+import java.lang.ref.*;
+
+// revisit equals() if ever made non-final
+
+final class WeakIdHashKey extends IdHashKey
+{
+ Ref keyRef;
+ int hash;
+
+ public WeakIdHashKey(Object keyObj, Identicator id, ReferenceQueue rq)
+ {
+ super( id );
+
+ if (keyObj == null)
+ throw new UnsupportedOperationException("Collection does not accept nulls!");
+
+ this.keyRef = new Ref( keyObj, rq );
+ this.hash = id.hash( keyObj );
+ }
+
+ public Ref getInternalRef()
+ { return this.keyRef; }
+
+ public Object getKeyObj()
+ { return keyRef.get(); }
+
+ public boolean equals(Object o)
+ {
+ // fast type-exact match for final class
+ if (o instanceof WeakIdHashKey)
+ {
+ WeakIdHashKey other = (WeakIdHashKey) o;
+ if (this.keyRef == other.keyRef)
+ return true;
+ else
+ {
+ Object myKeyObj = this.keyRef.get();
+ Object oKeyObj = other.keyRef.get();
+ if (myKeyObj == null || oKeyObj == null)
+ return false;
+ else
+ return id.identical( myKeyObj, oKeyObj );
+ }
+ }
+ else
+ return false;
+ }
+
+ public int hashCode()
+ { return hash; }
+
+ class Ref extends WeakReference
+ {
+ public Ref( Object referant, ReferenceQueue rq )
+ { super( referant, rq ); }
+
+ WeakIdHashKey getKey()
+ { return WeakIdHashKey.this; }
+ }
+}
diff --git a/src/classes/com/mchange/v1/io/InputStreamUtils.java b/src/classes/com/mchange/v1/io/InputStreamUtils.java
new file mode 100644
index 0000000..b2c6371
--- /dev/null
+++ b/src/classes/com/mchange/v1/io/InputStreamUtils.java
@@ -0,0 +1,142 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.io;
+
+import java.io.*;
+import com.mchange.v2.log.*;
+
+public final class InputStreamUtils
+{
+ private final static MLogger logger = MLog.getLogger( InputStreamUtils.class );
+
+ public static boolean compare(InputStream is1, InputStream is2, long num_bytes) throws IOException
+ {
+ int b;
+ for (long num_read = 0; num_read < num_bytes; ++num_read)
+ {
+ if ((b = is1.read()) != is2.read())
+ return false;
+ else if (b < 0) //both EOF
+ break;
+ }
+ return true;
+ }
+
+ public static boolean compare(InputStream is1, InputStream is2) throws IOException
+ {
+ int b = 0;
+ while (b >= 0)
+ if ((b = is1.read()) != is2.read())
+ return false;
+ return true;
+ }
+
+ public static byte[] getBytes(InputStream is, int max_len) throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(max_len);
+ for(int i = 0, b = is.read(); b >= 0 && i < max_len; b = is.read(), ++i)
+ baos.write(b);
+ return baos.toByteArray();
+ }
+
+ public static byte[] getBytes(InputStream is) throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for(int b = is.read(); b >= 0; b = is.read()) baos.write(b);
+ return baos.toByteArray();
+ }
+
+ public static String getContentsAsString(InputStream is, String enc)
+ throws IOException, UnsupportedEncodingException
+ {return new String(getBytes(is), enc);}
+
+ public static String getContentsAsString(InputStream is)
+ throws IOException
+ {
+ try
+ {return getContentsAsString(is, System.getProperty("file.encoding", "8859_1"));}
+ catch (UnsupportedEncodingException e)
+ {
+ throw new InternalError("You have no default character encoding, and " +
+ "iso-8859-1 is unsupported?!?!");
+ }
+ }
+
+ public static String getContentsAsString(InputStream is, int max_len, String enc)
+ throws IOException, UnsupportedEncodingException
+ {return new String(getBytes(is, max_len), enc);}
+
+ public static String getContentsAsString(InputStream is, int max_len)
+ throws IOException
+ {
+ try
+ {return getContentsAsString(is, max_len, System.getProperty("file.encoding", "8859_1"));}
+ catch (UnsupportedEncodingException e)
+ {
+ throw new InternalError("You have no default character encoding, and " +
+ "iso-8859-1 is unsupported?!?!");
+ }
+ }
+
+ public static InputStream getEmptyInputStream()
+ {return EMPTY_ISTREAM;}
+
+ public static void attemptClose(InputStream is)
+ {
+ try
+ {if (is != null) is.close();}
+ catch (IOException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "InputStream close FAILED.", e );
+ }
+ }
+
+ public static void skipFully(InputStream is, long num_bytes) throws EOFException, IOException
+ {
+ long num_skipped = 0;
+ while (num_skipped < num_bytes)
+ {
+ long just_skipped = is.skip(num_bytes - num_skipped);
+ if (just_skipped > 0)
+ num_skipped += just_skipped;
+ else
+ {
+ int test_byte = is.read();
+ if (is.read() < 0)
+ throw new EOFException("Skipped only " + num_skipped + " bytes to end of file.");
+ else
+ ++num_skipped;
+ }
+ }
+ }
+
+ /* Is it appropriate to treat this as a constant? Is it */
+ /* in any discernable sense changed by read() operations */
+ private static InputStream EMPTY_ISTREAM = new ByteArrayInputStream(new byte[0]);
+
+ private InputStreamUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/io/OutputStreamUtils.java b/src/classes/com/mchange/v1/io/OutputStreamUtils.java
new file mode 100644
index 0000000..435b96f
--- /dev/null
+++ b/src/classes/com/mchange/v1/io/OutputStreamUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.io;
+
+import com.mchange.v2.log.*;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public final class OutputStreamUtils
+{
+ private final static MLogger logger = MLog.getLogger( OutputStreamUtils.class );
+
+ public static void attemptClose(OutputStream os)
+ {
+ try
+ {if (os != null) os.close();}
+ catch (IOException e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "OutputStream close FAILED.", e );
+ //e.printStackTrace();
+ }
+ }
+
+ private OutputStreamUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/jvm/InternalNameUtils.java b/src/classes/com/mchange/v1/jvm/InternalNameUtils.java
new file mode 100644
index 0000000..0390b19
--- /dev/null
+++ b/src/classes/com/mchange/v1/jvm/InternalNameUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.jvm;
+
+public final class InternalNameUtils
+{
+ public static String dottifySlashesAndDollarSigns(String str)
+ { return _dottifySlashesAndDollarSigns( str ).toString(); }
+
+ public static String decodeType(String internalrep) throws TypeFormatException
+ { return _decodeType(internalrep).toString(); }
+
+ public static String decodeTypeList(String internalrep) throws TypeFormatException
+ {
+ StringBuffer sb = new StringBuffer(64);
+ _decodeTypeList(internalrep, 0, sb);
+ return sb.toString();
+ }
+
+ public static boolean isPrimitive(char rep)
+ {
+ return (rep == 'Z' ||
+ rep == 'B' ||
+ rep == 'C' ||
+ rep == 'S' ||
+ rep == 'I' ||
+ rep == 'J' ||
+ rep == 'F' ||
+ rep == 'D' ||
+ rep == 'V');
+ }
+
+ private static void _decodeTypeList(String typeList, int start_pos, StringBuffer appendTo) throws TypeFormatException
+ {
+ if (appendTo.length() != 0)
+ appendTo.append(' ');
+
+ char c = typeList.charAt(start_pos);
+ if (isPrimitive(c))
+ {
+ appendTo.append( _decodeType( typeList.substring(start_pos, start_pos + 1) ) );
+ ++start_pos;
+ }
+ else
+ {
+ int stop_index;
+
+ if (c == '[')
+ {
+ int finger = start_pos + 1;
+ while (typeList.charAt(finger) == '[')
+ ++finger;
+ if (typeList.charAt(finger) == 'L')
+ {
+ ++finger;
+ while (typeList.charAt( finger ) != ';')
+ ++finger;
+ }
+ stop_index = finger;
+ }
+ else
+ {
+ stop_index = typeList.indexOf(';', start_pos);
+ if (stop_index < 0)
+ throw new TypeFormatException(typeList.substring(start_pos) + " is neither a primitive nor semicolon terminated!");
+ }
+
+ appendTo.append( _decodeType( typeList.substring( start_pos, (start_pos = stop_index + 1) ) ) );
+ }
+ if (start_pos < typeList.length())
+ {
+ appendTo.append(',');
+ _decodeTypeList(typeList, start_pos, appendTo);
+ }
+ }
+
+ private static StringBuffer _decodeType(String type) throws TypeFormatException
+ {
+// System.err.println("_decodeType: " + type);
+
+ int array_level = 0;
+ StringBuffer out;
+
+ char c = type.charAt(0);
+
+ switch (c)
+ {
+ case 'Z':
+ out = new StringBuffer("boolean");
+ break;
+ case 'B':
+ out = new StringBuffer("byte");
+ break;
+ case 'C':
+ out = new StringBuffer("char");
+ break;
+ case 'S':
+ out = new StringBuffer("short");
+ break;
+ case 'I':
+ out = new StringBuffer("int");
+ break;
+ case 'J':
+ out = new StringBuffer("long");
+ break;
+ case 'F':
+ out = new StringBuffer("float");
+ break;
+ case 'D':
+ out = new StringBuffer("double");
+ break;
+ case 'V':
+ out = new StringBuffer("void");
+ break;
+ case '[':
+ ++array_level;
+ out = _decodeType(type.substring(1));
+ break;
+ case 'L':
+ out = _decodeSimpleClassType(type);
+ break;
+ default:
+ throw new TypeFormatException(type + " is not a valid inernal type name.");
+ }
+ for (int i = 0; i < array_level; ++i)
+ out.append("[]");
+ return out;
+ }
+
+ private static StringBuffer _decodeSimpleClassType(String type) throws TypeFormatException
+ {
+ int len = type.length();
+ if (type.charAt(0) != 'L' || type.charAt( len - 1 ) != ';')
+ throw new TypeFormatException(type + " is not a valid representation of a simple class type.");
+
+ return _dottifySlashesAndDollarSigns( type.substring(1, len - 1) );
+ }
+
+ private static StringBuffer _dottifySlashesAndDollarSigns(String s)
+ {
+ StringBuffer sb = new StringBuffer( s );
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ {
+ char c = sb.charAt(i);
+ if ( c == '/' || c == '$')
+ sb.setCharAt(i, '.');
+ }
+ return sb;
+ }
+
+ private InternalNameUtils()
+ {}
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ //System.out.println( decodeType( (new String[0]).getClass().getName() ) );
+ System.out.println(decodeTypeList(argv[0]));
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+// public static String dottifySlashes(String name)
+// {
+// StringBuffer sb = new StringBuffer(name);
+
+// int pos = name.indexOf('/', 0);
+// while (pos > 0)
+// {
+// sb.setCharAt(pos, '.');
+// ps = name.indexOf('/', ++pos);
+// }
+// return sb.toString();
+// }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/classes/com/mchange/v1/jvm/TypeFormatException.java b/src/classes/com/mchange/v1/jvm/TypeFormatException.java
new file mode 100644
index 0000000..a047e86
--- /dev/null
+++ b/src/classes/com/mchange/v1/jvm/TypeFormatException.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.jvm;
+
+public class TypeFormatException extends Exception
+{
+ TypeFormatException()
+ { super(); }
+
+ TypeFormatException( String msg )
+ { super( msg ); }
+}
diff --git a/src/classes/com/mchange/v1/lang/AmbiguousClassNameException.java b/src/classes/com/mchange/v1/lang/AmbiguousClassNameException.java
new file mode 100644
index 0000000..9c23ad7
--- /dev/null
+++ b/src/classes/com/mchange/v1/lang/AmbiguousClassNameException.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+public class AmbiguousClassNameException extends Exception
+{
+ AmbiguousClassNameException(String simpleName, Class c1, Class c2)
+ {
+ super( simpleName +
+ " could refer either to " + c1.getName() +
+ " or " + c2.getName() );
+ }
+}
diff --git a/src/classes/com/mchange/v1/lang/BooleanUtils.java b/src/classes/com/mchange/v1/lang/BooleanUtils.java
new file mode 100644
index 0000000..7ee9bca
--- /dev/null
+++ b/src/classes/com/mchange/v1/lang/BooleanUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+public final class BooleanUtils
+{
+ public static boolean parseBoolean(String str) throws IllegalArgumentException
+ {
+ if (str.equals("true"))
+ return true;
+ else if (str.equals("false"))
+ return false;
+ else
+ throw new IllegalArgumentException("\"str\" is neither \"true\" nor \"false\".");
+ }
+
+ private BooleanUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/lang/ClassUtils.java b/src/classes/com/mchange/v1/lang/ClassUtils.java
new file mode 100644
index 0000000..1c32b35
--- /dev/null
+++ b/src/classes/com/mchange/v1/lang/ClassUtils.java
@@ -0,0 +1,217 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+import java.util.*;
+import com.mchange.v1.jvm.*;
+
+public final class ClassUtils
+{
+ final static String[] EMPTY_SA = new String[0];
+
+ static Map primitivesToClasses;
+
+ static
+ {
+ HashMap tmp = new HashMap();
+ tmp.put( "boolean", boolean.class );
+ tmp.put( "int", int.class );
+ tmp.put( "char", char.class );
+ tmp.put( "short", short.class );
+ tmp.put( "int", int.class );
+ tmp.put( "long", long.class );
+ tmp.put( "float", float.class );
+ tmp.put( "double", double.class );
+ tmp.put( "void", void.class );
+
+ primitivesToClasses = Collections.unmodifiableMap( tmp );
+ }
+
+ public static Set allAssignableFrom(Class type)
+ {
+ Set out = new HashSet();
+
+ //type itself and superclasses (if any)
+ for (Class cl = type; cl != null; cl = cl.getSuperclass())
+ out.add( cl );
+
+ //super interfaces (if any)
+ addSuperInterfacesToSet( type, out );
+ return out;
+ }
+
+ public static String simpleClassName(Class cl)
+ {
+ String scn;
+ int array_level = 0;
+ while (cl.isArray())
+ {
+ ++array_level;
+ cl = cl.getComponentType();
+ }
+ scn = simpleClassName( cl.getName() );
+ if ( array_level > 0 )
+ {
+ StringBuffer sb = new StringBuffer(16);
+ sb.append( scn );
+ for( int i = 0; i < array_level; ++i)
+ sb.append("[]");
+ return sb.toString();
+ }
+ else
+ return scn;
+ }
+
+ private static String simpleClassName(String fqcn)
+ {
+ int pkgdot = fqcn.lastIndexOf('.');
+ if (pkgdot < 0)
+ return fqcn;
+ String scn = fqcn.substring(pkgdot + 1);
+ if (scn.indexOf('$') >= 0)
+ {
+ StringBuffer sb = new StringBuffer(scn);
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ {
+ if (sb.charAt(i) == '$')
+ sb.setCharAt(i, '.');
+ }
+ return sb.toString();
+ }
+ else
+ return scn;
+ }
+
+ public static boolean isPrimitive(String typeStr)
+ { return (primitivesToClasses.get( typeStr ) != null); }
+
+ public static Class classForPrimitive(String typeStr)
+ { return (Class) primitivesToClasses.get( typeStr ); }
+
+ public static Class forName(String fqcnOrPrimitive ) throws ClassNotFoundException
+ {
+ Class out = classForPrimitive( fqcnOrPrimitive );
+ if (out == null)
+ out = Class.forName( fqcnOrPrimitive );
+ return out;
+ }
+
+ public static Class forName( String fqOrSimple, String[] importPkgs, String[] importClasses )
+ throws AmbiguousClassNameException, ClassNotFoundException
+ {
+ try
+ { return Class.forName( fqOrSimple ); }
+ catch ( ClassNotFoundException e )
+ { return classForSimpleName( fqOrSimple, importPkgs, importClasses ); }
+ }
+
+ public static Class classForSimpleName( String simpleName, String[] importPkgs, String[] importClasses )
+ throws AmbiguousClassNameException, ClassNotFoundException
+ {
+ Set checkSet = new HashSet();
+ Class out = classForPrimitive( simpleName );
+
+ if (out == null)
+ {
+ if (importPkgs == null)
+ importPkgs = EMPTY_SA;
+
+ if (importClasses == null)
+ importClasses = EMPTY_SA;
+
+ for (int i = 0, len = importClasses.length; i < len; ++i)
+ {
+ String importSimpleName = fqcnLastElement( importClasses[i] );
+ if (! checkSet.add( importSimpleName ) )
+ throw new IllegalArgumentException("Duplicate imported classes: " +
+ importSimpleName);
+ if ( simpleName.equals( importSimpleName ) )
+ //we won't duplicate assign. we'd have caught it above
+ out = Class.forName( importClasses[i] );
+ }
+ if (out == null)
+ {
+ try { out = Class.forName("java.lang." + simpleName); }
+ catch (ClassNotFoundException e)
+ { /* just means we haven't found it yet */ }
+
+ for (int i = 0, len = importPkgs.length; i < len; ++i)
+ {
+ try
+ {
+ String tryClass = importPkgs[i] + '.' + simpleName;
+ Class test = Class.forName( tryClass );
+ if ( out == null )
+ out = test;
+ else
+ throw new AmbiguousClassNameException( simpleName, out, test );
+ }
+ catch (ClassNotFoundException e)
+ { /* just means we haven't found it yet */ }
+ }
+ }
+ }
+ if (out == null)
+ throw new ClassNotFoundException( "Could not find a class whose unqualified name is \042" +
+ simpleName + "\042 with the imports supplied. Import packages are " +
+ Arrays.asList( importPkgs ) + "; class imports are " +
+ Arrays.asList( importClasses ) );
+ else
+ return out;
+ }
+
+ public static String resolvableTypeName( Class type, String[] importPkgs, String[] importClasses )
+ throws ClassNotFoundException
+ {
+ String simpleName = simpleClassName( type );
+ try
+ { classForSimpleName( simpleName, importPkgs, importClasses ); }
+ catch ( AmbiguousClassNameException e )
+ { return type.getName(); }
+ return simpleName;
+ }
+
+ public static String fqcnLastElement(String fqcn)
+ {
+ int pkgdot = fqcn.lastIndexOf('.');
+ if (pkgdot < 0)
+ return fqcn;
+ return fqcn.substring(pkgdot + 1);
+ }
+
+
+ /* does not add type itself, only its superinterfaces */
+ private static void addSuperInterfacesToSet(Class type, Set set)
+ {
+ Class[] ifaces = type.getInterfaces();
+ for (int i = 0, len = ifaces.length; i < len; ++i)
+ {
+ set.add( ifaces[i] );
+ addSuperInterfacesToSet( ifaces[i], set );
+ }
+ }
+
+ private ClassUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/lang/GentleThread.java b/src/classes/com/mchange/v1/lang/GentleThread.java
new file mode 100644
index 0000000..fd714ff
--- /dev/null
+++ b/src/classes/com/mchange/v1/lang/GentleThread.java
@@ -0,0 +1,98 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+/**
+ * an abstract Thread class that provides
+ * utilities for easily defining Threads with
+ * safe versions of the deprecated thread
+ * methods stop(), resume(), and start()
+ */
+public abstract class GentleThread extends Thread
+{
+ boolean should_stop = false;
+ boolean should_suspend = false;
+
+ public GentleThread()
+ { super(); }
+
+ public GentleThread(String name)
+ { super( name ); }
+
+ public abstract void run();
+
+ /**
+ * a safe method for stopping properly implemented GentleThreads
+ */
+ public synchronized void gentleStop()
+ {should_stop = true;}
+
+ /**
+ * a safe method for suspending properly implemented GentleThreads
+ */
+ public synchronized void gentleSuspend()
+ {should_suspend = true;}
+
+ /**
+ * a safe method for resuming properly implemented GentleThreads
+ */
+ public synchronized void gentleResume()
+ {
+ should_suspend = false;
+ this.notifyAll();
+ }
+
+ /**
+ * tests whether the thread should stop.
+ * Subclasses should call this method periodically in
+ * their run method, and return from run() is the
+ * method returns true.
+ */
+ protected synchronized boolean shouldStop()
+ {return should_stop;}
+
+ /**
+ * tests whether the thread should suspend.
+ * Subclasses rarely call this method directly,
+ * and should call allowSuspend() periodically
+ * instead.
+ *
+ * @see #allowSuspend
+ */
+ protected synchronized boolean shouldSuspend()
+ {return should_suspend;}
+
+ /**
+ * tests whether the thread should suspend,
+ * and causes to the thread to pause if appropriate.
+ * Subclasses should call this method periodically
+ * in their run method to, um, allow suspension.
+ * Threads paused by allowSuspend() will be properly
+ * awoken by gentleResume()
+ *
+ * @see #gentleResume
+ */
+ protected synchronized void allowSuspend() throws InterruptedException
+ {while (should_suspend) this.wait();}
+}
diff --git a/src/classes/com/mchange/v1/lang/NullUtils.java b/src/classes/com/mchange/v1/lang/NullUtils.java
new file mode 100644
index 0000000..55c3081
--- /dev/null
+++ b/src/classes/com/mchange/v1/lang/NullUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.lang;
+
+/**
+ * @deprecated use com.mchange.v2.ObjectUtils.eqOrBothNull()
+ */
+public final class NullUtils
+{
+ public static boolean equalsOrBothNull(Object a, Object b)
+ {
+ if (a == b)
+ return true;
+ else if (a == null)
+ return false;
+ else
+ return a.equals( b );
+ }
+
+ private NullUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/util/AbstractMapEntry.java b/src/classes/com/mchange/v1/util/AbstractMapEntry.java
new file mode 100644
index 0000000..7e91019
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/AbstractMapEntry.java
@@ -0,0 +1,56 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.Map;
+import com.mchange.v2.lang.ObjectUtils;
+
+public abstract class AbstractMapEntry implements Map.Entry
+{
+ public abstract Object getKey();
+
+ public abstract Object getValue();
+
+ public abstract Object setValue(Object value);
+
+ public boolean equals(Object o)
+ {
+ if (o instanceof Map.Entry)
+ {
+ Map.Entry other = (Map.Entry) o;
+ return
+ ObjectUtils.eqOrBothNull( this.getKey(), other.getKey() ) &&
+ ObjectUtils.eqOrBothNull( this.getValue(), other.getValue() );
+ }
+ else
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return
+ (this.getKey() == null ? 0 : this.getKey().hashCode()) ^
+ (this.getValue() == null ? 0 : this.getValue().hashCode());
+ }
+}
diff --git a/src/classes/com/mchange/v1/util/ArrayUtils.java b/src/classes/com/mchange/v1/util/ArrayUtils.java
new file mode 100644
index 0000000..4bee927
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/ArrayUtils.java
@@ -0,0 +1,300 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import com.mchange.v2.lang.ObjectUtils;
+
+public final class ArrayUtils
+{
+ /**
+ * The array may contain nulls, but <TT>o</TT>
+ * must be non-null.
+ */
+ public static int indexOf(Object[] array, Object o)
+ {
+ for (int i = 0, len = array.length; i < len; ++i)
+ if (o.equals(array[i])) return i;
+ return -1;
+ }
+
+ public static int identityIndexOf(Object[] array, Object o)
+ {
+ for (int i = 0, len = array.length; i < len; ++i)
+ if (o == array[i]) return i;
+ return -1;
+ }
+
+ public static boolean startsWith( byte[] checkMe, byte[] maybePrefix )
+ {
+ int cm_len = checkMe.length;
+ int mp_len = maybePrefix.length;
+ if (cm_len < mp_len)
+ return false;
+ for (int i = 0; i < mp_len; ++i)
+ if (checkMe[i] != maybePrefix[i])
+ return false;
+ return true;
+ }
+
+ /**
+ * returns a hash-code for an array consistent with Arrays.equals( ... )
+ */
+ public static int hashArray(Object[] oo)
+ {
+ int len = oo.length;
+ int out = len;
+ for (int i = 0; i < len; ++i)
+ {
+ //we rotate the bits of the element hashes
+ //around so that the hash has some loaction
+ //dependency
+ int elem_hash = ObjectUtils.hashOrZero(oo[i]);
+ int rot = i % 32;
+ int rot_hash = elem_hash >>> rot;
+ rot_hash |= elem_hash << (32 - rot);
+ out ^= rot_hash;
+ }
+ return out;
+ }
+
+ /**
+ * returns a hash-code for an array consistent with Arrays.equals( ... )
+ */
+ public static int hashArray(int[] ii)
+ {
+ int len = ii.length;
+ int out = len;
+ for (int i = 0; i < len; ++i)
+ {
+ //we rotate the bits of the element hashes
+ //around so that the hash has some loaction
+ //dependency
+ int elem_hash = ii[i];
+ int rot = i % 32;
+ int rot_hash = elem_hash >>> rot;
+ rot_hash |= elem_hash << (32 - rot);
+ out ^= rot_hash;
+ }
+ return out;
+ }
+
+ public static int hashOrZeroArray(Object[] oo)
+ { return (oo == null ? 0 : hashArray(oo)); }
+
+ public static int hashOrZeroArray(int[] ii)
+ { return (ii == null ? 0 : hashArray(ii)); }
+
+ /**
+ * @deprecated use the various toString(T[] methods)
+ */
+ public static String stringifyContents(Object[] array)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ for (int i = 0, len = array.length; i < len; ++i)
+ {
+ if (i != 0)
+ sb.append(", ");
+ sb.append( array[i].toString() );
+ }
+ sb.append(" ]");
+ return sb.toString();
+ }
+
+ //these methods are obsoleted by Arrays.toString() in jdk1.5, but
+ //for libs that support older VMs...
+ private static String toString(String[] strings, int guessed_len)
+ {
+ StringBuffer sb = new StringBuffer( guessed_len );
+ boolean first = true;
+ sb.append('[');
+ for (int i = 0, len = strings.length; i < len; ++i)
+ {
+ if (first)
+ first = false;
+ else
+ sb.append(',');
+ sb.append( strings[i] );
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static String toString(boolean[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(byte[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(char[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(short[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(int[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(long[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(float[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(double[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+ public static String toString(Object[] arr)
+ {
+ String[] strings = new String[arr.length];
+ int chars = 0;
+ for(int i = 0, len = arr.length; i < len; ++i)
+ {
+ String str;
+ Object o = arr[i];
+ if (o instanceof Object[])
+ str = toString((Object[]) o);
+ else if (o instanceof double[])
+ str = toString((double[]) o);
+ else if (o instanceof float[])
+ str = toString((float[]) o);
+ else if (o instanceof long[])
+ str = toString((long[]) o);
+ else if (o instanceof int[])
+ str = toString((int[]) o);
+ else if (o instanceof short[])
+ str = toString((short[]) o);
+ else if (o instanceof char[])
+ str = toString((char[]) o);
+ else if (o instanceof byte[])
+ str = toString((byte[]) o);
+ else if (o instanceof boolean[])
+ str = toString((boolean[]) o);
+ else
+ str = String.valueOf(arr[i]);
+ chars += str.length();
+ strings[i] = str;
+ }
+ return toString(strings, chars + arr.length + 1);
+ }
+
+
+ private ArrayUtils()
+ {}
+
+ /*
+ public static void main(String[] argv)
+ {
+ int[] is = {1,2,3,4};
+ String[] ss = {"Hello", "There"};
+ Object[] os = {"Poop", is, ss, new Thread()};
+
+ System.out.println( toString(is) );
+ System.out.println( toString(ss) );
+ System.out.println( toString(os) );
+ }
+ */
+}
+
+
diff --git a/src/classes/com/mchange/v1/util/ClosableResource.java b/src/classes/com/mchange/v1/util/ClosableResource.java
new file mode 100644
index 0000000..a79c44a
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/ClosableResource.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+/**
+ * An interface intended to be shared by the many sorts
+ * of objects that offer some kind of close method to
+ * cleanup resources they might be using. (I wish Sun
+ * had defined, and used, such an interface in the standard
+ * APIs.
+ */
+public interface ClosableResource
+{
+ /**
+ * forces the release of any resources that might be
+ * associated with this object.
+ */
+ public void close() throws Exception;
+}
diff --git a/src/classes/com/mchange/v1/util/ClosableResourceUtils.java b/src/classes/com/mchange/v1/util/ClosableResourceUtils.java
new file mode 100644
index 0000000..a253914
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/ClosableResourceUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import com.mchange.v2.log.*;
+
+public final class ClosableResourceUtils
+{
+ private final static MLogger logger = MLog.getLogger( ClosableResourceUtils.class );
+
+ /**
+ * attempts to close the specified resource,
+ * logging any exception or failure, but allowing
+ * control flow to proceed normally regardless.
+ */
+ public static Exception attemptClose(ClosableResource cr)
+ {
+ try
+ {
+ if (cr != null) cr.close();
+ return null;
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "CloseableResource close FAILED.", e );
+ return e;
+ }
+ }
+
+ private ClosableResourceUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v1/util/DebugUtils.java b/src/classes/com/mchange/v1/util/DebugUtils.java
new file mode 100644
index 0000000..984a30d
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/DebugUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import com.mchange.util.AssertException;
+
+public class DebugUtils
+{
+ private DebugUtils() {}
+
+ public static void myAssert(boolean bool)
+ {if (!bool) throw new AssertException();}
+
+ public static void myAssert(boolean bool, String message)
+ {if (!bool) throw new AssertException(message);}
+}
+
diff --git a/src/classes/com/mchange/v1/util/SimpleMapEntry.java b/src/classes/com/mchange/v1/util/SimpleMapEntry.java
new file mode 100644
index 0000000..2a04667
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/SimpleMapEntry.java
@@ -0,0 +1,51 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.Map;
+
+public class SimpleMapEntry extends AbstractMapEntry implements Map.Entry
+{
+ Object key;
+ Object value;
+
+ public SimpleMapEntry(Object key, Object value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Object getKey()
+ { return key; }
+
+ public Object getValue()
+ { return value; }
+
+ public Object setValue(Object value)
+ {
+ Object old = value;
+ this.value = value;
+ return old;
+ }
+}
diff --git a/src/classes/com/mchange/v1/util/StringTokenizerUtils.java b/src/classes/com/mchange/v1/util/StringTokenizerUtils.java
new file mode 100644
index 0000000..007cbb7
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/StringTokenizerUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.StringTokenizer;
+
+public final class StringTokenizerUtils
+{
+ public static String[] tokenizeToArray(String str, String delim, boolean returntokens)
+ {
+ StringTokenizer st = new StringTokenizer(str, delim, returntokens);
+ String[] strings = new String[st.countTokens()];
+ for (int i = 0; st.hasMoreTokens(); ++i)
+ strings[i] = st.nextToken();
+ return strings;
+ }
+
+ public static String[] tokenizeToArray(String str, String delim)
+ {return tokenizeToArray(str, delim, false);}
+
+ public static String[] tokenizeToArray(String str)
+ {return tokenizeToArray(str, " \t\r\n");}
+}
+
+
+
+
diff --git a/src/classes/com/mchange/v1/util/WrapperIterator.java b/src/classes/com/mchange/v1/util/WrapperIterator.java
new file mode 100644
index 0000000..a894d5a
--- /dev/null
+++ b/src/classes/com/mchange/v1/util/WrapperIterator.java
@@ -0,0 +1,115 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.util;
+
+import java.util.*;
+import com.mchange.v1.util.DebugUtils;
+
+/**
+ * This implementation does not yet support removes once hasNext() has
+ * been called... will add if necessary.
+ */
+public abstract class WrapperIterator implements Iterator
+{
+ protected final static Object SKIP_TOKEN = new Object();
+
+ final static boolean DEBUG = true;
+
+ Iterator inner;
+ boolean supports_remove;
+ Object lastOut = null;
+ Object nextOut = SKIP_TOKEN;
+
+ public WrapperIterator(Iterator inner, boolean supports_remove)
+ {
+ this.inner = inner;
+ this.supports_remove = supports_remove;
+ }
+
+ public WrapperIterator(Iterator inner)
+ { this( inner, false ); }
+
+ public boolean hasNext()
+ {
+ findNext();
+ return nextOut != SKIP_TOKEN;
+ }
+
+ private void findNext()
+ {
+ if (nextOut == SKIP_TOKEN)
+ {
+ while (inner.hasNext() && nextOut == SKIP_TOKEN)
+ this.nextOut = transformObject( inner.next() );
+ }
+ }
+
+ public Object next()
+ {
+ findNext();
+ if (nextOut != SKIP_TOKEN)
+ {
+ lastOut = nextOut;
+ nextOut = SKIP_TOKEN;
+ }
+ else
+ throw new NoSuchElementException();
+
+ //postcondition
+ if (DEBUG)
+ DebugUtils.myAssert( nextOut == SKIP_TOKEN && lastOut != SKIP_TOKEN );
+ return lastOut;
+ }
+
+ public void remove()
+ {
+ if (supports_remove)
+ {
+ if (nextOut != SKIP_TOKEN)
+ throw new UnsupportedOperationException(this.getClass().getName() +
+ " cannot support remove after" +
+ " hasNext() has been called!");
+ if (lastOut != SKIP_TOKEN)
+ inner.remove();
+ else
+ throw new NoSuchElementException();
+ }
+ else
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * return SKIP_TOKEN to indicate an object should be
+ * skipped, i.e., not exposed as part of the iterator.
+ * (we don't use null, because we want to support iterators
+ * over null-accepting Collections.)
+ */
+ protected abstract Object transformObject(Object o);
+}
+
+
+
+
+
+
diff --git a/src/classes/com/mchange/v1/xml/DomParseUtils.java b/src/classes/com/mchange/v1/xml/DomParseUtils.java
new file mode 100644
index 0000000..e7c1e0d
--- /dev/null
+++ b/src/classes/com/mchange/v1/xml/DomParseUtils.java
@@ -0,0 +1,179 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v1.xml;
+
+import java.util.*;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import com.mchange.v1.util.DebugUtils;
+
+public final class DomParseUtils
+{
+ final static boolean DEBUG = true;
+
+ /**
+ * @return null if child doesn't exist.
+ */
+ public static String allTextFromUniqueChild(Element elem, String childTagName)
+ throws DOMException
+ { return allTextFromUniqueChild( elem, childTagName, false ); }
+
+ /**
+ * @return null if child doesn't exist.
+ */
+ public static String allTextFromUniqueChild(Element elem, String childTagName, boolean trim)
+ throws DOMException
+ {
+ Element uniqueChild = uniqueChildByTagName( elem, childTagName );
+ if (uniqueChild == null)
+ return null;
+ else
+ return DomParseUtils.allTextFromElement( uniqueChild, trim );
+ }
+
+ public static Element uniqueChild(Element elem, String childTagName) throws DOMException
+ { return uniqueChildByTagName( elem, childTagName); }
+
+ /**
+ * @deprecated use uniqueChild(Element elem, String childTagName)
+ */
+ public static Element uniqueChildByTagName(Element elem, String childTagName) throws DOMException
+ {
+ NodeList nl = elem.getElementsByTagName(childTagName);
+ int len = nl.getLength();
+ if (DEBUG)
+ DebugUtils.myAssert( len <= 1 ,
+ "There is more than one (" + len + ") child with tag name: " +
+ childTagName + "!!!" );
+ return (len == 1 ? (Element) nl.item( 0 ) : null);
+ }
+
+ public static String allText(Element elem) throws DOMException
+ { return allTextFromElement( elem ); }
+
+ public static String allText(Element elem, boolean trim) throws DOMException
+ { return allTextFromElement( elem, trim ); }
+
+ /** @deprecated use allText(Element elem) */
+ public static String allTextFromElement(Element elem) throws DOMException
+ { return allTextFromElement( elem, false); }
+
+ /** @deprecated use allText(Element elem, boolean trim) */
+ public static String allTextFromElement(Element elem, boolean trim) throws DOMException
+ {
+ StringBuffer textBuf = new StringBuffer();
+ NodeList nl = elem.getChildNodes();
+ for (int j = 0, len = nl.getLength(); j < len; ++j)
+ {
+ Node node = nl.item(j);
+ if (node instanceof Text) //includes Text and CDATA!
+ textBuf.append(node.getNodeValue());
+ }
+ String out = textBuf.toString();
+ return ( trim ? out.trim() : out );
+ }
+
+ public static String[] allTextFromImmediateChildElements( Element parent, String tagName )
+ throws DOMException
+ { return allTextFromImmediateChildElements( parent, tagName, false ); }
+
+ public static String[] allTextFromImmediateChildElements( Element parent, String tagName, boolean trim )
+ throws DOMException
+ {
+ NodeList nl = immediateChildElementsByTagName( parent, tagName );
+ int len = nl.getLength();
+ String[] out = new String[ len ];
+ for (int i = 0; i < len; ++i)
+ out[i] = allText( (Element) nl.item(i), trim );
+ return out;
+ }
+
+
+ public static NodeList immediateChildElementsByTagName( Element parent, String tagName )
+ throws DOMException
+ { return getImmediateChildElementsByTagName( parent, tagName ); }
+
+ /**
+ * @deprecated use immediateChildrenByTagName( Element parent, String tagName )
+ */
+ public static NodeList getImmediateChildElementsByTagName( Element parent, String tagName )
+ throws DOMException
+ {
+ final List nodes = new ArrayList();
+ for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling())
+ if (child instanceof Element && ((Element) child).getTagName().equals(tagName))
+ nodes.add(child);
+ return new NodeList()
+ {
+ public int getLength()
+ { return nodes.size(); }
+
+ public Node item( int i )
+ { return (Node) nodes.get( i ); }
+ };
+ }
+
+ public static String allTextFromUniqueImmediateChild(Element elem, String childTagName)
+ throws DOMException
+ {
+ Element uniqueChild = uniqueImmediateChildByTagName( elem, childTagName );
+ if (uniqueChild == null)
+ return null;
+ return DomParseUtils.allTextFromElement( uniqueChild );
+ }
+
+ public static Element uniqueImmediateChild(Element elem, String childTagName)
+ throws DOMException
+ { return uniqueImmediateChildByTagName( elem, childTagName); }
+
+ /**
+ * @deprecated use uniqueImmediateChild(Element elem, String childTagName)
+ */
+ public static Element uniqueImmediateChildByTagName(Element elem, String childTagName)
+ throws DOMException
+ {
+ NodeList nl = getImmediateChildElementsByTagName(elem, childTagName);
+ int len = nl.getLength();
+ if (DEBUG)
+ DebugUtils.myAssert( len <= 1 ,
+ "There is more than one (" + len + ") child with tag name: " +
+ childTagName + "!!!" );
+ return (len == 1 ? (Element) nl.item( 0 ) : null);
+ }
+
+ /**
+ * @deprecated use Element.getAttribute(String val)
+ */
+ public static String attrValFromElement(Element element, String attrName)
+ throws DOMException
+ {
+ Attr attr = element.getAttributeNode( attrName );
+ return (attr == null ? null : attr.getValue());
+ }
+
+ private DomParseUtils()
+ {}
+}
+
+
diff --git a/src/classes/com/mchange/v2/async/AsynchronousRunner.java b/src/classes/com/mchange/v2/async/AsynchronousRunner.java
new file mode 100644
index 0000000..9cfe663
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/AsynchronousRunner.java
@@ -0,0 +1,56 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import com.mchange.v1.util.ClosableResource;
+
+public interface AsynchronousRunner extends ClosableResource
+{
+ public void postRunnable(Runnable r);
+
+
+ /**
+ * Finish with this AsynchronousRunner, and clean-up
+ * any Threads or resources it may hold.
+ *
+ * @param skip_remaining_tasks Should be regarded as
+ * a hint, not a guarantee. If true, pending,
+ * not-yet-performed tasks will be skipped,
+ * if possible.
+ * Currently executing tasks may or
+ * may not be interrupted. If false, all
+ * previously scheduled tasks will be
+ * completed prior to clean-up. The method
+ * returns immediately regardless.
+ */
+ public void close( boolean skip_remaining_tasks );
+
+ /**
+ * Clean-up resources held by this asynchronous runner
+ * as soon as possible. Remaining tasks are skipped if possible,
+ * and any tasks executing when close() is called may
+ * or may not be interrupted. Equivalent to close( true ).
+ */
+ public void close();
+}
diff --git a/src/classes/com/mchange/v2/async/CarefulRunnableQueue.java b/src/classes/com/mchange/v2/async/CarefulRunnableQueue.java
new file mode 100644
index 0000000..b77e532
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/CarefulRunnableQueue.java
@@ -0,0 +1,235 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.LinkedList;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.util.ResourceClosedException;
+
+public class CarefulRunnableQueue implements RunnableQueue, Queuable, StrandedTaskReporting
+{
+ private final static MLogger logger = MLog.getLogger( CarefulRunnableQueue.class );
+
+ private List taskList = new LinkedList();
+ private TaskThread t = new TaskThread();
+
+ private boolean shutdown_on_interrupt;
+
+ private boolean gentle_close_requested = false;
+
+ private List strandedTasks = null;
+
+ public CarefulRunnableQueue(boolean daemon, boolean shutdown_on_interrupt)
+ {
+ this.shutdown_on_interrupt = shutdown_on_interrupt;
+ t.setDaemon( daemon );
+ t.start();
+ }
+
+ public RunnableQueue asRunnableQueue()
+ { return this; }
+
+ public synchronized void postRunnable(Runnable r)
+ {
+ try
+ {
+ if (gentle_close_requested)
+ throw new ResourceClosedException("Attempted to post a task to a closing " +
+ "CarefulRunnableQueue.");
+
+ taskList.add(r);
+ this.notifyAll();
+ }
+ catch (NullPointerException e)
+ {
+ //e.printStackTrace();
+ if (Debug.DEBUG)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "NullPointerException while posting Runnable.", e );
+ }
+ if (taskList == null)
+ throw new ResourceClosedException("Attempted to post a task to a CarefulRunnableQueue " +
+ "which has been closed, or whose TaskThread has been " +
+ "interrupted.");
+ else
+ throw e;
+ }
+ }
+
+ public synchronized void close( boolean skip_remaining_tasks )
+ {
+ if (skip_remaining_tasks)
+ {
+ t.safeStop();
+ t.interrupt();
+ }
+ else
+ gentle_close_requested = true;
+ }
+
+ public synchronized void close()
+ { this.close( true ); }
+
+ public synchronized List getStrandedTasks()
+ {
+ try
+ {
+ while (gentle_close_requested && taskList != null)
+ this.wait();
+ return strandedTasks;
+ }
+ catch (InterruptedException e)
+ {
+ // very, very rare I think...
+ // if necessary I'll try a more complex solution, but I don't think
+ // it's worth it.
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ Thread.currentThread() + " interrupted while waiting for stranded tasks from CarefulRunnableQueue.",
+ e );
+
+ throw new RuntimeException(Thread.currentThread() +
+ " interrupted while waiting for stranded tasks from CarefulRunnableQueue.");
+ }
+ }
+
+ private synchronized Runnable dequeueRunnable()
+ {
+ Runnable r = (Runnable) taskList.get(0);
+ taskList.remove(0);
+ return r;
+ }
+
+ private synchronized void awaitTask() throws InterruptedException
+ {
+ while (taskList.size() == 0)
+ {
+ if ( gentle_close_requested )
+ {
+ t.safeStop(); // remember t == Thread.currentThread()
+ t.interrupt();
+ }
+ this.wait();
+ }
+ }
+
+ class TaskThread extends Thread
+ {
+ boolean should_stop = false;
+
+ TaskThread()
+ { super("CarefulRunnableQueue.TaskThread"); }
+
+ public synchronized void safeStop()
+ { should_stop = true; }
+
+ private synchronized boolean shouldStop()
+ { return should_stop; }
+
+ public void run()
+ {
+ try
+ {
+ while (! shouldStop() )
+ {
+ try
+ {
+ awaitTask();
+ Runnable r = dequeueRunnable();
+ try
+ { r.run(); }
+ catch (Exception e)
+ {
+ //System.err.println(this.getClass().getName() + " -- Unexpected exception in task!");
+ //e.printStackTrace();
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log(MLevel.WARNING, this.getClass().getName() + " -- Unexpected exception in task!", e);
+ }
+ }
+ catch (InterruptedException e)
+ {
+ if (shutdown_on_interrupt)
+ {
+ CarefulRunnableQueue.this.close( false );
+// if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED )
+// System.err.println( this.toString() +
+// " interrupted. Shutting down after current tasks" +
+// " have completed." );
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.info(this.toString() +
+ " interrupted. Shutting down after current tasks" +
+ " have completed." );
+ }
+ else
+ {
+// if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED )
+// System.err.println( this.toString() +
+// " received interrupt. IGNORING." );
+ logger.info(this.toString() + " received interrupt. IGNORING." );
+ }
+ }
+ }
+ }
+// catch (ThreadDeath td) //DEBUG ONLY -- remove soon, swaldman 08-Jun-2003
+// {
+// System.err.print("c3p0-TRAVIS: ");
+// System.err.println(this.getName() + ": Some bastard used the deprecated stop() method to kill me!!!!");
+// td.printStackTrace();
+// throw td;
+// }
+// catch (Throwable t) //DEBUG ONLY -- remove soon, swaldman 08-Jun-2003
+// {
+// System.err.print("c3p0-TRAVIS: ");
+// System.err.println(this.getName() + ": Some unexpected Throwable occurred and killed me!!!!");
+// t.printStackTrace();
+// if (t instanceof Error)
+// throw (Error) t;
+// else if (t instanceof RuntimeException)
+// throw (RuntimeException) t;
+// else
+// throw new InternalError( t.toString() ); //we don't expect any checked Exceptions can happen here.
+// }
+ finally
+ {
+ synchronized ( CarefulRunnableQueue.this )
+ {
+ strandedTasks = Collections.unmodifiableList( taskList );
+ taskList = null;
+ t = null;
+ CarefulRunnableQueue.this.notifyAll(); //if anyone is waiting for stranded tasks...
+ //System.err.print("c3p0-TRAVIS: ");
+ //System.err.println("TaskThread dead. strandedTasks: " + strandedTasks);
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/async/Queuable.java b/src/classes/com/mchange/v2/async/Queuable.java
new file mode 100644
index 0000000..78b1350
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/Queuable.java
@@ -0,0 +1,29 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+public interface Queuable extends AsynchronousRunner
+{
+ public RunnableQueue asRunnableQueue();
+}
diff --git a/src/classes/com/mchange/v2/async/RoundRobinAsynchronousRunner.java b/src/classes/com/mchange/v2/async/RoundRobinAsynchronousRunner.java
new file mode 100644
index 0000000..de0eeaa
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/RoundRobinAsynchronousRunner.java
@@ -0,0 +1,154 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import com.mchange.v2.log.*;
+import com.mchange.v2.util.ResourceClosedException;
+
+/**
+ * A class that provides for effecient asynchronous execution
+ * of multiple tasks that may block, but that do not contend
+ * for the same locks. The order in which tasks will be executed
+ * is not guaranteed.
+ */
+public class RoundRobinAsynchronousRunner implements AsynchronousRunner, Queuable
+{
+ private final static MLogger logger = MLog.getLogger( RoundRobinAsynchronousRunner.class );
+
+ //MT: unchanging, individual elements are thread-safe
+ final RunnableQueue[] rqs;
+
+ //MT: protected by this' lock
+ int task_turn = 0;
+
+ //MT: protected by this' lock
+ int view_turn = 0;
+
+ public RoundRobinAsynchronousRunner( int num_threads, boolean daemon )
+ {
+ this.rqs = new RunnableQueue[ num_threads ];
+ for(int i = 0; i < num_threads; ++i)
+ rqs[i] = new CarefulRunnableQueue( daemon, false );
+ }
+
+ public synchronized void postRunnable(Runnable r)
+ {
+ try
+ {
+ int index = task_turn;
+ task_turn = (task_turn + 1) % rqs.length;
+ rqs[index].postRunnable( r );
+
+ /* we do this "long-hand" to avoid bad fragility if an exception */
+ /* occurs in postRunnable, causing the mod step of the original */
+ /* concise code to get skipped, and leading (if */
+ /* task_turn == rqs.length - 1 when the exception occurs) to an */
+ /* endless cascade of ArrayIndexOutOfBoundsExceptions. */
+ /* we might alternatively have just put the mod step into a */
+ /* finally block, but that's too fancy. */
+ /* thanks to Travis Reeder for reporting this problem. */
+
+ //rqs[task_turn++].postRunnable( r );
+ //task_turn %= rqs.length;
+ }
+ catch ( NullPointerException e )
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "NullPointerException while posting Runnable -- Probably we're closed.", e );
+ }
+ this.close( true );
+ throw new ResourceClosedException("Attempted to use a RoundRobinAsynchronousRunner in a closed or broken state.");
+ }
+ }
+
+ public synchronized RunnableQueue asRunnableQueue()
+ {
+ try
+ {
+ int index = view_turn;
+ view_turn = (view_turn + 1) % rqs.length;
+ return new RunnableQueueView( index );
+
+ /* same explanation as above */
+
+ //RunnableQueue out = new RunnableQueueView( view_turn++ );
+ //view_turn %= rqs.length;
+ //return out;
+ }
+ catch ( NullPointerException e )
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "NullPointerException in asRunnableQueue() -- Probably we're closed.", e );
+ }
+ this.close( true );
+ throw new ResourceClosedException("Attempted to use a RoundRobinAsynchronousRunner in a closed or broken state.");
+ }
+ }
+
+ public synchronized void close( boolean skip_remaining_tasks )
+ {
+ for (int i = 0, len = rqs.length; i < len; ++i)
+ {
+ attemptClose( rqs[i], skip_remaining_tasks );
+ rqs[i] = null;
+ }
+ }
+
+ public void close()
+ { close( true ); }
+
+ static void attemptClose(RunnableQueue rq, boolean skip_remaining_tasks)
+ {
+ try { rq.close( skip_remaining_tasks ); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "RunnableQueue close FAILED.", e );
+ }
+ }
+
+ class RunnableQueueView implements RunnableQueue
+ {
+ final int rq_num;
+
+ RunnableQueueView( int rq_num )
+ { this.rq_num = rq_num; }
+
+ public void postRunnable(Runnable r)
+ { rqs[ rq_num ].postRunnable( r ); }
+
+ public void close( boolean skip_remaining_tasks )
+ { }
+
+ public void close()
+ { /* ignore */ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/async/RunnableQueue.java b/src/classes/com/mchange/v2/async/RunnableQueue.java
new file mode 100644
index 0000000..3bf5578
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/RunnableQueue.java
@@ -0,0 +1,32 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+/**
+ * RunnableQueues guarantee that tasks will be
+ * executed in order, where other AsynchronousRunners
+ * may not.
+ */
+public interface RunnableQueue extends AsynchronousRunner
+{}
diff --git a/src/classes/com/mchange/v2/async/StrandedTaskReporting.java b/src/classes/com/mchange/v2/async/StrandedTaskReporting.java
new file mode 100644
index 0000000..bfbf91e
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/StrandedTaskReporting.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import java.util.List;
+
+public interface StrandedTaskReporting
+{
+ /**
+ * makes available any tasks that were unperformed when
+ * this AsynchronousRunner was closed, either explicitly
+ * using close() or close( true ), or implicitly because
+ * some failure or corruption killed the Object (most likely
+ * a Thread interruption.
+ *
+ * @return null if the AsynchronousRunner is still alive, a List
+ * of Runnables otherwise, which will be empty only if all tasks
+ * were performed before the AsynchronousRunner shut down.
+ */
+ public List getStrandedTasks();
+}
diff --git a/src/classes/com/mchange/v2/async/ThreadPerTaskAsynchronousRunner.java b/src/classes/com/mchange/v2/async/ThreadPerTaskAsynchronousRunner.java
new file mode 100644
index 0000000..8a26a90
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/ThreadPerTaskAsynchronousRunner.java
@@ -0,0 +1,261 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import java.util.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.util.ResourceClosedException;
+
+public class ThreadPerTaskAsynchronousRunner implements AsynchronousRunner
+{
+ final static int PRESUME_DEADLOCKED_MULTIPLE = 3; //after three times the interrupt period, we presume deadlock
+
+ final static MLogger logger = MLog.getLogger( ThreadPerTaskAsynchronousRunner.class );
+
+ //MT: unchanged post-ctor
+ final int max_task_threads;
+ final long interrupt_task_delay;
+
+ //MT: protected by this' lock
+ LinkedList queue = new LinkedList();
+ ArrayList running = new ArrayList(); //as a Collection -- duplicate-accepting-ness is important, order is not
+ ArrayList deadlockSnapshot = null;
+ boolean still_open = true;
+
+ //MT: thread-safe and not reassigned post-ctor
+ Thread dispatchThread = new DispatchThread();
+ Timer interruptAndDeadlockTimer;
+
+ public ThreadPerTaskAsynchronousRunner(int max_task_threads)
+ { this( max_task_threads, 0); }
+
+ public ThreadPerTaskAsynchronousRunner(int max_task_threads, long interrupt_task_delay)
+ {
+ this.max_task_threads = max_task_threads;
+ this.interrupt_task_delay = interrupt_task_delay;
+ if ( hasIdTimer() )
+ {
+ interruptAndDeadlockTimer = new Timer( true );
+ TimerTask deadlockChecker = new TimerTask()
+ {
+ public void run()
+ { checkForDeadlock(); }
+ };
+ long delay = interrupt_task_delay * PRESUME_DEADLOCKED_MULTIPLE;
+ interruptAndDeadlockTimer.schedule(deadlockChecker, delay, delay);
+ }
+
+ dispatchThread.start();
+ }
+
+ private boolean hasIdTimer()
+ { return (interrupt_task_delay > 0); }
+
+ public synchronized void postRunnable(Runnable r)
+ {
+ if ( still_open )
+ {
+ queue.add( r );
+ this.notifyAll();
+ }
+ else
+ throw new ResourceClosedException("Attempted to use a ThreadPerTaskAsynchronousRunner in a closed or broken state.");
+
+ }
+
+ public void close()
+ { close( true ); }
+
+ public synchronized void close( boolean skip_remaining_tasks )
+ {
+ if ( still_open )
+ {
+ this.still_open = false;
+ if (skip_remaining_tasks)
+ {
+ queue.clear();
+ for (Iterator ii = running.iterator(); ii.hasNext(); )
+ ((Thread) ii.next()).interrupt();
+ closeThreadResources();
+ }
+ }
+ }
+
+ public synchronized int getRunningCount()
+ { return running.size(); }
+
+ public synchronized Collection getRunningTasks()
+ { return (Collection) running.clone(); }
+
+ public synchronized int getWaitingCount()
+ { return queue.size(); }
+
+ public synchronized Collection getWaitingTasks()
+ { return (Collection) queue.clone(); }
+
+ public synchronized boolean isClosed()
+ { return !still_open; }
+
+ public synchronized boolean isDoneAndGone()
+ { return (!dispatchThread.isAlive() && running.isEmpty() && interruptAndDeadlockTimer == null); }
+
+ private synchronized void acknowledgeComplete(TaskThread tt)
+ {
+ if (! tt.isCompleted())
+ {
+ running.remove( tt );
+ tt.markCompleted();
+ ThreadPerTaskAsynchronousRunner.this.notifyAll();
+
+ if (! still_open && queue.isEmpty() && running.isEmpty())
+ closeThreadResources();
+ }
+ }
+
+ private synchronized void checkForDeadlock()
+ {
+ if (deadlockSnapshot == null)
+ {
+ if (running.size() == max_task_threads)
+ deadlockSnapshot = (ArrayList) running.clone();
+ }
+ else if (running.size() < max_task_threads)
+ deadlockSnapshot = null;
+ else if (deadlockSnapshot.equals( running )) //deadlock!
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ {
+ StringBuffer warningMsg = new StringBuffer(1024);
+ warningMsg.append("APPARENT DEADLOCK! (");
+ warningMsg.append( this );
+ warningMsg.append(") Deadlocked threads (unresponsive to interrupt()) are being set aside as hopeless and up to ");
+ warningMsg.append( max_task_threads );
+ warningMsg.append(" may now be spawned for new tasks. If tasks continue to deadlock, you may run out of memory. Deadlocked task list: ");
+ for (int i = 0, len = deadlockSnapshot.size(); i < len; ++i)
+ {
+ if (i != 0) warningMsg.append(", ");
+ warningMsg.append( ((TaskThread) deadlockSnapshot.get(i)).getTask() );
+ }
+
+ logger.log(MLevel.WARNING, warningMsg.toString());
+ }
+
+ // note "complete" here means from the perspective of the async runner, as complete
+ // as it will ever be, since the task is presumed hopelessly hung
+ for (int i = 0, len = deadlockSnapshot.size(); i < len; ++i)
+ acknowledgeComplete( (TaskThread) deadlockSnapshot.get(i) );
+ deadlockSnapshot = null;
+ }
+ else
+ deadlockSnapshot = (ArrayList) running.clone();
+ }
+
+ private void closeThreadResources()
+ {
+ if (interruptAndDeadlockTimer != null)
+ {
+ interruptAndDeadlockTimer.cancel();
+ interruptAndDeadlockTimer = null;
+ }
+ dispatchThread.interrupt();
+ }
+
+ class DispatchThread extends Thread
+ {
+ DispatchThread()
+ { super( "Dispatch-Thread-for-" + ThreadPerTaskAsynchronousRunner.this ); }
+
+ public void run()
+ {
+ synchronized (ThreadPerTaskAsynchronousRunner.this)
+ {
+ try
+ {
+ while (true)
+ {
+ while (queue.isEmpty() || running.size() == max_task_threads)
+ ThreadPerTaskAsynchronousRunner.this.wait();
+
+ Runnable next = (Runnable) queue.remove(0);
+ TaskThread doer = new TaskThread( next );
+ doer.start();
+ running.add( doer );
+ }
+ }
+ catch (InterruptedException e)
+ {
+ if (still_open) //we're not closed...
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, this.getName() + " unexpectedly interrupted! Shutting down!" );
+ close( false );
+ }
+ }
+ }
+ }
+ }
+
+ class TaskThread extends Thread
+ {
+ //MT: post-ctor constant
+ Runnable r;
+
+ //MT: protected by this' lock
+ boolean completed = false;
+
+ TaskThread( Runnable r )
+ {
+ super( "Task-Thread-for-" + ThreadPerTaskAsynchronousRunner.this );
+ this.r = r;
+ }
+
+ Runnable getTask()
+ { return r; }
+
+ synchronized void markCompleted()
+ { completed = true; }
+
+ synchronized boolean isCompleted()
+ { return completed; }
+
+ public void run()
+ {
+ try
+ {
+ if (hasIdTimer())
+ {
+ TimerTask interruptTask = new TimerTask()
+ {
+ public void run()
+ { TaskThread.this.interrupt(); }
+ };
+ interruptAndDeadlockTimer.schedule( interruptTask , interrupt_task_delay );
+ }
+ r.run();
+ }
+ finally
+ { acknowledgeComplete( this ); }
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/async/ThreadPoolAsynchronousRunner.java b/src/classes/com/mchange/v2/async/ThreadPoolAsynchronousRunner.java
new file mode 100644
index 0000000..230a036
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/ThreadPoolAsynchronousRunner.java
@@ -0,0 +1,718 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async;
+
+import java.util.*;
+import com.mchange.v2.log.*;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import com.mchange.v2.io.IndentedWriter;
+import com.mchange.v2.util.ResourceClosedException;
+
+public final class ThreadPoolAsynchronousRunner implements AsynchronousRunner
+{
+ final static MLogger logger = MLog.getLogger( ThreadPoolAsynchronousRunner.class );
+
+ final static int POLL_FOR_STOP_INTERVAL = 5000; //milliseconds
+
+ final static int DFLT_DEADLOCK_DETECTOR_INTERVAL = 10000; //milliseconds
+ final static int DFLT_INTERRUPT_DELAY_AFTER_APPARENT_DEADLOCK = 60000; //milliseconds
+ final static int DFLT_MAX_INDIVIDUAL_TASK_TIME = 0; //milliseconds, <= 0 means don't enforce a max task time
+
+ final static int DFLT_MAX_EMERGENCY_THREADS = 10;
+
+ int deadlock_detector_interval;
+ int interrupt_delay_after_apparent_deadlock;
+ int max_individual_task_time;
+
+ int num_threads;
+ boolean daemon;
+ HashSet managed;
+ HashSet available;
+ LinkedList pendingTasks;
+
+ Timer myTimer;
+ boolean should_cancel_timer;
+
+ TimerTask deadlockDetector = new DeadlockDetector();
+ TimerTask replacedThreadInterruptor = null;
+
+ Map stoppedThreadsToStopDates = new HashMap();
+
+ private ThreadPoolAsynchronousRunner( int num_threads,
+ boolean daemon,
+ int max_individual_task_time,
+ int deadlock_detector_interval,
+ int interrupt_delay_after_apparent_deadlock,
+ Timer myTimer,
+ boolean should_cancel_timer )
+ {
+ this.num_threads = num_threads;
+ this.daemon = daemon;
+ this.max_individual_task_time = max_individual_task_time;
+ this.deadlock_detector_interval = deadlock_detector_interval;
+ this.interrupt_delay_after_apparent_deadlock = interrupt_delay_after_apparent_deadlock;
+ this.myTimer = myTimer;
+ this.should_cancel_timer = should_cancel_timer;
+
+ recreateThreadsAndTasks();
+
+ myTimer.schedule( deadlockDetector, deadlock_detector_interval, deadlock_detector_interval );
+
+ }
+
+
+ public ThreadPoolAsynchronousRunner( int num_threads,
+ boolean daemon,
+ int max_individual_task_time,
+ int deadlock_detector_interval,
+ int interrupt_delay_after_apparent_deadlock,
+ Timer myTimer )
+ {
+ this( num_threads,
+ daemon,
+ max_individual_task_time,
+ deadlock_detector_interval,
+ interrupt_delay_after_apparent_deadlock,
+ myTimer,
+ false );
+ }
+
+ public ThreadPoolAsynchronousRunner( int num_threads,
+ boolean daemon,
+ int max_individual_task_time,
+ int deadlock_detector_interval,
+ int interrupt_delay_after_apparent_deadlock )
+ {
+ this( num_threads,
+ daemon,
+ max_individual_task_time,
+ deadlock_detector_interval,
+ interrupt_delay_after_apparent_deadlock,
+ new Timer( true ),
+ true );
+ }
+
+ public ThreadPoolAsynchronousRunner( int num_threads, boolean daemon, Timer sharedTimer )
+ {
+ this( num_threads,
+ daemon,
+ DFLT_MAX_INDIVIDUAL_TASK_TIME,
+ DFLT_DEADLOCK_DETECTOR_INTERVAL,
+ DFLT_INTERRUPT_DELAY_AFTER_APPARENT_DEADLOCK,
+ sharedTimer,
+ false );
+ }
+
+ public ThreadPoolAsynchronousRunner( int num_threads, boolean daemon )
+ {
+ this( num_threads,
+ daemon,
+ DFLT_MAX_INDIVIDUAL_TASK_TIME,
+ DFLT_DEADLOCK_DETECTOR_INTERVAL,
+ DFLT_INTERRUPT_DELAY_AFTER_APPARENT_DEADLOCK,
+ new Timer( true ),
+ true ); }
+
+ public synchronized void postRunnable(Runnable r)
+ {
+ try
+ {
+ pendingTasks.add( r );
+ this.notifyAll();
+ }
+ catch ( NullPointerException e )
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "NullPointerException while posting Runnable -- Probably we're closed.", e );
+ }
+ throw new ResourceClosedException("Attempted to use a ThreadPoolAsynchronousRunner in a closed or broken state.");
+ }
+ }
+
+ public synchronized int getThreadCount()
+ { return managed.size(); }
+
+ public void close( boolean skip_remaining_tasks )
+ {
+ synchronized ( this )
+ {
+ if (managed == null) return;
+ deadlockDetector.cancel();
+ //replacedThreadInterruptor.cancel();
+ if (should_cancel_timer)
+ myTimer.cancel();
+ myTimer = null;
+ for (Iterator ii = managed.iterator(); ii.hasNext(); )
+ {
+ PoolThread stopMe = (PoolThread) ii.next();
+ stopMe.gentleStop();
+ if (skip_remaining_tasks)
+ stopMe.interrupt();
+ }
+ managed = null;
+
+ if (!skip_remaining_tasks)
+ {
+ for (Iterator ii = pendingTasks.iterator(); ii.hasNext(); )
+ {
+ Runnable r = (Runnable) ii.next();
+ new Thread(r).start();
+ ii.remove();
+ }
+ }
+ available = null;
+ pendingTasks = null;
+ }
+ }
+
+ public void close()
+ { close( true ); }
+
+ public synchronized int getActiveCount()
+ { return managed.size() - available.size(); }
+
+ public synchronized int getIdleCount()
+ { return available.size(); }
+
+ public synchronized int getPendingTaskCount()
+ { return pendingTasks.size(); }
+
+ public synchronized String getStatus()
+ {
+ /*
+ StringBuffer sb = new StringBuffer( 512 );
+ sb.append( this.toString() );
+ sb.append( ' ' );
+ appendStatusString( sb );
+ return sb.toString();
+ */
+
+ return getMultiLineStatusString();
+ }
+
+ // done reflectively for jdk 1.3/1.4 compatability
+ public synchronized String getStackTraces()
+ { return getStackTraces(0); }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private String getStackTraces(int initial_indent)
+ {
+ if (managed == null)
+ return null;
+
+ try
+ {
+ Method m = Thread.class.getMethod("getStackTrace", null);
+
+ StringWriter sw = new StringWriter(2048);
+ IndentedWriter iw = new IndentedWriter( sw );
+ for (int i = 0; i < initial_indent; ++i)
+ iw.upIndent();
+ for (Iterator ii = managed.iterator(); ii.hasNext(); )
+ {
+ Object poolThread = ii.next();
+ Object[] stackTraces = (Object[]) m.invoke( poolThread, null );
+ iw.println( poolThread );
+ iw.upIndent();
+ for (int i = 0, len = stackTraces.length; i < len; ++i)
+ iw.println( stackTraces[i] );
+ iw.downIndent();
+ }
+ for (int i = 0; i < initial_indent; ++i)
+ iw.downIndent();
+ iw.flush(); // useless, but I feel better
+ String out = sw.toString();
+ iw.close(); // useless, but I feel better;
+ return out;
+ }
+ catch (NoSuchMethodException e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine( this + ": strack traces unavailable because this is a pre-Java 1.5 VM.");
+ return null;
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, this + ": An Exception occurred while trying to extract PoolThread stack traces.", e);
+ return null;
+ }
+ }
+
+ public synchronized String getMultiLineStatusString()
+ { return this.getMultiLineStatusString(0); }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private String getMultiLineStatusString(int initial_indent)
+ {
+ try
+ {
+ StringWriter sw = new StringWriter(2048);
+ IndentedWriter iw = new IndentedWriter( sw );
+
+ for (int i = 0; i < initial_indent; ++i)
+ iw.upIndent();
+
+ if (managed == null)
+ {
+ iw.print("[");
+ iw.print( this );
+ iw.println(" closed.]");
+ }
+ else
+ {
+ HashSet active = (HashSet) managed.clone();
+ active.removeAll( available );
+
+ iw.print("Managed Threads: ");
+ iw.println( managed.size() );
+ iw.print("Active Threads: ");
+ iw.println( active.size() );
+ iw.println("Active Tasks: ");
+ iw.upIndent();
+ for (Iterator ii = active.iterator(); ii.hasNext(); )
+ {
+ PoolThread pt = (PoolThread) ii.next();
+ iw.print( pt.getCurrentTask() );
+ iw.print( " (");
+ iw.print( pt.getName() );
+ iw.println(')');
+ }
+ iw.downIndent();
+ iw.println("Pending Tasks: ");
+ iw.upIndent();
+ for (int i = 0, len = pendingTasks.size(); i < len; ++i)
+ iw.println( pendingTasks.get( i ) );
+ iw.downIndent();
+ }
+
+ for (int i = 0; i < initial_indent; ++i)
+ iw.downIndent();
+ iw.flush(); // useless, but I feel better
+ String out = sw.toString();
+ iw.close(); // useless, but I feel better;
+ return out;
+ }
+ catch (IOException e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING, "Huh? An IOException when working with a StringWriter?!?", e);
+ throw new RuntimeException("Huh? An IOException when working with a StringWriter?!? " + e);
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private void appendStatusString( StringBuffer sb )
+ {
+ if (managed == null)
+ sb.append( "[closed]" );
+ else
+ {
+ HashSet active = (HashSet) managed.clone();
+ active.removeAll( available );
+ sb.append("[num_managed_threads: ");
+ sb.append( managed.size() );
+ sb.append(", num_active: ");
+ sb.append( active.size() );
+ sb.append("; activeTasks: ");
+ boolean first = true;
+ for (Iterator ii = active.iterator(); ii.hasNext(); )
+ {
+ if (first)
+ first = false;
+ else
+ sb.append(", ");
+ PoolThread pt = (PoolThread) ii.next();
+ sb.append( pt.getCurrentTask() );
+ sb.append( " (");
+ sb.append( pt.getName() );
+ sb.append(')');
+ }
+ sb.append("; pendingTasks: ");
+ for (int i = 0, len = pendingTasks.size(); i < len; ++i)
+ {
+ if (i != 0) sb.append(", ");
+ sb.append( pendingTasks.get( i ) );
+ }
+ sb.append(']');
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock (or is ctor)
+ private void recreateThreadsAndTasks()
+ {
+ if ( this.managed != null)
+ {
+ Date aboutNow = new Date();
+ for (Iterator ii = managed.iterator(); ii.hasNext(); )
+ {
+ PoolThread pt = (PoolThread) ii.next();
+ pt.gentleStop();
+ stoppedThreadsToStopDates.put( pt, aboutNow );
+ ensureReplacedThreadsProcessing();
+ }
+ }
+
+ this.managed = new HashSet();
+ this.available = new HashSet();
+ this.pendingTasks = new LinkedList();
+ for (int i = 0; i < num_threads; ++i)
+ {
+ Thread t = new PoolThread(i, daemon);
+ managed.add( t );
+ available.add( t );
+ t.start();
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private void processReplacedThreads()
+ {
+ long about_now = System.currentTimeMillis();
+ for (Iterator ii = stoppedThreadsToStopDates.keySet().iterator(); ii.hasNext(); )
+ {
+ PoolThread pt = (PoolThread) ii.next();
+ if (! pt.isAlive())
+ ii.remove();
+ else
+ {
+ Date d = (Date) stoppedThreadsToStopDates.get( pt );
+ if ((about_now - d.getTime()) > interrupt_delay_after_apparent_deadlock)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "Task " + pt.getCurrentTask() + " (in deadlocked PoolThread) failed to complete in maximum time " +
+ interrupt_delay_after_apparent_deadlock + "ms. Trying interrupt().");
+ pt.interrupt();
+ ii.remove();
+ }
+ //else keep waiting...
+ }
+ if (stoppedThreadsToStopDates.isEmpty())
+ stopReplacedThreadsProcessing();
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private void ensureReplacedThreadsProcessing()
+ {
+ if (replacedThreadInterruptor == null)
+ {
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.fine("Apparently some threads have been replaced. Replacement thread processing enabled.");
+
+ this.replacedThreadInterruptor = new ReplacedThreadInterruptor();
+ int replacedThreadProcessDelay = interrupt_delay_after_apparent_deadlock / 4;
+ myTimer.schedule( replacedThreadInterruptor, replacedThreadProcessDelay, replacedThreadProcessDelay );
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private void stopReplacedThreadsProcessing()
+ {
+ if (this.replacedThreadInterruptor != null)
+ {
+ this.replacedThreadInterruptor.cancel();
+ this.replacedThreadInterruptor = null;
+
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.fine("Apparently all replaced threads have either completed their tasks or been interrupted(). " +
+ "Replacement thread processing cancelled.");
+ }
+ }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ private void shuttingDown( PoolThread pt )
+ {
+ if (managed != null && managed.contains( pt )) //we are not closed, and this was a thread in the current pool, not a replaced thread
+ {
+ managed.remove( pt );
+ available.remove( pt );
+ PoolThread replacement = new PoolThread( pt.getIndex(), daemon );
+ managed.add( replacement );
+ available.add( replacement );
+ replacement.start();
+ }
+ }
+
+
+ class PoolThread extends Thread
+ {
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ Runnable currentTask;
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ boolean should_stop;
+
+ // post ctor immutable
+ int index;
+
+ // not shared. only accessed by the PoolThread itself
+ TimerTask maxIndividualTaskTimeEnforcer = null;
+
+ PoolThread(int index, boolean daemon)
+ {
+ this.setName( this.getClass().getName() + "-#" + index);
+ this.setDaemon( daemon );
+ this.index = index;
+ }
+
+ public int getIndex()
+ { return index; }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ void gentleStop()
+ { should_stop = true; }
+
+ // protected by ThreadPoolAsynchronousRunner.this' lock
+ // BE SURE CALLER OWNS ThreadPoolAsynchronousRunner.this' lock
+ Runnable getCurrentTask()
+ { return currentTask; }
+
+ // no need to sync. data not shared
+ private /* synchronized */ void setMaxIndividualTaskTimeEnforcer()
+ {
+ this.maxIndividualTaskTimeEnforcer = new MaxIndividualTaskTimeEnforcer( this );
+ myTimer.schedule( maxIndividualTaskTimeEnforcer, max_individual_task_time );
+ }
+
+ // no need to sync. data not shared
+ private /* synchronized */ void cancelMaxIndividualTaskTimeEnforcer()
+ {
+ this.maxIndividualTaskTimeEnforcer.cancel();
+ this.maxIndividualTaskTimeEnforcer = null;
+ }
+
+ public void run()
+ {
+ try
+ {
+ thread_loop:
+ while (true)
+ {
+ Runnable myTask;
+ synchronized ( ThreadPoolAsynchronousRunner.this )
+ {
+ while ( !should_stop && pendingTasks.size() == 0 )
+ ThreadPoolAsynchronousRunner.this.wait( POLL_FOR_STOP_INTERVAL );
+ if (should_stop)
+ break thread_loop;
+
+ if (! available.remove( this ) )
+ throw new InternalError("An unavailable PoolThread tried to check itself out!!!");
+ myTask = (Runnable) pendingTasks.remove(0);
+ currentTask = myTask;
+ }
+ try
+ {
+ if (max_individual_task_time > 0)
+ setMaxIndividualTaskTimeEnforcer();
+ myTask.run();
+ }
+ catch ( RuntimeException e )
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log(MLevel.WARNING, this + " -- caught unexpected Exception while executing posted task.", e);
+ //e.printStackTrace();
+ }
+ finally
+ {
+ if ( maxIndividualTaskTimeEnforcer != null )
+ cancelMaxIndividualTaskTimeEnforcer();
+
+ synchronized ( ThreadPoolAsynchronousRunner.this )
+ {
+ if (should_stop)
+ break thread_loop;
+
+ if ( available != null && ! available.add( this ) )
+ throw new InternalError("An apparently available PoolThread tried to check itself in!!!");
+ currentTask = null;
+ }
+ }
+ }
+ }
+ catch ( InterruptedException exc )
+ {
+// if ( Debug.TRACE > Debug.TRACE_NONE )
+// System.err.println(this + " interrupted. Shutting down.");
+
+ if ( Debug.TRACE > Debug.TRACE_NONE && logger.isLoggable( MLevel.FINE ) )
+ logger.fine(this + " interrupted. Shutting down.");
+ }
+
+ synchronized ( ThreadPoolAsynchronousRunner.this )
+ { ThreadPoolAsynchronousRunner.this.shuttingDown( this ); }
+ }
+ }
+
+ class DeadlockDetector extends TimerTask
+ {
+ LinkedList last = null;
+ LinkedList current = null;
+
+ public void run()
+ {
+ boolean run_stray_tasks = false;
+ synchronized ( ThreadPoolAsynchronousRunner.this )
+ {
+ if (pendingTasks.size() == 0)
+ {
+ last = null;
+ return;
+ }
+
+ current = (LinkedList) pendingTasks.clone();
+ if ( current.equals( last ) )
+ {
+ //System.err.println(this + " -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!");
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.warning(this + " -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!");
+ StringWriter sw = new StringWriter( 4096 );
+ PrintWriter pw = new PrintWriter( sw );
+ //StringBuffer sb = new StringBuffer( 512 );
+ //appendStatusString( sb );
+ //System.err.println( sb.toString() );
+ pw.print( this );
+ pw.println( " -- APPARENT DEADLOCK!!! Complete Status: ");
+ pw.print( ThreadPoolAsynchronousRunner.this.getMultiLineStatusString( 1 ) );
+ pw.println("Pool thread stack traces:");
+ String stackTraces = getStackTraces( 1 );
+ if (stackTraces == null)
+ pw.println("\t[Stack traces of deadlocked task threads not available.]");
+ else
+ pw.println( stackTraces );
+ pw.flush(); //superfluous, but I feel better
+ logger.warning( sw.toString() );
+ pw.close(); //superfluous, but I feel better
+ }
+ recreateThreadsAndTasks();
+ run_stray_tasks = true;
+ }
+ }
+ if (run_stray_tasks)
+ {
+ AsynchronousRunner ar = new ThreadPerTaskAsynchronousRunner( DFLT_MAX_EMERGENCY_THREADS, max_individual_task_time );
+ for ( Iterator ii = current.iterator(); ii.hasNext(); )
+ ar.postRunnable( (Runnable) ii.next() );
+ ar.close( false ); //tell the emergency runner to close itself when its tasks are complete
+ last = null;
+ }
+ else
+ last = current;
+
+ // under some circumstances, these lists seem to hold onto a lot of memory... presumably this
+ // is when long pending task lists build up for some reason... nevertheless, let's dereference
+ // things as soon as possible. [Thanks to Venkatesh Seetharamaiah for calling attention to this
+ // issue, and for documenting the source of object retention.]
+ current = null;
+ }
+ }
+
+ class MaxIndividualTaskTimeEnforcer extends TimerTask
+ {
+ PoolThread pt;
+ Thread interruptMe;
+ String threadStr;
+ String fixedTaskStr;
+
+ MaxIndividualTaskTimeEnforcer(PoolThread pt)
+ {
+ this.pt = pt;
+ this.interruptMe = pt;
+ this.threadStr = pt.toString();
+ this.fixedTaskStr = null;
+ }
+
+ MaxIndividualTaskTimeEnforcer(Thread interruptMe, String threadStr, String fixedTaskStr)
+ {
+ this.pt = null;
+ this.interruptMe = interruptMe;
+ this.threadStr = threadStr;
+ this.fixedTaskStr = fixedTaskStr;
+ }
+
+ public void run()
+ {
+ String taskStr;
+
+ if (fixedTaskStr != null)
+ taskStr = fixedTaskStr;
+ else if (pt != null)
+ {
+ synchronized (ThreadPoolAsynchronousRunner.this)
+ { taskStr = String.valueOf( pt.getCurrentTask() ); }
+ }
+ else
+ taskStr = "Unknown task?!";
+
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning("A task has exceeded the maximum allowable task time. Will interrupt() thread [" + threadStr
+ + "], with current task: " + taskStr);
+
+ interruptMe.interrupt();
+
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning("Thread [" + threadStr + "] interrupted.");
+ }
+ }
+
+ //not currently used...
+ private void runInEmergencyThread( final Runnable r )
+ {
+ final Thread t = new Thread( r );
+ t.start();
+ if (max_individual_task_time > 0)
+ {
+ TimerTask maxIndividualTaskTimeEnforcer = new MaxIndividualTaskTimeEnforcer(t, t + " [One-off emergency thread!!!]", r.toString());
+ myTimer.schedule( maxIndividualTaskTimeEnforcer, max_individual_task_time );
+ }
+ }
+
+ class ReplacedThreadInterruptor extends TimerTask
+ {
+ public void run()
+ {
+ synchronized (ThreadPoolAsynchronousRunner.this)
+ { processReplacedThreads(); }
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/async/junit/ThreadPerTaskAsynchronousRunnerJUnitTestCase.java b/src/classes/com/mchange/v2/async/junit/ThreadPerTaskAsynchronousRunnerJUnitTestCase.java
new file mode 100644
index 0000000..52f8ca1
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/junit/ThreadPerTaskAsynchronousRunnerJUnitTestCase.java
@@ -0,0 +1,208 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async.junit;
+
+import junit.framework.*;
+import com.mchange.v2.async.*;
+
+public class ThreadPerTaskAsynchronousRunnerJUnitTestCase extends TestCase
+{
+ ThreadPerTaskAsynchronousRunner runner;
+
+ boolean no_go = true;
+ int gone = 0;
+
+ protected void setUp()
+ {
+ runner = new ThreadPerTaskAsynchronousRunner(5);
+ }
+
+ protected void tearDown()
+ {
+ runner.close();
+ go(); //get any interrupt ignorers going...
+ }
+
+ private synchronized void go()
+ {
+ no_go = false;
+ this.notifyAll();
+ }
+
+ public void testBasicBehavior()
+ {
+ try
+ {
+ DumbTask dt = new DumbTask();
+ for( int i = 0; i < 10; ++i )
+ runner.postRunnable( dt );
+ Thread.sleep(1000); // not strictly safe, but should be plenty of time to get our tasks to the wait loop...
+ assertEquals( "running count should be 5", 5, runner.getRunningCount() );
+ assertEquals( "waiting count should be 5", 5, runner.getWaitingCount() );
+ go();
+ Thread.sleep(1000); // not strictly safe, but should be plenty of time to get our tasks to finish...
+ assertEquals( "running should be done.", 0, runner.getRunningCount() );
+ assertEquals( "waiting should be done.", 0, runner.getWaitingCount() );
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+ public void testBasicBehaviorFastNoSkipClose()
+ {
+ try
+ {
+ DumbTask dt = new DumbTask();
+ for( int i = 0; i < 10; ++i )
+ runner.postRunnable( dt );
+ runner.close( false );
+ Thread.sleep(1000); // not strictly safe, but should be plenty of time to get our tasks to the wait loop...
+ assertEquals( "running count should be 5", 5, runner.getRunningCount() );
+ assertEquals( "waiting count should be 5", 5, runner.getWaitingCount() );
+ go();
+ Thread.sleep(1000); // not strictly safe, but should be plenty of time to get our tasks to finish...
+ assertEquals( "running should be done.", 0, runner.getRunningCount() );
+ assertEquals( "waiting should be done.", 0, runner.getWaitingCount() );
+ assertTrue( runner.isDoneAndGone() );
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+ public void testBasicBehaviorFastSkipClose()
+ {
+ try
+ {
+ DumbTask dt = new DumbTask();
+ for( int i = 0; i < 10; ++i )
+ runner.postRunnable( dt );
+ runner.close( true );
+ Thread.sleep(1000); // not strictly safe, but should be plenty of time to interrupt and be done
+ assertTrue( runner.isDoneAndGone() );
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+ public void testDeadlockCase()
+ {
+ try
+ {
+ runner.close(); //we need a different set up...
+ runner = new ThreadPerTaskAsynchronousRunner(5, 1000); //interrupt tasks after 1 sec, consider deadlocked after ~3 secs..
+ DumbTask dt = new DumbTask( true );
+ for( int i = 0; i < 5; ++i )
+ runner.postRunnable( dt );
+ Thread.sleep(10000); // not strictly safe, but should be plenty of time to interrupt and be done
+ assertEquals( "running should be done.", 0, runner.getRunningCount() );
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+
+
+ public void testDeadlockWithPentUpTasks()
+ {
+ try
+ {
+ runner.close(); //we need a different set up...
+ runner = new ThreadPerTaskAsynchronousRunner(5, 1000); //interrupt tasks after 1 sec, consider deadlocked after ~3 secs..
+ //Runnable r = new Runnable() { public synchronized void run() { while (true) { try { this.wait();} catch (Exception e) {} } } };
+ Runnable r = new DumbTask( true );
+ Runnable r2 = new Runnable() { public void run() { System.out.println("done."); } };
+ for( int i = 0; i < 5; ++i )
+ runner.postRunnable( r );
+ for( int i = 0; i < 5; ++i )
+ runner.postRunnable( r2 );
+ Thread.sleep(10000); // not strictly safe, but should be plenty of time to interrupt and be done
+ assertEquals( "running should be done.", 0, runner.getRunningCount() );
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+
+
+ class DumbTask implements Runnable
+ {
+ boolean ignore_interrupts;
+
+ DumbTask()
+ { this( false ); }
+
+ DumbTask(boolean ignore_interrupts)
+ { this.ignore_interrupts = ignore_interrupts; }
+
+ public void run()
+ {
+ try
+ {
+ synchronized (ThreadPerTaskAsynchronousRunnerJUnitTestCase.this)
+ {
+ while (no_go)
+ {
+ try { ThreadPerTaskAsynchronousRunnerJUnitTestCase.this.wait(); }
+ catch (InterruptedException e)
+ {
+ if (ignore_interrupts)
+ System.err.println(this + ": interrupt ignored!");
+ else
+ {
+ e.fillInStackTrace();
+ throw e;
+ }
+ }
+ }
+ //System.err.println( ++gone );
+ ThreadPerTaskAsynchronousRunnerJUnitTestCase.this.notifyAll();
+ }
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+ }
+
+ public static void main(String[] argv)
+ {
+ junit.textui.TestRunner.run( new TestSuite( ThreadPerTaskAsynchronousRunnerJUnitTestCase.class ) );
+ //junit.swingui.TestRunner.run( SqlUtilsJUnitTestCase.class );
+ //new SqlUtilsJUnitTestCase().testGoodDebugLoggingOfNestedExceptions();
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/async/junit/ThreadPoolAsynchronousRunnerJUnitTestCase.java b/src/classes/com/mchange/v2/async/junit/ThreadPoolAsynchronousRunnerJUnitTestCase.java
new file mode 100644
index 0000000..2466e83
--- /dev/null
+++ b/src/classes/com/mchange/v2/async/junit/ThreadPoolAsynchronousRunnerJUnitTestCase.java
@@ -0,0 +1,121 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.async.junit;
+
+import junit.framework.*;
+import com.mchange.v2.async.*;
+
+public class ThreadPoolAsynchronousRunnerJUnitTestCase extends TestCase
+{
+ ThreadPoolAsynchronousRunner runner;
+
+ boolean no_go = true;
+ int gone = 0;
+
+ protected void setUp()
+ {
+ runner = new ThreadPoolAsynchronousRunner( 3,
+ true,
+ 1000,
+ 3 * 1000,
+ 3 * 1000);
+ }
+
+ protected void tearDown()
+ {
+ runner.close();
+ go(); //get any interrupt ignorers going...
+ }
+
+ private synchronized void go()
+ {
+ no_go = false;
+ this.notifyAll();
+ }
+
+ public void testDeadlockCase()
+ {
+ try
+ {
+ DumbTask dt = new DumbTask( true );
+ for( int i = 0; i < 5; ++i )
+ runner.postRunnable( dt );
+ Thread.sleep(500);
+ assertEquals("we should have three running tasks", 3, runner.getActiveCount() );
+ assertEquals("we should have two pending tasks", 2, runner.getPendingTaskCount() );
+ Thread.sleep(10000); // not strictly safe, but should be plenty of time to interrupt and be done
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ fail("Unexpected InterruptedException: " + e);
+ }
+ }
+
+ class DumbTask implements Runnable
+ {
+ boolean ignore_interrupts;
+
+ DumbTask()
+ { this( false ); }
+
+ DumbTask(boolean ignore_interrupts)
+ { this.ignore_interrupts = ignore_interrupts; }
+
+ public void run()
+ {
+ try
+ {
+ synchronized (ThreadPoolAsynchronousRunnerJUnitTestCase.this)
+ {
+ while (no_go)
+ {
+ try { ThreadPoolAsynchronousRunnerJUnitTestCase.this.wait(); }
+ catch (InterruptedException e)
+ {
+ if (ignore_interrupts)
+ System.err.println(this + ": interrupt ignored!");
+ else
+ {
+ e.fillInStackTrace();
+ throw e;
+ }
+ }
+ }
+ //System.err.println( ++gone );
+ ThreadPoolAsynchronousRunnerJUnitTestCase.this.notifyAll();
+ }
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+ }
+
+ public static void main(String[] argv)
+ {
+ junit.textui.TestRunner.run( new TestSuite( ThreadPoolAsynchronousRunnerJUnitTestCase.class ) );
+ //junit.swingui.TestRunner.run( SqlUtilsJUnitTestCase.class );
+ //new SqlUtilsJUnitTestCase().testGoodDebugLoggingOfNestedExceptions();
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/beans/BeansUtils.java b/src/classes/com/mchange/v2/beans/BeansUtils.java
new file mode 100644
index 0000000..7d67a8c
--- /dev/null
+++ b/src/classes/com/mchange/v2/beans/BeansUtils.java
@@ -0,0 +1,493 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.beans;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.util.*;
+import com.mchange.v2.log.*;
+
+import com.mchange.v2.lang.Coerce;
+
+public final class BeansUtils
+{
+ final static MLogger logger = MLog.getLogger( BeansUtils.class );
+
+ final static Object[] EMPTY_ARGS = new Object[0];
+
+ public static PropertyEditor findPropertyEditor( PropertyDescriptor pd )
+ {
+ PropertyEditor out = null;
+ Class editorClass = null;
+ try
+ {
+ editorClass = pd.getPropertyEditorClass();
+ if (editorClass != null)
+ out = (PropertyEditor) editorClass.newInstance();
+ }
+ catch (Exception e)
+ {
+// e.printStackTrace();
+// System.err.println("WARNING: Bad property editor class " + editorClass.getName() +
+// " registered for property " + pd.getName());
+ if (logger.isLoggable( MLevel.WARNING ) )
+ logger.log(MLevel.WARNING, "Bad property editor class " + editorClass.getName() + " registered for property " + pd.getName(), e);
+ }
+
+ if ( out == null )
+ out = PropertyEditorManager.findEditor( pd.getPropertyType() );
+ return out;
+ }
+
+ public static boolean equalsByAccessibleProperties( Object bean0, Object bean1 )
+ throws IntrospectionException
+ { return equalsByAccessibleProperties( bean0, bean1, Collections.EMPTY_SET ); }
+
+ public static boolean equalsByAccessibleProperties( Object bean0, Object bean1, Collection ignoreProps )
+ throws IntrospectionException
+ {
+ Map m0 = new HashMap();
+ Map m1 = new HashMap();
+ extractAccessiblePropertiesToMap( m0, bean0, ignoreProps );
+ extractAccessiblePropertiesToMap( m1, bean1, ignoreProps );
+ //System.err.println("Map0 -> " + m0);
+ //System.err.println("Map1 -> " + m1);
+ return m0.equals(m1);
+ }
+
+ public static void overwriteAccessibleProperties( Object sourceBean, Object destBean )
+ throws IntrospectionException
+ { overwriteAccessibleProperties( sourceBean, destBean, Collections.EMPTY_SET ); }
+
+ public static void overwriteAccessibleProperties( Object sourceBean, Object destBean, Collection ignoreProps )
+ throws IntrospectionException
+ {
+ try
+ {
+ BeanInfo beanInfo = Introspector.getBeanInfo( sourceBean.getClass(), Object.class ); //so we don't see message about getClass()
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ for( int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ if ( ignoreProps.contains( pd.getName() ) )
+ continue;
+
+ Method getter = pd.getReadMethod();
+ Method setter = pd.getWriteMethod();
+
+ if ( getter == null || setter == null )
+ {
+ if ( pd instanceof IndexedPropertyDescriptor )
+ {
+// System.err.println("WARNING: BeansUtils.overwriteAccessibleProperties() does not");
+// System.err.println("support indexed properties that do not provide single-valued");
+// System.err.println("array getters and setters! [The indexed methods provide no means");
+// System.err.println("of modifying the size of the array in the destination bean if");
+// System.err.println("it does not match the source.]");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning("BeansUtils.overwriteAccessibleProperties() does not" +
+ " support indexed properties that do not provide single-valued" +
+ " array getters and setters! [The indexed methods provide no means" +
+ " of modifying the size of the array in the destination bean if" +
+ " it does not match the source.]");
+ }
+
+ //System.err.println("Property inaccessible for overwriting: " + pd.getName());
+ if (logger.isLoggable( MLevel.INFO ))
+ logger.info("Property inaccessible for overwriting: " + pd.getName());
+ }
+ else
+ {
+ Object value = getter.invoke( sourceBean, EMPTY_ARGS );
+ setter.invoke( destBean, new Object[] { value } );
+ }
+ }
+ }
+ catch ( IntrospectionException e )
+ { throw e; }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "Converting exception to throwable IntrospectionException" );
+
+ throw new IntrospectionException( e.getMessage() );
+ }
+ }
+
+ public static void overwriteAccessiblePropertiesFromMap( Map sourceMap, Object destBean, boolean skip_nulls )
+ throws IntrospectionException
+ { overwriteAccessiblePropertiesFromMap( sourceMap, destBean, skip_nulls, Collections.EMPTY_SET ); }
+
+ public static void overwriteAccessiblePropertiesFromMap( Map sourceMap, Object destBean, boolean skip_nulls, Collection ignoreProps )
+ throws IntrospectionException
+ {
+ overwriteAccessiblePropertiesFromMap( sourceMap,
+ destBean,
+ skip_nulls,
+ ignoreProps,
+ false,
+ MLevel.WARNING,
+ MLevel.WARNING,
+ true);
+ }
+
+ public static void overwriteAccessiblePropertiesFromMap( Map sourceMap,
+ Object destBean,
+ boolean skip_nulls,
+ Collection ignoreProps,
+ boolean coerce_strings,
+ MLevel cantWriteLevel,
+ MLevel cantCoerceLevel,
+ boolean die_on_one_prop_failure)
+ throws IntrospectionException
+ {
+ if (cantWriteLevel == null)
+ cantWriteLevel = MLevel.WARNING;
+ if (cantCoerceLevel == null)
+ cantCoerceLevel = MLevel.WARNING;
+
+ Set sourceMapProps = sourceMap.keySet();
+
+ String propName = null;
+ BeanInfo beanInfo = Introspector.getBeanInfo( destBean.getClass(), Object.class ); //so we don't see message about getClass()
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ //System.err.println("ignoreProps: " + ignoreProps );
+ for( int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ propName = pd.getName();
+
+ if (! sourceMapProps.contains( propName ))
+ continue;
+
+ if ( ignoreProps != null && ignoreProps.contains( propName ) )
+ {
+ //System.err.println("ignoring: " + propName);
+ continue;
+ }
+ //else
+ // System.err.println("not ignoring: " + propName);
+
+ Object propVal = sourceMap.get( propName );
+ if (propVal == null)
+ {
+ if (skip_nulls) continue;
+ //do we need to worry about primitives here?
+ }
+
+ Method setter = pd.getWriteMethod();
+ boolean rethrow = false;
+
+ Class propType = pd.getPropertyType();;
+
+// try
+// {
+
+ if ( setter == null )
+ {
+ if ( pd instanceof IndexedPropertyDescriptor )
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.finer("BeansUtils.overwriteAccessiblePropertiesFromMap() does not" +
+ " support indexed properties that do not provide single-valued" +
+ " array getters and setters! [The indexed methods provide no means" +
+ " of modifying the size of the array in the destination bean if" +
+ " it does not match the source.]");
+
+ }
+
+ if ( logger.isLoggable( cantWriteLevel ))
+ {
+ String msg = "Property inaccessible for overwriting: " + propName;
+ logger.log( cantWriteLevel, msg );
+ if (die_on_one_prop_failure)
+ {
+ rethrow = true;
+ throw new IntrospectionException( msg );
+ }
+ }
+
+ }
+ else
+ {
+ if (coerce_strings &&
+ propVal != null &&
+ propVal.getClass() == String.class &&
+ (propType = pd.getPropertyType()) != String.class &&
+ Coerce.canCoerce( propType ))
+ {
+ Object coercedPropVal;
+ try
+ {
+ coercedPropVal = Coerce.toObject( (String) propVal, propType );
+ //System.err.println(propName + "-> coercedPropVal: " + coercedPropVal);
+ setter.invoke( destBean, new Object[] { coercedPropVal } );
+ }
+ catch (IllegalArgumentException e)
+ {
+ // thrown by Coerce.toObject()
+ // recall that NumberFormatException inherits from IllegalArgumentException
+ String msg =
+ "Failed to coerce property: " + propName +
+ " [propVal: " + propVal + "; propType: " + propType + "]";
+ if ( logger.isLoggable( cantCoerceLevel ) )
+ logger.log( cantCoerceLevel, msg, e );
+ if (die_on_one_prop_failure)
+ {
+ rethrow = true;
+ throw new IntrospectionException( msg );
+ }
+ }
+ catch (Exception e)
+ {
+ String msg =
+ "Failed to set property: " + propName +
+ " [propVal: " + propVal + "; propType: " + propType + "]";
+ if ( logger.isLoggable( cantWriteLevel ) )
+ logger.log( cantWriteLevel, msg, e );
+ if (die_on_one_prop_failure)
+ {
+ rethrow = true;
+ throw new IntrospectionException( msg );
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ //System.err.println("invoking method: " + setter);
+ setter.invoke( destBean, new Object[] { propVal } );
+ }
+ catch (Exception e)
+ {
+ String msg =
+ "Failed to set property: " + propName +
+ " [propVal: " + propVal + "; propType: " + propType + "]";
+ if ( logger.isLoggable( cantWriteLevel ) )
+ logger.log( cantWriteLevel, msg, e );
+ if (die_on_one_prop_failure)
+ {
+ rethrow = true;
+ throw new IntrospectionException( msg );
+ }
+ }
+ }
+ }
+// }
+// catch (Exception e)
+// {
+// if (e instanceof IntrospectionException && rethrow)
+// throw (IntrospectionException) e;
+// else
+// {
+// String msg =
+// "An exception occurred while trying to set property '" + propName +
+// "' to value '" + propVal + "'. ";
+// logger.log(MLevel.WARNING, msg, e);
+// if (die_on_one_prop_failure)
+// {
+// rethrow = true;
+// throw new IntrospectionException( msg + e.toString());
+// }
+// }
+// }
+ }
+ }
+
+ public static void appendPropNamesAndValues(StringBuffer appendIntoMe, Object bean, Collection ignoreProps) throws IntrospectionException
+ {
+ Map tmp = new TreeMap( String.CASE_INSENSITIVE_ORDER );
+ extractAccessiblePropertiesToMap( tmp, bean, ignoreProps );
+ boolean first = true;
+ for (Iterator ii = tmp.keySet().iterator(); ii.hasNext(); )
+ {
+ String key = (String) ii.next();
+ Object val = tmp.get( key );
+ if (first)
+ first = false;
+ else
+ appendIntoMe.append( ", " );
+ appendIntoMe.append( key );
+ appendIntoMe.append( " -> ");
+ appendIntoMe.append( val );
+ }
+ }
+
+
+ public static void extractAccessiblePropertiesToMap( Map fillMe, Object bean ) throws IntrospectionException
+ { extractAccessiblePropertiesToMap( fillMe, bean, Collections.EMPTY_SET ); }
+
+ public static void extractAccessiblePropertiesToMap( Map fillMe, Object bean, Collection ignoreProps ) throws IntrospectionException
+ {
+ String propName = null;
+ try
+ {
+ BeanInfo bi = Introspector.getBeanInfo( bean.getClass(), Object.class );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ propName = pd.getName();
+ if (ignoreProps.contains( propName ))
+ continue;
+
+ Method readMethod = pd.getReadMethod();
+ Object propVal = readMethod.invoke( bean, EMPTY_ARGS );
+ fillMe.put( propName, propVal );
+ }
+ }
+ catch ( IntrospectionException e )
+ {
+// if (propName != null)
+// System.err.println("Problem occurred while overwriting property: " + propName);
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning("Problem occurred while overwriting property: " + propName);
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ))
+ logger.logp( MLevel.FINE,
+ BeansUtils.class.getName(),
+ "extractAccessiblePropertiesToMap( Map fillMe, Object bean, Collection ignoreProps )",
+ (propName != null ? "Problem occurred while overwriting property: " + propName : "") + " throwing...",
+ e );
+ throw e;
+ }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ))
+ logger.logp( MLevel.FINE,
+ BeansUtils.class.getName(),
+ "extractAccessiblePropertiesToMap( Map fillMe, Object bean, Collection ignoreProps )",
+ "Caught unexpected Exception; Converting to IntrospectionException.",
+ e );
+ throw new IntrospectionException( e.toString() + (propName == null ? "" : " [" + propName + ']') );
+ }
+ }
+
+ private static void overwriteProperty( String propName, Object value, Method putativeSetter, Object target )
+ throws Exception
+ {
+ if ( putativeSetter.getDeclaringClass().isAssignableFrom( target.getClass() ) )
+ putativeSetter.invoke( target, new Object[] { value } );
+ else
+ {
+ BeanInfo beanInfo = Introspector.getBeanInfo( target.getClass(), Object.class );
+ PropertyDescriptor pd = null;
+
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ for( int i = 0, len = pds.length; i < len; ++i)
+ if (propName.equals( pds[i].getName() ))
+ {
+ pd = pds[i];
+ break;
+ }
+
+ Method targetSetter = pd.getWriteMethod();
+ targetSetter.invoke( target, new Object[] { value } );
+ }
+ }
+
+
+ public static void overwriteSpecificAccessibleProperties( Object sourceBean, Object destBean, Collection props )
+ throws IntrospectionException
+ {
+ try
+ {
+ Set _props = new HashSet(props);
+
+ BeanInfo beanInfo = Introspector.getBeanInfo( sourceBean.getClass(), Object.class ); //so we don't see message about getClass()
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ for( int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ String name = pd.getName();
+ if (! _props.remove( name ) )
+ continue;
+
+ Method getter = pd.getReadMethod();
+ Method setter = pd.getWriteMethod();
+
+ if ( getter == null || setter == null )
+ {
+ if ( pd instanceof IndexedPropertyDescriptor )
+ {
+// System.err.println("WARNING: BeansUtils.overwriteAccessibleProperties() does not");
+// System.err.println("support indexed properties that do not provide single-valued");
+// System.err.println("array getters and setters! [The indexed methods provide no means");
+// System.err.println("of modifying the size of the array in the destination bean if");
+// System.err.println("it does not match the source.]");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning("BeansUtils.overwriteAccessibleProperties() does not" +
+ " support indexed properties that do not provide single-valued" +
+ " array getters and setters! [The indexed methods provide no means" +
+ " of modifying the size of the array in the destination bean if" +
+ " it does not match the source.]");
+ }
+
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.info("Property inaccessible for overwriting: " + pd.getName());
+ }
+ else
+ {
+ Object value = getter.invoke( sourceBean, EMPTY_ARGS );
+ overwriteProperty( name, value, setter, destBean );
+ //setter.invoke( destBean, new Object[] { value } );
+ }
+ }
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ for (Iterator ii = _props.iterator(); ii.hasNext(); )
+ logger.warning("failed to find expected property: " + ii.next());
+ //System.err.println("failed to find expected property: " + ii.next());
+ }
+ }
+ catch ( IntrospectionException e )
+ { throw e; }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ))
+ logger.logp( MLevel.FINE,
+ BeansUtils.class.getName(),
+ "overwriteSpecificAccessibleProperties( Object sourceBean, Object destBean, Collection props )",
+ "Caught unexpected Exception; Converting to IntrospectionException.",
+ e );
+ throw new IntrospectionException( e.getMessage() );
+ }
+ }
+
+ public static void debugShowPropertyChange( PropertyChangeEvent evt )
+ {
+ System.err.println("PropertyChangeEvent: [ propertyName -> " + evt.getPropertyName() +
+ ", oldValue -> " + evt.getOldValue() +
+ ", newValue -> " + evt.getNewValue() +
+ " ]");
+ }
+
+ private BeansUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/beans/StateBean.java b/src/classes/com/mchange/v2/beans/StateBean.java
new file mode 100644
index 0000000..31d36a5
--- /dev/null
+++ b/src/classes/com/mchange/v2/beans/StateBean.java
@@ -0,0 +1,27 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.beans;
+
+public interface StateBean
+{}
diff --git a/src/classes/com/mchange/v2/beans/StateBeanExporter.java b/src/classes/com/mchange/v2/beans/StateBeanExporter.java
new file mode 100644
index 0000000..41becb3
--- /dev/null
+++ b/src/classes/com/mchange/v2/beans/StateBeanExporter.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.beans;
+
+/**
+ * offers a bean suitable for bean getter/setter-based serialization/deserialization
+ * a la XMLSerializer. Should have a constructor that accepts the exported Object and
+ * constructs a new bean with the same state.
+ */
+public interface StateBeanExporter
+{
+ public StateBean exportStateBean();
+}
diff --git a/src/classes/com/mchange/v2/beans/StateBeanImporter.java b/src/classes/com/mchange/v2/beans/StateBeanImporter.java
new file mode 100644
index 0000000..76e24be
--- /dev/null
+++ b/src/classes/com/mchange/v2/beans/StateBeanImporter.java
@@ -0,0 +1,29 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.beans;
+
+public interface StateBeanImporter extends StateBeanExporter
+{
+ public void importStateBean(StateBean sb);
+}
diff --git a/src/classes/com/mchange/v2/c3p0/AbstractConnectionCustomizer.java b/src/classes/com/mchange/v2/c3p0/AbstractConnectionCustomizer.java
new file mode 100644
index 0000000..e1bf3ec
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/AbstractConnectionCustomizer.java
@@ -0,0 +1,51 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * An abstract implementation of the
+ * ConnectionCustomizer interface
+ * in which all methods are no-ops.
+ *
+ * Just a convenience class since
+ * most clients will only need to
+ * implement a single method.
+ */
+public abstract class AbstractConnectionCustomizer implements ConnectionCustomizer
+{
+ public void onAcquire( Connection c, String parentDataSourceIdentityToken ) throws Exception
+ {}
+
+ public void onDestroy( Connection c, String parentDataSourceIdentityToken ) throws Exception
+ {}
+
+ public void onCheckOut( Connection c, String parentDataSourceIdentityToken ) throws Exception
+ {}
+
+ public void onCheckIn( Connection c, String parentDataSourceIdentityToken ) throws Exception
+ {}
+}
diff --git a/src/classes/com/mchange/v2/c3p0/AbstractConnectionTester.java b/src/classes/com/mchange/v2/c3p0/AbstractConnectionTester.java
new file mode 100644
index 0000000..b847253
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/AbstractConnectionTester.java
@@ -0,0 +1,83 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+
+/**
+ * <p>Having expanded the once-simple ConnectionTester interface to support both
+ * user-specified queries and return of root cause Exceptions (via an out-param),
+ * this interface has grown unnecessarily complex.</p>
+ *
+ * <p>If you wish to implement a custom Connection tester, here is the simple
+ * way to do it</p>
+ *
+ * <ol>
+ * <li>Extend {@link com.mchange.v2.c3p0.AbstractConnectionTester}</li>
+ * <li>Override only the two abstract methods</li>
+ * <ul>
+ * <li><tt>public int activeCheckConnection(Connection c, String preferredTestQuery, Throwable[] rootCauseOutParamHolder)</tt></li>
+ * <li><tt>public int statusOnException(Connection c, Throwable t, String preferredTestQuery, Throwable[] rootCauseOutParamHolder)</tt></li>
+ * </ul>
+ * <li>Take care to ensure that your methods are defined to allow <tt>preferredTestQuery</tt> and
+ * <tt>rootCauseOutParamHolder</tt> to be <tt>null</tt>.</li>
+ * </ol>
+ *
+ * <p>Parameter <tt>rootCauseOutParamHolder</tt> is an optional parameter, which if supplied, will be a Throwable array whose size
+ * it at least one. If a Connection test fails because of some Exception, the Connection tester may set this Exception as the
+ * zero-th element of the array to provide information about why and how the test failed.</p>
+ */
+public abstract class AbstractConnectionTester implements UnifiedConnectionTester
+{
+ /**
+ * Override, but remember that <tt>preferredTestQuery</tt> and <tt>rootCauseOutParamHolder</tt>
+ * can be null.
+ */
+ public abstract int activeCheckConnection(Connection c, String preferredTestQuery, Throwable[] rootCauseOutParamHolder);
+
+ /**
+ * Override, but remember that <tt>preferredTestQuery</tt> and <tt>rootCauseOutParamHolder</tt>
+ * can be null.
+ */
+ public abstract int statusOnException(Connection c, Throwable t, String preferredTestQuery, Throwable[] rootCauseOutParamHolder);
+
+ //usually just leave the rest of these as-is
+ public int activeCheckConnection(Connection c)
+ { return activeCheckConnection( c, null, null); }
+
+ public int activeCheckConnection(Connection c, Throwable[] rootCauseOutParamHolder)
+ { return activeCheckConnection( c, null, rootCauseOutParamHolder); }
+
+ public int activeCheckConnection(Connection c, String preferredTestQuery)
+ { return activeCheckConnection( c, preferredTestQuery, null); }
+
+ public int statusOnException(Connection c, Throwable t)
+ { return statusOnException( c, t, null, null); }
+
+ public int statusOnException(Connection c, Throwable t, Throwable[] rootCauseOutParamHolder)
+ { return statusOnException( c, t, null, rootCauseOutParamHolder); }
+
+ public int statusOnException(Connection c, Throwable t, String preferredTestQuery)
+ { return statusOnException( c, t, preferredTestQuery, null); }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/C3P0ProxyConnection.java b/src/classes/com/mchange/v2/c3p0/C3P0ProxyConnection.java
new file mode 100644
index 0000000..a97ef57
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/C3P0ProxyConnection.java
@@ -0,0 +1,83 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * <p><b>Most clients need never use or know about this interface -- c3p0-provided Connections
+ * can be treated like any other Connection.</b></p>
+ *
+ * <p>An interface implemented by proxy Connections returned
+ * by c3p0 PooledDataSources. It provides protected access to the underlying
+ * dbms-vendor specific Connection, which may be useful if you want to
+ * access non-standard API offered by your jdbc driver.
+ */
+public interface C3P0ProxyConnection extends Connection
+{
+ /**
+ * A token representing an unwrapped, unproxied jdbc Connection
+ * for use in {@link #rawConnectionOperation}
+ */
+ public final static Object RAW_CONNECTION = new Object();
+
+ /**
+ * <p>Allows one to work with the unproxied, raw Connection. Some
+ * database companies never got over the "common interfaces mean
+ * no more vendor lock-in!" thing, and offer non-standard API
+ * on their Connections. This method permits you to "pierce" the
+ * connection-pooling layer to call non-standard methods on the
+ * original Connection, or to pass the original Connections to
+ * functions that are not implementation neutral.</p>
+ *
+ * <p>To use this functionality, you'll need to cast a Connection
+ * retrieved from a c3p0 PooledDataSource to a
+ * C3P0ProxyConnection.</p>
+ *
+ * <p>This method works by making a reflective call of method <tt>m</tt> on
+ * Object <tt>target</tt> (which may be null for static methods), passing
+ * and argument list <tt>args</tt>. For the method target, or for any argument,
+ * you may substitute the special token <tt>C3P0ProxyConnection.RAW_CONNECTION</tt></p>
+ *
+ * <p>Any Statements or ResultSets returned by the operation will be proxied
+ * and c3p0-managed, meaning that these resources will be automatically closed
+ * if the user does not close them first when this Connection is checked back
+ * into the pool. <b>Any other resources returned by the operation are the user's
+ * responsibility to clean up!</b></p>
+ *
+ * <p>Incautious use of this method can corrupt the Connection pool, by breaking the invariant
+ * that all checked-in Connections should be equivalent. If your vendor supplies API
+ * that allows you to modify the state or configuration of a Connection in some nonstandard way,
+ * you might use this method to do so, and then check the Connection back into the pool.
+ * When you fetch another Connection from the PooledDataSource, it will be undefined
+ * whether the Connection returned will have your altered configuration, or the default
+ * configuration of a "fresh" Connection. Thus, it is inadvisable to use this method to call
+ * nonstandard mutators.
+ */
+ public Object rawConnectionOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException;
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/C3P0ProxyStatement.java b/src/classes/com/mchange/v2/c3p0/C3P0ProxyStatement.java
new file mode 100644
index 0000000..fe4ac6c
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/C3P0ProxyStatement.java
@@ -0,0 +1,84 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Statement;
+import java.sql.SQLException;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * <p><b>Most clients need never use or know about this interface -- c3p0-provided Statements
+ * can be treated like any other Statement.</b></p>
+ *
+ * <p>An interface implemented by proxy Connections returned
+ * by c3p0 PooledDataSources. It provides protected access to the underlying
+ * dbms-vendor specific Connection, which may be useful if you want to
+ * access non-standard API offered by your jdbc driver.
+ */
+public interface C3P0ProxyStatement extends Statement
+{
+ /**
+ * A token representing an unwrapped, unproxied jdbc Connection
+ * for use in {@link #rawStatementOperation}
+ */
+ public final static Object RAW_STATEMENT = new Object();
+
+ /**
+ * <p>Allows one to work with the unproxied, raw vendor-provided Statement . Some
+ * database companies never got over the "common interfaces mean
+ * no more vendor lock-in!" thing, and offer non-standard API
+ * on their Statements. This method permits you to "pierce" the
+ * connection-pooling layer to call non-standard methods on the
+ * original Statement, or to pass the original Statement to
+ * functions that are not implementation neutral.</p>
+ *
+ * <p>To use this functionality, you'll need to cast a Statement
+ * retrieved from a c3p0-provided Connection to a
+ * C3P0ProxyStatement.</p>
+ *
+ * <p>This method works by making a reflective call of method <tt>m</tt> on
+ * Object <tt>target</tt> (which may be null for static methods), passing
+ * and argument list <tt>args</tt>. For the method target, or for any argument,
+ * you may substitute the special token <tt>C3P0ProxyStatement.RAW_STATEMENT</tt></p>
+ *
+ * <p>Any ResultSets returned by the operation will be proxied
+ * and c3p0-managed, meaning that these resources will be automatically closed
+ * if the user does not close them first when this Statement is closed or checked
+ * into the statement cache. <b>Any other resources returned by the operation are the user's
+ * responsibility to clean up!</b></p>
+ *
+ * <p>If you have turned statement pooling on, incautious use of this method can corrupt the
+ * PreparedStatement cache, by breaking the invariant
+ * that all cached PreparedStatements should be equivalent to a PreparedStatement newly created
+ * with the same arguments to prepareStatement(...) or prepareCall(...). If your vendor supplies API
+ * that allows you to modify the state or configuration of a Statement in some nonstandard way,
+ * and you do not undo this modification prior to closing the Statement or the Connection that
+ * prepared it, future preparers of the same Statement may or may not see your modification,
+ * depending on your use of the cache. Thus, it is inadvisable to use this method to call
+ * nonstandard mutators on PreparedStatements if statement pooling is turned on..
+ */
+ public Object rawStatementOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException;
+}
diff --git a/src/classes/com/mchange/v2/c3p0/C3P0Registry.java b/src/classes/com/mchange/v2/c3p0/C3P0Registry.java
new file mode 100644
index 0000000..eeeb1c0
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/C3P0Registry.java
@@ -0,0 +1,339 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.util.*;
+import com.mchange.v2.coalesce.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.c3p0.cfg.C3P0ConfigUtils;
+import com.mchange.v2.c3p0.impl.*;
+
+import java.sql.SQLException;
+import com.mchange.v2.c3p0.impl.IdentityTokenized;
+import com.mchange.v2.c3p0.subst.C3P0Substitutions;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.util.DoubleWeakHashMap;
+
+import com.mchange.v2.c3p0.management.*;
+
+/*
+ * The primary purpose of C3P0Registry is to maintain a mapping of "identityTokens"
+ * to c3p0 DataSources so that if the same DataSource is looked up (and deserialized
+ * or dereferenced) via JNDI, c3p0 can ensure that the same instance is always returned.
+ * But there are subtle issues here. If C3P0Registry maintains hard references to
+ * DataSources, then they can never be garbage collected. But if c3p0 retains only
+ * weak references, then applications that look up DataSources, then dereference them,
+ * and then re-look them up again (not a great idea, but not uncommon) might see
+ * distinct DataSources over multiple lookups.
+ *
+ * C3P0 resolves this issue has followed: At first creation or lookup of a PooledDataSource,
+ * c3p0 creates a hard reference to that DataSource. So long as the DataSource has not
+ * been close()ed or DataSources.destroy()ed, subsequent lookups will consistently
+ * return the same DataSource. If the DataSource is never closed, then there is a potential
+ * memory leak (as well as the potential Thread leak and Connection leak). But if
+ * the DataSource is close()ed, only weak refernces to the DataSource will be retained.
+ * A lookup of a DataSource after it has been close()ed within the current VM may
+ * return the previously close()ed instance, or may return a fresh instance, depending
+ * on whether the weak reference has been cleared. In other words, the result of
+ * looking up a DataSource after having close()ed it in the current VM is undefined.
+ *
+ * Note that unpooled c3p0 DataSources are always held by weak references, since
+ * they are never explicitly close()ed. The result of looking up an unpooled DataSource,
+ * modifying it, dereferencing it, and then relooking up is therefore undefined as well.
+ *
+ * These issues are mostly academic. Under normal use scenarios, how c3p0 deals with
+ * maintaining its registry doesn't much matter. In the past, c3p0 maintained hard
+ * references to DataSources indefinitely. At least one user ran into side effects
+ * of the unwanted retention of old DataSources (in a process left to run for months
+ * at a time, and frequently reconstructing multiple DataSources), so now we take care
+ * to ensure that when users properly close() and dereference DataSources, they can
+ * indeed be garbage collected.
+ */
+public final class C3P0Registry
+{
+ private final static String MC_PARAM = "com.mchange.v2.c3p0.management.ManagementCoordinator";
+
+ //MT: thread-safe
+ final static MLogger logger = MLog.getLogger( C3P0Registry.class );
+
+ //MT: protected by class' lock
+ static boolean banner_printed = false;
+
+ //MT: protected by class' lock
+ static boolean registry_mbean_registered = false;
+
+ //MT: thread-safe, immutable
+ private static CoalesceChecker CC = IdentityTokenizedCoalesceChecker.INSTANCE;
+
+ //MT: protected by class' lock
+ //a weak, unsynchronized coalescer
+ private static Coalescer idtCoalescer = CoalescerFactory.createCoalescer(CC, true , false);
+
+ //MT: protected by class' lock
+ private static Map tokensToTokenized = new DoubleWeakHashMap();
+
+ //MT: protected by class' lock
+ private static HashSet unclosedPooledDataSources = new HashSet();
+
+ //MT: protected by its own lock
+ private static Map classNamesToConnectionTesters = Collections.synchronizedMap( new HashMap() );
+
+ //MT: protected by its own lock
+ private static Map classNamesToConnectionCustomizers = Collections.synchronizedMap( new HashMap() );
+
+ private static ManagementCoordinator mc;
+
+ static
+ {
+ classNamesToConnectionTesters.put(C3P0Defaults.connectionTesterClassName(), C3P0Defaults.connectionTester());
+
+ String userManagementCoordinator = C3P0ConfigUtils.getPropFileConfigProperty(MC_PARAM);
+ if (userManagementCoordinator != null)
+ {
+ try
+ {
+ mc = (ManagementCoordinator) Class.forName(userManagementCoordinator).newInstance();
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "Could not instantiate user-specified ManagementCoordinator " + userManagementCoordinator +
+ ". Using NullManagementCoordinator (c3p0 JMX management disabled!)",
+ e );
+ mc = new NullManagementCoordinator();
+ }
+ }
+ else
+ {
+ try
+ {
+ Class.forName("java.lang.management.ManagementFactory");
+
+ mc = (ManagementCoordinator) Class.forName( "com.mchange.v2.c3p0.management.ActiveManagementCoordinator" ).newInstance();
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.log( MLevel.INFO,
+ "jdk1.5 management interfaces unavailable... JMX support disabled.",
+ e);
+ mc = new NullManagementCoordinator();
+ }
+ }
+ }
+
+ public static ConnectionTester getConnectionTester( String className )
+ {
+ try
+ {
+ ConnectionTester out = (ConnectionTester) classNamesToConnectionTesters.get( className );
+ if (out == null)
+ {
+ out = (ConnectionTester) Class.forName( className ).newInstance();
+ classNamesToConnectionTesters.put( className, out );
+ }
+ return out;
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "Could not create for find ConnectionTester with class name '" +
+ className + "'. Using default.",
+ e );
+ return C3P0Defaults.connectionTester();
+ }
+ }
+
+ public static ConnectionCustomizer getConnectionCustomizer( String className ) throws SQLException
+ {
+ if ( className == null )
+ return null;
+ else
+ {
+ try
+ {
+ ConnectionCustomizer out = (ConnectionCustomizer) classNamesToConnectionCustomizers.get( className );
+ if (out == null)
+ {
+ out = (ConnectionCustomizer) Class.forName( className ).newInstance();
+ classNamesToConnectionCustomizers.put( className, out );
+ }
+ return out;
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "Could not create for find ConnectionCustomizer with class name '" +
+ className + "'.",
+ e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+ }
+
+ // must be called from a static sync'ed method
+ private static void banner()
+ {
+ if (! banner_printed )
+ {
+ if (logger.isLoggable( MLevel.INFO ) )
+ logger.info("Initializing c3p0-" + C3P0Substitutions.VERSION + " [built " + C3P0Substitutions.TIMESTAMP +
+ "; debug? " + C3P0Substitutions.DEBUG +
+ "; trace: " + C3P0Substitutions.TRACE
+ +']');
+ banner_printed = true;
+ }
+ }
+
+ // must be called from a static, sync'ed method
+ private static void attemptRegisterRegistryMBean()
+ {
+ if (! registry_mbean_registered)
+ {
+ mc.attemptManageC3P0Registry();
+ registry_mbean_registered = true;
+ }
+ }
+
+ // must be called with class' lock
+ private static boolean isIncorporated( IdentityTokenized idt )
+ { return tokensToTokenized.keySet().contains( idt.getIdentityToken() ); }
+
+ // must be called with class' lock
+ private static void incorporate( IdentityTokenized idt )
+ {
+ tokensToTokenized.put( idt.getIdentityToken(), idt );
+ if (idt instanceof PooledDataSource)
+ {
+ unclosedPooledDataSources.add( idt );
+ mc.attemptManagePooledDataSource( (PooledDataSource) idt );
+ }
+ }
+
+ public static synchronized IdentityTokenized reregister(IdentityTokenized idt)
+ {
+ if (idt instanceof PooledDataSource)
+ {
+ banner();
+ attemptRegisterRegistryMBean();
+ }
+
+ if (idt.getIdentityToken() == null)
+ throw new RuntimeException("[c3p0 issue] The identityToken of a registered object should be set prior to registration.");
+
+ IdentityTokenized coalesceCheck = (IdentityTokenized) idtCoalescer.coalesce(idt);
+
+ if (! isIncorporated( coalesceCheck ))
+ incorporate( coalesceCheck );
+
+ return coalesceCheck;
+ }
+
+ public static synchronized void markClosed(PooledDataSource pds)
+ {
+ unclosedPooledDataSources.remove(pds);
+ mc.attemptUnmanagePooledDataSource( pds );
+ if (unclosedPooledDataSources.isEmpty())
+ {
+ mc.attemptUnmanageC3P0Registry();
+ registry_mbean_registered = false;
+ }
+ }
+
+ public synchronized static Set getPooledDataSources()
+ { return (Set) unclosedPooledDataSources.clone(); }
+
+ public synchronized static Set pooledDataSourcesByName( String dataSourceName )
+ {
+ Set out = new HashSet();
+ for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); )
+ {
+ PooledDataSource pds = (PooledDataSource) ii.next();
+ if ( pds.getDataSourceName().equals( dataSourceName ) )
+ out.add( pds );
+ }
+ return out;
+ }
+
+ public synchronized static PooledDataSource pooledDataSourceByName( String dataSourceName )
+ {
+ for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext(); )
+ {
+ PooledDataSource pds = (PooledDataSource) ii.next();
+ if ( pds.getDataSourceName().equals( dataSourceName ) )
+ return pds;
+ }
+ return null;
+ }
+
+ public synchronized static Set allIdentityTokens()
+ {
+ Set out = Collections.unmodifiableSet( tokensToTokenized.keySet() );
+ //System.err.println( "allIdentityTokens(): " + out );
+ return out;
+ }
+
+ public synchronized static Set allIdentityTokenized()
+ {
+ HashSet out = new HashSet();
+ out.addAll( tokensToTokenized.values() );
+ //System.err.println( "allIdentityTokenized(): " + out );
+ return Collections.unmodifiableSet( out );
+ }
+
+ public synchronized static Set allPooledDataSources()
+ {
+ Set out = Collections.unmodifiableSet( unclosedPooledDataSources );
+ //System.err.println( "allPooledDataSources(): " + out );
+ return out;
+ }
+
+ public synchronized static int getNumPooledDataSources()
+ { return unclosedPooledDataSources.size(); }
+
+ public synchronized static int getNumPoolsAllDataSources() throws SQLException
+ {
+ int count = 0;
+ for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext();)
+ {
+ PooledDataSource pds = (PooledDataSource) ii.next();
+ count += pds.getNumUserPools();
+ }
+ return count;
+ }
+
+ public synchronized int getNumThreadsAllThreadPools() throws SQLException
+ {
+ int count = 0;
+ for (Iterator ii = unclosedPooledDataSources.iterator(); ii.hasNext();)
+ {
+ PooledDataSource pds = (PooledDataSource) ii.next();
+ count += pds.getNumHelperThreads();
+ }
+ return count;
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/ComboPooledDataSource.java b/src/classes/com/mchange/v2/c3p0/ComboPooledDataSource.java
new file mode 100644
index 0000000..970c3cb
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/ComboPooledDataSource.java
@@ -0,0 +1,684 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.beans.*;
+import java.io.*;
+import java.sql.*;
+import java.util.*;
+import javax.naming.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.naming.*;
+import com.mchange.v2.c3p0.impl.*;
+
+import javax.sql.DataSource;
+import com.mchange.v2.beans.BeansUtils;
+import com.mchange.v2.c3p0.cfg.C3P0Config;
+
+/**
+ * <p>For the meaning of most of these properties, please see c3p0's top-level documentation!</p>
+ */
+public final class ComboPooledDataSource extends AbstractPoolBackedDataSource implements PooledDataSource, Serializable, Referenceable
+{
+ final static MLogger logger = MLog.getLogger( ComboPooledDataSource.class );
+
+ final static Set TO_STRING_IGNORE_PROPS = new HashSet( Arrays.asList( new String[] {
+ "connection",
+ "lastAcquisitionFailureDefaultUser",
+ "lastCheckinFailureDefaultUser",
+ "lastCheckoutFailureDefaultUser",
+ "lastConnectionTestFailureDefaultUser",
+ "lastIdleTestFailureDefaultUser",
+ "logWriter",
+ "loginTimeout",
+ "numBusyConnections",
+ "numBusyConnectionsAllUsers",
+ "numBusyConnectionsDefaultUser",
+ "numConnections",
+ "numConnectionsAllUsers",
+ "numConnectionsDefaultUser",
+ "numFailedCheckinsDefaultUser",
+ "numFailedCheckoutsDefaultUser",
+ "numFailedIdleTestsDefaultUser",
+ "numIdleConnections",
+ "numIdleConnectionsAllUsers",
+ "numIdleConnectionsDefaultUser",
+ "numUnclosedOrphanedConnections",
+ "numUnclosedOrphanedConnectionsAllUsers",
+ "numUnclosedOrphanedConnectionsDefaultUser",
+ "numUserPools",
+ "effectivePropertyCycleDefaultUser",
+ "startTimeMillisDefaultUser",
+ "statementCacheNumCheckedOutDefaultUser",
+ "statementCacheNumCheckedOutStatementsAllUsers",
+ "statementCacheNumConnectionsWithCachedStatementsAllUsers",
+ "statementCacheNumConnectionsWithCachedStatementsDefaultUser",
+ "statementCacheNumStatementsAllUsers",
+ "statementCacheNumStatementsDefaultUser",
+ "threadPoolSize",
+ "threadPoolNumActiveThreads",
+ "threadPoolNumIdleThreads",
+ "threadPoolNumTasksPending",
+ "threadPoolStackTraces",
+ "threadPoolStatus",
+ "overrideDefaultUser",
+ "overrideDefaultPassword",
+ "password",
+ "reference",
+ "upTimeMillisDefaultUser",
+ "user",
+ "userOverridesAsString",
+ "allUsers",
+ "connectionPoolDataSource"
+ } ) );
+
+ // not reassigned post-ctor; mutable elements protected by their own locks
+ // when (very rarely) necessery, we sync this -> wcpds -> dmds
+
+ // note that serialization of these guys happens via out superclass
+ // we just have to make sure they get properly reset on deserialization
+ transient DriverManagerDataSource dmds;
+ transient WrapperConnectionPoolDataSource wcpds;
+
+ public ComboPooledDataSource()
+ { this( true ); }
+
+ public ComboPooledDataSource( boolean autoregister )
+ {
+ super( autoregister );
+
+ // System.err.println("...Initializing ComboPooledDataSource.");
+
+ dmds = new DriverManagerDataSource();
+ wcpds = new WrapperConnectionPoolDataSource();
+
+ wcpds.setNestedDataSource( dmds );
+
+ try
+ { this.setConnectionPoolDataSource( wcpds ); }
+ catch (PropertyVetoException e)
+ {
+ logger.log(MLevel.WARNING, "Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet!", e);
+ throw new RuntimeException("Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet! " + e);
+ }
+
+ // set things up in case there are future changes to our ConnectionPoolDataSource
+ //
+ setUpPropertyEvents();
+ }
+
+ private void setUpPropertyEvents()
+ {
+ VetoableChangeListener wcpdsConsistencyEnforcer = new VetoableChangeListener()
+ {
+ // always called within synchronized mutators of the parent class... needn't explicitly sync here
+ public void vetoableChange( PropertyChangeEvent evt ) throws PropertyVetoException
+ {
+ String propName = evt.getPropertyName();
+ Object val = evt.getNewValue();
+
+ if ( "connectionPoolDataSource".equals( propName ) )
+ {
+ if (val instanceof WrapperConnectionPoolDataSource)
+ {
+ DataSource nested = (DataSource) ((WrapperConnectionPoolDataSource)val).getNestedDataSource();
+ if (! (nested instanceof DriverManagerDataSource) )
+ throw new PropertyVetoException("ComboPooledDataSource requires that its unpooled DataSource " +
+ " be set at all times, and that it be a" +
+ " com.mchange.v2.c3p0.DriverManagerDataSource. Bad: " + nested, evt);
+ }
+ else
+ throw new PropertyVetoException("ComboPooledDataSource requires that its ConnectionPoolDataSource " +
+ " be set at all times, and that it be a" +
+ " com.mchange.v2.c3p0.WrapperConnectionPoolDataSource. Bad: " + val, evt);
+ }
+ }
+ };
+ this.addVetoableChangeListener( wcpdsConsistencyEnforcer );
+
+ PropertyChangeListener wcpdsStateUpdater = new PropertyChangeListener()
+ {
+ public void propertyChange( PropertyChangeEvent evt )
+ { updateLocalVarsFromCpdsProp(); }
+ };
+ this.addPropertyChangeListener( wcpdsStateUpdater );
+ }
+
+ private void updateLocalVarsFromCpdsProp()
+ {
+ this.wcpds = (WrapperConnectionPoolDataSource) this.getConnectionPoolDataSource();
+ this.dmds = (DriverManagerDataSource) wcpds.getNestedDataSource();
+ }
+
+ public ComboPooledDataSource(String configName)
+ {
+ this();
+ initializeNamedConfig( configName );
+ }
+
+// // workaround sun big id #6342411 (in which reflective
+// // access to a public method of a non-public class fails,
+// // even if the non-public class is accessed via a public
+// // subclass)
+// public String getDataSourceName()
+// { return super.getDataSourceName(); }
+
+ // DriverManagerDataSourceProperties (count: 4)
+ public String getDescription()
+ { return dmds.getDescription(); }
+
+ public void setDescription( String description )
+ { dmds.setDescription( description ); }
+
+ public String getDriverClass()
+ { return dmds.getDriverClass(); }
+
+ public void setDriverClass( String driverClass ) throws PropertyVetoException
+ {
+ dmds.setDriverClass( driverClass );
+// System.err.println("setting driverClass: " + driverClass);
+ }
+
+ public String getJdbcUrl()
+ {
+// System.err.println("getting jdbcUrl: " + dmds.getJdbcUrl());
+ return dmds.getJdbcUrl();
+ }
+
+ public void setJdbcUrl( String jdbcUrl )
+ {
+ dmds.setJdbcUrl( jdbcUrl );
+ this.resetPoolManager( false );
+// System.err.println("setting jdbcUrl: " + jdbcUrl + " [dmds@" + C3P0ImplUtils.identityToken( dmds ) + "]");
+// if (jdbcUrl == null)
+// new Exception("*** NULL SETTER ***").printStackTrace();
+ }
+
+ public Properties getProperties()
+ {
+ //System.err.println("getting properties: " + dmds.getProperties());
+ return dmds.getProperties();
+ }
+
+ public void setProperties( Properties properties )
+ {
+ //System.err.println("setting properties: " + properties);
+ dmds.setProperties( properties );
+ this.resetPoolManager(false);
+ }
+
+ // DriverManagerDataSource "virtual properties" based on properties
+ public String getUser()
+ { return dmds.getUser(); }
+
+ public void setUser( String user )
+ {
+ dmds.setUser( user );
+ this.resetPoolManager( false );
+ }
+
+ public String getPassword()
+ { return dmds.getPassword(); }
+
+ public void setPassword( String password )
+ {
+ dmds.setPassword( password );
+ this.resetPoolManager( false );
+ }
+
+ // WrapperConnectionPoolDataSource properties
+ public int getCheckoutTimeout()
+ { return wcpds.getCheckoutTimeout(); }
+
+ public void setCheckoutTimeout( int checkoutTimeout )
+ {
+ wcpds.setCheckoutTimeout( checkoutTimeout );
+ this.resetPoolManager( false );
+ }
+
+ public int getAcquireIncrement()
+ { return wcpds.getAcquireIncrement(); }
+
+ public void setAcquireIncrement( int acquireIncrement )
+ {
+ wcpds.setAcquireIncrement( acquireIncrement );
+ this.resetPoolManager( false );
+ }
+
+ public int getAcquireRetryAttempts()
+ { return wcpds.getAcquireRetryAttempts(); }
+
+ public void setAcquireRetryAttempts( int acquireRetryAttempts )
+ {
+ wcpds.setAcquireRetryAttempts( acquireRetryAttempts );
+ this.resetPoolManager( false );
+ }
+
+ public int getAcquireRetryDelay()
+ { return wcpds.getAcquireRetryDelay(); }
+
+ public void setAcquireRetryDelay( int acquireRetryDelay )
+ {
+ wcpds.setAcquireRetryDelay( acquireRetryDelay );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isAutoCommitOnClose()
+ { return wcpds.isAutoCommitOnClose(); }
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose )
+ {
+ wcpds.setAutoCommitOnClose( autoCommitOnClose );
+ this.resetPoolManager( false );
+ }
+
+ public String getConnectionTesterClassName()
+ { return wcpds.getConnectionTesterClassName(); }
+
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException
+ {
+ wcpds.setConnectionTesterClassName( connectionTesterClassName );
+ this.resetPoolManager( false );
+ }
+
+ public String getAutomaticTestTable()
+ { return wcpds.getAutomaticTestTable(); }
+
+ public void setAutomaticTestTable( String automaticTestTable )
+ {
+ wcpds.setAutomaticTestTable( automaticTestTable );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isForceIgnoreUnresolvedTransactions()
+ { return wcpds.isForceIgnoreUnresolvedTransactions(); }
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions )
+ {
+ wcpds.setForceIgnoreUnresolvedTransactions( forceIgnoreUnresolvedTransactions );
+ this.resetPoolManager( false );
+ }
+
+ public int getIdleConnectionTestPeriod()
+ { return wcpds.getIdleConnectionTestPeriod(); }
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod )
+ {
+ wcpds.setIdleConnectionTestPeriod( idleConnectionTestPeriod );
+ this.resetPoolManager( false );
+ }
+
+ public int getInitialPoolSize()
+ { return wcpds.getInitialPoolSize(); }
+
+ public void setInitialPoolSize( int initialPoolSize )
+ {
+ wcpds.setInitialPoolSize( initialPoolSize );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxIdleTime()
+ { return wcpds.getMaxIdleTime(); }
+
+ public void setMaxIdleTime( int maxIdleTime )
+ {
+ wcpds.setMaxIdleTime( maxIdleTime );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxPoolSize()
+ { return wcpds.getMaxPoolSize(); }
+
+ public void setMaxPoolSize( int maxPoolSize )
+ {
+ wcpds.setMaxPoolSize( maxPoolSize );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxStatements()
+ { return wcpds.getMaxStatements(); }
+
+ public void setMaxStatements( int maxStatements )
+ {
+ wcpds.setMaxStatements( maxStatements );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxStatementsPerConnection()
+ { return wcpds.getMaxStatementsPerConnection(); }
+
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection )
+ {
+ wcpds.setMaxStatementsPerConnection( maxStatementsPerConnection );
+ this.resetPoolManager( false );
+ }
+
+ public int getMinPoolSize()
+ { return wcpds.getMinPoolSize(); }
+
+ public void setMinPoolSize( int minPoolSize )
+ {
+ wcpds.setMinPoolSize( minPoolSize );
+ this.resetPoolManager( false );
+ }
+
+ public String getOverrideDefaultUser()
+ { return wcpds.getOverrideDefaultUser(); }
+
+ public void setOverrideDefaultUser(String overrideDefaultUser)
+ {
+ wcpds.setOverrideDefaultUser( overrideDefaultUser );
+ this.resetPoolManager( false );
+ }
+
+ public String getOverrideDefaultPassword()
+ { return wcpds.getOverrideDefaultPassword(); }
+
+ public void setOverrideDefaultPassword(String overrideDefaultPassword)
+ {
+ wcpds.setOverrideDefaultPassword( overrideDefaultPassword );
+ this.resetPoolManager( false );
+ }
+
+ public int getPropertyCycle()
+ { return wcpds.getPropertyCycle(); }
+
+ public void setPropertyCycle( int propertyCycle )
+ {
+ wcpds.setPropertyCycle( propertyCycle );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isBreakAfterAcquireFailure()
+ { return wcpds.isBreakAfterAcquireFailure(); }
+
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure )
+ {
+ wcpds.setBreakAfterAcquireFailure( breakAfterAcquireFailure );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isTestConnectionOnCheckout()
+ { return wcpds.isTestConnectionOnCheckout(); }
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout )
+ {
+ wcpds.setTestConnectionOnCheckout( testConnectionOnCheckout );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isTestConnectionOnCheckin()
+ { return wcpds.isTestConnectionOnCheckin(); }
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin )
+ {
+ wcpds.setTestConnectionOnCheckin( testConnectionOnCheckin );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isUsesTraditionalReflectiveProxies()
+ { return wcpds.isUsesTraditionalReflectiveProxies(); }
+
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies )
+ {
+ wcpds.setUsesTraditionalReflectiveProxies( usesTraditionalReflectiveProxies );
+ this.resetPoolManager( false );
+ }
+
+ public String getPreferredTestQuery()
+ { return wcpds.getPreferredTestQuery(); }
+
+ public void setPreferredTestQuery( String preferredTestQuery )
+ {
+ wcpds.setPreferredTestQuery( preferredTestQuery );
+ this.resetPoolManager( false );
+ }
+
+ public String getUserOverridesAsString()
+ { return wcpds.getUserOverridesAsString(); }
+
+ public void setUserOverridesAsString( String userOverridesAsString ) throws PropertyVetoException
+ {
+ wcpds.setUserOverridesAsString( userOverridesAsString );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxAdministrativeTaskTime()
+ { return wcpds.getMaxAdministrativeTaskTime(); }
+
+ public void setMaxAdministrativeTaskTime( int maxAdministrativeTaskTime )
+ {
+ wcpds.setMaxAdministrativeTaskTime( maxAdministrativeTaskTime );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxIdleTimeExcessConnections()
+ { return wcpds.getMaxIdleTimeExcessConnections(); }
+
+ public void setMaxIdleTimeExcessConnections( int maxIdleTimeExcessConnections )
+ {
+ wcpds.setMaxIdleTimeExcessConnections( maxIdleTimeExcessConnections );
+ this.resetPoolManager( false );
+ }
+
+ public int getMaxConnectionAge()
+ { return wcpds.getMaxConnectionAge(); }
+
+ public void setMaxConnectionAge( int maxConnectionAge )
+ {
+ wcpds.setMaxConnectionAge( maxConnectionAge );
+ this.resetPoolManager( false );
+ }
+
+ public String getConnectionCustomizerClassName()
+ { return wcpds.getConnectionCustomizerClassName(); }
+
+ public void setConnectionCustomizerClassName( String connectionCustomizerClassName )
+ {
+ wcpds.setConnectionCustomizerClassName( connectionCustomizerClassName );
+ this.resetPoolManager( false );
+ }
+
+ public int getUnreturnedConnectionTimeout()
+ { return wcpds.getUnreturnedConnectionTimeout(); }
+
+ public void setUnreturnedConnectionTimeout(int unreturnedConnectionTimeout)
+ {
+ wcpds.setUnreturnedConnectionTimeout( unreturnedConnectionTimeout );
+ this.resetPoolManager( false );
+ }
+
+ public boolean isDebugUnreturnedConnectionStackTraces()
+ { return wcpds.isDebugUnreturnedConnectionStackTraces(); }
+
+ public void setDebugUnreturnedConnectionStackTraces(boolean debugUnreturnedConnectionStackTraces)
+ {
+ wcpds.setDebugUnreturnedConnectionStackTraces( debugUnreturnedConnectionStackTraces );
+ this.resetPoolManager( false );
+ }
+
+ // shared properties (count: 1)
+ public String getFactoryClassLocation()
+ { return super.getFactoryClassLocation(); }
+
+ public void setFactoryClassLocation( String factoryClassLocation )
+ {
+ dmds.setFactoryClassLocation( factoryClassLocation );
+ wcpds.setFactoryClassLocation( factoryClassLocation );
+ super.setFactoryClassLocation( factoryClassLocation );
+ }
+
+ public String toString()
+ {
+ //System.err.println("ComboPooledDataSource.toString()");
+
+ StringBuffer sb = new StringBuffer(512);
+ sb.append( this.getClass().getName() );
+ sb.append(" [ ");
+ try { BeansUtils.appendPropNamesAndValues(sb, this, TO_STRING_IGNORE_PROPS); }
+ catch (Exception e)
+ {
+ sb.append( e.toString() );
+ //e.printStackTrace();
+ }
+ sb.append(" ]");
+
+// Map userOverrides = wcpds.getUserOverrides();
+// if (userOverrides != null)
+// sb.append("; userOverrides: " + userOverrides.toString());
+
+ return sb.toString();
+ }
+
+ // serialization stuff -- set up bound/constrained property event handlers on deserialization
+ private static final long serialVersionUID = 1;
+ private static final short VERSION = 0x0001;
+
+ private void writeObject( ObjectOutputStream oos ) throws IOException
+ {
+ oos.writeShort( VERSION );
+ }
+
+ private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ short version = ois.readShort();
+ switch (version)
+ {
+ case VERSION:
+ updateLocalVarsFromCpdsProp();
+ setUpPropertyEvents();
+ break;
+ default:
+ throw new IOException("Unsupported Serialized Version: " + version);
+ }
+ }
+}
+
+//now, referenceability happens exactly the same way it does for PoolBackedDataSource
+//all this stuff (and the maintenance hassle of it) should be unnecessary
+
+/*
+ // WrapperConnectionPoolDataSource properties -- count: 28
+//
+// ("checkoutTimeout");
+// ("acquireIncrement");
+// ("acquireRetryAttempts");
+// ("acquireRetryDelay");
+// ("autoCommitOnClose");
+// ("connectionTesterClassName");
+// ("forceIgnoreUnresolvedTransactions");
+// ("idleConnectionTestPeriod");
+// ("initialPoolSize");
+// ("maxIdleTime");
+// ("maxPoolSize");
+// ("maxStatements");
+// ("maxStatementsPerConnection");
+// ("minPoolSize");
+// ("propertyCycle");
+// ("breakAfterAcquireFailure");
+// ("testConnectionOnCheckout");
+// ("testConnectionOnCheckin");
+// ("usesTraditionalReflectiveProxies");
+// ("preferredTestQuery");
+// ("automaticTestTable");
+// ("userOverridesAsString");
+// ("overrideDefaultUser");
+// ("overrideDefaultPassword");
+// ("maxAdministrativeTaskTime");
+// ("maxIdleTimeExcessConnections");
+// ("maxConnectionAge");
+// ("connectionTesterClassName");
+
+ final static JavaBeanReferenceMaker referenceMaker = new JavaBeanReferenceMaker();
+
+ static
+ {
+ referenceMaker.setFactoryClassName( C3P0JavaBeanObjectFactory.class.getName() );
+
+ // DriverManagerDataSource properties (count: 4)
+ referenceMaker.addReferenceProperty("description");
+ referenceMaker.addReferenceProperty("driverClass");
+ referenceMaker.addReferenceProperty("jdbcUrl");
+ referenceMaker.addReferenceProperty("properties");
+
+ // WrapperConnectionPoolDataSource properties (count: 27)
+ referenceMaker.addReferenceProperty("checkoutTimeout");
+ referenceMaker.addReferenceProperty("acquireIncrement");
+ referenceMaker.addReferenceProperty("acquireRetryAttempts");
+ referenceMaker.addReferenceProperty("acquireRetryDelay");
+ referenceMaker.addReferenceProperty("autoCommitOnClose");
+ referenceMaker.addReferenceProperty("connectionTesterClassName");
+ referenceMaker.addReferenceProperty("forceIgnoreUnresolvedTransactions");
+ referenceMaker.addReferenceProperty("idleConnectionTestPeriod");
+ referenceMaker.addReferenceProperty("initialPoolSize");
+ referenceMaker.addReferenceProperty("maxIdleTime");
+ referenceMaker.addReferenceProperty("maxPoolSize");
+ referenceMaker.addReferenceProperty("maxStatements");
+ referenceMaker.addReferenceProperty("maxStatementsPerConnection");
+ referenceMaker.addReferenceProperty("minPoolSize");
+ referenceMaker.addReferenceProperty("propertyCycle");
+ referenceMaker.addReferenceProperty("breakAfterAcquireFailure");
+ referenceMaker.addReferenceProperty("testConnectionOnCheckout");
+ referenceMaker.addReferenceProperty("testConnectionOnCheckin");
+ referenceMaker.addReferenceProperty("usesTraditionalReflectiveProxies");
+ referenceMaker.addReferenceProperty("preferredTestQuery");
+ referenceMaker.addReferenceProperty("automaticTestTable");
+ referenceMaker.addReferenceProperty("userOverridesAsString");
+ referenceMaker.addReferenceProperty("overrideDefaultUser");
+ referenceMaker.addReferenceProperty("overrideDefaultPassword");
+ referenceMaker.addReferenceProperty("maxAdministrativeTaskTime");
+ referenceMaker.addReferenceProperty("maxIdleTimeExcessConnections");
+ referenceMaker.addReferenceProperty("maxConnectionAge");
+
+ // PoolBackedDataSource properties (count: 2)
+ referenceMaker.addReferenceProperty("dataSourceName");
+ referenceMaker.addReferenceProperty("numHelperThreads");
+
+ // identity token
+ referenceMaker.addReferenceProperty("identityToken");
+
+ // shared properties (count: 1)
+ referenceMaker.addReferenceProperty("factoryClassLocation");
+ }
+
+ public Reference getReference() throws NamingException
+ {
+ synchronized ( this )
+ {
+ synchronized ( wcpds )
+ {
+ synchronized( dmds )
+ {
+ //System.err.println("ComboPooledDataSource.getReference()!!!!");
+ //new Exception("PRINT-STACK-TRACE").printStackTrace();
+ //javax.naming.Reference out = referenceMaker.createReference( this );
+ //System.err.println(out);
+ //return out;
+
+ return referenceMaker.createReference( this );
+ }
+ }
+ }
+ }
+ */
diff --git a/src/classes/com/mchange/v2/c3p0/ConnectionCustomizer.java b/src/classes/com/mchange/v2/c3p0/ConnectionCustomizer.java
new file mode 100644
index 0000000..e4b0b0a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/ConnectionCustomizer.java
@@ -0,0 +1,89 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * <p>Implementations of this interface should
+ * be immutable, and should offer public,
+ * no argument constructors.</p>
+ *
+ * <p>The methods are handed raw, physical
+ * database Connections, not c3p0-generated
+ * proxies.</p>
+ *
+ * <p>Although c3p0 will ensure this with
+ * respect to state controlled by
+ * standard JDBC methods, any modifications
+ * of vendor-specific state shold be made
+ * consistently so that all Connections
+ * in the pool are interchangable.</p>
+ */
+public interface ConnectionCustomizer
+{
+ /**
+ * <p>Called immediately after a
+ * Connection is acquired from the
+ * underlying database for
+ * incorporation into the pool.</p>
+ *
+ * <p>This method is only called once
+ * per Connection. If standard JDBC
+ * Connection properties are modified
+ * [holdability, transactionIsolation,
+ * readOnly], those modifications
+ * will override defaults throughout
+ * the Connection's tenure in the
+ * pool.</p>
+ */
+ public void onAcquire( Connection c, String parentDataSourceIdentityToken )
+ throws Exception;
+
+ /**
+ * Called immediately before a
+ * Connection is destroyed after
+ * being removed from the pool.
+ */
+ public void onDestroy( Connection c, String parentDataSourceIdentityToken )
+ throws Exception;
+
+ /**
+ * Called immediately before a
+ * Connection is made available to
+ * a client upon checkout.
+ */
+ public void onCheckOut( Connection c, String parentDataSourceIdentityToken )
+ throws Exception;
+
+ /**
+ * Called immediately after a
+ * Connection is checked in,
+ * prior to reincorporation
+ * into the pool.
+ */
+ public void onCheckIn( Connection c, String parentDataSourceIdentityToken )
+ throws Exception;
+}
diff --git a/src/classes/com/mchange/v2/c3p0/ConnectionTester.java b/src/classes/com/mchange/v2/c3p0/ConnectionTester.java
new file mode 100644
index 0000000..2646db5
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/ConnectionTester.java
@@ -0,0 +1,65 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.io.Serializable;
+import java.sql.Connection;
+
+/**
+ * <p>Define your own Connection tester if you want to
+ * override c3p0's default behavior for testing the validity
+ * of Connections and responding to Connection errors encountered.</p>
+ *
+ * <p><b>Recommended:</b> If you'd like your ConnectionTester
+ * to support the user-configured <tt>preferredTestQuery</tt>
+ * parameter, please implement {@link com.mchange.v2.c3p0.UnifiedConnectionTester}.
+ *
+ * <p>ConnectionTesters should be Serializable, immutable,
+ * and must have public, no-arg constructors.</p>
+ *
+ * @see com.mchange.v2.c3p0.UnifiedConnectionTester
+ * @see com.mchange.v2.c3p0.AbstractConnectionTester
+ */
+public interface ConnectionTester extends Serializable
+{
+ public final static int CONNECTION_IS_OKAY = 0;
+ public final static int CONNECTION_IS_INVALID = -1;
+ public final static int DATABASE_IS_INVALID = -8;
+
+ public int activeCheckConnection(Connection c);
+
+ public int statusOnException(Connection c, Throwable t);
+
+ /**
+ * Multiple testers that are of the same
+ * class and use the same criteria for determining fatality
+ * should test as equals().
+ */
+ public boolean equals( Object o );
+
+ /**
+ * keep consistent with equals()
+ */
+ public int hashCode();
+}
diff --git a/src/classes/com/mchange/v2/c3p0/DataSources.java b/src/classes/com/mchange/v2/c3p0/DataSources.java
new file mode 100644
index 0000000..3e3f992
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/DataSources.java
@@ -0,0 +1,368 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import com.mchange.v2.log.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import javax.sql.ConnectionPoolDataSource;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.beans.BeansUtils;
+
+/**
+ * <p>A simple factory class for creating DataSources. Generally, users will call <tt>DataSources.unpooledDataSource()</tt> to get
+ * a basic DataSource, and then get a pooled version by calling <tt>DataSources.pooledDataSource()</tt>.</p>
+ *
+ * <p>Most users will not need to worry about configuration details. If you want to use a PreparedStatement cache, be sure to call
+ * the version of <tt>DataSources.pooledDataSource()</tt> that accepts a <tt>statement_cache_size</tt> parameter, and set that to
+ * be a number (much) greater than zero. (For maximum performance, you would set this to be several times the number kinds of
+ * PreparedStatements you expect your application to use.)</p>
+ *
+ * <p>For those interested in detailed configuration, note that c3p0 pools can be configured by explicit method calls on PoolConfig objects,
+ * by defining System properties, or by defining a <tt>c3p0.properties</tt> file in your resource path. See {@link com.mchange.v2.c3p0.PoolConfig}
+ * for details.</p>
+ *
+ */
+public final class DataSources
+{
+ final static MLogger logger = MLog.getLogger( DataSources.class );
+
+ final static Set WRAPPER_CXN_POOL_DATA_SOURCE_OVERWRITE_PROPS; //22 -- includes factory class location
+ final static Set POOL_BACKED_DATA_SOURCE_OVERWRITE_PROPS; //2 -- includes factory class location, excludes pool-owner id token
+
+ static
+ {
+ // As of c3p0-0.9.1
+ //
+ // This list is no longer updated, as the PoolConfig approach to setting up DataSources
+ // is now deprecated. (This was getting to be hard to maintain as new config properties
+ // were added.)
+ String[] props = new String[]
+ {
+ "checkoutTimeout", //1
+ "acquireIncrement", //2
+ "acquireRetryAttempts", //3
+ "acquireRetryDelay", //4
+ "autoCommitOnClose", //5
+ "connectionTesterClassName", //6
+ "forceIgnoreUnresolvedTransactions", //7
+ "idleConnectionTestPeriod", //8
+ "initialPoolSize", //9
+ "maxIdleTime", //10
+ "maxPoolSize", //11
+ "maxStatements", //12
+ "maxStatementsPerConnection", //13
+ "minPoolSize", //14
+ "propertyCycle", //15
+ "breakAfterAcquireFailure", //16
+ "testConnectionOnCheckout", //17
+ "testConnectionOnCheckin", //18
+ "usesTraditionalReflectiveProxies", //19
+ "preferredTestQuery", //20
+ "automaticTestTable", //21
+ "factoryClassLocation" //22
+ };
+
+ WRAPPER_CXN_POOL_DATA_SOURCE_OVERWRITE_PROPS = Collections.unmodifiableSet( new HashSet( Arrays.asList( props ) ) );
+
+ // As of c3p0-0.9.1
+ //
+ // This list is no longer updated, as the PoolConfig approach to setting up DataSources
+ // is now deprecated. (This was getting to be hard to maintain as new config properties
+ // were added.)
+ props = new String[]
+ {
+ "numHelperThreads",
+ "factoryClassLocation"
+ };
+
+ POOL_BACKED_DATA_SOURCE_OVERWRITE_PROPS = Collections.unmodifiableSet( new HashSet( Arrays.asList( props ) ) );
+ }
+
+ /**
+ * Defines an unpooled DataSource all of whose paramateres (especially jdbcUrl)
+ * should be set in config files.
+ */
+ public static DataSource unpooledDataSource() throws SQLException
+ {
+ DriverManagerDataSource out = new DriverManagerDataSource();
+ return out;
+ }
+
+ public static DataSource unpooledDataSource(String jdbcUrl) throws SQLException
+ {
+ DriverManagerDataSource out = new DriverManagerDataSource();
+ out.setJdbcUrl( jdbcUrl );
+ return out;
+ }
+
+ /**
+ * Defines an unpooled DataSource on the specified JDBC URL, authenticating with a username and password.
+ */
+ public static DataSource unpooledDataSource(String jdbcUrl, String user, String password) throws SQLException
+ {
+ Properties props = new Properties();
+ props.put(SqlUtils.DRIVER_MANAGER_USER_PROPERTY, user);
+ props.put(SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY, password);
+ return unpooledDataSource( jdbcUrl, props );
+ }
+
+ /**
+ * Defines an unpooled DataSource on the specified JDBC URL.
+ *
+ * @param driverProps the usual DriverManager properties for your JDBC driver
+ * (e.g. "user" and "password" for all drivers that support
+ * authentication)
+ *
+ * @see java.sql.DriverManager
+ */
+ public static DataSource unpooledDataSource(String jdbcUrl, Properties driverProps) throws SQLException
+ {
+ DriverManagerDataSource out = new DriverManagerDataSource();
+ out.setJdbcUrl( jdbcUrl );
+ out.setProperties( driverProps );
+ return out;
+ }
+
+ /**
+ * <p>Creates a pooled version of an unpooled DataSource using default configuration information.</p>
+ * <p><b>NOTE:</b> By default, statement pooling is turned off, because for simple databases that do
+ * not pre-parse and optimize PreparedStatements, statement caching is a net
+ * performance loss. But if your database <i>does</i> optimize PreparedStatements
+ * you'll want to turn StatementCaching on via {@link #pooledDataSource(javax.sql.DataSource, int)}.</p>
+ * @return a DataSource that can be cast to a {@link PooledDataSource} if you are interested in pool statistics
+ */
+ public static DataSource pooledDataSource( DataSource unpooledDataSource ) throws SQLException
+ { return pooledDataSource( unpooledDataSource, null, (Map) null ); }
+
+ /**
+ * <p>Creates a pooled version of an unpooled DataSource using default configuration information
+ * and the specified startement cache size.
+ * Use a value greater than zero to turn statement caching on.</p>
+ *
+ * @return a DataSource that can be cast to a {@link PooledDataSource} if you are interested in pool statistics
+ */
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, int statement_cache_size ) throws SQLException
+ {
+// PoolConfig pcfg = new PoolConfig();
+// pcfg.setMaxStatements( statement_cache_size );
+
+ Map overrideProps = new HashMap();
+ overrideProps.put( "maxStatements", new Integer( statement_cache_size ) );
+ return pooledDataSource( unpooledDataSource, null, overrideProps );
+ }
+
+ /**
+ * <p>Creates a pooled version of an unpooled DataSource using configuration
+ * information supplied explicitly by a {@link com.mchange.v2.c3p0.PoolConfig}.
+ *
+ * @return a DataSource that can be cast to a {@link PooledDataSource} if you are interested in pool statistics
+ *
+ * @deprecated if you want to set properties programmatically, please construct a ComboPooledDataSource and
+ * set its properties rather than using PoolConfig
+ */
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, PoolConfig pcfg ) throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource wcpds = new WrapperConnectionPoolDataSource();
+ wcpds.setNestedDataSource( unpooledDataSource );
+
+ // set PoolConfig info -- WrapperConnectionPoolDataSource properties
+ BeansUtils.overwriteSpecificAccessibleProperties( pcfg, wcpds, WRAPPER_CXN_POOL_DATA_SOURCE_OVERWRITE_PROPS );
+
+ PoolBackedDataSource nascent_pbds = new PoolBackedDataSource();
+ nascent_pbds.setConnectionPoolDataSource( wcpds );
+ BeansUtils.overwriteSpecificAccessibleProperties( pcfg, nascent_pbds, POOL_BACKED_DATA_SOURCE_OVERWRITE_PROPS );
+
+ return nascent_pbds;
+ }
+// catch ( PropertyVetoException e )
+// {
+// e.printStackTrace();
+// PropertyChangeEvent evt = e.getPropertyChangeEvent();
+// throw new SQLException("Illegal value attempted for property " + evt.getPropertyName() + ": " + evt.getNewValue());
+// }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ SQLException sqle = SqlUtils.toSQLException("Exception configuring pool-backed DataSource: " + e, e);
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ) && e != sqle)
+ logger.log( MLevel.FINE, "Converted exception to throwable SQLException", e );
+ throw sqle;
+ }
+ }
+
+ /*
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, String overrideDefaultUser, String overrideDefaultPassword ) throws SQLException
+ {
+ Map overrideProps;
+
+ if (overrideDefaultUser != null)
+ {
+ overrideProps = new HashMap();
+ overrideProps.put( "overrideDefaultUser", overrideDefaultUser );
+ overrideProps.put( "overrideDefaultPassword", overrideDefaultPassword );
+ }
+ else
+ overrideProps = null;
+
+ return pooledDataSource( unpooledDataSource, null, overrideProps );
+ }
+ */
+
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, String configName ) throws SQLException
+ { return pooledDataSource( unpooledDataSource, configName, null ); }
+
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, Map overrideProps ) throws SQLException
+ { return pooledDataSource( unpooledDataSource, null, overrideProps ); }
+
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, String configName, Map overrideProps ) throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource wcpds = new WrapperConnectionPoolDataSource(configName);
+ wcpds.setNestedDataSource( unpooledDataSource );
+ if (overrideProps != null)
+ BeansUtils.overwriteAccessiblePropertiesFromMap( overrideProps,
+ wcpds,
+ false,
+ null,
+ true,
+ MLevel.WARNING,
+ MLevel.WARNING,
+ false);
+
+ PoolBackedDataSource nascent_pbds = new PoolBackedDataSource(configName);
+ nascent_pbds.setConnectionPoolDataSource( wcpds );
+ if (overrideProps != null)
+ BeansUtils.overwriteAccessiblePropertiesFromMap( overrideProps,
+ nascent_pbds,
+ false,
+ null,
+ true,
+ MLevel.WARNING,
+ MLevel.WARNING,
+ false);
+
+ return nascent_pbds;
+ }
+// catch ( PropertyVetoException e )
+// {
+// e.printStackTrace();
+// PropertyChangeEvent evt = e.getPropertyChangeEvent();
+// throw new SQLException("Illegal value attempted for property " + evt.getPropertyName() + ": " + evt.getNewValue());
+// }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ SQLException sqle = SqlUtils.toSQLException("Exception configuring pool-backed DataSource: " + e, e);
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINE ) && e != sqle)
+ logger.log( MLevel.FINE, "Converted exception to throwable SQLException", e );
+ throw sqle;
+ }
+ }
+
+ /**
+ * <p>Creates a pooled version of an unpooled DataSource using configuration
+ * information supplied explicitly by a Java Properties object.</p>
+ *
+ * @return a DataSource that can be cast to a {@link PooledDataSource} if you are interested in pool statistics
+ * @see com.mchange.v2.c3p0.PoolConfig
+ */
+ public static DataSource pooledDataSource( DataSource unpooledDataSource, Properties props ) throws SQLException
+ {
+ //return pooledDataSource( unpooledDataSource, new PoolConfig( props ) );
+
+ Properties peeledProps = new Properties();
+ for (Iterator ii = props.keySet().iterator(); ii.hasNext(); )
+ {
+ String propKey = (String) ii.next();
+ String propVal = props.getProperty( propKey );
+ String peeledKey = (propKey.startsWith("c3p0.") ? propKey.substring(5) : propKey );
+ peeledProps.put( peeledKey, propVal );
+ }
+ return pooledDataSource( unpooledDataSource, null, peeledProps );
+ }
+
+ /**
+ * <p>Immediately releases resources (Threads and database Connections) that are
+ * held by a C3P0 DataSource.
+ *
+ * <p>Only DataSources created by the poolingDataSource() method hold any
+ * non-memory resources. Calling this method on unpooled DataSources is
+ * effectively a no-op.</p>
+ *
+ * <p>You can safely presume that destroying a pooled DataSource that is wrapped around
+ * another DataSource created by this library destroys both the outer and the wrapped
+ * DataSource. There is no reason to hold a reference to a nested DataSource in order
+ * to explicitly destroy it.</p>
+ *
+ * @see com.mchange.v2.c3p0.PoolConfig
+ */
+ public static void destroy( DataSource pooledDataSource ) throws SQLException
+ { destroy( pooledDataSource, false ); }
+
+
+ /**
+ * @deprecated forceDestroy() is no longer meaningful, as a set of pools is now
+ * directly associated with a DataSource, and not potentially shared.
+ * (This simplification was made possible by canonicalization of
+ * JNDI-looked-up DataSources within a virtual machine.) Just use
+ * DataSources.destroy().
+ *
+ * @see #destroy
+ */
+ public static void forceDestroy( DataSource pooledDataSource ) throws SQLException
+ { destroy( pooledDataSource, true ); }
+
+ private static void destroy( DataSource pooledDataSource, boolean force ) throws SQLException
+ {
+ if ( pooledDataSource instanceof PoolBackedDataSource)
+ {
+ ConnectionPoolDataSource cpds = ((PoolBackedDataSource) pooledDataSource).getConnectionPoolDataSource();
+ if (cpds instanceof WrapperConnectionPoolDataSource)
+ destroy( ((WrapperConnectionPoolDataSource) cpds).getNestedDataSource(), force );
+ }
+ if ( pooledDataSource instanceof PooledDataSource )
+ ((PooledDataSource) pooledDataSource).close( force );
+ }
+
+ private DataSources()
+ {}
+}
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/DriverManagerDataSource.java b/src/classes/com/mchange/v2/c3p0/DriverManagerDataSource.java
new file mode 100644
index 0000000..ba47c56
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/DriverManagerDataSource.java
@@ -0,0 +1,254 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.c3p0.cfg.C3P0Config;
+import com.mchange.v2.c3p0.impl.DriverManagerDataSourceBase;
+
+public final class DriverManagerDataSource extends DriverManagerDataSourceBase implements DataSource
+{
+ final static MLogger logger = MLog.getLogger( DriverManagerDataSource.class );
+
+ //MT: protected by this' lock
+ Driver driver;
+
+ //MT: protected by this' lock
+ boolean driver_class_loaded = false;
+
+ public DriverManagerDataSource()
+ { this( true ); }
+
+ public DriverManagerDataSource(boolean autoregister)
+ {
+ super( autoregister );
+
+ setUpPropertyListeners();
+
+ String user = C3P0Config.initializeStringPropertyVar("user", null);
+ String password = C3P0Config.initializeStringPropertyVar("password", null);
+
+ if (user != null)
+ this.setUser( user );
+
+ if (password != null)
+ this.setPassword( password );
+ }
+
+ private void setUpPropertyListeners()
+ {
+ PropertyChangeListener driverClassListener = new PropertyChangeListener()
+ {
+ public void propertyChange( PropertyChangeEvent evt )
+ {
+ if ( "driverClass".equals( evt.getPropertyName() ) )
+ setDriverClassLoaded( false );
+ }
+ };
+ this.addPropertyChangeListener( driverClassListener );
+ }
+
+ private synchronized boolean isDriverClassLoaded()
+ { return driver_class_loaded; }
+
+ private synchronized void setDriverClassLoaded(boolean dcl)
+ { this.driver_class_loaded = dcl; }
+
+ private void ensureDriverLoaded() throws SQLException
+ {
+ try
+ {
+ if (! isDriverClassLoaded())
+ {
+ if (driverClass != null)
+ Class.forName( driverClass );
+ setDriverClassLoaded( true );
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Could not load driverClass " + driverClass, e);
+ }
+ }
+
+ // should NOT be sync'ed -- driver() is sync'ed and that's enough
+ // sync'ing the method creates the danger that one freeze on connect
+ // blocks access to the entire DataSource
+
+ public Connection getConnection() throws SQLException
+ {
+ ensureDriverLoaded();
+
+ Connection out = driver().connect( jdbcUrl, properties );
+ if (out == null)
+ throw new SQLException("Apparently, jdbc URL '" + jdbcUrl + "' is not valid for the underlying " +
+ "driver [" + driver() + "].");
+ return out;
+ }
+
+ // should NOT be sync'ed -- driver() is sync'ed and that's enough
+ // sync'ing the method creates the danger that one freeze on connect
+ // blocks access to the entire DataSource
+
+ public Connection getConnection(String username, String password) throws SQLException
+ {
+ ensureDriverLoaded();
+
+ Connection out = driver().connect( jdbcUrl, overrideProps(username, password) );
+ if (out == null)
+ throw new SQLException("Apparently, jdbc URL '" + jdbcUrl + "' is not valid for the underlying " +
+ "driver [" + driver() + "].");
+ return out;
+ }
+
+ public PrintWriter getLogWriter() throws SQLException
+ { return DriverManager.getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out) throws SQLException
+ { DriverManager.setLogWriter( out ); }
+
+ public int getLoginTimeout() throws SQLException
+ { return DriverManager.getLoginTimeout(); }
+
+ public void setLoginTimeout(int seconds) throws SQLException
+ { DriverManager.setLoginTimeout( seconds ); }
+
+ //overrides
+ public synchronized void setJdbcUrl(String jdbcUrl)
+ {
+ //System.err.println( "setJdbcUrl( " + jdbcUrl + " )");
+ //new Exception("DEBUG STACK TRACE").printStackTrace();
+ super.setJdbcUrl( jdbcUrl );
+ clearDriver();
+ }
+
+ //"virtual properties"
+ public synchronized void setUser(String user)
+ {
+ String oldUser = this.getUser();
+ if (! eqOrBothNull( user, oldUser ))
+ {
+ if (user != null)
+ properties.put( SqlUtils.DRIVER_MANAGER_USER_PROPERTY, user );
+ else
+ properties.remove( SqlUtils.DRIVER_MANAGER_USER_PROPERTY );
+
+ pcs.firePropertyChange("user", oldUser, user);
+ }
+ }
+
+ public synchronized String getUser()
+ {
+// System.err.println("getUser() -- DriverManagerDataSource@" + System.identityHashCode( this ) +
+// " using Properties@" + System.identityHashCode( properties ));
+// new Exception("STACK TRACE DUMP").printStackTrace();
+ return properties.getProperty( SqlUtils.DRIVER_MANAGER_USER_PROPERTY );
+ }
+
+ public synchronized void setPassword(String password)
+ {
+ String oldPass = this.getPassword();
+ if (! eqOrBothNull( password, oldPass ))
+ {
+ if (password != null)
+ properties.put( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY, password );
+ else
+ properties.remove( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY );
+
+ pcs.firePropertyChange("password", oldPass, password);
+ }
+ }
+
+ public synchronized String getPassword()
+ { return properties.getProperty( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY ); }
+
+ private final Properties overrideProps(String user, String password)
+ {
+ Properties overriding = (Properties) properties.clone(); //we are relying on a defensive clone in our base class!!!
+
+ if (user != null)
+ overriding.put(SqlUtils.DRIVER_MANAGER_USER_PROPERTY, user);
+ else
+ overriding.remove(SqlUtils.DRIVER_MANAGER_USER_PROPERTY);
+
+ if (password != null)
+ overriding.put(SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY, password);
+ else
+ overriding.remove(SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY);
+
+ return overriding;
+ }
+
+ private synchronized Driver driver() throws SQLException
+ {
+ //System.err.println( "driver() <-- " + this );
+ if (driver == null)
+ driver = DriverManager.getDriver( jdbcUrl );
+ return driver;
+ }
+
+ private synchronized void clearDriver()
+ { driver = null; }
+
+ private static boolean eqOrBothNull( Object a, Object b )
+ { return (a == b || (a != null && a.equals(b))); }
+
+ // serialization stuff -- set up bound/constrained property event handlers on deserialization
+ private static final long serialVersionUID = 1;
+ private static final short VERSION = 0x0001;
+
+ private void writeObject( ObjectOutputStream oos ) throws IOException
+ {
+ oos.writeShort( VERSION );
+ }
+
+ private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ short version = ois.readShort();
+ switch (version)
+ {
+ case VERSION:
+ setUpPropertyListeners();
+ break;
+ default:
+ throw new IOException("Unsupported Serialized Version: " + version);
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/DriverManagerDataSourceFactory.java b/src/classes/com/mchange/v2/c3p0/DriverManagerDataSourceFactory.java
new file mode 100644
index 0000000..08a97ec
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/DriverManagerDataSourceFactory.java
@@ -0,0 +1,154 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.beans.PropertyChangeEvent;
+import java.sql.SQLException;
+import java.util.Properties;
+import javax.sql.DataSource;
+
+/**
+ * <P>A static factory that creates DataSources which simply forward
+ * calls to java.sql.DriverManager without any pooling or other fanciness.</P>
+ *
+ * <P>The DataSources returned are Refereneable and Serializable; they should
+ * be suitable for placement in a wide variety of JNDI Naming Services.</P>
+ *
+ * @deprecated Use the new factories in {@link com.mchange.v2.c3p0.DataSources}. See examples.
+ */
+public final class DriverManagerDataSourceFactory
+{
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * @param driverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param dfltUser a username (may be null) for authentication to the RDBMS
+ * @param dfltPassword a password (may be null) for authentication to the RDBMS
+ * @param refFactoryLoc a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ */
+ public static DataSource create(String driverClass,
+ String jdbcUrl,
+ String dfltUser,
+ String dfltPassword,
+ String refFactoryLoc)
+ throws SQLException
+ {
+ DriverManagerDataSource out = new DriverManagerDataSource();
+ out.setDriverClass( driverClass );
+ out.setJdbcUrl( jdbcUrl );
+ out.setUser( dfltUser );
+ out.setPassword( dfltPassword );
+ out.setFactoryClassLocation( refFactoryLoc );
+ return out;
+ }
+
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * @param driverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param props propertis object that should be passed to DriverManager.getConnection()
+ * @param refFactoryLoc a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ */
+ public static DataSource create(String driverClass,
+ String jdbcUrl,
+ Properties props,
+ String refFactoryLoc)
+ throws SQLException
+ {
+ DriverManagerDataSource out = new DriverManagerDataSource();
+ out.setDriverClass( driverClass );
+ out.setJdbcUrl( jdbcUrl );
+ out.setProperties( props );
+ out.setFactoryClassLocation( refFactoryLoc );
+ return out;
+ }
+
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * @param driverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param dfltUser a username (may be null) for authentication to the RDBMS
+ * @param dfltPassword a password (may be null) for authentication to the RDBMS
+ */
+ public static DataSource create(String driverClass,
+ String jdbcUrl,
+ String dfltUser,
+ String dfltPassword)
+ throws SQLException
+ { return create( driverClass, jdbcUrl, dfltUser, dfltPassword, null ); }
+
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * @param driverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ */
+ public static DataSource create(String driverClass, String jdbcUrl)
+ throws SQLException
+ { return DriverManagerDataSourceFactory.create( driverClass, jdbcUrl, (String) null, null); }
+
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * <P>Warning: since you do not set the driver class, the resulting DataSource
+ * will be less suitable for use via JNDI: JNDI clients will have to
+ * know the driver class and make sure themselves that it is preloaded!!!
+ *
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param dfltUser a username (may be null) for authentication to the RDBMS
+ * @param dfltPassword a password (may be null) for authentication to the RDBMS
+ */
+ public static DataSource create(String jdbcUrl, String dfltUser, String dfltPassword)
+ throws SQLException
+ { return DriverManagerDataSourceFactory.create( null, jdbcUrl, dfltUser, dfltPassword ); }
+
+ /**
+ * Creates an unpooled DataSource that users <TT>java.sql.DriverManager</TT>
+ * behind the scenes to acquire Connections.
+ *
+ * <P>Warning: since you do not set the driver class, the resulting DataSource
+ * will be less suitable for use via JNDI: JNDI clients will have to
+ * know the driver class and make sure themselves that it is preloaded!!!
+ *
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ */
+ public static DataSource create(String jdbcUrl)
+ throws SQLException
+ { return DriverManagerDataSourceFactory.create( null, jdbcUrl, (String) null, null ); }
+
+ private DriverManagerDataSourceFactory()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/c3p0/FullQueryConnectionTester.java b/src/classes/com/mchange/v2/c3p0/FullQueryConnectionTester.java
new file mode 100644
index 0000000..16eff05
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/FullQueryConnectionTester.java
@@ -0,0 +1,32 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+
+public interface FullQueryConnectionTester extends QueryConnectionTester
+{
+ public int statusOnException(Connection c, Throwable t, String preferredTestQuery);
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/JndiRefConnectionPoolDataSource.java b/src/classes/com/mchange/v2/c3p0/JndiRefConnectionPoolDataSource.java
new file mode 100644
index 0000000..c6bc462
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/JndiRefConnectionPoolDataSource.java
@@ -0,0 +1,311 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import com.mchange.v2.c3p0.impl.*;
+
+import java.beans.PropertyVetoException;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Hashtable;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+import com.mchange.v2.beans.BeansUtils;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.naming.JavaBeanReferenceMaker;
+import com.mchange.v2.naming.JavaBeanObjectFactory;
+import com.mchange.v2.naming.ReferenceMaker;
+
+public final class JndiRefConnectionPoolDataSource extends IdentityTokenResolvable implements ConnectionPoolDataSource, Serializable, Referenceable
+{
+ final static MLogger logger = MLog.getLogger( JndiRefConnectionPoolDataSource.class );
+
+ final static Collection IGNORE_PROPS = Arrays.asList( new String[] {"reference", "pooledConnection"} );
+
+ JndiRefForwardingDataSource jrfds;
+ WrapperConnectionPoolDataSource wcpds;
+
+ String identityToken;
+
+ public JndiRefConnectionPoolDataSource()
+ { this( true ); }
+
+ public JndiRefConnectionPoolDataSource( boolean autoregister )
+ {
+ jrfds = new JndiRefForwardingDataSource();
+ wcpds = new WrapperConnectionPoolDataSource();
+ wcpds.setNestedDataSource( jrfds );
+
+ if (autoregister)
+ {
+ this.identityToken = C3P0ImplUtils.allocateIdentityToken( this );
+ C3P0Registry.reregister( this );
+ }
+ }
+
+ public boolean isJndiLookupCaching()
+ { return jrfds.isCaching(); }
+
+ public void setJndiLookupCaching( boolean caching )
+ { jrfds.setCaching( caching ); }
+
+ public Hashtable getJndiEnv()
+ { return jrfds.getJndiEnv(); }
+
+ public void setJndiEnv( Hashtable jndiEnv )
+ { jrfds.setJndiEnv( jndiEnv ); }
+
+ public Object getJndiName()
+ { return jrfds.getJndiName(); }
+
+ public void setJndiName( Object jndiName ) throws PropertyVetoException
+ { jrfds.setJndiName( jndiName ); }
+
+ public int getAcquireIncrement()
+ { return wcpds.getAcquireIncrement(); }
+
+ public void setAcquireIncrement( int acquireIncrement )
+ { wcpds.setAcquireIncrement( acquireIncrement ); }
+
+ public int getAcquireRetryAttempts()
+ { return wcpds.getAcquireRetryAttempts(); }
+
+ public void setAcquireRetryAttempts( int ara )
+ { wcpds.setAcquireRetryAttempts( ara ); }
+
+ public int getAcquireRetryDelay()
+ { return wcpds.getAcquireRetryDelay(); }
+
+ public void setAcquireRetryDelay( int ard )
+ { wcpds.setAcquireRetryDelay( ard ); }
+
+ public boolean isAutoCommitOnClose()
+ { return wcpds.isAutoCommitOnClose(); }
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose )
+ { wcpds.setAutoCommitOnClose( autoCommitOnClose ); }
+
+ public void setAutomaticTestTable( String att )
+ { wcpds.setAutomaticTestTable( att ); }
+
+ public String getAutomaticTestTable()
+ { return wcpds.getAutomaticTestTable(); }
+
+ public void setBreakAfterAcquireFailure( boolean baaf )
+ { wcpds.setBreakAfterAcquireFailure( baaf ); }
+
+ public boolean isBreakAfterAcquireFailure()
+ { return wcpds.isBreakAfterAcquireFailure(); }
+
+ public void setCheckoutTimeout( int ct )
+ { wcpds.setCheckoutTimeout( ct ); }
+
+ public int getCheckoutTimeout()
+ { return wcpds.getCheckoutTimeout(); }
+
+ public String getConnectionTesterClassName()
+ { return wcpds.getConnectionTesterClassName(); }
+
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException
+ { wcpds.setConnectionTesterClassName( connectionTesterClassName ); }
+
+ public boolean isForceIgnoreUnresolvedTransactions()
+ { return wcpds.isForceIgnoreUnresolvedTransactions(); }
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions )
+ { wcpds.setForceIgnoreUnresolvedTransactions( forceIgnoreUnresolvedTransactions ); }
+
+ public String getIdentityToken()
+ { return identityToken; }
+
+ public void setIdentityToken(String identityToken)
+ { this.identityToken = identityToken; }
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod )
+ { wcpds.setIdleConnectionTestPeriod( idleConnectionTestPeriod ); }
+
+ public int getIdleConnectionTestPeriod()
+ { return wcpds.getIdleConnectionTestPeriod(); }
+
+ public int getInitialPoolSize()
+ { return wcpds.getInitialPoolSize(); }
+
+ public void setInitialPoolSize( int initialPoolSize )
+ { wcpds.setInitialPoolSize( initialPoolSize ); }
+
+ public int getMaxIdleTime()
+ { return wcpds.getMaxIdleTime(); }
+
+ public void setMaxIdleTime( int maxIdleTime )
+ { wcpds.setMaxIdleTime( maxIdleTime ); }
+
+ public int getMaxPoolSize()
+ { return wcpds.getMaxPoolSize(); }
+
+ public void setMaxPoolSize( int maxPoolSize )
+ { wcpds.setMaxPoolSize( maxPoolSize ); }
+
+ public int getMaxStatements()
+ { return wcpds.getMaxStatements(); }
+
+ public void setMaxStatements( int maxStatements )
+ { wcpds.setMaxStatements( maxStatements ); }
+
+ public int getMaxStatementsPerConnection()
+ { return wcpds.getMaxStatementsPerConnection(); }
+
+ public void setMaxStatementsPerConnection( int mspc )
+ { wcpds.setMaxStatementsPerConnection( mspc ); }
+
+ public int getMinPoolSize()
+ { return wcpds.getMinPoolSize(); }
+
+ public void setMinPoolSize( int minPoolSize )
+ { wcpds.setMinPoolSize( minPoolSize ); }
+
+ public String getPreferredTestQuery()
+ { return wcpds.getPreferredTestQuery(); }
+
+ public void setPreferredTestQuery( String ptq )
+ { wcpds.setPreferredTestQuery( ptq ); }
+
+ public int getPropertyCycle()
+ { return wcpds.getPropertyCycle(); }
+
+ public void setPropertyCycle( int propertyCycle )
+ { wcpds.setPropertyCycle( propertyCycle ); }
+
+ public boolean isTestConnectionOnCheckin()
+ { return wcpds.isTestConnectionOnCheckin(); }
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin )
+ { wcpds.setTestConnectionOnCheckin( testConnectionOnCheckin ); }
+
+ public boolean isTestConnectionOnCheckout()
+ { return wcpds.isTestConnectionOnCheckout(); }
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout )
+ { wcpds.setTestConnectionOnCheckout( testConnectionOnCheckout ); }
+
+ public boolean isUsesTraditionalReflectiveProxies()
+ { return wcpds.isUsesTraditionalReflectiveProxies(); }
+
+ public void setUsesTraditionalReflectiveProxies( boolean utrp )
+ { wcpds.setUsesTraditionalReflectiveProxies( utrp ); }
+
+ public String getFactoryClassLocation()
+ { return jrfds.getFactoryClassLocation(); }
+
+ public void setFactoryClassLocation( String factoryClassLocation )
+ {
+ jrfds.setFactoryClassLocation( factoryClassLocation );
+ wcpds.setFactoryClassLocation( factoryClassLocation );
+ }
+
+ final static JavaBeanReferenceMaker referenceMaker = new JavaBeanReferenceMaker();
+
+ static
+ {
+ referenceMaker.setFactoryClassName( C3P0JavaBeanObjectFactory.class.getName() );
+ referenceMaker.addReferenceProperty("acquireIncrement");
+ referenceMaker.addReferenceProperty("acquireRetryAttempts");
+ referenceMaker.addReferenceProperty("acquireRetryDelay");
+ referenceMaker.addReferenceProperty("autoCommitOnClose");
+ referenceMaker.addReferenceProperty("automaticTestTable");
+ referenceMaker.addReferenceProperty("checkoutTimeout");
+ referenceMaker.addReferenceProperty("connectionTesterClassName");
+ referenceMaker.addReferenceProperty("factoryClassLocation");
+ referenceMaker.addReferenceProperty("forceIgnoreUnresolvedTransactions");
+ referenceMaker.addReferenceProperty("idleConnectionTestPeriod");
+ referenceMaker.addReferenceProperty("identityToken");
+ referenceMaker.addReferenceProperty("initialPoolSize");
+ referenceMaker.addReferenceProperty("jndiEnv");
+ referenceMaker.addReferenceProperty("jndiLookupCaching");
+ referenceMaker.addReferenceProperty("jndiName");
+ referenceMaker.addReferenceProperty("maxIdleTime");
+ referenceMaker.addReferenceProperty("maxPoolSize");
+ referenceMaker.addReferenceProperty("maxStatements");
+ referenceMaker.addReferenceProperty("maxStatementsPerConnection");
+ referenceMaker.addReferenceProperty("minPoolSize");
+ referenceMaker.addReferenceProperty("preferredTestQuery");
+ referenceMaker.addReferenceProperty("propertyCycle");
+ referenceMaker.addReferenceProperty("testConnectionOnCheckin");
+ referenceMaker.addReferenceProperty("testConnectionOnCheckout");
+ referenceMaker.addReferenceProperty("usesTraditionalReflectiveProxies");
+ }
+
+ public Reference getReference() throws NamingException
+ { return referenceMaker.createReference( this ); }
+
+ //implementation of javax.sql.ConnectionPoolDataSource
+ public PooledConnection getPooledConnection()
+ throws SQLException
+ { return wcpds.getPooledConnection(); }
+
+ public PooledConnection getPooledConnection(String user, String password)
+ throws SQLException
+ { return wcpds.getPooledConnection( user, password ); }
+
+ public PrintWriter getLogWriter()
+ throws SQLException
+ { return wcpds.getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out)
+ throws SQLException
+ { wcpds.setLogWriter( out ); }
+
+ public void setLoginTimeout(int seconds)
+ throws SQLException
+ { wcpds.setLoginTimeout( seconds ); }
+
+ public int getLoginTimeout()
+ throws SQLException
+ { return wcpds.getLoginTimeout(); }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer(512);
+ sb.append( super.toString() );
+ sb.append(" [");
+ try { BeansUtils.appendPropNamesAndValues( sb, this, IGNORE_PROPS ); }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "An exception occurred while extracting property names and values for toString()", e);
+ sb.append( e.toString() );
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/JndiRefForwardingDataSource.java b/src/classes/com/mchange/v2/c3p0/JndiRefForwardingDataSource.java
new file mode 100644
index 0000000..03e0f9e
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/JndiRefForwardingDataSource.java
@@ -0,0 +1,169 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.VetoableChangeListener;
+import java.beans.PropertyVetoException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Hashtable;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.c3p0.impl.JndiRefDataSourceBase;
+
+final class JndiRefForwardingDataSource extends JndiRefDataSourceBase implements DataSource
+{
+ final static MLogger logger = MLog.getLogger( JndiRefForwardingDataSource.class );
+
+ //MT: protected by this' lock in all cases
+ transient DataSource cachedInner;
+
+ public JndiRefForwardingDataSource()
+ { this( true ); }
+
+ public JndiRefForwardingDataSource( boolean autoregister )
+ {
+ super( autoregister );
+ setUpPropertyListeners();
+ }
+
+ private void setUpPropertyListeners()
+ {
+ VetoableChangeListener l = new VetoableChangeListener()
+ {
+ public void vetoableChange( PropertyChangeEvent evt ) throws PropertyVetoException
+ {
+ Object val = evt.getNewValue();
+ if ( "jndiName".equals( evt.getPropertyName() ) )
+ {
+ if (! (val instanceof Name || val instanceof String) )
+ throw new PropertyVetoException("jndiName must be a String or a javax.naming.Name", evt);
+ }
+ }
+ };
+ this.addVetoableChangeListener( l );
+
+ PropertyChangeListener pcl = new PropertyChangeListener()
+ {
+ public void propertyChange( PropertyChangeEvent evt )
+ { cachedInner = null; }
+ };
+ this.addPropertyChangeListener( pcl );
+ }
+
+ //MT: called only from inner(), effectively synchrtonized
+ private DataSource dereference() throws SQLException
+ {
+ Object jndiName = this.getJndiName();
+ Hashtable jndiEnv = this.getJndiEnv();
+ try
+ {
+ InitialContext ctx;
+ if (jndiEnv != null)
+ ctx = new InitialContext( jndiEnv );
+ else
+ ctx = new InitialContext();
+ if (jndiName instanceof String)
+ return (DataSource) ctx.lookup( (String) jndiName );
+ else if (jndiName instanceof Name)
+ return (DataSource) ctx.lookup( (Name) jndiName );
+ else
+ throw new SQLException("Could not find ConnectionPoolDataSource with " +
+ "JNDI name: " + jndiName);
+ }
+ catch( NamingException e )
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "An Exception occurred while trying to look up a target DataSource via JNDI!", e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ private synchronized DataSource inner() throws SQLException
+ {
+ if (cachedInner != null)
+ return cachedInner;
+ else
+ {
+ DataSource out = dereference();
+ if (this.isCaching())
+ cachedInner = out;
+ return out;
+ }
+ }
+
+ public Connection getConnection() throws SQLException
+ { return inner().getConnection(); }
+
+ public Connection getConnection(String username, String password) throws SQLException
+ { return inner().getConnection( username, password ); }
+
+ public PrintWriter getLogWriter() throws SQLException
+ { return inner().getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out) throws SQLException
+ { inner().setLogWriter( out ); }
+
+ public int getLoginTimeout() throws SQLException
+ { return inner().getLoginTimeout(); }
+
+ public void setLoginTimeout(int seconds) throws SQLException
+ { inner().setLoginTimeout( seconds ); }
+
+ // serialization stuff -- set up bound/constrained property event handlers on deserialization
+ private static final long serialVersionUID = 1;
+ private static final short VERSION = 0x0001;
+
+ private void writeObject( ObjectOutputStream oos ) throws IOException
+ {
+ oos.writeShort( VERSION );
+ }
+
+ private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ short version = ois.readShort();
+ switch (version)
+ {
+ case VERSION:
+ setUpPropertyListeners();
+ break;
+ default:
+ throw new IOException("Unsupported Serialized Version: " + version);
+ }
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/PoolBackedDataSource.java b/src/classes/com/mchange/v2/c3p0/PoolBackedDataSource.java
new file mode 100644
index 0000000..ff8c61e
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/PoolBackedDataSource.java
@@ -0,0 +1,39 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource;
+
+public final class PoolBackedDataSource extends AbstractPoolBackedDataSource implements PooledDataSource
+{
+ public PoolBackedDataSource( boolean autoregister )
+ { super( autoregister ); }
+
+ public PoolBackedDataSource()
+ { this( true ); }
+
+ public PoolBackedDataSource(String configName)
+ { super( configName ); }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/PoolBackedDataSourceFactory.java b/src/classes/com/mchange/v2/c3p0/PoolBackedDataSourceFactory.java
new file mode 100644
index 0000000..92be4b1
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/PoolBackedDataSourceFactory.java
@@ -0,0 +1,670 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import com.mchange.v2.sql.SqlUtils;
+
+/**
+ * A class offering Factory methods for creating DataSources backed
+ * by Connection and Statement Pools.
+ *
+ * @deprecated Use the new factories in {@link com.mchange.v2.c3p0.DataSources}. See examples.
+ *
+ */
+public final class PoolBackedDataSourceFactory
+{
+ /**
+ * Creates a pool-backed DataSource that implements Referenceable
+ * for binding to JNDI name services. For this to work,
+ * <TT>unpooledDataSource</TT> must also implement Referenceable.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createReferenceable( DataSource unpooledDataSource,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements,
+ String factoryLocation ) throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource cpds = new WrapperConnectionPoolDataSource();
+ cpds.setNestedDataSource(unpooledDataSource);
+ cpds.setMinPoolSize( minPoolSize );
+ cpds.setMaxPoolSize( maxPoolSize );
+ cpds.setAcquireIncrement( acquireIncrement );
+ cpds.setMaxIdleTime( maxIdleTime );
+ cpds.setMaxStatements( maxStatements );
+ cpds.setFactoryClassLocation( factoryLocation );
+
+
+ PoolBackedDataSource out = new PoolBackedDataSource();
+ out.setConnectionPoolDataSource( cpds );
+ return out;
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException( e ); }
+ }
+
+ /**
+ * Creates a pool-backed DataSource that uses default pool parameters and
+ * implements Referenceable
+ * for binding to JNDI name services. For this to work,
+ * <TT>unpooledDataSource</TT> must also implement Referenceable.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createReferenceable( DataSource unpooledDataSource,
+ String factoryLocation )
+ throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource cpds = new WrapperConnectionPoolDataSource();
+ cpds.setNestedDataSource(unpooledDataSource);
+ cpds.setFactoryClassLocation( factoryLocation );
+
+ PoolBackedDataSource out = new PoolBackedDataSource();
+ out.setConnectionPoolDataSource( cpds );
+ return out;
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException( e ); }
+ }
+
+ /**
+ * Creates a pool-backed DataSource that implements Referenceable.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createReferenceable(String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements,
+ String factoryLocation ) throws SQLException
+ {
+ DataSource nested = DriverManagerDataSourceFactory.create( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password );
+ return createReferenceable( nested,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ factoryLocation );
+ }
+
+ /**
+ * Creates a pool-backed DataSource that implements Referenceable and uses
+ * default pooling parameters.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createReferenceable(String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password,
+ String factoryLocation )
+ throws SQLException
+ {
+ DataSource nested = DriverManagerDataSourceFactory.create( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password );
+ return createReferenceable( nested,
+ factoryLocation );
+ }
+
+ /**
+ * Creates a pool-backed DataSource that implements Serializable
+ * for binding to JNDI name services. For this to work,
+ * <TT>unpooledDataSource</TT> must also implement Serializable.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createSerializable( DataSource unpooledDataSource,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements)
+ throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource cpds = new WrapperConnectionPoolDataSource();
+ cpds.setNestedDataSource(unpooledDataSource);
+ cpds.setMinPoolSize( minPoolSize );
+ cpds.setMaxPoolSize( maxPoolSize );
+ cpds.setAcquireIncrement( acquireIncrement );
+ cpds.setMaxIdleTime( maxIdleTime );
+ cpds.setMaxStatements( maxStatements );
+
+ PoolBackedDataSource out = new PoolBackedDataSource();
+ out.setConnectionPoolDataSource( cpds );
+ return out;
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException( e ); }
+ }
+
+ /**
+ * Creates a pool-backed DataSource that uses default pool parameters and
+ * implements Serializable
+ * for binding to JNDI name services. For this to work,
+ * <TT>unpooledDataSource</TT> must also implement Serializable.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createSerializable( DataSource unpooledDataSource ) throws SQLException
+ {
+ try
+ {
+ WrapperConnectionPoolDataSource cpds = new WrapperConnectionPoolDataSource();
+ cpds.setNestedDataSource(unpooledDataSource);
+
+ PoolBackedDataSource out = new PoolBackedDataSource();
+ out.setConnectionPoolDataSource( cpds );
+ return out;
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException( e ); }
+ }
+
+
+ /**
+ * Creates a pool-backed DataSource that implements Serializable.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createSerializable( String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements)
+ throws SQLException
+ {
+ DataSource nested = DriverManagerDataSourceFactory.create( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password );
+ return createSerializable( nested,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements);
+ }
+
+ /**
+ * Creates a pool-backed DataSource that implements Serializable and uses
+ * default pooling parameters.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ *
+ * @deprecated all implementations are now both Referenceable and Serializable.
+ * use create()
+ */
+ public static DataSource createSerializable( String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password)
+ throws SQLException
+ {
+ DataSource nested = DriverManagerDataSourceFactory.create( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password );
+ return createSerializable( nested );
+ }
+
+ /**
+ * Creates a pool-backed DataSource using <TT>unpooledDataSource</TT>
+ * as its source for Connections. Not necessarily suitable for JNDI binding.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally. Used only if the JNDI service prefers
+ * References to Serialized Objects when Objects are bound.
+ */
+ public static DataSource create( DataSource unpooledDataSource,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements,
+ String factoryLocation) throws SQLException
+ {
+ return createReferenceable( unpooledDataSource,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ factoryLocation );
+ }
+
+ /**
+ * Creates a pool-backed DataSource using <TT>unpooledDataSource</TT>
+ * as its source for Connections. Not necessarily suitable for JNDI binding.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ */
+ public static DataSource create( DataSource unpooledDataSource,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements ) throws SQLException
+ {
+ return createReferenceable( unpooledDataSource,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ null );
+ }
+
+ /**
+ * Creates a pool-backed DataSource using <TT>unpooledDataSource</TT>
+ * as its source for Connections and default values for pool params.
+ *
+ * @param unpooledDataSource an unpooledDataSource to use as the
+ * primary source for connections.
+ */
+ public static DataSource create( DataSource unpooledDataSource ) throws SQLException
+ { return createSerializable( unpooledDataSource ); }
+
+ /**
+ * Creates a pool-backed DataSource.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally. Used only if the JNDI service prefers
+ * References to Serialized Objects when Objects are bound.
+ */
+ public static DataSource create( String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements,
+ String factoryLocation )
+ throws SQLException
+ {
+ return createReferenceable( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ factoryLocation );
+ }
+
+ /**
+ * Creates a pool-backed DataSource.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ */
+ public static DataSource create( String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements )
+ throws SQLException
+ {
+ return createReferenceable( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ null );
+ }
+ /**
+ * Creates a pool-backed DataSource.
+ *
+ * <P>Warning: If you use this method, you must make sure a JDBC driver
+ * capable of resolving <TT>jdbcUrl</TT> has been preloaded!</P>
+ *
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ * @param factoryLocation a codebase url where JNDI clients can find the
+ * c3p0 libraries. Use null if clients will be expected to have the
+ * libraries available locally. Used only if the JNDI service prefers
+ * References to Serialized Objects when Objects are bound.
+ */
+ public static DataSource create( String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements,
+ String factoryLocation )
+ throws SQLException
+ {
+ return create( null,
+ jdbcUrl,
+ user,
+ password,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ factoryLocation );
+ }
+
+ /**
+ * Creates a pool-backed DataSource.
+ *
+ * <P>Warning: If you use this method, you must make sure a JDBC driver
+ * capable of resolving <TT>jdbcUrl</TT> has been preloaded!</P>
+ *
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ * @param minPoolSize the minimum (and starting) number of Connections
+ * that should be held in the pool.
+ * @param maxPoolSize the maximum number of Connections
+ * that should be held in the pool.
+ * @param acquireIncrement the number of Connections that should be
+ * acquired at a time when the pool runs out of Connections
+ * @param maxIdleTime the maximum number of seconds a Connection should be
+ * allowed to remain idle before it is expired from the pool.
+ * A value of 0 means Connections never expire.
+ * @param maxStatements the maximum number of PreparedStatements that should
+ * be cached by this pool. A value of 0 means that Statement caching
+ * should be disabled.
+ */
+ public static DataSource create( String jdbcUrl,
+ String user,
+ String password,
+ int minPoolSize,
+ int maxPoolSize,
+ int acquireIncrement,
+ int maxIdleTime,
+ int maxStatements )
+ throws SQLException
+ {
+ return create( null,
+ jdbcUrl,
+ user,
+ password,
+ minPoolSize,
+ maxPoolSize,
+ acquireIncrement,
+ maxIdleTime,
+ maxStatements,
+ null );
+ }
+
+ /**
+ * Creates a pool-backed DataSource using default values for pool parameters.
+ * Not necessarily suitable for JNDI binding.
+ *
+ * @param jdbcDriverClass a jdbc driver class that can resolve <TT>jdbcUrl</TT>.
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ */
+ public static DataSource create( String jdbcDriverClass,
+ String jdbcUrl,
+ String user,
+ String password) throws SQLException
+ {
+ return createSerializable( jdbcDriverClass,
+ jdbcUrl,
+ user,
+ password );
+ }
+
+ /**
+ * Creates a pool-backed DataSource using default pool parameters.
+ *
+ *
+ * <P>Warning: If you use this method, you must make sure a JDBC driver
+ * capable of resolving <TT>jdbcUrl</TT> has been preloaded!</P>
+ *
+ * @param jdbcUrl the jdbcUrl of the RDBMS that Connections should be made to.
+ * @param user a username (may be null) for authentication to the RDBMS
+ * @param password a password (may be null) for authentication to the RDBMS
+ */
+ public static DataSource create( String jdbcUrl,
+ String user,
+ String password)
+ throws SQLException
+ {
+ return create( null,
+ jdbcUrl,
+ user,
+ password );
+ }
+
+ private PoolBackedDataSourceFactory()
+ {}
+}
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/PoolConfig.java b/src/classes/com/mchange/v2/c3p0/PoolConfig.java
new file mode 100644
index 0000000..bbba8f2
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/PoolConfig.java
@@ -0,0 +1,635 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.util.Properties;
+import java.io.InputStream;
+import java.io.IOException;
+import com.mchange.v1.io.InputStreamUtils;
+import com.mchange.v2.c3p0.impl.C3P0Defaults;
+import com.mchange.v2.cfg.MultiPropertiesConfig;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+
+/**
+ * <p>Encapsulates all the configuration information required by a c3p0 pooled DataSource.</p>
+ *
+ * <p>Newly constructed PoolConfig objects are preset with default values,
+ * which you can define yourself (see below),
+ * or you can rely on c3p0's built-in defaults. Just create a PoolConfig object, and change only the
+ * properties you care about. Then pass it to the {@link com.mchange.v2.c3p0.DataSources#pooledDataSource(javax.sql.DataSource, com.mchange.v2.c3p0.PoolConfig)}
+ * method, and you're off!</p>
+ *
+ * <p>For those interested in the details, configuration properties can be specified in several ways:</p>
+ * <ol>
+ * <li>Any property can be set explicitly by calling the corresponding method on a PoolConfig object.</li>
+ * <li>Any property will default to a value defined by a System Property, using the property name shown the table below.</li>
+ * <li>Any property not set in either of the above ways will default to a value found in a user-supplied Java properties file,
+ * which may be placed in the resource path of
+ * the ClassLoader that loaded the c3p0 libraries under the name <tt>/c3p0.properties</tt>.</li>
+ * <li>Any property not set in any of the above ways will be defined according c3p0's built-in defaults.</li>
+ * </ol>
+ *
+ * <p><i>Please see c3p0's main documentation for a description of all available parameters.</i></p>
+ *
+ * @deprecated as of c3p0-0.9.1. To manipulate config programmaticall, please use ComboPooledDataSource
+ *
+ */
+public final class PoolConfig
+{
+ final static MLogger logger;
+
+ public final static String INITIAL_POOL_SIZE = "c3p0.initialPoolSize";
+ public final static String MIN_POOL_SIZE = "c3p0.minPoolSize";
+ public final static String MAX_POOL_SIZE = "c3p0.maxPoolSize";
+ public final static String IDLE_CONNECTION_TEST_PERIOD = "c3p0.idleConnectionTestPeriod";
+ public final static String MAX_IDLE_TIME = "c3p0.maxIdleTime";
+ public final static String PROPERTY_CYCLE = "c3p0.propertyCycle";
+ public final static String MAX_STATEMENTS = "c3p0.maxStatements";
+ public final static String MAX_STATEMENTS_PER_CONNECTION = "c3p0.maxStatementsPerConnection";
+ public final static String CHECKOUT_TIMEOUT = "c3p0.checkoutTimeout";
+ public final static String ACQUIRE_INCREMENT = "c3p0.acquireIncrement";
+ public final static String ACQUIRE_RETRY_ATTEMPTS = "c3p0.acquireRetryAttempts";
+ public final static String ACQUIRE_RETRY_DELAY = "c3p0.acquireRetryDelay";
+ public final static String BREAK_AFTER_ACQUIRE_FAILURE = "c3p0.breakAfterAcquireFailure";
+ public final static String USES_TRADITIONAL_REFLECTIVE_PROXIES = "c3p0.usesTraditionalReflectiveProxies";
+ public final static String TEST_CONNECTION_ON_CHECKOUT = "c3p0.testConnectionOnCheckout";
+ public final static String TEST_CONNECTION_ON_CHECKIN = "c3p0.testConnectionOnCheckin";
+ public final static String CONNECTION_TESTER_CLASS_NAME = "c3p0.connectionTesterClassName";
+ public final static String AUTOMATIC_TEST_TABLE = "c3p0.automaticTestTable";
+ public final static String AUTO_COMMIT_ON_CLOSE = "c3p0.autoCommitOnClose";
+ public final static String FORCE_IGNORE_UNRESOLVED_TRANSACTIONS = "c3p0.forceIgnoreUnresolvedTransactions";
+ public final static String NUM_HELPER_THREADS = "c3p0.numHelperThreads";
+ public final static String PREFERRED_TEST_QUERY = "c3p0.preferredTestQuery";
+ public final static String FACTORY_CLASS_LOCATION = "c3p0.factoryClassLocation";
+
+ public final static String DEFAULT_CONFIG_RSRC_PATH = "/c3p0.properties";
+
+ final static PoolConfig DEFAULTS;
+
+ static
+ {
+ logger = MLog.getLogger( PoolConfig.class );
+
+ Properties rsrcProps = findResourceProperties();
+ PoolConfig rsrcDefaults = extractConfig( rsrcProps, null );
+
+ Properties sysProps;
+ try
+ { sysProps = System.getProperties(); }
+ catch ( SecurityException e )
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "Read of system Properties blocked -- ignoring any c3p0 configuration via System properties! " +
+ "(But any configuration via a c3p0.properties file is still okay!)",
+ e);
+ sysProps = new Properties(); //TODO -- an alternative approach to getting c3p0-specific sysprops if allowed
+ }
+ DEFAULTS = extractConfig( sysProps, rsrcDefaults );
+ }
+
+ public static int defaultNumHelperThreads()
+ { return DEFAULTS.getNumHelperThreads(); }
+
+ public static String defaultPreferredTestQuery()
+ { return DEFAULTS.getPreferredTestQuery(); }
+
+ public static String defaultFactoryClassLocation()
+ { return DEFAULTS.getFactoryClassLocation(); }
+
+ public static int defaultMaxStatements()
+ { return DEFAULTS.getMaxStatements(); }
+
+ public static int defaultMaxStatementsPerConnection()
+ { return DEFAULTS.getMaxStatementsPerConnection(); }
+
+ public static int defaultInitialPoolSize()
+ { return DEFAULTS.getInitialPoolSize(); }
+
+ public static int defaultMinPoolSize()
+ { return DEFAULTS.getMinPoolSize(); }
+
+ public static int defaultMaxPoolSize()
+ { return DEFAULTS.getMaxPoolSize(); }
+
+ public static int defaultIdleConnectionTestPeriod()
+ { return DEFAULTS.getIdleConnectionTestPeriod(); }
+
+ public static int defaultMaxIdleTime()
+ { return DEFAULTS.getMaxIdleTime(); }
+
+ public static int defaultPropertyCycle()
+ { return DEFAULTS.getPropertyCycle(); }
+
+ public static int defaultCheckoutTimeout()
+ { return DEFAULTS.getCheckoutTimeout(); }
+
+ public static int defaultAcquireIncrement()
+ { return DEFAULTS.getAcquireIncrement(); }
+
+ public static int defaultAcquireRetryAttempts()
+ { return DEFAULTS.getAcquireRetryAttempts(); }
+
+ public static int defaultAcquireRetryDelay()
+ { return DEFAULTS.getAcquireRetryDelay(); }
+
+ public static boolean defaultBreakAfterAcquireFailure()
+ { return DEFAULTS.isBreakAfterAcquireFailure(); }
+
+ public static String defaultConnectionTesterClassName()
+ { return DEFAULTS.getConnectionTesterClassName(); }
+
+ public static String defaultAutomaticTestTable()
+ { return DEFAULTS.getAutomaticTestTable(); }
+
+ public static boolean defaultTestConnectionOnCheckout()
+ { return DEFAULTS.isTestConnectionOnCheckout(); }
+
+ public static boolean defaultTestConnectionOnCheckin()
+ { return DEFAULTS.isTestConnectionOnCheckin(); }
+
+ public static boolean defaultAutoCommitOnClose()
+ { return DEFAULTS.isAutoCommitOnClose(); }
+
+ public static boolean defaultForceIgnoreUnresolvedTransactions()
+ { return DEFAULTS.isAutoCommitOnClose(); }
+
+ public static boolean defaultUsesTraditionalReflectiveProxies()
+ { return DEFAULTS.isUsesTraditionalReflectiveProxies(); }
+
+
+ int maxStatements;
+ int maxStatementsPerConnection;
+ int initialPoolSize;
+ int minPoolSize;
+ int maxPoolSize;
+ int idleConnectionTestPeriod;
+ int maxIdleTime;
+ int propertyCycle;
+ int checkoutTimeout;
+ int acquireIncrement;
+ int acquireRetryAttempts;
+ int acquireRetryDelay;
+ boolean breakAfterAcquireFailure;
+ boolean testConnectionOnCheckout;
+ boolean testConnectionOnCheckin;
+ boolean autoCommitOnClose;
+ boolean forceIgnoreUnresolvedTransactions;
+ boolean usesTraditionalReflectiveProxies;
+ String connectionTesterClassName;
+ String automaticTestTable;
+ int numHelperThreads;
+ String preferredTestQuery;
+ String factoryClassLocation;
+
+ private PoolConfig( Properties props, boolean init ) throws NumberFormatException
+ {
+ if (init)
+ extractConfig( this, props, DEFAULTS );
+ }
+
+ public PoolConfig( Properties props ) throws NumberFormatException
+ { this( props, true ); }
+
+ public PoolConfig() throws NumberFormatException
+ { this( null, true ); }
+
+ public int getNumHelperThreads()
+ { return numHelperThreads; }
+
+ public String getPreferredTestQuery()
+ { return preferredTestQuery; }
+
+ public String getFactoryClassLocation()
+ { return factoryClassLocation; }
+
+ public int getMaxStatements()
+ { return maxStatements; }
+
+ public int getMaxStatementsPerConnection()
+ { return maxStatementsPerConnection; }
+
+ public int getInitialPoolSize()
+ { return initialPoolSize; }
+
+ public int getMinPoolSize()
+ { return minPoolSize; }
+
+ public int getMaxPoolSize()
+ { return maxPoolSize; }
+
+ public int getIdleConnectionTestPeriod()
+ { return idleConnectionTestPeriod; }
+
+ public int getMaxIdleTime()
+ { return maxIdleTime; }
+
+ public int getPropertyCycle()
+ { return propertyCycle; }
+
+ public int getAcquireIncrement()
+ { return acquireIncrement; }
+
+ public int getCheckoutTimeout()
+ { return checkoutTimeout; }
+
+ public int getAcquireRetryAttempts()
+ { return acquireRetryAttempts; }
+
+ public int getAcquireRetryDelay()
+ { return acquireRetryDelay; }
+
+ public boolean isBreakAfterAcquireFailure()
+ { return this.breakAfterAcquireFailure; }
+
+ public boolean isUsesTraditionalReflectiveProxies()
+ { return this.usesTraditionalReflectiveProxies; }
+
+ public String getConnectionTesterClassName()
+ { return connectionTesterClassName; }
+
+ public String getAutomaticTestTable()
+ { return automaticTestTable; }
+
+ /**
+ * @deprecated use isTestConnectionOnCheckout
+ */
+ public boolean getTestConnectionOnCheckout()
+ { return testConnectionOnCheckout; }
+
+ public boolean isTestConnectionOnCheckout()
+ { return this.getTestConnectionOnCheckout(); }
+
+ public boolean isTestConnectionOnCheckin()
+ { return testConnectionOnCheckin; }
+
+ public boolean isAutoCommitOnClose()
+ { return this.autoCommitOnClose; }
+
+ public boolean isForceIgnoreUnresolvedTransactions()
+ { return this.forceIgnoreUnresolvedTransactions; }
+
+ public void setNumHelperThreads( int numHelperThreads )
+ { this.numHelperThreads = numHelperThreads; }
+
+ public void setPreferredTestQuery( String preferredTestQuery )
+ { this.preferredTestQuery = preferredTestQuery; }
+
+ public void setFactoryClassLocation( String factoryClassLocation )
+ { this.factoryClassLocation = factoryClassLocation; }
+
+ public void setMaxStatements( int maxStatements )
+ { this.maxStatements = maxStatements; }
+
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection )
+ { this.maxStatementsPerConnection = maxStatementsPerConnection; }
+
+ public void setInitialPoolSize( int initialPoolSize )
+ { this.initialPoolSize = initialPoolSize; }
+
+ public void setMinPoolSize( int minPoolSize )
+ { this.minPoolSize = minPoolSize; }
+
+ public void setMaxPoolSize( int maxPoolSize )
+ { this.maxPoolSize = maxPoolSize; }
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod )
+ { this.idleConnectionTestPeriod = idleConnectionTestPeriod; }
+
+ public void setMaxIdleTime( int maxIdleTime )
+ { this.maxIdleTime = maxIdleTime; }
+
+ public void setPropertyCycle( int propertyCycle )
+ { this.propertyCycle = propertyCycle; }
+
+ public void setCheckoutTimeout( int checkoutTimeout )
+ { this.checkoutTimeout = checkoutTimeout; }
+
+ public void setAcquireIncrement( int acquireIncrement )
+ { this.acquireIncrement = acquireIncrement; }
+
+ public void setAcquireRetryAttempts( int acquireRetryAttempts )
+ { this.acquireRetryAttempts = acquireRetryAttempts; }
+
+ public void setAcquireRetryDelay( int acquireRetryDelay )
+ { this.acquireRetryDelay = acquireRetryDelay; }
+
+ public void setConnectionTesterClassName( String connectionTesterClassName )
+ { this.connectionTesterClassName = connectionTesterClassName; }
+
+ public void setAutomaticTestTable( String automaticTestTable )
+ { this.automaticTestTable = automaticTestTable; }
+
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure )
+ { this.breakAfterAcquireFailure = breakAfterAcquireFailure; }
+
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies )
+ { this.usesTraditionalReflectiveProxies = usesTraditionalReflectiveProxies; }
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout )
+ { this.testConnectionOnCheckout = testConnectionOnCheckout; }
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin )
+ { this.testConnectionOnCheckin = testConnectionOnCheckin; }
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose )
+ { this.autoCommitOnClose = autoCommitOnClose; }
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions )
+ { this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions; }
+
+ private static PoolConfig extractConfig(Properties props, PoolConfig defaults) throws NumberFormatException
+ {
+ PoolConfig pcfg = new PoolConfig(null, false);
+ extractConfig( pcfg, props, defaults );
+ return pcfg;
+ }
+
+ private static void extractConfig(PoolConfig pcfg, Properties props, PoolConfig defaults) throws NumberFormatException
+ {
+ String maxStatementsStr = null;
+ String maxStatementsPerConnectionStr = null;
+ String initialPoolSizeStr = null;
+ String minPoolSizeStr = null;
+ String maxPoolSizeStr = null;
+ String idleConnectionTestPeriodStr = null;
+ String maxIdleTimeStr = null;
+ String propertyCycleStr = null;
+ String checkoutTimeoutStr = null;
+ String acquireIncrementStr = null;
+ String acquireRetryAttemptsStr = null;
+ String acquireRetryDelayStr = null;
+ String breakAfterAcquireFailureStr = null;
+ String usesTraditionalReflectiveProxiesStr = null;
+ String testConnectionOnCheckoutStr = null;
+ String testConnectionOnCheckinStr = null;
+ String autoCommitOnCloseStr = null;
+ String forceIgnoreUnresolvedTransactionsStr = null;
+ String connectionTesterClassName = null;
+ String automaticTestTable = null;
+ String numHelperThreadsStr = null;
+ String preferredTestQuery = null;
+ String factoryClassLocation = null;
+
+ if ( props != null )
+ {
+ maxStatementsStr = props.getProperty(MAX_STATEMENTS);
+ maxStatementsPerConnectionStr = props.getProperty(MAX_STATEMENTS_PER_CONNECTION);
+ initialPoolSizeStr = props.getProperty(INITIAL_POOL_SIZE);
+ minPoolSizeStr = props.getProperty(MIN_POOL_SIZE);
+ maxPoolSizeStr = props.getProperty(MAX_POOL_SIZE);
+ idleConnectionTestPeriodStr = props.getProperty(IDLE_CONNECTION_TEST_PERIOD);
+ maxIdleTimeStr = props.getProperty(MAX_IDLE_TIME);
+ propertyCycleStr = props.getProperty(PROPERTY_CYCLE);
+ checkoutTimeoutStr = props.getProperty(CHECKOUT_TIMEOUT);
+ acquireIncrementStr = props.getProperty(ACQUIRE_INCREMENT);
+ acquireRetryAttemptsStr = props.getProperty(ACQUIRE_RETRY_ATTEMPTS);
+ acquireRetryDelayStr = props.getProperty(ACQUIRE_RETRY_DELAY);
+ breakAfterAcquireFailureStr = props.getProperty(BREAK_AFTER_ACQUIRE_FAILURE);
+ usesTraditionalReflectiveProxiesStr = props.getProperty(USES_TRADITIONAL_REFLECTIVE_PROXIES);
+ testConnectionOnCheckoutStr = props.getProperty(TEST_CONNECTION_ON_CHECKOUT);
+ testConnectionOnCheckinStr = props.getProperty(TEST_CONNECTION_ON_CHECKIN);
+ autoCommitOnCloseStr = props.getProperty(AUTO_COMMIT_ON_CLOSE);
+ forceIgnoreUnresolvedTransactionsStr = props.getProperty(FORCE_IGNORE_UNRESOLVED_TRANSACTIONS);
+ connectionTesterClassName = props.getProperty(CONNECTION_TESTER_CLASS_NAME);
+ automaticTestTable = props.getProperty(AUTOMATIC_TEST_TABLE);
+ numHelperThreadsStr = props.getProperty(NUM_HELPER_THREADS);
+ preferredTestQuery = props.getProperty(PREFERRED_TEST_QUERY);
+ factoryClassLocation = props.getProperty(FACTORY_CLASS_LOCATION);
+ }
+
+ // maxStatements
+ if ( maxStatementsStr != null )
+ pcfg.setMaxStatements( Integer.parseInt( maxStatementsStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setMaxStatements( defaults.getMaxStatements() );
+ else
+ pcfg.setMaxStatements( C3P0Defaults.maxStatements() );
+
+ // maxStatementsPerConnection
+ if ( maxStatementsPerConnectionStr != null )
+ pcfg.setMaxStatementsPerConnection( Integer.parseInt( maxStatementsPerConnectionStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setMaxStatementsPerConnection( defaults.getMaxStatementsPerConnection() );
+ else
+ pcfg.setMaxStatementsPerConnection( C3P0Defaults.maxStatementsPerConnection() );
+
+ // initialPoolSize
+ if ( initialPoolSizeStr != null )
+ pcfg.setInitialPoolSize( Integer.parseInt( initialPoolSizeStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setInitialPoolSize( defaults.getInitialPoolSize() );
+ else
+ pcfg.setInitialPoolSize( C3P0Defaults.initialPoolSize() );
+
+ // minPoolSize
+ if ( minPoolSizeStr != null )
+ pcfg.setMinPoolSize( Integer.parseInt( minPoolSizeStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setMinPoolSize( defaults.getMinPoolSize() );
+ else
+ pcfg.setMinPoolSize( C3P0Defaults.minPoolSize() );
+
+ // maxPoolSize
+ if ( maxPoolSizeStr != null )
+ pcfg.setMaxPoolSize( Integer.parseInt( maxPoolSizeStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setMaxPoolSize( defaults.getMaxPoolSize() );
+ else
+ pcfg.setMaxPoolSize( C3P0Defaults.maxPoolSize() );
+
+ // maxIdleTime
+ if ( idleConnectionTestPeriodStr != null )
+ pcfg.setIdleConnectionTestPeriod( Integer.parseInt( idleConnectionTestPeriodStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setIdleConnectionTestPeriod( defaults.getIdleConnectionTestPeriod() );
+ else
+ pcfg.setIdleConnectionTestPeriod( C3P0Defaults.idleConnectionTestPeriod() );
+
+ // maxIdleTime
+ if ( maxIdleTimeStr != null )
+ pcfg.setMaxIdleTime( Integer.parseInt( maxIdleTimeStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setMaxIdleTime( defaults.getMaxIdleTime() );
+ else
+ pcfg.setMaxIdleTime( C3P0Defaults.maxIdleTime() );
+
+ // propertyCycle
+ if ( propertyCycleStr != null )
+ pcfg.setPropertyCycle( Integer.parseInt( propertyCycleStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setPropertyCycle( defaults.getPropertyCycle() );
+ else
+ pcfg.setPropertyCycle( C3P0Defaults.propertyCycle() );
+
+ // checkoutTimeout
+ if ( checkoutTimeoutStr != null )
+ pcfg.setCheckoutTimeout( Integer.parseInt( checkoutTimeoutStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setCheckoutTimeout( defaults.getCheckoutTimeout() );
+ else
+ pcfg.setCheckoutTimeout( C3P0Defaults.checkoutTimeout() );
+
+ // acquireIncrement
+ if ( acquireIncrementStr != null )
+ pcfg.setAcquireIncrement( Integer.parseInt( acquireIncrementStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setAcquireIncrement( defaults.getAcquireIncrement() );
+ else
+ pcfg.setAcquireIncrement( C3P0Defaults.acquireIncrement() );
+
+ // acquireRetryAttempts
+ if ( acquireRetryAttemptsStr != null )
+ pcfg.setAcquireRetryAttempts( Integer.parseInt( acquireRetryAttemptsStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setAcquireRetryAttempts( defaults.getAcquireRetryAttempts() );
+ else
+ pcfg.setAcquireRetryAttempts( C3P0Defaults.acquireRetryAttempts() );
+
+ // acquireRetryDelay
+ if ( acquireRetryDelayStr != null )
+ pcfg.setAcquireRetryDelay( Integer.parseInt( acquireRetryDelayStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setAcquireRetryDelay( defaults.getAcquireRetryDelay() );
+ else
+ pcfg.setAcquireRetryDelay( C3P0Defaults.acquireRetryDelay() );
+
+ // breakAfterAcquireFailure
+ if ( breakAfterAcquireFailureStr != null )
+ pcfg.setBreakAfterAcquireFailure( Boolean.valueOf(breakAfterAcquireFailureStr.trim()).booleanValue() );
+ else if (defaults != null)
+ pcfg.setBreakAfterAcquireFailure( defaults.isBreakAfterAcquireFailure() );
+ else
+ pcfg.setBreakAfterAcquireFailure( C3P0Defaults.breakAfterAcquireFailure() );
+
+ // usesTraditionalReflectiveProxies
+ if ( usesTraditionalReflectiveProxiesStr != null )
+ pcfg.setUsesTraditionalReflectiveProxies( Boolean.valueOf(usesTraditionalReflectiveProxiesStr.trim()).booleanValue() );
+ else if (defaults != null)
+ pcfg.setUsesTraditionalReflectiveProxies( defaults.isUsesTraditionalReflectiveProxies() );
+ else
+ pcfg.setUsesTraditionalReflectiveProxies( C3P0Defaults.usesTraditionalReflectiveProxies() );
+
+ // testConnectionOnCheckout
+ if ( testConnectionOnCheckoutStr != null )
+ pcfg.setTestConnectionOnCheckout( Boolean.valueOf(testConnectionOnCheckoutStr.trim()).booleanValue() );
+ else if (defaults != null)
+ pcfg.setTestConnectionOnCheckout( defaults.isTestConnectionOnCheckout() );
+ else
+ pcfg.setTestConnectionOnCheckout( C3P0Defaults.testConnectionOnCheckout() );
+
+ // testConnectionOnCheckin
+ if ( testConnectionOnCheckinStr != null )
+ pcfg.setTestConnectionOnCheckin( Boolean.valueOf(testConnectionOnCheckinStr.trim()).booleanValue() );
+ else if (defaults != null)
+ pcfg.setTestConnectionOnCheckin( defaults.isTestConnectionOnCheckin() );
+ else
+ pcfg.setTestConnectionOnCheckin( C3P0Defaults.testConnectionOnCheckin() );
+
+ // autoCommitOnClose
+ if ( autoCommitOnCloseStr != null )
+ pcfg.setAutoCommitOnClose( Boolean.valueOf(autoCommitOnCloseStr.trim()).booleanValue() );
+ else if (defaults != null)
+ pcfg.setAutoCommitOnClose( defaults.isAutoCommitOnClose() );
+ else
+ pcfg.setAutoCommitOnClose( C3P0Defaults.autoCommitOnClose() );
+
+ // forceIgnoreUnresolvedTransactions
+ if ( forceIgnoreUnresolvedTransactionsStr != null )
+ pcfg.setForceIgnoreUnresolvedTransactions( Boolean.valueOf( forceIgnoreUnresolvedTransactionsStr.trim() ).booleanValue() );
+ else if (defaults != null)
+ pcfg.setForceIgnoreUnresolvedTransactions( defaults.isForceIgnoreUnresolvedTransactions() );
+ else
+ pcfg.setForceIgnoreUnresolvedTransactions( C3P0Defaults.forceIgnoreUnresolvedTransactions() );
+
+ // connectionTesterClassName
+ if ( connectionTesterClassName != null )
+ pcfg.setConnectionTesterClassName( connectionTesterClassName.trim() );
+ else if (defaults != null)
+ pcfg.setConnectionTesterClassName( defaults.getConnectionTesterClassName() );
+ else
+ pcfg.setConnectionTesterClassName( C3P0Defaults.connectionTesterClassName() );
+
+ // automaticTestTable
+ if ( automaticTestTable != null )
+ pcfg.setAutomaticTestTable( automaticTestTable.trim() );
+ else if (defaults != null)
+ pcfg.setAutomaticTestTable( defaults.getAutomaticTestTable() );
+ else
+ pcfg.setAutomaticTestTable( C3P0Defaults.automaticTestTable() );
+
+ // numHelperThreads
+ if ( numHelperThreadsStr != null )
+ pcfg.setNumHelperThreads( Integer.parseInt( numHelperThreadsStr.trim() ) );
+ else if (defaults != null)
+ pcfg.setNumHelperThreads( defaults.getNumHelperThreads() );
+ else
+ pcfg.setNumHelperThreads( C3P0Defaults.numHelperThreads() );
+
+ // preferredTestQuery
+ if ( preferredTestQuery != null )
+ pcfg.setPreferredTestQuery( preferredTestQuery.trim() );
+ else if (defaults != null)
+ pcfg.setPreferredTestQuery( defaults.getPreferredTestQuery() );
+ else
+ pcfg.setPreferredTestQuery( C3P0Defaults.preferredTestQuery() );
+
+ // factoryClassLocation
+ if ( factoryClassLocation != null )
+ pcfg.setFactoryClassLocation( factoryClassLocation.trim() );
+ else if (defaults != null)
+ pcfg.setFactoryClassLocation( defaults.getFactoryClassLocation() );
+ else
+ pcfg.setFactoryClassLocation( C3P0Defaults.factoryClassLocation() );
+ }
+
+ private static Properties findResourceProperties()
+ { return MultiPropertiesConfig.readVmConfig().getPropertiesByResourcePath(DEFAULT_CONFIG_RSRC_PATH); }
+
+ private static Properties origFindResourceProperties()
+ {
+ Properties props = new Properties();
+
+ InputStream is = null;
+ try
+ {
+ is = PoolConfig.class.getResourceAsStream(DEFAULT_CONFIG_RSRC_PATH);
+ if ( is != null )
+ props.load( is );
+ }
+ catch (IOException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "An IOException occurred while trying to read Pool properties!", e );
+ props = new Properties();
+ }
+ finally
+ { InputStreamUtils.attemptClose( is ); }
+
+ return props;
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/PooledDataSource.java b/src/classes/com/mchange/v2/c3p0/PooledDataSource.java
new file mode 100644
index 0000000..7cc197a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/PooledDataSource.java
@@ -0,0 +1,283 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import java.util.Collection;
+
+/**
+ * <p><b>Most clients need never use or know about this interface -- c3p0 pooled DataSources
+ * can be treated like any other DataSource.</b></p>
+ *
+ * <p>The functionality in this interface will be only be of interest if 1) for administrative
+ * reasons you like to keep close track of the number and status of all Connections your application
+ * is using; 2) to work around problems encountered while managing a DataSource whose clients are
+ * poorly coded applications that leak Connections, but which you are not permitted to fix;
+ * or 3) to work around problems that may occur if an underlying jdbc driver / DBMS system is
+ * unreliable. In the third case, most users will be better off not using the present interface
+ * at all, and using the DataSources' <tt>maxIdleTime</tt>, <tt>idleConnectionTestPeriod</tt>,
+ * or <tt>testConnectionOnCheckout</tt> parameters to help your DataSources "automatically" heal.
+ * But for those who prefer a more direct, manual approach, this interface is for you. It is anticipated
+ * that the methods of this interface will primarily be of use to administrators managing c3p0
+ * PooledDataSources via JMX MBeans.</p>
+ *
+ * <a name="peruserpools"><h3>Method Names & Per-User Pools</h3></a>
+ *
+ * <p>To understand this interface, you need to realize that a c3p0 PooledDataSource may represent
+ * not just one pool of Connections, but many, if users call the method
+ * <tt>Connection getConnection(String username, String password)</tt> rather than the
+ * no-argument <tt>getConnection()</tt> method. If users make use of non-default username, password
+ * combinations, there will be a separate pool for each set of authentification criteria supplied.</p>
+ *
+ * <p>Many methods in this interface have three variants:</p>
+ * <ol>
+ * <li><tt><i><method-name></i>DefaultUser()</tt></li>
+ * <li><tt><i><method-name></i>(String username, String password)</tt></li>
+ * <li><tt><i><method-name></i>AllUsers()</tt></li>
+ * </ol>
+ * <p>The first variant makes use of the pool maintained for the default user --
+ * Connections created by calls to the no argument <tt>getConnection()</tt>,
+ * the second variant lets you keeps track of pools created by calling
+ * <tt>getConnection( <i>username</i>, <i>password</i> )</tt>, and the third variant
+ * provides aggregate information or performs operation on all pools.</p>
+ *
+ * <p>Under most circumstances, non-default authentication credentials will not
+ * be used, and methods of the first variant are sufficient to manage the DataSource.</p>
+ *
+ * <h3>Soft and Hard Resets</h3>
+ *
+ * <p>A properly configured PooledDataSource whose applications are careful to close all checked-out Connections
+ * would never need to use these methods. But, sometimes applications are untrustworthy
+ * and leak Connections, or database administrators suspect that Connections may be corrupt or invalid,
+ * and would like to force a pool to flush and acquire fresh Connections. This interface provides two
+ * ways to do so.</p>
+ *
+ * <ol>
+ * <li><b><tt>hardReset()</tt></b> immediately closes all Connections managed by the DataSource, including
+ * those that are currently checked out, bringing the DataSource back to the state it was in before
+ * the first client called getConnection(). This method is obviously disruptive, and should be with
+ * great care. Administrators who need to work around client applications that leak Connections, can
+ * periodically poll for pool exhaustion (using the methods of this class, or by attempting to retrieve
+ * a Connection and timing out) and use this method clean-up all Connections and start over. But calling
+ * this method risks breaking Connections in current use by valid applications.<br/><br/></li>
+ *
+ * <li><b><tt>softResetDefaultUser()</tt></b>, <b><tt>softReset( <i>username</i>, <i>password</i> )</tt></b> and
+ * <b><tt>softResetAllUsers()</tt></b> asks the DataSource to flush its current pool of Connections and
+ * reacquire <i>without</i> invalidating currently checked-out Connections. Currently checked out Connections
+ * are logically removed from the pool, but their destruction is deferred until a client attempts to close() / check-in
+ * the Connection. Administrators who suspect that some Connections in the pool may be invalid, but who do not
+ * wish to rely upon c3p0's automatic testing and detection mechanisms to resolve the problem, may call these
+ * methods to force a refresh without disrupting current clients. Administrators who suspect that clients may be
+ * leaking Connections may minimize disruptive hardReset() calls by using softReset() until the number of unclosed
+ * orphaned connections reaches an unacceptable level. (See <a href="#peruserpools">above</a> to understand
+ * why there are three variants of this method.)</li>
+ * </ol>
+ *
+ * <h3>Understanding Connection Counts</h3>
+ *
+ * <p>For each <a href="#peruserpools">per-user pool</a>, four different statistics are available:</p>
+ *
+ * <ol>
+ * <li><tt>numConnections</tt> represents the total number of Connections in the pool.<br/><br/></li>
+ * <li><tt>numIdleConnections</tt> represents the number of Connections in the pool that are currently available for checkout.<br/><br/></li>
+ * <li><tt>numBusyConnections</tt> represents the number of Connections in the pool that are currently checked out. The
+ * invariant <tt>numIdleConnections + numBusyConnections == numConnections</tt> should always hold.<br/><br/></li>
+ * <li><tt>numUnclosedOrphanedConnections</tt> will only be non-zero following a call to <tt>softReset()</tt>. It represents
+ * the number of Connections that were checked out when a soft reset occurred and were therefore
+ * silently excluded from the pool, and which remain unclosed by the client application.</li>
+ * </ol>
+ */
+public interface PooledDataSource extends DataSource
+{
+ public String getIdentityToken();
+ public String getDataSourceName();
+ public void setDataSourceName(String dataSourceName);
+
+ /** @deprecated use getNumConnectionsDefaultUser() */
+ public int getNumConnections() throws SQLException;
+
+ /** @deprecated use getNumIdleConnectionsDefaultUser() */
+ public int getNumIdleConnections() throws SQLException;
+
+ /** @deprecated use getNumBusyConnectionsDefaultUser() */
+ public int getNumBusyConnections() throws SQLException;
+
+ /** @deprecated use getNumUnclosedOrphanedConnectionsDefaultUser() */
+ public int getNumUnclosedOrphanedConnections() throws SQLException;
+
+ public int getNumConnectionsDefaultUser() throws SQLException;
+ public int getNumIdleConnectionsDefaultUser() throws SQLException;
+ public int getNumBusyConnectionsDefaultUser() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException;
+ public int getStatementCacheNumStatementsDefaultUser() throws SQLException;
+ public int getStatementCacheNumCheckedOutDefaultUser() throws SQLException;
+ public int getStatementCacheNumConnectionsWithCachedStatementsDefaultUser() throws SQLException;
+ public long getStartTimeMillisDefaultUser() throws SQLException;
+ public long getUpTimeMillisDefaultUser() throws SQLException;
+ public long getNumFailedCheckinsDefaultUser() throws SQLException;
+ public long getNumFailedCheckoutsDefaultUser() throws SQLException;
+ public long getNumFailedIdleTestsDefaultUser() throws SQLException;
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException;
+ public int getNumThreadsAwaitingCheckoutDefaultUser() throws SQLException;
+
+ /**
+ * Discards all Connections managed by the PooledDataSource's default-authentication pool
+ * and reacquires new Connections to populate.
+ * Current checked out Connections will still
+ * be valid, and should still be checked into the
+ * PooledDataSource (so the PooledDataSource can destroy
+ * them).
+ */
+ public void softResetDefaultUser() throws SQLException;
+
+ public int getNumConnections(String username, String password) throws SQLException;
+ public int getNumIdleConnections(String username, String password) throws SQLException;
+ public int getNumBusyConnections(String username, String password) throws SQLException;
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException;
+ public int getStatementCacheNumStatements(String username, String password) throws SQLException;
+ public int getStatementCacheNumCheckedOut(String username, String password) throws SQLException;
+ public int getStatementCacheNumConnectionsWithCachedStatements(String username, String password) throws SQLException;
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException;
+ public int getNumThreadsAwaitingCheckout(String username, String password) throws SQLException;
+
+ /**
+ * Discards all Connections managed by the PooledDataSource with the specified authentication credentials
+ * and reacquires new Connections to populate.
+ * Current checked out Connections will still
+ * be valid, and should still be checked into the
+ * PooledDataSource (so the PooledDataSource can destroy
+ * them).
+ */
+ public void softReset(String username, String password) throws SQLException;
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException;
+ public int getNumIdleConnectionsAllUsers() throws SQLException;
+ public int getNumConnectionsAllUsers() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException;
+
+ public int getStatementCacheNumStatementsAllUsers() throws SQLException;
+ public int getStatementCacheNumCheckedOutStatementsAllUsers() throws SQLException;
+ public int getStatementCacheNumConnectionsWithCachedStatementsAllUsers() throws SQLException;
+
+ public int getThreadPoolSize() throws SQLException;
+ public int getThreadPoolNumActiveThreads() throws SQLException;
+ public int getThreadPoolNumIdleThreads() throws SQLException;
+ public int getThreadPoolNumTasksPending() throws SQLException;
+
+ public String sampleThreadPoolStackTraces() throws SQLException;
+ public String sampleThreadPoolStatus() throws SQLException;
+
+ public String sampleStatementCacheStatusDefaultUser() throws SQLException;
+ public String sampleStatementCacheStatus(String username, String password) throws SQLException;
+
+ public Throwable getLastAcquisitionFailureDefaultUser() throws SQLException;
+ public Throwable getLastCheckinFailureDefaultUser() throws SQLException;
+ public Throwable getLastCheckoutFailureDefaultUser() throws SQLException;
+ public Throwable getLastIdleTestFailureDefaultUser() throws SQLException;
+ public Throwable getLastConnectionTestFailureDefaultUser() throws SQLException;
+
+ public Throwable getLastAcquisitionFailure(String username, String password) throws SQLException;
+ public Throwable getLastCheckinFailure(String username, String password) throws SQLException;
+ public Throwable getLastCheckoutFailure(String username, String password) throws SQLException;
+ public Throwable getLastIdleTestFailure(String username, String password) throws SQLException;
+ public Throwable getLastConnectionTestFailure(String username, String password) throws SQLException;
+
+ public String sampleLastAcquisitionFailureStackTraceDefaultUser() throws SQLException;
+ public String sampleLastCheckinFailureStackTraceDefaultUser() throws SQLException;
+ public String sampleLastCheckoutFailureStackTraceDefaultUser() throws SQLException;
+ public String sampleLastIdleTestFailureStackTraceDefaultUser() throws SQLException;
+ public String sampleLastConnectionTestFailureStackTraceDefaultUser() throws SQLException;
+
+ public String sampleLastAcquisitionFailureStackTrace(String username, String password) throws SQLException;
+ public String sampleLastCheckinFailureStackTrace(String username, String password) throws SQLException;
+ public String sampleLastCheckoutFailureStackTrace(String username, String password) throws SQLException;
+ public String sampleLastIdleTestFailureStackTrace(String username, String password) throws SQLException;
+ public String sampleLastConnectionTestFailureStackTrace(String username, String password) throws SQLException;
+
+ /**
+ * Discards all Connections managed by the PooledDataSource
+ * and reacquires new Connections to populate.
+ * Current checked out Connections will still
+ * be valid, and should still be checked into the
+ * PooledDataSource (so the PooledDataSource can destroy
+ * them).
+ */
+ public void softResetAllUsers() throws SQLException;
+
+ public int getNumUserPools() throws SQLException;
+ public int getNumHelperThreads() throws SQLException;
+
+ public Collection getAllUsers() throws SQLException;
+
+ /**
+ * Destroys all pooled and checked-out Connections associated with
+ * this DataSource immediately. The PooledDataSource is
+ * reset to its initial state prior to first Connection acquisition,
+ * with no pools yet active, but ready for requests.
+ */
+ public void hardReset() throws SQLException;
+
+ /**
+ * <p>C3P0 pooled DataSources use no resources before they are actually used in a VM,
+ * and they close themselves in their finalize() method. When they are active and
+ * pooling, they may have open database connections and their pool may spawn several threads
+ * for its maintenance. You can use this method to clean these resource methods up quickly
+ * when you will no longer be using this DataSource. The resources will actually be cleaned up only if
+ * no other DataSources are sharing the same pool.</p>
+ *
+ * <p>You can equivalently use the static method destroy() in the DataSources class to clean-up
+ * these resources.</p>
+ *
+ * <p>This is equivalent to calling close( false ).</p>
+ *
+ * @see DataSources#destroy
+ */
+ public void close() throws SQLException;
+
+ /**
+ * <p>Should be used only with great caution. If <tt>force_destroy</tt> is set to true,
+ * this immediately destroys any pool and cleans up all resources
+ * this DataSource may be using, <u><i>even if other DataSources are sharing that
+ * pool!</i></u> In general, it is difficult to know whether a pool is being shared by
+ * multiple DataSources. It may depend upon whether or not a JNDI implementation returns
+ * a single instance or multiple copies upon lookup (which is undefined by the JNDI spec).</p>
+ *
+ * <p>In general, this method should be used only when you wish to wind down all c3p0 pools
+ * in a ClassLoader. For example, when shutting down and restarting a web application
+ * that uses c3p0, you may wish to kill all threads making use of classes loaded by a
+ * web-app specific ClassLoader, so that the ClassLoader can be cleanly garbage collected.
+ * In this case, you may wish to use force destroy. Otherwise, it is much safer to use
+ * the simple destroy() method, which will not shut down pools that may still be in use.</p>
+ *
+ * <p><b>To close a pool normally, use the no argument close method, or set <tt>force_destroy</tt>
+ * to false.</b></p>
+ *
+ * @deprecated the force_destroy argument is now meaningless, as pools are no longer
+ * potentially shared between multiple DataSources.
+ *
+ * @see #close()
+ */
+ public void close(boolean force_destory) throws SQLException;
+}
diff --git a/src/classes/com/mchange/v2/c3p0/QueryConnectionTester.java b/src/classes/com/mchange/v2/c3p0/QueryConnectionTester.java
new file mode 100644
index 0000000..293307d
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/QueryConnectionTester.java
@@ -0,0 +1,32 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+
+public interface QueryConnectionTester extends ConnectionTester
+{
+ public int activeCheckConnection(Connection c, String preferredTestQuery);
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/SQLWarnings.java b/src/classes/com/mchange/v2/c3p0/SQLWarnings.java
new file mode 100644
index 0000000..ad1a3c0
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/SQLWarnings.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+
+public final class SQLWarnings
+{
+ final static MLogger logger = MLog.getLogger( SQLWarnings.class );
+
+ public static void logAndClearWarnings(Connection con) throws SQLException
+ {
+ if (logger.isLoggable(MLevel.INFO))
+ {
+ for(SQLWarning w = con.getWarnings(); w != null; w = w.getNextWarning())
+ logger.log(MLevel.INFO, w.getMessage(), w);
+ }
+ con.clearWarnings();
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/c3p0/UnifiedConnectionTester.java b/src/classes/com/mchange/v2/c3p0/UnifiedConnectionTester.java
new file mode 100644
index 0000000..6b4bc9e
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/UnifiedConnectionTester.java
@@ -0,0 +1,69 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.sql.Connection;
+
+/**
+ * <p>Having expanded the once-simple ConnectionTester interface to support both
+ * user-specified queries and return of root cause Exceptions (via an out-param),
+ * this interface has grown unnecessarily complex.</p>
+ *
+ * <p>If you wish to implement a custom Connection tester, here is the simple
+ * way to do it</p>
+ *
+ * <ol>
+ * <li>Extend {@link com.mchange.v2.c3p0.AbstractConnectionTester}</li>
+ * <li>Override only the two abstract methods</li>
+ * <ul>
+ * <li><tt>public int activeCheckConnection(Connection c, String preferredTestQuery, Throwable[] rootCauseOutParamHolder)</tt></li>
+ * <li><tt>public int statusOnException(Connection c, Throwable t, String preferredTestQuery, Throwable[] rootCauseOutParamHolder)</tt></li>
+ * </ul>
+ * <li>Take care to ensure that your methods are defined to allow <tt>preferredTestQuery</tt> and
+ * <tt>rootCauseOutParamHolder</tt> to be <tt>null</tt>.</li>
+ * </ol>
+ *
+ * <p>Parameter <tt>rootCauseOutParamHolder</tt> is an optional parameter, which if supplied, will be a Throwable array whose size
+ * it at least one. If a Connection test fails because of some Exception, the Connection tester may set this Exception as the
+ * zero-th element of the array to provide information about why and how the test failed.</p>
+ */
+public interface UnifiedConnectionTester extends FullQueryConnectionTester
+{
+ public final static int CONNECTION_IS_OKAY = ConnectionTester.CONNECTION_IS_OKAY;
+ public final static int CONNECTION_IS_INVALID = ConnectionTester.CONNECTION_IS_INVALID;
+ public final static int DATABASE_IS_INVALID = ConnectionTester.DATABASE_IS_INVALID;
+
+ public int activeCheckConnection(Connection c);
+ public int activeCheckConnection(Connection c, Throwable[] rootCauseOutParamHolder);
+ public int activeCheckConnection(Connection c, String preferredTestQuery);
+ public int activeCheckConnection(Connection c, String preferredTestQuery, Throwable[] rootCauseOutParamHolder);
+
+ public int statusOnException(Connection c, Throwable t);
+ public int statusOnException(Connection c, Throwable t, Throwable[] rootCauseOutParamHolder);
+ public int statusOnException(Connection c, Throwable t, String preferredTestQuery);
+ public int statusOnException(Connection c, Throwable t, String preferredTestQuery, Throwable[] rootCauseOutParamHolder);
+
+ public boolean equals(Object o);
+ public int hashCode();
+}
diff --git a/src/classes/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.java b/src/classes/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.java
new file mode 100644
index 0000000..a24a8a4
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.java
@@ -0,0 +1,286 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+import java.beans.PropertyChangeListener;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.cfg.C3P0Config;
+import com.mchange.v2.c3p0.impl.*;
+import com.mchange.v2.log.*;
+
+// MT: Most methods are left unsynchronized, because getNestedDataSource() is synchronized, and for most methods, that's
+// the only critical part. Previous oversynchronization led to hangs, when getting the Connection for one Thread happened
+// to hang, blocking access to getPooledConnection() for all Threads.
+public final class WrapperConnectionPoolDataSource extends WrapperConnectionPoolDataSourceBase implements ConnectionPoolDataSource
+{
+ final static MLogger logger = MLog.getLogger( WrapperConnectionPoolDataSource.class );
+
+ //MT: protected by this' lock
+ ConnectionTester connectionTester = C3P0ImplUtils.defaultConnectionTester();
+ Map userOverrides;
+
+ public WrapperConnectionPoolDataSource(boolean autoregister)
+ {
+ super( autoregister );
+
+ setUpPropertyListeners();
+
+ //set up initial value of userOverrides
+ try
+ { this.userOverrides = C3P0ImplUtils.parseUserOverridesAsString( this.getUserOverridesAsString() ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Failed to parse stringified userOverrides. " + this.getUserOverridesAsString(), e );
+ }
+ }
+
+ public WrapperConnectionPoolDataSource()
+ { this( true ); }
+
+ private void setUpPropertyListeners()
+ {
+ VetoableChangeListener setConnectionTesterListener = new VetoableChangeListener()
+ {
+ // always called within synchronized mutators of the parent class... needn't explicitly sync here
+ public void vetoableChange( PropertyChangeEvent evt ) throws PropertyVetoException
+ {
+ String propName = evt.getPropertyName();
+ Object val = evt.getNewValue();
+
+ if ( "connectionTesterClassName".equals( propName ) )
+ {
+ try
+ { recreateConnectionTester( (String) val ); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Failed to create ConnectionTester of class " + val, e );
+
+ throw new PropertyVetoException("Could not instantiate connection tester class with name '" + val + "'.", evt);
+ }
+ }
+ else if ("userOverridesAsString".equals( propName ))
+ {
+ try
+ { WrapperConnectionPoolDataSource.this.userOverrides = C3P0ImplUtils.parseUserOverridesAsString( (String) val ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Failed to parse stringified userOverrides. " + val, e );
+
+ throw new PropertyVetoException("Failed to parse stringified userOverrides. " + val, evt);
+ }
+ }
+ }
+ };
+ this.addVetoableChangeListener( setConnectionTesterListener );
+ }
+
+ public WrapperConnectionPoolDataSource( String configName )
+ {
+ this();
+
+ try
+ {
+ if (configName != null)
+ C3P0Config.bindNamedConfigToBean( this, configName );
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "Error binding WrapperConnectionPoolDataSource to named-config '" + configName +
+ "'. Some default-config values may be used.",
+ e);
+ }
+ }
+
+ // implementation of javax.sql.ConnectionPoolDataSource
+
+ public PooledConnection getPooledConnection()
+ throws SQLException
+ { return this.getPooledConnection( (ConnectionCustomizer) null, null ); }
+
+ // getNestedDataSource() is sync'ed, which is enough. Unsync'ed this method,
+ // because when sync'ed a hang in retrieving one connection blocks all
+ //
+ protected PooledConnection getPooledConnection( ConnectionCustomizer cc, String pdsIdt )
+ throws SQLException
+ {
+ DataSource nds = getNestedDataSource();
+ if (nds == null)
+ throw new SQLException( "No standard DataSource has been set beneath this wrapper! [ nestedDataSource == null ]");
+ Connection conn = nds.getConnection();
+ if (conn == null)
+ throw new SQLException("An (unpooled) DataSource returned null from its getConnection() method! " +
+ "DataSource: " + getNestedDataSource());
+ if ( this.isUsesTraditionalReflectiveProxies() )
+ {
+ //return new C3P0PooledConnection( new com.mchange.v2.c3p0.test.CloseReportingConnection( conn ),
+ return new C3P0PooledConnection( conn,
+ connectionTester,
+ this.isAutoCommitOnClose(),
+ this.isForceIgnoreUnresolvedTransactions(),
+ cc,
+ pdsIdt);
+ }
+ else
+ {
+ return new NewPooledConnection( conn,
+ connectionTester,
+ this.isAutoCommitOnClose(),
+ this.isForceIgnoreUnresolvedTransactions(),
+ this.getPreferredTestQuery(),
+ cc,
+ pdsIdt);
+ }
+ }
+
+ public PooledConnection getPooledConnection(String user, String password)
+ throws SQLException
+ { return this.getPooledConnection( user, password, null, null ); }
+
+ // getNestedDataSource() is sync'ed, which is enough. Unsync'ed this method,
+ // because when sync'ed a hang in retrieving one connection blocks all
+ //
+ protected PooledConnection getPooledConnection(String user, String password, ConnectionCustomizer cc, String pdsIdt)
+ throws SQLException
+ {
+ DataSource nds = getNestedDataSource();
+ if (nds == null)
+ throw new SQLException( "No standard DataSource has been set beneath this wrapper! [ nestedDataSource == null ]");
+ Connection conn = nds.getConnection(user, password);
+ if (conn == null)
+ throw new SQLException("An (unpooled) DataSource returned null from its getConnection() method! " +
+ "DataSource: " + getNestedDataSource());
+ if ( this.isUsesTraditionalReflectiveProxies() )
+ {
+ //return new C3P0PooledConnection( new com.mchange.v2.c3p0.test.CloseReportingConnection( conn ),
+ return new C3P0PooledConnection( conn,
+ connectionTester,
+ this.isAutoCommitOnClose(),
+ this.isForceIgnoreUnresolvedTransactions(),
+ cc,
+ pdsIdt);
+ }
+ else
+ {
+ return new NewPooledConnection( conn,
+ connectionTester,
+ this.isAutoCommitOnClose(),
+ this.isForceIgnoreUnresolvedTransactions(),
+ this.getPreferredTestQuery(),
+ cc,
+ pdsIdt);
+ }
+ }
+
+ public PrintWriter getLogWriter()
+ throws SQLException
+ { return getNestedDataSource().getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out)
+ throws SQLException
+ { getNestedDataSource().setLogWriter( out ); }
+
+ public void setLoginTimeout(int seconds)
+ throws SQLException
+ { getNestedDataSource().setLoginTimeout( seconds ); }
+
+ public int getLoginTimeout()
+ throws SQLException
+ { return getNestedDataSource().getLoginTimeout(); }
+
+ //"virtual properties"
+
+ public String getUser()
+ {
+ try { return C3P0ImplUtils.findAuth( this.getNestedDataSource() ).getUser(); }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "An Exception occurred while trying to find the 'user' property from our nested DataSource." +
+ " Defaulting to no specified username.", e );
+ return null;
+ }
+ }
+
+ public String getPassword()
+ {
+ try { return C3P0ImplUtils.findAuth( this.getNestedDataSource() ).getPassword(); }
+ catch (SQLException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "An Exception occurred while trying to find the 'password' property from our nested DataSource." +
+ " Defaulting to no specified password.", e );
+ return null;
+ }
+ }
+
+ public Map getUserOverrides()
+ { return userOverrides; }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append( super.toString() );
+
+// if (userOverrides != null)
+// sb.append("; userOverrides: " + userOverrides.toString());
+
+ return sb.toString();
+ }
+
+ protected String extraToStringInfo()
+ {
+ if (userOverrides != null)
+ return "; userOverrides: " + userOverrides.toString();
+ else
+ return null;
+ }
+
+ //other code
+ private synchronized void recreateConnectionTester(String className) throws Exception
+ {
+ if (className != null)
+ {
+ ConnectionTester ct = (ConnectionTester) Class.forName( className ).newInstance();
+ this.connectionTester = ct;
+ }
+ else
+ this.connectionTester = C3P0ImplUtils.defaultConnectionTester();
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/C3P0Config.java b/src/classes/com/mchange/v2/c3p0/cfg/C3P0Config.java
new file mode 100644
index 0000000..9144023
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/C3P0Config.java
@@ -0,0 +1,329 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.beans.*;
+import java.util.*;
+import com.mchange.v2.c3p0.impl.*;
+import com.mchange.v2.beans.*;
+import com.mchange.v2.cfg.*;
+import com.mchange.v2.log.*;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import com.mchange.v1.lang.BooleanUtils;
+
+//all internal maps should be HashMaps (the implementation presumes HashMaps)
+
+public final class C3P0Config
+{
+ public final static String CFG_FINDER_CLASSNAME_KEY = "com.mchange.v2.c3p0.cfg.finder";
+
+ public final static String DEFAULT_CONFIG_NAME = "default";
+
+ public final static C3P0Config MAIN;
+
+ final static MLogger logger = MLog.getLogger( C3P0Config.class );
+
+ static
+ {
+// Set knownProps = new HashSet();
+// knownProps.add("acquireIncrement");
+// knownProps.add("acquireRetryAttempts");
+// knownProps.add("acquireRetryDelay");
+// knownProps.add("autoCommitOnClose");
+// knownProps.add("automaticTestTable");
+// knownProps.add("breakAfterAcqireFailure");
+// knownProps.add("checkoutTimeout");
+// knownProps.add("connectionTesterClassName");
+// knownProps.add("factoryClassLocation");
+// knownProps.add("forceIgnoreUnresolvedTransactions");
+// knownProps.add("idleConnectionTestPeriod");
+// knownProps.add("initialPoolSize");
+// knownProps.add("maxIdleTime");
+// knownProps.add("maxPoolSize");
+
+ C3P0Config protoMain;
+
+ String cname = MultiPropertiesConfig.readVmConfig().getProperty( CFG_FINDER_CLASSNAME_KEY );
+
+ C3P0ConfigFinder cfgFinder = null;
+ try
+ {
+ if (cname != null)
+ cfgFinder = (C3P0ConfigFinder) Class.forName( cname ).newInstance();
+
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable(MLevel.WARNING) )
+ logger.log( MLevel.WARNING, "Could not load specified C3P0ConfigFinder class'" + cname + "'.", e);
+ }
+
+ try
+ {
+ if (cfgFinder == null)
+ {
+ Class.forName("org.w3c.dom.Node");
+ Class.forName("com.mchange.v2.c3p0.cfg.C3P0ConfigXmlUtils"); //fail nicely if we don't have XML libs
+ cfgFinder = new DefaultC3P0ConfigFinder();
+ }
+ protoMain = cfgFinder.findConfig();
+ }
+ catch (Exception e)
+ {
+
+ if ( logger.isLoggable(MLevel.WARNING) )
+ logger.log( MLevel.WARNING, "XML configuration disabled! Verify that standard XML libs are available.", e);
+
+ HashMap flatDefaults = C3P0ConfigUtils.extractHardcodedC3P0Defaults();
+ flatDefaults.putAll( C3P0ConfigUtils.extractC3P0PropertiesResources() );
+ protoMain = C3P0ConfigUtils.configFromFlatDefaults( flatDefaults );
+ }
+ MAIN = protoMain;
+
+ warnOnUnknownProperties( MAIN );
+ }
+
+ private static void warnOnUnknownProperties( C3P0Config cfg )
+ {
+ warnOnUnknownProperties( cfg.defaultConfig );
+ for (Iterator ii = cfg.configNamesToNamedScopes.values().iterator(); ii.hasNext(); )
+ warnOnUnknownProperties( (NamedScope) ii.next() );
+ }
+
+ private static void warnOnUnknownProperties( NamedScope scope )
+ {
+ warnOnUnknownProperties( scope.props );
+ for (Iterator ii = scope.userNamesToOverrides.values().iterator(); ii.hasNext(); )
+ warnOnUnknownProperties( (Map) ii.next() );
+ }
+
+ private static void warnOnUnknownProperties( Map propMap )
+ {
+ for (Iterator ii = propMap.keySet().iterator(); ii.hasNext(); )
+ {
+ String prop = (String) ii.next();
+ if (! C3P0Defaults.isKnownProperty( prop ) && logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING, "Unknown c3p0-config property: " + prop);
+ }
+ }
+
+ public static String getUnspecifiedUserProperty( String propKey, String configName )
+ {
+ String out = null;
+
+ if (configName == null)
+ out = (String) MAIN.defaultConfig.props.get( propKey );
+ else
+ {
+ NamedScope named = (NamedScope) MAIN.configNamesToNamedScopes.get( configName );
+ if (named != null)
+ out = (String) named.props.get(propKey);
+ else
+ logger.warning("named-config with name '" + configName + "' does not exist. Using default-config for property '" + propKey + "'.");
+
+ if (out == null)
+ out = (String) MAIN.defaultConfig.props.get( propKey );
+ }
+
+ return out;
+ }
+
+ public static Map getUnspecifiedUserProperties(String configName)
+ {
+ Map out = new HashMap();
+
+ out.putAll( MAIN.defaultConfig.props );
+
+ if (configName != null)
+ {
+ NamedScope named = (NamedScope) MAIN.configNamesToNamedScopes.get( configName );
+ if (named != null)
+ out.putAll( named.props );
+ else
+ logger.warning("named-config with name '" + configName + "' does not exist. Using default-config.");
+ }
+
+ return out;
+ }
+
+ public static Map getUserOverrides( String configName )
+ {
+ Map out = new HashMap();
+
+ NamedScope namedConfigScope = null;
+
+ if (configName != null)
+ namedConfigScope = (NamedScope) MAIN.configNamesToNamedScopes.get( configName );
+
+ out.putAll( MAIN.defaultConfig.userNamesToOverrides );
+
+ if (namedConfigScope != null)
+ out.putAll( namedConfigScope.userNamesToOverrides );
+
+ return (out.isEmpty() ? null : out );
+ }
+
+ public static String getUserOverridesAsString(String configName) throws IOException
+ {
+ Map userOverrides = getUserOverrides( configName );
+ if (userOverrides == null)
+ return null;
+ else
+ return C3P0ImplUtils.createUserOverridesAsString( userOverrides ).intern();
+ }
+
+ final static Class[] SUOAS_ARGS = new Class[] { String.class };
+
+ final static Collection SKIP_BIND_PROPS = Arrays.asList( new String[] {"loginTimeout", "properties"} );
+
+ public static void bindNamedConfigToBean(Object bean, String configName) throws IntrospectionException
+ {
+ Map defaultUserProps = C3P0Config.getUnspecifiedUserProperties( configName );
+ BeansUtils.overwriteAccessiblePropertiesFromMap( defaultUserProps,
+ bean,
+ false,
+ SKIP_BIND_PROPS,
+ true,
+ MLevel.FINEST,
+ MLevel.WARNING,
+ false);
+ try
+ {
+ Method m = bean.getClass().getMethod( "setUserOverridesAsString", SUOAS_ARGS );
+ m.invoke( bean, new Object[] {getUserOverridesAsString( configName )} );
+ }
+ catch (NoSuchMethodException e)
+ {
+ e.printStackTrace();
+ /* ignore */
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "An exception occurred while trying to bind user overrides " +
+ "for named config '" + configName + "'. Only default user configs " +
+ "will be used."
+ , e);
+ }
+ }
+
+ /*
+ * Note that on initialization of a DataSource, no config name is known.
+ * We initialize local vars using the default config. The DataSources class
+ * and/or constructors that accept a configName then overwrite the initial
+ * values with namedConfig overrides if supplied.
+ */
+ public static String initializeUserOverridesAsString()
+ {
+ try
+ { return getUserOverridesAsString( null ); }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING, "Error initializing default user overrides. User overrides may be ignored.", e);
+ return null;
+ }
+ }
+
+ public static String initializeStringPropertyVar(String propKey, String dflt)
+ {
+ String out = getUnspecifiedUserProperty( propKey, null );
+ if (out == null) out = dflt;
+ return out;
+ }
+
+ public static int initializeIntPropertyVar(String propKey, int dflt)
+ {
+ boolean set = false;
+ int out = -1;
+
+ String outStr = getUnspecifiedUserProperty( propKey, null );
+ if (outStr != null)
+ {
+ try
+ {
+ out = Integer.parseInt( outStr.trim() );
+ set = true;
+ }
+ catch (NumberFormatException e)
+ {
+ logger.info("'" + outStr + "' is not a legal value for property '" + propKey +
+ "'. Using default value: " + dflt);
+ }
+ }
+
+ if (!set)
+ out = dflt;
+
+ //System.err.println("initializing " + propKey + " to " + out);
+ return out;
+ }
+
+ public static boolean initializeBooleanPropertyVar(String propKey, boolean dflt)
+ {
+ boolean set = false;
+ boolean out = false;
+
+ String outStr = getUnspecifiedUserProperty( propKey, null );
+ if (outStr != null)
+ {
+ try
+ {
+ out = BooleanUtils.parseBoolean( outStr.trim() );
+ set = true;
+ }
+ catch (IllegalArgumentException e)
+ {
+ logger.info("'" + outStr + "' is not a legal value for property '" + propKey +
+ "'. Using default value: " + dflt);
+ }
+ }
+
+ if (!set)
+ out = dflt;
+
+ return out;
+ }
+
+
+
+ NamedScope defaultConfig;
+ HashMap configNamesToNamedScopes;
+
+ C3P0Config( NamedScope defaultConfig, HashMap configNamesToNamedScopes)
+ {
+ this.defaultConfig = defaultConfig;
+ this.configNamesToNamedScopes = configNamesToNamedScopes;
+ }
+
+// C3P0Config()
+// {
+// this.defaultConfig = new NamedScope();
+// this.configNamesToNamedScopes = new HashMap();
+// }
+
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigFinder.java b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigFinder.java
new file mode 100644
index 0000000..3e6efa1
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigFinder.java
@@ -0,0 +1,31 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.sql.SQLException;
+
+public interface C3P0ConfigFinder
+{
+ public C3P0Config findConfig() throws Exception;
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigUtils.java b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigUtils.java
new file mode 100644
index 0000000..8a856a7
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigUtils.java
@@ -0,0 +1,160 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import com.mchange.v2.cfg.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.c3p0.impl.*;
+
+public final class C3P0ConfigUtils
+{
+ public final static String PROPS_FILE_RSRC_PATH = "/c3p0.properties";
+ public final static String PROPS_FILE_PROP_PFX = "c3p0.";
+ public final static int PROPS_FILE_PROP_PFX_LEN = 5;
+
+ private final static String[] MISSPELL_PFXS = {"/c3pO", "/c3po", "/C3P0", "/C3PO"};
+
+ final static MLogger logger = MLog.getLogger( C3P0ConfigUtils.class );
+
+ static
+ {
+ if ( logger.isLoggable(MLevel.WARNING) && C3P0ConfigUtils.class.getResource( PROPS_FILE_RSRC_PATH ) == null )
+ {
+ // warn on a misspelling... its an ugly way to do this, but since resources are not listable...
+ for (int i = 0; i < MISSPELL_PFXS.length; ++i)
+ {
+ String test = MISSPELL_PFXS[i] + ".properties";
+ if (C3P0ConfigUtils.class.getResource( MISSPELL_PFXS[i] + ".properties" ) != null)
+ {
+ logger.warning("POSSIBLY MISSPELLED c3p0.properties CONFIG RESOURCE FOUND. " +
+ "Please ensure the file name is c3p0.properties, all lower case, " +
+ "with the digit 0 (NOT the letter O) in c3p0. It should be placed " +
+ " in the top level of c3p0's effective classpath.");
+ break;
+ }
+ }
+ }
+ }
+
+ public static HashMap extractHardcodedC3P0Defaults(boolean stringify)
+ {
+ HashMap out = new HashMap();
+
+ try
+ {
+ Method[] methods = C3P0Defaults.class.getMethods();
+ for (int i = 0, len = methods.length; i < len; ++i)
+ {
+ Method m = methods[i];
+ int mods = m.getModifiers();
+ if ((mods & Modifier.PUBLIC) != 0 && (mods & Modifier.STATIC) != 0 && m.getParameterTypes().length == 0)
+ {
+ if (stringify)
+ {
+ Object val = m.invoke( null, null );
+ if ( val != null )
+ out.put( m.getName(), String.valueOf( val ) );
+ }
+ else
+ out.put( m.getName(), m.invoke( null, null ) );
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ logger.log( MLevel.WARNING, "Failed to extract hardcoded default config!?", e );
+ }
+
+ return out;
+ }
+
+ public static HashMap extractHardcodedC3P0Defaults()
+ { return extractHardcodedC3P0Defaults( true ); }
+
+ public static HashMap extractC3P0PropertiesResources()
+ {
+ HashMap out = new HashMap();
+
+// Properties props = findResourceProperties();
+// props.putAll( findAllC3P0Properties() );
+
+ Properties props = findAllC3P0Properties();
+ for (Iterator ii = props.keySet().iterator(); ii.hasNext(); )
+ {
+ String key = (String) ii.next();
+ String val = (String) props.get(key);
+ if ( key.startsWith(PROPS_FILE_PROP_PFX) )
+ out.put( key.substring(PROPS_FILE_PROP_PFX_LEN).trim(), val.trim() );
+ }
+
+ return out;
+ }
+
+ public static C3P0Config configFromFlatDefaults(HashMap flatDefaults)
+ {
+ NamedScope defaults = new NamedScope();
+ defaults.props.putAll( flatDefaults );
+
+ HashMap configNamesToNamedScopes = new HashMap();
+
+ return new C3P0Config( defaults, configNamesToNamedScopes );
+ }
+
+ public static String getPropFileConfigProperty( String prop )
+ { return MultiPropertiesConfig.readVmConfig().getProperty( prop ); }
+
+ private static Properties findResourceProperties()
+ { return MultiPropertiesConfig.readVmConfig().getPropertiesByResourcePath(PROPS_FILE_RSRC_PATH); }
+
+ private static Properties findAllC3P0Properties()
+ { return MultiPropertiesConfig.readVmConfig().getPropertiesByPrefix("c3p0"); }
+
+ static Properties findAllC3P0SystemProperties()
+ {
+ Properties out = new Properties();
+
+ SecurityException sampleExc = null;
+ try
+ {
+ for (Iterator ii = C3P0Defaults.getKnownProperties().iterator(); ii.hasNext(); )
+ {
+ String key = (String) ii.next();
+ String prefixedKey = "c3p0." + key;
+ String value = System.getProperty( prefixedKey );
+ if (value != null && value.trim().length() > 0)
+ out.put( key, value );
+ }
+ }
+ catch (SecurityException e)
+ { sampleExc = e; }
+
+ return out;
+ }
+
+ private C3P0ConfigUtils()
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigXmlUtils.java b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigXmlUtils.java
new file mode 100644
index 0000000..3878e89
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/C3P0ConfigXmlUtils.java
@@ -0,0 +1,232 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.io.*;
+import java.util.*;
+import javax.xml.parsers.*;
+import org.w3c.dom.*;
+import com.mchange.v2.log.*;
+
+import com.mchange.v1.xml.DomParseUtils;
+
+public final class C3P0ConfigXmlUtils
+{
+ public final static String XML_CONFIG_RSRC_PATH = "/c3p0-config.xml";
+
+ final static MLogger logger = MLog.getLogger( C3P0ConfigXmlUtils.class );
+
+ public final static String LINESEP;
+
+ private final static String[] MISSPELL_PFXS = {"/c3p0", "/c3pO", "/c3po", "/C3P0", "/C3PO"};
+ private final static char[] MISSPELL_LINES = {'-', '_'};
+ private final static String[] MISSPELL_CONFIG = {"config", "CONFIG"};
+ private final static String[] MISSPELL_XML = {"xml", "XML"};
+
+ // its an ugly way to do this, but since resources are not listable...
+ //
+ // this is only executed once, and does about 40 tests (for now)
+ // should I care about the cost in initialization time?
+ //
+ // should only be run if we've checked for the correct file, but
+ // not found it
+ private final static void warnCommonXmlConfigResourceMisspellings()
+ {
+ if (logger.isLoggable( MLevel.WARNING) )
+ {
+ for (int a = 0, lena = MISSPELL_PFXS.length; a < lena; ++a)
+ {
+ StringBuffer sb = new StringBuffer(16);
+ sb.append( MISSPELL_PFXS[a] );
+ for (int b = 0, lenb = MISSPELL_LINES.length; b < lenb; ++b)
+ {
+ sb.append(MISSPELL_LINES[b]);
+ for (int c = 0, lenc = MISSPELL_CONFIG.length; c < lenc; ++c)
+ {
+ sb.append(MISSPELL_CONFIG[c]);
+ sb.append('.');
+ for (int d = 0, lend = MISSPELL_XML.length; d < lend; ++d)
+ {
+ sb.append(MISSPELL_XML[d]);
+ String test = sb.toString();
+ if (!test.equals(XML_CONFIG_RSRC_PATH))
+ {
+ Object hopefullyNull = C3P0ConfigXmlUtils.class.getResource( test );
+ if (hopefullyNull != null)
+ {
+ logger.warning("POSSIBLY MISSPELLED c3p0-conf.xml RESOURCE FOUND. " +
+ "Please ensure the file name is c3p0-config.xml, all lower case, " +
+ "with the digit 0 (NOT the letter O) in c3p0. It should be placed " +
+ " in the top level of c3p0's effective classpath.");
+ return;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ static
+ {
+ String ls;
+
+ try
+ { ls = System.getProperty("line.separator", "\r\n"); }
+ catch (Exception e)
+ { ls = "\r\n"; }
+
+ LINESEP = ls;
+
+ }
+
+ public static C3P0Config extractXmlConfigFromDefaultResource() throws Exception
+ {
+ InputStream is = null;
+
+ try
+ {
+ is = C3P0ConfigUtils.class.getResourceAsStream(XML_CONFIG_RSRC_PATH);
+ if ( is == null )
+ {
+ warnCommonXmlConfigResourceMisspellings();
+ return null;
+ }
+ else
+ return extractXmlConfigFromInputStream( is );
+ }
+ finally
+ {
+ try { if (is != null) is.close(); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log(MLevel.FINE,"Exception on resource InputStream close.", e);
+ }
+ }
+ }
+
+ public static C3P0Config extractXmlConfigFromInputStream(InputStream is) throws Exception
+ {
+ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = fact.newDocumentBuilder();
+ Document doc = db.parse( is );
+
+ return extractConfigFromXmlDoc(doc);
+ }
+
+ public static C3P0Config extractConfigFromXmlDoc(Document doc) throws Exception
+ {
+ Element docElem = doc.getDocumentElement();
+ if (docElem.getTagName().equals("c3p0-config"))
+ {
+ NamedScope defaults;
+ HashMap configNamesToNamedScopes = new HashMap();
+
+ Element defaultConfigElem = DomParseUtils.uniqueChild( docElem, "default-config" );
+ if (defaultConfigElem != null)
+ defaults = extractNamedScopeFromLevel( defaultConfigElem );
+ else
+ defaults = new NamedScope();
+ NodeList nl = DomParseUtils.immediateChildElementsByTagName(docElem, "named-config");
+ for (int i = 0, len = nl.getLength(); i < len; ++i)
+ {
+ Element namedConfigElem = (Element) nl.item(i);
+ String configName = namedConfigElem.getAttribute("name");
+ if (configName != null && configName.length() > 0)
+ {
+ NamedScope namedConfig = extractNamedScopeFromLevel( namedConfigElem );
+ configNamesToNamedScopes.put( configName, namedConfig);
+ }
+ else
+ logger.warning("Configuration XML contained named-config element without name attribute: " + namedConfigElem);
+ }
+ return new C3P0Config( defaults, configNamesToNamedScopes );
+ }
+ else
+ throw new Exception("Root element of c3p0 config xml should be 'c3p0-config', not '" + docElem.getTagName() + "'.");
+ }
+
+ private static NamedScope extractNamedScopeFromLevel(Element elem)
+ {
+ HashMap props = extractPropertiesFromLevel( elem );
+ HashMap userNamesToOverrides = new HashMap();
+
+ NodeList nl = DomParseUtils.immediateChildElementsByTagName(elem, "user-overrides");
+ for (int i = 0, len = nl.getLength(); i < len; ++i)
+ {
+ Element perUserConfigElem = (Element) nl.item(i);
+ String userName = perUserConfigElem.getAttribute("user");
+ if (userName != null && userName.length() > 0)
+ {
+ HashMap userProps = extractPropertiesFromLevel( perUserConfigElem );
+ userNamesToOverrides.put( userName, userProps );
+ }
+ else
+ logger.warning("Configuration XML contained user-overrides element without user attribute: " + LINESEP + perUserConfigElem);
+ }
+
+ return new NamedScope(props, userNamesToOverrides);
+ }
+
+ private static HashMap extractPropertiesFromLevel(Element elem)
+ {
+ // System.err.println( "extractPropertiesFromLevel()" );
+
+ HashMap out = new HashMap();
+
+ try
+ {
+ NodeList nl = DomParseUtils.immediateChildElementsByTagName(elem, "property");
+ int len = nl.getLength();
+ for (int i = 0; i < len; ++i)
+ {
+ Element propertyElem = (Element) nl.item(i);
+ String propName = propertyElem.getAttribute("name");
+ if (propName != null && propName.length() > 0)
+ {
+ String propVal = DomParseUtils.allTextFromElement(propertyElem, true);
+ out.put( propName, propVal );
+ //System.err.println( propName + " -> " + propVal );
+ }
+ else
+ logger.warning("Configuration XML contained property element without name attribute: " + LINESEP + propertyElem);
+ }
+ }
+ catch (Exception e)
+ {
+ logger.log( MLevel.WARNING,
+ "An exception occurred while reading config XML. " +
+ "Some configuration information has probably been ignored.",
+ e );
+ }
+
+ return out;
+ }
+
+ private C3P0ConfigXmlUtils()
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/DefaultC3P0ConfigFinder.java b/src/classes/com/mchange/v2/c3p0/cfg/DefaultC3P0ConfigFinder.java
new file mode 100644
index 0000000..f12ef70
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/DefaultC3P0ConfigFinder.java
@@ -0,0 +1,88 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Properties;
+import com.mchange.v2.cfg.MultiPropertiesConfig;
+
+public class DefaultC3P0ConfigFinder implements C3P0ConfigFinder
+{
+ final static String XML_CFG_FILE_KEY = "com.mchange.v2.c3p0.cfg.xml";
+
+ public C3P0Config findConfig() throws Exception
+ {
+ C3P0Config out;
+
+ HashMap flatDefaults = C3P0ConfigUtils.extractHardcodedC3P0Defaults();
+
+ // this includes System properties, but we have to check for System properties
+ // again, since we want system properties to override unspecified user, default-config
+ // properties in the XML
+ flatDefaults.putAll( C3P0ConfigUtils.extractC3P0PropertiesResources() );
+
+ String cfgFile = MultiPropertiesConfig.readVmConfig().getProperty( XML_CFG_FILE_KEY );
+ if (cfgFile == null)
+ {
+ C3P0Config xmlConfig = C3P0ConfigXmlUtils.extractXmlConfigFromDefaultResource();
+ if (xmlConfig != null)
+ {
+ insertDefaultsUnderNascentConfig( flatDefaults, xmlConfig );
+ out = xmlConfig;
+ }
+ else
+ out = C3P0ConfigUtils.configFromFlatDefaults( flatDefaults );
+ }
+ else
+ {
+ InputStream is = new BufferedInputStream( new FileInputStream( cfgFile ) );
+ try
+ {
+ C3P0Config xmlConfig = C3P0ConfigXmlUtils.extractXmlConfigFromInputStream( is );
+ insertDefaultsUnderNascentConfig( flatDefaults, xmlConfig );
+ out = xmlConfig;
+ }
+ finally
+ {
+ try {is.close();}
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ // overwrite default, unspecified user config with System properties
+ // defined values
+ Properties sysPropConfig = C3P0ConfigUtils.findAllC3P0SystemProperties();
+ out.defaultConfig.props.putAll( sysPropConfig );
+
+ return out;
+ }
+
+ private void insertDefaultsUnderNascentConfig(HashMap flatDefaults, C3P0Config config)
+ {
+ flatDefaults.putAll( config.defaultConfig.props );
+ config.defaultConfig.props = flatDefaults;
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/cfg/NamedScope.java b/src/classes/com/mchange/v2/c3p0/cfg/NamedScope.java
new file mode 100644
index 0000000..1299a16
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/cfg/NamedScope.java
@@ -0,0 +1,46 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.cfg;
+
+import java.util.*;
+
+//all internal maps should be HashMaps (the implementation presumes HashMaps)
+
+class NamedScope
+{
+ HashMap props;
+ HashMap userNamesToOverrides;
+
+ NamedScope()
+ {
+ this.props = new HashMap();
+ this.userNamesToOverrides = new HashMap();
+ }
+
+ NamedScope( HashMap props, HashMap userNamesToOverrides)
+ {
+ this.props = props;
+ this.userNamesToOverrides = userNamesToOverrides;
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/codegen/BeangenDataSourceGenerator.java b/src/classes/com/mchange/v2/c3p0/codegen/BeangenDataSourceGenerator.java
new file mode 100644
index 0000000..4eff81f
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/codegen/BeangenDataSourceGenerator.java
@@ -0,0 +1,230 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.codegen;
+
+import java.io.*;
+import java.util.*;
+import javax.xml.parsers.*;
+import org.w3c.dom.*;
+import com.mchange.v2.codegen.*;
+import com.mchange.v2.codegen.bean.*;
+import com.mchange.v2.c3p0.impl.*;
+
+import java.lang.reflect.Modifier;
+import com.mchange.v1.xml.DomParseUtils;
+
+public class BeangenDataSourceGenerator
+{
+ public static void main( String[] argv )
+ {
+ try
+ {
+ if (argv.length != 2)
+ {
+ System.err.println("java " + BeangenDataSourceGenerator.class.getName() +
+ " <infile.xml> <OutputFile.java>");
+ return;
+ }
+
+
+ File outFile = new File( argv[1] );
+ File parentDir = outFile.getParentFile();
+ if (! parentDir.exists())
+ {
+ System.err.println("Warning: making parent directory: " + parentDir);
+ parentDir.mkdirs();
+ }
+
+ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = fact.newDocumentBuilder();
+ Document doc = db.parse( new File( argv[0] ) );
+ ParsedPropertyBeanDocument parsed = new ParsedPropertyBeanDocument( doc );
+ Writer w = new BufferedWriter( new FileWriter( outFile ) );
+
+ SimplePropertyBeanGenerator gen = new SimplePropertyBeanGenerator();
+ gen.setGeneratorName( BeangenDataSourceGenerator.class.getName() );
+
+ // tightly coupled to the implementation of SimplePropertyBeanGenerator!
+ IndirectingSerializableExtension idse = new IndirectingSerializableExtension("com.mchange.v2.naming.ReferenceIndirector")
+ {
+ protected void generateExtraSerInitializers(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ if (BeangenUtils.hasBoundProperties( props ))
+ iw.println("this.pcs = new PropertyChangeSupport( this );");
+ if (BeangenUtils.hasConstrainedProperties( props ))
+ iw.println("this.vcs = new VetoableChangeSupport( this );");
+ }
+ };
+ gen.addExtension( idse );
+
+ PropsToStringGeneratorExtension tsge = new PropsToStringGeneratorExtension();
+ tsge.setExcludePropertyNames( Arrays.asList( new String[] {"userOverridesAsString","overrideDefaultUser","overrideDefaultPassword"} ) );
+ gen.addExtension( tsge );
+
+ PropertyReferenceableExtension prex = new PropertyReferenceableExtension();
+ prex.setUseExplicitReferenceProperties( true );
+ // we use the string version to creating dependencies between the bean generator and c3p0 classes
+ //prex.setFactoryClassName( C3P0JavaBeanObjectFactory.class.getName() );
+ prex.setFactoryClassName( "com.mchange.v2.c3p0.impl.C3P0JavaBeanObjectFactory" );
+ gen.addExtension( prex );
+
+ BooleanInitIdentityTokenConstructortorGeneratorExtension biitcge = new BooleanInitIdentityTokenConstructortorGeneratorExtension();
+ gen.addExtension( biitcge );
+
+ if ( parsed.getClassInfo().getClassName().equals("WrapperConnectionPoolDataSourceBase") )
+ gen.addExtension( new WcpdsExtrasGeneratorExtension() );
+
+ if (unmodifiableShadow( doc ) )
+ gen.addExtension( new UnmodifiableShadowGeneratorExtension() );
+
+
+ gen.generate( parsed.getClassInfo(), parsed.getProperties(), w );
+
+ w.flush();
+ w.close();
+
+ System.err.println("Processed: " + argv[0] ); //+ " -> " + argv[1]);
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+
+ private static boolean unmodifiableShadow( Document doc )
+ {
+ Element docElem = doc.getDocumentElement();
+ return DomParseUtils.uniqueChild(docElem, "unmodifiable-shadow") != null;
+ }
+
+ static class BooleanInitIdentityTokenConstructortorGeneratorExtension implements GeneratorExtension
+ {
+ public Collection extraGeneralImports() {return Collections.EMPTY_SET;}
+
+ public Collection extraSpecificImports()
+ {
+ Set out = new HashSet();
+ out.add( "com.mchange.v2.c3p0.C3P0Registry" );
+ return out;
+ }
+
+ public Collection extraInterfaceNames() {return Collections.EMPTY_SET;}
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ BeangenUtils.writeExplicitDefaultConstructor( Modifier.PRIVATE, info, iw);
+ iw.println();
+ iw.println("public " + info.getClassName() + "( boolean autoregister )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println( "if (autoregister)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this.identityToken = C3P0ImplUtils.allocateIdentityToken( this );");
+ iw.println("C3P0Registry.reregister( this );");
+ iw.downIndent();
+ iw.println("}");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ }
+
+ static class WcpdsExtrasGeneratorExtension implements GeneratorExtension
+ {
+ public Collection extraGeneralImports() {return Collections.EMPTY_SET;}
+
+ public Collection extraSpecificImports()
+ {
+ Set out = new HashSet();
+ out.add( "com.mchange.v2.c3p0.ConnectionCustomizer" );
+ out.add( "javax.sql.PooledConnection" );
+ out.add( "java.sql.SQLException" );
+ return out;
+ }
+
+ public Collection extraInterfaceNames() {return Collections.EMPTY_SET;}
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.println("protected abstract PooledConnection getPooledConnection( ConnectionCustomizer cc, String idt)" +
+ " throws SQLException;");
+ iw.println("protected abstract PooledConnection getPooledConnection(String user, String password, ConnectionCustomizer cc, String idt)" +
+ " throws SQLException;");
+ }
+ }
+
+
+ static class UnmodifiableShadowGeneratorExtension implements GeneratorExtension
+ {
+ BeanExtractingGeneratorExtension bege;
+ CompleteConstructorGeneratorExtension ccge;
+
+ {
+ bege = new BeanExtractingGeneratorExtension();
+ bege.setExtractMethodModifiers( Modifier.PRIVATE );
+ bege.setConstructorModifiers( Modifier.PUBLIC );
+
+ ccge = new CompleteConstructorGeneratorExtension();
+ }
+
+ public Collection extraGeneralImports()
+ {
+ Set out = new HashSet();
+ out.addAll( bege.extraGeneralImports() );
+ out.addAll( ccge.extraGeneralImports() );
+ return out;
+ }
+
+ public Collection extraSpecificImports()
+ {
+ Set out = new HashSet();
+ out.addAll( bege.extraSpecificImports() );
+ out.addAll( ccge.extraSpecificImports() );
+ return out;
+ }
+
+ public Collection extraInterfaceNames() {return Collections.EMPTY_SET;}
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ ClassInfo innerInfo = new SimpleClassInfo( info.getPackageName(),
+ Modifier.PUBLIC | Modifier.STATIC,
+ "UnmodifiableShadow",
+ info.getSuperclassName(),
+ info.getInterfaceNames(),
+ info.getGeneralImports(),
+ info.getSpecificImports() );
+
+ SimplePropertyBeanGenerator innerGen = new SimplePropertyBeanGenerator();
+ innerGen.setInner( true );
+ innerGen.setForceUnmodifiable( true );
+ innerGen.addExtension( bege );
+ innerGen.addExtension( ccge );
+ innerGen.generate( innerInfo, props, iw );
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java b/src/classes/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java
new file mode 100644
index 0000000..e72e46b
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/codegen/JdbcProxyGenerator.java
@@ -0,0 +1,1018 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.codegen;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.sql.*;
+import com.mchange.v2.codegen.*;
+import com.mchange.v2.codegen.intfc.*;
+import com.mchange.v2.c3p0.C3P0ProxyConnection;
+import com.mchange.v2.c3p0.C3P0ProxyStatement;
+
+public abstract class JdbcProxyGenerator extends DelegatorGenerator
+{
+ final static boolean PREMATURE_DETACH_DEBUG = false;
+
+ JdbcProxyGenerator()
+ {
+ this.setGenerateInnerSetter( false );
+ this.setGenerateInnerGetter( false );
+ this.setGenerateNoArgConstructor( false );
+ this.setGenerateWrappingConstructor( true );
+ this.setClassModifiers( Modifier.PUBLIC | Modifier.FINAL );
+ this.setMethodModifiers( Modifier.PUBLIC | Modifier.FINAL );
+ }
+
+ abstract String getInnerTypeName();
+
+ static final class NewProxyMetaDataGenerator extends JdbcProxyGenerator
+ {
+ String getInnerTypeName()
+ { return "DatabaseMetaData"; }
+
+ protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ String mname = method.getName();
+ Class retType = method.getReturnType();
+
+ if ( ResultSet.class.isAssignableFrom( retType ) )
+ {
+ iw.println("ResultSet innerResultSet = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("if (innerResultSet == null) return null;");
+ iw.println("return new NewProxyResultSet( innerResultSet, parentPooledConnection, inner, this );");
+ }
+ else if ( mname.equals( "getConnection" ) )
+ {
+ iw.println("return this.proxyCon;");
+ }
+ else
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generatePreDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ if ( method.getExceptionTypes().length > 0 )
+ super.generatePreDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generatePostDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ if ( method.getExceptionTypes().length > 0 )
+ super.generatePostDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {
+ super.generateExtraDeclarations( intfcl, genclass, iw );
+ iw.println();
+ iw.println("NewProxyConnection proxyCon;");
+ iw.println();
+ iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+ iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, NewProxyConnection proxyCon )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this( inner, parentPooledConnection );");
+ iw.println("this.proxyCon = proxyCon;");
+ iw.downIndent();
+ iw.println("}");
+ }
+ }
+
+ static final class NewProxyResultSetGenerator extends JdbcProxyGenerator
+ {
+ String getInnerTypeName()
+ { return "ResultSet"; }
+
+ protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ iw.println("if (proxyConn != null) proxyConn.maybeDirtyTransaction();");
+ iw.println();
+ String mname = method.getName();
+ Class retType = method.getReturnType();
+
+ if ( mname.equals("close") )
+ {
+ iw.println("if (! this.isDetached())");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("if (creator instanceof Statement)");
+ iw.upIndent();
+ iw.println("parentPooledConnection.markInactiveResultSetForStatement( (Statement) creator, inner );");
+ iw.downIndent();
+ iw.println("else if (creator instanceof DatabaseMetaData)");
+ iw.upIndent();
+ iw.println("parentPooledConnection.markInactiveMetaDataResultSet( inner );");
+ iw.downIndent();
+ iw.println("else if (creator instanceof Connection)");
+ iw.upIndent();
+ iw.println("parentPooledConnection.markInactiveRawConnectionResultSet( inner );");
+ iw.downIndent();
+ iw.println("else throw new InternalError(\042Must be Statement or DatabaseMetaData -- Bad Creator: \042 + creator);");
+
+ iw.println("this.detach();");
+ iw.println("inner.close();");
+ iw.println("this.inner = null;");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ else if ( mname.equals("getStatement") )
+ {
+ iw.println("if (creator instanceof Statement)");
+ iw.upIndent();
+ iw.println("return (Statement) creatorProxy;");
+ iw.downIndent();
+ iw.println("else if (creator instanceof DatabaseMetaData)");
+ iw.upIndent();
+ iw.println("return null;");
+ iw.downIndent();
+ iw.println("else throw new InternalError(\042Must be Statement or DatabaseMetaData -- Bad Creator: \042 + creator);");
+ }
+ else if ( mname.equals("isClosed") )
+ {
+ iw.println( "return this.isDetached();" );
+ }
+ else
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {
+ super.generateExtraDeclarations( intfcl, genclass, iw );
+ iw.println();
+ iw.println("Object creator;");
+ iw.println("Object creatorProxy;");
+ iw.println("NewProxyConnection proxyConn;");
+ iw.println();
+ iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+ iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, Object c, Object cProxy )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this( inner, parentPooledConnection );");
+ iw.println("this.creator = c;");
+ iw.println("this.creatorProxy = cProxy;");
+ iw.println("if (creatorProxy instanceof NewProxyConnection) this.proxyConn = (NewProxyConnection) cProxy;");
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ protected void generatePreDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ super.generatePreDelegateCode( intfcl, genclass, method, iw );
+ }
+ }
+
+ static final class NewProxyAnyStatementGenerator extends JdbcProxyGenerator
+ {
+ String getInnerTypeName()
+ { return "Statement"; }
+
+ private final static boolean CONCURRENT_ACCESS_DEBUG = false;
+
+ {
+ this.setExtraInterfaces( new Class[] { C3P0ProxyStatement.class } );
+ }
+
+ protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ iw.println("maybeDirtyTransaction();");
+ iw.println();
+
+ String mname = method.getName();
+ Class retType = method.getReturnType();
+
+ if ( ResultSet.class.isAssignableFrom( retType ) )
+ {
+ iw.println("ResultSet innerResultSet = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("if (innerResultSet == null) return null;");
+ iw.println("parentPooledConnection.markActiveResultSetForStatement( inner, innerResultSet );");
+ iw.println("return new NewProxyResultSet( innerResultSet, parentPooledConnection, inner, this );");
+ }
+ else if ( mname.equals("getConnection") )
+ {
+ iw.println("if (! this.isDetached())");
+ iw.upIndent();
+ iw.println("return creatorProxy;");
+ iw.downIndent();
+ iw.println("else");
+ iw.upIndent();
+ iw.println("throw new SQLException(\"You cannot operate on a closed Statement!\");");
+ iw.downIndent();
+ }
+ else if ( mname.equals("close") )
+ {
+ iw.println("if (! this.isDetached())");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("if ( is_cached )");
+ iw.upIndent();
+ iw.println("parentPooledConnection.checkinStatement( inner );");
+ iw.downIndent();
+ iw.println("else");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("parentPooledConnection.markInactiveUncachedStatement( inner );");
+
+ iw.println("try{ inner.close(); }");
+ iw.println("catch (Exception e )");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("if (logger.isLoggable( MLevel.WARNING ))");
+ iw.upIndent();
+ iw.println("logger.log( MLevel.WARNING, \042Exception on close of inner statement.\042, e);");
+ iw.downIndent();
+
+ iw.println( "SQLException sqle = SqlUtils.toSQLException( e );" );
+ iw.println( "throw sqle;" );
+ iw.downIndent();
+ iw.println("}");
+ iw.downIndent();
+ iw.println("}");
+
+ iw.println();
+ iw.println("this.detach();");
+ iw.println("this.inner = null;");
+ iw.println("this.creatorProxy = null;");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ else if ( mname.equals("isClosed") )
+ {
+ iw.println( "return this.isDetached();" );
+ }
+ else
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generatePreDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ // concurrent-access-debug only
+ if (CONCURRENT_ACCESS_DEBUG)
+ {
+ iw.println("Object record;");
+ iw.println("synchronized (concurrentAccessRecorder)");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("record = concurrentAccessRecorder.record();");
+ iw.println("int num_concurrent_clients = concurrentAccessRecorder.size();");
+ iw.println("if (num_concurrent_clients != 1)");
+ iw.upIndent();
+ iw.println("logger.log(MLevel.WARNING, " +
+ "concurrentAccessRecorder.getDump(\042Apparent concurrent access! (\042 + num_concurrent_clients + \042 clients.\042) );");
+ iw.downIndent();
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ }
+ // end concurrent-access-debug only
+
+ super.generatePreDelegateCode( intfcl, genclass, method, iw );
+ }
+
+ protected void generatePostDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ super.generatePostDelegateCode( intfcl, genclass, method, iw );
+
+ // concurrent-access-debug only
+ if (CONCURRENT_ACCESS_DEBUG)
+ {
+ iw.println("finally");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("concurrentAccessRecorder.remove( record );");
+ iw.downIndent();
+ iw.println("}");
+ }
+ // end concurrent-access-debug only
+ }
+
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {
+ super.generateExtraDeclarations( intfcl, genclass, iw );
+ iw.println();
+
+ // concurrent-access-debug only!
+ if (CONCURRENT_ACCESS_DEBUG)
+ {
+ iw.println("com.mchange.v2.debug.ThreadNameStackTraceRecorder concurrentAccessRecorder");
+ iw.upIndent();
+ iw.println("= new com.mchange.v2.debug.ThreadNameStackTraceRecorder(\042Concurrent Access Recorder\042);");
+ iw.downIndent();
+ }
+ // end concurrent-access-debug only!
+
+ iw.println("boolean is_cached;");
+ iw.println("NewProxyConnection creatorProxy;");
+ iw.println();
+ iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+ iw.println("( " + CodegenUtils.simpleClassName( intfcl ) +
+ " inner, NewPooledConnection parentPooledConnection, boolean cached, NewProxyConnection cProxy )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this( inner, parentPooledConnection );");
+ iw.println("this.is_cached = cached;");
+ iw.println("this.creatorProxy = cProxy;");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("public Object rawStatementOperation(Method m, Object target, Object[] args) " +
+ "throws IllegalAccessException, InvocationTargetException, SQLException");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("maybeDirtyTransaction();");
+ iw.println();
+ iw.println("if (target == C3P0ProxyStatement.RAW_STATEMENT) target = inner;");
+ iw.println("for (int i = 0, len = args.length; i < len; ++i)");
+ iw.upIndent();
+ iw.println("if (args[i] == C3P0ProxyStatement.RAW_STATEMENT) args[i] = inner;");
+ iw.downIndent();
+ iw.println("Object out = m.invoke(target, args);");
+ iw.println("if (out instanceof ResultSet)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("ResultSet innerResultSet = (ResultSet) out;");
+ iw.println("parentPooledConnection.markActiveResultSetForStatement( inner, innerResultSet );");
+ iw.println("out = new NewProxyResultSet( innerResultSet, parentPooledConnection, inner, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("return out;");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("void maybeDirtyTransaction()");
+ iw.println("{ creatorProxy.maybeDirtyTransaction(); }");
+ }
+
+ protected void generateExtraImports( IndentedWriter iw ) throws IOException
+ {
+ super.generateExtraImports( iw );
+ iw.println("import java.lang.reflect.InvocationTargetException;");
+ }
+
+
+ }
+
+// protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+// {
+// super.generateExtraDeclarations( intfcl, genclass, iw );
+// iw.println();
+// iw.println("Statement creatingStatement;");
+// iw.println();
+// iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+// iw.println("( " + CodegenUtils.simpleClassName( intfcl.getClass() ) + " inner, NewPooledConnection parentPooledConnection, Statement stmt )");
+// iw.println("{");
+// iw.upIndent();
+// iw.println("this( inner, parentPooledConnection );");
+// iw.println("this.creatingStatement = stmt;");
+// iw.downIndent();
+// iw.println("}");
+// }
+
+ static final class NewProxyConnectionGenerator extends JdbcProxyGenerator
+ {
+ String getInnerTypeName()
+ { return "Connection"; }
+
+ {
+ this.setMethodModifiers( Modifier.PUBLIC | Modifier.SYNCHRONIZED );
+ this.setExtraInterfaces( new Class[] { C3P0ProxyConnection.class } );
+ }
+
+ protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ String mname = method.getName();
+ if (mname.equals("createStatement"))
+ {
+ iw.println("txn_known_resolved = false;");
+ iw.println();
+ iw.println("Statement innerStmt = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("return new NewProxyStatement( innerStmt, parentPooledConnection, false, this );");
+ }
+ else if (mname.equals("prepareStatement"))
+ {
+ iw.println("txn_known_resolved = false;");
+ iw.println();
+ iw.println("PreparedStatement innerStmt;");
+ iw.println();
+ iw.println("if ( parentPooledConnection.isStatementCaching() )");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+
+ generateFindMethodAndArgs( method, iw );
+ iw.println("innerStmt = (PreparedStatement) parentPooledConnection.checkoutStatement( method, args );");
+ iw.println("return new NewProxyPreparedStatement( innerStmt, parentPooledConnection, true, this );");
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (ResourceClosedException e)");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("if ( logger.isLoggable( MLevel.FINE ) )");
+ iw.upIndent();
+ iw.println("logger.log( MLevel.FINE, " +
+ "\042A Connection tried to prepare a Statement via a Statement cache that is already closed. " +
+ "This can happen -- rarely -- if a DataSource is closed or reset() while Connections are checked-out and in use.\042, e );");
+ iw.downIndent();
+
+ // repeated code... any changes probably need to be duplicated below
+ iw.println("innerStmt = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("return new NewProxyPreparedStatement( innerStmt, parentPooledConnection, false, this );");
+
+ iw.downIndent();
+ iw.println("}");
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else");
+ iw.println("{");
+ iw.upIndent();
+
+ // repeated code... any changes probably need to be duplicated above
+ iw.println("innerStmt = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("return new NewProxyPreparedStatement( innerStmt, parentPooledConnection, false, this );");
+
+ iw.downIndent();
+ iw.println("}");
+
+ }
+ else if (mname.equals("prepareCall"))
+ {
+ iw.println("txn_known_resolved = false;");
+ iw.println();
+ iw.println("CallableStatement innerStmt;");
+ iw.println();
+ iw.println("if ( parentPooledConnection.isStatementCaching() )");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+
+ generateFindMethodAndArgs( method, iw );
+ iw.println("innerStmt = (CallableStatement) parentPooledConnection.checkoutStatement( method, args );");
+ iw.println("return new NewProxyCallableStatement( innerStmt, parentPooledConnection, true, this );");
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (ResourceClosedException e)");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("if ( logger.isLoggable( MLevel.FINE ) )");
+ iw.upIndent();
+ iw.println("logger.log( MLevel.FINE, " +
+ "\042A Connection tried to prepare a CallableStatement via a Statement cache that is already closed. " +
+ "This can happen -- rarely -- if a DataSource is closed or reset() while Connections are checked-out and in use.\042, e );");
+ iw.downIndent();
+
+ // repeated code... any changes probably need to be duplicated below
+ iw.println("innerStmt = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("return new NewProxyCallableStatement( innerStmt, parentPooledConnection, false, this );");
+
+ iw.downIndent();
+ iw.println("}");
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else");
+ iw.println("{");
+ iw.upIndent();
+
+ // repeated code... any changes probably need to be duplicated above
+ iw.println("innerStmt = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("return new NewProxyCallableStatement( innerStmt, parentPooledConnection, false, this );");
+
+ iw.downIndent();
+ iw.println("}");
+
+ }
+ else if (mname.equals("getMetaData"))
+ {
+ iw.println("txn_known_resolved = false;");
+ iw.println();
+ iw.println("if (this.metaData == null)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("DatabaseMetaData innerMetaData = inner." + CodegenUtils.methodCall( method ) + ";");
+ iw.println("this.metaData = new NewProxyDatabaseMetaData( innerMetaData, parentPooledConnection, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("return this.metaData;");
+ }
+ else if ( mname.equals("setTransactionIsolation") )
+ {
+ //do nothing with txn_known_resolved
+
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ iw.println( "parentPooledConnection.markNewTxnIsolation( " + CodegenUtils.generatedArgumentName( 0 ) + " );");
+ }
+ else if ( mname.equals("setCatalog") )
+ {
+ //do nothing with txn_known_resolved
+
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ iw.println( "parentPooledConnection.markNewCatalog( " + CodegenUtils.generatedArgumentName( 0 ) + " );");
+ }
+ else if ( mname.equals("setHoldability") )
+ {
+ //do nothing with txn_known_resolved
+
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ iw.println( "parentPooledConnection.markNewHoldability( " + CodegenUtils.generatedArgumentName( 0 ) + " );");
+ }
+ else if ( mname.equals("setReadOnly") )
+ {
+ //do nothing with txn_known_resolved
+
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ iw.println( "parentPooledConnection.markNewReadOnly( " + CodegenUtils.generatedArgumentName( 0 ) + " );");
+ }
+ else if ( mname.equals("setTypeMap") )
+ {
+ //do nothing with txn_known_resolved
+
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ iw.println( "parentPooledConnection.markNewTypeMap( " + CodegenUtils.generatedArgumentName( 0 ) + " );");
+ }
+ else if ( mname.equals("close") )
+ {
+ iw.println("if (! this.isDetached())");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("NewPooledConnection npc = parentPooledConnection;");
+ iw.println("this.detach();");
+ iw.println("npc.markClosedProxyConnection( this, txn_known_resolved );");
+ iw.println("this.inner = null;");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ))");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("logger.log( MLevel.FINE, this + \042: close() called more than once.\042 );");
+
+ // premature-detach-debug-debug only!
+ if (PREMATURE_DETACH_DEBUG)
+ {
+ iw.println("prematureDetachRecorder.record();");
+ iw.println("logger.warning( prematureDetachRecorder.getDump(\042Apparent multiple close of " +
+ getInnerTypeName() + ".\042) );");
+ }
+ // end-premature-detach-debug-only!
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ else if ( mname.equals("isClosed") )
+ {
+ iw.println("return this.isDetached();");
+ }
+ else
+ {
+ iw.println("txn_known_resolved = " +
+ ( mname.equals("commit") || mname.equals( "rollback" ) || mname.equals( "setAutoCommit" ) ) +
+ ';');
+ iw.println();
+ super.generateDelegateCode( intfcl, genclass, method, iw );
+ }
+ }
+
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {
+ iw.println("boolean txn_known_resolved = true;");
+ iw.println();
+ iw.println("DatabaseMetaData metaData = null;");
+ iw.println();
+
+// We've nothing to do with preferredTestQuery here... the stuff below was unnecessary
+
+// iw.println("String preferredTestQuery = null;");
+// iw.println();
+// iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+// iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection, String preferredTestQuery )");
+// iw.println("{");
+// iw.upIndent();
+// iw.println("this( inner, parentPooledConnection );");
+// iw.println("this.preferredTestQuery = preferredTestQuery;");
+// iw.downIndent();
+// iw.println("}");
+// iw.println();
+
+ iw.println("public Object rawConnectionOperation(Method m, Object target, Object[] args)");
+ iw.upIndent();
+ iw.println("throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException");
+ iw.downIndent();
+ iw.println("{");
+ iw.upIndent();
+ iw.println("maybeDirtyTransaction();");
+ iw.println();
+ iw.println("if (inner == null)");
+ iw.upIndent();
+ iw.println("throw new SQLException(\"You cannot operate on a closed Connection!\");");
+ iw.downIndent();
+
+ iw.println("if ( target == C3P0ProxyConnection.RAW_CONNECTION)");
+ iw.upIndent();
+ iw.println("target = inner;");
+ iw.downIndent();
+
+ iw.println("for (int i = 0, len = args.length; i < len; ++i)");
+ iw.upIndent();
+ iw.println("if (args[i] == C3P0ProxyConnection.RAW_CONNECTION)");
+ iw.upIndent();
+ iw.println("args[i] = inner;");
+ iw.downIndent();
+ iw.downIndent();
+
+ iw.println("Object out = m.invoke( target, args );");
+ iw.println();
+ iw.println("// we never cache Statements generated by an operation on the raw Connection");
+ iw.println("if (out instanceof CallableStatement)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("CallableStatement innerStmt = (CallableStatement) out;");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("out = new NewProxyCallableStatement( innerStmt, parentPooledConnection, false, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else if (out instanceof PreparedStatement)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("PreparedStatement innerStmt = (PreparedStatement) out;");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("out = new NewProxyPreparedStatement( innerStmt, parentPooledConnection, false, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else if (out instanceof Statement)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("Statement innerStmt = (Statement) out;");
+ iw.println("parentPooledConnection.markActiveUncachedStatement( innerStmt );");
+ iw.println("out = new NewProxyStatement( innerStmt, parentPooledConnection, false, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else if (out instanceof ResultSet)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("ResultSet innerRs = (ResultSet) out;");
+ iw.println("parentPooledConnection.markActiveRawConnectionResultSet( innerRs );");
+ iw.println("out = new NewProxyResultSet( innerRs, parentPooledConnection, inner, this );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else if (out instanceof DatabaseMetaData)");
+ iw.upIndent();
+ iw.println("out = new NewProxyDatabaseMetaData( (DatabaseMetaData) out, parentPooledConnection );");
+ iw.downIndent();
+ iw.println("return out;");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("synchronized void maybeDirtyTransaction()");
+ iw.println("{ txn_known_resolved = false; }");
+
+ super.generateExtraDeclarations( intfcl, genclass, iw );
+ }
+
+ void generateFindMethodAndArgs( Method method, IndentedWriter iw ) throws IOException
+ {
+ iw.println("Class[] argTypes = ");
+ iw.println("{");
+ iw.upIndent();
+
+ Class[] argTypes = method.getParameterTypes();
+ for (int i = 0, len = argTypes.length; i < len; ++i)
+ {
+ if (i != 0) iw.println(",");
+ iw.print( CodegenUtils.simpleClassName( argTypes[i] ) + ".class" );
+ }
+ iw.println();
+ iw.downIndent();
+ iw.println("};");
+ iw.println("Method method = Connection.class.getMethod( \042" + method.getName() + "\042 , argTypes );");
+ iw.println();
+ iw.println("Object[] args = ");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0, len = argTypes.length; i < len; ++i)
+ {
+ if (i != 0) iw.println(",");
+ String argName = CodegenUtils.generatedArgumentName( i );
+ Class argType = argTypes[i];
+ if (argType.isPrimitive())
+ {
+ if (argType == boolean.class)
+ iw.print( "Boolean.valueOf( " + argName + " )" );
+ else if (argType == byte.class)
+ iw.print( "new Byte( " + argName + " )" );
+ else if (argType == char.class)
+ iw.print( "new Character( " + argName + " )" );
+ else if (argType == short.class)
+ iw.print( "new Short( " + argName + " )" );
+ else if (argType == int.class)
+ iw.print( "new Integer( " + argName + " )" );
+ else if (argType == long.class)
+ iw.print( "new Long( " + argName + " )" );
+ else if (argType == float.class)
+ iw.print( "new Float( " + argName + " )" );
+ else if (argType == double.class)
+ iw.print( "new Double( " + argName + " )" );
+ }
+ else
+ iw.print( argName );
+ }
+
+ iw.downIndent();
+ iw.println("};");
+ }
+
+ protected void generateExtraImports( IndentedWriter iw ) throws IOException
+ {
+ super.generateExtraImports( iw );
+ iw.println("import java.lang.reflect.InvocationTargetException;");
+ iw.println("import com.mchange.v2.util.ResourceClosedException;");
+ }
+ }
+
+ protected void generatePreDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ generateTryOpener( iw );
+ }
+
+ protected void generatePostDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ generateTryCloserAndCatch( intfcl, genclass, method, iw );
+ }
+
+ void generateTryOpener( IndentedWriter iw ) throws IOException
+ {
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+ }
+
+ void generateTryCloserAndCatch( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (NullPointerException exc)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("if ( this.isDetached() )");
+ iw.println("{");
+ iw.upIndent();
+ //iw.println( "System.err.print(\042probably 'cuz we're closed -- \042);" );
+ //iw.println( "exc.printStackTrace();" );
+ if ( "close".equals( method.getName() ) )
+ {
+ iw.println("if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ))");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("logger.log( MLevel.FINE, this + \042: close() called more than once.\042 );");
+
+ // premature-detach-debug-debug only!
+ if (PREMATURE_DETACH_DEBUG)
+ {
+ iw.println("prematureDetachRecorder.record();");
+ iw.println("logger.warning( prematureDetachRecorder.getDump(\042Apparent multiple close of " +
+ getInnerTypeName() + ".\042) );");
+ }
+ // end-premature-detach-debug-only!
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ else
+ {
+ // premature-detach-debug-debug only!
+ if (PREMATURE_DETACH_DEBUG)
+ {
+ iw.println("prematureDetachRecorder.record();");
+ iw.println("logger.warning( prematureDetachRecorder.getDump(\042Use of already detached " +
+ getInnerTypeName() + ".\042) );");
+ }
+ // end-premature-detach-debug-only!
+
+ iw.println( "throw SqlUtils.toSQLException(\042You can't operate on a closed " + getInnerTypeName() + "!!!\042, exc);");
+ }
+ iw.downIndent();
+ iw.println("}");
+ iw.println( "else throw exc;" );
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (Exception exc)");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("if (! this.isDetached())");
+ iw.println("{");
+ iw.upIndent();
+ //iw.println( "exc.printStackTrace();" );
+ iw.println( "throw parentPooledConnection.handleThrowable( exc );" );
+ iw.downIndent();
+ iw.println("}");
+ iw.println("else throw SqlUtils.toSQLException( exc );");
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {
+ // premature-detach-debug-debug only!
+ if (PREMATURE_DETACH_DEBUG)
+ {
+ iw.println("com.mchange.v2.debug.ThreadNameStackTraceRecorder prematureDetachRecorder");
+ iw.upIndent();
+ iw.println("= new com.mchange.v2.debug.ThreadNameStackTraceRecorder(\042Premature Detach Recorder\042);");
+ iw.downIndent();
+ }
+ // end-premature-detach-debug-only!
+
+ iw.println("private final static MLogger logger = MLog.getLogger( \042" + genclass + "\042 );");
+ iw.println();
+
+ iw.println("volatile NewPooledConnection parentPooledConnection;");
+ iw.println();
+
+ iw.println("ConnectionEventListener cel = new ConnectionEventListener()");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("public void connectionErrorOccurred(ConnectionEvent evt)");
+ iw.println("{ /* DON'T detach()... IGNORE -- this could be an ordinary error. Leave it to the PooledConnection to test, but leave proxies intact */ }");
+ //BAD puppy -- iw.println("{ detach(); }");
+
+ iw.println();
+ iw.println("public void connectionClosed(ConnectionEvent evt)");
+ iw.println("{ detach(); }");
+
+ iw.downIndent();
+ iw.println("};");
+ iw.println();
+
+ iw.println("void attach( NewPooledConnection parentPooledConnection )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this.parentPooledConnection = parentPooledConnection;");
+ iw.println("parentPooledConnection.addConnectionEventListener( cel );");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("private void detach()");
+ iw.println("{");
+ iw.upIndent();
+
+ // factored out so we could define debug versions...
+ writeDetachBody(iw);
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.print( CodegenUtils.fqcnLastElement( genclass ) );
+ iw.println("( " + CodegenUtils.simpleClassName( intfcl ) + " inner, NewPooledConnection parentPooledConnection )");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("this( inner );");
+ iw.println("attach( parentPooledConnection );");
+ generateExtraConstructorCode( intfcl, genclass, iw );
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("boolean isDetached()");
+ iw.println("{ return (this.parentPooledConnection == null); }");
+ }
+
+ protected void writeDetachBody(IndentedWriter iw) throws IOException
+ {
+ // premature-detach-debug only
+ if (PREMATURE_DETACH_DEBUG)
+ {
+ iw.println("prematureDetachRecorder.record();");
+ iw.println("if (this.isDetached())");
+ iw.upIndent();
+ iw.println("logger.warning( prematureDetachRecorder.getDump(\042Double Detach.\042) );");
+ iw.downIndent();
+ }
+ // end premature-detach-debug only
+
+ iw.println("parentPooledConnection.removeConnectionEventListener( cel );");
+ iw.println("parentPooledConnection = null;");
+ }
+
+ protected void generateExtraImports( IndentedWriter iw ) throws IOException
+ {
+ iw.println("import java.sql.*;");
+ iw.println("import javax.sql.*;");
+ iw.println("import com.mchange.v2.log.*;");
+ iw.println("import java.lang.reflect.Method;");
+ iw.println("import com.mchange.v2.sql.SqlUtils;");
+ }
+
+ void generateExtraConstructorCode( Class intfcl, String genclass, IndentedWriter iw ) throws IOException
+ {}
+
+ public static void main( String[] argv )
+ {
+ try
+ {
+ if (argv.length != 1)
+ {
+ System.err.println("java " + JdbcProxyGenerator.class.getName() + " <source-root-directory>");
+ return;
+ }
+
+ File srcroot = new File( argv[0] );
+ if (! srcroot.exists() || !srcroot.canWrite() )
+ {
+ System.err.println(JdbcProxyGenerator.class.getName() + " -- sourceroot: " + argv[0] + " must exist and be writable");
+ return;
+ }
+
+ DelegatorGenerator mdgen = new NewProxyMetaDataGenerator();
+ DelegatorGenerator rsgen = new NewProxyResultSetGenerator();
+ DelegatorGenerator stgen = new NewProxyAnyStatementGenerator();
+ DelegatorGenerator cngen = new NewProxyConnectionGenerator();
+
+ genclass( cngen, Connection.class, "com.mchange.v2.c3p0.impl.NewProxyConnection", srcroot );
+ genclass( stgen, Statement.class, "com.mchange.v2.c3p0.impl.NewProxyStatement", srcroot );
+ genclass( stgen, PreparedStatement.class, "com.mchange.v2.c3p0.impl.NewProxyPreparedStatement", srcroot );
+ genclass( stgen, CallableStatement.class, "com.mchange.v2.c3p0.impl.NewProxyCallableStatement", srcroot );
+ genclass( rsgen, ResultSet.class, "com.mchange.v2.c3p0.impl.NewProxyResultSet", srcroot );
+ genclass( mdgen, DatabaseMetaData.class, "com.mchange.v2.c3p0.impl.NewProxyDatabaseMetaData", srcroot );
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+
+ static void genclass( DelegatorGenerator dg, Class intfcl, String fqcn, File srcroot ) throws IOException
+ {
+ File genDir = new File( srcroot, dirForFqcn( fqcn ) );
+ if (! genDir.exists() )
+ {
+ System.err.println( JdbcProxyGenerator.class.getName() + " -- creating directory: " + genDir.getAbsolutePath() );
+ genDir.mkdirs();
+ }
+ String fileName = CodegenUtils.fqcnLastElement( fqcn ) + ".java";
+ Writer w = null;
+ try
+ {
+ w = new BufferedWriter( new FileWriter( new File( genDir, fileName ) ) );
+ dg.writeDelegator( intfcl, fqcn, w );
+ w.flush();
+ System.err.println("Generated " + fileName);
+ }
+ finally
+ {
+ try { if (w != null) w.close(); }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+ }
+
+ static String dirForFqcn( String fqcn )
+ {
+ int last_dot = fqcn.lastIndexOf('.');
+ StringBuffer sb = new StringBuffer( fqcn.substring( 0, last_dot + 1) );
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ if (sb.charAt(i) == '.')
+ sb.setCharAt(i, '/');
+ return sb.toString();
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/filter/FilterDataSource.java b/src/classes/com/mchange/v2/c3p0/filter/FilterDataSource.java
new file mode 100644
index 0000000..63919c4
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/filter/FilterDataSource.java
@@ -0,0 +1,73 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.filter;
+
+import java.io.PrintWriter;
+import java.lang.String;
+import java.sql.Connection;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+
+
+public abstract class FilterDataSource implements DataSource
+{
+ protected DataSource inner;
+
+ public FilterDataSource(DataSource inner)
+ {
+ this.inner = inner;
+ }
+
+ public Connection getConnection() throws SQLException
+ {
+ return inner.getConnection();
+ }
+
+ public Connection getConnection(String a, String b) throws SQLException
+ {
+ return inner.getConnection(a, b);
+ }
+
+ public PrintWriter getLogWriter() throws SQLException
+ {
+ return inner.getLogWriter();
+ }
+
+ public int getLoginTimeout() throws SQLException
+ {
+ return inner.getLoginTimeout();
+ }
+
+ public void setLogWriter(PrintWriter a) throws SQLException
+ {
+ inner.setLogWriter(a);
+ }
+
+ public void setLoginTimeout(int a) throws SQLException
+ {
+ inner.setLoginTimeout(a);
+ }
+
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/AbstractC3P0PooledConnection.java b/src/classes/com/mchange/v2/c3p0/impl/AbstractC3P0PooledConnection.java
new file mode 100644
index 0000000..fc41fa8
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/AbstractC3P0PooledConnection.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.Connection;
+import javax.sql.PooledConnection;
+import com.mchange.v2.c3p0.stmt.GooGooStatementCache;
+import com.mchange.v1.util.ClosableResource;
+
+abstract class AbstractC3P0PooledConnection implements PooledConnection, ClosableResource
+{
+ abstract Connection getPhysicalConnection();
+ abstract void initStatementCache(GooGooStatementCache scache);
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/impl/AbstractIdentityTokenized.java b/src/classes/com/mchange/v2/c3p0/impl/AbstractIdentityTokenized.java
new file mode 100644
index 0000000..e1d669b
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/AbstractIdentityTokenized.java
@@ -0,0 +1,47 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+/*
+ * It would be convenient to put the getter/setter methods
+ * for the identity token here, but unfortunately we have no
+ * way of setting up the for Referenceability in multiple
+ * levels of a class hierarchy. So we leave the getters/setters,
+ * and variable initialization to code-generators.
+ */
+public abstract class AbstractIdentityTokenized implements IdentityTokenized
+{
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+
+ if (o instanceof IdentityTokenized)
+ return this.getIdentityToken().equals( ((IdentityTokenized) o).getIdentityToken() );
+ else
+ return false;
+ }
+
+ public int hashCode()
+ { return ~this.getIdentityToken().hashCode(); }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/impl/AbstractPoolBackedDataSource.java b/src/classes/com/mchange/v2/c3p0/impl/AbstractPoolBackedDataSource.java
new file mode 100644
index 0000000..423a4e4
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/AbstractPoolBackedDataSource.java
@@ -0,0 +1,500 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.io.*;
+import java.sql.*;
+
+import javax.sql.*;
+
+import com.mchange.lang.ThrowableUtils;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.log.*;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+import java.beans.PropertyChangeListener;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import com.mchange.v2.c3p0.cfg.C3P0Config;
+
+public abstract class AbstractPoolBackedDataSource extends PoolBackedDataSourceBase implements PooledDataSource
+{
+ final static MLogger logger = MLog.getLogger( AbstractPoolBackedDataSource.class );
+
+ final static String NO_CPDS_ERR_MSG =
+ "Attempted to use an uninitialized PoolBackedDataSource. " +
+ "Please call setConnectionPoolDataSource( ... ) to initialize.";
+
+ //MT: protected by this' lock
+ transient C3P0PooledConnectionPoolManager poolManager;
+ transient boolean is_closed = false;
+ //MT: end protected by this' lock
+
+ protected AbstractPoolBackedDataSource( boolean autoregister )
+ {
+ super( autoregister );
+ setUpPropertyEvents();
+ }
+
+ protected AbstractPoolBackedDataSource(String configName)
+ {
+ this( true );
+ initializeNamedConfig( configName );
+ }
+
+ private void setUpPropertyEvents()
+ {
+ PropertyChangeListener l = new PropertyChangeListener()
+ {
+ public void propertyChange( PropertyChangeEvent evt )
+ { resetPoolManager(); }
+ };
+ this.addPropertyChangeListener( l );
+ }
+
+
+ protected void initializeNamedConfig(String configName)
+ {
+ try
+ {
+ if (configName != null)
+ {
+ C3P0Config.bindNamedConfigToBean( this, configName );
+ if ( this.getDataSourceName().equals( this.getIdentityToken() ) ) //dataSourceName has not been specified in config
+ this.setDataSourceName( configName );
+ }
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "Error binding PoolBackedDataSource to named-config '" + configName +
+ "'. Some default-config values may be used.",
+ e);
+ }
+ }
+
+// Commented out method is just super.getReference() with a lot of extra printing
+
+// public javax.naming.Reference getReference() throws javax.naming.NamingException
+// {
+// System.err.println("getReference()!!!!");
+// new Exception("PRINT-STACK-TRACE").printStackTrace();
+// javax.naming.Reference out = super.getReference();
+// System.err.println(out);
+// return out;
+// }
+
+ // report our ID token as dataSourceName if we have no
+ // name explicitly set
+ public String getDataSourceName()
+ {
+ String out = super.getDataSourceName();
+ if (out == null)
+ out = this.getIdentityToken();
+ return out;
+ }
+
+ //implementation of javax.sql.DataSource
+ public Connection getConnection() throws SQLException
+ {
+ PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
+ return pc.getConnection();
+ }
+
+ public Connection getConnection(String username, String password) throws SQLException
+ {
+ PooledConnection pc = getPoolManager().getPool(username, password).checkoutPooledConnection();
+ return pc.getConnection();
+ }
+
+ public PrintWriter getLogWriter() throws SQLException
+ { return assertCpds().getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out) throws SQLException
+ { assertCpds().setLogWriter( out ); }
+
+ public int getLoginTimeout() throws SQLException
+ { return assertCpds().getLoginTimeout(); }
+
+ public void setLoginTimeout(int seconds) throws SQLException
+ { assertCpds().setLoginTimeout( seconds ); }
+
+ //implementation of com.mchange.v2.c3p0.PoolingDataSource
+ public int getNumConnections() throws SQLException
+ { return getPoolManager().getPool().getNumConnections(); }
+
+ public int getNumIdleConnections() throws SQLException
+ { return getPoolManager().getPool().getNumIdleConnections(); }
+
+ public int getNumBusyConnections() throws SQLException
+ { return getPoolManager().getPool().getNumBusyConnections(); }
+
+ public int getNumUnclosedOrphanedConnections() throws SQLException
+ { return getPoolManager().getPool().getNumUnclosedOrphanedConnections(); }
+
+ public int getNumConnectionsDefaultUser() throws SQLException
+ { return getNumConnections();}
+
+ public int getNumIdleConnectionsDefaultUser() throws SQLException
+ { return getNumIdleConnections();}
+
+ public int getNumBusyConnectionsDefaultUser() throws SQLException
+ { return getNumBusyConnections();}
+
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException
+ { return getNumUnclosedOrphanedConnections();}
+
+ public int getStatementCacheNumStatementsDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getStatementCacheNumStatements(); }
+
+ public int getStatementCacheNumCheckedOutDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getStatementCacheNumCheckedOut(); }
+
+ public int getStatementCacheNumConnectionsWithCachedStatementsDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getStatementCacheNumConnectionsWithCachedStatements(); }
+
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getEffectivePropertyCycle(); }
+
+ public long getStartTimeMillisDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getStartTime(); }
+
+ public long getUpTimeMillisDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getUpTime(); }
+
+ public long getNumFailedCheckinsDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getNumFailedCheckins(); }
+
+ public long getNumFailedCheckoutsDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getNumFailedCheckouts(); }
+
+ public long getNumFailedIdleTestsDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getNumFailedIdleTests(); }
+
+ public int getNumThreadsAwaitingCheckoutDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getNumThreadsAwaitingCheckout(); }
+
+ public int getThreadPoolSize() throws SQLException
+ { return getPoolManager().getThreadPoolSize(); }
+
+ public int getThreadPoolNumActiveThreads() throws SQLException
+ { return getPoolManager().getThreadPoolNumActiveThreads(); }
+
+ public int getThreadPoolNumIdleThreads() throws SQLException
+ { return getPoolManager().getThreadPoolNumIdleThreads(); }
+
+ public int getThreadPoolNumTasksPending() throws SQLException
+ { return getPoolManager().getThreadPoolNumTasksPending(); }
+
+ public String sampleThreadPoolStackTraces() throws SQLException
+ { return getPoolManager().getThreadPoolStackTraces(); }
+
+ public String sampleThreadPoolStatus() throws SQLException
+ { return getPoolManager().getThreadPoolStatus(); }
+
+ public String sampleStatementCacheStatusDefaultUser() throws SQLException
+ { return getPoolManager().getPool().dumpStatementCacheStatus(); }
+
+ public String sampleStatementCacheStatus(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).dumpStatementCacheStatus(); }
+
+ public Throwable getLastAcquisitionFailureDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getLastAcquisitionFailure(); }
+
+ public Throwable getLastCheckinFailureDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getLastCheckinFailure(); }
+
+ public Throwable getLastCheckoutFailureDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getLastCheckoutFailure(); }
+
+ public Throwable getLastIdleTestFailureDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getLastIdleTestFailure(); }
+
+ public Throwable getLastConnectionTestFailureDefaultUser() throws SQLException
+ { return getPoolManager().getPool().getLastConnectionTestFailure(); }
+
+ public Throwable getLastAcquisitionFailure(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getLastAcquisitionFailure(); }
+
+ public Throwable getLastCheckinFailure(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getLastCheckinFailure(); }
+
+ public Throwable getLastCheckoutFailure(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getLastCheckoutFailure(); }
+
+ public Throwable getLastIdleTestFailure(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getLastIdleTestFailure(); }
+
+ public Throwable getLastConnectionTestFailure(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getLastConnectionTestFailure(); }
+
+ public int getNumThreadsAwaitingCheckout(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumThreadsAwaitingCheckout(); }
+
+ public String sampleLastAcquisitionFailureStackTraceDefaultUser() throws SQLException
+ {
+ Throwable t = getLastAcquisitionFailureDefaultUser();
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastCheckinFailureStackTraceDefaultUser() throws SQLException
+ {
+ Throwable t = getLastCheckinFailureDefaultUser();
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastCheckoutFailureStackTraceDefaultUser() throws SQLException
+ {
+ Throwable t = getLastCheckoutFailureDefaultUser();
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastIdleTestFailureStackTraceDefaultUser() throws SQLException
+ {
+ Throwable t = getLastIdleTestFailureDefaultUser();
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastConnectionTestFailureStackTraceDefaultUser() throws SQLException
+ {
+ Throwable t = getLastConnectionTestFailureDefaultUser();
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastAcquisitionFailureStackTrace(String username, String password) throws SQLException
+ {
+ Throwable t = getLastAcquisitionFailure(username, password);
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastCheckinFailureStackTrace(String username, String password) throws SQLException
+ {
+ Throwable t = getLastCheckinFailure(username, password);
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastCheckoutFailureStackTrace(String username, String password) throws SQLException
+ {
+ Throwable t = getLastCheckoutFailure(username, password);
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastIdleTestFailureStackTrace(String username, String password) throws SQLException
+ {
+ Throwable t = getLastIdleTestFailure(username, password);
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public String sampleLastConnectionTestFailureStackTrace(String username, String password) throws SQLException
+ {
+ Throwable t = getLastConnectionTestFailure(username, password);
+ return t == null ? null : ThrowableUtils.extractStackTrace( t );
+ }
+
+ public void softResetDefaultUser() throws SQLException
+ { getPoolManager().getPool().reset(); }
+
+ public int getNumConnections(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumConnections(); }
+
+ public int getNumIdleConnections(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumIdleConnections(); }
+
+ public int getNumBusyConnections(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumBusyConnections(); }
+
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumUnclosedOrphanedConnections(); }
+
+ public int getStatementCacheNumStatements(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getStatementCacheNumStatements(); }
+
+ public int getStatementCacheNumCheckedOut(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getStatementCacheNumCheckedOut(); }
+
+ public int getStatementCacheNumConnectionsWithCachedStatements(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getStatementCacheNumConnectionsWithCachedStatements(); }
+
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getEffectivePropertyCycle(); }
+
+ public long getStartTimeMillis(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getStartTime(); }
+
+ public long getUpTimeMillis(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getUpTime(); }
+
+ public long getNumFailedCheckins(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumFailedCheckins(); }
+
+ public long getNumFailedCheckouts(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumFailedCheckouts(); }
+
+ public long getNumFailedIdleTests(String username, String password) throws SQLException
+ { return assertAuthPool(username, password).getNumFailedIdleTests(); }
+
+ public void softReset(String username, String password) throws SQLException
+ { assertAuthPool(username, password).reset(); }
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException
+ { return getPoolManager().getNumBusyConnectionsAllAuths(); }
+
+ public int getNumIdleConnectionsAllUsers() throws SQLException
+ { return getPoolManager().getNumIdleConnectionsAllAuths(); }
+
+ public int getNumConnectionsAllUsers() throws SQLException
+ { return getPoolManager().getNumConnectionsAllAuths(); }
+
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException
+ { return getPoolManager().getNumUnclosedOrphanedConnectionsAllAuths(); }
+
+ public int getStatementCacheNumStatementsAllUsers() throws SQLException
+ { return getPoolManager().getStatementCacheNumStatementsAllUsers(); }
+
+ public int getStatementCacheNumCheckedOutStatementsAllUsers() throws SQLException
+ { return getPoolManager().getStatementCacheNumCheckedOutStatementsAllUsers(); }
+
+ public synchronized int getStatementCacheNumConnectionsWithCachedStatementsAllUsers() throws SQLException
+ { return getPoolManager().getStatementCacheNumConnectionsWithCachedStatementsAllUsers(); }
+
+ public void softResetAllUsers() throws SQLException
+ { getPoolManager().softResetAllAuths(); }
+
+ public int getNumUserPools() throws SQLException
+ { return getPoolManager().getNumManagedAuths(); }
+
+ public Collection getAllUsers() throws SQLException
+ {
+ LinkedList out = new LinkedList();
+ Set auths = getPoolManager().getManagedAuths();
+ for ( Iterator ii = auths.iterator(); ii.hasNext(); )
+ out.add( ((DbAuth) ii.next()).getUser() );
+ return Collections.unmodifiableList( out );
+ }
+
+ public synchronized void hardReset()
+ {
+ resetPoolManager();
+ }
+
+ public synchronized void close()
+ {
+ resetPoolManager();
+ is_closed = true;
+
+ C3P0Registry.markClosed(this);
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable(MLevel.FINEST))
+ {
+ logger.log(MLevel.FINEST,
+ this.getClass().getName() + '@' + Integer.toHexString( System.identityHashCode( this ) ) +
+ " has been closed. ",
+ new Exception("DEBUG STACK TRACE for PoolBackedDataSource.close()."));
+ }
+ }
+
+ /**
+ * @deprecated the force_destroy argument is now meaningless, as pools are no longer
+ * potentially shared between multiple DataSources.
+ */
+ public void close(boolean force_destroy)
+ { close(); }
+
+ //other code
+ public synchronized void resetPoolManager() //used by other, wrapping datasources in package, and in mbean package
+ { resetPoolManager( true ); }
+
+ public synchronized void resetPoolManager( boolean close_checked_out_connections ) //used by other, wrapping datasources in package, and in mbean package
+ {
+ if ( poolManager != null )
+ {
+ poolManager.close( close_checked_out_connections );
+ poolManager = null;
+ }
+ }
+
+ private synchronized ConnectionPoolDataSource assertCpds() throws SQLException
+ {
+ if ( is_closed )
+ throw new SQLException(this + " has been closed() -- you can no longer use it.");
+
+ ConnectionPoolDataSource out = this.getConnectionPoolDataSource();
+ if ( out == null )
+ throw new SQLException(NO_CPDS_ERR_MSG);
+ return out;
+ }
+
+ private synchronized C3P0PooledConnectionPoolManager getPoolManager() throws SQLException
+ {
+ if (poolManager == null)
+ {
+ ConnectionPoolDataSource cpds = assertCpds();
+ poolManager = new C3P0PooledConnectionPoolManager(cpds, null, null, this.getNumHelperThreads(), this.getIdentityToken());
+ if (logger.isLoggable(MLevel.INFO))
+ logger.info("Initializing c3p0 pool... " + this.toString() /* + "; using pool manager: " + poolManager */);
+ }
+ return poolManager;
+ }
+
+ private C3P0PooledConnectionPool assertAuthPool( String username, String password ) throws SQLException
+ {
+ C3P0PooledConnectionPool authPool = getPoolManager().getPool(username, password, false);
+ if (authPool == null)
+ throw new SQLException("No pool has been yet been established for Connections authenticated by user '" +
+ username + "' with the password provided. [Use getConnection( username, password ) " +
+ "to initialize such a pool.]");
+ else
+ return authPool;
+ }
+
+ // serialization stuff -- set up bound/constrained property event handlers on deserialization
+ private static final long serialVersionUID = 1;
+ private static final short VERSION = 0x0001;
+
+ private void writeObject( ObjectOutputStream oos ) throws IOException
+ {
+ oos.writeShort( VERSION );
+ }
+
+ private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ short version = ois.readShort();
+ switch (version)
+ {
+ case VERSION:
+ setUpPropertyEvents();
+ break;
+ default:
+ throw new IOException("Unsupported Serialized Version: " + version);
+ }
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/AuthMaskingProperties.java b/src/classes/com/mchange/v2/c3p0/impl/AuthMaskingProperties.java
new file mode 100644
index 0000000..5f795ab
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/AuthMaskingProperties.java
@@ -0,0 +1,67 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+public class AuthMaskingProperties extends Properties
+{
+ public AuthMaskingProperties()
+ { super(); }
+
+ public AuthMaskingProperties( Properties p )
+ { super( p ); }
+
+ public static AuthMaskingProperties fromAnyProperties( Properties p )
+ {
+ AuthMaskingProperties out = new AuthMaskingProperties();
+ for( Enumeration e = p.propertyNames(); e.hasMoreElements(); )
+ {
+ String key = (String) e.nextElement();
+ out.setProperty( key, p.getProperty( key ) );
+ }
+ return out;
+ }
+
+ private String normalToString()
+ { return super.toString(); }
+
+ public String toString()
+ {
+ boolean hasUser = (this.get("user") != null);
+ boolean hasPassword = (this.get("password") != null);
+ if ( hasUser || hasPassword )
+ {
+ AuthMaskingProperties clone = (AuthMaskingProperties) this.clone();
+ if (hasUser)
+ clone.put("user", "******");
+ if (hasPassword)
+ clone.put("password", "******");
+ return clone.normalToString();
+ }
+ else
+ return this.normalToString();
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0Defaults.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0Defaults.java
new file mode 100644
index 0000000..9358b44
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0Defaults.java
@@ -0,0 +1,207 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.lang.reflect.*;
+import java.util.*;
+import com.mchange.v2.c3p0.ConnectionTester;
+
+// all public static methods should have the name of a c3p0 config property and
+// return its default value
+public final class C3P0Defaults
+{
+ private final static int MAX_STATEMENTS = 0;
+ private final static int MAX_STATEMENTS_PER_CONNECTION = 0;
+ private final static int INITIAL_POOL_SIZE = 3;
+ private final static int MIN_POOL_SIZE = 3;
+ private final static int MAX_POOL_SIZE = 15;
+ private final static int IDLE_CONNECTION_TEST_PERIOD = 0; //idle connections never tested
+ private final static int MAX_IDLE_TIME = 0; //seconds, 0 means connections never expire
+ private final static int PROPERTY_CYCLE = 0; //seconds
+ private final static int ACQUIRE_INCREMENT = 3;
+ private final static int ACQUIRE_RETRY_ATTEMPTS = 30;
+ private final static int ACQUIRE_RETRY_DELAY = 1000; //milliseconds
+ private final static int CHECKOUT_TIMEOUT = 0; //milliseconds
+ private final static int MAX_ADMINISTRATIVE_TASK_TIME = 0; //seconds
+ private final static int MAX_IDLE_TIME_EXCESS_CONNECTIONS = 0; //seconds
+ private final static int MAX_CONNECTION_AGE = 0; //seconds
+ private final static int UNRETURNED_CONNECTION_TIMEOUT = 0; //seconds
+
+ private final static boolean BREAK_AFTER_ACQUIRE_FAILURE = false;
+ private final static boolean TEST_CONNECTION_ON_CHECKOUT = false;
+ private final static boolean TEST_CONNECTION_ON_CHECKIN = false;
+ private final static boolean AUTO_COMMIT_ON_CLOSE = false;
+ private final static boolean FORCE_IGNORE_UNRESOLVED_TXNS = false;
+ private final static boolean USES_TRADITIONAL_REFLECTIVE_PROXIES = false;
+ private final static boolean DEBUG_UNRETURNED_CONNECTION_STACK_TRACES = false;
+
+ private final static ConnectionTester CONNECTION_TESTER = new DefaultConnectionTester();
+
+ private final static int NUM_HELPER_THREADS = 3;
+
+ private final static String AUTOMATIC_TEST_TABLE = null;
+ private final static String CONNECTION_CUSTOMIZER_CLASS_NAME = null;
+ private final static String DRIVER_CLASS = null;
+ private final static String JDBC_URL = null;
+ private final static String OVERRIDE_DEFAULT_USER = null;
+ private final static String OVERRIDE_DEFAULT_PASSWORD = null;
+ private final static String PASSWORD = null;
+ private final static String PREFERRED_TEST_QUERY = null;
+ private final static String FACTORY_CLASS_LOCATION = null;
+ private final static String USER_OVERRIDES_AS_STRING = null;
+ private final static String USER = null;
+
+ private static Set KNOWN_PROPERTIES;
+
+ static
+ {
+ Method[] methods = C3P0Defaults.class.getMethods();
+ Set s = new HashSet();
+ for (int i = 0, len = methods.length; i < len; ++i)
+ {
+ Method m = methods[i];
+ if (Modifier.isStatic( m.getModifiers() ) && m.getParameterTypes().length == 0)
+ s.add( m.getName() );
+ }
+ KNOWN_PROPERTIES = Collections.unmodifiableSet( s );
+ }
+
+ public static Set getKnownProperties()
+ { return KNOWN_PROPERTIES; }
+
+ public static boolean isKnownProperty( String s )
+ { return KNOWN_PROPERTIES.contains( s ); }
+
+ public static int maxStatements()
+ { return MAX_STATEMENTS; }
+
+ public static int maxStatementsPerConnection()
+ { return MAX_STATEMENTS_PER_CONNECTION; }
+
+ public static int initialPoolSize()
+ { return INITIAL_POOL_SIZE; }
+
+ public static int minPoolSize()
+ { return MIN_POOL_SIZE; }
+
+ public static int maxPoolSize()
+ { return MAX_POOL_SIZE; }
+
+ public static int idleConnectionTestPeriod()
+ { return IDLE_CONNECTION_TEST_PERIOD; }
+
+ public static int maxIdleTime()
+ { return MAX_IDLE_TIME; }
+
+ public static int unreturnedConnectionTimeout()
+ { return UNRETURNED_CONNECTION_TIMEOUT; }
+
+ public static int propertyCycle()
+ { return PROPERTY_CYCLE; }
+
+ public static int acquireIncrement()
+ { return ACQUIRE_INCREMENT; }
+
+ public static int acquireRetryAttempts()
+ { return ACQUIRE_RETRY_ATTEMPTS; }
+
+ public static int acquireRetryDelay()
+ { return ACQUIRE_RETRY_DELAY; }
+
+ public static int checkoutTimeout()
+ { return CHECKOUT_TIMEOUT; }
+
+ public static String connectionCustomizerClassName()
+ { return CONNECTION_CUSTOMIZER_CLASS_NAME; }
+
+ public static ConnectionTester connectionTester()
+ { return CONNECTION_TESTER; }
+
+ public static String connectionTesterClassName()
+ { return CONNECTION_TESTER.getClass().getName(); }
+
+ public static String automaticTestTable()
+ { return AUTOMATIC_TEST_TABLE; }
+
+ public static String driverClass()
+ { return DRIVER_CLASS; }
+
+ public static String jdbcUrl()
+ { return JDBC_URL; }
+
+ public static int numHelperThreads()
+ { return NUM_HELPER_THREADS; }
+
+ public static boolean breakAfterAcquireFailure()
+ { return BREAK_AFTER_ACQUIRE_FAILURE; }
+
+ public static boolean testConnectionOnCheckout()
+ { return TEST_CONNECTION_ON_CHECKOUT; }
+
+ public static boolean testConnectionOnCheckin()
+ { return TEST_CONNECTION_ON_CHECKIN; }
+
+ public static boolean autoCommitOnClose()
+ { return AUTO_COMMIT_ON_CLOSE; }
+
+ public static boolean forceIgnoreUnresolvedTransactions()
+ { return FORCE_IGNORE_UNRESOLVED_TXNS; }
+
+ public static boolean debugUnreturnedConnectionStackTraces()
+ { return DEBUG_UNRETURNED_CONNECTION_STACK_TRACES; }
+
+ public static boolean usesTraditionalReflectiveProxies()
+ { return USES_TRADITIONAL_REFLECTIVE_PROXIES; }
+
+ public static String preferredTestQuery()
+ { return PREFERRED_TEST_QUERY; }
+
+ public static String userOverridesAsString()
+ { return USER_OVERRIDES_AS_STRING; }
+
+ public static String factoryClassLocation()
+ { return FACTORY_CLASS_LOCATION; }
+
+ public static String overrideDefaultUser()
+ { return OVERRIDE_DEFAULT_USER; }
+
+ public static String overrideDefaultPassword()
+ { return OVERRIDE_DEFAULT_PASSWORD; }
+
+ public static String user()
+ { return USER; }
+
+ public static String password()
+ { return PASSWORD; }
+
+ public static int maxAdministrativeTaskTime()
+ { return MAX_ADMINISTRATIVE_TASK_TIME; }
+
+ public static int maxIdleTimeExcessConnections()
+ { return MAX_IDLE_TIME_EXCESS_CONNECTIONS; }
+
+ public static int maxConnectionAge()
+ { return MAX_CONNECTION_AGE; }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java
new file mode 100644
index 0000000..459e6be
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java
@@ -0,0 +1,381 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.beans.*;
+import java.util.*;
+import java.lang.reflect.*;
+import java.net.InetAddress;
+
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.cfg.MultiPropertiesConfig;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.sql.Connection;
+import java.sql.SQLException;
+import com.mchange.lang.ByteUtils;
+import com.mchange.v2.encounter.EncounterCounter;
+import com.mchange.v2.encounter.EqualityEncounterCounter;
+import com.mchange.v2.lang.VersionUtils;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.ser.SerializableUtils;
+import com.mchange.v2.sql.SqlUtils;
+
+public final class C3P0ImplUtils
+{
+ // turning this on would only test to generate long tokens
+ // on 64 bit machines, but since identityHashCode() is not
+ // GUARANTEED unique under 32-bit JVMs, even if in practice
+ // always is, we always test to be sure we're not reusing
+ // an identity token.
+ private final static boolean CONDITIONAL_LONG_TOKENS = false;
+
+ final static MLogger logger = MLog.getLogger( C3P0ImplUtils.class );
+
+ public final static DbAuth NULL_AUTH = new DbAuth(null,null);
+
+ public final static Object[] NOARGS = new Object[0];
+
+ private final static EncounterCounter ID_TOKEN_COUNTER;
+
+ static
+ {
+ if (CONDITIONAL_LONG_TOKENS)
+ {
+ boolean long_tokens;
+ Integer jnb = VersionUtils.jvmNumberOfBits();
+ if (jnb == null)
+ long_tokens = true;
+ else if (jnb.intValue() > 32)
+ long_tokens = true;
+ else
+ long_tokens = false;
+
+ if (long_tokens)
+ ID_TOKEN_COUNTER = new EqualityEncounterCounter();
+ else
+ ID_TOKEN_COUNTER = null;
+ }
+ else
+ ID_TOKEN_COUNTER = new EqualityEncounterCounter();
+ }
+
+ public final static String VMID_PROPKEY = "com.mchange.v2.c3p0.VMID";
+ private final static String VMID_PFX;
+
+ static
+ {
+ String vmid = MultiPropertiesConfig.readVmConfig().getProperty(VMID_PROPKEY);
+ if (vmid == null || (vmid = vmid.trim()).equals("") || vmid.equals("AUTO"))
+ VMID_PFX = generateVmId() + '|';
+ else if (vmid.equals("NONE"))
+ VMID_PFX = "";
+ else
+ VMID_PFX = vmid + "|";
+ }
+
+ //MT: protected by class' lock
+ static String connectionTesterClassName = null;
+ static ConnectionTester cachedTester = null;
+
+ private static String generateVmId()
+ {
+ DataOutputStream dos = null;
+ DataInputStream dis = null;
+ try
+ {
+ SecureRandom srand = new SecureRandom();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ dos = new DataOutputStream( baos );
+ try
+ {
+ dos.write( InetAddress.getLocalHost().getAddress() );
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.INFO))
+ logger.log(MLevel.INFO, "Failed to get local InetAddress for VMID. This is unlikely to matter. At all. We'll add some extra randomness", e);
+ dos.write( srand.nextInt() );
+ }
+ dos.writeLong(System.currentTimeMillis());
+ dos.write( srand.nextInt() );
+
+ int remainder = baos.size() % 4; //if it wasn't a 4 byte inet address
+ if (remainder > 0)
+ {
+ int pad = 4 - remainder;
+ byte[] pad_bytes = new byte[pad];
+ srand.nextBytes(pad_bytes);
+ dos.write(pad_bytes);
+ }
+
+ StringBuffer sb = new StringBuffer(32);
+ byte[] vmid_bytes = baos.toByteArray();
+ dis = new DataInputStream(new ByteArrayInputStream( vmid_bytes ) );
+ for (int i = 0, num_ints = vmid_bytes.length / 4; i < num_ints; ++i)
+ {
+ int signed = dis.readInt();
+ long unsigned = ((long) signed) & 0x00000000FFFFFFFFL;
+ sb.append(Long.toString(unsigned, Character.MAX_RADIX));
+ }
+ return sb.toString();
+ }
+ catch (IOException e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "Bizarro! IOException while reading/writing from ByteArray-based streams? " +
+ "We're skipping the VMID thing. It almost certainly doesn't matter, " +
+ "but please report the error.",
+ e);
+ return "";
+ }
+ finally
+ {
+ // this is like total overkill for byte-array based streams,
+ // but it's a good habit
+ try { if (dos != null) dos.close(); }
+ catch ( IOException e )
+ { logger.log(MLevel.WARNING, "Huh? Exception close()ing a byte-array bound OutputStream.", e); }
+ try { if (dis != null) dis.close(); }
+ catch ( IOException e )
+ { logger.log(MLevel.WARNING, "Huh? Exception close()ing a byte-array bound IntputStream.", e); }
+ }
+ }
+
+ // identityHashCode() is not a sufficient unique token, because they are
+ // not guaranteed unique, and in practice are occasionally not unique,
+ // particularly on 64-bit systems.
+
+ public static String allocateIdentityToken(Object o)
+ {
+ if (o == null)
+ return null;
+ else
+ {
+ String shortIdToken = Integer.toString( System.identityHashCode( o ), 16 );
+
+ //new Exception( "DEBUG_STACK_TRACE: " + o.getClass().getName() + " " + shortIdToken ).printStackTrace();
+
+ String out;
+ long count;
+ StringBuffer sb = new StringBuffer(128);
+ sb.append(VMID_PFX);
+ if (ID_TOKEN_COUNTER != null && ((count = ID_TOKEN_COUNTER.encounter( shortIdToken )) > 0))
+ {
+ sb.append( shortIdToken );
+ sb.append('#');
+ sb.append( count );
+ }
+ else
+ sb.append(shortIdToken);
+
+ out = sb.toString().intern();
+
+ return out;
+ }
+ }
+
+ public static DbAuth findAuth(Object o)
+ throws SQLException
+ {
+ if ( o == null )
+ return NULL_AUTH;
+
+ String user = null;
+ String password = null;
+
+ String overrideDefaultUser = null;
+ String overrideDefaultPassword = null;
+
+ try
+ {
+ BeanInfo bi = Introspector.getBeanInfo( o.getClass() );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ Class propCl = pd.getPropertyType();
+ String propName = pd.getName();
+ if (propCl == String.class)
+ {
+// System.err.println( "---> " + propName );
+// System.err.println( o.getClass() );
+// System.err.println( pd.getReadMethod() );
+
+ Method readMethod = pd.getReadMethod();
+ if (readMethod != null)
+ {
+ Object propVal = readMethod.invoke( o, NOARGS );
+ String value = (String) propVal;
+ if ("user".equals(propName))
+ user = value;
+ else if ("password".equals(propName))
+ password = value;
+ else if ("overrideDefaultUser".equals(propName))
+ overrideDefaultUser = value;
+ else if ("overrideDefaultPassword".equals(propName))
+ overrideDefaultPassword = value;
+ }
+ }
+ }
+ if (overrideDefaultUser != null)
+ return new DbAuth( overrideDefaultUser, overrideDefaultPassword );
+ else if (user != null)
+ return new DbAuth( user, password );
+ else
+ return NULL_AUTH;
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "An exception occurred while trying to extract the default authentification info from a bean.", e );
+ throw SqlUtils.toSQLException(e);
+ }
+ }
+
+ static void resetTxnState( Connection pCon,
+ boolean forceIgnoreUnresolvedTransactions,
+ boolean autoCommitOnClose,
+ boolean txnKnownResolved ) throws SQLException
+ {
+ if ( !forceIgnoreUnresolvedTransactions && !pCon.getAutoCommit() )
+ {
+ if (! autoCommitOnClose && ! txnKnownResolved)
+ {
+ //System.err.println("Rolling back potentially unresolved txn...");
+ pCon.rollback();
+ }
+ pCon.setAutoCommit( true ); //implies commit if not already rolled back.
+ }
+ }
+
+ public synchronized static ConnectionTester defaultConnectionTester()
+ {
+ String dfltCxnTesterClassName = PoolConfig.defaultConnectionTesterClassName();
+ if ( connectionTesterClassName != null && connectionTesterClassName.equals(dfltCxnTesterClassName) )
+ return cachedTester;
+ else
+ {
+ try
+ {
+ cachedTester = (ConnectionTester) Class.forName( dfltCxnTesterClassName ).newInstance();
+ connectionTesterClassName = cachedTester.getClass().getName();
+ }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log(MLevel.WARNING,
+ "Could not load ConnectionTester " + dfltCxnTesterClassName + ", using built in default.",
+ e);
+ cachedTester = C3P0Defaults.connectionTester();
+ connectionTesterClassName = cachedTester.getClass().getName();
+ }
+ return cachedTester;
+ }
+ }
+
+ public static boolean supportsMethod(Object target, String mname, Class[] argTypes)
+ {
+ try {return (target.getClass().getMethod( mname, argTypes ) != null); }
+ catch ( NoSuchMethodException e )
+ { return false; }
+ catch (SecurityException e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log(MLevel.FINE,
+ "We were denied access in a check of whether " + target + " supports method " + mname +
+ ". Prob means external clients have no access, returning false.",
+ e);
+ return false;
+ }
+ }
+
+ private final static String HASM_HEADER = "HexAsciiSerializedMap";
+
+ public static String createUserOverridesAsString( Map userOverrides ) throws IOException
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(HASM_HEADER);
+ sb.append('[');
+ sb.append( ByteUtils.toHexAscii( SerializableUtils.toByteArray( userOverrides ) ) );
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static Map parseUserOverridesAsString( String userOverridesAsString ) throws IOException, ClassNotFoundException
+ {
+ if (userOverridesAsString != null)
+ {
+ String hexAscii = userOverridesAsString.substring(HASM_HEADER.length() + 1, userOverridesAsString.length() - 1);
+ byte[] serBytes = ByteUtils.fromHexAscii( hexAscii );
+ return Collections.unmodifiableMap( (Map) SerializableUtils.fromByteArray( serBytes ) );
+ }
+ else
+ return Collections.EMPTY_MAP;
+ }
+
+ private C3P0ImplUtils()
+ {}
+}
+
+
+
+// Class methodClass = readMethod.getDeclaringClass();
+// Package methodPkg = methodClass.getPackage();
+// System.err.println( methodPkg.getName() + '\t' + C3P0ImplUtils.class.getPackage().getName() );
+// if (! methodPkg.getName().equals(
+// C3P0ImplUtils.class.getPackage().getName() ) )
+// {
+// System.err.println("public check: " + (methodClass.getModifiers() & Modifier.PUBLIC));
+// if ((methodClass.getModifiers() & Modifier.PUBLIC) == 0)
+// {
+// System.err.println("SKIPPED -- Can't Access!");
+// continue;
+// }
+// }
+// System.err.println( o );
+
+ /*
+ private final static ThreadLocal threadLocalConnectionCustomizer = new ThreadLocal();
+
+ // used so that C3P0PooledConnectionPool can pass a ConnectionCustomizer
+ // to WrapperConnectionPoolDataSource without altering that class' public API
+ public static void setThreadConnectionCustomizer(ConnectionCustomizer cc)
+ { threadLocalConnectionCustomizer.set( cc ); }
+
+ public static ConnectionCustomizer getThreadConnectionCustomizer()
+ { return threadLocalConnectionCustomizer.get(); }
+
+ public static void unsetThreadConnectionCustomizer()
+ { setThreadConnectionCustomizer( null ); }
+ */
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0JavaBeanObjectFactory.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0JavaBeanObjectFactory.java
new file mode 100644
index 0000000..968635c
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0JavaBeanObjectFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.Set;
+import com.mchange.v2.c3p0.C3P0Registry;
+import com.mchange.v2.naming.JavaBeanObjectFactory;
+
+public class C3P0JavaBeanObjectFactory extends JavaBeanObjectFactory
+{
+ private final static Class[] CTOR_ARG_TYPES = new Class[] { boolean.class };
+ private final static Object[] CTOR_ARGS = new Object[] { Boolean.FALSE };
+
+ protected Object createBlankInstance(Class beanClass) throws Exception
+ {
+ if ( IdentityTokenized.class.isAssignableFrom( beanClass ) )
+ {
+ Constructor ctor = beanClass.getConstructor( CTOR_ARG_TYPES );
+ return ctor.newInstance( CTOR_ARGS );
+ }
+ else
+ return super.createBlankInstance( beanClass );
+ }
+
+ protected Object findBean(Class beanClass, Map propertyMap, Set refProps ) throws Exception
+ {
+ Object out = super.findBean( beanClass, propertyMap, refProps );
+ if (out instanceof IdentityTokenized)
+ out = C3P0Registry.reregister( (IdentityTokenized) out );
+ //System.err.println("--> findBean()");
+ //System.err.println(out);
+ return out;
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnection.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnection.java
new file mode 100644
index 0000000..db10ed0
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnection.java
@@ -0,0 +1,1179 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.lang.reflect.*;
+import java.sql.*;
+import java.util.*;
+import javax.sql.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.sql.*;
+import com.mchange.v2.sql.filter.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.c3p0.stmt.*;
+import com.mchange.v2.c3p0.C3P0ProxyConnection;
+import com.mchange.v2.c3p0.util.ConnectionEventSupport;
+import com.mchange.v2.lang.ObjectUtils;
+
+public final class C3P0PooledConnection extends AbstractC3P0PooledConnection
+{
+ final static MLogger logger = MLog.getLogger( C3P0PooledConnection.class );
+
+ final static Class[] PROXY_CTOR_ARGS = new Class[]{ InvocationHandler.class };
+
+ final static Constructor CON_PROXY_CTOR;
+
+ final static Method RS_CLOSE_METHOD;
+ final static Method STMT_CLOSE_METHOD;
+
+ final static Object[] CLOSE_ARGS;
+
+ final static Set OBJECT_METHODS;
+
+ /**
+ * @deprecated use or rewrite in terms of ReflectUtils.findProxyConstructor()
+ */
+ private static Constructor createProxyConstructor(Class intfc) throws NoSuchMethodException
+ {
+ Class[] proxyInterfaces = new Class[] { intfc };
+ Class proxyCl = Proxy.getProxyClass(C3P0PooledConnection.class.getClassLoader(), proxyInterfaces);
+ return proxyCl.getConstructor( PROXY_CTOR_ARGS );
+ }
+
+ static
+ {
+ try
+ {
+ CON_PROXY_CTOR = createProxyConstructor( ProxyConnection.class );
+
+ Class[] argClasses = new Class[0];
+ RS_CLOSE_METHOD = ResultSet.class.getMethod("close", argClasses);
+ STMT_CLOSE_METHOD = Statement.class.getMethod("close", argClasses);
+
+ CLOSE_ARGS = new Object[0];
+
+ OBJECT_METHODS = Collections.unmodifiableSet( new HashSet( Arrays.asList( Object.class.getMethods() ) ) );
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ logger.log(MLevel.SEVERE, "An Exception occurred in static initializer of" + C3P0PooledConnection.class.getName(), e);
+ throw new InternalError("Something is very wrong, or this is a pre 1.3 JVM." +
+ "We cannot set up dynamic proxies and/or methods!");
+ }
+ }
+
+ //MT: post-constructor constants
+ final ConnectionTester connectionTester;
+ final boolean autoCommitOnClose;
+ final boolean forceIgnoreUnresolvedTransactions;
+ final boolean supports_setTypeMap;
+ final boolean supports_setHoldability;
+ final int dflt_txn_isolation;
+ final String dflt_catalog;
+ final int dflt_holdability;
+
+ //MT: thread-safe
+ final ConnectionEventSupport ces = new ConnectionEventSupport(this);
+
+ //MT: threadsafe, but reassigned (on close)
+ volatile Connection physicalConnection;
+ volatile Exception invalidatingException = null;
+
+ //MT: threadsafe, but reassigned, and a read + reassignment must happen
+ // atomically. protected by this' lock.
+ ProxyConnection exposedProxy;
+
+ //MT: protected by this' lock
+ int connection_status = ConnectionTester.CONNECTION_IS_OKAY;
+
+ /*
+ * contains all unclosed Statements not managed by a StatementCache
+ * associated with the physical connection
+ *
+ * MT: protected by its own lock, not reassigned
+ */
+ final Set uncachedActiveStatements = Collections.synchronizedSet( new HashSet() );
+
+ //MT: Thread-safe, assigned
+ volatile GooGooStatementCache scache;
+ volatile boolean isolation_lvl_nondefault = false;
+ volatile boolean catalog_nondefault = false;
+ volatile boolean holdability_nondefault = false;
+
+ public C3P0PooledConnection(Connection con,
+ ConnectionTester connectionTester,
+ boolean autoCommitOnClose,
+ boolean forceIgnoreUnresolvedTransactions,
+ ConnectionCustomizer cc,
+ String pdsIdt) throws SQLException
+ {
+ try
+ {
+ if (cc != null)
+ cc.onAcquire( con, pdsIdt );
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException(e); }
+
+ this.physicalConnection = con;
+ this.connectionTester = connectionTester;
+ this.autoCommitOnClose = autoCommitOnClose;
+ this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions;
+ this.supports_setTypeMap = C3P0ImplUtils.supportsMethod(con, "setTypeMap", new Class[]{ Map.class });
+ this.supports_setHoldability = C3P0ImplUtils.supportsMethod(con, "setHoldability", new Class[]{ int.class });
+ this.dflt_txn_isolation = con.getTransactionIsolation();
+ this.dflt_catalog = con.getCatalog();
+ this.dflt_holdability = (supports_setHoldability ? con.getHoldability() : ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ }
+
+ //used by C3P0PooledConnectionPool
+ Connection getPhysicalConnection()
+ { return physicalConnection; }
+
+ boolean isClosed() throws SQLException
+ { return (physicalConnection == null); }
+
+ void initStatementCache( GooGooStatementCache scache )
+ { this.scache = scache; }
+
+
+ //DEBUG
+ //Exception origGet = null;
+
+ // synchronized to protect exposedProxy
+ public synchronized Connection getConnection()
+ throws SQLException
+ {
+ if ( exposedProxy != null)
+ {
+ //DEBUG
+ //System.err.println("[DOUBLE_GET_TESTER] -- double getting a Connection from " + this );
+ //new Exception("[DOUBLE_GET_TESTER] -- Double-Get Stack Trace").printStackTrace();
+ //origGet.printStackTrace();
+
+// System.err.println("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
+// "it had already provided a client with a Connection that has not yet been " +
+// "closed. This probably indicates a bug in the connection pool!!!");
+
+ logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
+ "it had already provided a client with a Connection that has not yet been " +
+ "closed. This probably indicates a bug in the connection pool!!!");
+
+ return exposedProxy;
+ }
+ else
+ { return getCreateNewConnection(); }
+ }
+
+ // must be called from sync'ed method to protecte
+ // exposedProxy
+ private Connection getCreateNewConnection()
+ throws SQLException
+ {
+ try
+ {
+ //DEBUG
+ //origGet = new Exception("[DOUBLE_GET_TESTER] -- Orig Get");
+
+ ensureOkay();
+ /*
+ * we reset the physical connection when we close an exposed proxy
+ * no need to do it again when we create one
+ */
+ //reset();
+ return (exposedProxy = createProxyConnection());
+ }
+ catch (SQLException e)
+ { throw e; }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ logger.log(MLevel.WARNING, "Failed to acquire connection!", e);
+ throw new SQLException("Failed to acquire connection!");
+ }
+ }
+
+ public void closeAll() throws SQLException
+ {
+ if (scache != null)
+ scache.closeAll( physicalConnection );
+ }
+
+ public void close() throws SQLException
+ { this.close( false ); }
+
+ //TODO: factor out repetitive debugging code
+ private synchronized void close(boolean known_invalid) throws SQLException
+ {
+ //System.err.println("Closing " + this);
+ if ( physicalConnection != null )
+ {
+ try
+ {
+ StringBuffer debugOnlyLog = null;
+ if ( Debug.DEBUG && known_invalid )
+ {
+ debugOnlyLog = new StringBuffer();
+ debugOnlyLog.append("[ exceptions: ");
+ }
+
+ Exception exc = cleanupUncachedActiveStatements();
+ if (Debug.DEBUG && exc != null)
+ {
+ if (known_invalid)
+ debugOnlyLog.append( exc.toString() + ' ' );
+ else
+ logger.log(MLevel.WARNING, "An exception occurred while cleaning up uncached active Statements.", exc);
+ //exc.printStackTrace();
+ }
+
+ try
+ {
+ // we've got to use silentClose() rather than close() here,
+ // 'cuz if there's still an exposedProxy (say a user forgot to
+ // close his Connection) before we close, and we use regular (loud)
+ // close, we will try to check this dead or dying PooledConnection
+ // back into the pool. We only want to do this when close is called
+ // on user proxies, and the underlying PooledConnection might still
+ // be good. The PooledConnection itself should only be closed by the
+ // pool.
+ if (exposedProxy != null)
+ exposedProxy.silentClose( known_invalid );
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ {
+ if (known_invalid)
+ debugOnlyLog.append( e.toString() + ' ' );
+ else
+ logger.log(MLevel.WARNING, "An exception occurred.", exc);
+ //e.printStackTrace();
+ }
+ exc = e;
+ }
+ try
+ { this.closeAll(); }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ {
+ if (known_invalid)
+ debugOnlyLog.append( e.toString() + ' ' );
+ else
+ logger.log(MLevel.WARNING, "An exception occurred.", exc);
+ //e.printStackTrace();
+ }
+ exc = e;
+ }
+
+ try { physicalConnection.close(); }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ {
+ if (known_invalid)
+ debugOnlyLog.append( e.toString() + ' ' );
+ else
+ logger.log(MLevel.WARNING, "An exception occurred.", exc);
+ e.printStackTrace();
+ }
+ exc = e;
+ }
+
+ if (exc != null)
+ {
+ if (known_invalid)
+ {
+ debugOnlyLog.append(" ]");
+ if (Debug.DEBUG)
+ {
+// System.err.print("[DEBUG]" + this + ": while closing a PooledConnection known to be invalid, ");
+// System.err.println(" some exceptions occurred. This is probably not a problem:");
+// System.err.println( debugOnlyLog.toString() );
+
+
+ logger.fine(this + ": while closing a PooledConnection known to be invalid, " +
+ " some exceptions occurred. This is probably not a problem: " +
+ debugOnlyLog.toString() );
+ }
+ }
+ else
+ throw new SQLException("At least one error occurred while attempting " +
+ "to close() the PooledConnection: " + exc);
+ }
+ if (Debug.TRACE == Debug.TRACE_MAX)
+ logger.fine("C3P0PooledConnection closed. [" + this + ']');
+ //System.err.println("C3P0PooledConnection closed. [" + this + ']');
+ }
+ finally
+ { physicalConnection = null; }
+ }
+ }
+
+ public void addConnectionEventListener(ConnectionEventListener listener)
+ { ces.addConnectionEventListener( listener ); }
+
+ public void removeConnectionEventListener(ConnectionEventListener listener)
+ { ces.removeConnectionEventListener( listener ); }
+
+ private void reset() throws SQLException
+ { reset( false ); }
+
+ private void reset( boolean known_resolved_txn ) throws SQLException
+ {
+ ensureOkay();
+ C3P0ImplUtils.resetTxnState( physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, known_resolved_txn );
+ if (isolation_lvl_nondefault)
+ {
+ physicalConnection.setTransactionIsolation( dflt_txn_isolation );
+ isolation_lvl_nondefault = false;
+ }
+ if (catalog_nondefault)
+ {
+ physicalConnection.setCatalog( dflt_catalog );
+ catalog_nondefault = false;
+ }
+ if (holdability_nondefault) //we don't test if holdability is supported, 'cuz it can never go nondefault if it's not.
+ {
+ physicalConnection.setHoldability( dflt_holdability );
+ holdability_nondefault = false;
+ }
+
+ try
+ { physicalConnection.setReadOnly( false ); }
+ catch ( Throwable t )
+ {
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.log(MLevel.FINE, "A Throwable occurred while trying to reset the readOnly property of our Connection to false!", t);
+ }
+
+ try
+ { if (supports_setTypeMap) physicalConnection.setTypeMap( Collections.EMPTY_MAP ); }
+ catch ( Throwable t )
+ {
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.log(MLevel.FINE, "A Throwable occurred while trying to reset the typeMap property of our Connection to Collections.EMPTY_MAP!", t);
+ }
+ }
+
+ boolean closeAndRemoveResultSets(Set rsSet)
+ {
+ boolean okay = true;
+ synchronized (rsSet)
+ {
+ for (Iterator ii = rsSet.iterator(); ii.hasNext(); )
+ {
+ ResultSet rs = (ResultSet) ii.next();
+ try
+ { rs.close(); }
+ catch (SQLException e)
+ {
+ if (Debug.DEBUG)
+ logger.log(MLevel.WARNING, "An exception occurred while cleaning up a ResultSet.", e);
+ //e.printStackTrace();
+ okay = false;
+ }
+ finally
+ { ii.remove(); }
+ }
+ }
+ return okay;
+ }
+
+ void ensureOkay() throws SQLException
+ {
+ if (physicalConnection == null)
+ throw new SQLException( invalidatingException == null ?
+ "Connection is closed or broken." :
+ "Connection is broken. Invalidating Exception: " + invalidatingException.toString());
+ }
+
+ boolean closeAndRemoveResourcesInSet(Set s, Method closeMethod)
+ {
+ boolean okay = true;
+
+ Set temp;
+ synchronized (s)
+ { temp = new HashSet( s ); }
+
+ for (Iterator ii = temp.iterator(); ii.hasNext(); )
+ {
+ Object rsrc = ii.next();
+ try
+ { closeMethod.invoke(rsrc, CLOSE_ARGS); }
+ catch (Exception e)
+ {
+ Throwable t = e;
+ if (t instanceof InvocationTargetException)
+ t = ((InvocationTargetException) e).getTargetException();
+ logger.log(MLevel.WARNING, "An exception occurred while cleaning up a resource.", t);
+ //t.printStackTrace();
+ okay = false;
+ }
+ finally
+ { s.remove( rsrc ); }
+ }
+
+ // We had to abandon the idea of simply iterating over s directly, because
+ // our resource close methods sometimes try to remove the resource from
+ // its parent Set. This is important (when the user closes the resources
+ // directly), but leads to ConcurrenModificationExceptions while we are
+ // iterating over the Set to close. So, now we iterate over a copy, but remove
+ // from the original Set. Since removal is idempotent, it don't matter if
+ // the close method already provoked a remove. Sucks that we have to copy
+ // the set though.
+ //
+ // Original (direct iteration) version:
+ //
+ // synchronized (s)
+ // {
+ // for (Iterator ii = s.iterator(); ii.hasNext(); )
+ // {
+ // Object rsrc = ii.next();
+ // try
+ // { closeMethod.invoke(rsrc, CLOSE_ARGS); }
+ // catch (Exception e)
+ // {
+ // Throwable t = e;
+ // if (t instanceof InvocationTargetException)
+ // t = ((InvocationTargetException) e).getTargetException();
+ // t.printStackTrace();
+ // okay = false;
+ // }
+ // finally
+ // { ii.remove(); }
+ // }
+ // }
+
+
+ return okay;
+ }
+
+ private SQLException cleanupUncachedActiveStatements()
+ {
+ //System.err.println("IN uncachedActiveStatements.size(): " + uncachedActiveStatements.size());
+
+ boolean okay = closeAndRemoveResourcesInSet(uncachedActiveStatements, STMT_CLOSE_METHOD);
+
+ //System.err.println("OUT uncachedActiveStatements.size(): " + uncachedActiveStatements.size());
+
+ if (okay)
+ return null;
+ else
+ return new SQLException("An exception occurred while trying to " +
+ "clean up orphaned resources.");
+ }
+
+ ProxyConnection createProxyConnection() throws Exception
+ {
+ // we should always have a separate handler for each proxy connection, so
+ // that object methods behave as expected... the handler covers
+ // all object methods on behalf of the proxy.
+ InvocationHandler handler = new ProxyConnectionInvocationHandler();
+ return (ProxyConnection) CON_PROXY_CTOR.newInstance( new Object[] {handler} );
+ }
+
+ Statement createProxyStatement( Statement innerStmt ) throws Exception
+ { return this.createProxyStatement( false, innerStmt ); }
+
+
+ private static class StatementProxyingSetManagedResultSet extends SetManagedResultSet
+ {
+ private Statement proxyStatement;
+
+ StatementProxyingSetManagedResultSet(Set activeResultSets)
+ { super( activeResultSets ); }
+
+ public void setProxyStatement( Statement proxyStatement )
+ { this.proxyStatement = proxyStatement; }
+
+ public Statement getStatement() throws SQLException
+ { return (proxyStatement == null ? super.getStatement() : proxyStatement); }
+ }
+
+ /*
+ * TODO: factor all this convolution out into
+ * C3P0Statement
+ */
+ Statement createProxyStatement( //final Method cachedStmtProducingMethod,
+ //final Object[] cachedStmtProducingMethodArgs,
+ final boolean inner_is_cached,
+ final Statement innerStmt) throws Exception
+ {
+ final Set activeResultSets = Collections.synchronizedSet( new HashSet() );
+ final Connection parentConnection = exposedProxy;
+
+ if (Debug.DEBUG && parentConnection == null)
+ {
+// System.err.print("PROBABLE C3P0 BUG -- ");
+// System.err.println(this + ": created a proxy Statement when there is no active, exposed proxy Connection???");
+
+ logger.warning("PROBABLE C3P0 BUG -- " +
+ this + ": created a proxy Statement when there is no active, exposed proxy Connection???");
+
+ }
+
+
+ //we can use this one wrapper under all circumstances
+ //except jdbc3 CallableStatement multiple open ResultSets...
+ //avoid object allocation in statement methods where possible.
+ final StatementProxyingSetManagedResultSet mainResultSet = new StatementProxyingSetManagedResultSet( activeResultSets );
+
+ class WrapperStatementHelper
+ {
+ Statement wrapperStmt;
+ Statement nakedInner;
+
+ public WrapperStatementHelper(Statement wrapperStmt, Statement nakedInner)
+ {
+ this.wrapperStmt = wrapperStmt;
+ this.nakedInner = nakedInner;
+
+ if (! inner_is_cached)
+ uncachedActiveStatements.add( wrapperStmt );
+ }
+
+ private boolean closeAndRemoveActiveResultSets()
+ { return closeAndRemoveResultSets( activeResultSets ); }
+
+ public ResultSet wrap(ResultSet rs)
+ {
+ if (mainResultSet.getInner() == null)
+ {
+ mainResultSet.setInner(rs);
+ mainResultSet.setProxyStatement( wrapperStmt );
+ return mainResultSet;
+ }
+ else
+ {
+ //for the case of multiple open ResultSets
+ StatementProxyingSetManagedResultSet out
+ = new StatementProxyingSetManagedResultSet( activeResultSets );
+ out.setInner( rs );
+ out.setProxyStatement( wrapperStmt );
+ return out;
+ }
+ }
+
+ public void doClose()
+ throws SQLException
+ {
+ boolean okay = closeAndRemoveActiveResultSets();
+
+ if (inner_is_cached) //this statement was cached
+ scache.checkinStatement( innerStmt );
+ else
+ {
+ innerStmt.close();
+ uncachedActiveStatements.remove( wrapperStmt );
+ }
+
+ if (!okay)
+ throw new SQLException("Failed to close an orphaned ResultSet properly.");
+ }
+
+ public Object doRawStatementOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException, SQLException
+ {
+ if (target == C3P0ProxyStatement.RAW_STATEMENT)
+ target = nakedInner;
+ for (int i = 0, len = args.length; i < len; ++i)
+ if (args[i] == C3P0ProxyStatement.RAW_STATEMENT)
+ args[i] = nakedInner;
+
+ Object out = m.invoke(target, args);
+
+ if (out instanceof ResultSet)
+ out = wrap( (ResultSet) out );
+
+ return out;
+ }
+ }
+
+ if (innerStmt instanceof CallableStatement)
+ {
+ class ProxyCallableStatement extends FilterCallableStatement implements C3P0ProxyStatement
+ {
+ WrapperStatementHelper wsh;
+
+ ProxyCallableStatement(CallableStatement is)
+ {
+ super( is );
+ this.wsh = new WrapperStatementHelper(this, is);
+ }
+
+ public Connection getConnection()
+ { return parentConnection; }
+
+ public ResultSet getResultSet() throws SQLException
+ { return wsh.wrap( super.getResultSet() ); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return wsh.wrap( super.getGeneratedKeys() ); }
+
+ public ResultSet executeQuery(String sql) throws SQLException
+ { return wsh.wrap( super.executeQuery(sql) ); }
+
+ public ResultSet executeQuery() throws SQLException
+ { return wsh.wrap( super.executeQuery() ); }
+
+ public Object rawStatementOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException, SQLException
+ { return wsh.doRawStatementOperation( m, target, args); }
+
+ public void close() throws SQLException
+ { wsh.doClose(); }
+ }
+
+ return new ProxyCallableStatement((CallableStatement) innerStmt );
+ }
+ else if (innerStmt instanceof PreparedStatement)
+ {
+ class ProxyPreparedStatement extends FilterPreparedStatement implements C3P0ProxyStatement
+ {
+ WrapperStatementHelper wsh;
+
+ ProxyPreparedStatement(PreparedStatement ps)
+ {
+ super( ps );
+ this.wsh = new WrapperStatementHelper(this, ps);
+ }
+
+ public Connection getConnection()
+ { return parentConnection; }
+
+ public ResultSet getResultSet() throws SQLException
+ { return wsh.wrap( super.getResultSet() ); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return wsh.wrap( super.getGeneratedKeys() ); }
+
+ public ResultSet executeQuery(String sql) throws SQLException
+ { return wsh.wrap( super.executeQuery(sql) ); }
+
+ public ResultSet executeQuery() throws SQLException
+ { return wsh.wrap( super.executeQuery() ); }
+
+ public Object rawStatementOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException, SQLException
+ { return wsh.doRawStatementOperation( m, target, args); }
+
+ public void close() throws SQLException
+ { wsh.doClose(); }
+ }
+
+ return new ProxyPreparedStatement((PreparedStatement) innerStmt );
+ }
+ else
+ {
+ class ProxyStatement extends FilterStatement implements C3P0ProxyStatement
+ {
+ WrapperStatementHelper wsh;
+
+ ProxyStatement(Statement s)
+ {
+ super( s );
+ this.wsh = new WrapperStatementHelper(this, s);
+ }
+
+ public Connection getConnection()
+ { return parentConnection; }
+
+ public ResultSet getResultSet() throws SQLException
+ { return wsh.wrap( super.getResultSet() ); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return wsh.wrap( super.getGeneratedKeys() ); }
+
+ public ResultSet executeQuery(String sql) throws SQLException
+ { return wsh.wrap( super.executeQuery(sql) ); }
+
+ public Object rawStatementOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException, SQLException
+ { return wsh.doRawStatementOperation( m, target, args); }
+
+ public void close() throws SQLException
+ { wsh.doClose(); }
+ }
+
+ return new ProxyStatement( innerStmt );
+ }
+ }
+
+ final class ProxyConnectionInvocationHandler implements InvocationHandler
+ {
+ //MT: ThreadSafe, but reassigned -- protected by this' lock
+ Connection activeConnection = physicalConnection;
+ DatabaseMetaData metaData = null;
+ boolean connection_error_signaled = false;
+
+ /*
+ * contains all unclosed ResultSets derived from this Connection's metadata
+ * associated with the physical connection
+ *
+ * MT: protected by this' lock
+ */
+ final Set activeMetaDataResultSets = new HashSet();
+
+ //being careful with doRawConnectionOperation
+ //we initialize lazily, because this will be very rarely used
+ Set doRawResultSets = null;
+
+ boolean txn_known_resolved = true;
+
+ public String toString()
+ { return "C3P0ProxyConnection [Invocation Handler: " + super.toString() + ']'; }
+
+ private Object doRawConnectionOperation(Method m, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException, SQLException, Exception
+ {
+ if (activeConnection == null)
+ throw new SQLException("Connection previously closed. You cannot operate on a closed Connection.");
+
+ if (target == C3P0ProxyConnection.RAW_CONNECTION)
+ target = activeConnection;
+ for (int i = 0, len = args.length; i < len; ++i)
+ if (args[i] == C3P0ProxyConnection.RAW_CONNECTION)
+ args[i] = activeConnection;
+
+ Object out = m.invoke(target, args);
+
+ // we never cache Statements generated by an operation on the raw Connection
+ if (out instanceof Statement)
+ out = createProxyStatement( false, (Statement) out );
+ else if (out instanceof ResultSet)
+ {
+ if (doRawResultSets == null)
+ doRawResultSets = new HashSet();
+ out = new NullStatementSetManagedResultSet( (ResultSet) out, doRawResultSets );
+ }
+ return out;
+ }
+
+ public synchronized Object invoke(Object proxy, Method m, Object[] args)
+ throws Throwable
+ {
+ if ( OBJECT_METHODS.contains( m ) )
+ return m.invoke( this, args );
+
+ try
+ {
+ String mname = m.getName();
+ if (activeConnection != null)
+ {
+ if (mname.equals("rawConnectionOperation"))
+ {
+ ensureOkay();
+ txn_known_resolved = false;
+
+ return doRawConnectionOperation((Method) args[0], args[1], (Object[]) args[2]);
+ }
+ else if (mname.equals("setTransactionIsolation"))
+ {
+ ensureOkay();
+
+ //don't modify txn_known_resolved
+
+ m.invoke( activeConnection, args );
+
+ int lvl = ((Integer) args[0]).intValue();
+ isolation_lvl_nondefault = (lvl != dflt_txn_isolation);
+
+ //System.err.println("updated txn isolation to " + lvl + ", nondefault level? " + isolation_lvl_nondefault);
+
+ return null;
+ }
+ else if (mname.equals("setCatalog"))
+ {
+ ensureOkay();
+
+ //don't modify txn_known_resolved
+
+ m.invoke( activeConnection, args );
+
+ String catalog = (String) args[0];
+ catalog_nondefault = ObjectUtils.eqOrBothNull(catalog, dflt_catalog);
+
+ return null;
+ }
+ else if (mname.equals("setHoldability"))
+ {
+ ensureOkay();
+
+ //don't modify txn_known_resolved
+
+ m.invoke( activeConnection, args ); //will throw an exception if setHoldability() not supported...
+
+ int holdability = ((Integer) args[0]).intValue();
+ holdability_nondefault = (holdability != dflt_holdability);
+
+ return null;
+ }
+ else if (mname.equals("createStatement"))
+ {
+ ensureOkay();
+ txn_known_resolved = false;
+
+ Object stmt = m.invoke( activeConnection, args );
+ return createProxyStatement( (Statement) stmt );
+ }
+ else if (mname.equals("prepareStatement"))
+ {
+ ensureOkay();
+ txn_known_resolved = false;
+
+ Object pstmt;
+ if (scache == null)
+ {
+ pstmt = m.invoke( activeConnection, args );
+ return createProxyStatement( (Statement) pstmt );
+ }
+ else
+ {
+ pstmt = scache.checkoutStatement( physicalConnection,
+ m,
+ args );
+ return createProxyStatement( true,
+ (Statement) pstmt );
+ }
+ }
+ else if (mname.equals("prepareCall"))
+ {
+ ensureOkay();
+ txn_known_resolved = false;
+
+ Object cstmt;
+ if (scache == null)
+ {
+ cstmt = m.invoke( activeConnection, args );
+ return createProxyStatement( (Statement) cstmt );
+ }
+ else
+ {
+ cstmt = scache.checkoutStatement( physicalConnection, m, args );
+ return createProxyStatement( true,
+ (Statement) cstmt );
+ }
+ }
+ else if (mname.equals("getMetaData"))
+ {
+ ensureOkay();
+ txn_known_resolved = false; //views of tables etc. might be txn dependent
+
+ DatabaseMetaData innerMd = activeConnection.getMetaData();
+ if (metaData == null)
+ {
+ // exposedProxy is protected by C3P0PooledConnection.this' lock
+ synchronized (C3P0PooledConnection.this)
+ { metaData = new SetManagedDatabaseMetaData(innerMd, activeMetaDataResultSets, exposedProxy); }
+ }
+ return metaData;
+ }
+ else if (mname.equals("silentClose"))
+ {
+ //the PooledConnection doesn't have to be okay
+
+ doSilentClose( proxy, ((Boolean) args[0]).booleanValue(), this.txn_known_resolved );
+ return null;
+ }
+ else if ( mname.equals("close") )
+ {
+ //the PooledConnection doesn't have to be okay
+
+ Exception e = doSilentClose( proxy, false, this.txn_known_resolved );
+ if (! connection_error_signaled)
+ ces.fireConnectionClosed();
+ //System.err.println("close() called on a ProxyConnection.");
+ if (e != null)
+ {
+ // System.err.print("user close exception -- ");
+ // e.printStackTrace();
+ throw e;
+ }
+ else
+ return null;
+ }
+// else if ( mname.equals("finalize") ) //REMOVE THIS CASE -- TMP DEBUG
+// {
+// System.err.println("Connection apparently finalized!");
+// return m.invoke( activeConnection, args );
+// }
+ else
+ {
+ ensureOkay();
+
+
+ // we've disabled setting txn_known_resolved to true, ever, because
+ // we failed to deal with the case that clients would work with previously
+ // acquired Statements and ResultSets after a commit(), rollback(), or setAutoCommit().
+ // the new non-reflective proxies have been modified to deal with this case.
+ // here, with soon-to-be-deprecated in "traditional reflective proxies mode"
+ // we are reverting to the conservative, always-presume-you-have-to-rollback
+ // policy.
+
+ //txn_known_resolved = ( mname.equals("commit") || mname.equals( "rollback" ) || mname.equals( "setAutoCommit" ) );
+ txn_known_resolved = false;
+
+ return m.invoke( activeConnection, args );
+ }
+ }
+ else
+ {
+ if (mname.equals("close") ||
+ mname.equals("silentClose"))
+ return null;
+ else if (mname.equals("isClosed"))
+ return Boolean.TRUE;
+ else
+ {
+ throw new SQLException("You can't operate on " +
+ "a closed connection!!!");
+ }
+ }
+ }
+ catch (InvocationTargetException e)
+ {
+ Throwable convertMe = e.getTargetException();
+ SQLException sqle = handleMaybeFatalToPooledConnection( convertMe, proxy, false );
+ sqle.fillInStackTrace();
+ throw sqle;
+ }
+ }
+
+ private Exception doSilentClose(Object proxyConnection, boolean pooled_connection_is_dead)
+ { return doSilentClose( proxyConnection, pooled_connection_is_dead, false ); }
+
+ private Exception doSilentClose(Object proxyConnection, boolean pooled_connection_is_dead, boolean known_resolved_txn)
+ {
+ if ( activeConnection != null )
+ {
+ synchronized ( C3P0PooledConnection.this ) //uh oh... this is a nested lock acq... is there a deadlock hazard here?
+ {
+ if ( C3P0PooledConnection.this.exposedProxy == proxyConnection )
+ {
+ C3P0PooledConnection.this.exposedProxy = null;
+ //System.err.println("Reset exposed proxy.");
+
+ //DEBUG
+ //origGet = null;
+ }
+ else //else case -- DEBUG only
+ logger.warning("(c3p0 issue) doSilentClose( ... ) called on a proxyConnection " +
+ "other than the current exposed proxy for its PooledConnection. [exposedProxy: " +
+ exposedProxy + ", proxyConnection: " + proxyConnection);
+// System.err.println("[DEBUG] WARNING: doSilentClose( ... ) called on a proxyConnection " +
+// "other than the current exposed proxy for its PooledConnection. [exposedProxy: " +
+// exposedProxy + ", proxyConnection: " + proxyConnection);
+ }
+
+ Exception out = null;
+
+ Exception exc1 = null, exc2 = null, exc3 = null, exc4 = null;
+ try
+ {
+ if (! pooled_connection_is_dead)
+ C3P0PooledConnection.this.reset(known_resolved_txn);
+ }
+ catch (Exception e)
+ {
+ exc1 = e;
+ // if (Debug.DEBUG)
+ // {
+ // System.err.print("exc1 -- ");
+ // exc1.printStackTrace();
+ // }
+ }
+
+ exc2 = cleanupUncachedActiveStatements();
+ // if (Debug.DEBUG && exc2 != null)
+ // {
+ // System.err.print("exc2 -- ");
+ // exc2.printStackTrace();
+ // }
+ String errSource;
+ if (doRawResultSets != null)
+ {
+ activeMetaDataResultSets.addAll( doRawResultSets );
+ errSource = "DataBaseMetaData or raw Connection operation";
+ }
+ else
+ errSource = "DataBaseMetaData";
+
+ if (!closeAndRemoveResultSets( activeMetaDataResultSets ))
+ exc3 = new SQLException("Failed to close some " + errSource + " Result Sets.");
+ // if (Debug.DEBUG && exc3 != null)
+ // {
+ // System.err.print("exc3 -- ");
+ // exc3.printStackTrace();
+ // }
+ if (scache != null)
+ {
+ try
+ { scache.checkinAll( physicalConnection ); }
+ catch ( Exception e )
+ { exc4 = e; }
+ // if (Debug.DEBUG && exc4 != null)
+ // {
+ // System.err.print("exc4 -- ");
+ // exc4.printStackTrace();
+ // }
+ }
+
+ if (exc1 != null)
+ {
+ handleMaybeFatalToPooledConnection( exc1, proxyConnection, true );
+ out = exc1;
+ }
+ else if (exc2 != null)
+ {
+ handleMaybeFatalToPooledConnection( exc2, proxyConnection, true );
+ out = exc2;
+ }
+ else if (exc3 != null)
+ {
+ handleMaybeFatalToPooledConnection( exc3, proxyConnection, true );
+ out = exc3;
+ }
+ else if (exc4 != null)
+ {
+ handleMaybeFatalToPooledConnection( exc4, proxyConnection, true );
+ out = exc4;
+ }
+
+ // if (out != null)
+ // {
+ // System.err.print("out -- ");
+ // out.printStackTrace();
+ // }
+
+ activeConnection = null;
+ return out;
+ }
+ else
+ return null;
+ }
+
+ private SQLException handleMaybeFatalToPooledConnection( Throwable t, Object proxyConnection, boolean already_closed )
+ {
+ //System.err.println("handleMaybeFatalToPooledConnection()");
+
+ SQLException sqle = SqlUtils.toSQLException( t );
+ int status = connectionTester.statusOnException( physicalConnection, sqle );
+ updateConnectionStatus( status );
+ if (status != ConnectionTester.CONNECTION_IS_OKAY)
+ {
+ if (Debug.DEBUG)
+ {
+// System.err.print(C3P0PooledConnection.this + " will no longer be pooled because it has been " +
+// "marked invalid by the following Exception: ");
+// t.printStackTrace();
+
+ logger.log(MLevel.INFO,
+ C3P0PooledConnection.this + " will no longer be pooled because it has been marked invalid by an Exception.",
+ t );
+ }
+
+ invalidatingException = sqle;
+
+ /*
+ ------
+ A users have complained that SQLExceptions ought not close their Connections underneath
+ them under any circumstance. Signalling the Connection error after updating the Connection
+ status should be sufficient from the pool's perspective, because the PooledConnection
+ will be marked broken by the pool and will be destroyed on checkin. I think actually
+ close()ing the Connection when it appears to be broken rather than waiting for users
+ to close() it themselves is overly aggressive, so I'm commenting the old behavior out.
+ The only potential downside to this approach is that users who do not close() in a finally
+ clause properly might see their close()es skipped by exceptions that previously would
+ have led to automatic close(). But relying on the automatic close() was never reliable
+ (since it only ever happened when c3p0 determined a Connection to be absolutely broken),
+ and is generally speaking a client error that c3p0 ought not be responsible for dealing
+ with. I think it's right to leave this out. -- swaldman 2004-12-09
+ ------
+
+ if (! already_closed )
+ doSilentClose( proxyConnection, true );
+ */
+
+ if (! connection_error_signaled)
+ {
+ ces.fireConnectionErrorOccurred( sqle );
+ connection_error_signaled = true;
+ }
+ }
+ return sqle;
+ }
+
+
+ }
+
+ interface ProxyConnection extends C3P0ProxyConnection
+ { void silentClose( boolean known_invalid ) throws SQLException; }
+
+ public synchronized int getConnectionStatus()
+ { return this.connection_status; }
+
+ private synchronized void updateConnectionStatus(int status)
+ {
+ switch ( this.connection_status )
+ {
+ case ConnectionTester.DATABASE_IS_INVALID:
+ //can't get worse than this, do nothing.
+ break;
+ case ConnectionTester.CONNECTION_IS_INVALID:
+ if (status == ConnectionTester.DATABASE_IS_INVALID)
+ doBadUpdate(status);
+ break;
+ case ConnectionTester.CONNECTION_IS_OKAY:
+ if (status != ConnectionTester.CONNECTION_IS_OKAY)
+ doBadUpdate(status);
+ break;
+ default:
+ throw new InternalError(this + " -- Illegal Connection Status: " + this.connection_status);
+ }
+ }
+
+ //must be called from sync'ed method
+ private void doBadUpdate(int new_status)
+ {
+ this.connection_status = new_status;
+ try { this.close( true ); }
+ catch (SQLException e)
+ {
+// System.err.print("Broken Connection Close Error: ");
+// e.printStackTrace();
+
+ logger.log(MLevel.WARNING, "Broken Connection Close Error. ", e);
+ }
+ }
+}
+
+
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPool.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPool.java
new file mode 100644
index 0000000..407f346
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPool.java
@@ -0,0 +1,921 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import com.mchange.v2.c3p0.stmt.*;
+import com.mchange.v2.c3p0.ConnectionCustomizer;
+import com.mchange.v2.c3p0.SQLWarnings;
+import com.mchange.v2.c3p0.UnifiedConnectionTester;
+import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.util.LinkedList;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+import com.mchange.v1.db.sql.ConnectionUtils;
+import com.mchange.v2.async.AsynchronousRunner;
+import com.mchange.v2.async.ThreadPoolAsynchronousRunner;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.c3p0.ConnectionTester;
+import com.mchange.v2.c3p0.QueryConnectionTester;
+import com.mchange.v2.resourcepool.CannotAcquireResourceException;
+import com.mchange.v2.resourcepool.ResourcePool;
+import com.mchange.v2.resourcepool.ResourcePoolException;
+import com.mchange.v2.resourcepool.ResourcePoolFactory;
+import com.mchange.v2.resourcepool.TimeoutException;
+import com.mchange.v2.sql.SqlUtils;
+
+public final class C3P0PooledConnectionPool
+{
+ private final static boolean ASYNCHRONOUS_CONNECTION_EVENT_LISTENER = false;
+
+ private final static Throwable[] EMPTY_THROWABLE_HOLDER = new Throwable[1];
+
+ final static MLogger logger = MLog.getLogger( C3P0PooledConnectionPool.class );
+
+ final ResourcePool rp;
+ final ConnectionEventListener cl = new ConnectionEventListenerImpl();
+
+ final ConnectionTester connectionTester;
+ final GooGooStatementCache scache;
+
+ final int checkoutTimeout;
+
+ final AsynchronousRunner sharedTaskRunner;
+
+ final ThrowableHolderPool thp = new ThrowableHolderPool();
+
+ C3P0PooledConnectionPool( final ConnectionPoolDataSource cpds,
+ final DbAuth auth,
+ int min,
+ int max,
+ int start,
+ int inc,
+ int acq_retry_attempts,
+ int acq_retry_delay,
+ boolean break_after_acq_failure,
+ int checkoutTimeout, //milliseconds
+ int idleConnectionTestPeriod, //seconds
+ int maxIdleTime, //seconds
+ int maxIdleTimeExcessConnections, //seconds
+ int maxConnectionAge, //seconds
+ int propertyCycle, //seconds
+ int unreturnedConnectionTimeout, //seconds
+ boolean debugUnreturnedConnectionStackTraces,
+ final boolean testConnectionOnCheckout,
+ final boolean testConnectionOnCheckin,
+ int maxStatements,
+ int maxStatementsPerConnection,
+ final ConnectionTester connectionTester,
+ final ConnectionCustomizer connectionCustomizer,
+ final String testQuery,
+ final ResourcePoolFactory fact,
+ ThreadPoolAsynchronousRunner taskRunner,
+ final String parentDataSourceIdentityToken) throws SQLException
+ {
+ try
+ {
+ if (maxStatements > 0 && maxStatementsPerConnection > 0)
+ this.scache = new DoubleMaxStatementCache( taskRunner, maxStatements, maxStatementsPerConnection );
+ else if (maxStatementsPerConnection > 0)
+ this.scache = new PerConnectionMaxOnlyStatementCache( taskRunner, maxStatementsPerConnection );
+ else if (maxStatements > 0)
+ this.scache = new GlobalMaxOnlyStatementCache( taskRunner, maxStatements );
+ else
+ this.scache = null;
+
+ this.connectionTester = connectionTester;
+
+ this.checkoutTimeout = checkoutTimeout;
+
+ this.sharedTaskRunner = taskRunner;
+
+ class PooledConnectionResourcePoolManager implements ResourcePool.Manager
+ {
+ //SynchronizedIntHolder totalOpenedCounter = new SynchronizedIntHolder();
+ //SynchronizedIntHolder connectionCounter = new SynchronizedIntHolder();
+ //SynchronizedIntHolder failedCloseCounter = new SynchronizedIntHolder();
+
+ final boolean connectionTesterIsDefault = (connectionTester instanceof DefaultConnectionTester);
+ final boolean c3p0PooledConnections = (cpds instanceof WrapperConnectionPoolDataSource);
+
+ public Object acquireResource() throws Exception
+ {
+ PooledConnection out;
+
+ if ( connectionCustomizer == null)
+ {
+ out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
+ cpds.getPooledConnection() :
+ cpds.getPooledConnection( auth.getUser(),
+ auth.getPassword() ) );
+ }
+ else
+ {
+ try
+ {
+ WrapperConnectionPoolDataSourceBase wcpds = (WrapperConnectionPoolDataSourceBase) cpds;
+
+ out = (auth.equals( C3P0ImplUtils.NULL_AUTH ) ?
+ wcpds.getPooledConnection( connectionCustomizer, parentDataSourceIdentityToken ) :
+ wcpds.getPooledConnection( auth.getUser(),
+ auth.getPassword(),
+ connectionCustomizer, parentDataSourceIdentityToken ) );
+ }
+ catch (ClassCastException e)
+ {
+ throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 ConnectionPoolDataSource." +
+ " ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
+ }
+ }
+
+ //connectionCounter.increment();
+ //totalOpenedCounter.increment();
+
+ try
+ {
+ if (scache != null)
+ {
+ if (c3p0PooledConnections)
+ ((AbstractC3P0PooledConnection) out).initStatementCache(scache);
+ else
+ {
+ // System.err.print("Warning! StatementPooling not ");
+ // System.err.print("implemented for external (non-c3p0) ");
+ // System.err.println("ConnectionPoolDataSources.");
+
+ logger.warning("StatementPooling not " +
+ "implemented for external (non-c3p0) " +
+ "ConnectionPoolDataSources.");
+ }
+ }
+
+ // log and clear any SQLWarnings present upon acquisition
+ Connection con = null;
+ try
+ {
+ con = out.getConnection();
+ SQLWarnings.logAndClearWarnings( con );
+ }
+ finally
+ {
+ //invalidate the proxy Connection
+ ConnectionUtils.attemptClose( con );
+ }
+
+ out.addConnectionEventListener( cl );
+ return out;
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning("A PooledConnection was acquired, but an Exception occurred while preparing it for use. " +
+ "Attempting to destroy.");
+ try { destroyResource( out ); }
+ catch (Exception e2)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "An Exception occurred while trying to close partially acquired PooledConnection.",
+ e2 );
+ }
+
+ throw e;
+ }
+ finally
+ {
+ if (logger.isLoggable( MLevel.FINEST ))
+ logger.finest(this + ".acquireResource() returning. " );
+ //"Currently open Connections: " + connectionCounter.getValue() +
+ //"; Failed close count: " + failedCloseCounter.getValue() +
+ //"; Total processed by this pool: " + totalOpenedCounter.getValue());
+ }
+ }
+
+ // REFURBISHMENT:
+ // the PooledConnection refurbishes itself when
+ // its Connection view is closed, prior to being
+ // checked back in to the pool. But we still may want to
+ // test to make sure it is still good.
+
+ public void refurbishResourceOnCheckout( Object resc ) throws Exception
+ {
+ if ( testConnectionOnCheckout )
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ finerLoggingTestPooledConnection( resc, "CHECKOUT" );
+ else
+ testPooledConnection( resc );
+ }
+ if ( connectionCustomizer != null )
+ {
+ Connection physicalConnection = null;
+ try
+ {
+ physicalConnection = ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();
+ connectionCustomizer.onCheckOut( physicalConnection, parentDataSourceIdentityToken );
+ }
+ catch (ClassCastException e)
+ {
+ throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
+ " PooledConnection: " + resc +
+ "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
+ }
+ }
+ }
+
+ public void refurbishResourceOnCheckin( Object resc ) throws Exception
+ {
+ if ( connectionCustomizer != null )
+ {
+ Connection physicalConnection = null;
+ try
+ {
+ physicalConnection = ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();
+ connectionCustomizer.onCheckIn( physicalConnection, parentDataSourceIdentityToken );
+ SQLWarnings.logAndClearWarnings( physicalConnection );
+ }
+ catch (ClassCastException e)
+ {
+ throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
+ " PooledConnection: " + resc +
+ "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
+ }
+ }
+ else
+ {
+ PooledConnection pc = (PooledConnection) resc;
+ Connection con = null;
+
+ try
+ {
+ //we don't want any callbacks while we're clearing warnings
+ pc.removeConnectionEventListener( cl );
+
+ con = pc.getConnection();
+ SQLWarnings.logAndClearWarnings(con);
+ }
+ finally
+ {
+ // close the proxy Connection
+ ConnectionUtils.attemptClose(con);
+
+ pc.addConnectionEventListener( cl );
+ }
+ }
+
+ if ( testConnectionOnCheckin )
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ finerLoggingTestPooledConnection( resc, "CHECKIN" );
+ else
+ testPooledConnection( resc );
+ }
+ }
+
+ public void refurbishIdleResource( Object resc ) throws Exception
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ finerLoggingTestPooledConnection( resc, "IDLE CHECK" );
+ else
+ testPooledConnection( resc );
+ }
+
+ private void finerLoggingTestPooledConnection(Object resc, String testImpetus) throws Exception
+ {
+ logger.finer("Testing PooledConnection [" + resc + "] on " + testImpetus + ".");
+ try
+ {
+ testPooledConnection( resc );
+ logger.finer("Test of PooledConnection [" + resc + "] on "+testImpetus+" has SUCCEEDED.");
+ }
+ catch (Exception e)
+ {
+ logger.log(MLevel.FINER, "Test of PooledConnection [" + resc + "] on "+testImpetus+" has FAILED.", e);
+ e.fillInStackTrace();
+ throw e;
+ }
+ }
+
+ private void testPooledConnection(Object resc) throws Exception
+ {
+ PooledConnection pc = (PooledConnection) resc;
+
+ Throwable[] throwableHolder = EMPTY_THROWABLE_HOLDER;
+ int status;
+ Connection conn = null;
+ Throwable rootCause = null;
+ try
+ {
+ //we don't want any callbacks while we're testing the resource
+ pc.removeConnectionEventListener( cl );
+
+ conn = pc.getConnection(); //checkout proxy connection
+
+ // if this is a c3p0 pooled-connection, let's get underneath the
+ // proxy wrapper, and test the physical connection sometimes.
+ // this is faster, when the testQuery would not otherwise be cached,
+ // and it avoids a potential statusOnException() double-check by the
+ // PooledConnection implementation should the test query provoke an
+ // Exception
+ Connection testConn;
+ if (scache != null) //when there is a statement cache...
+ {
+ // if it's the slow, default query, faster to test the raw Connection
+ if (testQuery == null && connectionTesterIsDefault && c3p0PooledConnections)
+ testConn = ((AbstractC3P0PooledConnection) pc).getPhysicalConnection();
+ else //test will likely be faster on the proxied Connection, because the test query is probably cached
+ testConn = conn;
+ }
+ else //where there's no statement cache, better to use the physical connection, if we can get it
+ {
+ if (c3p0PooledConnections)
+ testConn = ((AbstractC3P0PooledConnection) pc).getPhysicalConnection();
+ else
+ testConn = conn;
+ }
+
+ if ( testQuery == null )
+ status = connectionTester.activeCheckConnection( testConn );
+ else
+ {
+ if (connectionTester instanceof UnifiedConnectionTester)
+ {
+ throwableHolder = thp.getThrowableHolder();
+ status = ((UnifiedConnectionTester) connectionTester).activeCheckConnection( testConn, testQuery, throwableHolder );
+ }
+ else if (connectionTester instanceof QueryConnectionTester)
+ status = ((QueryConnectionTester) connectionTester).activeCheckConnection( testConn, testQuery );
+ else
+ {
+ // System.err.println("[c3p0] WARNING: testQuery '" + testQuery +
+ // "' ignored. Please set a ConnectionTester that implements " +
+ // "com.mchange.v2.c3p0.advanced.QueryConnectionTester, or use the " +
+ // "DefaultConnectionTester, to test with the testQuery.");
+
+ logger.warning("[c3p0] testQuery '" + testQuery +
+ "' ignored. Please set a ConnectionTester that implements " +
+ "com.mchange.v2.c3p0.QueryConnectionTester, or use the " +
+ "DefaultConnectionTester, to test with the testQuery.");
+ status = connectionTester.activeCheckConnection( testConn );
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ logger.log(MLevel.FINE, "A Connection test failed with an Exception.", e);
+ //e.printStackTrace();
+ status = ConnectionTester.CONNECTION_IS_INVALID;
+// System.err.println("rootCause ------>");
+// e.printStackTrace();
+ rootCause = e;
+ }
+ finally
+ {
+ if (rootCause == null)
+ rootCause = throwableHolder[0];
+ else if (throwableHolder[0] != null && logger.isLoggable(MLevel.FINE))
+ logger.log(MLevel.FINE, "Internal Connection Test Exception", throwableHolder[0]);
+
+ if (throwableHolder != EMPTY_THROWABLE_HOLDER)
+ thp.returnThrowableHolder( throwableHolder );
+
+ ConnectionUtils.attemptClose( conn ); //invalidate proxy connection
+ pc.addConnectionEventListener( cl ); //should we move this to CONNECTION_IS_OKAY case? (it should work either way)
+ }
+
+ switch (status)
+ {
+ case ConnectionTester.CONNECTION_IS_OKAY:
+ break; //no problem, babe
+ case ConnectionTester.DATABASE_IS_INVALID:
+ rp.resetPool();
+ //intentional cascade...
+ case ConnectionTester.CONNECTION_IS_INVALID:
+ Exception throwMe;
+ if (rootCause == null)
+ throwMe = new SQLException("Connection is invalid");
+ else
+ throwMe = SqlUtils.toSQLException("Connection is invalid", rootCause);
+ throw throwMe;
+ default:
+ throw new Error("Bad Connection Tester (" +
+ connectionTester + ") " +
+ "returned invalid status (" + status + ").");
+ }
+ }
+
+ public void destroyResource(Object resc) throws Exception
+ {
+ try
+ {
+ if ( connectionCustomizer != null )
+ {
+ Connection physicalConnection = null;
+ try
+ {
+ physicalConnection = ((AbstractC3P0PooledConnection) resc).getPhysicalConnection();
+ connectionCustomizer.onDestroy( physicalConnection, parentDataSourceIdentityToken );
+ }
+ catch (ClassCastException e)
+ {
+ throw SqlUtils.toSQLException("Cannot use a ConnectionCustomizer with a non-c3p0 PooledConnection." +
+ " PooledConnection: " + resc +
+ "; ConnectionPoolDataSource: " + cpds.getClass().getName(), e);
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING,
+ "An exception occurred while executing the onDestroy() method of " + connectionCustomizer +
+ ". c3p0 will attempt to destroy the target Connection regardless, but this issue " +
+ " should be investigated and fixed.",
+ e );
+ }
+ }
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, "Preparing to destroy PooledConnection: " + resc);
+
+ ((PooledConnection) resc).close();
+
+ // inaccurate, as Connections can be removed more than once
+ //connectionCounter.decrement();
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER,
+ "Successfully destroyed PooledConnection: " + resc );
+ //". Currently open Connections: " + connectionCounter.getValue() +
+ //"; Failed close count: " + failedCloseCounter.getValue() +
+ //"; Total processed by this pool: " + totalOpenedCounter.getValue());
+ }
+ catch (Exception e)
+ {
+ //failedCloseCounter.increment();
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, "Failed to destroy PooledConnection: " + resc );
+ //". Currently open Connections: " + connectionCounter.getValue() +
+ //"; Failed close count: " + failedCloseCounter.getValue() +
+ //"; Total processed by this pool: " + totalOpenedCounter.getValue());
+
+ throw e;
+ }
+ }
+ }
+
+ ResourcePool.Manager manager = new PooledConnectionResourcePoolManager();
+
+ synchronized (fact)
+ {
+ fact.setMin( min );
+ fact.setMax( max );
+ fact.setStart( start );
+ fact.setIncrement( inc );
+ fact.setIdleResourceTestPeriod( idleConnectionTestPeriod * 1000);
+ fact.setResourceMaxIdleTime( maxIdleTime * 1000 );
+ fact.setExcessResourceMaxIdleTime( maxIdleTimeExcessConnections * 1000 );
+ fact.setResourceMaxAge( maxConnectionAge * 1000 );
+ fact.setExpirationEnforcementDelay( propertyCycle * 1000 );
+ fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
+ fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
+ fact.setAcquisitionRetryAttempts( acq_retry_attempts );
+ fact.setAcquisitionRetryDelay( acq_retry_delay );
+ fact.setBreakOnAcquisitionFailure( break_after_acq_failure );
+ rp = fact.createPool( manager );
+ }
+ }
+ catch (ResourcePoolException e)
+ { throw SqlUtils.toSQLException(e); }
+ }
+
+ public PooledConnection checkoutPooledConnection() throws SQLException
+ {
+ //System.err.println(this + " -- CHECKOUT");
+ try { return (PooledConnection) rp.checkoutResource( checkoutTimeout ); }
+ catch (TimeoutException e)
+ { throw SqlUtils.toSQLException("An attempt by a client to checkout a Connection has timed out.", e); }
+ catch (CannotAcquireResourceException e)
+ { throw SqlUtils.toSQLException("Connections could not be acquired from the underlying database!", "08001", e); }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException(e); }
+ }
+
+ public void checkinPooledConnection(PooledConnection pcon) throws SQLException
+ {
+ //System.err.println(this + " -- CHECKIN");
+ try { rp.checkinResource( pcon ); }
+ catch (ResourcePoolException e)
+ { throw SqlUtils.toSQLException(e); }
+ }
+
+ public float getEffectivePropertyCycle() throws SQLException
+ {
+ try
+ { return rp.getEffectiveExpirationEnforcementDelay() / 1000f; }
+ catch (ResourcePoolException e)
+ { throw SqlUtils.toSQLException(e); }
+ }
+
+ public int getNumThreadsAwaitingCheckout() throws SQLException
+ {
+ try
+ { return rp.getNumCheckoutWaiters(); }
+ catch (ResourcePoolException e)
+ { throw SqlUtils.toSQLException(e); }
+ }
+
+ public int getStatementCacheNumStatements()
+ { return scache == null ? 0 : scache.getNumStatements(); }
+
+ public int getStatementCacheNumCheckedOut()
+ { return scache == null ? 0 : scache.getNumStatementsCheckedOut(); }
+
+ public int getStatementCacheNumConnectionsWithCachedStatements()
+ { return scache == null ? 0 : scache.getNumConnectionsWithCachedStatements(); }
+
+ public String dumpStatementCacheStatus()
+ { return scache == null ? "Statement caching disabled." : scache.dumpStatementCacheStatus(); }
+
+ public void close() throws SQLException
+ { close( true ); }
+
+ public void close( boolean close_outstanding_connections ) throws SQLException
+ {
+ // System.err.println(this + " closing.");
+ Exception throwMe = null;
+
+ try { if (scache != null) scache.close(); }
+ catch (SQLException e)
+ { throwMe = e; }
+
+ try
+ { rp.close( close_outstanding_connections ); }
+ catch (ResourcePoolException e)
+ {
+ if ( throwMe != null && logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "An Exception occurred while closing the StatementCache.", throwMe);
+ throwMe = e;
+ }
+
+ if (throwMe != null)
+ throw SqlUtils.toSQLException( throwMe );
+ }
+
+ class ConnectionEventListenerImpl implements ConnectionEventListener
+ {
+
+ //
+ // We might want to check Connections in asynchronously,
+ // because this is called
+ // (indirectly) from a sync'ed method of NewPooledConnection, but
+ // NewPooledConnection may be closed synchronously from a sync'ed
+ // method of the resource pool, leading to a deadlock. Checking
+ // Connections in asynchronously breaks the cycle.
+ //
+ // But then we want checkins to happen quickly and reliably,
+ // whereas pool shutdowns are rare, so perhaps it's best to
+ // leave this synchronous, and let the closing of pooled
+ // resources on pool closes happen asynchronously to break
+ // the deadlock.
+ //
+ // For now we're leaving both versions around, but with faster
+ // and more reliable synchronous checkin enabled, and async closing
+ // of resources in BasicResourcePool.close().
+ //
+ public void connectionClosed(final ConnectionEvent evt)
+ {
+ //System.err.println("Checking in: " + evt.getSource());
+
+ if (ASYNCHRONOUS_CONNECTION_EVENT_LISTENER)
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ { doCheckinResource( evt ); }
+ };
+ sharedTaskRunner.postRunnable( r );
+ }
+ else
+ doCheckinResource( evt );
+ }
+
+ private void doCheckinResource(ConnectionEvent evt)
+ {
+ try
+ { rp.checkinResource( evt.getSource() ); }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING,
+ "An Exception occurred while trying to check a PooledConection into a ResourcePool.",
+ e );
+ }
+ }
+
+ //
+ // We might want to update the pool asynchronously, because this is called
+ // (indirectly) from a sync'ed method of NewPooledConnection, but
+ // NewPooledConnection may be closed synchronously from a sync'ed
+ // method of the resource pool, leading to a deadlock. Updating
+ // pool status asynchronously breaks the cycle.
+ //
+ // But then we want checkins to happen quickly and reliably,
+ // whereas pool shutdowns are rare, so perhaps it's best to
+ // leave all ConnectionEvent handling synchronous, and let the closing of pooled
+ // resources on pool closes happen asynchronously to break
+ // the deadlock.
+ //
+ // For now we're leaving both versions around, but with faster
+ // and more reliable synchrounous ConnectionEventHandling enabled, and async closing
+ // of resources in BasicResourcePool.close().
+ //
+ public void connectionErrorOccurred(final ConnectionEvent evt)
+ {
+// System.err.println("CONNECTION ERROR OCCURRED!");
+// System.err.println();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine("CONNECTION ERROR OCCURRED!");
+
+ final PooledConnection pc = (PooledConnection) evt.getSource();
+ int status;
+ if (pc instanceof C3P0PooledConnection)
+ status = ((C3P0PooledConnection) pc).getConnectionStatus();
+ else if (pc instanceof NewPooledConnection)
+ status = ((NewPooledConnection) pc).getConnectionStatus();
+ else //default to invalid connection, but not invalid database
+ status = ConnectionTester.CONNECTION_IS_INVALID;
+
+ final int final_status = status;
+
+ if (ASYNCHRONOUS_CONNECTION_EVENT_LISTENER)
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ { doMarkPoolStatus( pc, final_status ); }
+ };
+ sharedTaskRunner.postRunnable( r );
+ }
+ else
+ doMarkPoolStatus( pc, final_status );
+ }
+
+ private void doMarkPoolStatus(PooledConnection pc, int status)
+ {
+ try
+ {
+ switch (status)
+ {
+ case ConnectionTester.CONNECTION_IS_OKAY:
+ throw new RuntimeException("connectionErrorOcccurred() should only be " +
+ "called for errors fatal to the Connection.");
+ case ConnectionTester.CONNECTION_IS_INVALID:
+ rp.markBroken( pc );
+ break;
+ case ConnectionTester.DATABASE_IS_INVALID:
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.warning("A ConnectionTest has failed, reporting that all previously acquired Connections are likely invalid. " +
+ "The pool will be reset.");
+ rp.resetPool();
+ break;
+ default:
+ throw new RuntimeException("Bad Connection Tester (" + connectionTester + ") " +
+ "returned invalid status (" + status + ").");
+ }
+ }
+ catch ( ResourcePoolException e )
+ {
+ //System.err.println("Uh oh... our resource pool is probably broken!");
+ //e.printStackTrace();
+ logger.log(MLevel.WARNING, "Uh oh... our resource pool is probably broken!", e);
+ }
+ }
+ }
+
+ public int getNumConnections() throws SQLException
+ {
+ try { return rp.getPoolSize(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public int getNumIdleConnections() throws SQLException
+ {
+ try { return rp.getAvailableCount(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public int getNumBusyConnections() throws SQLException
+ {
+ try
+ {
+ synchronized ( rp )
+ { return (rp.getAwaitingCheckinCount() - rp.getExcludedCount()); }
+ }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public int getNumUnclosedOrphanedConnections() throws SQLException
+ {
+ try { return rp.getExcludedCount(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public long getStartTime() throws SQLException
+ {
+ try { return rp.getStartTime(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public long getUpTime() throws SQLException
+ {
+ try { return rp.getUpTime(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public long getNumFailedCheckins() throws SQLException
+ {
+ try { return rp.getNumFailedCheckins(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public long getNumFailedCheckouts() throws SQLException
+ {
+ try { return rp.getNumFailedCheckouts(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public long getNumFailedIdleTests() throws SQLException
+ {
+ try { return rp.getNumFailedIdleTests(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public Throwable getLastCheckinFailure() throws SQLException
+ {
+ try { return rp.getLastCheckinFailure(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public Throwable getLastCheckoutFailure() throws SQLException
+ {
+ try { return rp.getLastCheckoutFailure(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public Throwable getLastIdleTestFailure() throws SQLException
+ {
+ try { return rp.getLastIdleCheckFailure(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public Throwable getLastConnectionTestFailure() throws SQLException
+ {
+ try { return rp.getLastResourceTestFailure(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public Throwable getLastAcquisitionFailure() throws SQLException
+ {
+ try { return rp.getLastAcquisitionFailure(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ /**
+ * Discards all Connections managed by the pool
+ * and reacquires new Connections to populate.
+ * Current checked out Connections will still
+ * be valid, and should still be checked into the
+ * pool (so the pool can destroy them).
+ */
+ public void reset() throws SQLException
+ {
+ try { rp.resetPool(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log( MLevel.WARNING, null, e );
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ final static class ThrowableHolderPool
+ {
+ LinkedList l = new LinkedList();
+
+ synchronized Throwable[] getThrowableHolder()
+ {
+ if (l.size() == 0)
+ return new Throwable[1];
+ else
+ return (Throwable[]) l.remove(0);
+ }
+
+ synchronized void returnThrowableHolder(Throwable[] th)
+ {
+ th[0] = null;
+ l.add(th);
+ }
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPoolManager.java b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPoolManager.java
new file mode 100644
index 0000000..1819d81
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/C3P0PooledConnectionPoolManager.java
@@ -0,0 +1,1091 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.beans.*;
+import java.util.*;
+import java.lang.reflect.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.c3p0.cfg.*;
+import com.mchange.v2.async.*;
+import com.mchange.v2.coalesce.*;
+import com.mchange.v1.db.sql.*;
+import com.mchange.v2.log.*;
+import com.mchange.v1.lang.BooleanUtils;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.resourcepool.ResourcePoolFactory;
+import com.mchange.v2.resourcepool.BasicResourcePoolFactory;
+
+public final class C3P0PooledConnectionPoolManager
+{
+ private final static MLogger logger = MLog.getLogger( C3P0PooledConnectionPoolManager.class );
+
+ private final static boolean POOL_EVENT_SUPPORT = false;
+
+ private final static CoalesceChecker COALESCE_CHECKER = IdentityTokenizedCoalesceChecker.INSTANCE;
+
+ // unsync'ed coalescer -- we synchronize the static factory method that uses it
+ final static Coalescer COALESCER = CoalescerFactory.createCoalescer( COALESCE_CHECKER, true, false );
+
+ final static int DFLT_NUM_TASK_THREADS_PER_DATA_SOURCE = 3;
+
+ //MT: protected by this' lock
+ ThreadPoolAsynchronousRunner taskRunner;
+ Timer timer;
+ ResourcePoolFactory rpfact;
+ Map authsToPools;
+
+ /* MT: independently thread-safe, never reassigned post-ctor or factory */
+ final ConnectionPoolDataSource cpds;
+ final Map propNamesToReadMethods;
+ final Map flatPropertyOverrides;
+ final Map userOverrides;
+ final DbAuth defaultAuth;
+ final String parentDataSourceIdentityToken;
+ /* MT: end independently thread-safe, never reassigned post-ctor or factory */
+
+ /* MT: unchanging after constructor completes */
+ int num_task_threads = DFLT_NUM_TASK_THREADS_PER_DATA_SOURCE;
+
+ /* MT: end unchanging after constructor completes */
+
+ public int getThreadPoolSize()
+ { return taskRunner.getThreadCount(); }
+
+ public int getThreadPoolNumActiveThreads()
+ { return taskRunner.getActiveCount(); }
+
+ public int getThreadPoolNumIdleThreads()
+ { return taskRunner.getIdleCount(); }
+
+ public int getThreadPoolNumTasksPending()
+ { return taskRunner.getPendingTaskCount(); }
+
+ public String getThreadPoolStackTraces()
+ { return taskRunner.getStackTraces(); }
+
+ public String getThreadPoolStatus()
+ { return taskRunner.getStatus(); }
+
+ private synchronized void poolsInit()
+ {
+ this.timer = new Timer( true );
+
+ int matt = this.getMaxAdministrativeTaskTime( null );
+ if ( matt > 0 )
+ {
+ int matt_ms = matt * 1000;
+ this.taskRunner = new ThreadPoolAsynchronousRunner( num_task_threads,
+
+ true, // daemon thread
+
+ matt_ms, // wait before interrupt()
+
+ matt_ms * 3, // wait before deadlock declared if no tasks clear
+
+ matt_ms * 6, // wait before deadlock tasks are interrupted (again)
+ // after the hung thread has been cleared and replaced
+ // (in hopes of getting the thread to terminate for
+ // garbage collection)
+
+ timer );
+ }
+ else
+ this.taskRunner = new ThreadPoolAsynchronousRunner( num_task_threads, true, timer );
+ //this.taskRunner = new RoundRobinAsynchronousRunner( num_task_threads, true );
+ //this.rpfact = ResourcePoolFactory.createInstance( taskRunner, timer );
+ if (POOL_EVENT_SUPPORT)
+ this.rpfact = ResourcePoolFactory.createInstance( taskRunner, null, timer );
+ else
+ this.rpfact = BasicResourcePoolFactory.createNoEventSupportInstance( taskRunner, timer );
+ this.authsToPools = new HashMap();
+ }
+
+ private void poolsDestroy()
+ { poolsDestroy( true ); }
+
+ private synchronized void poolsDestroy( boolean close_outstanding_connections )
+ {
+ //System.err.println("poolsDestroy() -- " + this);
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ {
+ try
+ { ((C3P0PooledConnectionPool) ii.next()).close( close_outstanding_connections ); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log(MLevel.WARNING, "An Exception occurred while trying to clean up a pool!", e);
+ }
+ }
+
+ this.taskRunner.close( true );
+ this.timer.cancel();
+
+ this.taskRunner = null;
+ this.timer = null;
+ this.rpfact = null;
+ this.authsToPools = null;
+ }
+
+ public C3P0PooledConnectionPoolManager(ConnectionPoolDataSource cpds,
+ Map flatPropertyOverrides, // Map of properties, usually null
+ Map forceUserOverrides, // userNames to Map of properties, usually null
+ int num_task_threads,
+ String parentDataSourceIdentityToken)
+ throws SQLException
+ {
+ try
+ {
+ this.cpds = cpds;
+ this.flatPropertyOverrides = flatPropertyOverrides;
+ this.num_task_threads = num_task_threads;
+ this.parentDataSourceIdentityToken = parentDataSourceIdentityToken;
+
+ DbAuth auth = null;
+
+ if ( flatPropertyOverrides != null )
+ {
+ String overrideUser = (String) flatPropertyOverrides.get("overrideDefaultUser");
+ String overridePassword = (String) flatPropertyOverrides.get("overrideDefaultPassword");
+
+ if (overrideUser == null)
+ {
+ overrideUser = (String) flatPropertyOverrides.get("user");
+ overridePassword = (String) flatPropertyOverrides.get("password");
+ }
+
+ if (overrideUser != null)
+ auth = new DbAuth( overrideUser, overridePassword );
+ }
+
+ if (auth == null)
+ auth = C3P0ImplUtils.findAuth( cpds );
+
+ this.defaultAuth = auth;
+
+ Map tmp = new HashMap();
+ BeanInfo bi = Introspector.getBeanInfo( cpds.getClass() );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ PropertyDescriptor pd = null;
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ pd = pds[i];
+
+ String name = pd.getName();
+ Method m = pd.getReadMethod();
+
+ if (m != null)
+ tmp.put( name, m );
+ }
+ this.propNamesToReadMethods = tmp;
+
+ if (forceUserOverrides == null)
+ {
+ Method uom = (Method) propNamesToReadMethods.get( "userOverridesAsString" );
+ if (uom != null)
+ {
+ String uoas = (String) uom.invoke( cpds, null );
+ //System.err.println("uoas: " + uoas);
+ Map uo = C3P0ImplUtils.parseUserOverridesAsString( uoas );
+ this.userOverrides = uo;
+ }
+ else
+ this.userOverrides = Collections.EMPTY_MAP;
+ }
+ else
+ this.userOverrides = forceUserOverrides;
+
+ poolsInit();
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ logger.log(MLevel.FINE, null, e);
+ //e.printStackTrace();
+ throw SqlUtils.toSQLException(e);
+ }
+ }
+
+ public synchronized C3P0PooledConnectionPool getPool(String username, String password, boolean create) throws SQLException
+ {
+ if (create)
+ return getPool( username, password );
+ else
+ {
+ DbAuth checkAuth = new DbAuth( username, password );
+ C3P0PooledConnectionPool out = (C3P0PooledConnectionPool) authsToPools.get(checkAuth);
+ if (out == null)
+ throw new SQLException("No pool has been initialized for databse user '" + username + "' with the specified password.");
+ else
+ return out;
+ }
+ }
+
+ public C3P0PooledConnectionPool getPool(String username, String password)
+ throws SQLException
+ { return getPool( new DbAuth( username, password ) ); }
+
+ public synchronized C3P0PooledConnectionPool getPool(DbAuth auth)
+ throws SQLException
+ {
+ C3P0PooledConnectionPool out = (C3P0PooledConnectionPool) authsToPools.get(auth);
+ if (out == null)
+ {
+ out = createPooledConnectionPool(auth);
+ authsToPools.put( auth, out );
+ }
+ return out;
+ }
+
+ public synchronized Set getManagedAuths()
+ { return Collections.unmodifiableSet( authsToPools.keySet() ); }
+
+ public synchronized int getNumManagedAuths()
+ { return authsToPools.size(); }
+
+ public C3P0PooledConnectionPool getPool()
+ throws SQLException
+ { return getPool( defaultAuth ); }
+
+ public synchronized int getNumIdleConnectionsAllAuths() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getNumIdleConnections();
+ return out;
+ }
+
+ public synchronized int getNumBusyConnectionsAllAuths() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getNumBusyConnections();
+ return out;
+ }
+
+ public synchronized int getNumConnectionsAllAuths() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getNumConnections();
+ return out;
+ }
+
+ public synchronized int getNumUnclosedOrphanedConnectionsAllAuths() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getNumUnclosedOrphanedConnections();
+ return out;
+ }
+
+ public synchronized int getStatementCacheNumStatementsAllUsers() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getStatementCacheNumStatements();
+ return out;
+ }
+
+ public synchronized int getStatementCacheNumCheckedOutStatementsAllUsers() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getStatementCacheNumCheckedOut();
+ return out;
+ }
+
+ public synchronized int getStatementCacheNumConnectionsWithCachedStatementsAllUsers() throws SQLException
+ {
+ int out = 0;
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ out += ((C3P0PooledConnectionPool) ii.next()).getStatementCacheNumConnectionsWithCachedStatements();
+ return out;
+ }
+
+ public synchronized void softResetAllAuths() throws SQLException
+ {
+ for (Iterator ii = authsToPools.values().iterator(); ii.hasNext(); )
+ ((C3P0PooledConnectionPool) ii.next()).reset();
+ }
+
+ public void close()
+ { this.close( true ); }
+
+ public synchronized void close( boolean close_outstanding_connections )
+ {
+ // System.err.println("close()ing " + this);
+ if (authsToPools != null)
+ poolsDestroy( close_outstanding_connections );
+ }
+
+ protected synchronized void finalize()
+ {
+ // System.err.println("finalizing... " + this);
+ this.close();
+ }
+
+ private Object getObject(String propName, String userName)
+ {
+ Object out = null;
+
+ if (userName != null)
+ {
+ //userOverrides are usually config file defined, unless rarely used forceUserOverrides is supplied!
+ Map specificUserOverrides = (Map) userOverrides.get( userName );
+ if (specificUserOverrides != null)
+ out = specificUserOverrides.get( propName );
+ }
+
+ if (out == null && flatPropertyOverrides != null) //flatPropertyOverrides is a rarely used mechanism for forcing a config
+ out = flatPropertyOverrides.get( propName );
+
+ //if the ConnectionPoolDataSource has config parameter defined as a property use it
+ //(unless there was a user-specific or force override found above)
+ if (out == null)
+ {
+ try
+ {
+ Method m = (Method) propNamesToReadMethods.get( propName );
+ if (m != null)
+ {
+ Object readProp = m.invoke( cpds, null );
+ if (readProp != null)
+ out = readProp.toString();
+ }
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log(MLevel.WARNING,
+ "An exception occurred while trying to read property '" + propName +
+ "' from ConnectionPoolDataSource: " + cpds +
+ ". Default config value will be used.",
+ e );
+ }
+ }
+
+ //if the ConnectionPoolDataSource DID NOT have config parameter defined as a property
+ //(and there was no user-specific or force override)
+ //use config-defined default
+ if (out == null)
+ out = C3P0Config.getUnspecifiedUserProperty( propName, null );
+
+ return out;
+ }
+
+ private String getString(String propName, String userName)
+ {
+ Object o = getObject( propName, userName);
+ return (o == null ? null : o.toString());
+ }
+
+ private int getInt(String propName, String userName) throws Exception
+ {
+ Object o = getObject( propName, userName);
+ if (o instanceof Integer)
+ return ((Integer) o).intValue();
+ else if (o instanceof String)
+ return Integer.parseInt( (String) o );
+ else
+ throw new Exception("Unexpected object found for putative int property '" + propName +"': " + o);
+ }
+
+ private boolean getBoolean(String propName, String userName) throws Exception
+ {
+ Object o = getObject( propName, userName);
+ if (o instanceof Boolean)
+ return ((Boolean) o).booleanValue();
+ else if (o instanceof String)
+ return BooleanUtils.parseBoolean( (String) o );
+ else
+ throw new Exception("Unexpected object found for putative boolean property '" + propName +"': " + o);
+ }
+
+ public String getAutomaticTestTable(String userName)
+ { return getString("automaticTestTable", userName ); }
+
+ public String getPreferredTestQuery(String userName)
+ { return getString("preferredTestQuery", userName ); }
+
+ private int getInitialPoolSize(String userName)
+ {
+ try
+ { return getInt("initialPoolSize", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.initialPoolSize();
+ }
+ }
+
+ public int getMinPoolSize(String userName)
+ {
+ try
+ { return getInt("minPoolSize", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.minPoolSize();
+ }
+ }
+
+ private int getMaxPoolSize(String userName)
+ {
+ try
+ { return getInt("maxPoolSize", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxPoolSize();
+ }
+ }
+
+ private int getMaxStatements(String userName)
+ {
+ try
+ { return getInt("maxStatements", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxStatements();
+ }
+ }
+
+ private int getMaxStatementsPerConnection(String userName)
+ {
+ try
+ { return getInt("maxStatementsPerConnection", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxStatementsPerConnection();
+ }
+ }
+
+ private int getAcquireIncrement(String userName)
+ {
+ try
+ { return getInt("acquireIncrement", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.acquireIncrement();
+ }
+ }
+
+ private int getAcquireRetryAttempts(String userName)
+ {
+ try
+ { return getInt("acquireRetryAttempts", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.acquireRetryAttempts();
+ }
+ }
+
+ private int getAcquireRetryDelay(String userName)
+ {
+ try
+ { return getInt("acquireRetryDelay", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.acquireRetryDelay();
+ }
+ }
+
+ private boolean getBreakAfterAcquireFailure(String userName)
+ {
+ try
+ { return getBoolean("breakAfterAcquireFailure", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch boolean property", e);
+ return C3P0Defaults.breakAfterAcquireFailure();
+ }
+ }
+
+ private int getCheckoutTimeout(String userName)
+ {
+ try
+ { return getInt("checkoutTimeout", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.checkoutTimeout();
+ }
+ }
+
+ private int getIdleConnectionTestPeriod(String userName)
+ {
+ try
+ { return getInt("idleConnectionTestPeriod", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.idleConnectionTestPeriod();
+ }
+ }
+
+ private int getMaxIdleTime(String userName)
+ {
+ try
+ { return getInt("maxIdleTime", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxIdleTime();
+ }
+ }
+
+ private int getUnreturnedConnectionTimeout(String userName)
+ {
+ try
+ { return getInt("unreturnedConnectionTimeout", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.unreturnedConnectionTimeout();
+ }
+ }
+
+ private boolean getTestConnectionOnCheckout(String userName)
+ {
+ try
+ { return getBoolean("testConnectionOnCheckout", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch boolean property", e);
+ return C3P0Defaults.testConnectionOnCheckout();
+ }
+ }
+
+ private boolean getTestConnectionOnCheckin(String userName)
+ {
+ try
+ { return getBoolean("testConnectionOnCheckin", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch boolean property", e);
+ return C3P0Defaults.testConnectionOnCheckin();
+ }
+ }
+
+ private boolean getDebugUnreturnedConnectionStackTraces(String userName)
+ {
+ try
+ { return getBoolean("debugUnreturnedConnectionStackTraces", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch boolean property", e);
+ return C3P0Defaults.debugUnreturnedConnectionStackTraces();
+ }
+ }
+
+ private String getConnectionTesterClassName(String userName)
+ { return getString("connectionTesterClassName", userName ); }
+
+ private ConnectionTester getConnectionTester(String userName)
+ { return C3P0Registry.getConnectionTester( getConnectionTesterClassName( userName ) ); }
+
+ private String getConnectionCustomizerClassName(String userName)
+ { return getString("connectionCustomizerClassName", userName ); }
+
+ private ConnectionCustomizer getConnectionCustomizer(String userName) throws SQLException
+ { return C3P0Registry.getConnectionCustomizer( getConnectionCustomizerClassName( userName ) ); }
+
+ private int getMaxIdleTimeExcessConnections(String userName)
+ {
+ try
+ { return getInt("maxIdleTimeExcessConnections", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxIdleTimeExcessConnections();
+ }
+ }
+
+ private int getMaxAdministrativeTaskTime(String userName)
+ {
+ try
+ { return getInt("maxAdministrativeTaskTime", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxAdministrativeTaskTime();
+ }
+ }
+
+ private int getMaxConnectionAge(String userName)
+ {
+ try
+ { return getInt("maxConnectionAge", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.maxConnectionAge();
+ }
+ }
+
+ private int getPropertyCycle(String userName)
+ {
+ try
+ { return getInt("propertyCycle", userName ); }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not fetch int property", e);
+ return C3P0Defaults.propertyCycle();
+ }
+ }
+
+
+ // called only from sync'ed methods
+ private C3P0PooledConnectionPool createPooledConnectionPool(DbAuth auth) throws SQLException
+ {
+ String userName = auth.getUser();
+ String automaticTestTable = getAutomaticTestTable( userName );
+ String realTestQuery;
+
+ if (automaticTestTable != null)
+ {
+ realTestQuery = initializeAutomaticTestTable( automaticTestTable, auth );
+ if (this.getPreferredTestQuery( userName ) != null)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.logp(MLevel.WARNING,
+ C3P0PooledConnectionPoolManager.class.getName(),
+ "createPooledConnectionPool",
+ "[c3p0] Both automaticTestTable and preferredTestQuery have been set! " +
+ "Using automaticTestTable, and ignoring preferredTestQuery. Real test query is ''{0}''.",
+ realTestQuery
+ );
+ }
+ }
+ }
+ else
+ {
+ // when there is an automaticTestTable to be constructed, we
+ // have little choice but to grab a Connection on initialization
+ // to ensure that the table exists before the pool tries to
+ // test Connections. in c3p0-0.9.1-pre10, i added the check below
+ // to grab and destroy a cxn even when we don't
+ // need one, to ensure that db access params are correct before
+ // we start up a pool. a user who frequently creates and destroys
+ // PooledDataSources complained about the extra initialization
+ // time. the main use of this test was to prevent superfluous
+ // bad pools from being intialized when JMX users type bad
+ // authentification information into a query method. This is
+ // now prevented in AbstractPoolBackedDataSource. Still, it is
+ // easy for clients to start pools uselessly by asking for
+ // Connections with bad authentification information. We adopt
+ // the compromise position of "trusting" the DataSource's default
+ // authentification info (as defined by defaultAuth), but ensuring
+ // that authentification succeeds via the check below when non-default
+ // authentification info is provided.
+
+ if (! defaultAuth.equals( auth ))
+ ensureFirstConnectionAcquisition( auth );
+
+ realTestQuery = this.getPreferredTestQuery( userName );
+ }
+
+ C3P0PooledConnectionPool out = new C3P0PooledConnectionPool( cpds,
+ auth,
+ this.getMinPoolSize( userName ),
+ this.getMaxPoolSize( userName ),
+ this.getInitialPoolSize( userName ),
+ this.getAcquireIncrement( userName ),
+ this.getAcquireRetryAttempts( userName ),
+ this.getAcquireRetryDelay( userName ),
+ this.getBreakAfterAcquireFailure( userName ),
+ this.getCheckoutTimeout( userName ),
+ this.getIdleConnectionTestPeriod( userName ),
+ this.getMaxIdleTime( userName ),
+ this.getMaxIdleTimeExcessConnections( userName ),
+ this.getMaxConnectionAge( userName ),
+ this.getPropertyCycle( userName ),
+ this.getUnreturnedConnectionTimeout( userName ),
+ this.getDebugUnreturnedConnectionStackTraces( userName ),
+ this.getTestConnectionOnCheckout( userName ),
+ this.getTestConnectionOnCheckin( userName ),
+ this.getMaxStatements( userName ),
+ this.getMaxStatementsPerConnection( userName ),
+ this.getConnectionTester( userName ),
+ this.getConnectionCustomizer( userName ),
+ realTestQuery,
+ rpfact,
+ taskRunner,
+ parentDataSourceIdentityToken );
+ return out;
+ }
+
+
+ // only called from sync'ed methods
+ private String initializeAutomaticTestTable(String automaticTestTable, DbAuth auth) throws SQLException
+ {
+ PooledConnection throwawayPooledConnection = auth.equals( defaultAuth ) ?
+ cpds.getPooledConnection() :
+ cpds.getPooledConnection(auth.getUser(), auth.getPassword());
+ Connection c = null;
+ PreparedStatement testStmt = null;
+ PreparedStatement createStmt = null;
+ ResultSet mdrs = null;
+ ResultSet rs = null;
+ boolean exists;
+ boolean has_rows;
+ String out;
+ try
+ {
+ c = throwawayPooledConnection.getConnection();
+
+ DatabaseMetaData dmd = c.getMetaData();
+ String q = dmd.getIdentifierQuoteString();
+ String quotedTableName = q + automaticTestTable + q;
+ out = "SELECT * FROM " + quotedTableName;
+ mdrs = dmd.getTables( null, null, automaticTestTable, new String[] {"TABLE"} );
+ exists = mdrs.next();
+
+ //System.err.println("Table " + automaticTestTable + " exists? " + exists);
+
+ if (exists)
+ {
+ testStmt = c.prepareStatement( out );
+ rs = testStmt.executeQuery();
+ has_rows = rs.next();
+ if (has_rows)
+ throw new SQLException("automatic test table '" + automaticTestTable +
+ "' contains rows, and it should not! Please set this " +
+ "parameter to the name of a table c3p0 can create on its own, " +
+ "that is not used elsewhere in the database!");
+ }
+ else
+ {
+ String createSql = "CREATE TABLE " + quotedTableName + " ( a CHAR(1) )";
+ try
+ {
+ createStmt = c.prepareStatement( createSql );
+ createStmt.executeUpdate();
+ }
+ catch (SQLException e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log(MLevel.WARNING,
+ "An attempt to create an automatic test table failed. Create SQL: " +
+ createSql,
+ e );
+ throw e;
+ }
+ }
+ return out;
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( mdrs );
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( testStmt );
+ StatementUtils.attemptClose( createStmt );
+ ConnectionUtils.attemptClose( c );
+ try{ if (throwawayPooledConnection != null) throwawayPooledConnection.close(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log(MLevel.WARNING, "A PooledConnection failed to close.", e);
+ }
+ }
+ }
+
+ private void ensureFirstConnectionAcquisition(DbAuth auth) throws SQLException
+ {
+ PooledConnection throwawayPooledConnection = auth.equals( defaultAuth ) ?
+ cpds.getPooledConnection() :
+ cpds.getPooledConnection(auth.getUser(), auth.getPassword());
+ Connection c = null;
+ try
+ {
+ c = throwawayPooledConnection.getConnection();
+ }
+ finally
+ {
+ ConnectionUtils.attemptClose( c );
+ try{ if (throwawayPooledConnection != null) throwawayPooledConnection.close(); }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ logger.log(MLevel.WARNING, "A PooledConnection failed to close.", e);
+ }
+ }
+ }
+}
+
+
+
+
+
+//public static find(ConnectionPoolDataSource cpds,
+//DbAuth defaultAuth, //may be null
+//int maxStatements,
+//int minPoolSize,
+//int maxPoolSize,
+//int idleConnectionTestPeriod,
+//int maxIdleTime,
+//int acquireIncrement,
+//boolean testConnectionOnCheckout,
+//boolean autoCommitOnClose,
+//boolean forceIgnoreUnresolvedTransactions,
+//ConnectionTester connectionTester)
+//{
+//C3P0PooledConnectionPoolManager nascent = new C3P0PooledConnectionPoolManager( cpds,
+//defaultAuth,
+//maxStatements,
+//minPoolSize,
+//maxPoolSize,
+//idleConnectionTestPeriod,
+//maxIdleTime,
+//acquireIncrement,
+//testConnectionOnCheckout,
+//autoCommitOnClose,
+//forceIgnoreUnresolvedTransactions,
+//connectionTester);
+//C3P0PooledConnectionPoolManager out = (C3P0PooledConnectionPoolManager) coalescer.coalesce( nascent );
+//if ( out == nascent ) //the new guy is the ONE
+//out.poolInit();
+//return out;
+//}
+
+//private C3P0PooledConnectionPoolManager(ConnectionPoolDataSource cpds,
+//DbAuth defaultAuth, //may be null
+//int maxStatements,
+//int minPoolSize,
+//int maxPoolSize,
+//int idleConnectionTestPeriod,
+//int maxIdleTime,
+//int acquireIncrement,
+//boolean testConnectionOnCheckout,
+//boolean autoCommitOnClose,
+//boolean forceIgnoreUnresolvedTransactions,
+//ConnectionTester connectionTester)
+//{
+//this.cpds = cpds;
+//this.defaultAuth = (defaultAuth == null ? C3P0ImplUtils.NULL_AUTH : defaultAuth);
+//this.maxStatements = maxStatements;
+//this.minPoolSize = minPoolSize;
+//this.maxPoolSize = maxPoolSize;
+//this.idleConnectionTestPeriod = idleConnectionTestPeriod;
+//this.maxIdleTime = maxIdleTime;
+//this.acquireIncrement = acquireIncrement;
+//this.testConnectionOnCheckout = testConnectionOnCheckout;
+//this.autoCommitOnClose = autoCommitOnClose;
+//this.testConnectionOnCheckout = testConnectionOnCheckout;
+//this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions;
+//}
+
+//private final static CoalesceChecker COALESCE_CHECKER = new CoalesceChecker()
+//{
+//// note that we expect all ConnectionTesters of a single class to be effectively
+//// equivalent, since they are to be constructed via a no-arg ctor and no
+//// extra initialization is performed. thus we only compare the classes of ConnectionTesters.
+//public boolean checkCoalesce( Object a, Object b )
+//{
+//C3P0PooledConnectionPoolManager aa = (C3P0PooledConnectionPoolManager) a;
+//C3P0PooledConnectionPoolManager bb = (C3P0PooledConnectionPoolManager) b;
+
+//return
+//aa.poolOwnerIdentityToken.equals( bb.poolOwnerIdentityToken ) &&
+//(aa.preferredTestQuery == null ? (bb.preferredTestQuery == null ) : (aa.preferredTestQuery.equals( bb.preferredTestQuery ))) &&
+//(aa.automaticTestTable == null ? (bb.automaticTestTable == null ) : (aa.automaticTestTable.equals( bb.automaticTestTable ))) &&
+//aa.sourceCpdsIdentityToken.equals( bb.sourceCpdsIdentityToken ) &&
+//aa.num_task_threads == bb.num_task_threads &&
+//aa.maxStatements == bb.maxStatements &&
+//aa.maxStatementsPerConnection == bb.maxStatementsPerConnection &&
+//aa.minPoolSize == bb.minPoolSize &&
+//aa.idleConnectionTestPeriod == bb.idleConnectionTestPeriod &&
+//aa.maxIdleTime == bb.maxIdleTime &&
+//aa.checkoutTimeout == bb.checkoutTimeout &&
+//aa.acquireIncrement == bb.acquireIncrement &&
+//aa.acquireRetryAttempts == bb.acquireRetryAttempts &&
+//aa.acquireRetryDelay == bb.acquireRetryDelay &&
+//aa.breakAfterAcquireFailure == bb.breakAfterAcquireFailure &&
+//aa.testConnectionOnCheckout == bb.testConnectionOnCheckout &&
+//aa.testConnectionOnCheckin == bb.testConnectionOnCheckin &&
+//aa.autoCommitOnClose == bb.autoCommitOnClose &&
+//aa.forceIgnoreUnresolvedTransactions == bb.forceIgnoreUnresolvedTransactions &&
+//aa.defaultAuth.equals( bb.defaultAuth ) &&
+//aa.connectionTester.getClass().equals( bb.connectionTester.getClass() );
+//};
+
+//public int coalesceHash( Object a )
+//{
+//C3P0PooledConnectionPoolManager aa = (C3P0PooledConnectionPoolManager) a;
+//int out =
+//aa.poolOwnerIdentityToken.hashCode() ^
+//(aa.preferredTestQuery == null ? 0 : aa.preferredTestQuery.hashCode()) ^
+//(aa.automaticTestTable == null ? 0 : aa.automaticTestTable.hashCode()) ^
+//aa.sourceCpdsIdentityToken.hashCode() ^
+//aa.num_task_threads ^
+//aa.maxStatements ^
+//aa.maxStatementsPerConnection ^
+//aa.minPoolSize ^
+//aa.idleConnectionTestPeriod ^
+//aa.maxIdleTime ^
+//aa.checkoutTimeout ^
+//aa.acquireIncrement ^
+//aa.acquireRetryAttempts ^
+//aa.acquireRetryDelay ^
+//(aa.testConnectionOnCheckout ? 1<<0 : 0) ^
+//(aa.testConnectionOnCheckin ? 1<<1 : 0) ^
+//(aa.autoCommitOnClose ? 1<<2 : 0) ^
+//(aa.forceIgnoreUnresolvedTransactions ? 1<<3 : 0) ^
+//(aa.breakAfterAcquireFailure ? 1<<4 : 0) ^
+//aa.defaultAuth.hashCode() ^
+//aa.connectionTester.getClass().hashCode();
+////System.err.println("coalesceHash() --> " + out);
+//return out;
+//};
+//};
+
+//int maxStatements = PoolConfig.defaultMaxStatements();
+//int maxStatementsPerConnection = PoolConfig.defaultMaxStatementsPerConnection();
+//int minPoolSize = PoolConfig.defaultMinPoolSize();
+//int maxPoolSize = PoolConfig.defaultMaxPoolSize();
+//int idleConnectionTestPeriod = PoolConfig.defaultIdleConnectionTestPeriod();
+//int maxIdleTime = PoolConfig.defaultMaxIdleTime();
+//int checkoutTimeout = PoolConfig.defaultCheckoutTimeout();
+//int acquireIncrement = PoolConfig.defaultAcquireIncrement();
+//int acquireRetryAttempts = PoolConfig.defaultAcquireRetryAttempts();
+//int acquireRetryDelay = PoolConfig.defaultAcquireRetryDelay();
+//boolean breakAfterAcquireFailure = PoolConfig.defaultBreakAfterAcquireFailure();
+//boolean testConnectionOnCheckout = PoolConfig.defaultTestConnectionOnCheckout();
+//boolean testConnectionOnCheckin = PoolConfig.defaultTestConnectionOnCheckin();
+//boolean autoCommitOnClose = PoolConfig.defaultAutoCommitOnClose();
+//boolean forceIgnoreUnresolvedTransactions = PoolConfig.defaultForceIgnoreUnresolvedTransactions();
+//String preferredTestQuery = PoolConfig.defaultPreferredTestQuery();
+//String automaticTestTable = PoolConfig.defaultAutomaticTestTable();
+//DbAuth defaultAuth = C3P0ImplUtils.NULL_AUTH;
+//ConnectionTester connectionTester = C3P0ImplUtils.defaultConnectionTester();;
+
+
+//// we look for non-standard props user and
+//// password, available as read-only props on
+//// our implementation of ConnectionPoolDataSource.
+////
+//// If other implementations are used, the only
+//// hazard is the possibility that there will be
+//// two pools for the same real authorization credentials
+//// one for when the credentials are explicitly specified,
+//// and one for when the defaults are used.
+
+//this.defaultAuth = C3P0ImplUtils.findAuth( cpds );
+
+//BeanInfo bi = Introspector.getBeanInfo( cpds.getClass() );
+//PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+//for (int i = 0, len = pds.length; i < len; ++i)
+//{
+//PropertyDescriptor pd = pds[i];
+//Class propCl = pd.getPropertyType();
+//String propName = pd.getName();
+//Method readMethod = pd.getReadMethod();
+//Object propVal;
+//if (propCl == int.class)
+//{
+//propVal = readMethod.invoke( cpds, C3P0ImplUtils.NOARGS );
+//int value = ((Integer) propVal).intValue();
+//if ("maxStatements".equals(propName))
+//this.maxStatements = value;
+//else if ("maxStatementsPerConnection".equals(propName))
+//this.maxStatementsPerConnection = value;
+//else if ("minPoolSize".equals(propName))
+//this.minPoolSize = value;
+//else if ("maxPoolSize".equals(propName))
+//this.maxPoolSize = value;
+//else if ("idleConnectionTestPeriod".equals(propName))
+//this.idleConnectionTestPeriod = value;
+//else if ("maxIdleTime".equals(propName))
+//this.maxIdleTime = value;
+//else if ("checkoutTimeout".equals(propName))
+//this.checkoutTimeout = value;
+//else if ("acquireIncrement".equals(propName))
+//this.acquireIncrement = value;
+//else if ("acquireRetryAttempts".equals(propName))
+//this.acquireRetryAttempts = value;
+//else if ("acquireRetryDelay".equals(propName))
+//this.acquireRetryDelay = value;
+//// System.err.println( propName + " -> " + propVal );
+//}
+//else if (propCl == String.class)
+//{
+//propVal = readMethod.invoke( cpds, C3P0ImplUtils.NOARGS );
+//String value = (String) propVal;
+//if ("connectionTesterClassName".equals(propName))
+//this.connectionTester =
+//(ConnectionTester) Class.forName( value ).newInstance();
+//else if ("preferredTestQuery".equals(propName))
+//this.preferredTestQuery = value;
+//else if ("automaticTestTable".equals(propName))
+//this.automaticTestTable = value;
+//// System.err.println( propName + " -> " + propVal );
+//}
+//else if (propCl == boolean.class)
+//{
+//propVal = readMethod.invoke( cpds, C3P0ImplUtils.NOARGS );
+//boolean value = ((Boolean) propVal).booleanValue();
+//if ("testConnectionOnCheckout".equals(propName))
+//this.testConnectionOnCheckout = value;
+//else if ("testConnectionOnCheckin".equals(propName))
+//this.testConnectionOnCheckin = value;
+//else if ("autoCommitOnClose".equals(propName))
+//this.autoCommitOnClose = value;
+//else if ("forceIgnoreUnresolvedTransactions".equals(propName))
+//this.forceIgnoreUnresolvedTransactions = value;
+//else if ("breakAfterAcquireFailure".equals(propName))
+//this.breakAfterAcquireFailure = value;
+//// System.err.println( propName + " -> " + propVal );
+//}
+
+//}
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/DbAuth.java b/src/classes/com/mchange/v2/c3p0/impl/DbAuth.java
new file mode 100644
index 0000000..3d17022
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/DbAuth.java
@@ -0,0 +1,98 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.io.*;
+import com.mchange.v2.lang.ObjectUtils;
+import com.mchange.v2.ser.UnsupportedVersionException;
+
+public final class DbAuth implements Serializable
+{
+ transient String username;
+ transient String password;
+
+ public DbAuth(String username, String password)
+ {
+ this.username = username;
+ this.password = password;
+ }
+
+ public String getUser()
+ { return username; }
+
+ public String getPassword()
+ { return password; }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ else if (o != null && this.getClass() == o.getClass())
+ {
+ DbAuth other = (DbAuth) o;
+ return
+ ObjectUtils.eqOrBothNull(this.username, other.username) &&
+ ObjectUtils.eqOrBothNull(this.password, other.password);
+ }
+ else
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return
+ ObjectUtils.hashOrZero(username) ^
+ ObjectUtils.hashOrZero(password);
+ }
+
+ //Serialization
+ static final long serialVersionUID = 1; //override to take control of versioning
+ private final static short VERSION = 0x0001;
+
+ private void writeObject(ObjectOutputStream out) throws IOException
+ {
+ out.writeShort(VERSION);
+ out.writeObject(username); //may be null
+ out.writeObject(password); //may be null
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+ {
+ short version = in.readShort();
+ switch (version)
+ {
+ case 0x0001:
+ this.username = (String) in.readObject();
+ this.password = (String) in.readObject();
+ break;
+ default:
+ throw new UnsupportedVersionException(this, version);
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/DefaultConnectionTester.java b/src/classes/com/mchange/v2/c3p0/impl/DefaultConnectionTester.java
new file mode 100644
index 0000000..3a00d4c
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/DefaultConnectionTester.java
@@ -0,0 +1,237 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.*;
+import java.util.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.c3p0.AbstractConnectionTester;
+import com.mchange.v2.c3p0.FullQueryConnectionTester;
+import com.mchange.v1.db.sql.ResultSetUtils;
+import com.mchange.v1.db.sql.StatementUtils;
+
+public class DefaultConnectionTester extends AbstractConnectionTester
+{
+ final static MLogger logger = MLog.getLogger( DefaultConnectionTester.class );
+
+ final static int HASH_CODE = DefaultConnectionTester.class.getName().hashCode();
+
+ final static Set INVALID_DB_STATES;
+
+ static
+ {
+ Set temp = new HashSet();
+ temp.add("08001"); //SQL State "Unable to connect to data source"
+ temp.add("08007"); //SQL State "Connection failure during transaction"
+
+ // MySql appently uses this state to indicate a stale, expired
+ // connection when the database is fine, so we'll not presume
+ // this SQL state signals an invalid database.
+ //temp.add("08S01"); //SQL State "Communication link failure"
+
+ INVALID_DB_STATES = Collections.unmodifiableSet( temp );
+ }
+
+ public int activeCheckConnection(Connection c, String query, Throwable[] rootCauseOutParamHolder)
+ {
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Entering DefaultConnectionTester.activeCheckConnection(Connection c, String query). [query=" + query + "]");
+
+ if (query == null)
+ return activeCheckConnectionNoQuery( c, rootCauseOutParamHolder);
+ else
+ {
+ Statement stmt = null;
+ ResultSet rs = null;
+ try
+ {
+ //if (Math.random() < 0.1)
+ // throw new NullPointerException("Test.");
+
+ stmt = c.createStatement();
+ rs = stmt.executeQuery( query );
+ //rs.next();
+ return CONNECTION_IS_OKAY;
+ }
+ catch (SQLException e)
+ {
+ if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Connection " + c + " failed Connection test with an Exception! [query=" + query + "]", e );
+
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = e;
+
+ String state = e.getSQLState();
+ if ( INVALID_DB_STATES.contains( state ) )
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "SQL State '" + state +
+ "' of Exception which occurred during a Connection test (test with query '" + query +
+ "') implies that the database is invalid, " +
+ "and the pool should refill itself with fresh Connections.", e);
+ return DATABASE_IS_INVALID;
+ }
+ else
+ return CONNECTION_IS_INVALID;
+ }
+ catch (Exception e)
+ {
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "Connection " + c + " failed Connection test with an Exception!", e );
+
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = e;
+
+ return CONNECTION_IS_INVALID;
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( stmt );
+
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Exiting DefaultConnectionTester.activeCheckConnection(Connection c, String query). [query=" + query + "]");
+ }
+ }
+ }
+
+ public int statusOnException(Connection c, Throwable t, String query, Throwable[] rootCauseOutParamHolder)
+ {
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Entering DefaultConnectionTester.statusOnException(Connection c, Throwable t, String query) " + queryInfo(query));
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+ logger.log(MLevel.FINER, "Testing a Connection in response to an Exception:", t);
+
+ try
+ {
+ if (t instanceof SQLException)
+ {
+ String state = ((SQLException) t).getSQLState();
+ if ( INVALID_DB_STATES.contains( state ) )
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "SQL State '" + state +
+ "' of Exception tested by statusOnException() implies that the database is invalid, " +
+ "and the pool should refill itself with fresh Connections.", t);
+ return DATABASE_IS_INVALID;
+ }
+ else
+ return activeCheckConnection(c, query, rootCauseOutParamHolder);
+ }
+ else //something is broke
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Connection test failed because test-provoking Throwable is an unexpected, non-SQLException.", t);
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = t;
+ return CONNECTION_IS_INVALID;
+ }
+ }
+ catch (Exception e)
+ {
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "Connection " + c + " failed Connection test with an Exception!", e );
+
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = e;
+
+ return CONNECTION_IS_INVALID;
+ }
+ finally
+ {
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+// {
+// if ( logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Exiting DefaultConnectionTester.statusOnException(Connection c, Throwable t, String query) " + queryInfo(query));
+// }
+ }
+ }
+
+ private static String queryInfo(String query)
+ { return (query == null ? "[using default system-table query]" : "[query=" + query + "]"); }
+
+ private int activeCheckConnectionNoQuery(Connection c, Throwable[] rootCauseOutParamHolder)
+ {
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Entering DefaultConnectionTester.activeCheckConnection(Connection c). [using default system-table query]");
+
+ ResultSet rs = null;
+ try
+ {
+ rs = c.getMetaData().getTables( null,
+ null,
+ "PROBABLYNOT",
+ new String[] {"TABLE"} );
+ return CONNECTION_IS_OKAY;
+ }
+ catch (SQLException e)
+ {
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "Connection " + c + " failed default system-table Connection test with an Exception!", e );
+
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = e;
+
+ String state = e.getSQLState();
+ if ( INVALID_DB_STATES.contains( state ) )
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ "SQL State '" + state +
+ "' of Exception which occurred during a Connection test (fallback DatabaseMetaData test) implies that the database is invalid, " +
+ "and the pool should refill itself with fresh Connections.", e);
+ return DATABASE_IS_INVALID;
+ }
+ else
+ return CONNECTION_IS_INVALID;
+ }
+ catch (Exception e)
+ {
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "Connection " + c + " failed default system-table Connection test with an Exception!", e );
+
+ if (rootCauseOutParamHolder != null)
+ rootCauseOutParamHolder[0] = e;
+
+ return CONNECTION_IS_INVALID;
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+// if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ) )
+// logger.finer("Exiting DefaultConnectionTester.activeCheckConnection(Connection c). [using default system-table query]");
+ }
+ }
+
+
+ public boolean equals( Object o )
+ { return ( o != null && o.getClass() == DefaultConnectionTester.class ); }
+
+ public int hashCode()
+ { return HASH_CODE; }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenResolvable.java b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenResolvable.java
new file mode 100644
index 0000000..f7e2c5a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenResolvable.java
@@ -0,0 +1,60 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import com.mchange.v2.c3p0.*;
+import java.io.ObjectStreamException;
+
+/**
+ * This is a convenient base class for all classes
+ * that wish to establish an initial identity which
+ * will be the basis of a one-per vm identity: i.e.
+ * in any vm there should only ever be a single object
+ * with a given identity token (except transiently during
+ * canonicalization)
+ *
+ * It would be convenient to put the getter/setter methods
+ * for the identity token here, but unfortunately we have no
+ * way of setting up the for Referenceability in multiple
+ * levels of a class hierarchy. So we leave the getters/setters,
+ * and variable initialization to code-generators.
+ */
+public abstract class IdentityTokenResolvable extends AbstractIdentityTokenized
+{
+ public static Object doResolve(IdentityTokenized itd)
+ { return C3P0Registry.reregister( itd ); }
+
+ protected Object readResolve() throws ObjectStreamException
+ {
+ //System.err.println("READ RESOLVE!!!!");
+ Object out = doResolve( this );
+ verifyResolve( out );
+ //System.err.println("ORIG: " + this);
+ //System.err.println("RSLV: " + out);
+ return out;
+ }
+
+ protected void verifyResolve( Object o ) throws ObjectStreamException
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenized.java b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenized.java
new file mode 100644
index 0000000..a422e39
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenized.java
@@ -0,0 +1,30 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+public interface IdentityTokenized
+{
+ public String getIdentityToken();
+ public void setIdentityToken(String idToken);
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenizedCoalesceChecker.java b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenizedCoalesceChecker.java
new file mode 100644
index 0000000..fa6db0a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/IdentityTokenizedCoalesceChecker.java
@@ -0,0 +1,54 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import com.mchange.v2.coalesce.*;
+
+public final class IdentityTokenizedCoalesceChecker implements CoalesceChecker
+{
+ public static IdentityTokenizedCoalesceChecker INSTANCE = new IdentityTokenizedCoalesceChecker();
+
+ public boolean checkCoalesce( Object a, Object b )
+ {
+ IdentityTokenized aa = (IdentityTokenized) a;
+ IdentityTokenized bb = (IdentityTokenized) b;
+
+ String ta = aa.getIdentityToken();
+ String tb = bb.getIdentityToken();
+
+ if (ta == null || tb == null)
+ throw new NullPointerException( "[c3p0 bug] An IdentityTokenized object has no identity token set?!?! " + (ta == null ? ta : tb) );
+ else
+ return ta.equals(tb);
+ }
+
+ public int coalesceHash( Object a )
+ {
+ String t = ((IdentityTokenized) a).getIdentityToken();
+ return (t != null ? t.hashCode() : 0);
+ }
+
+ private IdentityTokenizedCoalesceChecker()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/InternalPooledConnection.java b/src/classes/com/mchange/v2/c3p0/impl/InternalPooledConnection.java
new file mode 100644
index 0000000..bb0d6ea
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/InternalPooledConnection.java
@@ -0,0 +1,34 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import javax.sql.*;
+import com.mchange.v2.c3p0.stmt.*;
+
+interface InternalPooledConnection extends PooledConnection
+{
+ public void initStatementCache( GooGooStatementCache scache );
+ public GooGooStatementCache getStatementCache();
+ public int getConnectionStatus();
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/NewPooledConnection.java b/src/classes/com/mchange/v2/c3p0/impl/NewPooledConnection.java
new file mode 100644
index 0000000..944a4e8
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/NewPooledConnection.java
@@ -0,0 +1,740 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.c3p0.stmt.*;
+import com.mchange.v2.c3p0.util.*;
+import com.mchange.v2.log.*;
+
+import java.lang.reflect.Method;
+import com.mchange.v2.lang.ObjectUtils;
+import com.mchange.v2.sql.SqlUtils;
+
+public final class NewPooledConnection extends AbstractC3P0PooledConnection{
+
+ private final static MLogger logger = MLog.getLogger( NewPooledConnection.class );
+
+ private final static SQLException NORMAL_CLOSE_PLACEHOLDER = new SQLException("This pooled Connection was explicitly close()ed by " +
+ "a client, not invalidated due to an error.");
+
+ //MT: protected by class lock
+ static Set holdabilityBugKeys = null;
+
+ //MT: thread-safe post-constructor constants
+ final Connection physicalConnection;
+ final ConnectionTester connectionTester;
+ final boolean autoCommitOnClose;
+ final boolean forceIgnoreUnresolvedTransactions;
+ final String preferredTestQuery;
+ final boolean supports_setHoldability;
+ final boolean supports_setReadOnly;
+ final boolean supports_setTypeMap;
+ final int dflt_txn_isolation;
+ final String dflt_catalog;
+ final int dflt_holdability;
+ final boolean dflt_readOnly;
+ final Map dflt_typeMap;
+ final ConnectionEventSupport ces;
+
+ //MT: protected by this' lock
+ GooGooStatementCache scache = null;
+ Throwable invalidatingException = null;
+ int connection_status = ConnectionTester.CONNECTION_IS_OKAY;
+ Set uncachedActiveStatements = new HashSet(); //cached statements are managed by the cache
+ Map resultSetsForStatements = new HashMap(); //for both cached and uncached statements
+ Set metaDataResultSets = new HashSet();
+ Set rawConnectionResultSets = null; //very rarely used, so we lazy initialize...
+ boolean connection_error_signaled = false;
+
+ //MT: thread-safe, volatile
+ volatile NewProxyConnection exposedProxy = null;
+ volatile boolean isolation_lvl_nondefault = false;
+ volatile boolean catalog_nondefault = false;
+ volatile boolean holdability_nondefault = false;
+ volatile boolean readOnly_nondefault = false;
+ volatile boolean typeMap_nondefault = false;
+
+ // public API
+ public NewPooledConnection(Connection con,
+ ConnectionTester connectionTester,
+ boolean autoCommitOnClose,
+ boolean forceIgnoreUnresolvedTransactions,
+ String preferredTestQuery,
+ ConnectionCustomizer cc,
+ String pdsIdt) throws SQLException
+ {
+ try
+ {
+ if (cc != null)
+ cc.onAcquire( con, pdsIdt );
+ }
+ catch (Exception e)
+ { throw SqlUtils.toSQLException(e); }
+
+ this.physicalConnection = con;
+ this.connectionTester = connectionTester;
+ this.autoCommitOnClose = autoCommitOnClose;
+ this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions;
+ this.preferredTestQuery = preferredTestQuery;
+ this.supports_setHoldability = C3P0ImplUtils.supportsMethod(con, "setHoldability", new Class[]{ int.class });
+ this.supports_setReadOnly = C3P0ImplUtils.supportsMethod(con, "setReadOnly", new Class[]{ boolean.class });
+ this.supports_setTypeMap = C3P0ImplUtils.supportsMethod(con, "setTypeMap", new Class[]{ Map.class });
+ this.dflt_txn_isolation = con.getTransactionIsolation();
+ this.dflt_catalog = con.getCatalog();
+ this.dflt_holdability = (supports_setHoldability ? carefulCheckHoldability(con) : ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ this.dflt_readOnly = (supports_setReadOnly ? carefulCheckReadOnly(con) : false);
+ this.dflt_typeMap = (supports_setTypeMap && (carefulCheckTypeMap(con) == null) ? null : Collections.EMPTY_MAP);
+ this.ces = new ConnectionEventSupport(this);
+ }
+
+ private static int carefulCheckHoldability(Connection con)
+ {
+ try { return con.getHoldability(); }
+ catch (Exception e)
+ {
+ if (false)
+ {
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, con + " threw an Exception when we tried to check its default " +
+ "holdability. This is not usually a problem! It just means the Connection " +
+ "doesn't support the holdability property, and c3p0 works around this.", e);
+ }
+ return ResultSet.CLOSE_CURSORS_AT_COMMIT;
+ }
+ catch (Error e) // Some DB2 drivers apparently throw an Error here, but I'm not comfortable swallowing Errors
+ {
+ synchronized (NewPooledConnection.class)
+ {
+ if (holdabilityBugKeys == null)
+ holdabilityBugKeys = new HashSet();
+ String hbk = holdabilityBugKey(con, e);
+ if (! holdabilityBugKeys.contains(hbk) )
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, con + " threw an Error when we tried to check its default " +
+ "holdability. This is probably due to a bug in your JDBC driver that c3p0 can harmlessly " +
+ "work around (reported for some DB2 drivers). Please verify that the error stack trace is consistent" +
+ "with the getHoldability() method not being properly implemented, and is not due to some deeper problem. " +
+ "This message will not be repeated for Connections of type " + con.getClass().getName() + " that " +
+ "provoke errors of type " + e.getClass().getName() + " when getHoldability() is called.", e);
+ holdabilityBugKeys.add(hbk);
+ }
+ }
+ return ResultSet.CLOSE_CURSORS_AT_COMMIT;
+ }
+ }
+
+ private static String holdabilityBugKey(Connection con, Error err)
+ { return con.getClass().getName() + '|' + err.getClass().getName(); }
+
+ private static boolean carefulCheckReadOnly(Connection con)
+ {
+ try { return con.isReadOnly(); }
+ catch (Exception e)
+ {
+ if (false)
+ {
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, con + " threw an Exception when we tried to check its default " +
+ "read only state. This is not usually a problem! It just means the Connection " +
+ "doesn't support the readOnly property, and c3p0 works around this.", e);
+ }
+ return false;
+ }
+ }
+
+ private static Map carefulCheckTypeMap(Connection con)
+ {
+ try { return con.getTypeMap(); }
+ catch (Exception e)
+ {
+ if (false)
+ {
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, con + " threw an Exception when we tried to check its default " +
+ "type map. This is not usually a problem! It just means the Connection " +
+ "doesn't support the typeMap property, and c3p0 works around this.", e);
+ }
+ return null;
+ }
+ }
+
+ public synchronized Connection getConnection() throws SQLException
+ {
+ try
+ {
+ //throw new SQLException("NOT IMPLEMENTED");
+ if ( exposedProxy == null )
+ {
+ exposedProxy = new NewProxyConnection( physicalConnection, this );
+ }
+ else
+ {
+// System.err.println("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
+// "it had already provided a client with a Connection that has not yet been " +
+// "closed. This probably indicates a bug in the connection pool!!!");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
+ "it had already provided a client with a Connection that has not yet been " +
+ "closed. This probably indicates a bug in the connection pool!!!");
+
+ }
+ return exposedProxy;
+ }
+ catch ( Exception e )
+ {
+ SQLException sqle = handleThrowable( e );
+ throw sqle;
+ }
+ }
+
+ public synchronized int getConnectionStatus()
+ { return connection_status; }
+
+ public synchronized void closeAll() throws SQLException
+ {
+ try
+ {
+ closeAllCachedStatements();
+ }
+ catch ( Exception e )
+ {
+ SQLException sqle = handleThrowable( e );
+ throw sqle;
+ }
+ }
+
+ public synchronized void close() throws SQLException
+ { close( null ); }
+
+ public void addConnectionEventListener(ConnectionEventListener cel)
+ { ces.addConnectionEventListener( cel ); }
+
+ public void removeConnectionEventListener(ConnectionEventListener cel)
+ { ces.removeConnectionEventListener( cel ); }
+
+ // api for C3P0PooledConnectionPool
+ public synchronized void initStatementCache( GooGooStatementCache scache )
+ { this.scache = scache; }
+
+ public synchronized GooGooStatementCache getStatementCache()
+ { return scache; }
+
+ //api for NewProxyConnections
+ void markNewTxnIsolation( int lvl ) //intentionally unsync'd -- isolation_lvl_nondefault is marked volatile
+ {
+ this.isolation_lvl_nondefault = (lvl != dflt_txn_isolation);
+ //System.err.println("isolation_lvl_nondefault: " + isolation_lvl_nondefault);
+ }
+
+ void markNewCatalog( String catalog ) //intentionally unsync'd -- catalog_nondefault is marked volatile
+ {
+ this.catalog_nondefault = ObjectUtils.eqOrBothNull(catalog, dflt_catalog);
+ }
+
+ void markNewHoldability( int holdability ) //intentionally unsync'd -- holdability_nondefault is marked volatile
+ {
+ this.holdability_nondefault = (holdability != dflt_holdability);
+ }
+
+ void markNewReadOnly( boolean readOnly ) //intentionally unsync'd -- readOnly_nondefault is marked volatile
+ {
+ this.readOnly_nondefault = (readOnly != dflt_readOnly);
+ }
+
+ void markNewTypeMap( Map typeMap ) //intentionally unsync'd -- typeMap_nondefault is marked volatile
+ {
+ this.typeMap_nondefault = (typeMap != dflt_typeMap);
+ }
+
+ synchronized Object checkoutStatement( Method stmtProducingMethod, Object[] args ) throws SQLException
+ { return scache.checkoutStatement( physicalConnection, stmtProducingMethod, args ); }
+
+ synchronized void checkinStatement( Statement stmt ) throws SQLException
+ {
+ cleanupStatementResultSets( stmt );
+ scache.checkinStatement( stmt );
+ }
+
+ synchronized void markActiveUncachedStatement( Statement stmt )
+ { uncachedActiveStatements.add( stmt ); }
+
+ synchronized void markInactiveUncachedStatement( Statement stmt )
+ {
+ cleanupStatementResultSets( stmt );
+ uncachedActiveStatements.remove( stmt );
+ }
+
+ synchronized void markActiveResultSetForStatement( Statement stmt, ResultSet rs )
+ {
+ Set rss = resultSets( stmt, true );
+ rss.add( rs );
+ }
+
+ synchronized void markInactiveResultSetForStatement( Statement stmt, ResultSet rs )
+ {
+ Set rss = resultSets( stmt, false );
+ if (rss == null)
+ {
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.fine( "ResultSet " + rs + " was apparently closed after the Statement that created it had already been closed." );
+ }
+ else if ( ! rss.remove( rs ) )
+ throw new InternalError("Marking a ResultSet inactive that we did not know was opened!");
+ }
+
+ synchronized void markActiveRawConnectionResultSet( ResultSet rs )
+ {
+ if (rawConnectionResultSets == null)
+ rawConnectionResultSets = new HashSet();
+ rawConnectionResultSets.add( rs );
+ }
+
+ synchronized void markInactiveRawConnectionResultSet( ResultSet rs )
+ {
+ if ( ! rawConnectionResultSets.remove( rs ) )
+ throw new InternalError("Marking a raw Connection ResultSet inactive that we did not know was opened!");
+ }
+
+ synchronized void markActiveMetaDataResultSet( ResultSet rs )
+ { metaDataResultSets.add( rs ); }
+
+ synchronized void markInactiveMetaDataResultSet( ResultSet rs )
+ { metaDataResultSets.remove( rs ); }
+
+ // internal synchronization to avoid sync'ed event multicasts
+ void markClosedProxyConnection( NewProxyConnection npc, boolean txn_known_resolved )
+ {
+ SQLException trouble = null;
+ try
+ {
+ synchronized( this )
+ {
+ try
+ {
+ if (npc != exposedProxy)
+ throw new InternalError("C3P0 Error: An exposed proxy asked a PooledConnection that was not its parents to clean up its resources!");
+
+ List closeExceptions = new LinkedList();
+ cleanupResultSets( closeExceptions );
+ cleanupUncachedStatements( closeExceptions );
+ checkinAllCachedStatements( closeExceptions );
+ if ( closeExceptions.size() > 0 )
+ {
+// System.err.println("[c3p0] The following Exceptions occurred while trying to clean up a Connection's stranded resources:");
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.info("[c3p0] The following Exceptions occurred while trying to clean up a Connection's stranded resources:");
+ for ( Iterator ii = closeExceptions.iterator(); ii.hasNext(); )
+ {
+ Throwable t = (Throwable) ii.next();
+// System.err.print("[c3p0 -- conection resource close Exception]: ");
+// t.printStackTrace();
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.log( MLevel.INFO, "[c3p0 -- conection resource close Exception]", t );
+ }
+ }
+ reset( txn_known_resolved );
+ }
+ catch (SQLException e) //Connection failed to reset!
+ {
+ //e.printStackTrace();
+ if (Debug.DEBUG && logger.isLoggable( MLevel.FINE ))
+ logger.log(MLevel.FINE, "An exception occurred while reseting a closed Connection. Invalidating Connection.", e);
+
+ updateConnectionStatus( ConnectionTester.CONNECTION_IS_INVALID );
+ }
+ }
+ }
+ finally
+ {
+ if (trouble != null)
+ fireConnectionErrorOccurred( trouble ); //should not be invoked from a sync'ed block
+ else
+ {
+ exposedProxy = null; //volatile
+ fireConnectionClosed(); //should not be invoked from a sync'ed block
+ }
+ }
+ }
+
+ private void reset( boolean txn_known_resolved ) throws SQLException
+ {
+ C3P0ImplUtils.resetTxnState( physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, txn_known_resolved );
+ if (isolation_lvl_nondefault)
+ {
+ physicalConnection.setTransactionIsolation( dflt_txn_isolation );
+ isolation_lvl_nondefault = false;
+ //System.err.println("reset txn isolation: " + dflt_txn_isolation);
+ }
+ if (catalog_nondefault)
+ {
+ physicalConnection.setCatalog( dflt_catalog );
+ catalog_nondefault = false;
+ }
+ if (holdability_nondefault) //this cannot go to true if holdability is not supported, so we don't have to check.
+ {
+ physicalConnection.setHoldability( dflt_holdability );
+ holdability_nondefault = false;
+ }
+ if (readOnly_nondefault)
+ {
+ physicalConnection.setReadOnly( dflt_readOnly );
+ readOnly_nondefault = false;
+ }
+ if (typeMap_nondefault)
+ {
+ physicalConnection.setTypeMap( dflt_typeMap );
+ typeMap_nondefault = false;
+ }
+ }
+
+ synchronized boolean isStatementCaching()
+ { return scache != null; }
+
+ //synchrnized internally to avoid holding locks during event multicast
+ SQLException handleThrowable( Throwable t )
+ {
+ boolean fire_cxn_error = false;
+ SQLException sqle = null;
+ try
+ {
+ synchronized (this)
+ {
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, this + " handling a throwable.", t );
+
+ sqle = SqlUtils.toSQLException( t );
+ //logger.warning("handle throwable ct: " + connectionTester);
+
+ int status;
+ if (connectionTester instanceof FullQueryConnectionTester)
+ status = ((FullQueryConnectionTester) connectionTester).statusOnException( physicalConnection, sqle, preferredTestQuery );
+ else
+ status = connectionTester.statusOnException( physicalConnection, sqle );
+
+ updateConnectionStatus( status );
+ if (status != ConnectionTester.CONNECTION_IS_OKAY)
+ {
+ if (Debug.DEBUG)
+ {
+// System.err.print(this + " invalidated by Exception: ");
+// t.printStackTrace();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log(MLevel.FINE, this + " invalidated by Exception.", t);
+ }
+
+ /*
+ ------
+ A users have complained that SQLExceptions ought not close their Connections underneath
+ them under any circumstance. Signalling the Connection error after updating the Connection
+ status should be sufficient from the pool's perspective, because the PooledConnection
+ will be marked broken by the pool and will be destroyed on checkin. I think actually
+ close()ing the Connection when it appears to be broken rather than waiting for users
+ to close() it themselves is overly aggressive, so I'm commenting the old behavior out.
+ The only potential downside to this approach is that users who do not close() in a finally
+ clause properly might see their close()es skipped by exceptions that previously would
+ have led to automatic close(). But relying on the automatic close() was never reliable
+ (since it only ever happened when c3p0 determined a Connection to be absolutely broken),
+ and is generally speaking a client error that c3p0 ought not be responsible for dealing
+ with. I think it's right to leave this out. -- swaldman 2004-12-09
+ ------
+
+ try { close( t ); }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ throw new InternalError("C3P0 Error: NewPooledConnection's private close() method should " +
+ "suppress any Exceptions if a throwable cause is provided.");
+ }
+ */
+
+
+ if (! connection_error_signaled)
+ fire_cxn_error = true;
+ else
+ {
+// System.err.println("[c3p0] Warning: PooledConnection that has already signalled a Connection error is still in use!");
+// System.err.println("[c3p0] Another error has occurred [ " + t + " ] which will not be reported to listeners!");
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.log(MLevel.WARNING, "[c3p0] A PooledConnection that has already signalled a Connection error is still in use!");
+ logger.log(MLevel.WARNING, "[c3p0] Another error has occurred [ " + t + " ] which will not be reported to listeners!", t);
+ }
+ }
+ }
+ }// end sync'ed block
+ }// end try block
+ finally
+ {
+ if (fire_cxn_error)
+ {
+ fireConnectionErrorOccurred( sqle ); //should not be invoked from a sync'ed block
+ connection_error_signaled = true;
+ }
+ }
+ return sqle;
+ }
+
+// private methods
+
+// should NOT be called from sync'ed method
+ private void fireConnectionClosed()
+ {
+ assert (! Thread.holdsLock(this));
+ ces.fireConnectionClosed();
+ }
+
+// should NOT be called from sync'ed method
+ private void fireConnectionErrorOccurred(SQLException error)
+ {
+ assert (! Thread.holdsLock(this));
+ ces.fireConnectionErrorOccurred( error );
+ }
+
+// methods below must be called from sync'ed methods
+
+ /*
+ * If a throwable cause is provided, the PooledConnection is known to be broken (cause is an invalidating exception)
+ * and this method will not throw any exceptions, even if some resource closes fail.
+ *
+ * If cause is null, then we think the PooledConnection is healthy, and we will report (throw) an exception
+ * if resources unexpectedlay fail to close.
+ */
+ private void close( Throwable cause ) throws SQLException
+ {
+ if ( this.invalidatingException == null )
+ {
+ List closeExceptions = new LinkedList();
+
+ // cleanup ResultSets
+ cleanupResultSets( closeExceptions );
+
+ // cleanup uncached Statements
+ cleanupUncachedStatements( closeExceptions );
+
+ // cleanup cached Statements
+ try
+ { closeAllCachedStatements(); }
+ catch ( SQLException e )
+ { closeExceptions.add(e); }
+
+ // cleanup physicalConnection
+ try
+ { physicalConnection.close(); }
+ catch ( SQLException e )
+ {
+ if (logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, "Failed to close physical Connection: " + physicalConnection, e );
+
+ closeExceptions.add(e);
+ }
+
+ // update our state to bad status and closed, and log any exceptions
+ if ( connection_status == ConnectionTester.CONNECTION_IS_OKAY )
+ connection_status = ConnectionTester.CONNECTION_IS_INVALID;
+ if ( cause == null )
+ {
+ this.invalidatingException = NORMAL_CLOSE_PLACEHOLDER;
+
+ if ( logger.isLoggable( MLevel.FINEST ) )
+ logger.log( MLevel.FINEST, this + " closed by a client.", new Exception("DEBUG -- CLOSE BY CLIENT STACK TRACE") );
+
+ logCloseExceptions( null, closeExceptions );
+
+ if (closeExceptions.size() > 0)
+ throw new SQLException("Some resources failed to close properly while closing " + this);
+ }
+ else
+ {
+ this.invalidatingException = cause;
+ if (Debug.TRACE >= Debug.TRACE_MED)
+ logCloseExceptions( cause, closeExceptions );
+ else
+ logCloseExceptions( cause, null );
+ }
+ }
+ }
+
+ private void cleanupResultSets( List closeExceptions )
+ {
+ cleanupAllStatementResultSets( closeExceptions );
+ cleanupUnclosedResultSetsSet( metaDataResultSets, closeExceptions );
+ if ( rawConnectionResultSets != null )
+ cleanupUnclosedResultSetsSet( rawConnectionResultSets, closeExceptions );
+ }
+
+ private void cleanupUnclosedResultSetsSet( Set rsSet, List closeExceptions )
+ {
+ for ( Iterator ii = rsSet.iterator(); ii.hasNext(); )
+ {
+ ResultSet rs = (ResultSet) ii.next();
+ try
+ { rs.close(); }
+ catch ( SQLException e )
+ { closeExceptions.add(e); }
+
+ ii.remove();
+ }
+ }
+
+ private void cleanupStatementResultSets( Statement stmt )
+ {
+ Set rss = resultSets( stmt, false );
+ if ( rss != null )
+ {
+ for ( Iterator ii = rss.iterator(); ii.hasNext(); )
+ {
+ try
+ { ((ResultSet) ii.next()).close(); }
+ catch ( Exception e )
+ {
+// System.err.print("ResultSet close() failed: ");
+// e.printStackTrace();
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.log(MLevel.INFO, "ResultSet close() failed.", e);
+ }
+ }
+ }
+ resultSetsForStatements.remove( stmt );
+ }
+
+ private void cleanupAllStatementResultSets( List closeExceptions )
+ {
+ for ( Iterator ii = resultSetsForStatements.keySet().iterator(); ii.hasNext(); )
+ {
+ Object stmt = ii.next();
+ Set rss = (Set) resultSetsForStatements.get( stmt );
+ for (Iterator jj = rss.iterator(); jj.hasNext(); )
+ {
+ ResultSet rs = (ResultSet) jj.next();
+ try
+ { rs.close(); }
+ catch ( SQLException e )
+ { closeExceptions.add(e); }
+ }
+ }
+ resultSetsForStatements.clear();
+ }
+
+ private void cleanupUncachedStatements( List closeExceptions )
+ {
+ for ( Iterator ii = uncachedActiveStatements.iterator(); ii.hasNext(); )
+ {
+ Statement stmt = (Statement) ii.next();
+ try
+ { stmt.close(); }
+ catch ( SQLException e )
+ { closeExceptions.add(e); }
+
+ ii.remove();
+ }
+ }
+
+ private void checkinAllCachedStatements( List closeExceptions )
+ {
+ try
+ {
+ if (scache != null)
+ scache.checkinAll( physicalConnection );
+ }
+ catch ( SQLException e )
+ { closeExceptions.add(e); }
+ }
+
+ private void closeAllCachedStatements() throws SQLException
+ {
+ if (scache != null)
+ scache.closeAll( physicalConnection );
+ }
+
+ private void updateConnectionStatus(int status)
+ {
+ switch ( this.connection_status )
+ {
+ case ConnectionTester.DATABASE_IS_INVALID:
+ //can't get worse than this, do nothing.
+ break;
+ case ConnectionTester.CONNECTION_IS_INVALID:
+ if (status == ConnectionTester.DATABASE_IS_INVALID)
+ this.connection_status = status;
+ break;
+ case ConnectionTester.CONNECTION_IS_OKAY:
+ if (status != ConnectionTester.CONNECTION_IS_OKAY)
+ this.connection_status = status;
+ break;
+ default:
+ throw new InternalError(this + " -- Illegal Connection Status: " + this.connection_status);
+ }
+ }
+
+ private Set resultSets( Statement stmt, boolean create )
+ {
+ Set out = (Set) resultSetsForStatements.get( stmt );
+ if ( out == null && create )
+ {
+ out = new HashSet();
+ resultSetsForStatements.put( stmt, out );
+ }
+ return out;
+ }
+
+// used by C3P0PooledConnectionPool
+ Connection getPhysicalConnection()
+ { return physicalConnection; }
+
+// static utility functions
+ private static void logCloseExceptions( Throwable cause, Collection exceptions )
+ {
+ if ( logger.isLoggable( MLevel.INFO ) )
+ {
+ if (cause != null)
+ {
+ // System.err.println("[c3p0] A PooledConnection died due to the following error!");
+ // cause.printStackTrace();
+ logger.log(MLevel.INFO, "[c3p0] A PooledConnection died due to the following error!", cause);
+ }
+ if ( exceptions != null && exceptions.size() > 0)
+ {
+ if ( cause == null )
+ logger.info("[c3p0] Exceptions occurred while trying to close a PooledConnection's resources normally.");
+ //System.err.println("[c3p0] The following Exceptions occurred while trying to close a PooledConnection's resources normally.");
+ else
+ logger.info("[c3p0] Exceptions occurred while trying to close a Broken PooledConnection.");
+ //System.err.println("[c3p0] The following Exceptions occurred while trying to close a broken PooledConnection.");
+ for ( Iterator ii = exceptions.iterator(); ii.hasNext(); )
+ {
+ Throwable t = (Throwable) ii.next();
+ // System.err.print("[c3p0 -- close Exception]: ");
+ // t.printStackTrace();
+ logger.log(MLevel.INFO, "[c3p0] NewPooledConnection close Exception.", t);
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/NullStatementSetManagedResultSet.java b/src/classes/com/mchange/v2/c3p0/impl/NullStatementSetManagedResultSet.java
new file mode 100644
index 0000000..a0998c6
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/NullStatementSetManagedResultSet.java
@@ -0,0 +1,47 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+/*
+ * Created on Apr 6, 2004
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Set;
+
+
+final class NullStatementSetManagedResultSet extends SetManagedResultSet
+{
+NullStatementSetManagedResultSet(Set activeResultSets)
+{ super( activeResultSets ); }
+
+NullStatementSetManagedResultSet(ResultSet inner, Set activeResultSets)
+{ super( inner, activeResultSets); }
+
+public Statement getStatement()
+{ return null; }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/impl/SetManagedDatabaseMetaData.java b/src/classes/com/mchange/v2/c3p0/impl/SetManagedDatabaseMetaData.java
new file mode 100644
index 0000000..83d5c80
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/SetManagedDatabaseMetaData.java
@@ -0,0 +1,136 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.*;
+import java.util.Set;
+import com.mchange.v2.sql.filter.FilterDatabaseMetaData;
+
+final class SetManagedDatabaseMetaData extends FilterDatabaseMetaData
+{
+ Set activeResultSets;
+ Connection returnableProxy;
+
+ SetManagedDatabaseMetaData( DatabaseMetaData inner, Set activeResultSets, Connection returnableProxy )
+ {
+ super( inner );
+ this.activeResultSets = activeResultSets;
+ this.returnableProxy = returnableProxy;
+ }
+
+ public Connection getConnection() throws SQLException
+ { return returnableProxy; }
+
+ public ResultSet getProcedures(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getProcedures(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getProcedureColumns(String a, String b, String c, String d) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getProcedureColumns(a, b, c, d), activeResultSets );
+ }
+
+ public ResultSet getTables(String a, String b, String c, String[] d) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getTables(a, b, c, d), activeResultSets );
+ }
+
+ public ResultSet getSchemas() throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getSchemas(), activeResultSets );
+ }
+
+ public ResultSet getCatalogs() throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getCatalogs(), activeResultSets );
+ }
+
+ public ResultSet getTableTypes() throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getTableTypes(), activeResultSets );
+ }
+
+ public ResultSet getColumns(String a, String b, String c, String d) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getColumns(a, b, c, d), activeResultSets );
+ }
+
+ public ResultSet getColumnPrivileges(String a, String b, String c, String d) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getColumnPrivileges(a, b, c, d), activeResultSets );
+ }
+
+ public ResultSet getTablePrivileges(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getTablePrivileges(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getBestRowIdentifier(String a, String b, String c, int d, boolean e) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getBestRowIdentifier(a, b, c, d, e), activeResultSets );
+ }
+
+ public ResultSet getVersionColumns(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getVersionColumns(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getPrimaryKeys(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getPrimaryKeys(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getImportedKeys(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getImportedKeys(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getExportedKeys(String a, String b, String c) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getExportedKeys(a, b, c), activeResultSets );
+ }
+
+ public ResultSet getCrossReference(String a, String b, String c, String d, String e, String f) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getCrossReference(a, b, c, d, e, f), activeResultSets );
+ }
+
+ public ResultSet getTypeInfo() throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getTypeInfo(), activeResultSets );
+ }
+
+ public ResultSet getIndexInfo(String a, String b, String c, boolean d, boolean e) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getIndexInfo(a, b, c, d, e), activeResultSets );
+ }
+
+ public ResultSet getUDTs(String a, String b, String c, int[] d) throws SQLException
+ {
+ return new NullStatementSetManagedResultSet( inner.getUDTs(a, b, c, d), activeResultSets );
+ }
+}
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/impl/SetManagedResultSet.java b/src/classes/com/mchange/v2/c3p0/impl/SetManagedResultSet.java
new file mode 100644
index 0000000..e05b463
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/SetManagedResultSet.java
@@ -0,0 +1,60 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.*;
+import java.util.Set;
+import com.mchange.v2.sql.filter.FilterResultSet;
+
+abstract class SetManagedResultSet extends FilterResultSet
+{
+ Set activeResultSets;
+
+ SetManagedResultSet(Set activeResultSets)
+ {
+ this.activeResultSets = activeResultSets;
+ }
+
+ SetManagedResultSet(ResultSet inner, Set activeResultSets)
+ {
+ super( inner );
+ this.activeResultSets = activeResultSets;
+ }
+
+ public synchronized void setInner(ResultSet inner)
+ {
+ this.inner = inner;
+ activeResultSets.add( inner );
+ }
+
+ public synchronized void close() throws SQLException
+ {
+ if ( inner != null )
+ {
+ inner.close();
+ activeResultSets.remove( inner );
+ inner = null;
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/impl/SnatchFromSetResultSet.java b/src/classes/com/mchange/v2/c3p0/impl/SnatchFromSetResultSet.java
new file mode 100644
index 0000000..1eaf123
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/impl/SnatchFromSetResultSet.java
@@ -0,0 +1,49 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.impl;
+
+import java.sql.*;
+import java.util.Set;
+import com.mchange.v2.sql.filter.FilterResultSet;
+
+final class SnatchFromSetResultSet extends FilterResultSet
+{
+ Set activeResultSets;
+
+ SnatchFromSetResultSet(Set activeResultSets)
+ { this.activeResultSets = activeResultSets; }
+
+ public synchronized void setInner(ResultSet inner)
+ {
+ this.inner = inner;
+ activeResultSets.add( inner );
+ }
+
+ public synchronized void close() throws SQLException
+ {
+ inner.close();
+ activeResultSets.remove( inner );
+ inner = null;
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSource.java b/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSource.java
new file mode 100644
index 0000000..bc4303d
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSource.java
@@ -0,0 +1,500 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.jboss;
+
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.log.*;
+import java.beans.PropertyVetoException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import javax.sql.DataSource;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingException;
+
+public class C3P0PooledDataSource implements C3P0PooledDataSourceMBean
+{
+ private final static MLogger logger = MLog.getLogger( C3P0PooledDataSource.class );
+
+ String jndiName;
+
+ ComboPooledDataSource combods = new ComboPooledDataSource();
+
+ private void rebind() throws NamingException
+ { rebind(null); }
+
+ private void rebind(String unbindName) throws NamingException
+ {
+ InitialContext ictx = new InitialContext();
+ if (unbindName != null)
+ ictx.unbind( unbindName );
+
+ if (jndiName != null)
+ {
+ // Thanks to David D. Kilzer for this code to auto-create
+ // subcontext paths!
+ Name name = ictx.getNameParser( jndiName ).parse( jndiName );
+ Context ctx = ictx;
+ for (int i = 0, max = name.size() - 1; i < max; i++)
+ {
+ try
+ { ctx = ctx.createSubcontext( name.get( i ) ); }
+ catch (NameAlreadyBoundException ignore)
+ { ctx = (Context) ctx.lookup( name.get( i ) ); }
+ }
+
+ ictx.rebind( jndiName, combods );
+ }
+
+
+ }
+
+ // Jndi Setup Names
+ public void setJndiName(String jndiName) throws NamingException
+ {
+ String unbindName = this.jndiName;
+ this.jndiName = jndiName;
+ rebind( unbindName );
+ }
+
+ public String getJndiName()
+ { return jndiName; }
+
+ // DriverManagerDataSourceProperties (count: 4)
+ public String getDescription()
+ { return combods.getDescription(); }
+
+ public void setDescription( String description ) throws NamingException
+ {
+ combods.setDescription( description );
+ rebind();
+ }
+
+ public String getDriverClass()
+ { return combods.getDriverClass(); }
+
+ public void setDriverClass( String driverClass ) throws PropertyVetoException, NamingException
+ {
+ combods.setDriverClass( driverClass );
+ rebind();
+ }
+
+ public String getJdbcUrl()
+ { return combods.getJdbcUrl(); }
+
+ public void setJdbcUrl( String jdbcUrl ) throws NamingException
+ {
+ combods.setJdbcUrl( jdbcUrl );
+ rebind();
+ }
+
+ // DriverManagerDataSource "virtual properties" based on properties
+ public String getUser()
+ { return combods.getUser(); }
+
+ public void setUser( String user ) throws NamingException
+ {
+ combods.setUser( user );
+ rebind();
+ }
+
+ public String getPassword()
+ { return combods.getPassword(); }
+
+ public void setPassword( String password ) throws NamingException
+ {
+ combods.setPassword( password );
+ rebind();
+ }
+
+ // WrapperConnectionPoolDataSource properties (count: 21)
+ public int getCheckoutTimeout()
+ { return combods.getCheckoutTimeout(); }
+
+ public void setCheckoutTimeout( int checkoutTimeout ) throws NamingException
+ {
+ combods.setCheckoutTimeout( checkoutTimeout );
+ rebind();
+ }
+
+ public int getAcquireIncrement()
+ { return combods.getAcquireIncrement(); }
+
+ public void setAcquireIncrement( int acquireIncrement ) throws NamingException
+ {
+ combods.setAcquireIncrement( acquireIncrement );
+ rebind();
+ }
+
+ public int getAcquireRetryAttempts()
+ { return combods.getAcquireRetryAttempts(); }
+
+ public void setAcquireRetryAttempts( int acquireRetryAttempts ) throws NamingException
+ {
+ combods.setAcquireRetryAttempts( acquireRetryAttempts );
+ rebind();
+ }
+
+ public int getAcquireRetryDelay()
+ { return combods.getAcquireRetryDelay(); }
+
+ public void setAcquireRetryDelay( int acquireRetryDelay ) throws NamingException
+ {
+ combods.setAcquireRetryDelay( acquireRetryDelay );
+ rebind();
+ }
+
+ public boolean isAutoCommitOnClose()
+ { return combods.isAutoCommitOnClose(); }
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose ) throws NamingException
+ {
+ combods.setAutoCommitOnClose( autoCommitOnClose );
+ rebind();
+ }
+
+ public String getConnectionTesterClassName()
+ { return combods.getConnectionTesterClassName(); }
+
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException, NamingException
+ {
+ combods.setConnectionTesterClassName( connectionTesterClassName );
+ rebind();
+ }
+
+ public String getAutomaticTestTable()
+ { return combods.getAutomaticTestTable(); }
+
+ public void setAutomaticTestTable( String automaticTestTable ) throws NamingException
+ {
+ combods.setAutomaticTestTable( automaticTestTable );
+ rebind();
+ }
+
+ public boolean isForceIgnoreUnresolvedTransactions()
+ { return combods.isForceIgnoreUnresolvedTransactions(); }
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions ) throws NamingException
+ {
+ combods.setForceIgnoreUnresolvedTransactions( forceIgnoreUnresolvedTransactions );
+ rebind();
+ }
+
+ public int getIdleConnectionTestPeriod()
+ { return combods.getIdleConnectionTestPeriod(); }
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod ) throws NamingException
+ {
+ combods.setIdleConnectionTestPeriod( idleConnectionTestPeriod );
+ rebind();
+ }
+
+ public int getInitialPoolSize()
+ { return combods.getInitialPoolSize(); }
+
+ public void setInitialPoolSize( int initialPoolSize ) throws NamingException
+ {
+ combods.setInitialPoolSize( initialPoolSize );
+ rebind();
+ }
+
+ public int getMaxIdleTime()
+ { return combods.getMaxIdleTime(); }
+
+ public void setMaxIdleTime( int maxIdleTime ) throws NamingException
+ {
+ combods.setMaxIdleTime( maxIdleTime );
+ rebind();
+ }
+
+ public int getMaxPoolSize()
+ { return combods.getMaxPoolSize(); }
+
+ public void setMaxPoolSize( int maxPoolSize ) throws NamingException
+ {
+ combods.setMaxPoolSize( maxPoolSize );
+ rebind();
+ }
+
+ public int getMaxStatements()
+ { return combods.getMaxStatements(); }
+
+ public void setMaxStatements( int maxStatements ) throws NamingException
+ {
+ combods.setMaxStatements( maxStatements );
+ rebind();
+ }
+
+ public int getMaxStatementsPerConnection()
+ { return combods.getMaxStatementsPerConnection(); }
+
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection ) throws NamingException
+ {
+ combods.setMaxStatementsPerConnection( maxStatementsPerConnection );
+ rebind();
+ }
+
+ public int getMinPoolSize()
+ { return combods.getMinPoolSize(); }
+
+ public void setMinPoolSize( int minPoolSize ) throws NamingException
+ {
+ combods.setMinPoolSize( minPoolSize );
+ rebind();
+ }
+
+ public int getPropertyCycle()
+ { return combods.getPropertyCycle(); }
+
+ public void setPropertyCycle( int propertyCycle ) throws NamingException
+ {
+ combods.setPropertyCycle( propertyCycle );
+ rebind();
+ }
+
+ public boolean isBreakAfterAcquireFailure()
+ { return combods.isBreakAfterAcquireFailure(); }
+
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure ) throws NamingException
+ {
+ combods.setBreakAfterAcquireFailure( breakAfterAcquireFailure );
+ rebind();
+ }
+
+ public boolean isTestConnectionOnCheckout()
+ { return combods.isTestConnectionOnCheckout(); }
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout ) throws NamingException
+ {
+ combods.setTestConnectionOnCheckout( testConnectionOnCheckout );
+ rebind();
+ }
+
+ public boolean isTestConnectionOnCheckin()
+ { return combods.isTestConnectionOnCheckin(); }
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin ) throws NamingException
+ {
+ combods.setTestConnectionOnCheckin( testConnectionOnCheckin );
+ rebind();
+ }
+
+ public boolean isUsesTraditionalReflectiveProxies()
+ { return combods.isUsesTraditionalReflectiveProxies(); }
+
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies ) throws NamingException
+ {
+ combods.setUsesTraditionalReflectiveProxies( usesTraditionalReflectiveProxies );
+ rebind();
+ }
+
+ public String getPreferredTestQuery()
+ { return combods.getPreferredTestQuery(); }
+
+ public void setPreferredTestQuery( String preferredTestQuery ) throws NamingException
+ {
+ combods.setPreferredTestQuery( preferredTestQuery );
+ rebind();
+ }
+
+ // PoolBackedDataSource properties (count: 2)
+ public String getDataSourceName()
+ { return combods.getDataSourceName(); }
+
+ public void setDataSourceName( String name ) throws NamingException
+ {
+ combods.setDataSourceName( name );
+ rebind();
+ }
+
+ public int getNumHelperThreads()
+ { return combods.getNumHelperThreads(); }
+
+ public void setNumHelperThreads( int numHelperThreads ) throws NamingException
+ {
+ combods.setNumHelperThreads( numHelperThreads );
+ rebind();
+ }
+
+ // shared properties (count: 1)
+ public String getFactoryClassLocation()
+ { return combods.getFactoryClassLocation(); }
+
+ public void setFactoryClassLocation( String factoryClassLocation ) throws NamingException
+ {
+ combods.setFactoryClassLocation( factoryClassLocation );
+ rebind();
+ }
+
+ // PooledDataSource statistics
+
+ public int getNumUserPools() throws SQLException
+ { return combods.getNumUserPools(); }
+
+ public int getNumConnectionsDefaultUser() throws SQLException
+ { return combods.getNumConnectionsDefaultUser(); }
+
+ public int getNumIdleConnectionsDefaultUser() throws SQLException
+ { return combods.getNumIdleConnectionsDefaultUser(); }
+
+ public int getNumBusyConnectionsDefaultUser() throws SQLException
+ { return combods.getNumBusyConnectionsDefaultUser(); }
+
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException
+ { return combods.getNumUnclosedOrphanedConnectionsDefaultUser(); }
+
+ public int getNumConnections(String username, String password) throws SQLException
+ { return combods.getNumConnections(username, password); }
+
+ public int getNumIdleConnections(String username, String password) throws SQLException
+ { return combods.getNumIdleConnections(username, password); }
+
+ public int getNumBusyConnections(String username, String password) throws SQLException
+ { return combods.getNumBusyConnections(username, password); }
+
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException
+ { return combods.getNumUnclosedOrphanedConnections(username, password); }
+
+ public int getNumConnectionsAllUsers() throws SQLException
+ { return combods.getNumConnectionsAllUsers(); }
+
+ public int getNumIdleConnectionsAllUsers() throws SQLException
+ { return combods.getNumIdleConnectionsAllUsers(); }
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException
+ { return combods.getNumBusyConnectionsAllUsers(); }
+
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException
+ { return combods.getNumUnclosedOrphanedConnectionsAllUsers(); }
+
+ // PooledDataSource operations
+ public void softResetDefaultUser() throws SQLException
+ { combods.softResetDefaultUser(); }
+
+ public void softReset(String username, String password) throws SQLException
+ { combods.softReset(username, password); }
+
+ public void softResetAllUsers() throws SQLException
+ { combods.softResetAllUsers(); }
+
+ public void hardReset() throws SQLException
+ { combods.hardReset(); }
+
+ public void close() throws SQLException
+ { combods.close(); }
+
+ //JBoss only... (but these methods need not be called for the mbean to work)
+ public void create() throws Exception
+ { }
+
+ // the mbean works without this, but if called we start populating the pool early
+ public void start() throws Exception
+ {
+ //System.err.println("Bound C3P0 PooledDataSource to name '" + jndiName + "'. Starting...");
+ logger.log(MLevel.INFO, "Bound C3P0 PooledDataSource to name ''{0}''. Starting...", jndiName);
+ combods.getNumBusyConnectionsDefaultUser(); //just touch the datasource to start it up.
+ }
+
+
+ public void stop()
+ { }
+
+ public void destroy()
+ {
+ try
+ {
+ combods.close();
+ logger.log(MLevel.INFO, "Destroyed C3P0 PooledDataSource with name ''{0}''.", jndiName);
+ }
+ catch (Exception e)
+ {
+ logger.log(MLevel.INFO, "Failed to destroy C3P0 PooledDataSource.", e);
+ }
+ }
+
+ public String getConnectionCustomizerClassName()
+ { return combods.getConnectionCustomizerClassName(); }
+
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException
+ { return combods.getEffectivePropertyCycle(username, password); }
+
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException
+ { return combods.getEffectivePropertyCycleDefaultUser(); }
+
+ public int getMaxAdministrativeTaskTime()
+ { return combods.getMaxAdministrativeTaskTime(); }
+
+ public int getMaxConnectionAge()
+ { return combods.getMaxConnectionAge(); }
+
+ public int getMaxIdleTimeExcessConnections()
+ { return combods.getMaxIdleTimeExcessConnections(); }
+
+ public int getUnreturnedConnectionTimeout()
+ { return combods.getUnreturnedConnectionTimeout(); }
+
+ public boolean isDebugUnreturnedConnectionStackTraces()
+ { return combods.isDebugUnreturnedConnectionStackTraces(); }
+
+ public void setConnectionCustomizerClassName(String connectionCustomizerClassName) throws NamingException
+ {
+ combods.setConnectionCustomizerClassName(connectionCustomizerClassName);
+ rebind();
+ }
+
+ public void setDebugUnreturnedConnectionStackTraces(boolean debugUnreturnedConnectionStackTraces) throws NamingException
+ {
+ combods.setDebugUnreturnedConnectionStackTraces(debugUnreturnedConnectionStackTraces);
+ rebind();
+ }
+
+ public void setMaxAdministrativeTaskTime(int maxAdministrativeTaskTime) throws NamingException
+ {
+ combods.setMaxAdministrativeTaskTime(maxAdministrativeTaskTime);
+ rebind();
+ }
+
+ public void setMaxConnectionAge(int maxConnectionAge) throws NamingException
+ {
+ combods.setMaxConnectionAge( maxConnectionAge );
+ rebind();
+ }
+
+ public void setMaxIdleTimeExcessConnections(int maxIdleTimeExcessConnections) throws NamingException
+ {
+ combods.setMaxIdleTimeExcessConnections(maxIdleTimeExcessConnections);
+ rebind();
+ }
+
+ public void setUnreturnedConnectionTimeout(int unreturnedConnectionTimeout) throws NamingException
+ {
+ combods.setUnreturnedConnectionTimeout(unreturnedConnectionTimeout);
+ rebind();
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSourceMBean.java b/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSourceMBean.java
new file mode 100644
index 0000000..236fa02
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/jboss/C3P0PooledDataSourceMBean.java
@@ -0,0 +1,184 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.jboss;
+
+import com.mchange.v2.c3p0.*;
+import java.beans.PropertyVetoException;
+import java.sql.SQLException;
+import java.util.Properties;
+import javax.naming.NamingException;
+
+public interface C3P0PooledDataSourceMBean
+{
+ // Jndi Setup
+ public void setJndiName(String jndiName) throws NamingException;
+
+ public String getJndiName();
+
+ // DriverManagerDataSourceProperties
+ public String getDescription();
+
+ public void setDescription( String description ) throws NamingException;
+
+ public String getDriverClass();
+
+ public void setDriverClass( String driverClass ) throws PropertyVetoException, NamingException;
+
+ public String getJdbcUrl();
+
+ public void setJdbcUrl( String jdbcUrl ) throws NamingException;
+
+ // DriverManagerDataSource "virtual properties" based on properties
+ public String getUser();
+
+ public void setUser( String user ) throws NamingException;
+
+ public String getPassword();
+
+ public void setPassword( String password ) throws NamingException;
+
+ // WrapperConnectionPoolDataSource properties
+ public int getUnreturnedConnectionTimeout();
+ public void setUnreturnedConnectionTimeout(int unreturnedConnectionTimeout) throws NamingException;
+
+ public boolean isDebugUnreturnedConnectionStackTraces();
+ public void setDebugUnreturnedConnectionStackTraces(boolean debugUnreturnedConnectionStackTraces) throws NamingException;
+
+ public String getConnectionCustomizerClassName();
+ public void setConnectionCustomizerClassName( String connectionCustomizerClassName ) throws NamingException;
+
+ public int getMaxConnectionAge();
+ public void setMaxConnectionAge( int maxConnectionAge ) throws NamingException;
+
+ public int getMaxIdleTimeExcessConnections();
+ public void setMaxIdleTimeExcessConnections( int maxIdleTimeExcessConnections ) throws NamingException;
+
+ public int getMaxAdministrativeTaskTime();
+ public void setMaxAdministrativeTaskTime( int maxAdministrativeTaskTime ) throws NamingException;
+
+ public int getCheckoutTimeout();
+ public void setCheckoutTimeout( int checkoutTimeout ) throws NamingException;
+
+ public int getAcquireIncrement();
+ public void setAcquireIncrement( int acquireIncrement ) throws NamingException;
+
+ public int getAcquireRetryAttempts();
+ public void setAcquireRetryAttempts( int acquireRetryAttempts ) throws NamingException;
+
+ public int getAcquireRetryDelay();
+ public void setAcquireRetryDelay( int acquireRetryDelay ) throws NamingException;
+
+ public boolean isAutoCommitOnClose();
+ public void setAutoCommitOnClose( boolean autoCommitOnClose ) throws NamingException;
+
+ public String getConnectionTesterClassName();
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException, NamingException;
+
+ public String getAutomaticTestTable();
+ public void setAutomaticTestTable( String automaticTestTable ) throws NamingException;
+
+ public boolean isForceIgnoreUnresolvedTransactions();
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions ) throws NamingException;
+
+ public int getIdleConnectionTestPeriod();
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod ) throws NamingException;
+
+ public int getInitialPoolSize();
+ public void setInitialPoolSize( int initialPoolSize ) throws NamingException;
+
+ public int getMaxIdleTime();
+ public void setMaxIdleTime( int maxIdleTime ) throws NamingException;
+
+ public int getMaxPoolSize();
+ public void setMaxPoolSize( int maxPoolSize ) throws NamingException;
+
+ public int getMaxStatements();
+ public void setMaxStatements( int maxStatements ) throws NamingException;
+
+ public int getMaxStatementsPerConnection();
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection ) throws NamingException;
+
+ public int getMinPoolSize();
+ public void setMinPoolSize( int minPoolSize ) throws NamingException;
+
+ public int getPropertyCycle();
+ public void setPropertyCycle( int propertyCycle ) throws NamingException;
+
+ public boolean isBreakAfterAcquireFailure();
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure ) throws NamingException;
+
+ public boolean isTestConnectionOnCheckout();
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout ) throws NamingException;
+
+ public boolean isTestConnectionOnCheckin();
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin ) throws NamingException;
+
+ public boolean isUsesTraditionalReflectiveProxies();
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies ) throws NamingException;
+
+ public String getPreferredTestQuery();
+ public void setPreferredTestQuery( String preferredTestQuery ) throws NamingException;
+
+ // PoolBackedDataSource properties (count: 2)
+ public int getNumHelperThreads();
+ public void setNumHelperThreads( int numHelperThreads ) throws NamingException;
+
+ // shared properties (count: 1)
+ public String getFactoryClassLocation();
+ public void setFactoryClassLocation( String factoryClassLocation ) throws NamingException;
+
+ // PooledDataSource statistics
+
+ public int getNumUserPools() throws SQLException;
+
+ public int getNumConnectionsDefaultUser() throws SQLException;
+ public int getNumIdleConnectionsDefaultUser() throws SQLException;
+ public int getNumBusyConnectionsDefaultUser() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException;
+
+ public int getNumConnections(String username, String password) throws SQLException;
+ public int getNumIdleConnections(String username, String password) throws SQLException;
+ public int getNumBusyConnections(String username, String password) throws SQLException;
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException;
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException;
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException;
+ public int getNumIdleConnectionsAllUsers() throws SQLException;
+ public int getNumConnectionsAllUsers() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException;
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException;
+
+ // PooledDataSource operations
+ public void softResetDefaultUser() throws SQLException;
+ public void softReset(String username, String password) throws SQLException;
+ public void softResetAllUsers() throws SQLException;
+ public void hardReset() throws SQLException;
+ public void close() throws SQLException;
+
+ //JBoss only... (but these methods need not be called for the mbean to work)
+ public void create() throws Exception;
+ public void start() throws Exception;
+ public void stop();
+ public void destroy();
+}
diff --git a/src/classes/com/mchange/v2/c3p0/management/ActiveManagementCoordinator.java b/src/classes/com/mchange/v2/c3p0/management/ActiveManagementCoordinator.java
new file mode 100644
index 0000000..5d09175
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/ActiveManagementCoordinator.java
@@ -0,0 +1,151 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.lang.management.*;
+import javax.management.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.c3p0.*;
+
+public class ActiveManagementCoordinator implements ManagementCoordinator
+{
+ private final static String C3P0_REGISTRY_NAME = "com.mchange.v2.c3p0:type=C3P0Registry";
+
+ //MT: thread-safe
+ final static MLogger logger = MLog.getLogger( ActiveManagementCoordinator.class );
+
+ MBeanServer mbs;
+
+ public ActiveManagementCoordinator() throws Exception
+ {
+ this.mbs = ManagementFactory.getPlatformMBeanServer();
+ }
+
+ public void attemptManageC3P0Registry()
+ {
+ try
+ {
+ ObjectName name = new ObjectName(C3P0_REGISTRY_NAME );
+ C3P0RegistryManager mbean = new C3P0RegistryManager();
+
+ if (mbs.isRegistered(name))
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ {
+ logger.warning("A C3P0Registry mbean is already registered. " +
+ "This probably means that an application using c3p0 was undeployed, " +
+ "but not all PooledDataSources were closed prior to undeployment. " +
+ "This may lead to resource leaks over time. Please take care to close " +
+ "all PooledDataSources.");
+ }
+ mbs.unregisterMBean(name);
+ }
+ mbs.registerMBean(mbean, name);
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "Failed to set up C3P0RegistryManager mBean. " +
+ "[c3p0 will still function normally, but management via JMX may not be possible.]",
+ e);
+ }
+ }
+
+ public void attemptUnmanageC3P0Registry()
+ {
+ try
+ {
+ ObjectName name = new ObjectName(C3P0_REGISTRY_NAME );
+ if (mbs.isRegistered(name))
+ {
+ mbs.unregisterMBean(name);
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "C3P0Registry mbean unregistered.");
+ }
+ else if (logger.isLoggable(MLevel.FINE))
+ logger.fine("The C3P0Registry mbean was not found in the registry, so could not be unregistered.");
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "An Exception occurred while trying to unregister the C3P0RegistryManager mBean." +
+ e);
+ }
+ }
+
+ public void attemptManagePooledDataSource(PooledDataSource pds)
+ {
+ String name = getPdsObjectNameStr( pds );
+ try
+ {
+ //PooledDataSourceManager mbean = new PooledDataSourceManager( pds );
+ //mbs.registerMBean(mbean, ObjectName.getInstance(name));
+ //if (logger.isLoggable(MLevel.FINER))
+ // logger.log(MLevel.FINER, "MBean: " + name + " registered.");
+
+ // DynamicPooledDataSourceManagerMBean registers itself on construction (and logs its own registration)
+ DynamicPooledDataSourceManagerMBean mbean = new DynamicPooledDataSourceManagerMBean( pds, name, mbs );
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "Failed to set up a PooledDataSourceManager mBean. [" + name + "] " +
+ "[c3p0 will still functioning normally, but management via JMX may not be possible.]",
+ e);
+ }
+ }
+
+
+ public void attemptUnmanagePooledDataSource(PooledDataSource pds)
+ {
+ String nameStr = getPdsObjectNameStr( pds );
+ try
+ {
+ ObjectName name = new ObjectName( nameStr );
+ if (mbs.isRegistered(name))
+ {
+ mbs.unregisterMBean(name);
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "MBean: " + nameStr + " unregistered.");
+ }
+ else
+ if (logger.isLoggable(MLevel.FINE))
+ logger.fine("The mbean " + nameStr + " was not found in the registry, so could not be unregistered.");
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "An Exception occurred while unregistering mBean. [" + nameStr + "] " +
+ e);
+ }
+ }
+
+ private String getPdsObjectNameStr(PooledDataSource pds)
+ { return "com.mchange.v2.c3p0:type=PooledDataSource[" + pds.getIdentityToken() + "]"; }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManager.java b/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManager.java
new file mode 100644
index 0000000..519a3fb
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManager.java
@@ -0,0 +1,77 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.util.*;
+import java.sql.SQLException;
+import com.mchange.v2.c3p0.C3P0Registry;
+import com.mchange.v2.c3p0.subst.C3P0Substitutions;
+
+public class C3P0RegistryManager implements C3P0RegistryManagerMBean
+{
+ public String[] getAllIdentityTokens()
+ {
+ Set tokens = C3P0Registry.allIdentityTokens();
+ return (String[]) tokens.toArray( new String[ tokens.size() ] );
+ }
+
+ public Set getAllIdentityTokenized()
+ { return C3P0Registry.allIdentityTokenized(); }
+
+ public Set getAllPooledDataSources()
+ { return C3P0Registry.allPooledDataSources(); }
+
+ public int getAllIdentityTokenCount()
+ { return C3P0Registry.allIdentityTokens().size(); }
+
+ public int getAllIdentityTokenizedCount()
+ { return C3P0Registry.allIdentityTokenized().size(); }
+
+ public int getAllPooledDataSourcesCount()
+ { return C3P0Registry.allPooledDataSources().size(); }
+
+ public String[] getAllIdentityTokenizedStringified()
+ { return stringifySet( C3P0Registry.allIdentityTokenized() ); }
+
+ public String[] getAllPooledDataSourcesStringified()
+ { return stringifySet( C3P0Registry.allPooledDataSources() ); }
+
+ public int getNumPooledDataSources() throws SQLException
+ { return C3P0Registry.getNumPooledDataSources(); }
+
+ public int getNumPoolsAllDataSources() throws SQLException
+ { return C3P0Registry.getNumPoolsAllDataSources(); }
+
+ public String getC3p0Version()
+ { return C3P0Substitutions.VERSION ; }
+
+ private String[] stringifySet(Set s)
+ {
+ String[] out = new String[ s.size() ];
+ int i = 0;
+ for (Iterator ii = s.iterator(); ii.hasNext(); )
+ out[i++] = ii.next().toString();
+ return out;
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManagerMBean.java b/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManagerMBean.java
new file mode 100644
index 0000000..de7d10d
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/C3P0RegistryManagerMBean.java
@@ -0,0 +1,46 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.sql.SQLException;
+import java.util.Set;
+
+public interface C3P0RegistryManagerMBean
+{
+ public String[] getAllIdentityTokens();
+ public Set getAllIdentityTokenized();
+ public Set getAllPooledDataSources();
+
+ public int getAllIdentityTokenCount();
+ public int getAllIdentityTokenizedCount();
+ public int getAllPooledDataSourcesCount();
+
+ public String[] getAllIdentityTokenizedStringified();
+ public String[] getAllPooledDataSourcesStringified();
+
+ public int getNumPooledDataSources() throws SQLException;
+ public int getNumPoolsAllDataSources() throws SQLException;
+
+ public String getC3p0Version();
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/management/DynamicPooledDataSourceManagerMBean.java b/src/classes/com/mchange/v2/c3p0/management/DynamicPooledDataSourceManagerMBean.java
new file mode 100644
index 0000000..c7460a9
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/DynamicPooledDataSourceManagerMBean.java
@@ -0,0 +1,619 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.*;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.DataSource;
+
+import com.mchange.v1.lang.ClassUtils;
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+import com.mchange.v2.c3p0.DriverManagerDataSource;
+import com.mchange.v2.c3p0.PooledDataSource;
+import com.mchange.v2.c3p0.PoolBackedDataSource;
+import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
+import com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.management.ManagementUtils;
+
+public class DynamicPooledDataSourceManagerMBean implements DynamicMBean
+{
+ final static MLogger logger = MLog.getLogger( DynamicPooledDataSourceManagerMBean.class );
+
+ final static Set HIDE_PROPS;
+ final static Set HIDE_OPS;
+ final static Set FORCE_OPS;
+
+ final static Set FORCE_READ_ONLY_PROPS;
+
+ static
+ {
+ Set hpTmp = new HashSet();
+ hpTmp.add("connectionPoolDataSource");
+ hpTmp.add("nestedDataSource");
+ hpTmp.add("reference");
+ hpTmp.add("connection");
+ hpTmp.add("password");
+ hpTmp.add("pooledConnection");
+ hpTmp.add("properties");
+ hpTmp.add("logWriter");
+ hpTmp.add("lastAcquisitionFailureDefaultUser");
+ hpTmp.add("lastCheckoutFailureDefaultUser");
+ hpTmp.add("lastCheckinFailureDefaultUser");
+ hpTmp.add("lastIdleTestFailureDefaultUser");
+ hpTmp.add("lastConnectionTestFailureDefaultUser");
+ HIDE_PROPS = Collections.unmodifiableSet( hpTmp );
+
+ Class[] userPassArgs = new Class[] { String.class, String.class };
+ Set hoTmp = new HashSet();
+ try
+ {
+ hoTmp.add(PooledDataSource.class.getMethod("close", new Class[] { boolean.class }) );
+ hoTmp.add(PooledDataSource.class.getMethod("getConnection", userPassArgs ) );
+
+ hoTmp.add(PooledDataSource.class.getMethod("getLastAcquisitionFailure", userPassArgs ) );
+ hoTmp.add(PooledDataSource.class.getMethod("getLastCheckinFailure", userPassArgs ) );
+ hoTmp.add(PooledDataSource.class.getMethod("getLastCheckoutFailure", userPassArgs ) );
+ hoTmp.add(PooledDataSource.class.getMethod("getLastIdleTestFailure", userPassArgs ) );
+ hoTmp.add(PooledDataSource.class.getMethod("getLastConnectionTestFailure", userPassArgs ) );
+ }
+ catch (Exception e)
+ {
+ logger.log(MLevel.WARNING, "Tried to hide an operation from being exposed by mbean, but failed to find the operation!", e);
+ }
+ HIDE_OPS = Collections.unmodifiableSet(hoTmp);
+
+ Set fropTmp = new HashSet();
+ fropTmp.add("identityToken");
+ FORCE_READ_ONLY_PROPS = Collections.unmodifiableSet(fropTmp);
+
+ Set foTmp = new HashSet();
+ FORCE_OPS = Collections.unmodifiableSet(foTmp);
+ }
+
+ final static MBeanOperationInfo[] OP_INFS = extractOpInfs();
+
+ MBeanInfo info = null;
+
+ PooledDataSource pds;
+ String mbeanName;
+ MBeanServer mbs;
+
+ ConnectionPoolDataSource cpds;
+ DataSource unpooledDataSource;
+
+ //attr names to attr infos
+ Map pdsAttrInfos;
+ Map cpdsAttrInfos;
+ Map unpooledDataSourceAttrInfos;
+
+ PropertyChangeListener pcl = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String propName = evt.getPropertyName();
+ Object val = evt.getNewValue();
+
+ if ("nestedDataSource".equals(propName) || "connectionPoolDataSource".equals(propName))
+ reinitialize();
+ }
+ };
+
+ public DynamicPooledDataSourceManagerMBean(PooledDataSource pds, String mbeanName, MBeanServer mbs)
+ throws Exception
+ {
+ this.pds = pds;
+ this.mbeanName = mbeanName;
+ this.mbs = mbs;
+
+ if (pds instanceof ComboPooledDataSource)
+ /* do nothing */;
+ else if (pds instanceof AbstractPoolBackedDataSource)
+ ((AbstractPoolBackedDataSource) pds).addPropertyChangeListener(pcl);
+ else
+ logger.warning(this + "managing an unexpected PooledDataSource. Only top-level attributes will be available. PooledDataSource: " + pds);
+
+ Exception e = reinitialize();
+ if (e != null)
+ throw e;
+ }
+
+ private synchronized Exception reinitialize()
+ {
+ try
+ {
+ // for ComboPooledDataSource, everything we care about is exposed via the PooledDataSource
+ // for other implementations, we have to pay attention to nested DataSources
+ if (!(pds instanceof ComboPooledDataSource) && pds instanceof AbstractPoolBackedDataSource)
+ {
+ if (this.cpds instanceof WrapperConnectionPoolDataSource) //implies non-null, this is a reinit
+ ((WrapperConnectionPoolDataSource) this.cpds).removePropertyChangeListener(pcl);
+
+
+ // yeah, we reassign instantly, but for my comfort...
+ this.cpds = null;
+ this.unpooledDataSource = null;
+
+ this.cpds = ((AbstractPoolBackedDataSource) pds).getConnectionPoolDataSource();
+
+ if (cpds instanceof WrapperConnectionPoolDataSource)
+ {
+ this.unpooledDataSource = ((WrapperConnectionPoolDataSource) cpds).getNestedDataSource();
+ ((WrapperConnectionPoolDataSource) this.cpds).addPropertyChangeListener(pcl);
+ }
+ }
+
+ pdsAttrInfos = extractAttributeInfos( pds );
+ cpdsAttrInfos = extractAttributeInfos( cpds );
+ unpooledDataSourceAttrInfos = extractAttributeInfos( unpooledDataSource );
+
+ Set allAttrNames = new HashSet();
+ allAttrNames.addAll(pdsAttrInfos.keySet());
+ allAttrNames.addAll(cpdsAttrInfos.keySet());
+ allAttrNames.addAll(unpooledDataSourceAttrInfos.keySet());
+
+ Set allAttrs = new HashSet();
+ for(Iterator ii = allAttrNames.iterator(); ii.hasNext();)
+ {
+ String name = (String) ii.next();
+ Object attrInfo;
+ attrInfo = pdsAttrInfos.get(name);
+ if (attrInfo == null)
+ attrInfo = cpdsAttrInfos.get(name);
+ if (attrInfo == null)
+ attrInfo = unpooledDataSourceAttrInfos.get(name);
+ allAttrs.add(attrInfo);
+ }
+
+ String className = this.getClass().getName();
+ MBeanAttributeInfo[] attrInfos = (MBeanAttributeInfo[]) allAttrs.toArray(new MBeanAttributeInfo[ allAttrs.size() ]);
+ Class[] ctorArgClasses = {PooledDataSource.class, String.class, MBeanServer.class};
+ MBeanConstructorInfo[] constrInfos
+ = new MBeanConstructorInfo[] { new MBeanConstructorInfo("Constructor from PooledDataSource", this.getClass().getConstructor(ctorArgClasses)) };
+ this.info = new MBeanInfo( this.getClass().getName(),
+ "An MBean to monitor and manage a PooledDataSource",
+ attrInfos,
+ constrInfos,
+ OP_INFS,
+ null);
+
+ // we need to reregister when the attributes we support may have changed, to be sure
+ // that the MBeanInfo is reread.
+ try
+ {
+ ObjectName oname = ObjectName.getInstance( mbeanName );
+ if (mbs.isRegistered( oname ))
+ {
+ mbs.unregisterMBean( oname );
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "MBean: " + mbeanName + " unregistered, in order to be reregistered after update.");
+ }
+ mbs.registerMBean( this, oname );
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "MBean: " + mbeanName + " registered.");
+
+ return null;
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable(MLevel.WARNING) )
+ logger.log(MLevel.WARNING,
+ "An Exception occurred while registering/reregistering mbean " + mbeanName +
+ ". MBean may not be registered, or may not work properly.",
+ e );
+ return e;
+ }
+ }
+ catch (NoSuchMethodException e)
+ {
+ if (logger.isLoggable(MLevel.SEVERE))
+ logger.log( MLevel.SEVERE,
+ "Huh? We can't find our own constructor?? The one we're in?",
+ e);
+ return e;
+ }
+ }
+
+ // this method is fragile, makes assumptions that may have to change with
+ // the PooledDataSource interface. It presumes that methods that look like
+ // JavaBean properties should be skipped as attributes, that methods with
+ // two string arguments are always username and password, that methods with
+ // a return value are simple getters, while void methods are modifiers. At the
+ // time of this writing, these assumptions all hold for PooledDataSource.
+ // But beware the future.
+ private static MBeanOperationInfo[] extractOpInfs()
+ {
+ MBeanParameterInfo user = new MBeanParameterInfo("user", "java.lang.String", "The database username of a pool-owner.");
+ MBeanParameterInfo pwd = new MBeanParameterInfo("password", "java.lang.String", "The database password of a pool-owner.");
+ MBeanParameterInfo[] userPass = {user, pwd};
+ MBeanParameterInfo[] empty = {};
+
+ Method[] meths = PooledDataSource.class.getMethods();
+ Set attrInfos = new TreeSet(ManagementUtils.OP_INFO_COMPARATOR);
+
+ for (int i = 0; i < meths.length; ++i)
+ {
+ Method meth = meths[i];
+ if (HIDE_OPS.contains(meth))
+ continue;
+
+ String mname = meth.getName();
+ Class[] params = meth.getParameterTypes();
+
+ if (! FORCE_OPS.contains(mname))
+ {
+ //get rid of things we'd have picked up as attributes
+ if (mname.startsWith("set") && params.length == 1)
+ continue;
+ if ((mname.startsWith("get") || mname.startsWith("is")) && params.length == 0)
+ continue;
+ }
+
+ Class retType = meth.getReturnType();
+ int impact = (retType == void.class ? MBeanOperationInfo.ACTION : MBeanOperationInfo.INFO);
+ MBeanParameterInfo[] pi;
+ if (params.length == 2 && params[0] == String.class && params[1] == String.class)
+ pi = userPass;
+ else if (params.length == 0)
+ pi = empty;
+ else
+ pi = null;
+
+ MBeanOperationInfo opi;
+ if (pi != null)
+ opi = new MBeanOperationInfo( mname, // name
+ null, // desc
+ pi,
+ retType.getName(),
+ impact );
+ else
+ {
+ //System.err.println("autobuilding opi from meth " + meth);
+ opi = new MBeanOperationInfo(meth.toString(), meth);
+ }
+
+ //System.err.println("Created MBeanOperationInfo: " + opi + " [" + opi.getName() + ']');
+ attrInfos.add( opi );
+ }
+
+ return (MBeanOperationInfo[]) attrInfos.toArray( new MBeanOperationInfo[ attrInfos.size() ] );
+ }
+
+ public synchronized Object getAttribute(String attr) throws AttributeNotFoundException, MBeanException, ReflectionException
+ {
+ try
+ {
+ AttrRec rec = attrRecForAttribute(attr);
+ if (rec == null)
+ throw new AttributeNotFoundException(attr);
+ else
+ {
+ MBeanAttributeInfo ai = rec.attrInfo;
+ if (! ai.isReadable() )
+ throw new IllegalArgumentException(attr + " not readable.");
+ else
+ {
+ String name = ai.getName();
+ String pfx = ai.isIs() ? "is" : "get";
+ String mname = pfx + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ Object target = rec.target;
+ Method m = target.getClass().getMethod(mname, null);
+ return m.invoke(target, null);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Failed to get requested attribute: " + attr, e);
+ throw new MBeanException(e);
+ }
+ }
+
+ public synchronized AttributeList getAttributes(String[] attrs)
+ {
+ AttributeList al = new AttributeList();
+ for (int i = 0, len = attrs.length; i < len; ++i)
+ {
+ String attr = attrs[i];
+ try
+ {
+ Object val = getAttribute(attr);
+ al.add(new Attribute(attr, val));
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Failed to get requested attribute (for list): " + attr, e);
+ }
+ }
+ return al;
+ }
+
+ private AttrRec attrRecForAttribute(String attr)
+ {
+ assert (Thread.holdsLock(this));
+
+ if (pdsAttrInfos.containsKey(attr))
+ return new AttrRec(pds, (MBeanAttributeInfo) pdsAttrInfos.get(attr));
+ else if (cpdsAttrInfos.containsKey(attr))
+ return new AttrRec(cpds, (MBeanAttributeInfo) cpdsAttrInfos.get(attr));
+ else if (unpooledDataSourceAttrInfos.containsKey(attr))
+ return new AttrRec(unpooledDataSource, (MBeanAttributeInfo) unpooledDataSourceAttrInfos.get(attr));
+ else
+ return null;
+ }
+
+ public synchronized MBeanInfo getMBeanInfo()
+ {
+ if (info == null)
+ reinitialize();
+ return info;
+ }
+
+ public synchronized Object invoke(String operation, Object[] paramVals, String[] signature) throws MBeanException, ReflectionException
+ {
+ try
+ {
+ int slen = signature.length;
+ Class[] paramTypes = new Class[ slen ];
+ for (int i = 0; i < slen; ++i)
+ paramTypes[i] = ClassUtils.forName( signature[i] );
+
+ //all operations should be on pds
+ Method m = pds.getClass().getMethod(operation, paramTypes);
+ return m.invoke(pds, paramVals);
+ }
+ catch (NoSuchMethodException e)
+ {
+ // although not generally legal as of the latest JMX spec,
+ // someone could be trying to work with attributes through
+ // invoke. If so, we try to deal
+ try
+ {
+ boolean two = false;
+ if (signature.length == 0 && ( operation.startsWith("get") || (two = operation.startsWith("is")) ))
+ {
+ int i = two ? 2 : 3;
+ String attr = Character.toLowerCase(operation.charAt(i)) + operation.substring(i + 1);
+ return getAttribute( attr );
+ }
+ else if (signature.length == 1 && operation.startsWith("set"))
+ {
+ setAttribute(new Attribute(Character.toLowerCase(operation.charAt(3)) + operation.substring(4), paramVals[0]));
+ return null;
+ }
+ else
+ throw new MBeanException(e);
+ }
+ catch (Exception e2)
+ { throw new MBeanException(e2); }
+ }
+ catch (Exception e)
+ { throw new MBeanException(e); }
+ }
+
+ public synchronized void setAttribute(Attribute attrObj) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
+ {
+ try
+ {
+ String attr = attrObj.getName();
+
+ if (attr == "factoryClassLocation") // special case
+ {
+ if (pds instanceof ComboPooledDataSource)
+ {
+ ((ComboPooledDataSource) pds).setFactoryClassLocation((String) attrObj.getValue());
+ return;
+ }
+ else if (pds instanceof AbstractPoolBackedDataSource)
+ {
+ String val = (String) attrObj.getValue();
+ AbstractPoolBackedDataSource apbds = ((AbstractPoolBackedDataSource) pds);
+ apbds.setFactoryClassLocation( val );
+ ConnectionPoolDataSource checkDs1 = apbds.getConnectionPoolDataSource();
+ if (checkDs1 instanceof WrapperConnectionPoolDataSource)
+ {
+ WrapperConnectionPoolDataSource wcheckDs1 = (WrapperConnectionPoolDataSource) checkDs1;
+ wcheckDs1.setFactoryClassLocation( val );
+ DataSource checkDs2 = wcheckDs1.getNestedDataSource();
+ if (checkDs2 instanceof DriverManagerDataSource)
+ ((DriverManagerDataSource) checkDs2).setFactoryClassLocation( val );
+ }
+ return;
+ }
+ // else try treating factoryClassLocation like any other attribute
+ // on the presumption that some future, unexpected DataSource that
+ // exposes this property will not require the property to be set at
+ // multiple levels, as PoolBackedDataSource does...
+ }
+
+ AttrRec rec = attrRecForAttribute(attr);
+ if (rec == null)
+ throw new AttributeNotFoundException(attr);
+ else
+ {
+ MBeanAttributeInfo ai = rec.attrInfo;
+ if (! ai.isWritable() )
+ throw new IllegalArgumentException(attr + " not writable.");
+ else
+ {
+ Class attrType = ClassUtils.forName( rec.attrInfo.getType() );
+ String name = ai.getName();
+ String pfx = "set";
+ String mname = pfx + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ Object target = rec.target;
+ Method m = target.getClass().getMethod(mname, new Class[] {attrType});
+ m.invoke(target, new Object[] { attrObj.getValue() });
+
+ // if we were unable to set this attribute directly in the PooledDataSource,
+ // we are updating a property of a nested DataSource, and we should reset
+ // the pool manager of the PooledDataSource implementation so that these
+ // properties are reread and the changes take effect.
+ if (target != pds)
+ {
+ if (pds instanceof AbstractPoolBackedDataSource)
+ ((AbstractPoolBackedDataSource) pds).resetPoolManager(false);
+ else if (logger.isLoggable(MLevel.WARNING))
+ logger.warning("MBean set a nested ConnectionPoolDataSource or DataSource parameter on an unknown PooledDataSource type. " +
+ "Could not reset the pool manager, so the changes may not take effect. " + "" +
+ "c3p0 may need to be updated for PooledDataSource type " + pds.getClass() + ".");
+
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Failed to set requested attribute: " + attrObj, e);
+ throw new MBeanException(e);
+ }
+ }
+
+ public synchronized AttributeList setAttributes(AttributeList al)
+ {
+ AttributeList out = new AttributeList();
+ for (int i = 0, len = al.size(); i < len; ++i)
+ {
+ Attribute attrObj = (Attribute) al.get(i);
+
+ try
+ {
+ this.setAttribute( attrObj );
+ out.add(attrObj);
+ }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Failed to set requested attribute (from list): " + attrObj, e);
+ }
+ }
+ return out;
+ }
+
+ private static Map extractAttributeInfos(Object bean)
+ {
+ if ( bean != null)
+ {
+ try
+ {
+ Map out = new HashMap();
+ BeanInfo beanInfo = Introspector.getBeanInfo( bean.getClass(), Object.class ); //so we don't see getClass() as a property
+ PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+ //System.err.println("ignoreProps: " + ignoreProps );
+ for( int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+
+ String name;
+ String desc;
+ Method getter;
+ Method setter;
+
+ name = pd.getName();
+
+ if (HIDE_PROPS.contains( name ))
+ continue;
+
+ desc = getDescription( name );
+ getter = pd.getReadMethod();
+ setter = pd.getWriteMethod();
+
+ if (FORCE_READ_ONLY_PROPS.contains(name))
+ setter = null;
+
+ /*
+ * Note that it's not a problem that these
+ * getters and setters are not against this class
+ * the MBeanAttributInfo just uses the method
+ * names and attribute type to construct itself,
+ * and does not hold the methods themselves for
+ * future invocation.
+ */
+
+ try
+ {
+ out.put( name, new MBeanAttributeInfo(name, desc, getter, setter) );
+ }
+ catch (javax.management.IntrospectionException e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING, "IntrospectionException while setting up MBean attribute '" + name + "'", e);
+ }
+ }
+
+ return Collections.synchronizedMap(out);
+ }
+ catch (java.beans.IntrospectionException e)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.log( MLevel.WARNING, "IntrospectionException while setting up MBean attributes for " + bean, e);
+ return Collections.EMPTY_MAP;
+ }
+ }
+ else
+ return Collections.EMPTY_MAP;
+ }
+
+ // TODO: use a ResourceBundle for attribute descriptions.
+ // (Extra credit -- build from xml file, build docs same way)
+ private static String getDescription(String attrName)
+ { return null; }
+
+ private static class AttrRec
+ {
+ Object target;
+ MBeanAttributeInfo attrInfo;
+
+ AttrRec(Object target, MBeanAttributeInfo attrInfo)
+ {
+ this.target = target;
+ this.attrInfo = attrInfo;
+ }
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/c3p0/management/ManagementCoordinator.java b/src/classes/com/mchange/v2/c3p0/management/ManagementCoordinator.java
new file mode 100644
index 0000000..32fda97
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/ManagementCoordinator.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import com.mchange.v2.c3p0.PooledDataSource;
+
+public interface ManagementCoordinator
+{
+ public void attemptManageC3P0Registry();
+ public void attemptUnmanageC3P0Registry();
+ public void attemptManagePooledDataSource(PooledDataSource pds);
+ public void attemptUnmanagePooledDataSource( PooledDataSource pds );
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/management/NullManagementCoordinator.java b/src/classes/com/mchange/v2/c3p0/management/NullManagementCoordinator.java
new file mode 100644
index 0000000..d3fe25f
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/NullManagementCoordinator.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import com.mchange.v2.c3p0.PooledDataSource;
+
+public class NullManagementCoordinator implements ManagementCoordinator
+{
+ public void attemptManageC3P0Registry()
+ {}
+
+ public void attemptUnmanageC3P0Registry()
+ {}
+
+ public void attemptManagePooledDataSource(PooledDataSource pds)
+ {}
+
+ public void attemptUnmanagePooledDataSource( PooledDataSource pds )
+ {}
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManager.java b/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManager.java
new file mode 100644
index 0000000..718bf84
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManager.java
@@ -0,0 +1,126 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import com.mchange.v2.c3p0.PooledDataSource;
+
+public class PooledDataSourceManager implements PooledDataSourceManagerMBean
+{
+ PooledDataSource pds;
+
+ public PooledDataSourceManager( PooledDataSource pds )
+ { this.pds = pds; }
+
+ public String getIdentityToken()
+ { return pds.getIdentityToken(); }
+
+ public String getDataSourceName()
+ { return pds.getDataSourceName(); }
+
+ public void setDataSourceName(String dataSourceName)
+ { pds.setDataSourceName( dataSourceName ); }
+
+ public int getNumConnectionsDefaultUser() throws SQLException
+ { return pds.getNumConnectionsDefaultUser(); }
+
+ public int getNumIdleConnectionsDefaultUser() throws SQLException
+ { return pds.getNumIdleConnectionsDefaultUser(); }
+
+ public int getNumBusyConnectionsDefaultUser() throws SQLException
+ { return pds.getNumBusyConnectionsDefaultUser(); }
+
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException
+ { return pds.getNumUnclosedOrphanedConnectionsDefaultUser(); }
+
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException
+ { return pds.getEffectivePropertyCycleDefaultUser(); }
+
+ public int getThreadPoolSize() throws SQLException
+ { return pds.getThreadPoolSize(); }
+
+ public int getThreadPoolNumActiveThreads() throws SQLException
+ { return pds.getThreadPoolNumActiveThreads(); }
+
+ public int getThreadPoolNumIdleThreads() throws SQLException
+ { return pds.getThreadPoolNumIdleThreads(); }
+
+ public int getThreadPoolNumTasksPending() throws SQLException
+ { return pds.getThreadPoolNumTasksPending(); }
+
+ public String sampleThreadPoolStackTraces() throws SQLException
+ { return pds.sampleThreadPoolStackTraces(); }
+
+ public String sampleThreadPoolStatus() throws SQLException
+ { return pds.sampleThreadPoolStatus(); }
+
+ public void softResetDefaultUser() throws SQLException
+ { pds.softResetDefaultUser(); }
+
+ public int getNumConnections(String username, String password) throws SQLException
+ { return pds.getNumConnections( username, password ); }
+
+ public int getNumIdleConnections(String username, String password) throws SQLException
+ { return pds.getNumIdleConnections( username, password ); }
+
+ public int getNumBusyConnections(String username, String password) throws SQLException
+ { return pds.getNumBusyConnections( username, password ); }
+
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException
+ { return pds.getNumUnclosedOrphanedConnections( username, password ); }
+
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException
+ { return pds.getEffectivePropertyCycle( username, password ); }
+
+ public void softReset(String username, String password) throws SQLException
+ { pds.softReset( username, password ); }
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException
+ { return pds.getNumBusyConnectionsAllUsers(); }
+
+ public int getNumIdleConnectionsAllUsers() throws SQLException
+ { return pds.getNumIdleConnectionsAllUsers(); }
+
+ public int getNumConnectionsAllUsers() throws SQLException
+ { return pds.getNumConnectionsAllUsers(); }
+
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException
+ { return pds.getNumUnclosedOrphanedConnectionsAllUsers(); }
+
+ public void softResetAllUsers() throws SQLException
+ { pds.softResetAllUsers(); }
+
+ public int getNumUserPools() throws SQLException
+ { return pds.getNumUserPools(); }
+
+ public Collection getAllUsers() throws SQLException
+ { return pds.getAllUsers(); }
+
+ public void hardReset() throws SQLException
+ { pds.hardReset(); }
+
+ public void close() throws SQLException
+ { pds.close(); }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManagerMBean.java b/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManagerMBean.java
new file mode 100644
index 0000000..9f3b2ec
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/management/PooledDataSourceManagerMBean.java
@@ -0,0 +1,61 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.management;
+
+import java.sql.SQLException;
+import java.util.Collection;
+
+public interface PooledDataSourceManagerMBean
+{
+ public String getIdentityToken();
+ public String getDataSourceName();
+ public void setDataSourceName(String dataSourceName);
+ public int getNumConnectionsDefaultUser() throws SQLException;
+ public int getNumIdleConnectionsDefaultUser() throws SQLException;
+ public int getNumBusyConnectionsDefaultUser() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException;
+ public float getEffectivePropertyCycleDefaultUser() throws SQLException;
+ public void softResetDefaultUser() throws SQLException;
+ public int getNumConnections(String username, String password) throws SQLException;
+ public int getNumIdleConnections(String username, String password) throws SQLException;
+ public int getNumBusyConnections(String username, String password) throws SQLException;
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException;
+ public float getEffectivePropertyCycle(String username, String password) throws SQLException;
+ public void softReset(String username, String password) throws SQLException;
+ public int getNumBusyConnectionsAllUsers() throws SQLException;
+ public int getNumIdleConnectionsAllUsers() throws SQLException;
+ public int getNumConnectionsAllUsers() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException;
+ public int getThreadPoolSize() throws SQLException;
+ public int getThreadPoolNumActiveThreads() throws SQLException;
+ public int getThreadPoolNumIdleThreads() throws SQLException;
+ public int getThreadPoolNumTasksPending() throws SQLException;
+ public String sampleThreadPoolStackTraces() throws SQLException;
+ public String sampleThreadPoolStatus() throws SQLException;
+ public void softResetAllUsers() throws SQLException;
+ public int getNumUserPools() throws SQLException;
+ public Collection getAllUsers() throws SQLException;
+ public void hardReset() throws SQLException;
+ public void close() throws SQLException;
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSource.java b/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSource.java
new file mode 100644
index 0000000..9d0d53b
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSource.java
@@ -0,0 +1,433 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.mbean;
+
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.log.*;
+import java.beans.PropertyVetoException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import javax.sql.DataSource;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingException;
+
+/**
+ * @deprecated Please use com.mchange.v2.c3p0.jboss.C3P0PooledDataSource
+ */
+public class C3P0PooledDataSource implements C3P0PooledDataSourceMBean
+{
+ private final static MLogger logger = MLog.getLogger( C3P0PooledDataSource.class );
+
+ String jndiName;
+
+ ComboPooledDataSource combods = new ComboPooledDataSource();
+
+ private void rebind() throws NamingException
+ { rebind(null); }
+
+ private void rebind(String unbindName) throws NamingException
+ {
+ InitialContext ictx = new InitialContext();
+ if (unbindName != null)
+ ictx.unbind( unbindName );
+
+ if (jndiName != null)
+ {
+ // Thanks to David D. Kilzer for this code to auto-create
+ // subcontext paths!
+ Name name = ictx.getNameParser( jndiName ).parse( jndiName );
+ Context ctx = ictx;
+ for (int i = 0, max = name.size() - 1; i < max; i++)
+ {
+ try
+ { ctx = ctx.createSubcontext( name.get( i ) ); }
+ catch (NameAlreadyBoundException ignore)
+ { ctx = (Context) ctx.lookup( name.get( i ) ); }
+ }
+
+ ictx.rebind( jndiName, combods );
+ }
+
+
+ }
+
+ // Jndi Setup Names
+ public void setJndiName(String jndiName) throws NamingException
+ {
+ String unbindName = this.jndiName;
+ this.jndiName = jndiName;
+ rebind( unbindName );
+ }
+
+ public String getJndiName()
+ { return jndiName; }
+
+ // DriverManagerDataSourceProperties (count: 4)
+ public String getDescription()
+ { return combods.getDescription(); }
+
+ public void setDescription( String description ) throws NamingException
+ {
+ combods.setDescription( description );
+ rebind();
+ }
+
+ public String getDriverClass()
+ { return combods.getDriverClass(); }
+
+ public void setDriverClass( String driverClass ) throws PropertyVetoException, NamingException
+ {
+ combods.setDriverClass( driverClass );
+ rebind();
+ }
+
+ public String getJdbcUrl()
+ { return combods.getJdbcUrl(); }
+
+ public void setJdbcUrl( String jdbcUrl ) throws NamingException
+ {
+ combods.setJdbcUrl( jdbcUrl );
+ rebind();
+ }
+
+ // DriverManagerDataSource "virtual properties" based on properties
+ public String getUser()
+ { return combods.getUser(); }
+
+ public void setUser( String user ) throws NamingException
+ {
+ combods.setUser( user );
+ rebind();
+ }
+
+ public String getPassword()
+ { return combods.getPassword(); }
+
+ public void setPassword( String password ) throws NamingException
+ {
+ combods.setPassword( password );
+ rebind();
+ }
+
+ // WrapperConnectionPoolDataSource properties (count: 21)
+ public int getCheckoutTimeout()
+ { return combods.getCheckoutTimeout(); }
+
+ public void setCheckoutTimeout( int checkoutTimeout ) throws NamingException
+ {
+ combods.setCheckoutTimeout( checkoutTimeout );
+ rebind();
+ }
+
+ public int getAcquireIncrement()
+ { return combods.getAcquireIncrement(); }
+
+ public void setAcquireIncrement( int acquireIncrement ) throws NamingException
+ {
+ combods.setAcquireIncrement( acquireIncrement );
+ rebind();
+ }
+
+ public int getAcquireRetryAttempts()
+ { return combods.getAcquireRetryAttempts(); }
+
+ public void setAcquireRetryAttempts( int acquireRetryAttempts ) throws NamingException
+ {
+ combods.setAcquireRetryAttempts( acquireRetryAttempts );
+ rebind();
+ }
+
+ public int getAcquireRetryDelay()
+ { return combods.getAcquireRetryDelay(); }
+
+ public void setAcquireRetryDelay( int acquireRetryDelay ) throws NamingException
+ {
+ combods.setAcquireRetryDelay( acquireRetryDelay );
+ rebind();
+ }
+
+ public boolean isAutoCommitOnClose()
+ { return combods.isAutoCommitOnClose(); }
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose ) throws NamingException
+ {
+ combods.setAutoCommitOnClose( autoCommitOnClose );
+ rebind();
+ }
+
+ public String getConnectionTesterClassName()
+ { return combods.getConnectionTesterClassName(); }
+
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException, NamingException
+ {
+ combods.setConnectionTesterClassName( connectionTesterClassName );
+ rebind();
+ }
+
+ public String getAutomaticTestTable()
+ { return combods.getAutomaticTestTable(); }
+
+ public void setAutomaticTestTable( String automaticTestTable ) throws NamingException
+ {
+ combods.setAutomaticTestTable( automaticTestTable );
+ rebind();
+ }
+
+ public boolean isForceIgnoreUnresolvedTransactions()
+ { return combods.isForceIgnoreUnresolvedTransactions(); }
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions ) throws NamingException
+ {
+ combods.setForceIgnoreUnresolvedTransactions( forceIgnoreUnresolvedTransactions );
+ rebind();
+ }
+
+ public int getIdleConnectionTestPeriod()
+ { return combods.getIdleConnectionTestPeriod(); }
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod ) throws NamingException
+ {
+ combods.setIdleConnectionTestPeriod( idleConnectionTestPeriod );
+ rebind();
+ }
+
+ public int getInitialPoolSize()
+ { return combods.getInitialPoolSize(); }
+
+ public void setInitialPoolSize( int initialPoolSize ) throws NamingException
+ {
+ combods.setInitialPoolSize( initialPoolSize );
+ rebind();
+ }
+
+ public int getMaxIdleTime()
+ { return combods.getMaxIdleTime(); }
+
+ public void setMaxIdleTime( int maxIdleTime ) throws NamingException
+ {
+ combods.setMaxIdleTime( maxIdleTime );
+ rebind();
+ }
+
+ public int getMaxPoolSize()
+ { return combods.getMaxPoolSize(); }
+
+ public void setMaxPoolSize( int maxPoolSize ) throws NamingException
+ {
+ combods.setMaxPoolSize( maxPoolSize );
+ rebind();
+ }
+
+ public int getMaxStatements()
+ { return combods.getMaxStatements(); }
+
+ public void setMaxStatements( int maxStatements ) throws NamingException
+ {
+ combods.setMaxStatements( maxStatements );
+ rebind();
+ }
+
+ public int getMaxStatementsPerConnection()
+ { return combods.getMaxStatementsPerConnection(); }
+
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection ) throws NamingException
+ {
+ combods.setMaxStatementsPerConnection( maxStatementsPerConnection );
+ rebind();
+ }
+
+ public int getMinPoolSize()
+ { return combods.getMinPoolSize(); }
+
+ public void setMinPoolSize( int minPoolSize ) throws NamingException
+ {
+ combods.setMinPoolSize( minPoolSize );
+ rebind();
+ }
+
+ public int getPropertyCycle()
+ { return combods.getPropertyCycle(); }
+
+ public void setPropertyCycle( int propertyCycle ) throws NamingException
+ {
+ combods.setPropertyCycle( propertyCycle );
+ rebind();
+ }
+
+ public boolean isBreakAfterAcquireFailure()
+ { return combods.isBreakAfterAcquireFailure(); }
+
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure ) throws NamingException
+ {
+ combods.setBreakAfterAcquireFailure( breakAfterAcquireFailure );
+ rebind();
+ }
+
+ public boolean isTestConnectionOnCheckout()
+ { return combods.isTestConnectionOnCheckout(); }
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout ) throws NamingException
+ {
+ combods.setTestConnectionOnCheckout( testConnectionOnCheckout );
+ rebind();
+ }
+
+ public boolean isTestConnectionOnCheckin()
+ { return combods.isTestConnectionOnCheckin(); }
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin ) throws NamingException
+ {
+ combods.setTestConnectionOnCheckin( testConnectionOnCheckin );
+ rebind();
+ }
+
+ public boolean isUsesTraditionalReflectiveProxies()
+ { return combods.isUsesTraditionalReflectiveProxies(); }
+
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies ) throws NamingException
+ {
+ combods.setUsesTraditionalReflectiveProxies( usesTraditionalReflectiveProxies );
+ rebind();
+ }
+
+ public String getPreferredTestQuery()
+ { return combods.getPreferredTestQuery(); }
+
+ public void setPreferredTestQuery( String preferredTestQuery ) throws NamingException
+ {
+ combods.setPreferredTestQuery( preferredTestQuery );
+ rebind();
+ }
+
+ // PoolBackedDataSource properties (count: 2)
+ public String getDataSourceName()
+ { return combods.getDataSourceName(); }
+
+ public void setDataSourceName( String name ) throws NamingException
+ {
+ combods.setDataSourceName( name );
+ rebind();
+ }
+
+ public int getNumHelperThreads()
+ { return combods.getNumHelperThreads(); }
+
+ public void setNumHelperThreads( int numHelperThreads ) throws NamingException
+ {
+ combods.setNumHelperThreads( numHelperThreads );
+ rebind();
+ }
+
+ // shared properties (count: 1)
+ public String getFactoryClassLocation()
+ { return combods.getFactoryClassLocation(); }
+
+ public void setFactoryClassLocation( String factoryClassLocation ) throws NamingException
+ {
+ combods.setFactoryClassLocation( factoryClassLocation );
+ rebind();
+ }
+
+ // PooledDataSource statistics
+
+ public int getNumUserPools() throws SQLException
+ { return combods.getNumUserPools(); }
+
+ public int getNumConnectionsDefaultUser() throws SQLException
+ { return combods.getNumConnectionsDefaultUser(); }
+
+ public int getNumIdleConnectionsDefaultUser() throws SQLException
+ { return combods.getNumIdleConnectionsDefaultUser(); }
+
+ public int getNumBusyConnectionsDefaultUser() throws SQLException
+ { return combods.getNumBusyConnectionsDefaultUser(); }
+
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException
+ { return combods.getNumUnclosedOrphanedConnectionsDefaultUser(); }
+
+ public int getNumConnections(String username, String password) throws SQLException
+ { return combods.getNumConnections(username, password); }
+
+ public int getNumIdleConnections(String username, String password) throws SQLException
+ { return combods.getNumIdleConnections(username, password); }
+
+ public int getNumBusyConnections(String username, String password) throws SQLException
+ { return combods.getNumBusyConnections(username, password); }
+
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException
+ { return combods.getNumUnclosedOrphanedConnections(username, password); }
+
+ public int getNumConnectionsAllUsers() throws SQLException
+ { return combods.getNumConnectionsAllUsers(); }
+
+ public int getNumIdleConnectionsAllUsers() throws SQLException
+ { return combods.getNumIdleConnectionsAllUsers(); }
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException
+ { return combods.getNumBusyConnectionsAllUsers(); }
+
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException
+ { return combods.getNumUnclosedOrphanedConnectionsAllUsers(); }
+
+ // PooledDataSource operations
+ public void softResetDefaultUser() throws SQLException
+ { combods.softResetDefaultUser(); }
+
+ public void softReset(String username, String password) throws SQLException
+ { combods.softReset(username, password); }
+
+ public void softResetAllUsers() throws SQLException
+ { combods.softResetAllUsers(); }
+
+ public void hardReset() throws SQLException
+ { combods.hardReset(); }
+
+ public void close() throws SQLException
+ { combods.close(); }
+
+ //JBoss only... (but these methods need not be called for the mbean to work)
+ public void create() throws Exception
+ { }
+
+ // the mbean works without this, but if called we start populating the pool early
+ public void start() throws Exception
+ {
+ //System.err.println("Bound C3P0 PooledDataSource to name '" + jndiName + "'. Starting...");
+ logger.log(MLevel.INFO, "Bound C3P0 PooledDataSource to name ''{0}''. Starting...", jndiName);
+ combods.getNumBusyConnectionsDefaultUser(); //just touch the datasource to start it up.
+ }
+
+
+ public void stop()
+ { }
+
+ public void destroy()
+ { }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSourceMBean.java b/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSourceMBean.java
new file mode 100644
index 0000000..ce4ade6
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/mbean/C3P0PooledDataSourceMBean.java
@@ -0,0 +1,190 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.mbean;
+
+import com.mchange.v2.c3p0.*;
+import java.beans.PropertyVetoException;
+import java.sql.SQLException;
+import java.util.Properties;
+import javax.naming.NamingException;
+
+/**
+ * @deprecated Please use com.mchange.v2.c3p0.jboss.C3P0PooledDataSourceMBean
+ */
+public interface C3P0PooledDataSourceMBean
+{
+ // Jndi Setup
+ public void setJndiName(String jndiName) throws NamingException;
+
+ public String getJndiName();
+
+ // DriverManagerDataSourceProperties
+ public String getDescription();
+
+ public void setDescription( String description ) throws NamingException;
+
+ public String getDriverClass();
+
+ public void setDriverClass( String driverClass ) throws PropertyVetoException, NamingException;
+
+ public String getJdbcUrl();
+
+ public void setJdbcUrl( String jdbcUrl ) throws NamingException;
+
+ // DriverManagerDataSource "virtual properties" based on properties
+ public String getUser();
+
+ public void setUser( String user ) throws NamingException;
+
+ public String getPassword();
+
+ public void setPassword( String password ) throws NamingException;
+
+ // WrapperConnectionPoolDataSource properties
+ public int getCheckoutTimeout();
+
+ public void setCheckoutTimeout( int checkoutTimeout ) throws NamingException;
+
+ public int getAcquireIncrement();
+
+ public void setAcquireIncrement( int acquireIncrement ) throws NamingException;
+
+ public int getAcquireRetryAttempts();
+
+ public void setAcquireRetryAttempts( int acquireRetryAttempts ) throws NamingException;
+
+ public int getAcquireRetryDelay();
+
+ public void setAcquireRetryDelay( int acquireRetryDelay ) throws NamingException;
+
+ public boolean isAutoCommitOnClose();
+
+ public void setAutoCommitOnClose( boolean autoCommitOnClose ) throws NamingException;
+
+ public String getConnectionTesterClassName();
+
+ public void setConnectionTesterClassName( String connectionTesterClassName ) throws PropertyVetoException, NamingException;
+
+ public String getAutomaticTestTable();
+
+ public void setAutomaticTestTable( String automaticTestTable ) throws NamingException;
+
+ public boolean isForceIgnoreUnresolvedTransactions();
+
+ public void setForceIgnoreUnresolvedTransactions( boolean forceIgnoreUnresolvedTransactions ) throws NamingException;
+
+ public int getIdleConnectionTestPeriod();
+
+ public void setIdleConnectionTestPeriod( int idleConnectionTestPeriod ) throws NamingException;
+
+ public int getInitialPoolSize();
+
+ public void setInitialPoolSize( int initialPoolSize ) throws NamingException;
+
+ public int getMaxIdleTime();
+
+ public void setMaxIdleTime( int maxIdleTime ) throws NamingException;
+
+ public int getMaxPoolSize();
+
+ public void setMaxPoolSize( int maxPoolSize ) throws NamingException;
+
+ public int getMaxStatements();
+
+ public void setMaxStatements( int maxStatements ) throws NamingException;
+
+ public int getMaxStatementsPerConnection();
+
+ public void setMaxStatementsPerConnection( int maxStatementsPerConnection ) throws NamingException;
+
+ public int getMinPoolSize();
+
+ public void setMinPoolSize( int minPoolSize ) throws NamingException;
+
+ public int getPropertyCycle();
+
+ public void setPropertyCycle( int propertyCycle ) throws NamingException;
+
+ public boolean isBreakAfterAcquireFailure();
+
+ public void setBreakAfterAcquireFailure( boolean breakAfterAcquireFailure ) throws NamingException;
+
+ public boolean isTestConnectionOnCheckout();
+
+ public void setTestConnectionOnCheckout( boolean testConnectionOnCheckout ) throws NamingException;
+
+ public boolean isTestConnectionOnCheckin();
+
+ public void setTestConnectionOnCheckin( boolean testConnectionOnCheckin ) throws NamingException;
+
+ public boolean isUsesTraditionalReflectiveProxies();
+
+ public void setUsesTraditionalReflectiveProxies( boolean usesTraditionalReflectiveProxies ) throws NamingException;
+
+ public String getPreferredTestQuery();
+
+ public void setPreferredTestQuery( String preferredTestQuery ) throws NamingException;
+
+ // PoolBackedDataSource properties (count: 2)
+ public int getNumHelperThreads();
+
+ public void setNumHelperThreads( int numHelperThreads ) throws NamingException;
+
+ // shared properties (count: 1)
+ public String getFactoryClassLocation();
+
+ public void setFactoryClassLocation( String factoryClassLocation ) throws NamingException;
+
+ // PooledDataSource statistics
+
+ public int getNumUserPools() throws SQLException;
+
+ public int getNumConnectionsDefaultUser() throws SQLException;
+ public int getNumIdleConnectionsDefaultUser() throws SQLException;
+ public int getNumBusyConnectionsDefaultUser() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsDefaultUser() throws SQLException;
+
+ public int getNumConnections(String username, String password) throws SQLException;
+ public int getNumIdleConnections(String username, String password) throws SQLException;
+ public int getNumBusyConnections(String username, String password) throws SQLException;
+ public int getNumUnclosedOrphanedConnections(String username, String password) throws SQLException;
+
+ public int getNumBusyConnectionsAllUsers() throws SQLException;
+ public int getNumIdleConnectionsAllUsers() throws SQLException;
+ public int getNumConnectionsAllUsers() throws SQLException;
+ public int getNumUnclosedOrphanedConnectionsAllUsers() throws SQLException;
+
+ // PooledDataSource operations
+ public void softResetDefaultUser() throws SQLException;
+ public void softReset(String username, String password) throws SQLException;
+ public void softResetAllUsers() throws SQLException;
+ public void hardReset() throws SQLException;
+ public void close() throws SQLException;
+
+ //JBoss only... (but these methods need not be called for the mbean to work)
+ public void create() throws Exception;
+ public void start() throws Exception;
+ public void stop();
+ public void destroy();
+}
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/DoubleMaxStatementCache.java b/src/classes/com/mchange/v2/c3p0/stmt/DoubleMaxStatementCache.java
new file mode 100644
index 0000000..ebfe2a5
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/DoubleMaxStatementCache.java
@@ -0,0 +1,73 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.*;
+import com.mchange.v2.async.AsynchronousRunner;
+
+public final class DoubleMaxStatementCache extends GooGooStatementCache
+{
+ //MT: protected by this' lock
+ int max_statements;
+ Deathmarch globalDeathmarch = new Deathmarch();
+
+ int max_statements_per_connection;
+ DeathmarchConnectionStatementManager dcsm;
+
+ public DoubleMaxStatementCache(AsynchronousRunner blockingTaskAsyncRunner, int max_statements, int max_statements_per_connection)
+ {
+ super( blockingTaskAsyncRunner );
+ this.max_statements = max_statements;
+ this.max_statements_per_connection = max_statements_per_connection;
+ }
+
+ //called only in parent's constructor
+ protected ConnectionStatementManager createConnectionStatementManager()
+ { return (this.dcsm = new DeathmarchConnectionStatementManager()); }
+
+ //called by parent only with this' lock
+ void addStatementToDeathmarches( Object pstmt, Connection physicalConnection )
+ {
+ globalDeathmarch.deathmarchStatement( pstmt );
+ dcsm.getDeathmarch( physicalConnection ).deathmarchStatement( pstmt );
+ }
+
+ void removeStatementFromDeathmarches( Object pstmt, Connection physicalConnection )
+ {
+ globalDeathmarch.undeathmarchStatement( pstmt );
+ dcsm.getDeathmarch( physicalConnection ).undeathmarchStatement( pstmt );
+ }
+
+ boolean prepareAssimilateNewStatement(Connection pcon)
+ {
+ int cxn_stmt_count = dcsm.getNumStatementsForConnection( pcon );
+ if (cxn_stmt_count < max_statements_per_connection) //okay... we can cache another for the connection, but how 'bout globally?
+ {
+ int global_size = this.countCachedStatements();
+ return ( global_size < max_statements || (global_size == max_statements && globalDeathmarch.cullNext()) );
+ }
+ else //we can only cache if we can clear one from the Connection (which implies clearing one globally, so we needn't check max_statements)
+ return (cxn_stmt_count == max_statements_per_connection && dcsm.getDeathmarch( pcon ).cullNext());
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/GlobalMaxOnlyStatementCache.java b/src/classes/com/mchange/v2/c3p0/stmt/GlobalMaxOnlyStatementCache.java
new file mode 100644
index 0000000..58e7d03
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/GlobalMaxOnlyStatementCache.java
@@ -0,0 +1,57 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.*;
+import com.mchange.v2.async.AsynchronousRunner;
+
+public final class GlobalMaxOnlyStatementCache extends GooGooStatementCache
+{
+ //MT: protected by this' lock
+ int max_statements;
+ Deathmarch globalDeathmarch = new Deathmarch();
+
+ public GlobalMaxOnlyStatementCache(AsynchronousRunner blockingTaskAsyncRunner, int max_statements)
+ {
+ super( blockingTaskAsyncRunner );
+ this.max_statements = max_statements;
+ }
+
+ //called only in parent's constructor
+ protected ConnectionStatementManager createConnectionStatementManager()
+ { return new SimpleConnectionStatementManager(); }
+
+ //called by parent only with this' lock
+ void addStatementToDeathmarches( Object pstmt, Connection physicalConnection )
+ { globalDeathmarch.deathmarchStatement( pstmt ); }
+
+ void removeStatementFromDeathmarches( Object pstmt, Connection physicalConnection )
+ { globalDeathmarch.undeathmarchStatement( pstmt ); }
+
+ boolean prepareAssimilateNewStatement(Connection pcon)
+ {
+ int global_size = this.countCachedStatements();
+ return ( global_size < max_statements || (global_size == max_statements && globalDeathmarch.cullNext()) );
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/GooGooStatementCache.java b/src/classes/com/mchange/v2/c3p0/stmt/GooGooStatementCache.java
new file mode 100644
index 0000000..b71bd66
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/GooGooStatementCache.java
@@ -0,0 +1,801 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.util.*;
+import java.sql.*;
+import java.lang.reflect.*;
+import com.mchange.v2.async.AsynchronousRunner;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.util.ResourceClosedException;
+import com.mchange.v2.log.*;
+import com.mchange.v1.db.sql.StatementUtils;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.io.IOException;
+import com.mchange.v2.io.IndentedWriter;
+
+public abstract class GooGooStatementCache
+{
+ private final static MLogger logger = MLog.getLogger( GooGooStatementCache.class );
+
+ private final static int DESTROY_NEVER = 0;
+ private final static int DESTROY_IF_CHECKED_IN = 1 << 0;
+ private final static int DESTROY_IF_CHECKED_OUT = 1 << 1;
+ private final static int DESTROY_ALWAYS = (DESTROY_IF_CHECKED_IN | DESTROY_IF_CHECKED_OUT);
+
+ /* MT: protected by this's lock */
+
+ // contains all statements in the cache,
+ // organized by connection
+ ConnectionStatementManager cxnStmtMgr;
+
+ // contains all statements in the cache,
+ // bound to the keys that produced them
+ HashMap stmtToKey = new HashMap();
+
+ // maps all known keys to their set of statements
+ // and to a queue of statements, if any, available
+ // for checkout
+ HashMap keyToKeyRec = new HashMap();
+
+ // contains all checked out statements -- in the cache,
+ // but not currently available for checkout, nor for
+ // culling in case of overflow
+ HashSet checkedOut = new HashSet();
+
+ /* MT: end protected by this' lock */
+
+ /* MT: protected by its own lock */
+
+ AsynchronousRunner blockingTaskAsyncRunner;
+
+ // This set is used to ensure that multiple threads
+ // do not try to remove the same statement from the
+ // cache, if for example a Statement is both deathmarched
+ // away and its parent Connection is closed.
+ //
+ // ALL ACCESS SHOULD BE EXPLICITLY SYNCHRONIZED
+ // ON removalPending's lock!
+ HashSet removalPending = new HashSet();
+
+ /* MT: end protected by its own lock */
+
+ public GooGooStatementCache(AsynchronousRunner blockingTaskAsyncRunner)
+ {
+ this.blockingTaskAsyncRunner = blockingTaskAsyncRunner;
+ this.cxnStmtMgr = createConnectionStatementManager();
+ }
+
+ public synchronized int getNumStatements()
+ { return this.isClosed() ? -1 : countCachedStatements(); }
+
+ public synchronized int getNumStatementsCheckedOut()
+ { return this.isClosed() ? -1 : checkedOut.size(); }
+
+ public synchronized int getNumConnectionsWithCachedStatements()
+ { return isClosed() ? -1 : cxnStmtMgr.getNumConnectionsWithCachedStatements(); }
+
+ public synchronized String dumpStatementCacheStatus()
+ {
+ if (isClosed())
+ return this + "status: Closed.";
+ else
+ {
+ StringWriter sw = new StringWriter(2048);
+ IndentedWriter iw = new IndentedWriter( sw );
+ try
+ {
+ iw.print(this);
+ iw.println(" status:");
+ iw.upIndent();
+ iw.println("core stats:");
+ iw.upIndent();
+ iw.print("num cached statements: ");
+ iw.println( this.countCachedStatements() );
+ iw.print("num cached statements in use: ");
+ iw.println( checkedOut.size() );
+ iw.print("num connections with cached statements: ");
+ iw.println(cxnStmtMgr.getNumConnectionsWithCachedStatements());
+ iw.downIndent();
+ iw.println("cached statement dump:");
+ iw.upIndent();
+ for (Iterator ii = cxnStmtMgr.connectionSet().iterator(); ii.hasNext();)
+ {
+ Connection pcon = (Connection) ii.next();
+ iw.print(pcon);
+ iw.println(':');
+ iw.upIndent();
+ for (Iterator jj = cxnStmtMgr.statementSet(pcon).iterator(); jj.hasNext();)
+ iw.println(jj.next());
+ iw.downIndent();
+ }
+
+ iw.downIndent();
+ iw.downIndent();
+ return sw.toString();
+ }
+ catch (IOException e)
+ {
+ if (logger.isLoggable(MLevel.SEVERE))
+ logger.log(MLevel.SEVERE, "Huh? We've seen an IOException writing to s StringWriter?!", e);
+ return e.toString();
+ }
+ }
+ }
+
+ abstract ConnectionStatementManager createConnectionStatementManager();
+
+ public synchronized Object checkoutStatement( Connection physicalConnection,
+ Method stmtProducingMethod,
+ Object[] args )
+ throws SQLException, ResourceClosedException
+ {
+ try
+ {
+ Object out = null;
+
+ StatementCacheKey key = StatementCacheKey.find( physicalConnection,
+ stmtProducingMethod,
+ args );
+ LinkedList l = checkoutQueue( key );
+ if (l == null || l.isEmpty()) //we need a new statement
+ {
+ // we might wait() here...
+ // don't presume atomicity before and after!
+ out = acquireStatement( physicalConnection, stmtProducingMethod, args );
+
+ if ( prepareAssimilateNewStatement( physicalConnection ) )
+ assimilateNewCheckedOutStatement( key, physicalConnection, out );
+ // else case: we can't assimilate the statement...
+ // so, we just return our newly created statement, without caching it.
+ // on check-in, it will simply be destroyed... this is an "overload statement"
+ }
+ else //okay, we can use an old one
+ {
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ logger.finest(this.getClass().getName() + " ----> CACHE HIT");
+ //System.err.println("-------------> CACHE HIT!");
+
+ out = l.get(0);
+ l.remove(0);
+ if (! checkedOut.add( out ))
+ throw new RuntimeException("Internal inconsistency: " +
+ "Checking out a statement marked " +
+ "as already checked out!");
+ removeStatementFromDeathmarches( out, physicalConnection );
+ }
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+ //System.err.print("checkoutStatement(): ");
+ //printStats();
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("checkoutStatement: " + statsString());
+ }
+
+ return out;
+ }
+ catch (NullPointerException npe)
+ {
+ if (checkedOut == null) //we're closed
+ {
+ if (logger.isLoggable(MLevel.FINE))
+ logger.log( MLevel.FINE,
+ "A client attempted to work with a closed Statement cache, " + "" +
+ "provoking a NullPointerException. c3p0 recovers, but this should be rare.",
+ npe);
+ throw new ResourceClosedException( npe );
+ }
+ else
+ throw npe;
+ }
+ }
+
+ public synchronized void checkinStatement( Object pstmt )
+ throws SQLException
+ {
+ if (checkedOut == null) //we're closed
+ {
+ synchronousDestroyStatement( pstmt );
+
+ return;
+ }
+ else if (! checkedOut.remove( pstmt ) )
+ {
+ if (! ourResource( pstmt ) ) //this is not our resource, or it is an overload statement
+ destroyStatement( pstmt ); // so we just destroy
+ //in the else case, it's already checked-in, so we ignore
+
+ return;
+ }
+
+ try
+ { refreshStatement( (PreparedStatement) pstmt ); }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ {
+// System.err.println("Problem with checked-in Statement, discarding.");
+// e.printStackTrace();
+ if (logger.isLoggable(MLevel.INFO))
+ logger.log(MLevel.INFO, "Problem with checked-in Statement, discarding.", e);
+ }
+
+ // swaldman -- 2004-01-31: readd problem statement to checkedOut for consistency
+ // the statement is not yet checked-in, but it is removed from checked out, and this
+ // violates the consistency assumption of removeStatement(). Thanks to Zach Scott for
+ // calling attention to this issue.
+ checkedOut.add( pstmt );
+
+ removeStatement( pstmt, DESTROY_ALWAYS ); //force destruction of the statement even though it appears checked-out
+ return;
+ }
+
+ StatementCacheKey key = (StatementCacheKey) stmtToKey.get( pstmt );
+ if (Debug.DEBUG && key == null)
+ throw new RuntimeException("Internal inconsistency: " +
+ "A checked-out statement has no key associated with it!");
+
+ LinkedList l = checkoutQueue( key );
+ l.add( pstmt );
+ addStatementToDeathmarches( pstmt, key.physicalConnection );
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+// System.err.print("checkinStatement(): ");
+// printStats();
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("checkinStatement(): " + statsString());
+ }
+ }
+
+
+ public synchronized void checkinAll(Connection pcon)
+ throws SQLException
+ {
+ //new Exception("checkinAll()").printStackTrace();
+
+ Set stmtSet = cxnStmtMgr.statementSet( pcon );
+ if (stmtSet != null)
+ {
+ for (Iterator ii = stmtSet.iterator(); ii.hasNext(); )
+ {
+ Object stmt = ii.next();
+ if (checkedOut.contains( stmt ))
+ checkinStatement( stmt );
+ }
+ }
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+// System.err.print("checkinAll(): ");
+// printStats();
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.log(MLevel.FINEST, "checkinAll(): " + statsString());
+ }
+ }
+
+ /*
+ * we only selectively sync' parts of this method, because we wish to wait for
+ * Statements we wish to destroy the Statements synchronously, but without
+ * holding the pool's lock.
+ */
+ public void closeAll(Connection pcon) throws SQLException
+ {
+// System.err.println( this + ": closeAll( " + pcon + " )" );
+// new Exception("closeAll()").printStackTrace();
+
+// assert !Thread.holdsLock( this );
+
+ if (! this.isClosed())
+ {
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+ if (logger.isLoggable(MLevel.FINEST))
+ {
+ logger.log(MLevel.FINEST, "ENTER METHOD: closeAll( " + pcon + " )! -- num_connections: " +
+ cxnStmtMgr.getNumConnectionsWithCachedStatements());
+ //logger.log(MLevel.FINEST, "Set of statements for connection: " + cSet + (cSet != null ? "; size: " + cSet.size() : ""));
+ }
+ }
+
+ Set stmtSet = null;
+ synchronized (this)
+ {
+ Set cSet = cxnStmtMgr.statementSet( pcon );
+
+ if (cSet != null)
+ {
+ //the removeStatement(...) removes from cSet, so we can't be iterating over cSet directly
+ stmtSet = new HashSet( cSet );
+ //System.err.println("SIZE FOR CONNECTION SET: " + stmtSet.size());
+
+ for (Iterator ii = stmtSet.iterator(); ii.hasNext(); )
+ {
+ Object stmt = ii.next();
+ // we remove without destroying, leaving the destruction
+ // until when we lose the pool's lock
+ removeStatement( stmt, DESTROY_NEVER );
+ }
+ }
+ }
+
+ if ( stmtSet != null )
+ {
+ for (Iterator ii = stmtSet.iterator(); ii.hasNext(); )
+ {
+ Object stmt = ii.next();
+ synchronousDestroyStatement( stmt );
+ }
+ }
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("closeAll(): " + statsString());
+ }
+ }
+// else
+// {
+// if (logger.isLoggable(MLevel.FINER))
+// logger.log(MLevel.FINER,
+// this + ": call to closeAll() when statment cache is already closed! [not harmful! debug only!]",
+// new Exception("DUPLICATE CLOSE DEBUG STACK TRACE."));
+// }
+ }
+
+ public synchronized void close()
+ throws SQLException
+ {
+ //System.err.println( this + ": close()" );
+
+ if (! isClosed())
+ {
+ for (Iterator ii = stmtToKey.keySet().iterator(); ii.hasNext(); )
+ synchronousDestroyStatement( ii.next() );
+
+ cxnStmtMgr = null;
+ stmtToKey = null;
+ keyToKeyRec = null;
+ checkedOut = null;
+ }
+ else
+ {
+ if (logger.isLoggable(MLevel.FINE))
+ logger.log(MLevel.FINE, this + ": duplicate call to close() [not harmful! -- debug only!]", new Exception("DUPLICATE CLOSE DEBUG STACK TRACE."));
+ }
+
+ }
+
+
+ public synchronized boolean isClosed()
+ { return cxnStmtMgr == null; }
+
+ /* non-public methods that needn't be called with this' lock below */
+
+ private void destroyStatement( final Object pstmt )
+ {
+ class StatementCloseTask implements Runnable
+ {
+ public void run()
+ { StatementUtils.attemptClose( (PreparedStatement) pstmt ); }
+ }
+
+ Runnable r = new StatementCloseTask();
+
+ blockingTaskAsyncRunner.postRunnable(r);
+ }
+
+ private void synchronousDestroyStatement( final Object pstmt )
+ { StatementUtils.attemptClose( (PreparedStatement) pstmt ); }
+
+ /* end non-public methods that needn't be called with this' lock */
+
+
+
+ /* non-public methods that MUST be called with this' lock */
+
+ abstract boolean prepareAssimilateNewStatement(Connection pcon);
+
+ abstract void addStatementToDeathmarches( Object pstmt, Connection physicalConnection );
+ abstract void removeStatementFromDeathmarches( Object pstmt, Connection physicalConnection );
+
+ final int countCachedStatements()
+ { return stmtToKey.size(); }
+
+ private void assimilateNewCheckedOutStatement( StatementCacheKey key,
+ Connection pConn,
+ Object ps )
+ {
+ stmtToKey.put( ps, key );
+ HashSet ks = keySet( key );
+ if (ks == null)
+ keyToKeyRec.put( key, new KeyRec() );
+ else
+ {
+ //System.err.println("-------> Multiply prepared statement! " + key.stmtText );
+ if (logger.isLoggable(MLevel.INFO))
+ logger.info("Multiply prepared statement! " + key.stmtText );
+ if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
+ logger.fine("(The same statement has already been prepared by this Connection, " +
+ "and that other instance has not yet been closed, so the statement pool " +
+ "has to prepare a second PreparedStatement object rather than reusing " +
+ "the previously-cached Statement. The new Statement will be cached, in case " +
+ "you frequently need multiple copies of this Statement.)");
+ }
+ keySet( key ).add( ps );
+ cxnStmtMgr.addStatementForConnection( ps, pConn );
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+// System.err.println("cxnStmtMgr.statementSet( " + pConn + " ).size(): " +
+// cxnStmtMgr.statementSet( pConn ).size());
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("cxnStmtMgr.statementSet( " + pConn + " ).size(): " +
+ cxnStmtMgr.statementSet( pConn ).size());
+ }
+
+ checkedOut.add( ps );
+ }
+
+ private void removeStatement( Object ps , int destruction_policy )
+ {
+ synchronized (removalPending)
+ {
+ if ( removalPending.contains( ps ) )
+ return;
+ else
+ removalPending.add(ps);
+ }
+
+ StatementCacheKey sck = (StatementCacheKey) stmtToKey.remove( ps );
+ removeFromKeySet( sck, ps );
+ Connection pConn = sck.physicalConnection;
+
+ boolean checked_in = !checkedOut.contains( ps );
+
+ if ( checked_in )
+ {
+ removeStatementFromDeathmarches( ps, pConn );
+ removeFromCheckoutQueue( sck , ps );
+ if ((destruction_policy & DESTROY_IF_CHECKED_IN) != 0)
+ destroyStatement( ps );
+ }
+ else
+ {
+ checkedOut.remove( ps );
+ if ((destruction_policy & DESTROY_IF_CHECKED_OUT) != 0)
+ destroyStatement( ps );
+ }
+
+
+ boolean check = cxnStmtMgr.removeStatementForConnection( ps, pConn );
+ if (Debug.DEBUG && check == false)
+ {
+ //new Exception("WARNING: removed a statement that apparently wasn't in a statement set!!!").printStackTrace();
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING,
+ this + " removed a statement that apparently wasn't in a statement set!!!",
+ new Exception("LOG STACK TRACE"));
+ }
+
+ synchronized (removalPending)
+ { removalPending.remove(ps); }
+ }
+
+ private Object acquireStatement(final Connection pConn,
+ final Method stmtProducingMethod,
+ final Object[] args )
+ throws SQLException
+ {
+ try
+ {
+ final Object[] outHolder = new Object[1];
+ final SQLException[] exceptionHolder = new SQLException[1];
+
+ class StmtAcquireTask implements Runnable
+ {
+ public void run()
+ {
+ try
+ {
+ outHolder[0] =
+ stmtProducingMethod.invoke( pConn,
+ args );
+ }
+ catch ( InvocationTargetException e )
+ {
+ Throwable targetException = e.getTargetException();
+ if ( targetException instanceof SQLException )
+ exceptionHolder[0] = (SQLException) targetException;
+ else
+ exceptionHolder[0]
+ = SqlUtils.toSQLException(targetException);
+ }
+ catch ( Exception e )
+ { exceptionHolder[0] = SqlUtils.toSQLException(e); }
+ finally
+ {
+ synchronized ( GooGooStatementCache.this )
+ { GooGooStatementCache.this.notifyAll(); }
+ }
+ }
+ }
+
+ Runnable r = new StmtAcquireTask();
+ blockingTaskAsyncRunner.postRunnable(r);
+
+ while ( outHolder[0] == null && exceptionHolder[0] == null )
+ this.wait(); //give up our lock while the Statement gets prepared
+ if (exceptionHolder[0] != null)
+ throw exceptionHolder[0];
+ else
+ {
+ Object out = outHolder[0];
+ return out;
+ }
+ }
+ catch ( InterruptedException e )
+ { throw SqlUtils.toSQLException( e ); }
+ }
+
+ private KeyRec keyRec( StatementCacheKey key )
+ { return ((KeyRec) keyToKeyRec.get( key )); }
+
+ private HashSet keySet( StatementCacheKey key )
+ {
+ KeyRec rec = keyRec( key );
+ return (rec == null ? null : rec.allStmts);
+ }
+
+ private boolean removeFromKeySet( StatementCacheKey key, Object pstmt )
+ {
+ boolean out;
+ HashSet stmtSet = keySet( key );
+ out = stmtSet.remove( pstmt );
+ if (stmtSet.isEmpty() && checkoutQueue( key ).isEmpty())
+ keyToKeyRec.remove( key );
+ return out;
+ }
+
+ private LinkedList checkoutQueue( StatementCacheKey key )
+ {
+ KeyRec rec = keyRec( key );
+ return ( rec == null ? null : rec.checkoutQueue );
+ }
+
+ private boolean removeFromCheckoutQueue( StatementCacheKey key, Object pstmt )
+ {
+ boolean out;
+ LinkedList q = checkoutQueue( key );
+ out = q.remove( pstmt );
+ if (q.isEmpty() && keySet( key ).isEmpty())
+ keyToKeyRec.remove( key );
+ return out;
+ }
+
+ private boolean ourResource( Object ps )
+ { return stmtToKey.keySet().contains( ps ); }
+
+ private void refreshStatement( PreparedStatement ps ) throws Exception
+ { ps.clearParameters(); }
+
+ private void printStats()
+ {
+ //new Exception("printStats()").printStackTrace();
+ int total_size = this.countCachedStatements();
+ int checked_out_size = checkedOut.size();
+ int num_connections = cxnStmtMgr.getNumConnectionsWithCachedStatements();
+ int num_keys = keyToKeyRec.size();
+ System.err.print(this.getClass().getName() + " stats -- ");
+ System.err.print("total size: " + total_size);
+ System.err.print("; checked out: " + checked_out_size);
+ System.err.print("; num connections: " + num_connections);
+ System.err.println("; num keys: " + num_keys);
+ }
+
+ private String statsString()
+ {
+ int total_size = this.countCachedStatements();
+ int checked_out_size = checkedOut.size();
+ int num_connections = cxnStmtMgr.getNumConnectionsWithCachedStatements();
+ int num_keys = keyToKeyRec.size();
+
+ StringBuffer sb = new StringBuffer(255);
+ sb.append(this.getClass().getName());
+ sb.append(" stats -- ");
+ sb.append("total size: ");
+ sb.append(total_size);
+ sb.append("; checked out: ");
+ sb.append(checked_out_size);
+ sb.append("; num connections: ");
+ sb.append(num_connections);
+ sb.append("; num keys: ");
+ sb.append(num_keys);
+ return sb.toString();
+ }
+
+
+ private static class KeyRec
+ {
+ HashSet allStmts = new HashSet();
+ LinkedList checkoutQueue = new LinkedList();
+ }
+
+ protected class Deathmarch
+ {
+ TreeMap longsToStmts = new TreeMap();
+ HashMap stmtsToLongs = new HashMap();
+
+ long last_long = -1;
+
+ public void deathmarchStatement( Object ps )
+ {
+ //System.err.println("deathmarchStatement( " + ps + " )");
+ if (Debug.DEBUG)
+ {
+ Long old = (Long) stmtsToLongs.get( ps );
+ if (old != null)
+ throw new RuntimeException("Internal inconsistency: " +
+ "A statement is being double-deathmatched. no checked-out statements should be in a deathmarch already; " +
+ "no already checked-in statement should be deathmarched!");
+ }
+
+ Long youth = getNextLong();
+ stmtsToLongs.put( ps, youth );
+ longsToStmts.put( youth, ps );
+ }
+
+ public void undeathmarchStatement( Object ps )
+ {
+ Long old = (Long) stmtsToLongs.remove( ps );
+ if (Debug.DEBUG && old == null)
+ throw new RuntimeException("Internal inconsistency: " +
+ "A (not new) checking-out statement is not in deathmarch.");
+ Object check = longsToStmts.remove( old );
+ if (Debug.DEBUG && old == null)
+ throw new RuntimeException("Internal inconsistency: " +
+ "A (not new) checking-out statement is not in deathmarch.");
+ }
+
+ public boolean cullNext()
+ {
+ if ( longsToStmts.isEmpty() )
+ return false;
+ else
+ {
+ Long l = (Long) longsToStmts.firstKey();
+ Object ps = longsToStmts.get( l );
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+// System.err.println("CULLING: " +
+// ((StatementCacheKey) stmtToKey.get(ps)).stmtText);
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("CULLING: " + ((StatementCacheKey) stmtToKey.get(ps)).stmtText);
+ }
+ // we do not undeathmarch the statement ourselves, because removeStatement( ... )
+ // should remove from all deathmarches...
+ removeStatement( ps, DESTROY_ALWAYS );
+ if (Debug.DEBUG && this.contains( ps ))
+ throw new RuntimeException("Inconsistency!!! Statement culled from deathmarch failed to be removed by removeStatement( ... )!");
+ return true;
+ }
+ }
+
+ public boolean contains( Object ps )
+ { return stmtsToLongs.keySet().contains( ps ); }
+
+ public int size()
+ { return longsToStmts.size(); }
+
+ private Long getNextLong()
+ { return new Long( ++last_long ); }
+ }
+
+ protected static abstract class ConnectionStatementManager
+ {
+ Map cxnToStmtSets = new HashMap();
+
+ public int getNumConnectionsWithCachedStatements()
+ { return cxnToStmtSets.size(); }
+
+ public Set connectionSet()
+ { return cxnToStmtSets.keySet(); }
+
+ public Set statementSet( Connection pcon )
+ { return (Set) cxnToStmtSets.get( pcon ); }
+
+ public int getNumStatementsForConnection( Connection pcon )
+ {
+ Set stmtSet = statementSet( pcon );
+ return (stmtSet == null ? 0 : stmtSet.size());
+ }
+
+ public void addStatementForConnection( Object ps, Connection pcon )
+ {
+ Set stmtSet = statementSet( pcon );
+ if (stmtSet == null)
+ {
+ stmtSet = new HashSet();
+ cxnToStmtSets.put( pcon, stmtSet );
+ }
+ stmtSet.add( ps );
+ }
+
+ public boolean removeStatementForConnection( Object ps, Connection pcon )
+ {
+ boolean out;
+
+ Set stmtSet = statementSet( pcon );
+ if ( stmtSet != null )
+ {
+ out = stmtSet.remove( ps );
+ if (stmtSet.isEmpty())
+ cxnToStmtSets.remove( pcon );
+ }
+ else
+ out = false;
+
+ return out;
+ }
+ }
+
+ // i want this as optimized as possible, so i'm adopting the philosophy that all
+ // classes are abstract or final, to help enable compiler inlining...
+ protected static final class SimpleConnectionStatementManager extends ConnectionStatementManager
+ {}
+
+ protected final class DeathmarchConnectionStatementManager extends ConnectionStatementManager
+ {
+ Map cxnsToDms = new HashMap();
+
+ public void addStatementForConnection( Object ps, Connection pcon )
+ {
+ super.addStatementForConnection( ps, pcon );
+ Deathmarch dm = (Deathmarch) cxnsToDms.get( pcon );
+ if (dm == null)
+ {
+ dm = new Deathmarch();
+ cxnsToDms.put( pcon, dm );
+ }
+ }
+
+ public boolean removeStatementForConnection( Object ps, Connection pcon )
+ {
+ boolean out = super.removeStatementForConnection( ps, pcon );
+ if (out)
+ {
+ if ( statementSet( pcon ) == null )
+ cxnsToDms.remove( pcon );
+ }
+ return out;
+ }
+
+ public Deathmarch getDeathmarch( Connection pcon )
+ { return (Deathmarch) cxnsToDms.get( pcon ); }
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/MemoryCoalescedStatementCacheKey.java b/src/classes/com/mchange/v2/c3p0/stmt/MemoryCoalescedStatementCacheKey.java
new file mode 100644
index 0000000..8e4f67a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/MemoryCoalescedStatementCacheKey.java
@@ -0,0 +1,165 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.lang.reflect.Method;
+import com.mchange.v2.coalesce.*;
+
+final class MemoryCoalescedStatementCacheKey extends StatementCacheKey
+{
+ //MT: not thread-safe, but protected within the find() method
+ // by StatementCacheKey.class lock
+ final static Coalescer keyCoalescer = CoalescerFactory.createCoalescer( true, false );
+
+ static StatementCacheKey _find( Connection pcon, Method stmtProducingMethod, Object[] args )
+ {
+ ///BEGIN FIND LOGIC///
+ String stmtText = (String) args[0];
+ boolean is_callable = stmtProducingMethod.getName().equals("prepareCall");
+ int result_set_type;
+ int result_set_concurrency;
+
+ int[] columnIndexes;
+ String[] columnNames;
+ Integer autogeneratedKeys;
+ Integer resultSetHoldability;
+
+ if (args.length == 1)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 2)
+ {
+ Class[] argTypes = stmtProducingMethod.getParameterTypes();
+ if (argTypes[1].isArray())
+ {
+ Class baseType = argTypes[1].getComponentType();
+ if (baseType == int.class) //second arg is columnIndexes
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = (int[]) args[1];
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (baseType == String.class)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = (String[]) args[1];
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else
+ throw new IllegalArgumentException("c3p0 probably needs to be updated for some new " +
+ "JDBC spec! As of JDBC3, we expect two arg statement " +
+ "producing methods where the second arg is either " +
+ "an int, int array, or String array.");
+ }
+ else //it should be a boxed int, autogeneratedKeys
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = (Integer) args[1];
+ resultSetHoldability = null;
+ }
+ }
+ else if (args.length == 3)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 4)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = (Integer) args[3];
+ }
+ else
+ throw new IllegalArgumentException("Unexpected number of args to " +
+ stmtProducingMethod.getName() );
+ ///END FIND LOGIC///
+
+
+ StatementCacheKey uncanonical
+ = new MemoryCoalescedStatementCacheKey( pcon,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+ return (StatementCacheKey) keyCoalescer.coalesce( uncanonical );
+ }
+
+ MemoryCoalescedStatementCacheKey( Connection physicalConnection,
+ String stmtText,
+ boolean is_callable,
+ int result_set_type,
+ int result_set_concurrency,
+ int[] columnIndexes,
+ String[] columnNames,
+ Integer autogeneratedKeys,
+ Integer resultSetHoldability )
+ {
+ super( physicalConnection,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+ }
+
+ public boolean equals( Object o )
+ { return StatementCacheKey.equals( this, o ); }
+
+ public int hashCode()
+ { return StatementCacheKey.hashCode( this ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/PerConnectionMaxOnlyStatementCache.java b/src/classes/com/mchange/v2/c3p0/stmt/PerConnectionMaxOnlyStatementCache.java
new file mode 100644
index 0000000..8ec2d65
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/PerConnectionMaxOnlyStatementCache.java
@@ -0,0 +1,57 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.*;
+import com.mchange.v2.async.AsynchronousRunner;
+
+public final class PerConnectionMaxOnlyStatementCache extends GooGooStatementCache
+{
+ //MT: protected by this' lock
+ int max_statements_per_connection;
+ DeathmarchConnectionStatementManager dcsm;
+
+ public PerConnectionMaxOnlyStatementCache(AsynchronousRunner blockingTaskAsyncRunner, int max_statements_per_connection)
+ {
+ super( blockingTaskAsyncRunner );
+ this.max_statements_per_connection = max_statements_per_connection;
+ }
+
+ //called only in parent's constructor
+ protected ConnectionStatementManager createConnectionStatementManager()
+ { return (this.dcsm = new DeathmarchConnectionStatementManager()); }
+
+ //called by parent only with this' lock
+ void addStatementToDeathmarches( Object pstmt, Connection physicalConnection )
+ { dcsm.getDeathmarch( physicalConnection ).deathmarchStatement( pstmt ); }
+
+ void removeStatementFromDeathmarches( Object pstmt, Connection physicalConnection )
+ { dcsm.getDeathmarch( physicalConnection ).undeathmarchStatement( pstmt ); }
+
+ boolean prepareAssimilateNewStatement(Connection pcon)
+ {
+ int cxn_stmt_count = dcsm.getNumStatementsForConnection( pcon );
+ return ( cxn_stmt_count < max_statements_per_connection || (cxn_stmt_count == max_statements_per_connection && dcsm.getDeathmarch( pcon ).cullNext()) );
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/SimpleStatementCacheKey.java b/src/classes/com/mchange/v2/c3p0/stmt/SimpleStatementCacheKey.java
new file mode 100644
index 0000000..b569a89
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/SimpleStatementCacheKey.java
@@ -0,0 +1,155 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.lang.reflect.Method;
+
+final class SimpleStatementCacheKey extends StatementCacheKey
+{
+ static StatementCacheKey _find( Connection pcon, Method stmtProducingMethod, Object[] args )
+ {
+ ///BEGIN FIND LOGIC///
+ String stmtText = (String) args[0];
+ boolean is_callable = stmtProducingMethod.getName().equals("prepareCall");
+ int result_set_type;
+ int result_set_concurrency;
+
+ int[] columnIndexes;
+ String[] columnNames;
+ Integer autogeneratedKeys;
+ Integer resultSetHoldability;
+
+ if (args.length == 1)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 2)
+ {
+ Class[] argTypes = stmtProducingMethod.getParameterTypes();
+ if (argTypes[1].isArray())
+ {
+ Class baseType = argTypes[1].getComponentType();
+ if (baseType == int.class) //second arg is columnIndexes
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = (int[]) args[1];
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (baseType == String.class)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = (String[]) args[1];
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else
+ throw new IllegalArgumentException("c3p0 probably needs to be updated for some new " +
+ "JDBC spec! As of JDBC3, we expect two arg statement " +
+ "producing methods where the second arg is either " +
+ "an int, int array, or String array.");
+ }
+ else //it should be a boxed int, autogeneratedKeys
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = (Integer) args[1];
+ resultSetHoldability = null;
+ }
+ }
+ else if (args.length == 3)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 4)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = (Integer) args[3];
+ }
+ else
+ throw new IllegalArgumentException("Unexpected number of args to " +
+ stmtProducingMethod.getName() );
+ ///END FIND LOGIC///
+
+
+ return new SimpleStatementCacheKey( pcon,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+ }
+
+ SimpleStatementCacheKey( Connection physicalConnection,
+ String stmtText,
+ boolean is_callable,
+ int result_set_type,
+ int result_set_concurrency,
+ int[] columnIndexes,
+ String[] columnNames,
+ Integer autogeneratedKeys,
+ Integer resultSetHoldability )
+ {
+ super( physicalConnection,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+ }
+
+ public boolean equals( Object o )
+ { return StatementCacheKey.equals( this, o ); }
+
+ public int hashCode()
+ { return StatementCacheKey.hashCode( this ); }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/StatementCache.java b/src/classes/com/mchange/v2/c3p0/stmt/StatementCache.java
new file mode 100644
index 0000000..fcdf82a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/StatementCache.java
@@ -0,0 +1,49 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.lang.reflect.*;
+import java.sql.*;
+import com.mchange.v1.util.ClosableResource;
+
+public interface StatementCache extends ClosableResource
+{
+ public Object checkoutStatement( Connection physicalConnection,
+ Method stmtProducingMethod,
+ Object[] args )
+ throws SQLException;
+
+ public void checkinStatement( Object pstmt )
+ throws SQLException;
+
+ public void checkinAll( Connection pcon )
+ throws SQLException;
+
+ public void closeAll( Connection pcon )
+ throws SQLException;
+
+ public void close()
+ throws SQLException;
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheBenchmark.java b/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheBenchmark.java
new file mode 100644
index 0000000..77b72d9
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheBenchmark.java
@@ -0,0 +1,175 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class StatementCacheBenchmark
+{
+ final static String EMPTY_TABLE_CREATE = "CREATE TABLE emptyyukyuk (a varchar(8), b varchar(8))";
+ final static String EMPTY_TABLE_SELECT = "SELECT * FROM emptyyukyuk";
+ final static String EMPTY_TABLE_DROP = "DROP TABLE emptyyukyuk";
+
+ final static String EMPTY_TABLE_CONDITIONAL_SELECT = "SELECT * FROM emptyyukyuk where a = ?";
+
+ final static int NUM_ITERATIONS = 2000;
+
+ public static void main(String[] argv)
+ {
+ DataSource ds_unpooled = null;
+ DataSource ds_pooled = null;
+ try
+ {
+
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+
+ ds_unpooled = DriverManagerDataSourceFactory.create(jdbc_url, username, password);
+ ds_pooled
+ = PoolBackedDataSourceFactory.create(jdbc_url,
+ username,
+ password,
+ 5,
+ 20,
+ 5,
+ 0,
+ 100 );
+
+ create(ds_pooled);
+
+ perform( ds_pooled, "pooled" );
+ perform( ds_unpooled, "unpooled" );
+ }
+ catch( Exception e )
+ { e.printStackTrace(); }
+ finally
+ {
+ try { drop(ds_pooled); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ private static void perform( DataSource ds, String name )
+ throws SQLException
+ {
+ Connection c = null;
+ PreparedStatement ps = null;
+ try
+ {
+ c = ds.getConnection();
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < NUM_ITERATIONS; ++i)
+ {
+ PreparedStatement test =
+ c.prepareStatement( EMPTY_TABLE_CONDITIONAL_SELECT );
+ test.close();
+ }
+ long end = System.currentTimeMillis();
+ System.err.println(name + " --> " +
+ (end - start) / (float) NUM_ITERATIONS +
+ " [" + NUM_ITERATIONS + " iterations]");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( ps );
+ ConnectionUtils.attemptClose( c );
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ StatementCacheBenchmark.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+
+ static void create(DataSource ds)
+ throws SQLException
+ {
+ System.err.println("Creating test schema.");
+ Connection con = null;
+ PreparedStatement ps1 = null;
+ try
+ {
+ con = ds.getConnection();
+ ps1 = con.prepareStatement(EMPTY_TABLE_CREATE);
+ ps1.executeUpdate();
+ System.err.println("Test schema created.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( ps1 );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ static void drop(DataSource ds)
+ throws SQLException
+ {
+ Connection con = null;
+ PreparedStatement ps1 = null;
+ try
+ {
+ con = ds.getConnection();
+ ps1 = con.prepareStatement(EMPTY_TABLE_DROP);
+ ps1.executeUpdate();
+ }
+ finally
+ {
+ StatementUtils.attemptClose( ps1 );
+ ConnectionUtils.attemptClose( con );
+ }
+ System.err.println("Test schema dropped.");
+ }
+}
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheKey.java b/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheKey.java
new file mode 100644
index 0000000..ab2bea2
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/StatementCacheKey.java
@@ -0,0 +1,180 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.Connection;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import com.mchange.v1.util.ArrayUtils;
+import com.mchange.v2.lang.ObjectUtils;
+
+abstract class StatementCacheKey
+{
+ static final int SIMPLE = 0;
+ static final int MEMORY_COALESCED = 1;
+ static final int VALUE_IDENTITY = 2;
+
+ //NOTE: subclasses rely upon their _find logic being protected by StatementCacheKey.class' lock!
+ public synchronized static StatementCacheKey find( Connection pcon, Method stmtProducingMethod, Object[] args )
+ {
+ switch ( VALUE_IDENTITY )
+ {
+ case SIMPLE:
+ return SimpleStatementCacheKey._find( pcon, stmtProducingMethod, args );
+ case MEMORY_COALESCED:
+ return MemoryCoalescedStatementCacheKey._find( pcon, stmtProducingMethod, args );
+ case VALUE_IDENTITY:
+ return ValueIdentityStatementCacheKey._find( pcon, stmtProducingMethod, args );
+ default:
+ throw new InternalError("StatementCacheKey.find() is misconfigured.");
+ }
+ }
+
+ //MT: instances are treated as immutable once they
+ // have been initialized and handed to
+ // a client. (Factories may reinitialize
+ // instances that never get released to
+ // clients -- those factories must prevent
+ // concurrent access to these recycled,
+ // nascent keys.)
+ Connection physicalConnection;
+ String stmtText;
+ boolean is_callable;
+ int result_set_type;
+ int result_set_concurrency;
+
+ int[] columnIndexes; //jdbc3, null means default
+ String[] columnNames; //jdbc3, null means default
+
+ Integer autogeneratedKeys; //jdbc3, null means driver default, which the spec does not sepcify
+ Integer resultSetHoldability; //jdbc3, null means driver default, which the spec does not sepcify
+
+ StatementCacheKey()
+ {}
+
+ StatementCacheKey( Connection physicalConnection,
+ String stmtText,
+ boolean is_callable,
+ int result_set_type,
+ int result_set_concurrency,
+ int[] columnIndexes,
+ String[] columnNames,
+ Integer autogeneratedKeys,
+ Integer resultSetHoldability )
+ {
+ init( physicalConnection,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability
+ );
+ }
+
+ void init( Connection physicalConnection,
+ String stmtText,
+ boolean is_callable,
+ int result_set_type,
+ int result_set_concurrency,
+ int[] columnIndexes, //jdbc3
+ String[] columnNames, //jdbc3
+ Integer autogeneratedKeys, //jdbc3
+ Integer resultSetHoldability) //jdbc3
+ {
+ this.physicalConnection = physicalConnection;
+ this.stmtText = stmtText;
+ this.is_callable = is_callable;
+ this.result_set_type = result_set_type;
+ this.result_set_concurrency = result_set_concurrency;
+ this.columnIndexes = columnIndexes;
+ this.columnNames = columnNames;
+ this.autogeneratedKeys = autogeneratedKeys;
+ this.resultSetHoldability = resultSetHoldability;
+ }
+
+ static boolean equals(StatementCacheKey _this, Object o)
+ {
+ //TODO: assert( _this != null )
+
+ if ( _this == o )
+ return true;
+ if (o instanceof StatementCacheKey)
+ {
+ StatementCacheKey sck = (StatementCacheKey) o;
+
+// System.err.println( sck.physicalConnection + " " +
+// _this.physicalConnection + " equals? " +
+// sck.physicalConnection.equals( _this.physicalConnection ) );
+
+ return
+ sck.physicalConnection.equals(_this.physicalConnection) &&
+ sck.stmtText.equals(_this.stmtText) &&
+ sck.is_callable == _this.is_callable &&
+ sck.result_set_type == _this.result_set_type &&
+ sck.result_set_concurrency == _this.result_set_concurrency &&
+ Arrays.equals( sck.columnIndexes, _this.columnIndexes ) &&
+ Arrays.equals( sck.columnNames, _this.columnNames ) &&
+ ObjectUtils.eqOrBothNull( sck.autogeneratedKeys, _this.autogeneratedKeys ) &&
+ ObjectUtils.eqOrBothNull( sck.resultSetHoldability, _this.resultSetHoldability );
+ }
+ else
+ return false;
+ }
+
+ static int hashCode(StatementCacheKey _this)
+ {
+ return
+ _this.physicalConnection.hashCode() ^
+ _this.stmtText.hashCode() ^
+ (_this.is_callable ? 1 : 0) ^
+ _this.result_set_type ^
+ _this.result_set_concurrency ^
+ ArrayUtils.hashOrZeroArray( _this.columnIndexes ) ^
+ ArrayUtils.hashOrZeroArray( _this.columnNames ) ^
+ ObjectUtils.hashOrZero( _this.autogeneratedKeys ) ^ //this is okay -- genuine constants are non-zer0
+ ObjectUtils.hashOrZero( _this.resultSetHoldability ); //this is okay -- genuine constants are non-zer0
+ }
+
+ public String toString()
+ {
+ StringBuffer out = new StringBuffer(128);
+ out.append("[" + this.getClass().getName() + ": ");
+ out.append("physicalConnection->" + physicalConnection);
+ out.append(", stmtText->" + stmtText);
+ out.append(", is_callable->" + is_callable);
+ out.append(", result_set_type->" + result_set_type);
+ out.append(", result_set_concurrency->" + result_set_concurrency);
+ out.append(", columnIndexes->" + ArrayUtils.toString(columnIndexes));
+ out.append(", columnNames->" + ArrayUtils.toString(columnNames));
+ out.append(", autogeneratedKeys->" + autogeneratedKeys);
+ out.append(", resultSetHoldability->" + resultSetHoldability);
+ out.append(']');
+ return out.toString();
+ }
+}
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/stmt/ValueIdentityStatementCacheKey.java b/src/classes/com/mchange/v2/c3p0/stmt/ValueIdentityStatementCacheKey.java
new file mode 100644
index 0000000..097e8d9
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/stmt/ValueIdentityStatementCacheKey.java
@@ -0,0 +1,202 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.stmt;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.lang.reflect.Method;
+import com.mchange.v2.coalesce.*;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.lang.reflect.Method;
+import com.mchange.v2.coalesce.*;
+
+final class ValueIdentityStatementCacheKey extends StatementCacheKey
+{
+ //MT: not thread-safe, but protected within the find() method
+ // by StatementCacheKey.class lock
+ final static Coalescer keyCoalescer;
+
+ //MT: modified only within StatementCacheKey.class-locked find() method
+ static ValueIdentityStatementCacheKey spare = new ValueIdentityStatementCacheKey();
+
+ static
+ {
+ CoalesceChecker cc = new CoalesceChecker()
+ {
+ public boolean checkCoalesce( Object a, Object b )
+ { return StatementCacheKey.equals( (StatementCacheKey) a, b ); }
+
+ public int coalesceHash( Object a )
+ { return ((ValueIdentityStatementCacheKey) a).cached_hash; }
+ };
+
+ //make weak, unsync'ed coalescer
+ keyCoalescer = CoalescerFactory.createCoalescer( cc, true, false );
+ }
+
+ static StatementCacheKey _find( Connection pcon, Method stmtProducingMethod, Object[] args )
+ {
+ ///BEGIN FIND LOGIC///
+ String stmtText = (String) args[0];
+ boolean is_callable = stmtProducingMethod.getName().equals("prepareCall");
+ int result_set_type;
+ int result_set_concurrency;
+
+ int[] columnIndexes;
+ String[] columnNames;
+ Integer autogeneratedKeys;
+ Integer resultSetHoldability;
+
+ if (args.length == 1)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 2)
+ {
+ Class[] argTypes = stmtProducingMethod.getParameterTypes();
+ if (argTypes[1].isArray())
+ {
+ Class baseType = argTypes[1].getComponentType();
+ if (baseType == int.class) //second arg is columnIndexes
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = (int[]) args[1];
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (baseType == String.class)
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = (String[]) args[1];
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else
+ throw new IllegalArgumentException("c3p0 probably needs to be updated for some new " +
+ "JDBC spec! As of JDBC3, we expect two arg statement " +
+ "producing methods where the second arg is either " +
+ "an int, int array, or String array.");
+ }
+ else //it should be a boxed int, autogeneratedKeys
+ {
+ result_set_type = ResultSet.TYPE_FORWARD_ONLY;
+ result_set_concurrency = ResultSet.CONCUR_READ_ONLY;
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = (Integer) args[1];
+ resultSetHoldability = null;
+ }
+ }
+ else if (args.length == 3)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = null;
+ }
+ else if (args.length == 4)
+ {
+ result_set_type = ((Integer) args[1]).intValue();
+ result_set_concurrency = ((Integer) args[2]).intValue();
+ columnIndexes = null;
+ columnNames = null;
+ autogeneratedKeys = null;
+ resultSetHoldability = (Integer) args[3];
+ }
+ else
+ throw new IllegalArgumentException("Unexpected number of args to " +
+ stmtProducingMethod.getName() );
+ ///END FIND LOGIC///
+
+
+ // we keep around a "spare" and initialize it over and over again
+ // rather than allocating, because usually we'll find the statement we're
+ // looking for is already in the coalescer, and we can avoid the
+ // allocation altogether.
+ spare.init( pcon,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+
+ StatementCacheKey out = (StatementCacheKey) keyCoalescer.coalesce( spare );
+
+// System.err.println( "StatementCacheKey -> " + out );
+// System.err.println( "Key is coalesced already? " + (out != spare) );
+// System.err.println( "Keys in coalescer: " + keyCoalescer.countCoalesced() );
+
+ if (out == spare)
+ spare = new ValueIdentityStatementCacheKey();
+ return out;
+ }
+
+ void init( Connection physicalConnection,
+ String stmtText,
+ boolean is_callable,
+ int result_set_type,
+ int result_set_concurrency,
+ int[] columnIndexes,
+ String[] columnNames,
+ Integer autogeneratedKeys,
+ Integer resultSetHoldability )
+ {
+ super.init( physicalConnection,
+ stmtText,
+ is_callable,
+ result_set_type,
+ result_set_concurrency,
+ columnIndexes,
+ columnNames,
+ autogeneratedKeys,
+ resultSetHoldability );
+ this.cached_hash = StatementCacheKey.hashCode( this );
+ }
+
+ // extra instance varieable
+ int cached_hash;
+
+ // Note that we DON'T override equals() or hashCode() here -- each instance let the coalescer guarantee a
+ // single instance exists that would equals() it (that is, itself), and we rely on Object's default equals() and
+ // hashCode methods do their thangs.
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/subst/C3P0Substitutions.java b/src/classes/com/mchange/v2/c3p0/subst/C3P0Substitutions.java
new file mode 100644
index 0000000..195bf6b
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/subst/C3P0Substitutions.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.subst;
+
+public final class C3P0Substitutions
+{
+ public final static String VERSION = "@c3p0.version@";
+ public final static String DEBUG = "@c3p0.debug@";
+ public final static String TRACE = "@c3p0.trace@";
+ public final static String TIMESTAMP = "@c3p0.timestamp@";
+
+ private C3P0Substitutions()
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/test/AlwaysFailConnectionTester.java b/src/classes/com/mchange/v2/c3p0/test/AlwaysFailConnectionTester.java
new file mode 100644
index 0000000..bf6c4c7
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/AlwaysFailConnectionTester.java
@@ -0,0 +1,64 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.sql.Connection;
+import com.mchange.v2.c3p0.QueryConnectionTester;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+
+public final class AlwaysFailConnectionTester implements QueryConnectionTester
+{
+ final static MLogger logger = MLog.getLogger( AlwaysFailConnectionTester.class );
+
+ {
+ logger.log(MLevel.WARNING, "Instantiated: " + this, new Exception("Instantiation Stack Trace.") );
+ }
+
+ public int activeCheckConnection(Connection c)
+ {
+ logger.warning(this + ": activeCheckConnection(Connection c)");
+ return CONNECTION_IS_INVALID;
+ }
+
+ public int statusOnException(Connection c, Throwable t)
+ {
+ logger.warning(this + ": statusOnException(Connection c, Throwable t)");
+ return CONNECTION_IS_INVALID;
+ }
+
+ public int activeCheckConnection(Connection c, String preferredTestQuery)
+ {
+ logger.warning(this + ": activeCheckConnection(Connection c, String preferredTestQuery)");
+ return CONNECTION_IS_INVALID;
+ }
+
+ public boolean equals( Object o )
+ { return (o instanceof AlwaysFailConnectionTester); }
+
+ public int hashCode()
+ { return 1; }
+}
+
diff --git a/src/classes/com/mchange/v2/c3p0/test/C3P0BenchmarkApp.java b/src/classes/com/mchange/v2/c3p0/test/C3P0BenchmarkApp.java
new file mode 100644
index 0000000..c7c4121
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/C3P0BenchmarkApp.java
@@ -0,0 +1,732 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+import com.mchange.v2.c3p0.DriverManagerDataSource;
+
+public final class C3P0BenchmarkApp
+{
+ final static String EMPTY_TABLE_CREATE = "CREATE TABLE emptyyukyuk (a varchar(8), b varchar(8))";
+ final static String EMPTY_TABLE_SELECT = "SELECT * FROM emptyyukyuk";
+ final static String EMPTY_TABLE_DROP = "DROP TABLE emptyyukyuk";
+
+ final static String EMPTY_TABLE_CONDITIONAL_SELECT = "SELECT * FROM emptyyukyuk where a = ?";
+
+ final static String N_ENTRY_TABLE_CREATE = "CREATE TABLE n_entryyukyuk (a INTEGER)";
+ final static String N_ENTRY_TABLE_INSERT = "INSERT INTO n_entryyukyuk VALUES ( ? )";
+ final static String N_ENTRY_TABLE_SELECT = "SELECT * FROM n_entryyukyuk";
+ final static String N_ENTRY_TABLE_DROP = "DROP TABLE n_entryyukyuk";
+
+ //final static int NUM_ITERATIONS = 20;
+ final static int NUM_ITERATIONS = 2000;
+ //final static int NUM_ITERATIONS = 10000;
+ //final static int NUM_ITERATIONS = 20000;
+ //final static int NUM_ITERATIONS = 100000;
+
+ public static void main(String[] argv)
+ {
+ if (argv.length > 0)
+ {
+ System.err.println( C3P0BenchmarkApp.class.getName() +
+ " now requires no args. Please set everything in standard c3p0 config files.");
+ return;
+ }
+
+// com.mchange.v2.log.MLog.getLogger( C3P0BenchmarkApp.class ).info("this is some info.");
+// com.mchange.v2.log.MLog.getLogger( C3P0BenchmarkApp.class ).log(com.mchange.v2.log.MLevel.WARNING, "this is a warning.", new Exception("test"));
+// com.mchange.v2.log.MLog.getLogger( C3P0BenchmarkApp.class ).log(com.mchange.v2.log.MLevel.FINE, "this is fine.");
+
+// System.getProperties().put("sprong", java.awt.Color.blue);
+// System.getProperties().put(java.awt.Color.blue, "sprong");
+
+
+ DataSource ds_unpooled = null;
+ DataSource ds_pooled = null;
+ try
+ {
+/*
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+*/
+
+// ds_unpooled = DriverManagerDataSourceFactory.create(jdbc_url, username, password);
+
+// ds_pooled
+// // = PoolBackedDataSourceFactory.create(jdbc_url, username, password);
+// = PoolBackedDataSourceFactory.create(jdbc_url,
+// username,
+// password,
+// 5,
+// 20,
+// 5,
+// 0,
+// 100 );
+
+ //ds_unpooled = DataSources.unpooledDataSource(jdbc_url, username, password);
+ //ds_pooled = DataSources.pooledDataSource( ds_unpooled );
+ ds_unpooled = new DriverManagerDataSource();
+
+ //DataSource ds_unpooled_screwy = C3P0TestUtils.unreliableCommitDataSource( ds_unpooled );
+ //ds_pooled = DataSources.pooledDataSource( ds_unpooled_screwy );
+
+// PoolConfig pc = new PoolConfig();
+// pc.setMaxStatements(200);
+// pc.setCheckoutTimeout(500);
+// ds_pooled = DataSources.pooledDataSource( ds_unpooled, pc );
+// ds_pooled = DataSources.pooledDataSource( ds_unpooled, "foo", "goo" );
+
+ //ds_pooled = DataSources.pooledDataSource(ds_unpooled);
+
+ //ComboPooledDataSource cpds = new ComboPooledDataSource("dumbTestConfig");
+ ComboPooledDataSource cpds = new ComboPooledDataSource();
+ //cpds.setJdbcUrl( jdbc_url );
+ //cpds.setUser( username );
+ //cpds.setPassword( password );
+ ds_pooled = cpds;
+
+// ComboPooledDataSource cpds2 = new ComboPooledDataSource();
+// System.err.println("Made second ComboPooledDataSource.");
+// cpds.getNumIdleConnectionsDefaultUser();
+// cpds2.getNumIdleConnectionsDefaultUser();
+
+// Properties badProps = new Properties();
+// badProps.put("badprop", null);
+// DataSource appendix = DataSources.pooledDataSource(ds_unpooled, badProps);
+
+ create(ds_pooled);
+
+ System.out.println("Please wait. Tests can be very slow.");
+ List l = new ArrayList();
+ l.add( new ConnectionAcquisitionTest() );
+ l.add( new StatementCreateTest() );
+ l.add( new StatementEmptyTableSelectTest() );
+ //l.add( new DataBaseMetaDataListNonexistentTablesTest() );
+ l.add( new PreparedStatementEmptyTableSelectTest() );
+ l.add( new PreparedStatementAcquireTest() );
+ l.add( new ResultSetReadTest() );
+ l.add( new FiveThreadPSQueryTestTest() );
+ for (int i = 0, len = l.size(); i < len; ++i)
+ ((Test) l.get(i)).perform( ds_unpooled, ds_pooled, NUM_ITERATIONS );
+ }
+ catch( Throwable t )
+ {
+ System.err.print("Aborting tests on Throwable -- ");
+ t.printStackTrace();
+ if (t instanceof Error)
+ throw (Error) t;
+ }
+ finally
+ {
+ //System.err.println( "pooled data sources: " + C3P0Registry.getPooledDataSources() );
+
+ try { drop(ds_pooled); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+
+ try { DataSources.destroy(ds_pooled); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+
+ try { DataSources.destroy(ds_unpooled); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ /*
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ C3P0BenchmarkApp.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+*/
+ static void create(DataSource ds)
+ throws SQLException
+ {
+ System.err.println("Creating test schema.");
+ Connection con = null;
+ PreparedStatement ps1 = null;
+ PreparedStatement ps2 = null;
+ PreparedStatement ps3 = null;
+ try
+ {
+ con = ds.getConnection();
+ ps1 = con.prepareStatement(EMPTY_TABLE_CREATE);
+ ps2 = con.prepareStatement(N_ENTRY_TABLE_CREATE);
+ ps3 = con.prepareStatement(N_ENTRY_TABLE_INSERT);
+
+ ps1.executeUpdate();
+ ps2.executeUpdate();
+
+ for (int i = 0; i < NUM_ITERATIONS; ++i)
+ {
+ ps3.setInt(1, i );
+ ps3.executeUpdate();
+ System.err.print('.');
+ }
+ System.err.println();
+ System.err.println("Test schema created.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( ps1 );
+ StatementUtils.attemptClose( ps2 );
+ StatementUtils.attemptClose( ps3 );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ static void drop(DataSource ds)
+ throws SQLException
+ {
+ Connection con = null;
+ PreparedStatement ps1 = null;
+ PreparedStatement ps2 = null;
+ try
+ {
+ con = ds.getConnection();
+ ps1 = con.prepareStatement(EMPTY_TABLE_DROP);
+ ps2 = con.prepareStatement(N_ENTRY_TABLE_DROP);
+
+ ps1.executeUpdate();
+ ps2.executeUpdate();
+
+ // should be superfluous 'cuz should be autocommit
+ //con.commit();
+
+ System.err.println("Test schema dropped.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( ps1 );
+ StatementUtils.attemptClose( ps2 );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ static abstract class Test
+ {
+ String name;
+
+ Test(String name)
+ { this.name = name; }
+
+ public void perform(DataSource unpooled, DataSource pooled, int iterations) throws Exception
+ {
+ double msecs_unpooled = test(unpooled, iterations) / ((double) iterations);
+ double msecs_pooled = test(pooled, iterations) / ((double) iterations);
+ System.out.println(name + " [ " + iterations + " iterations ]:");
+ System.out.println('\t' + "unpooled: " + msecs_unpooled + " msecs");
+ System.out.println('\t' + " pooled: " + msecs_pooled + " msecs");
+ System.out.println('\t' + "speed-up factor: " + msecs_unpooled / msecs_pooled + " times");
+ System.out.println('\t' + "speed-up absolute: " + (msecs_unpooled - msecs_pooled) +
+ " msecs");
+ System.out.println();
+
+// PooledDataSource pds = (PooledDataSource) pooled;
+// System.out.println( pds.getNumConnections() );
+// System.out.println( pds.getNumIdleConnections() );
+// System.out.println( pds.getNumBusyConnections() );
+// System.out.println( pds.getNumConnectionsAllAuths() );
+ }
+
+ protected abstract long test(DataSource ds, int n) throws Exception;
+ }
+
+ static class ConnectionAcquisitionTest extends Test
+ {
+ ConnectionAcquisitionTest()
+ { super("Connection Acquisition and Cleanup"); }
+
+ protected long test(DataSource ds, int n) throws Exception
+ {
+ long start;
+ long end;
+
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ {
+ Connection con = null;
+ try
+ { con = ds.getConnection(); }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+ //System.err.print(i + "\t");
+ }
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ }
+
+ static class StatementCreateTest extends Test
+ {
+ StatementCreateTest()
+ { super("Statement Creation and Cleanup"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ Connection con = null;
+ try
+ {
+ con = ds.getConnection();
+ return test( con , n );
+ }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+ //{}
+ }
+
+ long test(Connection con, int n) throws SQLException
+ {
+ long start;
+ long end;
+
+ Statement stmt = null;
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ {
+ try
+ { stmt = con.createStatement(); }
+ finally
+ { StatementUtils.attemptClose( stmt ); }
+ }
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ }
+
+
+ static class StatementEmptyTableSelectTest extends Test
+ {
+ StatementEmptyTableSelectTest()
+ { super("Empty Table Statement Select (on a single Statement)"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ Connection con = null;
+ Statement stmt = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ //System.err.println( stmt.getClass().getName() );
+ return test( stmt , n );
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ long test(Statement stmt, int n) throws SQLException
+ {
+ long start;
+ long end;
+
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ stmt.executeQuery(EMPTY_TABLE_SELECT).close();
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ }
+
+ static class DataBaseMetaDataListNonexistentTablesTest extends Test
+ {
+ DataBaseMetaDataListNonexistentTablesTest()
+ { super("DataBaseMetaDataListNonexistentTablesTest"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ Connection con = null;
+ Statement stmt = null;
+ try
+ {
+ con = ds.getConnection();
+ return test( con , n );
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ long test(Connection con, int n) throws SQLException
+ {
+ ResultSet rs = null;
+
+ try
+ {
+ long start;
+ long end;
+
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ rs = con.getMetaData().getTables( null,
+ null,
+ "PROBABLYNOT",
+ new String[] {"TABLE"} );
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ finally
+ { ResultSetUtils.attemptClose( rs ); }
+ }
+ }
+
+ static class PreparedStatementAcquireTest extends Test
+ {
+ PreparedStatementAcquireTest()
+ { super("Acquire and Cleanup a PreparedStatement (same statement, many times)"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ long start;
+ long end;
+
+ Connection con = null;
+ PreparedStatement pstmt = null;
+ try
+ {
+ con = ds.getConnection();
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ {
+ try
+ { pstmt = con.prepareStatement(EMPTY_TABLE_CONDITIONAL_SELECT); }
+
+/*
+ Leftover random abuses from ad hoc testing...
+
+ {
+ pstmt = con.prepareStatement(EMPTY_TABLE_CONDITIONAL_SELECT,
+ ResultSet.TYPE_SCROLL_SENSITIVE,
+ ResultSet.CONCUR_UPDATABLE,
+ ResultSet.HOLD_CURSORS_OVER_COMMIT);
+ }
+
+
+ { pstmt = con.prepareStatement(N_ENTRY_TABLE_INSERT); }
+*/
+ finally
+ { StatementUtils.attemptClose( pstmt ); }
+ }
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+ }
+ }
+
+ static class PreparedStatementEmptyTableSelectTest extends Test
+ {
+ PreparedStatementEmptyTableSelectTest()
+ { super("Empty Table PreparedStatement Select (on a single PreparedStatement)"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ Connection con = null;
+ PreparedStatement pstmt = null;
+ try
+ {
+ con = ds.getConnection();
+ pstmt = con.prepareStatement(EMPTY_TABLE_SELECT);
+
+// Leftover from ad-hoc testing...
+//
+// pstmt = con.prepareStatement(EMPTY_TABLE_SELECT,
+// ResultSet.TYPE_SCROLL_SENSITIVE,
+// ResultSet.CONCUR_UPDATABLE,
+// ResultSet.HOLD_CURSORS_OVER_COMMIT);
+ return test( pstmt , n );
+ }
+ finally
+ {
+ StatementUtils.attemptClose( pstmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ long test(PreparedStatement pstmt, int n) throws SQLException
+ {
+ long start;
+ long end;
+
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ pstmt.executeQuery().close();
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ }
+
+ static class ResultSetReadTest extends Test
+ {
+ ResultSetReadTest()
+ { super("Reading one row / one entry from a result set"); }
+
+ protected long test(DataSource ds, int n) throws SQLException
+ {
+ if (n > 10000)
+ throw new IllegalArgumentException("10K max.");
+
+ long start;
+ long end;
+
+ Connection con = null;
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+
+ try
+ {
+ con = ds.getConnection();
+ pstmt = con.prepareStatement(N_ENTRY_TABLE_SELECT);
+ rs = pstmt.executeQuery();
+
+ start = System.currentTimeMillis();
+ for (int i = 0; i < n; ++i)
+ {
+ if (! rs.next() )
+ System.err.println("huh?");
+ rs.getInt(1);
+ }
+ end = System.currentTimeMillis();
+ return end - start;
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( pstmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+ }
+
+ static class FiveThreadPSQueryTestTest extends Test
+ {
+ // only for stupid test to simulate (illegal) concurrent access to a Statement
+// volatile Statement stmt;
+
+ FiveThreadPSQueryTestTest()
+ {
+ super( "Five threads getting a connection, executing a query, " +
+ System.getProperty( "line.separator" ) +
+ "and retrieving results concurrently via a prepared statement (in a transaction)." );
+ }
+
+ protected long test(final DataSource ds, final int n) throws Exception
+ {
+ class QueryThread extends Thread
+ {
+ QueryThread(int num)
+ { super("QueryThread-" + num);}
+
+ public void run()
+ {
+ Connection con = null;
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+
+ for (int i = 0; i < (n / 5); ++i)
+ {
+ try
+ {
+ con = ds.getConnection();
+
+// System.err.println("before txn isolation set: " + con.getTransactionIsolation());
+// con.setTransactionIsolation( Connection.TRANSACTION_SERIALIZABLE );
+// //con.setTransactionIsolation( Connection.TRANSACTION_READ_UNCOMMITTED );
+// System.err.println("after txn isolation set: " + con.getTransactionIsolation());
+
+ con.setAutoCommit( false );
+
+ pstmt = con.prepareStatement( EMPTY_TABLE_CONDITIONAL_SELECT );
+
+// if (Math.random() < 0.5)
+// stmt = pstmt;
+// else if (stmt != null)
+// stmt.getResultSet();
+
+// if (Math.random() < 0.1 && con instanceof C3P0ProxyConnection)
+// con.close();
+
+ pstmt.setString(1, "boo");
+ rs = pstmt.executeQuery();
+ while( rs.next() )
+ System.err.println("Huh?? Empty table has values?");
+ //System.out.println(this + " " + i);
+
+// if (ds instanceof PooledDataSource)
+// {
+// PooledDataSource pds = (PooledDataSource) ds;
+// System.err.println("numConnections: " + pds.getNumConnections() );
+// System.err.println("numIdleConnections: " + pds.getNumIdleConnections() );
+// System.err.println("numBusyConnections: " + pds.getNumBusyConnections() );
+// System.err.println();
+// }
+
+ con.commit();
+ }
+ catch (Exception e)
+ {
+ System.err.print("FiveThreadPSQueryTestTest exception -- ");
+ e.printStackTrace();
+ try { if (con != null) con.rollback(); }
+ catch (SQLException e2)
+ {
+ System.err.print("Rollback on exception failed! -- ");
+ e2.printStackTrace();
+ }
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( pstmt );
+ ConnectionUtils.attemptClose( con );
+ con = null;
+
+// StatementUtils.attemptClose( pstmt ); //dup close
+// ConnectionUtils.attemptClose( con ); //dup close
+// try { System.err.println( pstmt.getConnection() ); } catch (Exception e) {e.printStackTrace();}
+// ResultSetUtils.attemptClose( rs );
+ }
+ }
+ //System.out.println(this + " finished.");
+ }
+ }
+
+ long start = System.currentTimeMillis();
+
+ Thread[] ts = new Thread[5];
+ for (int i = 0; i < 5; ++i)
+ {
+ ts[i] = new QueryThread(i);
+ ts[i].start();
+ }
+ for (int i = 0; i < 5; ++i)
+ ts[i].join();
+
+ return System.currentTimeMillis() - start;
+ }
+
+ }
+
+
+// static class TenByTwoResultSetReadTest extends Test
+// {
+// TenByTwoResultSetReadTest()
+// { super("Reading all entryies from a 10 row 2 col result set"); }
+
+// protected long test(DataSource ds, int n) throws SQLException
+// {
+// long start;
+// long end;
+
+// long start_ctrl;
+// long end_ctrl;
+
+// Connection con = null;
+// PreparedStatement pstmt = null;
+// ResultSet rs = null;
+
+// start = System.currentTimeMillis();
+// for (int i = 0; i < n; ++i)
+// {
+// try
+// {
+// con = ds.getConnection();
+// pstmt = con.prepareStatement(N_ENTRY_TABLE_SELECT);
+// rs = pstmt.executeQuery();
+// while( rs.next() )
+// {
+// rs.getInt(1);
+// rs.getInt(2);
+// }
+// }
+// finally
+// {
+// ResultSetUtils.attemptClose( rs );
+// StatementUtils.attemptClose( pstmt );
+// ConnectionUtils.attemptClose( con );
+// }
+// }
+// end = System.currentTimeMillis();
+
+
+// start_ctrl = System.currentTimeMillis();
+// for (int i = 0; i < n; ++i)
+// {
+// try
+// {
+// con = ds.getConnection();
+// pstmt = con.prepareStatement(N_ENTRY_TABLE_SELECT);
+// rs = pstmt.executeQuery();
+// }
+// finally
+// {
+// ResultSetUtils.attemptClose( rs );
+// StatementUtils.attemptClose( pstmt );
+// ConnectionUtils.attemptClose( con );
+// }
+// }
+// end_ctrl = System.currentTimeMillis();
+
+// return (end - start) - (end_ctrl - start_ctrl);
+// }
+// }
+}
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/test/ConnectionDispersionTest.java b/src/classes/com/mchange/v2/c3p0/test/ConnectionDispersionTest.java
new file mode 100644
index 0000000..cc711a9
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/ConnectionDispersionTest.java
@@ -0,0 +1,233 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class ConnectionDispersionTest
+{
+ private final static int DELAY_TIME = 120000;
+ //private final static int DELAY_TIME = 300000;
+
+ private final static int NUM_THREADS = 600;
+ //private final static int NUM_THREADS = 300;
+ //private final static int NUM_THREADS = 50;
+
+ private final static Integer ZERO = new Integer(0);
+
+ private static boolean should_go = false;
+
+ private static DataSource cpds;
+
+ private static int ready_count = 0;
+
+ private static synchronized void setDataSource(DataSource ds)
+ { cpds = ds; }
+
+ private static synchronized DataSource getDataSource()
+ { return cpds; }
+
+ private static synchronized int ready()
+ { return ++ready_count; }
+
+ private static synchronized boolean isReady()
+ { return ready_count == NUM_THREADS; }
+
+ private static synchronized void start()
+ {
+ should_go = true;
+ ConnectionDispersionTest.class.notifyAll();
+ }
+
+ private static synchronized void stop()
+ {
+ should_go = false;
+ ConnectionDispersionTest.class.notifyAll();
+ }
+
+ private static synchronized boolean shouldGo()
+ { return should_go; }
+
+ public static void main(String[] argv)
+ {
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+
+ try
+ {
+ ComboPooledDataSource ds = new ComboPooledDataSource();
+ ds.setJdbcUrl( jdbc_url );
+ ds.setUser( username );
+ ds.setPassword( password );
+ setDataSource( ds );
+
+ List threads = new ArrayList( NUM_THREADS );
+
+ for (int i = 0; i < NUM_THREADS; ++i)
+ {
+ Thread t = new CompeteThread();
+ t.start();
+ threads.add( t );
+ Thread.currentThread().yield();
+ }
+
+ synchronized ( ConnectionDispersionTest.class )
+ { while (! isReady()) ConnectionDispersionTest.class.wait(); }
+
+ System.err.println("Starting the race.");
+ start();
+
+ System.err.println("Sleeping " + ((float) DELAY_TIME/1000) +
+ " seconds to let the race run");
+ Thread.sleep(DELAY_TIME);
+ System.err.println("Stopping the race.");
+ stop();
+ for (int i = 0; i < NUM_THREADS; ++i)
+ ((Thread) threads.get(i)).join();
+
+ Map outcomeMap = new TreeMap();
+ for (int i = 0; i < NUM_THREADS; ++i)
+ {
+ Integer outcome = new Integer( ((CompeteThread) threads.get(i)).getCount() );
+ Integer old = (Integer) outcomeMap.get( outcome );
+ if (old == null)
+ old = ZERO;
+ outcomeMap.put( outcome, new Integer(old.intValue() + 1) );
+ }
+
+ int last = 0;
+ for (Iterator ii = outcomeMap.keySet().iterator(); ii.hasNext(); )
+ {
+ Integer outcome = (Integer) ii.next();
+ Integer count = (Integer) outcomeMap.get( outcome );
+ int oc = outcome.intValue();
+ int c = count.intValue();
+ for (; last < oc; ++last)
+ System.out.println(String.valueOf(10000 + last).substring(1) + ": ");
+ ++last;
+ System.out.print(String.valueOf(10000 + oc).substring(1) + ": ");
+// if (oc < 10)
+// System.out.print(' ');
+ for(int i = 0; i < c; ++i)
+ System.out.print('*');
+ System.out.println();
+ }
+
+// List outcomes = new ArrayList(NUM_THREADS);
+// for (int i = 0; i < NUM_THREADS; ++i)
+// outcomes.add( new Integer( ((CompeteThread) threads.get(i)).getCount() ) );
+// Collections.sort( outcomes );
+
+// System.out.println("Connection counts:");
+// for (int i = 0; i < NUM_THREADS; ++i)
+// System.out.println( outcomes.get(i) + " (" + i + ")");
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static class CompeteThread extends Thread
+ {
+ DataSource ds;
+ int count;
+
+ synchronized void increment()
+ { ++count; }
+
+ synchronized int getCount()
+ { return count; }
+
+ public void run()
+ {
+ try
+ {
+ this.ds = getDataSource();
+ synchronized ( ConnectionDispersionTest.class )
+ {
+ ready();
+ ConnectionDispersionTest.class.wait();
+ }
+ while ( shouldGo() )
+ {
+ Connection c = null;
+ ResultSet rs = null;
+ try
+ {
+ c = ds.getConnection();
+ increment();
+ rs = c.getMetaData().getTables( null,
+ null,
+ "PROBABLYNOT",
+ new String[] {"TABLE"} );
+ }
+ catch (SQLException e)
+ { e.printStackTrace(); }
+ finally
+ {
+ try {if (rs != null) rs.close(); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+
+ try {if (c != null) c.close(); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ ConnectionDispersionTest.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/FreezableDriverManagerDataSource.java b/src/classes/com/mchange/v2/c3p0/test/FreezableDriverManagerDataSource.java
new file mode 100644
index 0000000..f172ab3
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/FreezableDriverManagerDataSource.java
@@ -0,0 +1,283 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import javax.sql.DataSource;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.c3p0.cfg.C3P0Config;
+import com.mchange.v2.c3p0.impl.DriverManagerDataSourceBase;
+
+
+// this is a copy-paste hack job to do a quick simulation of how c3p0 responds when
+// getConnection() freezes but does not fail.
+public final class FreezableDriverManagerDataSource extends DriverManagerDataSourceBase implements DataSource
+{
+ final static MLogger logger = MLog.getLogger( FreezableDriverManagerDataSource.class );
+
+ final static File FREEZE_FILE = new File("/tmp/c3p0_freeze_file");
+
+ //MT: protected by this' lock
+ Driver driver;
+
+ //MT: protected by this' lock
+ boolean driver_class_loaded = false;
+
+ public FreezableDriverManagerDataSource()
+ { this( true ); }
+
+ public FreezableDriverManagerDataSource(boolean autoregister)
+ {
+ super( autoregister );
+
+ setUpPropertyListeners();
+
+ String user = C3P0Config.initializeStringPropertyVar("user", null);
+ String password = C3P0Config.initializeStringPropertyVar("password", null);
+
+ if (user != null)
+ this.setUser( user );
+
+ if (password != null)
+ this.setPassword( password );
+ }
+
+ private void waitNoFreezeFile() throws SQLException
+ {
+ try
+ {
+ while (true)
+ {
+ if (! FREEZE_FILE.exists())
+ break;
+ Thread.sleep(1000);
+ }
+ }
+ catch (InterruptedException e)
+ {
+ logger.log(MLevel.WARNING, "Frozen cxn acquire interrupted.", e);
+ throw new SQLException( e.toString() );
+ }
+ }
+
+ private void setUpPropertyListeners()
+ {
+ PropertyChangeListener driverClassListener = new PropertyChangeListener()
+ {
+ public void propertyChange( PropertyChangeEvent evt )
+ {
+ Object val = evt.getNewValue();
+ if ( "driverClass".equals( evt.getPropertyName() ) )
+ setDriverClassLoaded( false );
+ }
+ };
+ this.addPropertyChangeListener( driverClassListener );
+ }
+
+ private synchronized boolean isDriverClassLoaded()
+ { return driver_class_loaded; }
+
+ private synchronized void setDriverClassLoaded(boolean dcl)
+ { this.driver_class_loaded = dcl; }
+
+ private void ensureDriverLoaded() throws SQLException
+ {
+ try
+ {
+ if (! isDriverClassLoaded())
+ {
+ if (driverClass != null)
+ Class.forName( driverClass );
+ setDriverClassLoaded( true );
+ }
+ }
+ catch (ClassNotFoundException e)
+ {
+ if (logger.isLoggable(MLevel.WARNING))
+ logger.log(MLevel.WARNING, "Could not load driverClass " + driverClass, e);
+ }
+ }
+
+ // should NOT be sync'ed -- driver() is sync'ed and that's enough
+ // sync'ing the method creates the danger that one freeze on connect
+ // blocks access to the entire DataSource
+
+ public Connection getConnection() throws SQLException
+ {
+ ensureDriverLoaded();
+
+ waitNoFreezeFile();
+
+ Connection out = driver().connect( jdbcUrl, properties );
+ if (out == null)
+ throw new SQLException("Apparently, jdbc URL '" + jdbcUrl + "' is not valid for the underlying " +
+ "driver [" + driver() + "].");
+ return out;
+ }
+
+ // should NOT be sync'ed -- driver() is sync'ed and that's enough
+ // sync'ing the method creates the danger that one freeze on connect
+ // blocks access to the entire DataSource
+
+ public Connection getConnection(String username, String password) throws SQLException
+ {
+ ensureDriverLoaded();
+
+ waitNoFreezeFile();
+
+ Connection out = driver().connect( jdbcUrl, overrideProps(username, password) );
+ if (out == null)
+ throw new SQLException("Apparently, jdbc URL '" + jdbcUrl + "' is not valid for the underlying " +
+ "driver [" + driver() + "].");
+ return out;
+ }
+
+ public PrintWriter getLogWriter() throws SQLException
+ { return DriverManager.getLogWriter(); }
+
+ public void setLogWriter(PrintWriter out) throws SQLException
+ { DriverManager.setLogWriter( out ); }
+
+ public int getLoginTimeout() throws SQLException
+ { return DriverManager.getLoginTimeout(); }
+
+ public void setLoginTimeout(int seconds) throws SQLException
+ { DriverManager.setLoginTimeout( seconds ); }
+
+ //overrides
+ public synchronized void setJdbcUrl(String jdbcUrl)
+ {
+ //System.err.println( "setJdbcUrl( " + jdbcUrl + " )");
+ //new Exception("DEBUG STACK TRACE").printStackTrace();
+ super.setJdbcUrl( jdbcUrl );
+ clearDriver();
+ }
+
+ //"virtual properties"
+ public synchronized void setUser(String user)
+ {
+ String oldUser = this.getUser();
+ if (! eqOrBothNull( user, oldUser ))
+ {
+ if (user != null)
+ properties.put( SqlUtils.DRIVER_MANAGER_USER_PROPERTY, user );
+ else
+ properties.remove( SqlUtils.DRIVER_MANAGER_USER_PROPERTY );
+
+ pcs.firePropertyChange("user", oldUser, user);
+ }
+ }
+
+ public synchronized String getUser()
+ {
+// System.err.println("getUser() -- DriverManagerDataSource@" + System.identityHashCode( this ) +
+// " using Properties@" + System.identityHashCode( properties ));
+// new Exception("STACK TRACE DUMP").printStackTrace();
+ return properties.getProperty( SqlUtils.DRIVER_MANAGER_USER_PROPERTY );
+ }
+
+ public synchronized void setPassword(String password)
+ {
+ String oldPass = this.getPassword();
+ if (! eqOrBothNull( password, oldPass ))
+ {
+ if (password != null)
+ properties.put( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY, password );
+ else
+ properties.remove( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY );
+
+ pcs.firePropertyChange("password", oldPass, password);
+ }
+ }
+
+ public synchronized String getPassword()
+ { return properties.getProperty( SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY ); }
+
+ private final Properties overrideProps(String user, String password)
+ {
+ Properties overriding = (Properties) properties.clone(); //we are relying on a defensive clone in our base class!!!
+
+ if (user != null)
+ overriding.put(SqlUtils.DRIVER_MANAGER_USER_PROPERTY, user);
+ else
+ overriding.remove(SqlUtils.DRIVER_MANAGER_USER_PROPERTY);
+
+ if (password != null)
+ overriding.put(SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY, password);
+ else
+ overriding.remove(SqlUtils.DRIVER_MANAGER_PASSWORD_PROPERTY);
+
+ return overriding;
+ }
+
+ private synchronized Driver driver() throws SQLException
+ {
+ //System.err.println( "driver() <-- " + this );
+ if (driver == null)
+ driver = DriverManager.getDriver( jdbcUrl );
+ return driver;
+ }
+
+ private synchronized void clearDriver()
+ { driver = null; }
+
+ private static boolean eqOrBothNull( Object a, Object b )
+ { return (a == b || (a != null && a.equals(b))); }
+
+ // serialization stuff -- set up bound/constrained property event handlers on deserialization
+ private static final long serialVersionUID = 1;
+ private static final short VERSION = 0x0001;
+
+ private void writeObject( ObjectOutputStream oos ) throws IOException
+ {
+ oos.writeShort( VERSION );
+ }
+
+ private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
+ {
+ short version = ois.readShort();
+ switch (version)
+ {
+ case VERSION:
+ setUpPropertyListeners();
+ break;
+ default:
+ throw new IOException("Unsupported Serialized Version: " + version);
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/JavaBeanRefTest.java b/src/classes/com/mchange/v2/c3p0/test/JavaBeanRefTest.java
new file mode 100644
index 0000000..14ae83a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/JavaBeanRefTest.java
@@ -0,0 +1,51 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import javax.naming.*;
+import com.mchange.v2.naming.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v2.c3p0.impl.*;
+
+public final class JavaBeanRefTest
+{
+ public static void main(String[] argv)
+ {
+ try
+ {
+ ComboPooledDataSource cpds = new ComboPooledDataSource();
+ Reference ref = cpds.getReference();
+ ComboPooledDataSource cpdsJBOF = (ComboPooledDataSource) (new JavaBeanObjectFactory()).getObjectInstance( ref, null, null, null );
+ ComboPooledDataSource cpdsCJBOF = (ComboPooledDataSource) (new C3P0JavaBeanObjectFactory()).getObjectInstance( ref, null, null, null );
+ System.err.println( "cpds: " + cpds );
+ System.err.println( "cpdsJBOF: " + cpdsJBOF );
+ System.err.println( "cpdsCJBOF: " + cpdsCJBOF );
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ private JavaBeanRefTest()
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/test/JndiBindTest.java b/src/classes/com/mchange/v2/c3p0/test/JndiBindTest.java
new file mode 100644
index 0000000..0f54fc6
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/JndiBindTest.java
@@ -0,0 +1,101 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import javax.naming.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+
+public final class JndiBindTest
+{
+ public static void main(String[] argv)
+ {
+ try
+ {
+ String driverClass = null;
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+ String dmds_name = null;
+ String cpds_name = null;
+ String pbds_name = null;
+
+ if (argv.length == 7)
+ {
+ driverClass = argv[0];
+ jdbc_url = argv[1];
+ username = argv[2];
+ password = argv[3];
+ dmds_name = argv[4];
+ cpds_name = argv[5];
+ pbds_name = argv[6];
+ }
+ else if (argv.length == 5)
+ {
+ driverClass = argv[0];
+ jdbc_url = argv[1];
+ username = null;
+ password = null;
+ dmds_name = argv[2];
+ cpds_name = argv[3];
+ pbds_name = argv[4];
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+
+ DataSource dmds = DriverManagerDataSourceFactory.create( driverClass,
+ jdbc_url,
+ username,
+ password );
+ WrapperConnectionPoolDataSource cpds = new WrapperConnectionPoolDataSource();
+ cpds.setNestedDataSource(dmds);
+ DataSource pbds = PoolBackedDataSourceFactory.create( driverClass,
+ jdbc_url,
+ username,
+ password );
+
+ InitialContext ctx = new InitialContext();
+ ctx.rebind( dmds_name , dmds );
+ System.out.println( "DriverManagerDataSource bounds as " + dmds_name );
+ ctx.rebind( cpds_name , cpds );
+ System.out.println( "ConnectionPoolDataSource bounds as " + cpds_name );
+ ctx.rebind( pbds_name , pbds );
+ System.out.println( "PoolDataSource bounds as " + pbds_name );
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " + JndiBindTest.class.getName() + " \\");
+ System.err.println("\t<jdbc_driver_class> \\");
+ System.err.println("\t<jdbc_url> [<username> <password>] \\");
+ System.err.println("\t<dmds_name> <cpds_name> <pbds_name>" );
+ System.exit(-1);
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/JndiLookupTest.java b/src/classes/com/mchange/v2/c3p0/test/JndiLookupTest.java
new file mode 100644
index 0000000..8993aa5
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/JndiLookupTest.java
@@ -0,0 +1,75 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import javax.naming.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+
+public final class JndiLookupTest
+{
+ public static void main(String[] argv)
+ {
+ try
+ {
+
+ String dmds_name = null;
+ String cpds_name = null;
+ String pbds_name = null;
+
+ if (argv.length == 3)
+ {
+ dmds_name = argv[0];
+ cpds_name = argv[1];
+ pbds_name = argv[2];
+ }
+ else
+ usage();
+
+ InitialContext ctx = new InitialContext();
+ DataSource dmds = (DataSource) ctx.lookup( dmds_name );
+ dmds.getConnection().close();
+ System.out.println( "DriverManagerDataSource " + dmds_name +
+ " sucessfully looked up and checked.");
+ ConnectionPoolDataSource cpds = (ConnectionPoolDataSource) ctx.lookup( cpds_name );
+ cpds.getPooledConnection().close();
+ System.out.println( "ConnectionPoolDataSource " + cpds_name +
+ " sucessfully looked up and checked.");
+ DataSource pbds = (DataSource) ctx.lookup( pbds_name );
+ pbds.getConnection().close();
+ System.out.println( "PoolBackedDataSource " + pbds_name +
+ " sucessfully looked up and checked.");
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " +
+ JndiLookupTest.class.getName() + " \\");
+ System.err.println("\t<dmds_name> <wcpds_name> <wpbds_name>" );
+ System.exit(-1);
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/ListTablesTest.java b/src/classes/com/mchange/v2/c3p0/test/ListTablesTest.java
new file mode 100644
index 0000000..5d46c8b
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/ListTablesTest.java
@@ -0,0 +1,59 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.sql.*;
+import javax.sql.*;
+import javax.naming.*;
+import com.mchange.v1.db.sql.*;
+
+public final class ListTablesTest
+{
+ public static void main(String[] argv)
+ {
+ try
+ {
+ InitialContext ctx = new InitialContext();
+ DataSource ds = (DataSource) ctx.lookup(argv[0]);
+ System.err.println( ds.getClass() );
+ Connection con = null;
+ ResultSet rs = null;
+ try
+ {
+ con = ds.getConnection();
+ DatabaseMetaData md = con.getMetaData();
+ rs = md.getTables( null, null, "%", null);
+ while (rs.next())
+ System.out.println(rs.getString(3));
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/LoadPoolBackedDataSource.java b/src/classes/com/mchange/v2/c3p0/test/LoadPoolBackedDataSource.java
new file mode 100644
index 0000000..65ec68e
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/LoadPoolBackedDataSource.java
@@ -0,0 +1,244 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+import com.mchange.v2.c3p0.DriverManagerDataSource;
+
+public final class LoadPoolBackedDataSource
+{
+ final static int NUM_THREADS = 50;
+ final static int ITERATIONS_PER_THREAD = 1000;
+
+ static Random random = new Random();
+ static DataSource ds;
+
+ public static void main(String[] argv)
+ {
+ if (argv.length > 0)
+ {
+ System.err.println( LoadPoolBackedDataSource.class.getName() +
+ " now requires no args. Please set everything in standard c3p0 config files.");
+ return;
+ }
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+
+ /*
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+*/
+
+ try
+ {
+ //DataSource ds_unpooled = DataSources.unpooledDataSource(jdbc_url, username, password);
+ DataSource ds_unpooled = DataSources.unpooledDataSource();
+ ds = DataSources.pooledDataSource( ds_unpooled );
+
+ Connection con = null;
+ Statement stmt = null;
+
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("CREATE TABLE testpbds ( a varchar(16), b varchar(16) )");
+ System.err.println( "LoadPoolBackedDataSource -- TEST SCHEMA CREATED" );
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ System.err.println("relation testpbds already exists, or something " +
+ "bad happened.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+
+ Thread[] threads = new Thread[NUM_THREADS];
+ for (int i = 0; i < NUM_THREADS; ++i)
+ {
+ Thread t = new ChurnThread();
+ threads[i] = t;
+ t.start();
+ System.out.println("THREAD MADE [" + i + "]");
+ Thread.sleep(1000);
+ }
+ for (int i = 0; i < NUM_THREADS; ++i)
+ threads[i].join();
+
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ finally
+ {
+ Connection con = null;
+ Statement stmt = null;
+
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("DROP TABLE testpbds");
+ System.err.println( "LoadPoolBackedDataSource -- TEST SCHEMA DROPPED" );
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+ }
+
+ static class ChurnThread extends Thread
+ {
+ public void run()
+ {
+ try
+ {
+ for( int i = 0; i < ITERATIONS_PER_THREAD; ++i )
+ {
+ Connection con = null;
+ try
+ {
+ con = ds.getConnection();
+ int select = random.nextInt(3);
+ switch (select)
+ {
+ case 0:
+ executeSelect( con );
+ break;
+ case 1:
+ executeInsert( con );
+ break;
+ case 2:
+ executeDelete( con );
+ break;
+ }
+ PooledDataSource pds = (PooledDataSource) ds;
+ System.out.println( pds.getNumConnectionsDefaultUser() );
+ System.out.println( pds.getNumIdleConnectionsDefaultUser() );
+ System.out.println( pds.getNumBusyConnectionsDefaultUser() );
+ System.out.println( pds.getNumConnectionsAllUsers() );
+ }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+
+ //Thread.sleep( random.nextInt( 1000 ) );
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ static void executeInsert(Connection con) throws SQLException
+ {
+ Statement stmt = null;
+ try
+ {
+ stmt = con.createStatement();
+ stmt.executeUpdate("INSERT INTO testpbds VALUES ('" +
+ random.nextInt() + "', '" +
+ random.nextInt() + "')");
+ System.out.println("INSERTION");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ }
+ }
+
+ static void executeDelete(Connection con) throws SQLException
+ {
+ Statement stmt = null;
+ try
+ {
+ stmt = con.createStatement();
+ stmt.executeUpdate("DELETE FROM testpbds;");
+ System.out.println("DELETION");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ }
+ }
+
+ static void executeSelect(Connection con) throws SQLException
+ {
+ long l = System.currentTimeMillis();
+ Statement stmt = null;
+ ResultSet rs = null;
+ try
+ {
+ stmt = con.createStatement();
+ rs = stmt.executeQuery("SELECT count(*) FROM testpbds");
+ rs.next(); //we assume one row, one col
+ System.out.println("SELECT [count=" + rs.getInt(1) + ", time=" +
+ (System.currentTimeMillis() - l) + " msecs]");
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( stmt );
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ LoadPoolBackedDataSource.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+
+
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/OneThreadRepeatedInsertOrQueryTest.java b/src/classes/com/mchange/v2/c3p0/test/OneThreadRepeatedInsertOrQueryTest.java
new file mode 100644
index 0000000..edba820
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/OneThreadRepeatedInsertOrQueryTest.java
@@ -0,0 +1,163 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class OneThreadRepeatedInsertOrQueryTest
+{
+ final static String INSERT_STMT = "INSERT INTO testpbds VALUES ( ? , ? )";
+ final static String SELECT_STMT = "SELECT count(*) FROM testpbds";
+
+ static Random random = new Random();
+ static DataSource ds;
+
+ public static void main(String[] argv)
+ {
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+
+
+ try
+ {
+ DataSource ds_unpooled = DataSources.unpooledDataSource(jdbc_url, username, password);
+ ds = DataSources.pooledDataSource( ds_unpooled );
+
+ Connection con = null;
+ Statement stmt = null;
+
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("CREATE TABLE testpbds ( a varchar(16), b varchar(16) )");
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ System.err.println("relation testpbds already exists, or something " +
+ "bad happened.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+
+ while(true)
+ {
+ con = null;
+ try
+ {
+ con = ds.getConnection();
+ boolean select = random.nextBoolean();
+ if (select)
+ executeSelect( con );
+ else
+ executeInsert( con );
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+
+ //Thread.sleep( random.nextInt( 1000 ) );
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static void executeInsert(Connection con) throws SQLException
+ {
+ PreparedStatement pstmt = null;
+ try
+ {
+ pstmt = con.prepareStatement(INSERT_STMT);
+ pstmt.setInt(1, random.nextInt());
+ pstmt.setInt(2, random.nextInt());
+ pstmt.executeUpdate();
+ System.out.println("INSERTION");
+ }
+ finally
+ {
+ // make sure forgetting this doesn't starve
+ // statement cache, as long as the connection
+ // closes...
+
+ StatementUtils.attemptClose( pstmt );
+ }
+ }
+
+ static void executeSelect(Connection con) throws SQLException
+ {
+ long l = System.currentTimeMillis();
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try
+ {
+ pstmt = con.prepareStatement(SELECT_STMT);
+ rs = pstmt.executeQuery();
+ rs.next(); //we assume one row, one col
+ System.out.println("SELECT [count=" + rs.getInt(1) + ", time=" +
+ (System.currentTimeMillis() - l) + " msecs]");
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( pstmt );
+ }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ OneThreadRepeatedInsertOrQueryTest.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/PSLoadPoolBackedDataSource.java b/src/classes/com/mchange/v2/c3p0/test/PSLoadPoolBackedDataSource.java
new file mode 100644
index 0000000..41f53d2
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/PSLoadPoolBackedDataSource.java
@@ -0,0 +1,226 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class PSLoadPoolBackedDataSource
+{
+ final static String INSERT_STMT = "INSERT INTO testpbds VALUES ( ? , ? )";
+ final static String SELECT_STMT = "SELECT count(*) FROM testpbds";
+ final static String DELETE_STMT = "DELETE FROM testpbds";
+
+ static Random random = new Random();
+ static DataSource ds;
+
+ public static void main(String[] argv)
+ {
+ if (argv.length > 0)
+ {
+ System.err.println( PSLoadPoolBackedDataSource.class.getName() +
+ " now requires no args. Please set everything in standard c3p0 config files.");
+ return;
+ }
+
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+
+ /*
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+ */
+
+ try
+ {
+ //DataSource ds_unpooled = DataSources.unpooledDataSource(jdbc_url, username, password);
+ //DataSource ds_unpooled = new FreezableDriverManagerDataSource();
+
+ DataSource ds_unpooled = DataSources.unpooledDataSource();
+ ds = DataSources.pooledDataSource( ds_unpooled );
+
+ //new java.io.BufferedReader(new java.io.InputStreamReader(System.in)).readLine();
+
+ Connection con = null;
+ Statement stmt = null;
+
+ try
+ {
+ con = ds_unpooled.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("CREATE TABLE testpbds ( a varchar(16), b varchar(16) )");
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ System.err.println("relation testpbds already exists, or something " +
+ "bad happened.");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+
+ //for (int i = 0; i < 5; ++i)
+ for (int i = 0; i < 50; ++i)
+ {
+ Thread t = new ChurnThread();
+ t.start();
+ System.out.println("THREAD MADE [" + i + "]");
+ Thread.sleep(1000);
+ }
+
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static class ChurnThread extends Thread
+ {
+ public void run()
+ {
+ try
+ {
+ while(true)
+ {
+ Connection con = null;
+ try
+ {
+ con = ds.getConnection();
+ int select = random.nextInt(3);
+ switch (select)
+ {
+ case 0:
+ executeSelect( con );
+ break;
+ case 1:
+ executeInsert( con );
+ break;
+ case 2:
+ executeDelete( con );
+ break;
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ finally
+ { ConnectionUtils.attemptClose( con ); }
+
+ //Thread.sleep( random.nextInt( 1000 ) );
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+
+ static void executeInsert(Connection con) throws SQLException
+ {
+ PreparedStatement pstmt = null;
+ try
+ {
+ pstmt = con.prepareStatement(INSERT_STMT);
+ pstmt.setInt(1, random.nextInt());
+ pstmt.setInt(2, random.nextInt());
+ pstmt.executeUpdate();
+ System.out.println("INSERTION");
+ }
+ finally
+ {
+ // make sure forgetting this doesn't starve
+ // statement cache, as long as the connection
+ // closes...
+
+ StatementUtils.attemptClose( pstmt );
+ }
+ }
+
+ static void executeSelect(Connection con) throws SQLException
+ {
+ long l = System.currentTimeMillis();
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try
+ {
+ pstmt = con.prepareStatement(SELECT_STMT);
+ rs = pstmt.executeQuery();
+ rs.next(); //we assume one row, one col
+ System.out.println("SELECT [count=" + rs.getInt(1) + ", time=" +
+ (System.currentTimeMillis() - l) + " msecs]");
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( pstmt );
+ }
+ }
+
+ static void executeDelete(Connection con) throws SQLException
+ {
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try
+ {
+ pstmt = con.prepareStatement(DELETE_STMT);
+ int deleted = pstmt.executeUpdate();
+ System.out.println("DELETE [" + deleted + " rows]");
+ }
+ finally
+ {
+ ResultSetUtils.attemptClose( rs );
+ StatementUtils.attemptClose( pstmt );
+ }
+ }
+
+ /*
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ PSLoadPoolBackedDataSource.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+ */
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/ProxyWrappersTest.java b/src/classes/com/mchange/v2/c3p0/test/ProxyWrappersTest.java
new file mode 100644
index 0000000..b145ec4
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/ProxyWrappersTest.java
@@ -0,0 +1,73 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class ProxyWrappersTest
+{
+ public static void main(String[] argv)
+ {
+ ComboPooledDataSource cpds = null;
+ Connection c = null;
+ try
+ {
+ cpds = new ComboPooledDataSource();
+ cpds.setDriverClass( "org.postgresql.Driver" );
+ cpds.setJdbcUrl( "jdbc:postgresql://localhost/c3p0-test" );
+ cpds.setUser("swaldman");
+ cpds.setPassword("test");
+ cpds.setMinPoolSize(5);
+ cpds.setAcquireIncrement(5);
+ cpds.setMaxPoolSize(20);
+
+ c = cpds.getConnection();
+ c.setAutoCommit( false );
+ Statement stmt = c.createStatement();
+ stmt.executeUpdate("CREATE TABLE pwtest_table (col1 char(5), col2 char(5))");
+ ResultSet rs = stmt.executeQuery("SELECT * FROM pwtest_table");
+ System.err.println("rs: " + rs);
+ System.err.println("rs.getStatement(): " + rs.getStatement());
+ System.err.println("rs.getStatement().getConnection(): " + rs.getStatement().getConnection());
+ }
+ catch( Exception e )
+ { e.printStackTrace(); }
+ finally
+ {
+ try { if (c!= null) c.rollback(); }
+ catch (Exception e) { e.printStackTrace(); }
+ try { if (cpds!= null) cpds.close(); }
+ catch (Exception e) { e.printStackTrace(); }
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/test/RawConnectionOpTest.java b/src/classes/com/mchange/v2/c3p0/test/RawConnectionOpTest.java
new file mode 100644
index 0000000..6c52b89
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/RawConnectionOpTest.java
@@ -0,0 +1,108 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.lang.reflect.Method;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+import com.mchange.v2.c3p0.C3P0ProxyConnection;
+import com.mchange.v2.c3p0.C3P0ProxyStatement;
+import com.mchange.v2.c3p0.util.TestUtils;
+
+public final class RawConnectionOpTest
+{
+ public static void main(String[] argv)
+ {
+ try
+ {
+ String jdbc_url = null;
+ String username = null;
+ String password = null;
+
+ if (argv.length == 3)
+ {
+ jdbc_url = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbc_url = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbc_url.startsWith("jdbc:") )
+ usage();
+
+ ComboPooledDataSource cpds = new ComboPooledDataSource();
+ cpds.setJdbcUrl( jdbc_url );
+ cpds.setUser( username );
+ cpds.setPassword( password );
+ cpds.setMaxPoolSize( 10 );
+// cpds.setUsesTraditionalReflectiveProxies( true );
+
+ C3P0ProxyConnection conn = (C3P0ProxyConnection) cpds.getConnection();
+ Method toStringMethod = Object.class.getMethod("toString", new Class[]{});
+ Method identityHashCodeMethod = System.class.getMethod("identityHashCode", new Class[] {Object.class});
+ System.out.println("rawConnection.toString() -> " +
+ conn.rawConnectionOperation(toStringMethod, C3P0ProxyConnection.RAW_CONNECTION, new Object[]{}));
+ Integer ihc = (Integer) conn.rawConnectionOperation(identityHashCodeMethod, null, new Object[]{C3P0ProxyConnection.RAW_CONNECTION});
+ System.out.println("System.identityHashCode( rawConnection ) -> " + Integer.toHexString( ihc.intValue() ));
+
+ C3P0ProxyStatement stmt = (C3P0ProxyStatement) conn.createStatement();
+ System.out.println("rawStatement.toString() -> " +
+ stmt.rawStatementOperation(toStringMethod, C3P0ProxyStatement.RAW_STATEMENT, new Object[]{}));
+ Integer ihc2 = (Integer) stmt.rawStatementOperation(identityHashCodeMethod, null, new Object[]{C3P0ProxyStatement.RAW_STATEMENT});
+ System.out.println("System.identityHashCode( rawStatement ) -> " + Integer.toHexString( ihc2.intValue() ));
+
+ conn.close();
+
+ for (int i = 0; i < 10; ++i)
+ {
+ C3P0ProxyConnection check = null;
+ try
+ {
+ check = (C3P0ProxyConnection) cpds.getConnection();
+ //System.err.println( TestUtils.samePhysicalConnection( conn, check ) );
+ System.err.println( TestUtils.physicalConnectionIdentityHashCode( check ) == ihc.intValue() );
+ }
+ finally
+ { /* if (check != null) check.close(); */ }
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ private static void usage()
+ {
+ System.err.println("java " + RawConnectionOpTest.class.getName() + " \\");
+ System.err.println("\t<jdbc_driver_class> \\");
+ System.err.println("\t<jdbc_url> [<username> <password>]");
+ System.exit(-1);
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/StatsTest.java b/src/classes/com/mchange/v2/c3p0/test/StatsTest.java
new file mode 100644
index 0000000..c215e3a
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/StatsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+
+public final class StatsTest
+{
+ static void display( ComboPooledDataSource cpds ) throws Exception
+ {
+ System.err.println("numConnections: " + cpds.getNumConnections());
+ System.err.println("numBusyConnections: " + cpds.getNumBusyConnections());
+ System.err.println("numIdleConnections: " + cpds.getNumIdleConnections());
+ System.err.println("numUnclosedOrphanedConnections: " + cpds.getNumUnclosedOrphanedConnections());
+ System.err.println();
+ }
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ ComboPooledDataSource cpds = new ComboPooledDataSource();
+ cpds.setJdbcUrl( argv[0] );
+ cpds.setUser( argv[1] );
+ cpds.setPassword( argv[2] );
+ cpds.setMinPoolSize(5);
+ cpds.setAcquireIncrement(5);
+ cpds.setMaxPoolSize(20);
+
+ System.err.println("Initial...");
+ display( cpds );
+ Thread.sleep(2000);
+
+ HashSet hs = new HashSet();
+ for (int i = 0; i < 20; ++i)
+ {
+ Connection c = cpds.getConnection();
+ hs.add( c );
+ System.err.println( "Adding (" + (i + 1) + ") " + c );
+ display( cpds );
+ Thread.sleep(1000);
+
+// if (i == 9)
+// {
+// //System.err.println("hardReset()ing");
+// //cpds.hardReset();
+// System.err.println("softReset()ing");
+// cpds.softReset();
+// }
+ }
+
+ int count = 0;
+ for (Iterator ii = hs.iterator(); ii.hasNext(); )
+ {
+ Connection c = ((Connection) ii.next());
+ System.err.println("Removing " + ++count);
+ ii.remove();
+ try { c.getMetaData().getTables( null, null, "PROBABLYNOT", new String[] {"TABLE"} ); }
+ catch (Exception e)
+ {
+ System.err.println( e );
+ System.err.println();
+ continue;
+ }
+ finally
+ { c.close(); }
+ display( cpds );
+ }
+
+ System.err.println("Closing data source, \"forcing\" garbage collection, and sleeping for 5 seconds...");
+ cpds.close();
+ System.gc();
+ System.err.println("Main Thread: Sleeping for five seconds!");
+ Thread.sleep(5000);
+// System.gc();
+// Thread.sleep(5000);
+ System.err.println("Bye!");
+ }
+ catch( Exception e )
+ { e.printStackTrace(); }
+ }
+}
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/test/TestConnectionCustomizer.java b/src/classes/com/mchange/v2/c3p0/test/TestConnectionCustomizer.java
new file mode 100644
index 0000000..3cf1d49
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/TestConnectionCustomizer.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import com.mchange.v2.c3p0.*;
+import java.sql.Connection;
+
+public class TestConnectionCustomizer extends AbstractConnectionCustomizer
+{
+ public void onAcquire( Connection c, String pdsIdt )
+ { System.err.println("Acquired " + c + " [" + pdsIdt + "]"); }
+
+ public void onDestroy( Connection c, String pdsIdt )
+ { System.err.println("Destroying " + c + " [" + pdsIdt + "]"); }
+
+ public void onCheckOut( Connection c, String pdsIdt )
+ { System.err.println("Checked out " + c + " [" + pdsIdt + "]"); }
+
+ public void onCheckIn( Connection c, String pdsIdt )
+ { System.err.println("Checking in " + c + " [" + pdsIdt + "]"); }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/TestRefSerStuff.java b/src/classes/com/mchange/v2/c3p0/test/TestRefSerStuff.java
new file mode 100644
index 0000000..a6d35d3
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/TestRefSerStuff.java
@@ -0,0 +1,185 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test;
+
+import java.sql.*;
+import javax.sql.*;
+import com.mchange.v2.c3p0.*;
+import com.mchange.v1.db.sql.*;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import com.mchange.v2.naming.ReferenceableUtils;
+import com.mchange.v2.ser.SerializableUtils;
+import com.mchange.v2.c3p0.DriverManagerDataSource;
+import com.mchange.v2.c3p0.PoolBackedDataSource;
+
+
+public final class TestRefSerStuff
+{
+ static void create(DataSource ds) throws SQLException
+ {
+ Connection con = null;
+ Statement stmt = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("CREATE TABLE TRSS_TABLE ( a_col VARCHAR(16) )");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ static void drop(DataSource ds) throws SQLException
+ {
+ Connection con = null;
+ Statement stmt = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ stmt.executeUpdate("DROP TABLE TRSS_TABLE");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ static void doSomething(DataSource ds) throws SQLException
+ {
+ Connection con = null;
+ Statement stmt = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ int i = stmt.executeUpdate("INSERT INTO TRSS_TABLE VALUES ('" +
+ System.currentTimeMillis() + "')");
+ if (i != 1)
+ throw new SQLException("Insert failed somehow strange!");
+ }
+ finally
+ {
+ StatementUtils.attemptClose( stmt );
+ ConnectionUtils.attemptClose( con );
+ }
+ }
+
+ /*
+ private static void usage()
+ {
+ System.err.println("java " +
+ "-Djdbc.drivers=<comma_sep_list_of_drivers> " +
+ TestRefSerStuff.class.getName() +
+ " <jdbc_url> [<username> <password>]" );
+ System.exit(-1);
+ }
+ */
+
+ static void doTest(DataSource checkMe) throws Exception
+ {
+ doSomething( checkMe );
+ System.err.println("\tcreated: " + checkMe);
+ DataSource afterSer = (DataSource) SerializableUtils.testSerializeDeserialize( checkMe );
+ doSomething( afterSer );
+ System.err.println("\tafter ser: " + afterSer );
+ Reference ref = ((Referenceable) checkMe).getReference();
+// System.err.println("ref: " + ref);
+// System.err.println("Factory Class: " + ref.getFactoryClassName());
+ DataSource afterRef = (DataSource) ReferenceableUtils.referenceToObject( ref,
+ null,
+ null,
+ null );
+// System.err.println("afterRef data source: " + afterRef);
+ doSomething( afterRef );
+ System.err.println("\tafter ref: " + afterRef );
+ }
+
+ public static void main( String[] argv )
+ {
+ if (argv.length > 0)
+ {
+ System.err.println( TestRefSerStuff.class.getName() +
+ " now requires no args. Please set everything in standard c3p0 config files.");
+ return;
+ }
+
+ /*
+ String jdbcUrl = null;
+ String username = null;
+ String password = null;
+ if (argv.length == 3)
+ {
+ jdbcUrl = argv[0];
+ username = argv[1];
+ password = argv[2];
+ }
+ else if (argv.length == 1)
+ {
+ jdbcUrl = argv[0];
+ username = null;
+ password = null;
+ }
+ else
+ usage();
+
+ if (! jdbcUrl.startsWith("jdbc:") )
+ usage();
+ */
+
+ try
+ {
+ DriverManagerDataSource dmds = new DriverManagerDataSource();
+ //dmds.setJdbcUrl( jdbcUrl );
+ //dmds.setUser( username );
+ //dmds.setPassword( password );
+ try { drop( dmds ); }
+ catch (Exception e)
+ { /* Ignore */ }
+ create( dmds );
+
+ System.err.println("DriverManagerDataSource:");
+ doTest( dmds );
+
+ WrapperConnectionPoolDataSource wcpds = new WrapperConnectionPoolDataSource();
+ wcpds.setNestedDataSource( dmds );
+ PoolBackedDataSource pbds = new PoolBackedDataSource();
+ pbds.setConnectionPoolDataSource( wcpds );
+
+ System.err.println("PoolBackedDataSource:");
+ doTest( pbds );
+
+ ComboPooledDataSource cpds = new ComboPooledDataSource();
+ doTest( cpds );
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/junit/C3P0JUnitTestCaseBase.java b/src/classes/com/mchange/v2/c3p0/test/junit/C3P0JUnitTestCaseBase.java
new file mode 100644
index 0000000..db49e59
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/junit/C3P0JUnitTestCaseBase.java
@@ -0,0 +1,62 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test.junit;
+
+import com.mchange.v2.c3p0.*;
+import junit.framework.TestCase;
+
+public abstract class C3P0JUnitTestCaseBase extends TestCase
+{
+ protected ComboPooledDataSource cpds;
+
+ protected void setUp()
+ {
+ //we let this stuff get setup in c3p0.properties now
+
+ /*
+ String url = System.getProperty("c3p0.test.jdbc.url");
+ String user = System.getProperty("c3p0.test.jdbc.user");
+ String password = System.getProperty("c3p0.test.jdbc.password");
+ */
+
+ //C3P0JUnitTestConfig.loadDrivers();
+ cpds = new ComboPooledDataSource();
+
+ /*
+ cpds.setJdbcUrl( url );
+ cpds.setUser( user );
+ cpds.setPassword( password );
+ */
+ }
+
+ protected void tearDown()
+ {
+ try { cpds.close(); }
+ catch ( Exception e )
+ {
+ System.err.println("Exception on DataSource close in JUnit test tearDown():");
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/test/junit/ConnectionPropertiesResetJUnitTestCase.java b/src/classes/com/mchange/v2/c3p0/test/junit/ConnectionPropertiesResetJUnitTestCase.java
new file mode 100644
index 0000000..b3d7895
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/junit/ConnectionPropertiesResetJUnitTestCase.java
@@ -0,0 +1,106 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test.junit;
+
+import java.sql.*;
+import java.util.*;
+import junit.framework.*;
+
+public final class ConnectionPropertiesResetJUnitTestCase extends C3P0JUnitTestCaseBase
+{
+ final static Map TM;
+
+ static
+ {
+ Map tmp = new HashMap();
+ tmp.put("FAKE", SQLData.class);
+ TM = Collections.unmodifiableMap( tmp );
+ }
+
+ public void testAllConnectionDefaultsReset()
+ {
+// System.err.println("XOXO err");
+// System.out.println("XOXO out");
+
+ cpds.setInitialPoolSize(5);
+ cpds.setMinPoolSize(5);
+ cpds.setMaxPoolSize(5);
+ cpds.setMaxIdleTime(0);
+ cpds.setTestConnectionOnCheckout(false);
+ cpds.setTestConnectionOnCheckin(false);
+ cpds.setIdleConnectionTestPeriod(0);
+
+ String dfltCat;
+ int dflt_txn_isolation;
+
+ try
+ {
+ Connection con = null;
+ try
+ {
+ con = cpds.getConnection();
+
+
+ dfltCat = con.getCatalog();
+ dflt_txn_isolation = con.getTransactionIsolation();
+
+ try { con.setReadOnly(true); } catch (Exception e) { /* setReadOnly() not supported */ }
+ try { con.setTypeMap(TM); } catch (Exception e) { /* setTypeMap() not supported */ }
+ try { con.setCatalog("C3P0TestCatalogXXX"); } catch (Exception e) { /* setCatalog() not supported */ }
+ try
+ {
+ con.setTransactionIsolation( dflt_txn_isolation == Connection.TRANSACTION_SERIALIZABLE ?
+ Connection.TRANSACTION_READ_COMMITTED :
+ Connection.TRANSACTION_SERIALIZABLE );
+ }
+ catch (Exception e) { /* setTransactionIsolation() not fully supported */ }
+ }
+ finally
+ {
+ try { if (con != null) con.close(); }
+ catch (Exception e) {}
+ }
+
+ Connection[] cons = new Connection[5];
+ for (int i = 0; i < 5; ++i)
+ {
+ cons[i] = cpds.getConnection();
+ assertFalse( "Connection from pool should not be readOnly!", cons[i].isReadOnly() );
+
+ // some drivers return null rather than an empty type map
+ Map typeMap = cons[i].getTypeMap();
+ assertTrue( "Connection from pool should have an empty type map!", (typeMap == null ? true : typeMap.isEmpty() ) );
+
+ assertEquals( "Connection from pool should have default catalog set!", dfltCat, cons[i].getCatalog() );
+ assertEquals( "Connection from pool should have default txn isolation set!", dflt_txn_isolation, cons[i].getTransactionIsolation() );
+ cons[i].close();
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ fail( e.getMessage() );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/c3p0/test/junit/MarshallUnmarshallDataSourcesJUnitTestCase.java b/src/classes/com/mchange/v2/c3p0/test/junit/MarshallUnmarshallDataSourcesJUnitTestCase.java
new file mode 100644
index 0000000..f473896
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/test/junit/MarshallUnmarshallDataSourcesJUnitTestCase.java
@@ -0,0 +1,111 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.test.junit;
+
+import java.util.*;
+import javax.naming.Reference;
+import junit.framework.*;
+import com.mchange.v2.ser.SerializableUtils;
+import com.mchange.v2.beans.BeansUtils;
+import com.mchange.v2.naming.ReferenceableUtils;
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+
+public final class MarshallUnmarshallDataSourcesJUnitTestCase extends C3P0JUnitTestCaseBase
+{
+ final static Collection EXCLUDE_PROPS = Arrays.asList( new String[]{
+ "allUsers",
+ "connection",
+ "connectionPoolDataSource",
+ "effectivePropertyCycleDefaultUser",
+ "lastCheckinFailureDefaultUser",
+ "lastCheckoutFailureDefaultUser",
+ "lastConnectionTestFailureDefaultUser",
+ "lastIdleTestFailureDefaultUser",
+ "logWriter",
+ "numBusyConnections",
+ "numBusyConnectionsAllUsers",
+ "numBusyConnectionsDefaultUser",
+ "numConnections",
+ "numConnectionsAllUsers",
+ "numConnectionsDefaultUser",
+ "numFailedCheckinsDefaultUser",
+ "numFailedCheckoutsDefaultUser",
+ "numFailedIdleTestsDefaultUser",
+ "numIdleConnections",
+ "numIdleConnectionsAllUsers",
+ "numIdleConnectionsDefaultUser",
+ "numUnclosedOrphanedConnections",
+ "numUnclosedOrphanedConnectionsAllUsers",
+ "numUnclosedOrphanedConnectionsDefaultUser",
+ "numUserPools",
+ "startTimeMillisDefaultUser",
+ "statementCacheNumCheckedOutDefaultUser",
+ "statementCacheNumCheckedOutStatementsAllUsers",
+ "statementCacheNumConnectionsWithCachedStatementsAllUsers",
+ "statementCacheNumConnectionsWithCachedStatementsDefaultUser",
+ "statementCacheNumStatementsAllUsers",
+ "statementCacheNumStatementsDefaultUser",
+ "threadPoolSize",
+ "threadPoolNumActiveThreads",
+ "threadPoolNumIdleThreads",
+ "threadPoolNumTasksPending",
+ "threadPoolStackTraces",
+ "threadPoolStatus",
+ "upTimeMillisDefaultUser"
+ } );
+
+ public void testSerializationRoundTrip()
+ {
+ try
+ {
+ cpds.setIdentityToken("poop"); //simulate a never-before-seen data source, so it's a new registration on deserialization
+ byte[] pickled = SerializableUtils.toByteArray(cpds);
+ ComboPooledDataSource unpickled = (ComboPooledDataSource) SerializableUtils.fromByteArray( pickled );
+ assertTrue( "Marshalled and unmarshalled DataSources should have the same properties!",
+ BeansUtils.equalsByAccessibleProperties( cpds, unpickled, EXCLUDE_PROPS ) );
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ fail( e.getMessage() );
+ }
+ }
+
+ public void testRefDerefRoundTrip()
+ {
+ try
+ {
+ cpds.setIdentityToken("scoop"); //simulate a never-before-seen data source, so it's a new registration on deserialization
+ Reference ref = cpds.getReference();
+ ComboPooledDataSource unpickled = (ComboPooledDataSource) ReferenceableUtils.referenceToObject( ref, null, null, null );
+ assertTrue( "Marshalled and unmarshalled DataSources should have the same properties!",
+ BeansUtils.equalsByAccessibleProperties( cpds, unpickled, EXCLUDE_PROPS ) );
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ fail( e.getMessage() );
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/util/CloseReportingConnectionWrapper.java b/src/classes/com/mchange/v2/c3p0/util/CloseReportingConnectionWrapper.java
new file mode 100644
index 0000000..7f93396
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/util/CloseReportingConnectionWrapper.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.util;
+
+import java.sql.*;
+import com.mchange.v2.sql.filter.*;
+
+public class CloseReportingConnectionWrapper extends FilterConnection
+{
+ public CloseReportingConnectionWrapper( Connection conn )
+ { super( conn ); }
+
+ public void close() throws SQLException
+ {
+ //System.err.print("ADRIAN -- ");
+ new SQLWarning("Connection.close() called!").printStackTrace();
+ super.close();
+ }
+}
diff --git a/src/classes/com/mchange/v2/c3p0/util/ConnectionEventSupport.java b/src/classes/com/mchange/v2/c3p0/util/ConnectionEventSupport.java
new file mode 100644
index 0000000..e5bd165
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/util/ConnectionEventSupport.java
@@ -0,0 +1,76 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.util;
+
+import java.util.*;
+import java.sql.*;
+import javax.sql.*;
+
+public class ConnectionEventSupport
+{
+ PooledConnection source;
+ HashSet mlisteners = new HashSet();
+
+ public ConnectionEventSupport(PooledConnection source)
+ { this.source = source; }
+
+ public synchronized void addConnectionEventListener(ConnectionEventListener mlistener)
+ {mlisteners.add(mlistener);}
+
+ public synchronized void removeConnectionEventListener(ConnectionEventListener mlistener)
+ {mlisteners.remove(mlistener);}
+
+ public void fireConnectionClosed()
+ {
+ Set mlCopy;
+
+ synchronized (this)
+ { mlCopy = (Set) mlisteners.clone(); }
+
+ ConnectionEvent evt = new ConnectionEvent(source);
+ for (Iterator i = mlCopy.iterator(); i.hasNext();)
+ {
+ ConnectionEventListener cl = (ConnectionEventListener) i.next();
+ cl.connectionClosed(evt);
+ }
+ }
+
+ public void fireConnectionErrorOccurred(SQLException error)
+ {
+ Set mlCopy;
+
+ synchronized (this)
+ { mlCopy = (Set) mlisteners.clone(); }
+
+ ConnectionEvent evt = new ConnectionEvent(source, error);
+ for (Iterator i = mlCopy.iterator(); i.hasNext();)
+ {
+ ConnectionEventListener cl = (ConnectionEventListener) i.next();
+ cl.connectionErrorOccurred(evt);
+ }
+ }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/c3p0/util/TestUtils.java b/src/classes/com/mchange/v2/c3p0/util/TestUtils.java
new file mode 100644
index 0000000..dac0bb1
--- /dev/null
+++ b/src/classes/com/mchange/v2/c3p0/util/TestUtils.java
@@ -0,0 +1,182 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.c3p0.util;
+
+import java.sql.*;
+import javax.sql.*;
+import java.lang.reflect.*;
+import java.util.Random;
+import com.mchange.v2.sql.SqlUtils;
+import com.mchange.v2.c3p0.C3P0ProxyConnection;
+
+public final class TestUtils
+{
+ private final static Method OBJECT_EQUALS;
+ private final static Method IDENTITY_HASHCODE;
+ private final static Method IPCFP;
+
+
+ static
+ {
+ try
+ {
+ OBJECT_EQUALS = Object.class.getMethod("equals", new Class[] { Object.class });
+ IDENTITY_HASHCODE = System.class.getMethod("identityHashCode", new Class[] {Object.class});
+
+ // is this okay? getting a method from a class while it is still being initialized?
+ IPCFP = TestUtils.class.getMethod("isPhysicalConnectionForProxy", new Class[] {Connection.class, C3P0ProxyConnection.class});
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ throw new RuntimeException("Huh? Can't reflectively get ahold of expected methods?");
+ }
+ }
+
+ /**
+ * In general, if this method returns true for two distinct C3P0ProxyConnections, it indicates a c3p0 bug.
+ * Once a proxy Connection is close()ed, it should not permit any sort of operation. Prior to Connection
+ * close(), there should be at most one valid proxy Connection associated with a given physical Connection.
+ */
+ public static boolean samePhysicalConnection( C3P0ProxyConnection con1, C3P0ProxyConnection con2 ) throws SQLException
+ {
+ try
+ {
+ Object out = con1.rawConnectionOperation( IPCFP, null, new Object[] { C3P0ProxyConnection.RAW_CONNECTION, con2 } );
+ return ((Boolean) out).booleanValue();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public static boolean isPhysicalConnectionForProxy( Connection physicalConnection, C3P0ProxyConnection proxy ) throws SQLException
+ {
+ try
+ {
+ Object out = proxy.rawConnectionOperation( OBJECT_EQUALS, physicalConnection, new Object[] { C3P0ProxyConnection.RAW_CONNECTION } );
+ return ((Boolean) out).booleanValue();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+ public static int physicalConnectionIdentityHashCode( C3P0ProxyConnection conn ) throws SQLException
+ {
+ try
+ {
+ Object out = conn.rawConnectionOperation( IDENTITY_HASHCODE, null, new Object[] { C3P0ProxyConnection.RAW_CONNECTION } );
+ return ((Integer) out).intValue();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw SqlUtils.toSQLException( e );
+ }
+ }
+
+
+ public static DataSource unreliableCommitDataSource(DataSource ds) throws Exception
+ {
+ return (DataSource) Proxy.newProxyInstance( TestUtils.class.getClassLoader(),
+ new Class[] { DataSource.class },
+ new StupidDataSourceInvocationHandler( ds ) );
+ }
+
+ private TestUtils()
+ {}
+
+ static class StupidDataSourceInvocationHandler implements InvocationHandler
+ {
+ DataSource ds;
+ Random r = new Random();
+
+ StupidDataSourceInvocationHandler(DataSource ds)
+ { this.ds = ds; }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ if ( "getConnection".equals(method.getName()) )
+ {
+ Connection conn = (Connection) method.invoke( ds, args );
+ return Proxy.newProxyInstance( TestUtils.class.getClassLoader(),
+ new Class[] { Connection.class },
+ new StupidConnectionInvocationHandler( conn ) );
+ }
+ else
+ return method.invoke( ds, args );
+ }
+ }
+
+ static class StupidConnectionInvocationHandler implements InvocationHandler
+ {
+ Connection conn;
+ Random r = new Random();
+
+ boolean invalid = false;
+
+ StupidConnectionInvocationHandler(Connection conn)
+ { this.conn = conn; }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable
+ {
+ if ("close".equals(method.getName()))
+ {
+ if (invalid)
+ {
+ new Exception("Duplicate close() called on Connection!!!").printStackTrace();
+ }
+ else
+ {
+ //new Exception("CLOSE CALLED ON UNPOOLED DATASOURCE CONNECTION!").printStackTrace();
+ invalid = true;
+ }
+ return null;
+ }
+ else if ( invalid )
+ throw new SQLException("Connection closed -- cannot " + method.getName());
+ else if ( "commit".equals(method.getName()) && r.nextInt(100) == 0 )
+ {
+ conn.rollback();
+ throw new SQLException("Random commit exception!!!");
+ }
+ else if (r.nextInt(200) == 0)
+ {
+ conn.rollback();
+ conn.close();
+ throw new SQLException("Random Fatal Exception Occurred!!!");
+ }
+ else
+ return method.invoke( conn, args );
+ }
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/cfg/BasicMultiPropertiesConfig.java b/src/classes/com/mchange/v2/cfg/BasicMultiPropertiesConfig.java
new file mode 100644
index 0000000..a5bdd4d
--- /dev/null
+++ b/src/classes/com/mchange/v2/cfg/BasicMultiPropertiesConfig.java
@@ -0,0 +1,286 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cfg;
+
+import java.util.*;
+import java.io.*;
+import com.mchange.v2.log.*;
+
+public class BasicMultiPropertiesConfig extends MultiPropertiesConfig
+{
+ String[] rps;
+ Map propsByResourcePaths = new HashMap();
+ Map propsByPrefixes;
+
+ Properties propsByKey;
+
+ public BasicMultiPropertiesConfig(String[] resourcePaths)
+ { this( resourcePaths, null ); }
+
+ public BasicMultiPropertiesConfig(String[] resourcePaths, MLogger logger)
+ {
+ List goodPaths = new ArrayList();
+ for( int i = 0, len = resourcePaths.length; i < len; ++i )
+ {
+ String rp = resourcePaths[i];
+ if ("/".equals(rp))
+ {
+ try
+ {
+ propsByResourcePaths.put( rp, System.getProperties() );
+ goodPaths.add( rp );
+ }
+ catch (SecurityException e)
+ {
+ if (logger != null)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "Read of system Properties blocked -- " +
+ "ignoring any configuration via System properties, and using Empty Properties! " +
+ "(But any configuration via a resource properties files is still okay!)",
+ e );
+ }
+ else
+ {
+ System.err.println("Read of system Properties blocked -- " +
+ "ignoring any configuration via System properties, and using Empty Properties! " +
+ "(But any configuration via a resource properties files is still okay!)");
+ e.printStackTrace();
+ }
+ }
+ }
+ else
+ {
+ Properties p = new Properties();
+ InputStream pis = MultiPropertiesConfig.class.getResourceAsStream( rp );
+ if ( pis != null )
+ {
+ try
+ {
+ p.load( pis );
+ propsByResourcePaths.put( rp, p );
+ goodPaths.add( rp );
+ }
+ catch (IOException e)
+ {
+ if (logger != null)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "An IOException occurred while loading configuration properties from resource path '" + rp + "'.",
+ e );
+ }
+ else
+ e.printStackTrace();
+ }
+ finally
+ {
+ try { if ( pis != null ) pis.close(); }
+ catch (IOException e)
+ {
+ if (logger != null)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING,
+ "An IOException occurred while closing InputStream from resource path '" + rp + "'.",
+ e );
+ }
+ else
+ e.printStackTrace();
+ }
+ }
+ }
+ else
+ {
+ if (logger != null)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine( "Configuration properties not found at ResourcePath '" + rp + "'. [logger name: " + logger.getName() + ']' );
+ }
+// else if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+// System.err.println("Configuration properties not found at ResourcePath '" + rp + "'." );
+ }
+ }
+ }
+
+ this.rps = (String[]) goodPaths.toArray( new String[ goodPaths.size() ] );
+ this.propsByPrefixes = Collections.unmodifiableMap( extractPrefixMapFromRsrcPathMap(rps, propsByResourcePaths) );
+ this.propsByResourcePaths = Collections.unmodifiableMap( propsByResourcePaths );
+ this.propsByKey = extractPropsByKey(rps, propsByResourcePaths);
+ }
+
+
+ private static String extractPrefix( String s )
+ {
+ int lastdot = s.lastIndexOf('.');
+ if ( lastdot < 0 )
+ return null;
+ else
+ return s.substring(0, lastdot);
+ }
+
+ private static Properties findProps(String rp, Map pbrp)
+ {
+ //System.err.println("findProps( " + rp + ", ... )");
+ Properties p;
+
+ // MOVED THIS LOGIC INTO CONSTRUCTOR ABOVE, TO TREAT SYSTEM PROPS UNIFORMLY
+ // WITH THE REST, AND TO AVOID UNINTENTIONAL ATTEMPTS TO READ RESOURCE "/"
+ // AS STREAM -- swaldman, 2006-01-19
+
+// if ( "/".equals( rp ) )
+// {
+// try { p = System.getProperties(); }
+// catch ( SecurityException e )
+// {
+// System.err.println(BasicMultiPropertiesConfig.class.getName() +
+// " Read of system Properties blocked -- ignoring any configuration via System properties, and using Empty Properties! " +
+// "(But any configuration via a resource properties files is still okay!)");
+// p = new Properties();
+// }
+// }
+// else
+ p = (Properties) pbrp.get( rp );
+
+// System.err.println( p );
+
+ return p;
+ }
+
+ private static Properties extractPropsByKey( String[] resourcePaths, Map pbrp )
+ {
+ Properties out = new Properties();
+ for (int i = 0, len = resourcePaths.length; i < len; ++i)
+ {
+ String rp = resourcePaths[i];
+ Properties p = findProps( rp, pbrp );
+ if (p == null)
+ {
+ System.err.println("Could not find loaded properties for resource path: " + rp);
+ continue;
+ }
+ for (Iterator ii = p.keySet().iterator(); ii.hasNext(); )
+ {
+ Object kObj = ii.next();
+ if (!(kObj instanceof String))
+ {
+ // note that we can not use the MLog library here, because initialization
+ // of that library depends on this function.
+ System.err.println( BasicMultiPropertiesConfig.class.getName() + ": " +
+ "Properties object found at resource path " +
+ ("/".equals(rp) ? "[system properties]" : "'" + rp + "'") +
+ "' contains a key that is not a String: " +
+ kObj);
+ System.err.println("Skipping...");
+ continue;
+ }
+ Object vObj = p.get( kObj );
+ if (vObj != null && !(vObj instanceof String))
+ {
+ // note that we can not use the MLog library here, because initialization
+ // of that library depends on this function.
+ System.err.println( BasicMultiPropertiesConfig.class.getName() + ": " +
+ "Properties object found at resource path " +
+ ("/".equals(rp) ? "[system properties]" : "'" + rp + "'") +
+ " contains a value that is not a String: " +
+ vObj);
+ System.err.println("Skipping...");
+ continue;
+ }
+
+ String key = (String) kObj;
+ String val = (String) vObj;
+ out.put( key, val );
+ }
+ }
+ return out;
+ }
+
+ private static Map extractPrefixMapFromRsrcPathMap(String[] resourcePaths, Map pbrp)
+ {
+ Map out = new HashMap();
+ //for( Iterator ii = pbrp.values().iterator(); ii.hasNext(); )
+ for (int i = 0, len = resourcePaths.length; i < len; ++i)
+ {
+ String rp = resourcePaths[i];
+ Properties p = findProps( rp, pbrp );
+ if (p == null)
+ {
+ System.err.println(BasicMultiPropertiesConfig.class.getName() + " -- Could not find loaded properties for resource path: " + rp);
+ continue;
+ }
+ for (Iterator jj = p.keySet().iterator(); jj.hasNext(); )
+ {
+ Object kObj = jj.next();
+ if (! (kObj instanceof String))
+ {
+ // note that we can not use the MLog library here, because initialization
+ // of that library depends on this function.
+ System.err.println( BasicMultiPropertiesConfig.class.getName() + ": " +
+ "Properties object found at resource path " +
+ ("/".equals(rp) ? "[system properties]" : "'" + rp + "'") +
+ "' contains a key that is not a String: " +
+ kObj);
+ System.err.println("Skipping...");
+ continue;
+ }
+
+ String key = (String) kObj;
+ String prefix = extractPrefix( key );
+ while (prefix != null)
+ {
+ Properties byPfx = (Properties) out.get( prefix );
+ if (byPfx == null)
+ {
+ byPfx = new Properties();
+ out.put( prefix, byPfx );
+ }
+ byPfx.put( key, p.get( key ) );
+
+ prefix=extractPrefix( prefix );
+ }
+ }
+ }
+ return out;
+ }
+
+ public String[] getPropertiesResourcePaths()
+ { return (String[]) rps.clone(); }
+
+ public Properties getPropertiesByResourcePath(String path)
+ {
+ Properties out = ((Properties) propsByResourcePaths.get( path ));
+ return (out == null ? new Properties() : out);
+ }
+
+ public Properties getPropertiesByPrefix(String pfx)
+ {
+ Properties out = ((Properties) propsByPrefixes.get( pfx ));
+ return (out == null ? new Properties() : out);
+ }
+
+ public String getProperty( String key )
+ { return propsByKey.getProperty( key ); }
+}
diff --git a/src/classes/com/mchange/v2/cfg/CombinedMultiPropertiesConfig.java b/src/classes/com/mchange/v2/cfg/CombinedMultiPropertiesConfig.java
new file mode 100644
index 0000000..a7c8203
--- /dev/null
+++ b/src/classes/com/mchange/v2/cfg/CombinedMultiPropertiesConfig.java
@@ -0,0 +1,102 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cfg;
+
+import java.util.*;
+
+class CombinedMultiPropertiesConfig extends MultiPropertiesConfig
+{
+ MultiPropertiesConfig[] configs;
+ String[] resourcePaths;
+
+ CombinedMultiPropertiesConfig( MultiPropertiesConfig[] configs )
+ {
+ this.configs = configs;
+
+ List allPaths = new LinkedList();
+ for (int i = configs.length - 1; i >= 0; --i)
+ {
+ String[] rps = configs[i].getPropertiesResourcePaths();
+ for (int j = rps.length - 1; j >= 0; --j)
+ {
+ String rp = rps[j];
+ if (! allPaths.contains( rp ) )
+ allPaths.add(0, rp);
+ }
+ }
+ this.resourcePaths = (String[]) allPaths.toArray( new String[ allPaths.size() ] );
+ }
+
+ public String[] getPropertiesResourcePaths()
+ { return (String[]) resourcePaths.clone(); }
+
+ public Properties getPropertiesByResourcePath(String path)
+ {
+ for (int i = configs.length - 1; i >= 0; --i)
+ {
+ MultiPropertiesConfig config = configs[i];
+ Properties check = config.getPropertiesByResourcePath(path);
+ if (check != null)
+ return check;
+ }
+ return null;
+ }
+
+ public Properties getPropertiesByPrefix(String pfx)
+ {
+ List entries = new LinkedList();
+ for (int i = configs.length - 1; i >= 0; --i)
+ {
+ MultiPropertiesConfig config = configs[i];
+ Properties check = config.getPropertiesByPrefix(pfx);
+ if (check != null)
+ entries.addAll( 0, check.entrySet() );
+ }
+ if (entries.size() == 0)
+ return null;
+ else
+ {
+ Properties out = new Properties();
+ for (Iterator ii = entries.iterator(); ii.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) ii.next();
+ out.put( entry.getKey(), entry.getValue() );
+ }
+ return out;
+ }
+ }
+
+ public String getProperty( String key )
+ {
+ for (int i = configs.length - 1; i >= 0; --i)
+ {
+ MultiPropertiesConfig config = configs[i];
+ String check = config.getProperty(key);
+ if (check != null)
+ return check;
+ }
+ return null;
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/cfg/MultiPropertiesConfig.java b/src/classes/com/mchange/v2/cfg/MultiPropertiesConfig.java
new file mode 100644
index 0000000..8f17ee8
--- /dev/null
+++ b/src/classes/com/mchange/v2/cfg/MultiPropertiesConfig.java
@@ -0,0 +1,132 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cfg;
+
+import java.util.*;
+import java.io.*;
+import com.mchange.v2.log.*;
+
+/**
+ * MultiPropertiesConfig allows applications to accept configuration data
+ * from a more than one property file (each of which is to be loaded from
+ * a unique path using this class' ClassLoader's resource-loading mechanism),
+ * and permits access to property data via the resource path from which the
+ * properties were loaded, via the prefix of the property (where hierarchical
+ * property names are presumed to be '.'-separated), and simply by key.
+ * In the by-key and by-prefix indices, when two definitions conflict, the
+ * key value pairing specified in the MOST RECENT properties file shadows
+ * earlier definitions, and files are loaded in the order of the list of
+ * resource paths provided a constructor.
+ *
+ * The rescource path "/" is a special case that always refers to System
+ * properties. No actual resource will be loaded.
+
+ * The class manages a special instance called "vmConfig" which is accessable
+ * via a static method. It's resource path is list specified by a text-file,
+ * itself a ClassLoader managed resource, which must be located at
+ * <tt>/com/mchange/v2/cfg/vmConfigResourcePaths.txt</tt>. This file should
+ * be one resource path per line, with blank lines ignored and lines beginning
+ * with '#' treated as comments.
+ */
+public abstract class MultiPropertiesConfig
+{
+ final static MultiPropertiesConfig EMPTY = new BasicMultiPropertiesConfig( new String[0] );
+
+ final static String VM_CONFIG_RSRC_PATHS = "/com/mchange/v2/cfg/vmConfigResourcePaths.txt";
+
+ static MultiPropertiesConfig vmConfig = null;
+
+ public static MultiPropertiesConfig read(String[] resourcePath, MLogger logger)
+ { return new BasicMultiPropertiesConfig( resourcePath, logger ); }
+
+ public static MultiPropertiesConfig read(String[] resourcePath)
+ { return new BasicMultiPropertiesConfig( resourcePath ); }
+
+ public static MultiPropertiesConfig combine( MultiPropertiesConfig[] configs )
+ { return new CombinedMultiPropertiesConfig( configs ); }
+
+ public static MultiPropertiesConfig readVmConfig(String[] defaultResources, String[] preemptingResources)
+ {
+ List l = new LinkedList();
+ if (defaultResources != null)
+ l.add( read( defaultResources ) );
+ l.add( readVmConfig() );
+ if (preemptingResources != null)
+ l.add( read( preemptingResources ) );
+ return combine( (MultiPropertiesConfig[]) l.toArray( new MultiPropertiesConfig[ l.size() ] ) );
+ }
+
+ public static MultiPropertiesConfig readVmConfig()
+ {
+ if ( vmConfig == null )
+ {
+ List rps = new ArrayList();
+
+ BufferedReader br = null;
+ try
+ {
+ InputStream is = MultiPropertiesConfig.class.getResourceAsStream( VM_CONFIG_RSRC_PATHS );
+ if ( is != null )
+ {
+ br = new BufferedReader( new InputStreamReader( is, "8859_1" ) );
+ String rp;
+ while ((rp = br.readLine()) != null)
+ {
+ rp = rp.trim();
+ if ("".equals( rp ) || rp.startsWith("#"))
+ continue;
+
+ rps.add( rp );
+ }
+ vmConfig = new BasicMultiPropertiesConfig( (String[]) rps.toArray( new String[ rps.size() ] ) );
+ }
+ else
+ {
+ System.err.println("com.mchange.v2.cfg.MultiPropertiesConfig: Resource path list could not be found at resource path: " + VM_CONFIG_RSRC_PATHS);
+ System.err.println("com.mchange.v2.cfg.MultiPropertiesConfig: Using empty vmconfig.");
+ vmConfig = EMPTY;
+ }
+ }
+ catch (IOException e)
+ { e.printStackTrace(); }
+ finally
+ {
+ try { if ( br != null ) br.close(); }
+ catch (IOException e) { e.printStackTrace(); }
+ }
+ }
+ return vmConfig;
+ }
+
+ public boolean foundVmConfig()
+ { return vmConfig != EMPTY; }
+
+ public abstract String[] getPropertiesResourcePaths();
+
+ public abstract Properties getPropertiesByResourcePath(String path);
+
+ public abstract Properties getPropertiesByPrefix(String pfx);
+
+ public abstract String getProperty( String key );
+}
diff --git a/src/classes/com/mchange/v2/cfg/junit/BasicMultiPropertiesConfigJUnitTestCase.java b/src/classes/com/mchange/v2/cfg/junit/BasicMultiPropertiesConfigJUnitTestCase.java
new file mode 100644
index 0000000..fe63d8b
--- /dev/null
+++ b/src/classes/com/mchange/v2/cfg/junit/BasicMultiPropertiesConfigJUnitTestCase.java
@@ -0,0 +1,56 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.cfg.junit;
+
+import junit.framework.*;
+import com.mchange.v2.cfg.*;
+
+public final class BasicMultiPropertiesConfigJUnitTestCase extends TestCase
+{
+ final static String RP_A = "/com/mchange/v2/cfg/junit/a.properties";
+ final static String RP_B = "/com/mchange/v2/cfg/junit/b.properties";
+
+ public void testNoSystemConfig()
+ {
+ MultiPropertiesConfig mpc = new BasicMultiPropertiesConfig(new String[] {RP_A, RP_B});
+ //System.err.println(mpc.getProperty( "user.home" ));
+ assertTrue( "/b/home".equals( mpc.getProperty( "user.home" ) ) );
+ }
+
+ public void testSystemShadows()
+ {
+ MultiPropertiesConfig mpc = new BasicMultiPropertiesConfig(new String[] {RP_A, RP_B, "/"});
+ //System.err.println(mpc.getProperty( "user.home" ));
+ assertTrue( (! "/b/home".equals( mpc.getProperty( "user.home" ) ) ) &&
+ (! "/a/home".equals( mpc.getProperty( "user.home" ) ) ) );
+ }
+
+ public void testSystemShadowed()
+ {
+ MultiPropertiesConfig mpc = new BasicMultiPropertiesConfig(new String[] {RP_A, "/", RP_B});
+ //System.err.println(mpc.getProperty( "user.home" ));
+ assertTrue( "/b/home".equals( mpc.getProperty( "user.home" ) ) );
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/coalesce/AbstractStrongCoalescer.java b/src/classes/com/mchange/v2/coalesce/AbstractStrongCoalescer.java
new file mode 100644
index 0000000..0a7687e
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/AbstractStrongCoalescer.java
@@ -0,0 +1,54 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+
+class AbstractStrongCoalescer implements Coalescer
+{
+ Map coalesced;
+
+ AbstractStrongCoalescer( Map coalesced )
+ { this.coalesced = coalesced; }
+
+ public Object coalesce( Object o )
+ {
+ Object out = coalesced.get( o );
+ if ( out == null )
+ {
+ coalesced.put( o , o );
+ out = o;
+ }
+ return out;
+ }
+
+ public int countCoalesced()
+ { return coalesced.size(); }
+
+ public Iterator iterator()
+ { return new CoalescerIterator( coalesced.keySet().iterator() ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/coalesce/AbstractWeakCoalescer.java b/src/classes/com/mchange/v2/coalesce/AbstractWeakCoalescer.java
new file mode 100644
index 0000000..fee4aaf
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/AbstractWeakCoalescer.java
@@ -0,0 +1,61 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+import java.lang.ref.WeakReference;
+
+class AbstractWeakCoalescer implements Coalescer
+{
+ Map wcoalesced;
+
+ AbstractWeakCoalescer( Map wcoalesced )
+ { this.wcoalesced = wcoalesced; }
+
+ public Object coalesce( Object o )
+ {
+ //System.err.println("AbstractWeakCoalescer.coalesce( " + o + " )");
+ Object out = null;
+
+ WeakReference wr = (WeakReference) wcoalesced.get( o );
+ if ( wr != null )
+ out = wr.get(); //there is a conceivable race that would
+ //permit wr be cleared
+ if ( out == null )
+ {
+ wcoalesced.put( o , new WeakReference(o) );
+ out = o;
+ }
+ return out;
+ }
+
+ public int countCoalesced()
+ { return wcoalesced.size(); }
+
+ public Iterator iterator()
+ { return new CoalescerIterator( wcoalesced.keySet().iterator() ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/coalesce/CoalesceChecker.java b/src/classes/com/mchange/v2/coalesce/CoalesceChecker.java
new file mode 100644
index 0000000..736aa1c
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/CoalesceChecker.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+public interface CoalesceChecker
+{
+ /**
+ * @return true iff a and b should be considered equivalent,
+ * so that a Coalescer should simply return whichever
+ * Object it considers canonical.
+ */
+ public boolean checkCoalesce( Object a, Object b );
+
+ /**
+ * Any two objects for which checkCoalese() would return true <b>must</b>
+ * coalesce hash to the same value!!!
+ */
+ public int coalesceHash( Object a );
+}
diff --git a/src/classes/com/mchange/v2/coalesce/CoalesceIdenticator.java b/src/classes/com/mchange/v2/coalesce/CoalesceIdenticator.java
new file mode 100644
index 0000000..4d9786e
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/CoalesceIdenticator.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import com.mchange.v1.identicator.*;
+
+class CoalesceIdenticator implements Identicator
+{
+ CoalesceChecker cc;
+
+ CoalesceIdenticator( CoalesceChecker cc )
+ { this.cc = cc; }
+
+ public boolean identical(Object a, Object b)
+ { return cc.checkCoalesce( a , b ); }
+
+ public int hash(Object o)
+ { return cc.coalesceHash( o ); }
+}
diff --git a/src/classes/com/mchange/v2/coalesce/Coalescer.java b/src/classes/com/mchange/v2/coalesce/Coalescer.java
new file mode 100644
index 0000000..492db70
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/Coalescer.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.Iterator;
+
+public interface Coalescer
+{
+ public Object coalesce( Object o );
+ public int countCoalesced();
+ public Iterator iterator();
+}
diff --git a/src/classes/com/mchange/v2/coalesce/CoalescerFactory.java b/src/classes/com/mchange/v2/coalesce/CoalescerFactory.java
new file mode 100644
index 0000000..135e217
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/CoalescerFactory.java
@@ -0,0 +1,98 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+public final class CoalescerFactory
+{
+ /**
+ * <p>Creates a "Coalescer" that coalesces Objects according to their
+ * equals() method. Given a set of n Objects among whom equals() would
+ * return true, calling coalescer.coalesce() in any order on any sequence
+ * of these Objects will always return a single "canonical" instance.</p>
+ *
+ * <p>This method creates a weak, synchronized coalesecer, safe for use
+ * by multiple Threads.</p>
+ */
+ public static Coalescer createCoalescer()
+ { return createCoalescer( true, true ); }
+
+ /**
+ * <p>Creates a "Coalescer" that coalesces Objects according to their
+ * equals() method. Given a set of n Objects among whom equals() would
+ * return true, calling coalescer.coalesce() in any order on any sequence
+ * of these Objects will always return a single "canonical" instance.</p>
+ *
+ * @param weak if true, the Coalescer will use WeakReferences to hold
+ * its canonical instances, allowing them to be garbage
+ * collected if they are nowhere in use.
+ *
+ * @param synced if true, access to the Coalescer will be automatically
+ * synchronized. if set to false, then users must manually
+ * synchronize access.
+ */
+ public static Coalescer createCoalescer( boolean weak, boolean synced )
+ { return createCoalescer( null, weak, synced ); }
+
+ /**
+ * <p>Creates a "Coalescer" that coalesces Objects according to the
+ * checkCoalesce() method of a "CoalesceChecker". Given a set of
+ * n Objects among whom calling cc.checkCoalesce() on any pair would
+ * return true, calling coalescer.coalesce() in any order on any sequence
+ * of these Objects will always return a single "canonical" instance.
+ * This allows one to define immutable value Objects whose equals()
+ * method is a mere identity test -- one can use a Coalescer in a
+ * factory method to ensure that no two instances with the same values
+ * are made available to clients.</p>
+ *
+ * @param cc CoalesceChecker that will be used to determine whether two
+ * objects are equivalent and can be coalesced. [If cc is null, then two
+ * objects will be coalesced iff o1.equals( o2 ).]
+ *
+ * @param weak if true, the Coalescer will use WeakReferences to hold
+ * its canonical instances, allowing them to be garbage
+ * collected if they are nowhere in use.
+ *
+ * @param synced if true, access to the Coalescer will be automatically
+ * synchronized. if set to false, then users must manually
+ * synchronize access.
+ */
+ public static Coalescer createCoalescer( CoalesceChecker cc, boolean weak, boolean synced )
+ {
+ Coalescer out;
+ if ( cc == null )
+ {
+ out = ( weak ?
+ (Coalescer) new WeakEqualsCoalescer() :
+ (Coalescer) new StrongEqualsCoalescer() );
+ }
+ else
+ {
+ out = ( weak ?
+ (Coalescer) new WeakCcCoalescer( cc ) :
+ (Coalescer) new StrongCcCoalescer( cc ) );
+ }
+ return ( synced ? new SyncedCoalescer( out ) : out );
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/coalesce/CoalescerIterator.java b/src/classes/com/mchange/v2/coalesce/CoalescerIterator.java
new file mode 100644
index 0000000..55f8919
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/CoalescerIterator.java
@@ -0,0 +1,43 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+
+class CoalescerIterator implements Iterator
+{
+ Iterator inner;
+
+ CoalescerIterator(Iterator inner)
+ { this.inner = inner; }
+
+ public boolean hasNext()
+ { return inner.hasNext(); }
+
+ public Object next()
+ { return inner.next(); }
+
+ public void remove()
+ { throw new UnsupportedOperationException("Objects cannot be removed from a coalescer!"); }
+}
diff --git a/src/classes/com/mchange/v2/coalesce/StrongCcCoalescer.java b/src/classes/com/mchange/v2/coalesce/StrongCcCoalescer.java
new file mode 100644
index 0000000..13d1313
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/StrongCcCoalescer.java
@@ -0,0 +1,37 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+import com.mchange.v1.identicator.IdHashMap;
+
+final class StrongCcCoalescer extends AbstractStrongCoalescer
+ implements Coalescer
+{
+ StrongCcCoalescer( CoalesceChecker cc )
+ { super( new IdHashMap( new CoalesceIdenticator( cc ) ) ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/coalesce/StrongEqualsCoalescer.java b/src/classes/com/mchange/v2/coalesce/StrongEqualsCoalescer.java
new file mode 100644
index 0000000..0821a37
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/StrongEqualsCoalescer.java
@@ -0,0 +1,37 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+import java.lang.ref.WeakReference;
+
+final class StrongEqualsCoalescer extends AbstractStrongCoalescer
+ implements Coalescer
+{
+ StrongEqualsCoalescer()
+ { super( new HashMap() ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/coalesce/SyncedCoalescer.java b/src/classes/com/mchange/v2/coalesce/SyncedCoalescer.java
new file mode 100644
index 0000000..b5bf994
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/SyncedCoalescer.java
@@ -0,0 +1,43 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.Iterator;
+
+class SyncedCoalescer implements Coalescer
+{
+ Coalescer inner;
+
+ public SyncedCoalescer( Coalescer inner )
+ { this.inner = inner; }
+
+ public synchronized Object coalesce( Object o )
+ { return inner.coalesce( o ); }
+
+ public synchronized int countCoalesced()
+ { return inner.countCoalesced(); }
+
+ public synchronized Iterator iterator()
+ { return inner.iterator(); }
+}
diff --git a/src/classes/com/mchange/v2/coalesce/WeakCcCoalescer.java b/src/classes/com/mchange/v2/coalesce/WeakCcCoalescer.java
new file mode 100644
index 0000000..e380928
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/WeakCcCoalescer.java
@@ -0,0 +1,37 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+import java.lang.ref.WeakReference;
+import com.mchange.v1.identicator.IdWeakHashMap;
+
+final class WeakCcCoalescer extends AbstractWeakCoalescer implements Coalescer
+{
+ WeakCcCoalescer(CoalesceChecker cc)
+ { super( new IdWeakHashMap( new CoalesceIdenticator( cc ) ) ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/coalesce/WeakEqualsCoalescer.java b/src/classes/com/mchange/v2/coalesce/WeakEqualsCoalescer.java
new file mode 100644
index 0000000..898f19b
--- /dev/null
+++ b/src/classes/com/mchange/v2/coalesce/WeakEqualsCoalescer.java
@@ -0,0 +1,36 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.coalesce;
+
+import java.util.*;
+import java.lang.ref.WeakReference;
+
+class WeakEqualsCoalescer extends AbstractWeakCoalescer
+{
+ WeakEqualsCoalescer()
+ { super( new WeakHashMap() ); }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/codegen/CodegenUtils.java b/src/classes/com/mchange/v2/codegen/CodegenUtils.java
new file mode 100644
index 0000000..feac274
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/CodegenUtils.java
@@ -0,0 +1,175 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen;
+
+import java.lang.reflect.*;
+import java.io.File;
+import java.io.Writer;
+import com.mchange.v1.lang.ClassUtils;
+
+public final class CodegenUtils
+{
+ public static String getModifierString( int modifiers )
+ {
+ StringBuffer sb = new StringBuffer(32);
+ if ( Modifier.isPublic( modifiers ) )
+ sb.append("public ");
+ if ( Modifier.isProtected( modifiers ) )
+ sb.append("protected ");
+ if ( Modifier.isPrivate( modifiers ) )
+ sb.append("private ");
+ if ( Modifier.isAbstract( modifiers ) )
+ sb.append("abstract ");
+ if ( Modifier.isStatic( modifiers ) )
+ sb.append("static ");
+ if ( Modifier.isFinal( modifiers ) )
+ sb.append("final ");
+ if ( Modifier.isSynchronized( modifiers ) )
+ sb.append("synchronized ");
+ if ( Modifier.isTransient( modifiers ) )
+ sb.append("transient ");
+ if ( Modifier.isVolatile( modifiers ) )
+ sb.append("volatile ");
+ if ( Modifier.isStrict( modifiers ) )
+ sb.append("strictfp ");
+ if ( Modifier.isNative( modifiers ) )
+ sb.append("native ");
+ if ( Modifier.isInterface( modifiers ) ) //????
+ sb.append("interface ");
+ return sb.toString().trim();
+ }
+
+ public static Class unarrayClass( Class cl )
+ {
+ Class out = cl;
+ while ( out.isArray() )
+ out = out.getComponentType();
+ return out;
+ }
+
+ public static boolean inSamePackage(String cn1, String cn2)
+ {
+ int pkgdot = cn1.lastIndexOf('.');
+ int pkgdot2 = cn2.lastIndexOf('.');
+
+ //always return true of one class is a primitive or unpackages
+ if (pkgdot < 0 || pkgdot2 < 0)
+ return true;
+ if ( cn1.substring(0, pkgdot).equals(cn1.substring(0, pkgdot)) )
+ {
+ if (cn2.indexOf('.') >= 0)
+ return false;
+ else
+ return true;
+ }
+ else
+ return false;
+ }
+
+ /**
+ * @return fully qualified class name last element
+ */
+ public static String fqcnLastElement(String fqcn)
+ { return ClassUtils.fqcnLastElement( fqcn ); }
+
+ public static String methodSignature( Method m )
+ { return methodSignature( m, null ); }
+
+ public static String methodSignature( Method m, String[] argNames )
+ { return methodSignature( Modifier.PUBLIC, m, argNames ); }
+
+ public static String methodSignature( int modifiers, Method m, String[] argNames )
+ {
+ StringBuffer sb = new StringBuffer(256);
+ sb.append(getModifierString(modifiers));
+ sb.append(' ');
+ sb.append( ClassUtils.simpleClassName( m.getReturnType() ) );
+ sb.append(' ');
+ sb.append( m.getName() );
+ sb.append('(');
+ Class[] cls = m.getParameterTypes();
+ for(int i = 0, len = cls.length; i < len; ++i)
+ {
+ if (i != 0)
+ sb.append(", ");
+ sb.append( ClassUtils.simpleClassName( cls[i] ) );
+ sb.append(' ');
+ sb.append( argNames == null ? String.valueOf((char) ('a' + i)) : argNames[i] );
+ }
+ sb.append(')');
+ Class[] excClasses = m.getExceptionTypes();
+ if (excClasses.length > 0)
+ {
+ sb.append(" throws ");
+ for (int i = 0, len = excClasses.length; i < len; ++i)
+ {
+ if (i != 0)
+ sb.append(", ");
+ sb.append( ClassUtils.simpleClassName( excClasses[i] ) );
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String methodCall( Method m )
+ { return methodCall( m, null ); }
+
+ public static String methodCall( Method m, String[] argNames )
+ {
+ StringBuffer sb = new StringBuffer(256);
+ sb.append( m.getName() );
+ sb.append('(');
+ Class[] cls = m.getParameterTypes();
+ for(int i = 0, len = cls.length; i < len; ++i)
+ {
+ if (i != 0)
+ sb.append(", ");
+ sb.append( argNames == null ? generatedArgumentName( i ) : argNames[i] );
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ public static String generatedArgumentName( int index )
+ { return String.valueOf((char) ('a' + index)); }
+
+ public static String simpleClassName( Class cl )
+ { return ClassUtils.simpleClassName( cl ); }
+
+ public static IndentedWriter toIndentedWriter( Writer w )
+ { return (w instanceof IndentedWriter ? (IndentedWriter) w : new IndentedWriter(w)); }
+
+ public static String packageNameToFileSystemDirPath(String packageName)
+ {
+ StringBuffer sb = new StringBuffer( packageName );
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ if ( sb.charAt(i) == '.' )
+ sb.setCharAt(i, File.separatorChar);
+ sb.append( File.separatorChar );
+ return sb.toString();
+ }
+
+ private CodegenUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/codegen/IndentedWriter.java b/src/classes/com/mchange/v2/codegen/IndentedWriter.java
new file mode 100644
index 0000000..a84e0dd
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/IndentedWriter.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen;
+
+import java.io.*;
+
+/**
+ * @deprecated -- please user com.mchange.v2.io.IndentedWriter
+ */
+public class IndentedWriter extends com.mchange.v2.io.IndentedWriter
+{
+ public IndentedWriter( Writer out )
+ { super( out ); }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/BeanExtractingGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/BeanExtractingGeneratorExtension.java
new file mode 100644
index 0000000..0dce8ce
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/BeanExtractingGeneratorExtension.java
@@ -0,0 +1,121 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class BeanExtractingGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+ int method_modifiers = Modifier.PRIVATE;
+
+ public void setConstructorModifiers( int ctor_modifiers )
+ { this.ctor_modifiers = ctor_modifiers; }
+
+ public int getConstructorModifiers()
+ { return ctor_modifiers; }
+
+ public void setExtractMethodModifiers( int method_modifiers )
+ { this.method_modifiers = method_modifiers; }
+
+ public int getExtractMethodModifiers()
+ { return method_modifiers; }
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ {
+ Set set = new HashSet();
+ set.add("java.beans.BeanInfo");
+ set.add("java.beans.PropertyDescriptor");
+ set.add("java.beans.Introspector");
+ set.add("java.beans.IntrospectionException");
+ set.add("java.lang.reflect.InvocationTargetException");
+ return set;
+ }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.println("private static Class[] NOARGS = new Class[0];");
+ iw.println();
+ iw.print( CodegenUtils.getModifierString( method_modifiers ) );
+ iw.print(" void extractPropertiesFromBean( Object bean ) throws InvocationTargetException, IllegalAccessException, IntrospectionException");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("BeanInfo bi = Introspector.getBeanInfo( bean.getClass() );");
+ iw.println("PropertyDescriptor[] pds = bi.getPropertyDescriptors();");
+ iw.println("for (int i = 0, len = pds.length; i < len; ++i)");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ iw.println("if (\"" + props[i].getName() + "\".equals( pds[i].getName() ) )");
+ iw.upIndent();
+ iw.println("this." + props[i].getName() + " = " + extractorExpr( props[i], propTypes[i] ) + ';');
+ iw.downIndent();
+ }
+ iw.println("}"); //for loop
+
+
+ iw.downIndent();
+ iw.println("}"); //method
+ iw.println();
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.println(' ' + info.getClassName() + "( Object bean ) throws InvocationTargetException, IllegalAccessException, IntrospectionException");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("extractPropertiesFromBean( bean );");
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ private String extractorExpr( Property prop, Class propType )
+ {
+ if ( propType.isPrimitive() )
+ {
+ String castType = BeangenUtils.capitalize( prop.getSimpleTypeName() );
+ String valueMethod = prop.getSimpleTypeName() + "Value()";
+
+ if ( propType == char.class)
+ castType = "Character";
+ else if ( propType == int.class)
+ castType = "Integer";
+
+ return "((" + castType + ") pds[i].getReadMethod().invoke( bean, NOARGS ))." + valueMethod;
+ }
+ else
+ return "(" + prop.getSimpleTypeName() + ") pds[i].getReadMethod().invoke( bean, NOARGS )";
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/BeangenUtils.java b/src/classes/com/mchange/v2/codegen/bean/BeangenUtils.java
new file mode 100644
index 0000000..cf86f41
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/BeangenUtils.java
@@ -0,0 +1,247 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.Comparator;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import com.mchange.v1.lang.ClassUtils;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public final class BeangenUtils
+{
+ public final static Comparator PROPERTY_COMPARATOR = new Comparator()
+ {
+ public int compare(Object a, Object b)
+ {
+ Property aa = (Property) a;
+ Property bb = (Property) b;
+
+ return String.CASE_INSENSITIVE_ORDER.compare(aa.getName(), bb.getName() );
+ }
+ };
+
+ public static String capitalize( String propName )
+ {
+ char c = propName.charAt( 0 );
+ return Character.toUpperCase(c) + propName.substring(1);
+ }
+
+// public static Class[] attemptResolveTypes(ClassInfo info, Property[] props)
+// {
+// String[] gen = info.getGeneralImports();
+// String[] spc = info.getSpecificImports();
+
+// Class[] out = new Class[ props.length ];
+// for ( int i = 0, len = props.length; i < len; ++i )
+// {
+// String name = props[i].getSimpleTypeName();
+// try
+// { out[i] = ClassUtils.forName( name , gen, spc ); }
+// catch ( Exception e )
+// {
+// e.printStackTrace();
+// System.err.println("WARNING: " + this.getClass().getName() + " could not resolve " +
+// "property type '" + name + "'.");
+// out[i] = null;
+// }
+// }
+// }
+
+ public static void writeExplicitDefaultConstructor( int ctor_modifiers, ClassInfo info, IndentedWriter iw) throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.println(' ' + info.getClassName() + "()");
+ iw.println("{}");
+ }
+
+
+ public static void writeArgList(Property[] props, boolean declare_types, IndentedWriter iw ) throws IOException
+ {
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ if (i != 0)
+ iw.print(", ");
+ if (declare_types)
+ iw.print(props[i].getSimpleTypeName() + ' ');
+ iw.print( props[i].getName() );
+ }
+ }
+
+ /**
+ * @deprecated use writePropertyVariable
+ */
+ public static void writePropertyMember( Property prop, IndentedWriter iw ) throws IOException
+ { writePropertyVariable( prop, iw ); }
+
+ public static void writePropertyVariable( Property prop, IndentedWriter iw ) throws IOException
+ { writePropertyVariable( prop, prop.getDefaultValueExpression(), iw ); }
+
+ /**
+ * @deprecated use writePropertyVariable
+ */
+ public static void writePropertyMember( Property prop, String defaultValueExpression, IndentedWriter iw ) throws IOException
+ { writePropertyVariable( prop, defaultValueExpression, iw ); }
+
+ public static void writePropertyVariable( Property prop, String defaultValueExpression, IndentedWriter iw ) throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( prop.getVariableModifiers() ) );
+ iw.print( ' ' + prop.getSimpleTypeName() + ' ' + prop.getName());
+ String dflt = defaultValueExpression;
+ if (dflt != null)
+ iw.print( " = " + dflt );
+ iw.println(';');
+ }
+
+ public static void writePropertyGetter( Property prop, IndentedWriter iw ) throws IOException
+ { writePropertyGetter( prop, prop.getDefensiveCopyExpression(), iw ); }
+
+ public static void writePropertyGetter( Property prop, String defensiveCopyExpression, IndentedWriter iw ) throws IOException
+ {
+ String pfx = ("boolean".equals( prop.getSimpleTypeName() ) ? "is" : "get" );
+ iw.print( CodegenUtils.getModifierString( prop.getGetterModifiers() ) );
+ iw.println(' ' + prop.getSimpleTypeName() + ' ' + pfx + BeangenUtils.capitalize( prop.getName() ) + "()");
+ String retVal = defensiveCopyExpression;
+ if (retVal == null) retVal = prop.getName();
+ iw.println("{ return " + retVal + "; }");
+ }
+
+ public static void writePropertySetter( Property prop, IndentedWriter iw )
+ throws IOException
+ { writePropertySetter( prop, prop.getDefensiveCopyExpression(), iw ); }
+
+ public static void writePropertySetter( Property prop, String setterDefensiveCopyExpression, IndentedWriter iw )
+ throws IOException
+ {
+ String setVal = setterDefensiveCopyExpression;
+ if (setVal == null) setVal = prop.getName();
+ String usualGetExpression = ("this." + prop.getName());
+ String usualSetStatement = ("this." + prop.getName() + " = " + setVal + ';');
+ writePropertySetterWithGetExpressionSetStatement(prop, usualGetExpression, usualSetStatement, iw);
+ }
+
+ public static void writePropertySetterWithGetExpressionSetStatement( Property prop, String getExpression, String setStatement, IndentedWriter iw )
+ throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( prop.getSetterModifiers() ) );
+ iw.print(" void set" + BeangenUtils.capitalize( prop.getName() ) + "( " + prop.getSimpleTypeName() + ' ' + prop.getName() + " )");
+ if ( prop.isConstrained() )
+ iw.println(" throws PropertyVetoException");
+ else
+ iw.println();
+ iw.println('{');
+ iw.upIndent();
+
+ if ( changeMarked( prop ) )
+ {
+ iw.println( prop.getSimpleTypeName() + " oldVal = " + getExpression + ';');
+
+ String oldValExpr = "oldVal";
+ String newValExpr = prop.getName();
+ String changeCheck;
+
+ String simpleTypeName = prop.getSimpleTypeName();
+ if ( ClassUtils.isPrimitive( simpleTypeName ) )
+ {
+ Class propType = ClassUtils.classForPrimitive( simpleTypeName );
+
+ // PropertyChangeSupport already has overloads
+ // for boolean and int
+ if (propType == byte.class)
+ {
+ oldValExpr = "new Byte( "+ oldValExpr +" )";
+ newValExpr = "new Byte( "+ newValExpr +" )";
+ }
+ else if (propType == char.class)
+ {
+ oldValExpr = "new Character( "+ oldValExpr +" )";
+ newValExpr = "new Character( "+ newValExpr +" )";
+ }
+ else if (propType == short.class)
+ {
+ oldValExpr = "new Short( "+ oldValExpr +" )";
+ newValExpr = "new Short( "+ newValExpr +" )";
+ }
+ else if (propType == float.class)
+ {
+ oldValExpr = "new Float( "+ oldValExpr +" )";
+ newValExpr = "new Float( "+ newValExpr +" )";
+ }
+ else if (propType == double.class)
+ {
+ oldValExpr = "new Double( "+ oldValExpr +" )";
+ newValExpr = "new Double( "+ newValExpr +" )";
+ }
+
+ changeCheck = "oldVal != " + prop.getName();
+ }
+ else
+ changeCheck = "! eqOrBothNull( oldVal, " + prop.getName() + " )";
+
+ if ( prop.isConstrained() )
+ {
+ iw.println("if ( " + changeCheck + " )");
+ iw.upIndent();
+ iw.println("vcs.fireVetoableChange( \"" + prop.getName() + "\", " + oldValExpr + ", " + newValExpr + " );");
+ iw.downIndent();
+ }
+
+ iw.println( setStatement );
+
+ if ( prop.isBound() )
+ {
+ iw.println("if ( " + changeCheck + " )");
+ iw.upIndent();
+ iw.println("pcs.firePropertyChange( \"" + prop.getName() + "\", " + oldValExpr + ", " + newValExpr + " );");
+ iw.downIndent();
+ }
+ }
+ else
+ iw.println( setStatement );
+
+ iw.downIndent();
+ iw.println('}');
+ }
+
+ public static boolean hasBoundProperties(Property[] props)
+ {
+ for (int i = 0, len = props.length; i < len; ++i)
+ if (props[i].isBound()) return true;
+ return false;
+ }
+
+ public static boolean hasConstrainedProperties(Property[] props)
+ {
+ for (int i = 0, len = props.length; i < len; ++i)
+ if (props[i].isConstrained()) return true;
+ return false;
+ }
+
+ private static boolean changeMarked( Property prop )
+ { return prop.isBound() || prop.isConstrained(); }
+
+ private BeangenUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/ClassInfo.java b/src/classes/com/mchange/v2/codegen/bean/ClassInfo.java
new file mode 100644
index 0000000..08bd7db
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ClassInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+public interface ClassInfo
+{
+ public String getPackageName();
+ public int getModifiers();
+ public String getClassName();
+ public String getSuperclassName();
+ public String[] getInterfaceNames();
+ public String[] getGeneralImports();
+ public String[] getSpecificImports();
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/CloneableExtension.java b/src/classes/com/mchange/v2/codegen/bean/CloneableExtension.java
new file mode 100644
index 0000000..e245bb9
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/CloneableExtension.java
@@ -0,0 +1,122 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.io.IOException;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class CloneableExtension implements GeneratorExtension
+{
+ boolean export_public;
+ boolean exception_swallowing;
+
+ String mLoggerName = null;
+
+ public boolean isExportPublic()
+ { return export_public; }
+
+ public void setExportPublic(boolean export_public)
+ { this.export_public = export_public; }
+
+ public boolean isExceptionSwallowing()
+ { return exception_swallowing; }
+
+ public void setExceptionSwallowing(boolean exception_swallowing)
+ { this.exception_swallowing = exception_swallowing; }
+
+ public String getMLoggerName()
+ { return mLoggerName; }
+
+ public void setMLoggerName( String mLoggerName )
+ { this.mLoggerName = mLoggerName; }
+
+ public CloneableExtension(boolean export_public, boolean exception_swallowing)
+ {
+ this.export_public = export_public;
+ this.exception_swallowing = exception_swallowing;
+ }
+
+ public CloneableExtension()
+ { this ( true, false ); }
+
+ public Collection extraGeneralImports()
+ { return (mLoggerName == null ? ((Collection) Collections.EMPTY_SET) : ((Collection) Arrays.asList( new String[] {"com.mchange.v2.log"} )) ); }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ {
+ Set set = new HashSet();
+ set.add( "Cloneable" );
+ return set;
+ }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ if (export_public)
+ {
+ iw.print("public Object clone()");
+ if ( !exception_swallowing )
+ iw.println(" throws CloneNotSupportedException");
+ else
+ iw.println();
+ iw.println("{");
+ iw.upIndent();
+ if ( exception_swallowing )
+ {
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+ }
+ iw.println( "return super.clone();" );
+ if ( exception_swallowing )
+ {
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (CloneNotSupportedException e)");
+ iw.println("{");
+ iw.upIndent();
+ if (mLoggerName == null)
+ iw.println("e.printStackTrace();");
+ else
+ {
+ iw.println("if ( " + mLoggerName + ".isLoggable( MLevel.FINE ) )" );
+ iw.upIndent();
+ iw.println( mLoggerName + ".log( MLevel.FINE, \"Inconsistent clone() definitions between subclass and superclass! \", e );");
+ iw.downIndent();
+ }
+ iw.println("throw new RuntimeException(\"Inconsistent clone() definitions between subclass and superclass! \" + e);" );
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ //else, write nothing... just add Cloneable to interface definitions...
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/CompleteConstructorGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/CompleteConstructorGeneratorExtension.java
new file mode 100644
index 0000000..7cefb9f
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/CompleteConstructorGeneratorExtension.java
@@ -0,0 +1,67 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class CompleteConstructorGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.print( info.getClassName() + "( ");
+ BeangenUtils.writeArgList(props, true, iw);
+ iw.println(" )");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ iw.print("this." + props[i].getName() + " = ");
+ String setExp = props[i].getDefensiveCopyExpression();
+ if (setExp == null)
+ setExp = props[i].getName();
+ iw.println(setExp + ';');
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/CopyConstructorGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/CopyConstructorGeneratorExtension.java
new file mode 100644
index 0000000..53685dd
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/CopyConstructorGeneratorExtension.java
@@ -0,0 +1,73 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class CopyConstructorGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.print(" " + info.getClassName() + "( ");
+ iw.print( info.getClassName() + " copyMe" );
+ iw.println(" )");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ String propGetterMethodCall;
+ if (propTypes[i] == boolean.class)
+ propGetterMethodCall = "is" + BeangenUtils.capitalize( props[i].getName() ) + "()";
+ else
+ propGetterMethodCall = "get" + BeangenUtils.capitalize( props[i].getName() ) + "()";
+ iw.println(props[i].getSimpleTypeName() + ' ' + props[i].getName() + " = copyMe." + propGetterMethodCall + ';');
+ iw.print("this." + props[i].getName() + " = ");
+ String setExp = props[i].getDefensiveCopyExpression();
+ if (setExp == null)
+ setExp = props[i].getName();
+ iw.println(setExp + ';');
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/ExplicitDefaultConstructorGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/ExplicitDefaultConstructorGeneratorExtension.java
new file mode 100644
index 0000000..2e93e43
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ExplicitDefaultConstructorGeneratorExtension.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class ExplicitDefaultConstructorGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ { BeangenUtils.writeExplicitDefaultConstructor( ctor_modifiers, info, iw); }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/ExplicitPropsConstructorGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/ExplicitPropsConstructorGeneratorExtension.java
new file mode 100644
index 0000000..17330e4
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ExplicitPropsConstructorGeneratorExtension.java
@@ -0,0 +1,120 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import com.mchange.v2.log.*;
+
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+
+/**
+ * Writes a constructor that takes an explicitly listed subset of a bean's properties
+ * for its arguments, and sets these properties initial values appropriately.
+ * Skips any specified names for properties that are not found in a bean being generated.
+ * Writes nothing if there are none of the property names are properties of the bean.
+ */
+public class ExplicitPropsConstructorGeneratorExtension implements GeneratorExtension
+{
+ final static MLogger logger = MLog.getLogger( ExplicitPropsConstructorGeneratorExtension.class );
+
+ String[] propNames;
+
+ boolean skips_silently = false;
+
+ public ExplicitPropsConstructorGeneratorExtension()
+ {}
+
+ public ExplicitPropsConstructorGeneratorExtension(String[] propNames)
+ { this.propNames = propNames; }
+
+ public String[] getPropNames()
+ { return (String[]) propNames.clone(); }
+
+ public void setPropNames(String[] propNames)
+ { this.propNames = (String[]) propNames.clone(); }
+
+ public boolean isSkipsSilently()
+ { return skips_silently; }
+
+ public void setsSkipsSilently( boolean skips_silently )
+ { this.skips_silently = skips_silently; }
+
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ Map propNamesToProps = new HashMap();
+ for (int i = 0, len = props.length; i < len; ++i)
+ propNamesToProps.put( props[i].getName(), props[i] );
+
+ List subPropsList = new ArrayList( propNames.length );
+ for (int i = 0, len = propNames.length; i < len; ++i)
+ {
+ Property p = (Property) propNamesToProps.get( propNames[i] );
+ if ( p == null )
+ logger.warning("Could not include property '" + propNames[i] +"' in explicit-props-constructor generated for bean class '" +
+ info.getClassName() +"' because the property is not defined for the bean. Skipping.");
+ else
+ subPropsList.add(p);
+ }
+
+ if (subPropsList.size() > 0)
+ {
+ Property[] subProps = (Property[]) subPropsList.toArray( new Property[ subPropsList.size() ] );
+
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.print( info.getClassName() + "( ");
+ BeangenUtils.writeArgList(subProps, true, iw);
+ iw.println(" )");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0, len = subProps.length; i < len; ++i)
+ {
+ iw.print("this." + subProps[i].getName() + " = ");
+ String setExp = subProps[i].getDefensiveCopyExpression();
+ if (setExp == null)
+ setExp = subProps[i].getName();
+ iw.println(setExp + ';');
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/GeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/GeneratorExtension.java
new file mode 100644
index 0000000..7aa2e49
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/GeneratorExtension.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.io.IOException;
+import com.mchange.v2.codegen.IndentedWriter;
+
+/**
+ * By the time generate(...) is called, all extra interfaces and imports from all
+ * GeneratorExtensions should be incorporated into the passed-in ClassInfo object.
+ */
+public interface GeneratorExtension
+{
+ public Collection extraGeneralImports();
+ public Collection extraSpecificImports();
+ public Collection extraInterfaceNames();
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException;
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/IndirectingSerializableExtension.java b/src/classes/com/mchange/v2/codegen/bean/IndirectingSerializableExtension.java
new file mode 100644
index 0000000..8e606ae
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/IndirectingSerializableExtension.java
@@ -0,0 +1,153 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.io.Serializable;
+import java.io.IOException;
+import com.mchange.v2.codegen.IndentedWriter;
+import com.mchange.v2.ser.IndirectPolicy;
+
+public class IndirectingSerializableExtension extends SerializableExtension
+{
+ protected String findIndirectorExpr;
+ protected String indirectorClassName;
+
+ /**
+ * We expect this indirector to be a public class with a public no_arg ctor;
+ * If you need the indirector initialized somehow, you'll have to extend
+ * the class.
+ *
+ * @see #writeInitializeIndirector
+ * @see #writeExtraDeclarations
+ */
+ public IndirectingSerializableExtension( String indirectorClassName )
+ {
+ this.indirectorClassName = indirectorClassName;
+ this.findIndirectorExpr = "new " + indirectorClassName + "()";
+ }
+
+ protected IndirectingSerializableExtension()
+ {}
+
+ public Collection extraSpecificImports()
+ {
+ Collection col = super.extraSpecificImports();
+ col.add( indirectorClassName );
+ col.add( "com.mchange.v2.ser.IndirectlySerialized" );
+ col.add( "com.mchange.v2.ser.Indirector" );
+ col.add( "com.mchange.v2.ser.SerializableUtils" );
+ col.add( "java.io.NotSerializableException" );
+ return col;
+ }
+
+ protected IndirectPolicy indirectingPolicy( Property prop, Class propType )
+ {
+ if (Serializable.class.isAssignableFrom( propType ))
+ return IndirectPolicy.DEFINITELY_DIRECT;
+ else
+ return IndirectPolicy.INDIRECT_ON_EXCEPTION;
+ }
+
+ /**
+ * hook method... does nothing by default... override at will.
+ * The indirector will be called, uh, "indirector".
+ * You are in the middle of a method when you define this.
+ */
+ protected void writeInitializeIndirector( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {}
+
+ protected void writeExtraDeclarations(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {}
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ super.generate( info, superclassType, props, propTypes, iw);
+ writeExtraDeclarations( info, superclassType, props, propTypes, iw);
+ }
+
+ protected void writeStoreObject( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {
+ IndirectPolicy policy = indirectingPolicy( prop, propType );
+ if (policy == IndirectPolicy.DEFINITELY_INDIRECT)
+ writeIndirectStoreObject( prop, propType, iw );
+ else if (policy == IndirectPolicy.INDIRECT_ON_EXCEPTION)
+ {
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("//test serialize");
+ iw.println("SerializableUtils.toByteArray(" + prop.getName() + ");");
+ super.writeStoreObject( prop, propType, iw );
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (NotSerializableException nse)");
+ iw.println("{");
+ iw.upIndent();
+ writeIndirectStoreObject( prop, propType, iw );
+ iw.downIndent();
+ iw.println("}");
+ }
+ else if (policy == IndirectPolicy.DEFINITELY_DIRECT)
+ super.writeStoreObject( prop, propType, iw );
+ else
+ throw new InternalError("indirectingPolicy() overridden to return unknown policy: " + policy);
+ }
+
+ protected void writeIndirectStoreObject( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {
+ iw.println("try");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("Indirector indirector = " + findIndirectorExpr + ';');
+ writeInitializeIndirector( prop, propType, iw );
+ iw.println("oos.writeObject( indirector.indirectForm( " + prop.getName() + " ) );");
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println("catch (IOException indirectionIOException)");
+ iw.println("{ throw indirectionIOException; }");
+ iw.println("catch (Exception indirectionOtherException)");
+ iw.println("{ throw new IOException(\"Problem indirectly serializing " + prop.getName() + ": \" + indirectionOtherException.toString() ); }");
+ }
+
+ protected void writeUnstoreObject( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {
+ IndirectPolicy policy = indirectingPolicy( prop, propType );
+ if (policy == IndirectPolicy.DEFINITELY_INDIRECT || policy == IndirectPolicy.INDIRECT_ON_EXCEPTION)
+ {
+ iw.println("Object o = ois.readObject();");
+ iw.println("if (o instanceof IndirectlySerialized) o = ((IndirectlySerialized) o).getObject();");
+ iw.println("this." + prop.getName() + " = (" + prop.getSimpleTypeName() + ") o;");
+ }
+ else if (policy == IndirectPolicy.DEFINITELY_DIRECT)
+ super.writeUnstoreObject( prop, propType, iw );
+ else
+ throw new InternalError("indirectingPolicy() overridden to return unknown policy: " + policy);
+ }
+
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/InnerBeanPropertyBeanGenerator.java b/src/classes/com/mchange/v2/codegen/bean/InnerBeanPropertyBeanGenerator.java
new file mode 100644
index 0000000..17f8502
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/InnerBeanPropertyBeanGenerator.java
@@ -0,0 +1,200 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.*;
+
+public class InnerBeanPropertyBeanGenerator extends SimplePropertyBeanGenerator
+{
+ String innerBeanClassName;
+
+ int inner_bean_member_modifiers = Modifier.PROTECTED;
+
+ int inner_bean_accessor_modifiers = Modifier.PROTECTED;
+ int inner_bean_replacer_modifiers = Modifier.PROTECTED;
+
+ String innerBeanInitializationExpression = null; //if you want it to stay null, set to String "null"
+
+ public void setInnerBeanClassName(String innerBeanClassName)
+ { this.innerBeanClassName = innerBeanClassName; }
+
+ public String getInnerBeanClassName()
+ { return innerBeanClassName; }
+
+ private String defaultInnerBeanInitializationExpression()
+ { return "new " + innerBeanClassName + "()"; }
+
+ private String findInnerBeanClassName()
+ { return (innerBeanClassName == null ? "InnerBean" : innerBeanClassName); }
+
+ private String findInnerBeanInitializationExpression()
+ { return (innerBeanInitializationExpression == null ? defaultInnerBeanInitializationExpression() : innerBeanInitializationExpression); }
+
+ private int findInnerClassModifiers()
+ {
+ int out = Modifier.STATIC;
+ if (Modifier.isPublic( inner_bean_accessor_modifiers ) || Modifier.isPublic( inner_bean_replacer_modifiers ))
+ out |= Modifier.PUBLIC;
+ else if (Modifier.isProtected( inner_bean_accessor_modifiers ) || Modifier.isProtected( inner_bean_replacer_modifiers ))
+ out |= Modifier.PROTECTED;
+ else if (Modifier.isPrivate( inner_bean_accessor_modifiers ) && Modifier.isPrivate( inner_bean_replacer_modifiers ))
+ out |= Modifier.PRIVATE;
+ //else leave as package accessible
+ return out;
+ }
+
+
+ //TODO: add a hook for subclassses to custom define maskedProps
+ private void writeSyntheticInnerBeanClass() throws IOException
+ {
+ int num_props = props.length;
+ Property[] maskedProps = new Property[ num_props ];
+ for (int i = 0; i < num_props; ++i)
+ {
+ maskedProps[i] = new SimplePropertyMask( props[i] )
+ {
+ public int getVariableModifiers()
+ { return Modifier.PRIVATE | Modifier.TRANSIENT; }
+ };
+ }
+
+ ClassInfo ci = new WrapperClassInfo( info )
+ {
+ public String getClassName()
+ { return "InnerBean"; }
+
+ public int getModifiers()
+ { return findInnerClassModifiers(); }
+ };
+
+ createInnerGenerator().generate( ci, maskedProps, iw );
+ }
+
+ protected PropertyBeanGenerator createInnerGenerator()
+ {
+ SimplePropertyBeanGenerator innerGenerator = new SimplePropertyBeanGenerator();
+ innerGenerator.setInner( true );
+ innerGenerator.addExtension( new SerializableExtension() );
+ CloneableExtension ce = new CloneableExtension();
+ ce.setExceptionSwallowing( true );
+ innerGenerator.addExtension( ce );
+ return innerGenerator;
+ }
+
+ protected void writeOtherVariables() throws IOException
+ {
+ iw.println( CodegenUtils.getModifierString( inner_bean_member_modifiers ) + ' ' +
+ findInnerBeanClassName() + " innerBean = " + findInnerBeanInitializationExpression() + ';');
+ iw.println();
+ iw.println( CodegenUtils.getModifierString( inner_bean_accessor_modifiers ) + ' ' +
+ findInnerBeanClassName() + " accessInnerBean()");
+ iw.println("{ return innerBean; }");
+ }
+
+ protected void writeOtherFunctions() throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( inner_bean_replacer_modifiers ) + ' ' +
+ findInnerBeanClassName() + " replaceInnerBean( " + findInnerBeanClassName() + " innerBean )");
+ if (constrainedProperties())
+ iw.println(" throws PropertyVetoException");
+ else
+ iw.println();
+ iw.println("{");
+ iw.upIndent();
+ iw.println("beforeReplaceInnerBean();");
+ iw.println("this.innerBean = innerBean;");
+ iw.println("afterReplaceInnerBean();");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+
+ boolean is_abstract = Modifier.isAbstract( info.getModifiers() );
+ iw.print("protected ");
+ if (is_abstract)
+ iw.print("abstract ");
+ iw.print("void beforeReplaceInnerBean()");
+ if (constrainedProperties())
+ iw.print(" throws PropertyVetoException");
+ if (is_abstract)
+ iw.println(';');
+ else
+ iw.println(" {} //hook method for subclasses");
+ iw.println();
+
+ iw.print("protected ");
+ if (is_abstract)
+ iw.print("abstract ");
+ iw.print("void afterReplaceInnerBean()");
+ if (is_abstract)
+ iw.println(';');
+ else
+ iw.println(" {} //hook method for subclasses");
+ iw.println();
+
+ BeangenUtils.writeExplicitDefaultConstructor( Modifier.PUBLIC, info, iw );
+ iw.println();
+ iw.println("public " + info.getClassName() + "(" + findInnerBeanClassName() + " innerBean)");
+ iw.println("{ this.innerBean = innerBean; }");
+ }
+
+ protected void writeOtherClasses() throws IOException
+ {
+ if (innerBeanClassName == null)
+ writeSyntheticInnerBeanClass();
+ }
+
+ protected void writePropertyVariable( Property prop ) throws IOException
+ { /* do nothing... we have no members, only the inner bean */ }
+
+ protected void writePropertyGetter( Property prop, Class propType ) throws IOException
+ {
+ String stn = prop.getSimpleTypeName();
+ String pfx = ("boolean".equals( stn ) ? "is" : "get" );
+ String methodName = pfx + BeangenUtils.capitalize( prop.getName() );
+ iw.print( CodegenUtils.getModifierString( prop.getGetterModifiers() ) );
+ iw.println(' ' + prop.getSimpleTypeName() + ' ' + methodName + "()");
+ iw.println('{');
+ iw.upIndent();
+ iw.println( stn + ' ' + prop.getName() + " = innerBean." + methodName + "();");
+ String retVal = this.getGetterDefensiveCopyExpression( prop, propType );
+ if (retVal == null) retVal = prop.getName();
+ iw.println("return " + retVal + ';');
+ iw.downIndent();
+ iw.println('}');
+ }
+
+ protected void writePropertySetter( Property prop, Class propType ) throws IOException
+ {
+ String stn = prop.getSimpleTypeName();
+ String pfx = ("boolean".equals( stn ) ? "is" : "get" );
+
+ String setVal = this.getSetterDefensiveCopyExpression( prop, propType );
+ if (setVal == null) setVal = prop.getName();
+ String getExpression = ("innerBean." + pfx + BeangenUtils.capitalize( prop.getName() ) + "()");
+ String setStatement = ("innerBean.set" + BeangenUtils.capitalize( prop.getName() ) + "( " + setVal + " );");
+ BeangenUtils.writePropertySetterWithGetExpressionSetStatement(prop, getExpression, setStatement, iw);
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/codegen/bean/ParsedPropertyBeanDocument.java b/src/classes/com/mchange/v2/codegen/bean/ParsedPropertyBeanDocument.java
new file mode 100644
index 0000000..d478c97
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ParsedPropertyBeanDocument.java
@@ -0,0 +1,180 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import org.w3c.dom.*;
+import java.lang.reflect.Modifier;
+import com.mchange.v1.xml.DomParseUtils;
+
+public class ParsedPropertyBeanDocument
+{
+ final static String[] EMPTY_SA = new String[0];
+
+ String packageName;
+ int class_modifiers;
+ String className;
+ String superclassName;
+ String[] interfaceNames = EMPTY_SA;
+ String[] generalImports = EMPTY_SA;
+ String[] specificImports = EMPTY_SA;
+ Property[] properties;
+
+ public ParsedPropertyBeanDocument(Document doc)
+ {
+ Element rootElem = doc.getDocumentElement();
+ this.packageName = DomParseUtils.allTextFromUniqueChild( rootElem, "package" );
+ Element modifiersElem = DomParseUtils.uniqueImmediateChild( rootElem, "modifiers" );
+ if (modifiersElem != null)
+ class_modifiers = parseModifiers( modifiersElem );
+ else
+ class_modifiers = Modifier.PUBLIC;
+
+ Element importsElem = DomParseUtils.uniqueChild( rootElem, "imports" );
+ if (importsElem != null)
+ {
+ this.generalImports = DomParseUtils.allTextFromImmediateChildElements( importsElem, "general" );
+ this.specificImports = DomParseUtils.allTextFromImmediateChildElements( importsElem, "specific" );
+ }
+ this.className = DomParseUtils.allTextFromUniqueChild( rootElem, "output-class" );
+ this.superclassName = DomParseUtils.allTextFromUniqueChild( rootElem, "extends" );
+
+ Element implementsElem = DomParseUtils.uniqueChild( rootElem, "implements" );
+ if (implementsElem != null)
+ this.interfaceNames = DomParseUtils.allTextFromImmediateChildElements( implementsElem, "interface" );
+ Element propertiesElem = DomParseUtils.uniqueChild( rootElem, "properties" );
+ this.properties = findProperties( propertiesElem );
+ }
+
+
+ public ClassInfo getClassInfo()
+ {
+ return new ClassInfo()
+ {
+ public String getPackageName()
+ { return packageName; }
+
+ public int getModifiers()
+ { return class_modifiers; }
+
+ public String getClassName()
+ { return className; }
+
+ public String getSuperclassName()
+ { return superclassName; }
+
+ public String[] getInterfaceNames()
+ { return interfaceNames; }
+
+ public String[] getGeneralImports()
+ { return generalImports; }
+
+ public String[] getSpecificImports()
+ { return specificImports; }
+ };
+ }
+
+ public Property[] getProperties()
+ { return (Property[]) properties.clone(); }
+
+ private Property[] findProperties( Element propertiesElem )
+ {
+ NodeList nl = DomParseUtils.immediateChildElementsByTagName( propertiesElem, "property" );
+ int len = nl.getLength();
+ Property[] out = new Property[ len ];
+ for( int i = 0; i < len; ++i)
+ {
+ Element propertyElem = (Element) nl.item( i );
+
+ int variable_modifiers;
+ String name;
+ String simpleTypeName;
+ String defensiveCopyExpression;
+ String defaultValueExpression;
+ int getter_modifiers;
+ int setter_modifiers;
+ boolean is_read_only;
+ boolean is_bound;
+ boolean is_constrained;
+
+ variable_modifiers = modifiersThroughParentElem( propertyElem, "variable", Modifier.PRIVATE );
+ name = DomParseUtils.allTextFromUniqueChild( propertyElem, "name", true );
+ simpleTypeName = DomParseUtils.allTextFromUniqueChild( propertyElem, "type", true );
+ defensiveCopyExpression = DomParseUtils.allTextFromUniqueChild( propertyElem, "defensive-copy", true );
+ defaultValueExpression = DomParseUtils.allTextFromUniqueChild( propertyElem, "default-value", true );
+ getter_modifiers = modifiersThroughParentElem( propertyElem, "getter", Modifier.PUBLIC );
+ setter_modifiers = modifiersThroughParentElem( propertyElem, "setter", Modifier.PUBLIC );
+ Element readOnlyElem = DomParseUtils.uniqueChild( propertyElem, "read-only" );
+ is_read_only = (readOnlyElem != null);
+ Element isBoundElem = DomParseUtils.uniqueChild( propertyElem, "bound" );
+ is_bound = (isBoundElem != null);
+ Element isConstrainedElem = DomParseUtils.uniqueChild( propertyElem, "constrained" );
+ is_constrained = (isConstrainedElem != null);
+ out[i] = new SimpleProperty( variable_modifiers, name, simpleTypeName, defensiveCopyExpression,
+ defaultValueExpression, getter_modifiers, setter_modifiers,
+ is_read_only, is_bound, is_constrained );
+ }
+ return out;
+ }
+
+ private static int modifiersThroughParentElem( Element grandparentElem, String parentElemName, int default_mods )
+ {
+ Element parentElem = DomParseUtils.uniqueChild( grandparentElem, parentElemName );
+ if (parentElem != null )
+ {
+ Element modifiersElem = DomParseUtils.uniqueChild( parentElem, "modifiers" );
+ if (modifiersElem != null)
+ return parseModifiers( modifiersElem );
+ else
+ return default_mods;
+ }
+ else
+ return default_mods;
+ }
+
+ private static int parseModifiers( Element modifiersElem )
+ {
+ int out = 0;
+ String[] all_modifiers = DomParseUtils.allTextFromImmediateChildElements( modifiersElem, "modifier", true );
+ for ( int i = 0, len = all_modifiers.length; i < len; ++i)
+ {
+ String modifier = all_modifiers[i];
+ if ("public".equals( modifier )) out |= Modifier.PUBLIC;
+ else if ("protected".equals( modifier )) out |= Modifier.PROTECTED;
+ else if ("private".equals( modifier )) out |= Modifier.PRIVATE;
+ else if ("final".equals( modifier )) out |= Modifier.FINAL;
+ else if ("abstract".equals( modifier )) out |= Modifier.ABSTRACT;
+ else if ("static".equals( modifier )) out |= Modifier.STATIC;
+ else if ("synchronized".equals( modifier )) out |= Modifier.SYNCHRONIZED;
+ else if ("volatile".equals( modifier )) out |= Modifier.VOLATILE;
+ else if ("transient".equals( modifier )) out |= Modifier.TRANSIENT;
+ else if ("strictfp".equals( modifier )) out |= Modifier.STRICT;
+ else if ("native".equals( modifier )) out |= Modifier.NATIVE;
+ else if ("interface".equals( modifier )) out |= Modifier.INTERFACE; // ????
+ else throw new IllegalArgumentException("Bad modifier: " + modifier);
+ }
+ return out;
+ }
+
+
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/Property.java b/src/classes/com/mchange/v2/codegen/bean/Property.java
new file mode 100644
index 0000000..e8bd671
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/Property.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+public interface Property
+{
+ public int getVariableModifiers();
+ public String getName();
+ public String getSimpleTypeName();
+ public String getDefensiveCopyExpression();
+ public String getDefaultValueExpression();
+ public int getGetterModifiers();
+ public int getSetterModifiers();
+ public boolean isReadOnly();
+ public boolean isBound();
+ public boolean isConstrained();
+
+ //indexed properties not supported... use array or List valued props
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/PropertyBeanGenerator.java b/src/classes/com/mchange/v2/codegen/bean/PropertyBeanGenerator.java
new file mode 100644
index 0000000..a8e3747
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/PropertyBeanGenerator.java
@@ -0,0 +1,31 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.io.*;
+
+public interface PropertyBeanGenerator
+{
+ public void generate( ClassInfo info, Property[] props, Writer w ) throws IOException;
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/PropertyComparator.java b/src/classes/com/mchange/v2/codegen/bean/PropertyComparator.java
new file mode 100644
index 0000000..f8cb806
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/PropertyComparator.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+class PropertyComparator
+{
+ public int compare(Object a, Object b)
+ {
+ Property aa = (Property) a;
+ Property bb = (Property) b;
+
+ return (aa.getName().compareTo(bb.getName()));
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/PropertyMapConstructorGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/PropertyMapConstructorGeneratorExtension.java
new file mode 100644
index 0000000..6aa9e95
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/PropertyMapConstructorGeneratorExtension.java
@@ -0,0 +1,95 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class PropertyMapConstructorGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ {
+ Set set = new HashSet();
+ set.add("java.util.Map");
+ return set;
+ }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( ctor_modifiers ) );
+ iw.print(' ' + info.getClassName() + "( Map map )");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println( "Object raw;" );
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ Property prop = props[i];
+ String propName = prop.getName();
+ Class propType = propTypes[i];
+ iw.println("raw = map.get( \"" + propName + "\" );");
+ iw.println("if (raw != null)");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.print("this." + propName + " = ");
+ if ( propType == boolean.class )
+ iw.println( "((Boolean) raw ).booleanValue();" );
+ else if ( propType == byte.class )
+ iw.println( "((Byte) raw ).byteValue();" );
+ else if ( propType == char.class )
+ iw.println( "((Character) raw ).charValue();" );
+ else if ( propType == short.class )
+ iw.println( "((Short) raw ).shortValue();" );
+ else if ( propType == int.class )
+ iw.println( "((Integer) raw ).intValue();" );
+ else if ( propType == long.class )
+ iw.println( "((Long) raw ).longValue();" );
+ else if ( propType == float.class )
+ iw.println( "((Float) raw ).floatValue();" );
+ else if ( propType == double.class )
+ iw.println( "((Double) raw ).doubleValue();" );
+ iw.println("raw = null;");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/PropertyReferenceableExtension.java b/src/classes/com/mchange/v2/codegen/bean/PropertyReferenceableExtension.java
new file mode 100644
index 0000000..06479f3
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/PropertyReferenceableExtension.java
@@ -0,0 +1,112 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import com.mchange.v2.codegen.IndentedWriter;
+import com.mchange.v2.naming.JavaBeanObjectFactory;
+import com.mchange.v2.naming.JavaBeanReferenceMaker;
+
+import java.io.IOException;
+
+public class PropertyReferenceableExtension implements GeneratorExtension
+{
+ boolean explicit_reference_properties = false;
+
+ String factoryClassName = JavaBeanObjectFactory.class.getName();
+
+ String javaBeanReferenceMakerClassName = JavaBeanReferenceMaker.class.getName();
+
+ public void setUseExplicitReferenceProperties( boolean explicit_reference_properties )
+ { this.explicit_reference_properties = explicit_reference_properties; }
+
+ public boolean getUseExplicitReferenceProperties()
+ { return explicit_reference_properties; }
+
+ public void setFactoryClassName( String factoryClassName )
+ { this.factoryClassName = factoryClassName; }
+
+ public String getFactoryClassName()
+ { return factoryClassName; }
+
+// public void setJavaBeanReferenceMakerClassName( String javaBeanReferenceMakerClassName )
+// { this.javaBeanReferenceMakerClassName = javaBeanReferenceMakerClassName; }
+
+// public String getJavaBeanReferenceMakerClassName()
+// { return javaBeanReferenceMakerClassName; }
+
+ public Collection extraGeneralImports()
+ {
+ Set set = new HashSet();
+ return set;
+ }
+
+ public Collection extraSpecificImports()
+ {
+ Set set = new HashSet();
+ set.add( "javax.naming.Reference" );
+ set.add( "javax.naming.Referenceable" );
+ set.add( "javax.naming.NamingException" );
+ set.add( "com.mchange.v2.naming.JavaBeanObjectFactory" );
+ set.add( "com.mchange.v2.naming.JavaBeanReferenceMaker" );
+ set.add( "com.mchange.v2.naming.ReferenceMaker" );
+ return set;
+ }
+
+ public Collection extraInterfaceNames()
+ {
+ Set set = new HashSet();
+ set.add( "Referenceable" );
+ return set;
+ }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.println("final static JavaBeanReferenceMaker referenceMaker = new " + javaBeanReferenceMakerClassName + "();");
+ iw.println();
+ iw.println("static");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("referenceMaker.setFactoryClassName( \"" + factoryClassName + "\" );");
+ if ( explicit_reference_properties )
+ {
+ for( int i = 0, len = props.length; i < len; ++i)
+ iw.println("referenceMaker.addReferenceProperty(\"" + props[i].getName() + "\");");
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("public Reference getReference() throws NamingException");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("return referenceMaker.createReference( this );");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/PropsToStringGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/PropsToStringGeneratorExtension.java
new file mode 100644
index 0000000..293f4e0
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/PropsToStringGeneratorExtension.java
@@ -0,0 +1,89 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+
+import java.io.IOException;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class PropsToStringGeneratorExtension implements GeneratorExtension
+{
+ private Collection excludePropNames = null;
+
+ public void setExcludePropertyNames( Collection excludePropNames )
+ { this.excludePropNames = excludePropNames; }
+
+ public Collection getExcludePropertyNames()
+ { return excludePropNames; }
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.println("public String toString()");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("StringBuffer sb = new StringBuffer();");
+ iw.println("sb.append( super.toString() );");
+ iw.println("sb.append(\" [ \");");
+
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ Property prop = props[i];
+
+ if ( excludePropNames != null && excludePropNames.contains( prop.getName() ) )
+ continue;
+
+ iw.println("sb.append( \"" + prop.getName() + " -> \"" + " + " + prop.getName() + " );");
+ if ( i != len - 1 )
+ iw.println("sb.append( \", \");");
+ }
+
+ iw.println();
+ iw.println("String extraToStringInfo = this.extraToStringInfo();");
+ iw.println("if (extraToStringInfo != null)");
+ iw.upIndent();
+ iw.println("sb.append( extraToStringInfo );");
+ iw.downIndent();
+
+
+ iw.println("sb.append(\" ]\");");
+ iw.println("return sb.toString();");
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+ iw.println("protected String extraToStringInfo()");
+ iw.println("{ return null; }");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/ResolvedClassInfo.java b/src/classes/com/mchange/v2/codegen/bean/ResolvedClassInfo.java
new file mode 100644
index 0000000..641872b
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ResolvedClassInfo.java
@@ -0,0 +1,30 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+public interface ResolvedClassInfo extends ClassInfo
+{
+ public Class[] getInterfaces();
+ public Class[] getSuperclass();
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/ResolvedProperty.java b/src/classes/com/mchange/v2/codegen/bean/ResolvedProperty.java
new file mode 100644
index 0000000..c311fce
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/ResolvedProperty.java
@@ -0,0 +1,29 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+public interface ResolvedProperty extends Property
+{
+ public Class getType();
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SerializableExtension.java b/src/classes/com/mchange/v2/codegen/bean/SerializableExtension.java
new file mode 100644
index 0000000..b15087d
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SerializableExtension.java
@@ -0,0 +1,201 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.io.IOException;
+import com.mchange.v2.codegen.IndentedWriter;
+
+
+/**
+ * Note: this class pays no attention to whether users have marked any property variables as transient.
+ * In fact, it will work most efficiently if users mark ALL variables as transient... to define transient
+ * properties for this class, use the constructor which allows a user-specified set of transients.
+ */
+public class SerializableExtension implements GeneratorExtension
+{
+ Set transientProperties;
+ Map transientPropertyInitializers;
+
+ /**
+ * @param transientProperties a set of Strings, the names of all properties that should be considered transient and not serialized
+ * @param transientPropertyInitializers an optional Map of a subset of the transient property names to non-default initialization
+ * expressions, which should be unterminated expressions, and which will be used verbatim in
+ * the generated code.
+ */
+ public SerializableExtension(Set transientProperties, Map transientPropertyInitializers)
+ {
+ this.transientProperties = transientProperties;
+ this.transientPropertyInitializers = transientPropertyInitializers;
+ }
+
+ public SerializableExtension()
+ { this ( Collections.EMPTY_SET, null ); }
+
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ {
+ Set set = new HashSet();
+ set.add( "java.io.IOException" );
+ set.add( "java.io.Serializable" );
+ set.add( "java.io.ObjectOutputStream" );
+ set.add( "java.io.ObjectInputStream" );
+ return set;
+ }
+
+ public Collection extraInterfaceNames()
+ {
+ Set set = new HashSet();
+ set.add( "Serializable" );
+ return set;
+ }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ iw.println("private static final long serialVersionUID = 1;");
+ iw.println("private static final short VERSION = 0x0001;");
+ iw.println();
+ iw.println("private void writeObject( ObjectOutputStream oos ) throws IOException");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println( "oos.writeShort( VERSION );" );
+
+ for( int i = 0, len = props.length; i < len; ++i )
+ {
+ Property prop = props[i];
+ if (! transientProperties.contains( prop.getName() ) )
+ {
+ Class propType = propTypes[i];
+ if (propType != null && propType.isPrimitive()) //primitives should always resolve, object types may not, and be null
+ {
+ if (propType == byte.class)
+ iw.println("oos.writeByte(" + prop.getName() + ");");
+ else if (propType == char.class)
+ iw.println("oos.writeChar(" + prop.getName() + ");");
+ else if (propType == short.class)
+ iw.println("oos.writeShort(" + prop.getName() + ");");
+ else if (propType == int.class)
+ iw.println("oos.writeInt(" + prop.getName() + ");");
+ else if (propType == boolean.class)
+ iw.println("oos.writeBoolean(" + prop.getName() + ");");
+ else if (propType == long.class)
+ iw.println("oos.writeLong(" + prop.getName() + ");");
+ else if (propType == float.class)
+ iw.println("oos.writeFloat(" + prop.getName() + ");");
+ else if (propType == double.class)
+ iw.println("oos.writeDouble(" + prop.getName() + ");");
+ }
+ else
+ writeStoreObject( prop, propType, iw );
+ }
+ }
+ generateExtraSerWriteStatements( info, superclassType, props, propTypes, iw);
+ iw.downIndent();
+ iw.println("}");
+ iw.println();
+
+ iw.println("private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException");
+ iw.println("{");
+ iw.upIndent();
+ iw.println("short version = ois.readShort();");
+ iw.println("switch (version)");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("case VERSION:");
+ iw.upIndent();
+ for( int i = 0, len = props.length; i < len; ++i )
+ {
+ Property prop = props[i];
+ if (! transientProperties.contains( prop.getName() ) )
+ {
+ Class propType = propTypes[i];
+ if (propType != null && propType.isPrimitive()) //if a propType is unresolvable, it ain't a primitive
+ {
+ if (propType == byte.class)
+ iw.println("this." + prop.getName() + " = ois.readByte();");
+ else if (propType == char.class)
+ iw.println("this." + prop.getName() + " = ois.readChar();");
+ else if (propType == short.class)
+ iw.println("this." + prop.getName() + " = ois.readShort();");
+ else if (propType == int.class)
+ iw.println("this." + prop.getName() + " = ois.readInt();");
+ else if (propType == boolean.class)
+ iw.println("this." + prop.getName() + " = ois.readBoolean();");
+ else if (propType == long.class)
+ iw.println("this." + prop.getName() + " = ois.readLong();");
+ else if (propType == float.class)
+ iw.println("this." + prop.getName() + " = ois.readFloat();");
+ else if (propType == double.class)
+ iw.println("this." + prop.getName() + " = ois.readDouble();");
+ }
+ else
+ writeUnstoreObject( prop, propType, iw );
+ }
+ else
+ {
+ String initializer = (String) transientPropertyInitializers.get( prop.getName() );
+ if (initializer != null)
+ iw.println("this." + prop.getName() + " = " + initializer +';');
+ }
+ }
+ generateExtraSerInitializers( info, superclassType, props, propTypes, iw);
+ iw.println("break;");
+ iw.downIndent();
+ iw.println("default:");
+ iw.upIndent();
+ iw.println("throw new IOException(\"Unsupported Serialized Version: \" + version);");
+ iw.downIndent();
+
+ iw.downIndent();
+ iw.println("}");
+
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ protected void writeStoreObject( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {
+ iw.println("oos.writeObject( " + prop.getName() + " );");
+ }
+
+ protected void writeUnstoreObject( Property prop, Class propType, IndentedWriter iw ) throws IOException
+ {
+ iw.println("this." + prop.getName() + " = (" + prop.getSimpleTypeName() + ") ois.readObject();");
+ }
+
+ protected void generateExtraSerWriteStatements(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {}
+
+ protected void generateExtraSerInitializers(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {}
+
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SimpleClassInfo.java b/src/classes/com/mchange/v2/codegen/bean/SimpleClassInfo.java
new file mode 100644
index 0000000..03be360
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SimpleClassInfo.java
@@ -0,0 +1,62 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.lang.reflect.Modifier;
+
+public class SimpleClassInfo implements ClassInfo
+{
+ String packageName;
+ int modifiers;
+ String className;
+ String superclassName;
+ String[] interfaceNames;
+ String[] generalImports;
+ String[] specificImports;
+
+ public String getPackageName() { return packageName; }
+ public int getModifiers() { return modifiers; }
+ public String getClassName() { return className; }
+ public String getSuperclassName() { return superclassName; }
+ public String[] getInterfaceNames() { return interfaceNames; }
+ public String[] getGeneralImports() { return generalImports; }
+ public String[] getSpecificImports() { return specificImports; }
+
+ public SimpleClassInfo( String packageName,
+ int modifiers,
+ String className,
+ String superclassName,
+ String[] interfaceNames,
+ String[] generalImports,
+ String[] specificImports )
+ {
+ this.packageName = packageName;
+ this.modifiers = modifiers;
+ this.className = className;
+ this.superclassName = superclassName;
+ this.interfaceNames = interfaceNames;
+ this.generalImports = generalImports;
+ this.specificImports = specificImports;
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SimpleProperty.java b/src/classes/com/mchange/v2/codegen/bean/SimpleProperty.java
new file mode 100644
index 0000000..e15a108
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SimpleProperty.java
@@ -0,0 +1,94 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.lang.reflect.Modifier;
+
+public class SimpleProperty implements Property
+{
+ int variable_modifiers;
+ String name;
+ String simpleTypeName;
+ String defensiveCopyExpression;
+ String defaultValueExpression;
+ int getter_modifiers;
+ int setter_modifiers;
+ boolean is_read_only;
+ boolean is_bound;
+ boolean is_constrained;
+
+ public int getVariableModifiers() { return variable_modifiers; }
+ public String getName() { return name; }
+ public String getSimpleTypeName() { return simpleTypeName; }
+ public String getDefensiveCopyExpression() { return defensiveCopyExpression; }
+ public String getDefaultValueExpression() { return defaultValueExpression; }
+ public int getGetterModifiers() { return getter_modifiers; }
+ public int getSetterModifiers() { return setter_modifiers; }
+ public boolean isReadOnly() { return is_read_only; }
+ public boolean isBound() { return is_bound; }
+ public boolean isConstrained() { return is_constrained; }
+
+ public SimpleProperty( int variable_modifiers,
+ String name,
+ String simpleTypeName,
+ String defensiveCopyExpression,
+ String defaultValueExpression,
+ int getter_modifiers,
+ int setter_modifiers,
+ boolean is_read_only,
+ boolean is_bound,
+ boolean is_constrained )
+ {
+ this.variable_modifiers = variable_modifiers;
+ this.name = name;
+ this.simpleTypeName = simpleTypeName;
+ this.defensiveCopyExpression = defensiveCopyExpression;
+ this.defaultValueExpression = defaultValueExpression;
+ this.getter_modifiers = getter_modifiers;
+ this.setter_modifiers = setter_modifiers;
+ this.is_read_only = is_read_only;
+ this.is_bound = is_bound;
+ this.is_constrained = is_constrained;
+ }
+
+ public SimpleProperty( String name,
+ String simpleTypeName,
+ String defensiveCopyExpression,
+ String defaultValueExpression,
+ boolean is_read_only,
+ boolean is_bound,
+ boolean is_constrained )
+ {
+ this ( Modifier.PRIVATE,
+ name,
+ simpleTypeName,
+ defensiveCopyExpression,
+ defaultValueExpression,
+ Modifier.PUBLIC,
+ Modifier.PUBLIC,
+ is_read_only,
+ is_bound,
+ is_constrained );
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SimplePropertyBeanGenerator.java b/src/classes/com/mchange/v2/codegen/bean/SimplePropertyBeanGenerator.java
new file mode 100644
index 0000000..ed3d84e
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SimplePropertyBeanGenerator.java
@@ -0,0 +1,610 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.io.*;
+import java.util.*;
+import com.mchange.v2.log.*;
+import java.lang.reflect.Modifier;
+import com.mchange.v1.lang.ClassUtils;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class SimplePropertyBeanGenerator implements PropertyBeanGenerator
+{
+ private final static MLogger logger = MLog.getLogger( SimplePropertyBeanGenerator.class );
+
+ private boolean inner = false;
+ private int java_version = 130; //1.3.0
+ private boolean force_unmodifiable = false;
+ private String generatorName = this.getClass().getName();
+
+ // helper vars for generate method
+ protected ClassInfo info;
+ protected Property[] props;
+ protected IndentedWriter iw;
+
+ protected Set generalImports;
+ protected Set specificImports;
+ protected Set interfaceNames;
+
+ protected Class superclassType;
+ protected List interfaceTypes;
+ protected Class[] propertyTypes;
+
+ protected List generatorExtensions = new ArrayList();
+
+ public synchronized void setInner( boolean inner )
+ { this.inner = inner; }
+
+ public synchronized boolean isInner()
+ { return inner; }
+
+ /**
+ * @param version a three digit number -- for example Java 1.3.1 is 131
+ */
+ public synchronized void setJavaVersion(int java_version)
+ { this.java_version = java_version; }
+
+ public synchronized int getJavaVersion()
+ { return java_version; }
+
+ public synchronized void setGeneratorName(String generatorName)
+ { this.generatorName = generatorName; }
+
+ public synchronized String getGeneratorName()
+ { return generatorName; }
+
+ public synchronized void setForceUnmodifiable(boolean force_unmodifiable)
+ { this.force_unmodifiable = force_unmodifiable; }
+
+ public synchronized boolean isForceUnmodifiable()
+ { return force_unmodifiable; }
+
+ public synchronized void addExtension( GeneratorExtension ext )
+ { generatorExtensions.add( ext ); }
+
+ public synchronized void removeExtension( GeneratorExtension ext )
+ { generatorExtensions.remove( ext ); }
+
+ public synchronized void generate( ClassInfo info, Property[] props, Writer w) throws IOException
+ {
+ this.info = info;
+ this.props = props;
+ Arrays.sort( props, BeangenUtils.PROPERTY_COMPARATOR );
+ this.iw = ( w instanceof IndentedWriter ? (IndentedWriter) w : new IndentedWriter(w));
+
+ this.generalImports = new TreeSet();
+ if ( info.getGeneralImports() != null )
+ generalImports.addAll( Arrays.asList( info.getGeneralImports() ) );
+
+ this.specificImports = new TreeSet();
+ if ( info.getSpecificImports() != null )
+ specificImports.addAll( Arrays.asList( info.getSpecificImports() ) );
+
+ this.interfaceNames = new TreeSet();
+ if ( info.getInterfaceNames() != null )
+ interfaceNames.addAll( Arrays.asList( info.getInterfaceNames() ) );
+
+ addInternalImports();
+ addInternalInterfaces();
+
+ resolveTypes();
+
+ if (! inner )
+ {
+ writeHeader();
+ iw.println();
+ }
+
+ writeClassDeclaration();
+ iw.println('{');
+ iw.upIndent();
+
+ writeCoreBody();
+
+ iw.downIndent();
+ iw.println('}');
+ }
+
+ protected void resolveTypes()
+ {
+ String[] gen = (String[]) generalImports.toArray( new String[ generalImports.size() ] );
+ String[] spc = (String[]) specificImports.toArray( new String[ specificImports.size() ] );
+
+ if ( info.getSuperclassName() != null )
+ {
+ try
+ { superclassType = ClassUtils.forName( info.getSuperclassName(), gen, spc ); }
+ catch ( Exception e )
+ {
+// System.err.println("WARNING: " + this.getClass().getName() + " could not resolve " +
+// "superclass '" + info.getSuperclassName() + "'.");
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning(this.getClass().getName() + " could not resolve superclass '" + info.getSuperclassName() + "'.");
+
+ superclassType = null;
+ }
+ }
+
+ interfaceTypes = new ArrayList( interfaceNames.size() );
+ for ( Iterator ii = interfaceNames.iterator(); ii.hasNext(); )
+ {
+ String name = (String) ii.next();
+ try
+ { interfaceTypes.add( ClassUtils.forName( name , gen, spc ) ); }
+ catch ( Exception e )
+ {
+// System.err.println("WARNING: " + this.getClass().getName() + " could not resolve " +
+// "interface '" + name + "'.");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning(this.getClass().getName() + " could not resolve interface '" + name + "'.");
+
+ interfaceTypes.add( null );
+ }
+ }
+
+ propertyTypes = new Class[ props.length ];
+ for ( int i = 0, len = props.length; i < len; ++i )
+ {
+ String name = props[i].getSimpleTypeName();
+ try
+ { propertyTypes[i] = ClassUtils.forName( name , gen, spc ); }
+ catch ( Exception e )
+ {
+// e.printStackTrace();
+// System.err.println("WARNING: " + this.getClass().getName() + " could not resolve " +
+// "property type '" + name + "'.");
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, this.getClass().getName() + " could not resolve property type '" + name + "'.", e);
+
+ propertyTypes[i] = null;
+ }
+ }
+ }
+
+ protected void addInternalImports()
+ {
+ if (boundProperties())
+ {
+ specificImports.add("java.beans.PropertyChangeEvent");
+ specificImports.add("java.beans.PropertyChangeSupport");
+ specificImports.add("java.beans.PropertyChangeListener");
+ }
+ if (constrainedProperties())
+ {
+ specificImports.add("java.beans.PropertyChangeEvent");
+ specificImports.add("java.beans.PropertyVetoException");
+ specificImports.add("java.beans.VetoableChangeSupport");
+ specificImports.add("java.beans.VetoableChangeListener");
+ }
+
+ for (Iterator ii = generatorExtensions.iterator(); ii.hasNext(); )
+ {
+ GeneratorExtension ge = (GeneratorExtension) ii.next();
+ specificImports.addAll( ge.extraSpecificImports() );
+ generalImports.addAll( ge.extraGeneralImports() );
+ }
+ }
+
+ protected void addInternalInterfaces()
+ {
+ for (Iterator ii = generatorExtensions.iterator(); ii.hasNext(); )
+ {
+ GeneratorExtension ge = (GeneratorExtension) ii.next();
+ interfaceNames.addAll( ge.extraInterfaceNames() );
+ }
+ }
+
+ protected void writeCoreBody() throws IOException
+ {
+ writeJavaBeansChangeSupport();
+ writePropertyVariables();
+ writeOtherVariables();
+ iw.println();
+
+ writeGetterSetterPairs();
+ if ( boundProperties() )
+ {
+ iw.println();
+ writeBoundPropertyEventSourceMethods();
+ }
+ if ( constrainedProperties() )
+ {
+ iw.println();
+ writeConstrainedPropertyEventSourceMethods();
+ }
+ writeInternalUtilityFunctions();
+ writeOtherFunctions();
+
+ writeOtherClasses();
+
+ String[] completed_intfc_names = (String[]) interfaceNames.toArray( new String[ interfaceNames.size() ] );
+ String[] completed_gen_imports = (String[]) generalImports.toArray( new String[ generalImports.size() ] );
+ String[] completed_spc_imports = (String[]) specificImports.toArray( new String[ specificImports.size() ] );
+ ClassInfo completedClassInfo = new SimpleClassInfo( info.getPackageName(),
+ info.getModifiers(),
+ info.getClassName(),
+ info.getSuperclassName(),
+ completed_intfc_names,
+ completed_gen_imports,
+ completed_spc_imports );
+ for (Iterator ii = generatorExtensions.iterator(); ii.hasNext(); )
+ {
+ GeneratorExtension ext = (GeneratorExtension) ii.next();
+ iw.println();
+ ext.generate( completedClassInfo, superclassType, props, propertyTypes, iw );
+ }
+ }
+
+ protected void writeInternalUtilityFunctions() throws IOException
+ {
+ iw.println("private boolean eqOrBothNull( Object a, Object b )");
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("return");
+ iw.upIndent();
+ iw.println("a == b ||");
+ iw.println("(a != null && a.equals(b));");
+ iw.downIndent();
+
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ protected void writeConstrainedPropertyEventSourceMethods() throws IOException
+ {
+ iw.println("public void addVetoableChangeListener( VetoableChangeListener vcl )");
+ iw.println("{ vcs.addVetoableChangeListener( vcl ); }");
+ iw.println();
+
+ iw.println("public void removeVetoableChangeListener( VetoableChangeListener vcl )");
+ iw.println("{ vcs.removeVetoableChangeListener( vcl ); }");
+ iw.println();
+
+ if (java_version >= 140)
+ {
+ iw.println("public VetoableChangeListener[] getVetoableChangeListeners()");
+ iw.println("{ return vcs.getPropertyChangeListeners(); }");
+ }
+ }
+
+ protected void writeBoundPropertyEventSourceMethods() throws IOException
+ {
+ iw.println("public void addPropertyChangeListener( PropertyChangeListener pcl )");
+ iw.println("{ pcs.addPropertyChangeListener( pcl ); }");
+ iw.println();
+
+ iw.println("public void addPropertyChangeListener( String propName, PropertyChangeListener pcl )");
+ iw.println("{ pcs.addPropertyChangeListener( propName, pcl ); }");
+ iw.println();
+
+ iw.println("public void removePropertyChangeListener( PropertyChangeListener pcl )");
+ iw.println("{ pcs.removePropertyChangeListener( pcl ); }");
+ iw.println();
+
+ iw.println("public void removePropertyChangeListener( String propName, PropertyChangeListener pcl )");
+ iw.println("{ pcs.removePropertyChangeListener( propName, pcl ); }");
+ iw.println();
+
+ if (java_version >= 140)
+ {
+ iw.println("public PropertyChangeListener[] getPropertyChangeListeners()");
+ iw.println("{ return pcs.getPropertyChangeListeners(); }");
+ }
+ }
+
+ protected void writeJavaBeansChangeSupport() throws IOException
+ {
+ if ( boundProperties() )
+ {
+ iw.println("protected PropertyChangeSupport pcs = new PropertyChangeSupport( this );");
+ iw.println();
+ iw.println("protected PropertyChangeSupport getPropertyChangeSupport()");
+ iw.println("{ return pcs; }");
+
+ }
+ if ( constrainedProperties() )
+ {
+ iw.println("protected VetoableChangeSupport vcs = new VetoableChangeSupport( this );");
+ iw.println();
+ iw.println("protected VetoableChangeSupport getVetoableChangeSupport()");
+ iw.println("{ return vcs; }");
+ }
+ }
+
+ protected void writeOtherVariables() throws IOException //hook method for subclasses
+ {}
+
+ protected void writeOtherFunctions() throws IOException //hook method for subclasses
+ {}
+
+ protected void writeOtherClasses() throws IOException //hook method for subclasses
+ {}
+
+ protected void writePropertyVariables() throws IOException
+ {
+ for (int i = 0, len = props.length; i < len; ++i)
+ writePropertyVariable( props[i] );
+ }
+
+ protected void writePropertyVariable( Property prop ) throws IOException
+ {
+ BeangenUtils.writePropertyVariable( prop, iw );
+// iw.print( CodegenUtils.getModifierString( prop.getVariableModifiers() ) );
+// iw.print( ' ' + prop.getSimpleTypeName() + ' ' + prop.getName());
+// String dflt = prop.getDefaultValueExpression();
+// if (dflt != null)
+// iw.print( " = " + dflt );
+// iw.println(';');
+ }
+
+ /**
+ * @deprecated
+ */
+ protected void writePropertyMembers() throws IOException
+ { throw new InternalError("writePropertyMembers() deprecated and removed. please us writePropertyVariables()."); }
+
+ /**
+ * @deprecated
+ */
+ protected void writePropertyMember( Property prop ) throws IOException
+ { throw new InternalError("writePropertyMember() deprecated and removed. please us writePropertyVariable()."); }
+
+ protected void writeGetterSetterPairs() throws IOException
+ {
+ for (int i = 0, len = props.length; i < len; ++i)
+ {
+ writeGetterSetterPair( props[i], propertyTypes[i] );
+ if ( i != len - 1) iw.println();
+ }
+ }
+
+ protected void writeGetterSetterPair( Property prop, Class propType ) throws IOException
+ {
+ writePropertyGetter( prop, propType );
+
+ if (! prop.isReadOnly() && ! force_unmodifiable)
+ {
+ iw.println();
+ writePropertySetter( prop, propType );
+ }
+ }
+
+ protected void writePropertyGetter( Property prop, Class propType ) throws IOException
+ {
+ BeangenUtils.writePropertyGetter( prop, this.getGetterDefensiveCopyExpression( prop, propType ), iw );
+
+// String pfx = ("boolean".equals( prop.getSimpleTypeName() ) ? "is" : "get" );
+// iw.print( CodegenUtils.getModifierString( prop.getGetterModifiers() ) );
+// iw.println(' ' + prop.getSimpleTypeName() + ' ' + pfx + BeangenUtils.capitalize( prop.getName() ) + "()");
+// String retVal = getGetterDefensiveCopyExpression( prop, propType );
+// if (retVal == null) retVal = prop.getName();
+// iw.println("{ return " + retVal + "; }");
+ }
+
+
+// boolean changeMarked( Property prop )
+// { return prop.isBound() || prop.isConstrained(); }
+
+ protected void writePropertySetter( Property prop, Class propType ) throws IOException
+ {
+ BeangenUtils.writePropertySetter( prop, this.getSetterDefensiveCopyExpression( prop, propType ), iw );
+
+// iw.print( CodegenUtils.getModifierString( prop.getSetterModifiers() ) );
+// iw.print(" void set" + BeangenUtils.capitalize( prop.getName() ) + "( " + prop.getSimpleTypeName() + ' ' + prop.getName() + " )");
+// if ( prop.isConstrained() )
+// iw.println(" throws PropertyVetoException");
+// else
+// iw.println();
+// String setVal = getSetterDefensiveCopyExpression( prop, propType );
+// if (setVal == null) setVal = prop.getName();
+// iw.println('{');
+// iw.upIndent();
+
+
+// if ( changeMarked( prop ) )
+// {
+// iw.println( prop.getSimpleTypeName() + " oldVal = this." + prop.getName() + ';');
+
+// String oldValExpr = "oldVal";
+// String newValExpr = prop.getName();
+// String changeCheck;
+// if ( propType != null && propType.isPrimitive() ) //sometimes the type can't be resolved. if so, it ain't primitive.
+// {
+// // PropertyChange support already has overrides
+// // for boolean and int
+// if (propType == byte.class)
+// {
+// oldValExpr = "new Byte( "+ oldValExpr +" )";
+// newValExpr = "new Byte( "+ newValExpr +" )";
+// }
+// else if (propType == char.class)
+// {
+// oldValExpr = "new Character( "+ oldValExpr +" )";
+// newValExpr = "new Character( "+ newValExpr +" )";
+// }
+// else if (propType == short.class)
+// {
+// oldValExpr = "new Short( "+ oldValExpr +" )";
+// newValExpr = "new Short( "+ newValExpr +" )";
+// }
+// else if (propType == float.class)
+// {
+// oldValExpr = "new Float( "+ oldValExpr +" )";
+// newValExpr = "new Float( "+ newValExpr +" )";
+// }
+// else if (propType == double.class)
+// {
+// oldValExpr = "new Double( "+ oldValExpr +" )";
+// newValExpr = "new Double( "+ newValExpr +" )";
+// }
+
+// changeCheck = "oldVal != " + prop.getName();
+// }
+// else
+// changeCheck = "! eqOrBothNull( oldVal, " + prop.getName() + " )";
+
+// if ( prop.isConstrained() )
+// {
+// iw.println("if ( " + changeCheck + " )");
+// iw.upIndent();
+// iw.println("vcs.fireVetoableChange( \"" + prop.getName() + "\", " + oldValExpr + ", " + newValExpr + " );");
+// iw.downIndent();
+// }
+
+// iw.println("this." + prop.getName() + " = " + setVal + ';');
+
+// if ( prop.isBound() )
+// {
+// iw.println("if ( " + changeCheck + " )");
+// iw.upIndent();
+// iw.println("pcs.firePropertyChange( \"" + prop.getName() + "\", " + oldValExpr + ", " + newValExpr + " );");
+// iw.downIndent();
+// }
+// }
+// else
+// iw.println("this." + prop.getName() + " = " + setVal + ';');
+
+// iw.downIndent();
+// iw.println('}');
+ }
+
+ protected String getGetterDefensiveCopyExpression( Property prop, Class propType )
+ { return prop.getDefensiveCopyExpression(); }
+
+ protected String getSetterDefensiveCopyExpression( Property prop, Class propType )
+ { return prop.getDefensiveCopyExpression(); }
+
+ protected String getConstructorDefensiveCopyExpression( Property prop, Class propType )
+ { return prop.getDefensiveCopyExpression(); }
+
+ protected void writeHeader() throws IOException
+ {
+ writeBannerComments();
+ iw.println();
+ iw.println("package " + info.getPackageName() + ';');
+ iw.println();
+ writeImports();
+ }
+
+ protected void writeBannerComments() throws IOException
+ {
+ iw.println("/*");
+ iw.println(" * This class autogenerated by " + generatorName + '.');
+ iw.println(" * DO NOT HAND EDIT!");
+ iw.println(" */");
+ }
+
+ protected void writeImports() throws IOException
+ {
+ for ( Iterator ii = generalImports.iterator(); ii.hasNext(); )
+ iw.println("import " + ii.next() + ".*;");
+ for ( Iterator ii = specificImports.iterator(); ii.hasNext(); )
+ iw.println("import " + ii.next() + ";");
+ }
+
+ protected void writeClassDeclaration() throws IOException
+ {
+ iw.print( CodegenUtils.getModifierString( info.getModifiers() ) + " class " + info.getClassName() );
+ String superclassName = info.getSuperclassName();
+ if (superclassName != null)
+ iw.print( " extends " + superclassName );
+ if (interfaceNames.size() > 0)
+ {
+ iw.print(" implements ");
+ boolean first = true;
+ for (Iterator ii = interfaceNames.iterator(); ii.hasNext(); )
+ {
+ if (first)
+ first = false;
+ else
+ iw.print(", ");
+
+ iw.print( (String) ii.next() );
+ }
+ }
+ iw.println();
+ }
+
+ boolean boundProperties()
+ { return BeangenUtils.hasBoundProperties( props ); }
+
+ boolean constrainedProperties()
+ { return BeangenUtils.hasConstrainedProperties( props ); }
+
+ public static void main( String[] argv )
+ {
+ try
+ {
+ ClassInfo info = new SimpleClassInfo("test",
+ Modifier.PUBLIC,
+ argv[0],
+ null,
+ null,
+ new String[] {"java.awt"},
+ null);
+
+ Property[] props =
+ {
+ new SimpleProperty( "number",
+ "int",
+ null,
+ "7",
+ false,
+ true,
+ false
+ ),
+ new SimpleProperty( "fpNumber",
+ "float",
+ null,
+ null,
+ true,
+ true,
+ false
+ ),
+ new SimpleProperty( "location",
+ "Point",
+ "new Point( location.x, location.y )",
+ "new Point( 0, 0 )",
+ false,
+ true,
+ true
+ )
+ };
+
+ FileWriter fw = new FileWriter( argv[0] + ".java" );
+ SimplePropertyBeanGenerator g = new SimplePropertyBeanGenerator();
+ g.addExtension( new SerializableExtension() );
+ g.generate(info, props, fw );
+ fw.flush();
+ fw.close();
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SimplePropertyMask.java b/src/classes/com/mchange/v2/codegen/bean/SimplePropertyMask.java
new file mode 100644
index 0000000..0626c8c
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SimplePropertyMask.java
@@ -0,0 +1,64 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.lang.reflect.Modifier;
+
+class SimplePropertyMask implements Property
+{
+ Property p;
+
+ SimplePropertyMask(Property p)
+ { this.p = p; }
+
+ public int getVariableModifiers()
+ { return Modifier.PRIVATE; }
+
+ public String getName()
+ { return p.getName(); }
+
+ public String getSimpleTypeName()
+ { return p.getSimpleTypeName(); }
+
+ public String getDefensiveCopyExpression()
+ { return null; }
+
+ public String getDefaultValueExpression()
+ { return p.getDefaultValueExpression(); }
+
+ public int getGetterModifiers()
+ { return Modifier.PUBLIC; }
+
+ public int getSetterModifiers()
+ { return Modifier.PUBLIC; }
+
+ public boolean isReadOnly()
+ { return false; }
+
+ public boolean isBound()
+ { return false; }
+
+ public boolean isConstrained()
+ { return false; }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/SimpleStateBeanImportExportGeneratorExtension.java b/src/classes/com/mchange/v2/codegen/bean/SimpleStateBeanImportExportGeneratorExtension.java
new file mode 100644
index 0000000..ae1ccbb
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/SimpleStateBeanImportExportGeneratorExtension.java
@@ -0,0 +1,108 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.util.*;
+import java.lang.reflect.Modifier;
+import java.io.IOException;
+import com.mchange.v2.codegen.CodegenUtils;
+import com.mchange.v2.codegen.IndentedWriter;
+
+public class SimpleStateBeanImportExportGeneratorExtension implements GeneratorExtension
+{
+ int ctor_modifiers = Modifier.PUBLIC;
+
+ public Collection extraGeneralImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraSpecificImports()
+ { return Collections.EMPTY_SET; }
+
+ public Collection extraInterfaceNames()
+ { return Collections.EMPTY_SET; }
+
+ static class SimplePropertyMask implements Property
+ {
+ Property p;
+
+ SimplePropertyMask(Property p)
+ { this.p = p; }
+
+ public int getVariableModifiers()
+ { return Modifier.PRIVATE; }
+
+ public String getName()
+ { return p.getName(); }
+
+ public String getSimpleTypeName()
+ { return p.getSimpleTypeName(); }
+
+ public String getDefensiveCopyExpression()
+ { return null; }
+
+ public String getDefaultValueExpression()
+ { return null; }
+
+ public int getGetterModifiers()
+ { return Modifier.PUBLIC; }
+
+ public int getSetterModifiers()
+ { return Modifier.PUBLIC; }
+
+ public boolean isReadOnly()
+ { return false; }
+
+ public boolean isBound()
+ { return false; }
+
+ public boolean isConstrained()
+ { return false; }
+ }
+
+ public void generate(ClassInfo info, Class superclassType, Property[] props, Class[] propTypes, IndentedWriter iw)
+ throws IOException
+ {
+ int num_props = props.length;
+ Property[] masked = new Property[ num_props ];
+ for (int i = 0; i < num_props; ++i)
+ masked[i] = new SimplePropertyMask( props[i] );
+
+ iw.println("protected static class SimpleStateBean implements ExportedState");
+ iw.println("{");
+ iw.upIndent();
+
+ for (int i = 0; i < num_props; ++i)
+ {
+ masked[i] = new SimplePropertyMask( props[i] );
+ BeangenUtils.writePropertyMember( masked[i], iw );
+ iw.println();
+ BeangenUtils.writePropertyGetter( masked[i], iw );
+ iw.println();
+ BeangenUtils.writePropertySetter( masked[i], iw );
+ }
+
+ iw.downIndent();
+ iw.println("}");
+ }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/WrapperClassInfo.java b/src/classes/com/mchange/v2/codegen/bean/WrapperClassInfo.java
new file mode 100644
index 0000000..35caa40
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/WrapperClassInfo.java
@@ -0,0 +1,40 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+public abstract class WrapperClassInfo implements ClassInfo
+{
+ ClassInfo inner;
+
+ public WrapperClassInfo(ClassInfo info)
+ { this.inner = info; }
+
+ public String getPackageName() { return inner.getPackageName(); }
+ public int getModifiers() { return inner.getModifiers(); }
+ public String getClassName() { return inner.getClassName(); }
+ public String getSuperclassName() { return inner.getSuperclassName(); }
+ public String[] getInterfaceNames() { return inner.getInterfaceNames(); }
+ public String[] getGeneralImports() { return inner.getGeneralImports(); }
+ public String[] getSpecificImports() { return inner.getSpecificImports(); }
+}
diff --git a/src/classes/com/mchange/v2/codegen/bean/WrapperProperty.java b/src/classes/com/mchange/v2/codegen/bean/WrapperProperty.java
new file mode 100644
index 0000000..d752487
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/bean/WrapperProperty.java
@@ -0,0 +1,67 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.bean;
+
+import java.lang.reflect.Modifier;
+
+public abstract class WrapperProperty implements Property
+{
+ Property p;
+
+ public WrapperProperty(Property p)
+ { this.p = p; }
+
+ protected Property getInner()
+ { return p; }
+
+ public int getVariableModifiers()
+ { return p.getVariableModifiers(); }
+
+ public String getName()
+ { return p.getName(); }
+
+ public String getSimpleTypeName()
+ { return p.getSimpleTypeName(); }
+
+ public String getDefensiveCopyExpression()
+ { return p.getDefensiveCopyExpression(); }
+
+ public String getDefaultValueExpression()
+ { return p.getDefaultValueExpression(); }
+
+ public int getGetterModifiers()
+ { return p.getGetterModifiers(); }
+
+ public int getSetterModifiers()
+ { return p.getSetterModifiers(); }
+
+ public boolean isReadOnly()
+ { return p.isReadOnly(); }
+
+ public boolean isBound()
+ { return p.isBound(); }
+
+ public boolean isConstrained()
+ { return p.isConstrained(); }
+}
diff --git a/src/classes/com/mchange/v2/codegen/intfc/DelegatorGenerator.java b/src/classes/com/mchange/v2/codegen/intfc/DelegatorGenerator.java
new file mode 100644
index 0000000..8d1ed00
--- /dev/null
+++ b/src/classes/com/mchange/v2/codegen/intfc/DelegatorGenerator.java
@@ -0,0 +1,259 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.codegen.intfc;
+
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+import com.mchange.v2.codegen.*;
+import com.mchange.v1.lang.ClassUtils;
+
+public class DelegatorGenerator
+{
+ int class_modifiers = Modifier.PUBLIC | Modifier.ABSTRACT;
+ int method_modifiers = Modifier.PUBLIC;
+ int wrapping_ctor_modifiers = Modifier.PUBLIC;
+ int default_ctor_modifiers = Modifier.PUBLIC;
+ boolean wrapping_constructor = true;
+ boolean default_constructor = true;
+ boolean inner_getter = true;
+ boolean inner_setter = true;
+
+ Class superclass = null;
+ Class[] extraInterfaces = null;
+
+ final static Comparator classComp = new Comparator()
+ {
+ public int compare(Object a, Object b)
+ { return ((Class) a).getName().compareTo(((Class) b).getName()); }
+ };
+
+ public void setGenerateInnerSetter( boolean b )
+ { this.inner_setter = b; }
+
+ public boolean isGenerateInnerSetter()
+ { return inner_setter; }
+
+ public void setGenerateInnerGetter( boolean b )
+ { this.inner_getter = b; }
+
+ public boolean isGenerateInnerGetter()
+ { return inner_getter; }
+
+ public void setGenerateNoArgConstructor( boolean b )
+ { this.default_constructor = b; }
+
+ public boolean isGenerateNoArgConstructor()
+ { return default_constructor; }
+
+ public void setGenerateWrappingConstructor( boolean b )
+ { this.wrapping_constructor = b; }
+
+ public boolean isGenerateWrappingConstructor()
+ { return wrapping_constructor; }
+
+ public void setWrappingConstructorModifiers( int modifiers )
+ { this.wrapping_ctor_modifiers = modifiers; }
+
+ public int getWrappingConstructorModifiers()
+ { return wrapping_ctor_modifiers; }
+
+ public void setNoArgConstructorModifiers( int modifiers )
+ { this.default_ctor_modifiers = modifiers; }
+
+ public int getNoArgConstructorModifiers()
+ { return default_ctor_modifiers; }
+
+ public void setMethodModifiers( int modifiers )
+ { this.method_modifiers = modifiers; }
+
+ public int getMethodModifiers()
+ { return method_modifiers; }
+
+ public void setClassModifiers( int modifiers )
+ { this.class_modifiers = modifiers; }
+
+ public int getClassModifiers()
+ { return class_modifiers; }
+
+ public void setSuperclass( Class superclass )
+ { this.superclass = superclass; }
+
+ public Class getSuperclass()
+ { return superclass; }
+
+ public void setExtraInterfaces( Class[] extraInterfaces )
+ { this.extraInterfaces = extraInterfaces; }
+
+ public Class[] getExtraInterfaces()
+ { return extraInterfaces; }
+
+ public void writeDelegator(Class intfcl, String genclass, Writer w) throws IOException
+ {
+ IndentedWriter iw = CodegenUtils.toIndentedWriter(w);
+
+ String pkg = genclass.substring(0, genclass.lastIndexOf('.'));
+ String sgc = CodegenUtils.fqcnLastElement( genclass );
+ String scn = (superclass != null ? ClassUtils.simpleClassName( superclass ) : null);
+ String sin = ClassUtils.simpleClassName( intfcl );
+ String[] eins = null;
+ if (extraInterfaces != null)
+ {
+ eins = new String[ extraInterfaces.length ];
+ for (int i = 0, len = extraInterfaces.length; i < len; ++i)
+ eins[i] = ClassUtils.simpleClassName( extraInterfaces[i] );
+ }
+
+ Set imports = new TreeSet( classComp );
+
+ Method[] methods = intfcl.getMethods();
+
+ //TODO: don't add array classes!
+ //build import set
+ if (! CodegenUtils.inSamePackage( intfcl.getName(), genclass ) )
+ imports.add( intfcl );
+ if (superclass != null && ! CodegenUtils.inSamePackage( superclass.getName(), genclass ) )
+ imports.add( superclass );
+ if (extraInterfaces != null)
+ {
+ for (int i = 0, len = extraInterfaces.length; i < len; ++i)
+ {
+ Class checkMe = extraInterfaces[i];
+ if (! CodegenUtils.inSamePackage( checkMe.getName(), genclass ) )
+ imports.add( checkMe );
+ }
+ }
+ for (int i = 0, len = methods.length; i < len; ++i)
+ {
+ Class[] args = methods[i].getParameterTypes();
+ for (int j = 0, jlen = args.length; j < jlen; ++j)
+ {
+ if (! CodegenUtils.inSamePackage( args[j].getName(), genclass ) )
+ imports.add( CodegenUtils.unarrayClass( args[j] ) );
+ }
+ Class[] excClasses = methods[i].getExceptionTypes();
+ for (int j = 0, jlen = excClasses.length; j < jlen; ++j)
+ {
+ if (! CodegenUtils.inSamePackage( excClasses[j].getName(), genclass ) )
+ {
+ //System.err.println("Adding exception type: " + excClasses[j]);
+ imports.add( CodegenUtils.unarrayClass( excClasses[j] ) );
+ }
+ }
+ if (! CodegenUtils.inSamePackage( methods[i].getReturnType().getName(), genclass ) )
+ imports.add( CodegenUtils.unarrayClass( methods[i].getReturnType() ) );
+ }
+ generateBannerComment( iw );
+ iw.println("package " + pkg + ';');
+ iw.println();
+ for (Iterator ii = imports.iterator(); ii.hasNext(); )
+ iw.println("import "+ ((Class) ii.next()).getName() + ';');
+ generateExtraImports( iw );
+ iw.println();
+ iw.print(CodegenUtils.getModifierString( class_modifiers ) + " class " + sgc);
+ if (superclass != null)
+ iw.print(" extends " + scn);
+ iw.print(" implements " + sin);
+ if (eins != null)
+ for (int i = 0, len = eins.length; i < len; ++i)
+ iw.print(", " + eins[i]);
+ iw.println();
+ iw.println("{");
+ iw.upIndent();
+
+ iw.println("protected " + sin + " inner;");
+ iw.println();
+
+ if ( wrapping_constructor )
+ {
+ iw.println("public" + ' ' + sgc + '(' + sin + " inner)");
+ iw.println("{ this.inner = inner; }");
+ }
+
+ if (default_constructor)
+ {
+ iw.println();
+ iw.println("public" + ' ' + sgc + "()");
+ iw.println("{}");
+ }
+
+ if (inner_setter)
+ {
+ iw.println();
+ iw.println( CodegenUtils.getModifierString( method_modifiers ) + " void setInner( " + sin + " inner )");
+ iw.println( "{ this.inner = inner; }" );
+ }
+ if (inner_getter)
+ {
+ iw.println();
+ iw.println( CodegenUtils.getModifierString( method_modifiers ) + ' ' + sin + " getInner()");
+ iw.println( "{ return inner; }" );
+ }
+ iw.println();
+ for (int i = 0, len = methods.length; i < len; ++i)
+ {
+ Method method = methods[i];
+ Class retType = method.getReturnType();
+
+ if (i != 0) iw.println();
+ iw.println( CodegenUtils.methodSignature( method_modifiers, method, null ) );
+ iw.println("{");
+ iw.upIndent();
+
+ generatePreDelegateCode( intfcl, genclass, method, iw );
+ generateDelegateCode( intfcl, genclass, method, iw );
+ generatePostDelegateCode( intfcl, genclass, method, iw );
+
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ iw.println();
+ generateExtraDeclarations( intfcl, genclass, iw );
+
+ iw.downIndent();
+ iw.println("}");
+ }
+
+ protected void generateDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException
+ {
+ Class retType = method.getReturnType();
+
+ iw.println( (retType == void.class ? "" : "return " ) + "inner." + CodegenUtils.methodCall( method ) + ";" );
+ }
+
+ protected void generateBannerComment( IndentedWriter iw ) throws IOException
+ {
+ iw.println("/*");
+ iw.println(" * This class generated by " + this.getClass().getName());
+ iw.println(" * " + new Date());
+ iw.println(" * DO NOT HAND EDIT!!!!");
+ iw.println(" */");
+ }
+
+ protected void generateExtraImports( IndentedWriter iw ) throws IOException {}
+ protected void generatePreDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException {}
+ protected void generatePostDelegateCode( Class intfcl, String genclass, Method method, IndentedWriter iw ) throws IOException {}
+ protected void generateExtraDeclarations( Class intfcl, String genclass, IndentedWriter iw ) throws IOException {}
+}
diff --git a/src/classes/com/mchange/v2/debug/DebugConstants.java b/src/classes/com/mchange/v2/debug/DebugConstants.java
new file mode 100644
index 0000000..07cb5db
--- /dev/null
+++ b/src/classes/com/mchange/v2/debug/DebugConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.debug;
+
+public interface DebugConstants
+{
+ public final static int TRACE_NONE = 0;
+ public final static int TRACE_MED = 5;
+ public final static int TRACE_MAX = 10;
+}
diff --git a/src/classes/com/mchange/v2/debug/ThreadNameStackTraceRecorder.java b/src/classes/com/mchange/v2/debug/ThreadNameStackTraceRecorder.java
new file mode 100644
index 0000000..400080d
--- /dev/null
+++ b/src/classes/com/mchange/v2/debug/ThreadNameStackTraceRecorder.java
@@ -0,0 +1,135 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.debug;
+
+import java.text.*;
+import java.util.*;
+import com.mchange.lang.ThrowableUtils;
+
+public class ThreadNameStackTraceRecorder
+{
+ final static String NL = System.getProperty("line.separator", "\r\n");
+
+ Set set = new HashSet();
+
+ String dumpHeader;
+ String stackTraceHeader;
+
+ public ThreadNameStackTraceRecorder( String dumpHeader )
+ { this( dumpHeader, "Debug Stack Trace." ); }
+
+ public ThreadNameStackTraceRecorder( String dumpHeader, String stackTraceHeader )
+ {
+ this.dumpHeader = dumpHeader;
+ this.stackTraceHeader = stackTraceHeader;
+ }
+
+ public synchronized Object record()
+ {
+ Record r = new Record( stackTraceHeader );
+ set.add( r );
+ return r;
+ }
+
+ public synchronized void remove( Object rec )
+ { set.remove( rec ); }
+
+ public synchronized int size()
+ { return set.size(); }
+
+ public synchronized String getDump()
+ { return getDump(null); }
+
+ public synchronized String getDump(String locationSpecificNote)
+ {
+ DateFormat df = new SimpleDateFormat("dd-MMMM-yyyy HH:mm:ss.SSSS");
+
+ StringBuffer sb = new StringBuffer(2047);
+ sb.append(NL);
+ sb.append("----------------------------------------------------");
+ sb.append(NL);
+ sb.append( dumpHeader );
+ sb.append(NL);
+ if (locationSpecificNote != null)
+ {
+ sb.append( locationSpecificNote );
+ sb.append( NL );
+ }
+ boolean first = true;
+ for (Iterator ii = set.iterator(); ii.hasNext(); )
+ {
+ if (first)
+ first = false;
+ else
+ {
+ sb.append("---");
+ sb.append( NL );
+ }
+
+ Record r = (Record) ii.next();
+ sb.append(df.format( new Date( r.time ) ));
+ sb.append(" --> Thread Name: ");
+ sb.append(r.threadName);
+ sb.append(NL);
+ sb.append("Stack Trace: ");
+ sb.append( ThrowableUtils.extractStackTrace( r.stackTrace ) );
+ }
+ sb.append("----------------------------------------------------");
+ sb.append(NL);
+ return sb.toString();
+ }
+
+ private final static class Record implements Comparable
+ {
+ long time;
+ String threadName;
+ Throwable stackTrace;
+
+ Record(String sth)
+ {
+ this.time = System.currentTimeMillis();
+ this.threadName = Thread.currentThread().getName();
+ this.stackTrace = new Exception( sth );
+ }
+
+ public int compareTo( Object o )
+ {
+ Record oo = (Record) o;
+ if ( this.time > oo.time )
+ return 1;
+ else if (this.time < oo.time )
+ return -1;
+ else
+ {
+ int mine = System.identityHashCode( this );
+ int yours = System.identityHashCode( oo );
+ if (mine > yours)
+ return 1;
+ else if (mine < yours)
+ return -1;
+ return 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/encounter/AbstractEncounterCounter.java b/src/classes/com/mchange/v2/encounter/AbstractEncounterCounter.java
new file mode 100644
index 0000000..ec30817
--- /dev/null
+++ b/src/classes/com/mchange/v2/encounter/AbstractEncounterCounter.java
@@ -0,0 +1,57 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.encounter;
+
+import java.util.Map;
+
+class AbstractEncounterCounter implements EncounterCounter
+{
+ final static Long ONE = new Long(1);
+ Map m;
+
+ AbstractEncounterCounter(Map m)
+ { this.m = m; }
+
+ /**
+ * @return how many times have I seen this object before?
+ */
+ public long encounter(Object o)
+ {
+ Long oldLong = (Long) m.get(o);
+ Long newLong;
+ long out;
+ if (oldLong == null)
+ {
+ out = 0;
+ newLong = ONE;
+ }
+ else
+ {
+ out = oldLong.longValue();
+ newLong = new Long(out + 1);
+ }
+ m.put( o, newLong );
+ return out;
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/encounter/EncounterCounter.java b/src/classes/com/mchange/v2/encounter/EncounterCounter.java
new file mode 100644
index 0000000..3f91e88
--- /dev/null
+++ b/src/classes/com/mchange/v2/encounter/EncounterCounter.java
@@ -0,0 +1,32 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.encounter;
+
+public interface EncounterCounter
+{
+ /**
+ * @return how many times have I seen this object before?
+ */
+ public long encounter(Object o);
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/encounter/EqualityEncounterCounter.java b/src/classes/com/mchange/v2/encounter/EqualityEncounterCounter.java
new file mode 100644
index 0000000..91f105f
--- /dev/null
+++ b/src/classes/com/mchange/v2/encounter/EqualityEncounterCounter.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.encounter;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+public class EqualityEncounterCounter extends AbstractEncounterCounter
+{
+ public EqualityEncounterCounter()
+ { super( new WeakHashMap() ); }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/holders/ChangeNotifyingSynchronizedIntHolder.java b/src/classes/com/mchange/v2/holders/ChangeNotifyingSynchronizedIntHolder.java
new file mode 100644
index 0000000..e98f8f4
--- /dev/null
+++ b/src/classes/com/mchange/v2/holders/ChangeNotifyingSynchronizedIntHolder.java
@@ -0,0 +1,98 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.holders;
+
+import java.io.*;
+import com.mchange.v2.ser.UnsupportedVersionException;
+
+public final class ChangeNotifyingSynchronizedIntHolder implements ThreadSafeIntHolder, Serializable
+{
+ transient int value;
+ transient boolean notify_all;
+
+ public ChangeNotifyingSynchronizedIntHolder( int value, boolean notify_all )
+ {
+ this.value = value;
+ this.notify_all = notify_all;
+ }
+
+ public ChangeNotifyingSynchronizedIntHolder()
+ { this(0, true); }
+
+ public synchronized int getValue()
+ { return value; }
+
+ public synchronized void setValue(int value)
+ {
+ if (value != this.value)
+ {
+ this.value = value;
+ doNotify();
+ }
+ }
+
+ public synchronized void increment()
+ {
+ ++value;
+ doNotify();
+ }
+
+ public synchronized void decrement()
+ {
+ --value;
+ doNotify();
+ }
+
+ //must be called from a sync'ed block...
+ private void doNotify()
+ {
+ if (notify_all) this.notifyAll();
+ else this.notify();
+ }
+
+ //Serialization
+ static final long serialVersionUID = 1; //override to take control of versioning
+ private final static short VERSION = 0x0001;
+
+ private void writeObject(ObjectOutputStream out) throws IOException
+ {
+ out.writeShort(VERSION);
+ out.writeInt(value);
+ out.writeBoolean(notify_all);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException
+ {
+ short version = in.readShort();
+ switch (version)
+ {
+ case 0x0001:
+ this.value = in.readInt();
+ this.notify_all = in.readBoolean();
+ break;
+ default:
+ throw new UnsupportedVersionException(this, version);
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/holders/SynchronizedIntHolder.java b/src/classes/com/mchange/v2/holders/SynchronizedIntHolder.java
new file mode 100644
index 0000000..afd2967
--- /dev/null
+++ b/src/classes/com/mchange/v2/holders/SynchronizedIntHolder.java
@@ -0,0 +1,73 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.holders;
+
+import java.io.*;
+import com.mchange.v2.ser.UnsupportedVersionException;
+
+public class SynchronizedIntHolder implements ThreadSafeIntHolder, Serializable
+{
+ transient int value;
+
+ public SynchronizedIntHolder( int value )
+ { this.value = value; }
+
+ public SynchronizedIntHolder()
+ { this(0); }
+
+ public synchronized int getValue()
+ { return value; }
+
+ public synchronized void setValue(int value)
+ { this.value = value; }
+
+ public synchronized void increment()
+ { ++value; }
+
+ public synchronized void decrement()
+ { --value; }
+
+ //Serialization
+ static final long serialVersionUID = 1; //override to take control of versioning
+ private final static short VERSION = 0x0001;
+
+ private void writeObject(ObjectOutputStream out) throws IOException
+ {
+ out.writeShort(VERSION);
+ out.writeInt(value);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException
+ {
+ short version = in.readShort();
+ switch (version)
+ {
+ case 0x0001:
+ this.value = in.readInt();
+ break;
+ default:
+ throw new UnsupportedVersionException(this, version);
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/holders/ThreadSafeIntHolder.java b/src/classes/com/mchange/v2/holders/ThreadSafeIntHolder.java
new file mode 100644
index 0000000..3771c75
--- /dev/null
+++ b/src/classes/com/mchange/v2/holders/ThreadSafeIntHolder.java
@@ -0,0 +1,30 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.holders;
+
+public interface ThreadSafeIntHolder
+{
+ public int getValue();
+ public void setValue(int i);
+}
diff --git a/src/classes/com/mchange/v2/io/IndentedWriter.java b/src/classes/com/mchange/v2/io/IndentedWriter.java
new file mode 100644
index 0000000..3d1cc7c
--- /dev/null
+++ b/src/classes/com/mchange/v2/io/IndentedWriter.java
@@ -0,0 +1,154 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.io;
+
+import java.io.*;
+
+public class IndentedWriter extends FilterWriter
+{
+ final static String EOL;
+
+ static
+ {
+ String eol = System.getProperty( "line.separator" );
+ EOL = ( eol != null ? eol : "\r\n" );
+ }
+
+ int indent_level = 0;
+ boolean at_line_start = true;
+
+ public IndentedWriter( Writer out )
+ { super( out ); }
+
+ private boolean isEol( char c )
+ { return ( c == '\r' || c == '\n' ); }
+
+ public void upIndent()
+ { ++indent_level; }
+
+ public void downIndent()
+ { --indent_level; }
+
+ public void write( int c ) throws IOException
+ {
+ out.write( c );
+ at_line_start = isEol( (char) c );
+ }
+
+ public void write( char[] chars, int off, int len ) throws IOException
+ {
+ out.write( chars, off, len );
+ at_line_start = isEol( chars[ off + len - 1] );
+ }
+
+ public void write( String s, int off, int len ) throws IOException
+ {
+ if (len > 0)
+ {
+ out.write( s, off, len );
+ at_line_start = isEol( s.charAt( off + len - 1) );
+ }
+ }
+
+ private void printIndent() throws IOException
+ {
+ for (int i = 0; i < indent_level; ++i)
+ out.write( '\t' );
+ }
+
+ public void print( String s ) throws IOException
+ {
+ if ( at_line_start )
+ printIndent();
+ out.write(s);
+ char last = s.charAt( s.length() - 1 );
+ at_line_start = isEol( last );
+ }
+
+ public void println( String s ) throws IOException
+ {
+ if ( at_line_start )
+ printIndent();
+ out.write(s);
+ out.write( EOL );
+ at_line_start = true;
+ }
+
+ public void print( boolean x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( byte x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( char x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( short x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( int x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( long x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( float x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( double x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void print( Object x ) throws IOException
+ { print( String.valueOf(x) ); }
+
+ public void println( boolean x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( byte x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( char x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( short x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( int x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( long x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( float x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( double x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println( Object x ) throws IOException
+ { println( String.valueOf(x) ); }
+
+ public void println() throws IOException
+ { println( "" ); }
+}
diff --git a/src/classes/com/mchange/v2/lang/Coerce.java b/src/classes/com/mchange/v2/lang/Coerce.java
new file mode 100644
index 0000000..acb592e
--- /dev/null
+++ b/src/classes/com/mchange/v2/lang/Coerce.java
@@ -0,0 +1,138 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.lang;
+
+import java.util.*;
+
+public final class Coerce
+{
+ final static Set CAN_COERCE;
+
+ static
+ {
+ Class[] classes =
+ {
+ byte.class,
+ boolean.class,
+ char.class,
+ short.class,
+ int.class,
+ long.class,
+ float.class,
+ double.class,
+ String.class,
+ Byte.class,
+ Boolean.class,
+ Character.class,
+ Short.class,
+ Integer.class,
+ Long.class,
+ Float.class,
+ Double.class
+ };
+ Set tmp = new HashSet();
+ tmp.addAll( Arrays.asList( classes ) );
+ CAN_COERCE = Collections.unmodifiableSet( tmp );
+ }
+
+ public static boolean canCoerce( Class cl )
+ { return CAN_COERCE.contains( cl ); }
+
+ public static boolean canCoerce( Object o )
+ { return canCoerce( o.getClass() ); }
+
+ public static int toInt( String s )
+ {
+ try { return Integer.parseInt( s ); }
+ catch ( NumberFormatException e )
+ { return (int) Double.parseDouble( s ); }
+ }
+
+ public static long toLong( String s )
+ {
+ try { return Long.parseLong( s ); }
+ catch ( NumberFormatException e )
+ { return (long) Double.parseDouble( s ); }
+ }
+
+ public static float toFloat( String s )
+ { return Float.parseFloat( s ); }
+
+ public static double toDouble( String s )
+ { return Double.parseDouble( s ); }
+
+ public static byte toByte( String s )
+ { return (byte) toInt(s); }
+
+ public static short toShort( String s )
+ { return (short) toInt(s); }
+
+ public static boolean toBoolean( String s )
+ { return Boolean.valueOf( s ).booleanValue(); }
+
+ public static char toChar( String s )
+ {
+ s = s.trim();
+ if (s.length() == 1)
+ return s.charAt( 0 );
+ else
+ return (char) toInt(s);
+ }
+
+ public static Object toObject( String s, Class type )
+ {
+ if ( type == byte.class) type = Byte.class;
+ else if ( type == boolean.class) type = Boolean.class;
+ else if ( type == char.class) type = Character.class;
+ else if ( type == short.class) type = Short.class;
+ else if ( type == int.class) type = Integer.class;
+ else if ( type == long.class) type = Long.class;
+ else if ( type == float.class) type = Float.class;
+ else if ( type == double.class) type = Double.class;
+
+ if ( type == String.class )
+ return s;
+ else if ( type == Byte.class )
+ return new Byte( toByte( s ) );
+ else if ( type == Boolean.class )
+ return Boolean.valueOf( s );
+ else if ( type == Character.class )
+ return new Character( toChar( s ) );
+ else if ( type == Short.class )
+ return new Short( toShort( s ) );
+ else if ( type == Integer.class )
+ return new Integer( s );
+ else if ( type == Long.class )
+ return new Long( s );
+ else if ( type == Float.class )
+ return new Float( s );
+ else if ( type == Double.class )
+ return new Double( s );
+ else
+ throw new IllegalArgumentException("Cannot coerce to type: " + type.getName());
+ }
+
+ private Coerce()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/lang/ObjectUtils.java b/src/classes/com/mchange/v2/lang/ObjectUtils.java
new file mode 100644
index 0000000..282a616
--- /dev/null
+++ b/src/classes/com/mchange/v2/lang/ObjectUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.lang;
+
+public final class ObjectUtils
+{
+ public static boolean eqOrBothNull(Object a, Object b)
+ {
+ if (a == b)
+ return true;
+ else if (a == null)
+ return false;
+ else
+ return a.equals(b);
+ }
+
+ /**
+ * Note -- if you are using Arrays.equals( ... ) or similar
+ * and want a compatible hash method, see methods in
+ * {@link com.mchange.v1.util.ArrayUtils#hashOrZeroArray ArrayUtils}.
+ */
+ public static int hashOrZero(Object o)
+ { return (o == null ? 0 : o.hashCode()); }
+
+ private ObjectUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/lang/ThreadGroupUtils.java b/src/classes/com/mchange/v2/lang/ThreadGroupUtils.java
new file mode 100644
index 0000000..7af494a
--- /dev/null
+++ b/src/classes/com/mchange/v2/lang/ThreadGroupUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.lang;
+
+public final class ThreadGroupUtils
+{
+ public static ThreadGroup rootThreadGroup()
+ {
+ ThreadGroup tg = Thread.currentThread().getThreadGroup();
+ ThreadGroup ptg = tg.getParent();
+ while (ptg != null)
+ {
+ tg = ptg;
+ ptg = tg.getParent();
+ }
+ return tg;
+ }
+
+ private ThreadGroupUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/lang/ThreadUtils.java b/src/classes/com/mchange/v2/lang/ThreadUtils.java
new file mode 100644
index 0000000..5448458
--- /dev/null
+++ b/src/classes/com/mchange/v2/lang/ThreadUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.lang;
+
+import com.mchange.v2.log.*;
+import java.lang.reflect.Method;
+
+public final class ThreadUtils
+{
+ private final static MLogger logger = MLog.getLogger( ThreadUtils.class );
+
+ final static Method holdsLock;
+
+ static
+ {
+ Method _holdsLock;
+ try
+ { _holdsLock = Thread.class.getMethod("holdsLock", new Class[] { Object.class }); }
+ catch (NoSuchMethodException e)
+ { _holdsLock = null; }
+
+ holdsLock = _holdsLock;
+ }
+
+ public static void enumerateAll( Thread[] threads )
+ { ThreadGroupUtils.rootThreadGroup().enumerate( threads ); }
+
+ /**
+ * @returns null if cannot be determined, otherwise true or false
+ */
+ public static Boolean reflectiveHoldsLock( Object o )
+ {
+ try
+ {
+ if (holdsLock == null)
+ return null;
+ else
+ return (Boolean) holdsLock.invoke( null, new Object[] { o } );
+ }
+ catch (Exception e)
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.log( MLevel.FINER, "An Exception occurred while trying to call Thread.holdsLock( ... ) reflectively.", e);
+ return null;
+ }
+ }
+
+ private ThreadUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/lang/VersionUtils.java b/src/classes/com/mchange/v2/lang/VersionUtils.java
new file mode 100644
index 0000000..ca73499
--- /dev/null
+++ b/src/classes/com/mchange/v2/lang/VersionUtils.java
@@ -0,0 +1,182 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.lang;
+
+import com.mchange.v2.log.*;
+import com.mchange.v1.util.StringTokenizerUtils;
+
+public final class VersionUtils
+{
+ private final static MLogger logger = MLog.getLogger( VersionUtils.class );
+
+ private final static int[] DFLT_VERSION_ARRAY = {1,1};
+
+ private final static int[] JDK_VERSION_ARRAY;
+ private final static int JDK_VERSION; //two digit int... 10 for 1.0, 11 for 1.1, etc.
+
+ private final static Integer NUM_BITS;
+
+ static
+ {
+ String vstr = System.getProperty( "java.version" );
+ int[] v;
+ if (vstr == null)
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning("Could not find java.version System property. Defaulting to JDK 1.1");
+ v = DFLT_VERSION_ARRAY;
+ }
+ else
+ {
+ try { v = extractVersionNumberArray( vstr, "._" ); }
+ catch ( NumberFormatException e )
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning("java.version ''" + vstr + "'' could not be parsed. Defaulting to JDK 1.1.");
+ v = DFLT_VERSION_ARRAY;
+ }
+ }
+ int jdkv = 0;
+ if (v.length > 0)
+ jdkv += (v[0] * 10);
+ if (v.length > 1)
+ jdkv += (v[1]);
+
+ JDK_VERSION_ARRAY = v;
+ JDK_VERSION = jdkv;
+
+ //System.err.println( JDK_VERSION );
+
+ Integer tmpNumBits;
+ try
+ {
+ String numBitsStr = System.getProperty("sun.arch.data.model");
+ if (numBitsStr == null)
+ tmpNumBits = null;
+ else
+ tmpNumBits = new Integer( numBitsStr );
+ }
+ catch (Exception e)
+ {
+ tmpNumBits = null;
+ }
+
+ if (tmpNumBits == null || tmpNumBits.intValue() == 32 || tmpNumBits.intValue() == 64)
+ NUM_BITS = tmpNumBits;
+ else
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning("Determined a surprising jvmNumerOfBits: " + tmpNumBits +
+ ". Setting jvmNumberOfBits to unknown (null).");
+ NUM_BITS = null;
+ }
+ }
+
+ /**
+ * @return null if unknown,
+ * an Integer (as of 2006 always 32 or 64)
+ * otherwise
+ */
+ public static Integer jvmNumberOfBits()
+ { return NUM_BITS; }
+
+ public static boolean isJavaVersion10()
+ { return (JDK_VERSION == 10); }
+
+ public static boolean isJavaVersion11()
+ { return (JDK_VERSION == 11); }
+
+ public static boolean isJavaVersion12()
+ { return (JDK_VERSION == 12); }
+
+ public static boolean isJavaVersion13()
+ { return (JDK_VERSION == 13); }
+
+ public static boolean isJavaVersion14()
+ { return (JDK_VERSION == 14); }
+
+ public static boolean isJavaVersion15()
+ { return (JDK_VERSION == 15); }
+
+ public static boolean isAtLeastJavaVersion10()
+ { return (JDK_VERSION >= 10); }
+
+ public static boolean isAtLeastJavaVersion11()
+ { return (JDK_VERSION >= 11); }
+
+ public static boolean isAtLeastJavaVersion12()
+ { return (JDK_VERSION >= 12); }
+
+ public static boolean isAtLeastJavaVersion13()
+ { return (JDK_VERSION >= 13); }
+
+ public static boolean isAtLeastJavaVersion14()
+ { return (JDK_VERSION >= 14); }
+
+ public static boolean isAtLeastJavaVersion15()
+ { return (JDK_VERSION >= 15); }
+
+ public static int[] extractVersionNumberArray(String versionString, String delims)
+ throws NumberFormatException
+ {
+ String[] intStrs = StringTokenizerUtils.tokenizeToArray( versionString, delims, false );
+ int len = intStrs.length;
+ int[] out = new int[ len ];
+ for (int i = 0; i < len; ++i)
+ out[i] = Integer.parseInt( intStrs[i] );
+ return out;
+ }
+
+ public boolean prefixMatches( int[] pfx, int[] fullVersion )
+ {
+ if (pfx.length > fullVersion.length)
+ return false;
+ else
+ {
+ for (int i = 0, len = pfx.length; i < len; ++i)
+ if (pfx[i] != fullVersion[i])
+ return false;
+ return true;
+ }
+ }
+
+ public static int lexicalCompareVersionNumberArrays(int[] a, int[] b)
+ {
+ int alen = a.length;
+ int blen = b.length;
+ for (int i = 0; i < alen; ++i)
+ {
+ if (i == blen)
+ return 1; //a is larger if they are the same to a point, but a has an extra version number
+ else if (a[i] > b[i])
+ return 1;
+ else if (a[i] < b[i])
+ return -1;
+ }
+ if (blen > alen)
+ return -1; //a is smaller if they are the same to a point, but b has an extra version number
+ else
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/FallbackMLog.java b/src/classes/com/mchange/v2/log/FallbackMLog.java
new file mode 100644
index 0000000..12b239a
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/FallbackMLog.java
@@ -0,0 +1,357 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+import com.mchange.lang.ThrowableUtils;
+
+public final class FallbackMLog extends MLog
+{
+ final static MLevel DEFAULT_CUTOFF_LEVEL;
+
+ static
+ {
+ MLevel dflt = null;
+ String dfltName = MLog.CONFIG.getProperty( "com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL" );
+ if (dfltName != null)
+ dflt = MLevel.fromSeverity( dfltName );
+ if (dflt == null)
+ dflt = MLevel.INFO;
+ DEFAULT_CUTOFF_LEVEL = dflt;
+ }
+
+ MLogger logger = new FallbackMLogger();
+
+ public synchronized MLogger getMLogger(String name)
+ { return logger; }
+
+ public MLogger getMLogger(Class cl)
+ { return getLogger( cl.getName() ); }
+
+
+ public MLogger getMLogger()
+ { return logger; }
+
+ private final static class FallbackMLogger implements MLogger
+ {
+ MLevel cutoffLevel = DEFAULT_CUTOFF_LEVEL;
+
+ private void formatrb(MLevel l, String srcClass, String srcMeth, String rbname, String msg, Object[] params, Throwable t)
+ {
+ ResourceBundle rb = ResourceBundle.getBundle( rbname );
+ if (msg != null && rb != null)
+ {
+ String check = rb.getString( msg );
+ if (check != null)
+ msg = check;
+ }
+ format( l, srcClass, srcMeth, msg, params, t);
+ }
+
+ private void format(MLevel l, String srcClass, String srcMeth, String msg, Object[] params, Throwable t)
+ { System.err.println( formatString( l, srcClass, srcMeth, msg, params, t ) ); }
+
+ private String formatString(MLevel l, String srcClass, String srcMeth, String msg, Object[] params, Throwable t)
+ {
+ boolean add_parens = (srcMeth != null && ! srcMeth.endsWith(")"));
+
+ StringBuffer sb = new StringBuffer(256);
+ sb.append(l.getLineHeader());
+ sb.append(' ');
+ if (srcClass != null && srcMeth != null)
+ {
+ sb.append('[');
+ sb.append( srcClass );
+ sb.append( '.' );
+ sb.append( srcMeth );
+ if (add_parens)
+ sb.append("()");
+ sb.append( ']' );
+ }
+ else if (srcClass != null)
+ {
+ sb.append('[');
+ sb.append( srcClass );
+ sb.append( ']' );
+ }
+ else if (srcMeth != null)
+ {
+ sb.append('[');
+ sb.append( srcMeth );
+ if (add_parens)
+ sb.append("()");
+ sb.append( ']' );
+ }
+ if (msg == null)
+ {
+ if (params != null)
+ {
+ sb.append("params: ");
+ for (int i = 0, len = params.length; i < len; ++i)
+ {
+ if (i != 0) sb.append(", ");
+ sb.append( params[i] );
+ }
+ }
+ }
+ else
+ {
+ if (params == null)
+ sb.append( msg );
+ else
+ {
+ MessageFormat mfmt = new MessageFormat( msg );
+ sb.append( mfmt.format( params ) );
+ }
+ }
+
+ if (t != null)
+ sb.append( ThrowableUtils.extractStackTrace( t ) );
+
+ return sb.toString();
+ }
+
+ public ResourceBundle getResourceBundle()
+ {
+ //warn("Using logger " + this.getClass().getName() + ", which does not support ResourceBundles.");
+ return null;
+ }
+
+ public String getResourceBundleName()
+ { return null; }
+
+ public void setFilter(Object java14Filter) throws SecurityException
+ {
+ warning("Using FallbackMLog -- Filters not supported!");
+ }
+
+ public Object getFilter()
+ {
+ return null;
+ }
+
+ public void log(MLevel l, String msg)
+ {
+ if ( isLoggable( l ) )
+ format( l, null, null, msg, null, null );
+ }
+
+ public void log(MLevel l, String msg, Object param)
+ {
+ if ( isLoggable( l ) )
+ format( l, null, null, msg, new Object[] { param }, null );
+ }
+
+ public void log(MLevel l,String msg, Object[] params)
+ {
+ if ( isLoggable( l ) )
+ format( l, null, null, msg, params, null );
+ }
+
+ public void log(MLevel l, String msg, Throwable t)
+ {
+ if ( isLoggable( l ) )
+ format( l, null, null, msg, null, t );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg)
+ {
+ if ( isLoggable( l ) )
+ format( l, srcClass, srcMeth, msg, null, null );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object param)
+ {
+ if ( isLoggable( l ) )
+ format( l, srcClass, srcMeth, msg, new Object[] { param }, null );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object[] params)
+ {
+ if ( isLoggable( l ) )
+ format( l, srcClass, srcMeth, msg, params, null );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Throwable t)
+ {
+ if ( isLoggable( l ) )
+ format( l, srcClass, srcMeth, msg, null, t );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg)
+ {
+ if ( isLoggable( l ) )
+ formatrb( l, srcClass, srcMeth, rb, msg, null, null );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object param)
+ {
+ if ( isLoggable( l ) )
+ formatrb( l, srcClass, srcMeth, rb, msg, new Object[] { param }, null );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object[] params)
+ {
+ if ( isLoggable( l ) )
+ formatrb( l, srcClass, srcMeth, rb, msg, params, null );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Throwable t)
+ {
+ if ( isLoggable( l ) )
+ formatrb( l, srcClass, srcMeth, rb, msg, null, t );
+ }
+
+ public void entering(String srcClass, String srcMeth)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ format(MLevel.FINER, srcClass, srcMeth, "Entering method.", null, null);
+ }
+
+ public void entering(String srcClass, String srcMeth, Object param)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ format(MLevel.FINER, srcClass, srcMeth, "Entering method with argument " + param, null, null);
+ }
+
+ public void entering(String srcClass, String srcMeth, Object[] params)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ {
+ if (params == null)
+ entering( srcClass, srcMeth );
+ else
+ {
+ StringBuffer sb = new StringBuffer(128);
+ sb.append("( ");
+ for (int i = 0, len = params.length; i < len; ++i)
+ {
+ if (i != 0) sb.append(", ");
+ sb.append( params[i] );
+ }
+ sb.append(" )");
+ format(MLevel.FINER, srcClass, srcMeth, "Entering method with arguments " + sb.toString(), null, null);
+ }
+ }
+ }
+
+ public void exiting(String srcClass, String srcMeth)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ format(MLevel.FINER, srcClass, srcMeth, "Exiting method.", null, null);
+ }
+
+ public void exiting(String srcClass, String srcMeth, Object result)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ format(MLevel.FINER, srcClass, srcMeth, "Exiting method with result " + result, null, null);
+ }
+
+ public void throwing(String srcClass, String srcMeth, Throwable t)
+ {
+ if ( isLoggable( MLevel.FINE ) )
+ format(MLevel.FINE, srcClass, srcMeth, "Throwing exception." , null, t);
+ }
+
+ public void severe(String msg)
+ {
+ if ( isLoggable( MLevel.SEVERE ) )
+ format(MLevel.SEVERE, null, null, msg, null, null);
+ }
+
+ public void warning(String msg)
+ {
+ if ( isLoggable( MLevel.WARNING ) )
+ format(MLevel.WARNING, null, null, msg, null, null);
+ }
+
+ public void info(String msg)
+ {
+ if ( isLoggable( MLevel.INFO ) )
+ format(MLevel.INFO, null, null, msg, null, null);
+ }
+
+ public void config(String msg)
+ {
+ if ( isLoggable( MLevel.CONFIG ) )
+ format(MLevel.CONFIG, null, null, msg, null, null);
+ }
+
+ public void fine(String msg)
+ {
+ if ( isLoggable( MLevel.FINE ) )
+ format(MLevel.FINE, null, null, msg, null, null);
+ }
+
+ public void finer(String msg)
+ {
+ if ( isLoggable( MLevel.FINER ) )
+ format(MLevel.FINER, null, null, msg, null, null);
+ }
+
+ public void finest(String msg)
+ {
+ if ( isLoggable( MLevel.FINEST ) )
+ format(MLevel.FINEST, null, null, msg, null, null);
+ }
+
+ public void setLevel(MLevel l) throws SecurityException
+ { this.cutoffLevel = l; }
+
+ public synchronized MLevel getLevel()
+ { return cutoffLevel; }
+
+ public synchronized boolean isLoggable(MLevel l)
+ { return (l.intValue() >= cutoffLevel.intValue()); }
+
+ public String getName()
+ { return "global"; }
+
+ public void addHandler(Object h) throws SecurityException
+ {
+ warning("Using FallbackMLog -- Handlers not supported.");
+ }
+
+ public void removeHandler(Object h) throws SecurityException
+ {
+ warning("Using FallbackMLog -- Handlers not supported.");
+ }
+
+ public Object[] getHandlers()
+ {
+ warning("Using FallbackMLog -- Handlers not supported.");
+ return new Object[0];
+ }
+
+ public void setUseParentHandlers(boolean uph)
+ {
+ warning("Using FallbackMLog -- Handlers not supported.");
+ }
+
+ public boolean getUseParentHandlers()
+ { return false; }
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/LogUtils.java b/src/classes/com/mchange/v2/log/LogUtils.java
new file mode 100644
index 0000000..83e6963
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/LogUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+public final class LogUtils
+{
+ public static String createParamsList(Object[] params)
+ {
+ StringBuffer sb = new StringBuffer(511);
+ LogUtils.appendParamsList( sb, params );
+ return sb.toString();
+ }
+
+ public static void appendParamsList(StringBuffer sb, Object[] params)
+ {
+ sb.append("[params: ");
+ for (int i = 0, len = params.length; i < len; ++i)
+ {
+ if (i != 0) sb.append(", ");
+ sb.append( params[i] );
+ }
+ sb.append(']');
+ }
+
+ private LogUtils()
+ {}
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/MLevel.java b/src/classes/com/mchange/v2/log/MLevel.java
new file mode 100644
index 0000000..6cea61f
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/MLevel.java
@@ -0,0 +1,155 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+import java.util.*;
+
+public final class MLevel
+{
+ public final static MLevel ALL;
+ public final static MLevel CONFIG;
+ public final static MLevel FINE;
+ public final static MLevel FINER;
+ public final static MLevel FINEST;
+ public final static MLevel INFO;
+ public final static MLevel OFF;
+ public final static MLevel SEVERE;
+ public final static MLevel WARNING;
+
+ private final static Map integersToMLevels;
+ private final static Map namesToMLevels;
+
+ public static MLevel fromIntValue(int intval)
+ { return (MLevel) integersToMLevels.get( new Integer( intval ) ); }
+
+ public static MLevel fromSeverity(String name)
+ { return (MLevel) namesToMLevels.get( name ); }
+
+ static
+ {
+ Class lvlClass;
+ boolean jdk14api; //not just jdk14 -- it is possible for the api to be present with older vms
+ try
+ {
+ lvlClass = Class.forName( "java.util.logging.Level" );
+ jdk14api = true;
+ }
+ catch (ClassNotFoundException e )
+ {
+ lvlClass = null;
+ jdk14api = false;
+ }
+
+ MLevel all;
+ MLevel config;
+ MLevel fine;
+ MLevel finer;
+ MLevel finest;
+ MLevel info;
+ MLevel off;
+ MLevel severe;
+ MLevel warning;
+
+ try
+ {
+ // numeric values match the intvalues from java.util.logging.Level
+ all = new MLevel( (jdk14api ? lvlClass.getField("ALL").get(null) : null), Integer.MIN_VALUE, "ALL" );
+ config = new MLevel( (jdk14api ? lvlClass.getField("CONFIG").get(null) : null), 700, "CONFIG" );
+ fine = new MLevel( (jdk14api ? lvlClass.getField("FINE").get(null) : null), 500, "FINE" );
+ finer = new MLevel( (jdk14api ? lvlClass.getField("FINER").get(null) : null), 400, "FINER" );
+ finest = new MLevel( (jdk14api ? lvlClass.getField("FINEST").get(null) : null), 300, "FINEST" );
+ info = new MLevel( (jdk14api ? lvlClass.getField("INFO").get(null) : null), 800, "INFO" );
+ off = new MLevel( (jdk14api ? lvlClass.getField("OFF").get(null) : null), Integer.MAX_VALUE, "OFF" );
+ severe = new MLevel( (jdk14api ? lvlClass.getField("SEVERE").get(null) : null), 900, "SEVERE" );
+ warning = new MLevel( (jdk14api ? lvlClass.getField("WARNING").get(null) : null), 1000, "WARNING" );
+ }
+ catch ( Exception e )
+ {
+ e.printStackTrace();
+ throw new InternalError("Huh? java.util.logging.Level is here, but not its expected public fields?");
+ }
+
+ ALL = all;
+ CONFIG = config;
+ FINE = fine;
+ FINER = finer;
+ FINEST = finest;
+ INFO = info;
+ OFF = off;
+ SEVERE = severe;
+ WARNING = warning;
+
+ Map tmp = new HashMap();
+ tmp.put( new Integer(all.intValue()), all);
+ tmp.put( new Integer(config.intValue()), config);
+ tmp.put( new Integer(fine.intValue()), fine);
+ tmp.put( new Integer(finer.intValue()), finer);
+ tmp.put( new Integer(finest.intValue()), finest);
+ tmp.put( new Integer(info.intValue()), info);
+ tmp.put( new Integer(off.intValue()), off);
+ tmp.put( new Integer(severe.intValue()), severe);
+ tmp.put( new Integer(warning.intValue()), warning);
+
+ integersToMLevels = Collections.unmodifiableMap( tmp );
+
+ tmp = new HashMap();
+ tmp.put( all.getSeverity(), all);
+ tmp.put( config.getSeverity(), config);
+ tmp.put( fine.getSeverity(), fine);
+ tmp.put( finer.getSeverity(), finer);
+ tmp.put( finest.getSeverity(), finest);
+ tmp.put( info.getSeverity(), info);
+ tmp.put( off.getSeverity(), off);
+ tmp.put( severe.getSeverity(), severe);
+ tmp.put( warning.getSeverity(), warning);
+
+ namesToMLevels = Collections.unmodifiableMap( tmp );
+ }
+
+ Object level;
+ int intval;
+ String lvlstring;
+
+ public int intValue()
+ { return intval; }
+
+ public Object asJdk14Level()
+ { return level; }
+
+ public String getSeverity()
+ { return lvlstring; }
+
+ public String toString()
+ { return this.getClass().getName() + this.getLineHeader(); }
+
+ public String getLineHeader()
+ { return "[" + lvlstring + ']';}
+
+ private MLevel(Object level, int intval, String lvlstring)
+ {
+ this.level = level;
+ this.intval = intval;
+ this.lvlstring = lvlstring;
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/MLog.java b/src/classes/com/mchange/v2/log/MLog.java
new file mode 100644
index 0000000..c5e75f0
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/MLog.java
@@ -0,0 +1,251 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+import java.util.List;
+import java.util.ArrayList;
+import com.mchange.v1.util.StringTokenizerUtils;
+import com.mchange.v2.cfg.MultiPropertiesConfig;
+
+public abstract class MLog
+{
+ final static NameTransformer transformer;
+ final static MLog mlog;
+
+ final static MultiPropertiesConfig CONFIG;
+
+ final static MLogger logger;
+
+ static
+ {
+ String[] defaults = new String[]
+ {
+ "/com/mchange/v2/log/default-mchange-log.properties",
+ "/mchange-log.properties",
+ "/"
+ };
+ CONFIG = MultiPropertiesConfig.readVmConfig( defaults, null );
+
+ String classnamesStr = CONFIG.getProperty("com.mchange.v2.log.MLog");
+ String[] classnames = null;
+ if (classnamesStr == null)
+ classnamesStr = CONFIG.getProperty("com.mchange.v2.log.mlog");
+ if (classnamesStr != null)
+ classnames = StringTokenizerUtils.tokenizeToArray( classnamesStr, ", \t\r\n" );
+
+ boolean warn = false;
+ MLog tmpml = null;
+ if (classnames != null)
+ tmpml = findByClassnames( classnames );
+ if (tmpml == null)
+ tmpml = findByClassnames( MLogClasses.CLASSNAMES );
+ if (tmpml == null)
+ {
+ warn = true;
+ tmpml = new FallbackMLog();
+ }
+ mlog = tmpml;
+ if (warn)
+ info("Using " + mlog.getClass().getName() + " -- Named logger's not supported, everything goes to System.err.");
+
+ logger = mlog.getLogger( MLog.class );
+ String loggerDesc = mlog.getClass().getName();
+ if ("com.mchange.v2.log.jdk14logging.Jdk14MLog".equals( loggerDesc ))
+ loggerDesc = "java 1.4+ standard";
+ else if ("com.mchange.v2.log.log4j.Log4jMLog".equals( loggerDesc ))
+ loggerDesc = "log4j";
+
+ if (logger.isLoggable( MLevel.INFO ))
+ logger.log( MLevel.INFO, "MLog clients using " + loggerDesc + " logging.");
+
+ NameTransformer tmpt = null;
+ String tClassName = CONFIG.getProperty("com.mchange.v2.log.NameTransformer");
+ if (tClassName == null)
+ tClassName = CONFIG.getProperty("com.mchange.v2.log.nametransformer");
+ try
+ {
+ if (tClassName != null)
+ tmpt = (NameTransformer) Class.forName( tClassName ).newInstance();
+ }
+ catch ( Exception e )
+ {
+ System.err.println("Failed to instantiate com.mchange.v2.log.NameTransformer '" + tClassName + "'!");
+ e.printStackTrace();
+ }
+ transformer = tmpt;
+
+ //System.err.println(mlog);
+ }
+
+ public static MLog findByClassnames( String[] classnames )
+ {
+ List attempts = null;
+ for (int i = 0, len = classnames.length; i < len; ++i)
+ {
+ try { return (MLog) Class.forName( classnames[i] ).newInstance(); }
+ catch (Exception e)
+ {
+ if (attempts == null)
+ attempts = new ArrayList();
+ attempts.add( classnames[i] );
+// System.err.println("com.mchange.v2.log.MLog '" + classnames[i] + "' could not be loaded!");
+// e.printStackTrace();
+ }
+ }
+ System.err.println("Tried without success to load the following MLog classes:");
+ for (int i = 0, len = attempts.size(); i < len; ++i)
+ System.err.println("\t" + attempts.get(i));
+ return null;
+ }
+
+ public static MLog instance()
+ { return mlog; }
+
+ public static MLogger getLogger(String name)
+ {
+ MLogger out;
+ if ( transformer == null )
+ out = instance().getMLogger( name );
+ else
+ {
+ String xname = transformer.transformName( name );
+ if (xname != null)
+ out = instance().getMLogger( xname );
+ else
+ out = instance().getMLogger( name );
+ }
+ return out;
+ }
+
+ public static MLogger getLogger(Class cl)
+ {
+ MLogger out;
+ if ( transformer == null )
+ out = instance().getMLogger( cl );
+ else
+ {
+ String xname = transformer.transformName( cl );
+ if (xname != null)
+ out = instance().getMLogger( xname );
+ else
+ out = instance().getMLogger( cl );
+ }
+ return out;
+ }
+
+ public static MLogger getLogger()
+ {
+ MLogger out;
+ if ( transformer == null )
+ out = instance().getMLogger();
+ else
+ {
+ String xname = transformer.transformName();
+ if (xname != null)
+ out = instance().getMLogger( xname );
+ else
+ out = instance().getMLogger();
+ }
+ return out;
+ }
+
+ public static void log(MLevel l, String msg)
+ { instance().getLogger().log( l, msg ); }
+
+ public static void log(MLevel l, String msg, Object param)
+ { instance().getLogger().log( l, msg, param ); }
+
+ public static void log(MLevel l,String msg, Object[] params)
+ { instance().getLogger().log( l, msg, params ); }
+
+ public static void log(MLevel l, String msg,Throwable t)
+ { instance().getLogger().log( l, msg, t ); }
+
+ public static void logp(MLevel l, String srcClass, String srcMeth, String msg)
+ { instance().getLogger().logp( l, srcClass, srcMeth, msg ); }
+
+ public static void logp(MLevel l, String srcClass, String srcMeth, String msg, Object param)
+ { instance().getLogger().logp( l, srcClass, srcMeth, msg, param ); }
+
+ public static void logp(MLevel l, String srcClass, String srcMeth, String msg, Object[] params)
+ { instance().getLogger().logp( l, srcClass, srcMeth, msg, params ); }
+
+ public static void logp(MLevel l, String srcClass, String srcMeth, String msg, Throwable t)
+ { instance().getLogger().logp( l, srcClass, srcMeth, msg, t ); }
+
+ public static void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg)
+ { instance().getLogger().logp( l, srcClass, srcMeth, rb, msg ); }
+
+ public static void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object param)
+ { instance().getLogger().logrb( l, srcClass, srcMeth, rb, msg, param ); }
+
+ public static void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object[] params)
+ { instance().getLogger().logrb( l, srcClass, srcMeth, rb, msg, params ); }
+
+ public static void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Throwable t)
+ { instance().getLogger().logrb( l, srcClass, srcMeth, rb, msg, t ); }
+
+ public static void entering(String srcClass, String srcMeth)
+ { instance().getLogger().entering( srcClass, srcMeth ); }
+
+ public static void entering(String srcClass, String srcMeth, Object param)
+ { instance().getLogger().entering( srcClass, srcMeth, param ); }
+
+ public static void entering(String srcClass, String srcMeth, Object params[])
+ { instance().getLogger().entering( srcClass, srcMeth, params ); }
+
+ public static void exiting(String srcClass, String srcMeth)
+ { instance().getLogger().exiting( srcClass, srcMeth ); }
+
+ public static void exiting(String srcClass, String srcMeth, Object result)
+ { instance().getLogger().exiting( srcClass, srcMeth, result ); }
+
+ public static void throwing(String srcClass, String srcMeth, Throwable t)
+ { instance().getLogger().throwing( srcClass, srcMeth, t); }
+
+ public static void severe(String msg)
+ { instance().getLogger().severe( msg ); }
+
+ public static void warning(String msg)
+ { instance().getLogger().warning( msg ); }
+
+ public static void info(String msg)
+ { instance().getLogger().info( msg ); }
+
+ public static void config(String msg)
+ { instance().getLogger().config( msg ); }
+
+ public static void fine(String msg)
+ { instance().getLogger().fine( msg ); }
+
+ public static void finer(String msg)
+ { instance().getLogger().finer( msg ); }
+
+ public static void finest(String msg)
+ { instance().getLogger().finest( msg ); }
+
+ public abstract MLogger getMLogger(String name);
+ public abstract MLogger getMLogger(Class cl);
+ public abstract MLogger getMLogger();
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/MLogClasses.java b/src/classes/com/mchange/v2/log/MLogClasses.java
new file mode 100644
index 0000000..6b32283
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/MLogClasses.java
@@ -0,0 +1,36 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+public final class MLogClasses
+{
+ public final static String[] CLASSNAMES =
+ {
+ "com.mchange.v2.log.jdk14logging.Jdk14MLog",
+ "com.mchange.v2.log.FallbackMLog"
+ };
+
+ private MLogClasses()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/log/MLogger.java b/src/classes/com/mchange/v2/log/MLogger.java
new file mode 100644
index 0000000..fa6cdc8
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/MLogger.java
@@ -0,0 +1,78 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+import java.util.*;
+
+/**
+ * This is an interface designed to wrap around the JDK1.4 logging API, without
+ * having any compilation dependencies on that API, so that applications that use
+ * MLogger in a non JDK1.4 environment, or where some other logging library is
+ * prefrerred, may do so.
+ *
+ * Calls to handler and filter related methods may be ignored if some logging
+ * system besides jdk1.4 logging is the underlying library.
+ */
+public interface MLogger
+{
+ public ResourceBundle getResourceBundle();
+ public String getResourceBundleName();
+ public void setFilter(Object java14Filter) throws SecurityException;
+ public Object getFilter();
+ public void log(MLevel l, String msg);
+ public void log(MLevel l, String msg, Object param);
+ public void log(MLevel l,String msg, Object[] params);
+ public void log(MLevel l, String msg,Throwable t);
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg);
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object param);
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object[] params);
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Throwable t);
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg);
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object param);
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object[] params);
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Throwable t);
+ public void entering(String srcClass, String srcMeth);
+ public void entering(String srcClass, String srcMeth, Object param);
+ public void entering(String srcClass, String srcMeth, Object params[]);
+ public void exiting(String srcClass, String srcMeth);
+ public void exiting(String srcClass, String srcMeth, Object result);
+ public void throwing(String srcClass, String srcMeth, Throwable t);
+ public void severe(String msg);
+ public void warning(String msg);
+ public void info(String msg);
+ public void config(String msg);
+ public void fine(String msg);
+ public void finer(String msg);
+ public void finest(String msg);
+ public void setLevel(MLevel l) throws SecurityException;
+ public MLevel getLevel();
+ public boolean isLoggable(MLevel l);
+ public String getName();
+ public void addHandler(Object h) throws SecurityException;
+ public void removeHandler(Object h) throws SecurityException;
+ public Object[] getHandlers();
+ public void setUseParentHandlers(boolean uph);
+ public boolean getUseParentHandlers();
+ }
+
diff --git a/src/classes/com/mchange/v2/log/NameTransformer.java b/src/classes/com/mchange/v2/log/NameTransformer.java
new file mode 100644
index 0000000..e4e72e4
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/NameTransformer.java
@@ -0,0 +1,41 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+/**
+ * <p>When the methods return a name, the log requested from MLog.getLogger( XXX )
+ * the logger actually acquired will be based on the String returned.</p>
+ *
+ * <p>When the methods return null, no transformation will occur, and the logger
+ * that would have been returned without a transformer will be returned.</p>
+ *
+ * <p>Implementing classes must have public, no-arg constructors, through which
+ * they will be instantiated.</p>
+ */
+public interface NameTransformer
+{
+ public String transformName( String name );
+ public String transformName( Class cl );
+ public String transformName();
+}
diff --git a/src/classes/com/mchange/v2/log/PackageNames.java b/src/classes/com/mchange/v2/log/PackageNames.java
new file mode 100644
index 0000000..7a71900
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/PackageNames.java
@@ -0,0 +1,43 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log;
+
+public class PackageNames implements NameTransformer
+{
+ public String transformName( String name )
+ { return null; }
+
+ public String transformName( Class cl )
+ {
+ String fqcn = cl.getName();
+ int i = fqcn.lastIndexOf('.');
+ if (i <= 0)
+ return "";
+ else
+ return fqcn.substring(0,i);
+ }
+
+ public String transformName()
+ { return null; }
+}
diff --git a/src/classes/com/mchange/v2/log/jdk14logging/Jdk14MLog.java b/src/classes/com/mchange/v2/log/jdk14logging/Jdk14MLog.java
new file mode 100644
index 0000000..a194bb1
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/jdk14logging/Jdk14MLog.java
@@ -0,0 +1,390 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log.jdk14logging;
+
+import java.util.*;
+import java.util.logging.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.util.DoubleWeakHashMap;
+
+public final class Jdk14MLog extends MLog
+{
+ private static String[] UNKNOWN_ARRAY = new String[] {"UNKNOWN_CLASS", "UNKNOWN_METHOD"};
+
+ private final static String CHECK_CLASS = "java.util.logging.Logger";
+
+ private final Map namedLoggerMap = new DoubleWeakHashMap();
+
+ MLogger global = null;
+
+ public Jdk14MLog() throws ClassNotFoundException
+ { Class.forName( CHECK_CLASS ); }
+
+ public synchronized MLogger getMLogger(String name)
+ {
+ name = name.intern();
+
+ MLogger out = (MLogger) namedLoggerMap.get( name );
+ if (out == null)
+ {
+ Logger lg = Logger.getLogger(name);
+ out = new Jdk14MLogger( lg );
+ namedLoggerMap.put( name, out );
+ }
+ return out;
+ }
+
+ public synchronized MLogger getMLogger(Class cl)
+ { return getLogger( cl.getName() ); }
+
+
+ public synchronized MLogger getMLogger()
+ {
+ if (global == null)
+ global = new Jdk14MLogger( LogManager.getLogManager().getLogger("global") );
+ return global;
+ }
+
+ /*
+ * We have to do this ourselves when class and method aren't provided,
+ * because the automatic extraction of this information will find the
+ * (not very informative) calls in this class.
+ */
+ private static String[] findCallingClassAndMethod()
+ {
+ StackTraceElement[] ste = new Throwable().getStackTrace();
+ for (int i = 0, len = ste.length; i < len; ++i)
+ {
+ StackTraceElement check = ste[i];
+ String cn = check.getClassName();
+ if (cn != null && ! cn.startsWith("com.mchange.v2.log.jdk14logging"))
+ return new String[] { check.getClassName(), check.getMethodName() };
+ }
+ return UNKNOWN_ARRAY;
+ }
+
+
+
+ private final static class Jdk14MLogger implements MLogger
+ {
+ volatile Logger logger;
+
+ Jdk14MLogger( Logger logger )
+ {
+ this.logger = logger;
+ //System.err.println("LOGGER: " + this.logger);
+ }
+
+ private static Level level(MLevel lvl)
+ { return (Level) lvl.asJdk14Level(); }
+
+ public ResourceBundle getResourceBundle()
+ { return logger.getResourceBundle(); }
+
+ public String getResourceBundleName()
+ { return logger.getResourceBundleName(); }
+
+ public void setFilter(Object java14Filter) throws SecurityException
+ {
+ if (! (java14Filter instanceof Filter))
+ throw new IllegalArgumentException("MLogger.setFilter( ... ) requires a java.util.logging.Filter. " +
+ "This is not enforced by the compiler only to permit building under jdk 1.3");
+ logger.setFilter( (Filter) java14Filter );
+ }
+
+ public Object getFilter()
+ { return logger.getFilter(); }
+
+ public void log(MLevel l, String msg)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( level(l), sa[0], sa[1], msg );
+ }
+
+ public void log(MLevel l, String msg, Object param)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( level(l), sa[0], sa[1], msg, param );
+ }
+
+ public void log(MLevel l,String msg, Object[] params)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( level(l), sa[0], sa[1], msg, params );
+ }
+
+ public void log(MLevel l, String msg, Throwable t)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( level(l), sa[0], sa[1], msg, t );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logp( level(l), srcClass, srcMeth, msg );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object param)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logp( level(l), srcClass, srcMeth, msg, param );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object[] params)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logp( level(l), srcClass, srcMeth, msg, params );
+ }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Throwable t)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logp( level(l), srcClass, srcMeth, msg, t );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logrb( level(l), srcClass, srcMeth, rb, msg );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object param)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logrb( level(l), srcClass, srcMeth, rb, msg, param );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object[] params)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logrb( level(l), srcClass, srcMeth, rb, msg, params );
+ }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Throwable t)
+ {
+ if (! logger.isLoggable( level(l) )) return;
+
+ if (srcClass == null && srcMeth == null)
+ {
+ String[] sa = findCallingClassAndMethod();
+ srcClass = sa[0];
+ srcMeth = sa[1];
+ }
+ logger.logrb( level(l), srcClass, srcMeth, rb, msg, t );
+ }
+
+ public void entering(String srcClass, String srcMeth)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.entering( srcClass, srcMeth );
+ }
+
+ public void entering(String srcClass, String srcMeth, Object param)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.entering( srcClass, srcMeth, param );
+ }
+
+ public void entering(String srcClass, String srcMeth, Object params[])
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.entering( srcClass, srcMeth, params );
+ }
+
+ public void exiting(String srcClass, String srcMeth)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.exiting( srcClass, srcMeth );
+ }
+
+ public void exiting(String srcClass, String srcMeth, Object result)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.exiting( srcClass, srcMeth, result );
+ }
+
+ public void throwing(String srcClass, String srcMeth, Throwable t)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ logger.throwing( srcClass, srcMeth, t );
+ }
+
+ public void severe(String msg)
+ {
+ if (! logger.isLoggable( Level.SEVERE )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.SEVERE, sa[0], sa[1], msg );
+ }
+
+ public void warning(String msg)
+ {
+ if (! logger.isLoggable( Level.WARNING )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.WARNING, sa[0], sa[1], msg );
+ }
+
+ public void info(String msg)
+ {
+ if (! logger.isLoggable( Level.INFO )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.INFO, sa[0], sa[1], msg );
+ }
+
+ public void config(String msg)
+ {
+ if (! logger.isLoggable( Level.CONFIG )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.CONFIG, sa[0], sa[1], msg );
+ }
+
+ public void fine(String msg)
+ {
+ if (! logger.isLoggable( Level.FINE )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.FINE, sa[0], sa[1], msg );
+ }
+
+ public void finer(String msg)
+ {
+ if (! logger.isLoggable( Level.FINER )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.FINER, sa[0], sa[1], msg );
+ }
+
+ public void finest(String msg)
+ {
+ if (! logger.isLoggable( Level.FINEST )) return;
+
+ String[] sa = findCallingClassAndMethod();
+ logger.logp( Level.FINEST, sa[0], sa[1], msg );
+ }
+
+ public void setLevel(MLevel l) throws SecurityException
+ { logger.setLevel( level(l) ); }
+
+ public MLevel getLevel()
+ { return MLevel.fromIntValue( logger.getLevel().intValue() ); }
+
+ public boolean isLoggable(MLevel l)
+ { return logger.isLoggable( level(l) ); }
+
+ public String getName()
+ { return logger.getName(); }
+
+ public void addHandler(Object h) throws SecurityException
+ {
+ if (! (h instanceof Handler))
+ throw new IllegalArgumentException("MLogger.addHandler( ... ) requires a java.util.logging.Handler. " +
+ "This is not enforced by the compiler only to permit building under jdk 1.3");
+ logger.addHandler( (Handler) h );
+ }
+
+ public void removeHandler(Object h) throws SecurityException
+ {
+ if (! (h instanceof Handler))
+ throw new IllegalArgumentException("MLogger.removeHandler( ... ) requires a java.util.logging.Handler. " +
+ "This is not enforced by the compiler only to permit building under jdk 1.3");
+ logger.removeHandler( (Handler) h );
+ }
+
+ public Object[] getHandlers()
+ { return logger.getHandlers(); }
+
+ public void setUseParentHandlers(boolean uph)
+ { logger.setUseParentHandlers( uph ); }
+
+ public boolean getUseParentHandlers()
+ { return logger.getUseParentHandlers(); }
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/log/log4j/Log4jMLog.java b/src/classes/com/mchange/v2/log/log4j/Log4jMLog.java
new file mode 100644
index 0000000..4436319
--- /dev/null
+++ b/src/classes/com/mchange/v2/log/log4j/Log4jMLog.java
@@ -0,0 +1,313 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.log.log4j;
+
+import java.text.*;
+import java.util.*;
+
+import com.mchange.v2.log.*;
+import com.mchange.v2.util.DoubleWeakHashMap;
+
+import org.apache.log4j.*;
+
+public final class Log4jMLog extends MLog
+{
+ final static String CHECK_CLASS = "org.apache.log4j.Logger";
+
+ MLogger global = null;
+
+ public Log4jMLog() throws ClassNotFoundException
+ { Class.forName( CHECK_CLASS ); }
+
+ public MLogger getMLogger(String name)
+ {
+ Logger lg = Logger.getLogger(name);
+ return new Log4jMLogger( lg );
+ }
+
+ public MLogger getMLogger(Class cl)
+ {
+ Logger lg = Logger.getLogger(cl);
+ return new Log4jMLogger( lg );
+ }
+
+
+ public MLogger getMLogger()
+ {
+ Logger lg = Logger.getRootLogger();
+ return new Log4jMLogger( lg );
+ }
+
+ private final static class Log4jMLogger implements MLogger
+ {
+ final static String FQCN = Log4jMLogger.class.getName();
+
+ // protected by this' lock
+ MLevel myLevel = null;
+
+ volatile Logger logger;
+
+ Log4jMLogger( Logger logger )
+ { this.logger = logger; }
+
+ private static MLevel guessMLevel(Level lvl)
+ {
+ if (lvl == null)
+ return null;
+ else if (lvl == Level.ALL)
+ return MLevel.ALL;
+ else if (lvl == Level.DEBUG)
+ return MLevel.FINEST;
+ else if (lvl == Level.ERROR)
+ return MLevel.SEVERE;
+ else if (lvl == Level.FATAL)
+ return MLevel.SEVERE;
+ else if (lvl == Level.INFO)
+ return MLevel.INFO;
+ else if (lvl == Level.OFF)
+ return MLevel.OFF;
+ else if (lvl == Level.WARN)
+ return MLevel.WARNING;
+ else
+ throw new IllegalArgumentException("Unknown level: " + lvl);
+ }
+
+ private static Level level(MLevel lvl)
+ {
+ if (lvl == null)
+ return null;
+ else if (lvl == MLevel.ALL)
+ return Level.ALL;
+ else if (lvl == MLevel.CONFIG)
+ return Level.DEBUG;
+ else if (lvl == MLevel.FINE)
+ return Level.DEBUG;
+ else if (lvl == MLevel.FINER)
+ return Level.DEBUG;
+ else if (lvl == MLevel.FINEST)
+ return Level.DEBUG;
+ else if (lvl == MLevel.INFO)
+ return Level.INFO;
+ else if (lvl == MLevel.INFO)
+ return Level.OFF;
+ else if (lvl == MLevel.SEVERE)
+ return Level.ERROR;
+ else if (lvl == MLevel.WARNING)
+ return Level.WARN;
+ else
+ throw new IllegalArgumentException("Unknown MLevel: " + lvl);
+ }
+
+ private static String createMessage(String srcClass, String srcMeth, String msg)
+ {
+ StringBuffer sb = new StringBuffer(511);
+ sb.append("[class: ");
+ sb.append( srcClass );
+ sb.append("; method: ");
+ sb.append( srcMeth );
+ if (! srcMeth.endsWith(")"))
+ sb.append("()");
+ sb.append("] ");
+ sb.append( msg );
+ return sb.toString();
+ }
+
+ private static String createMessage(String srcMeth, String msg)
+ {
+ StringBuffer sb = new StringBuffer(511);
+ sb.append("[method: ");
+ sb.append( srcMeth );
+ if (! srcMeth.endsWith(")"))
+ sb.append("()");
+ sb.append("] ");
+ sb.append( msg );
+ return sb.toString();
+ }
+
+ public ResourceBundle getResourceBundle()
+ { return null; }
+
+ public String getResourceBundleName()
+ { return null; }
+
+ public void setFilter(Object java14Filter) throws SecurityException
+ { warning("setFilter() not supported by MLogger " + this.getClass().getName()); }
+
+ public Object getFilter()
+ { return null; }
+
+ private void log(Level lvl, Object msg, Throwable t)
+ { logger.log( FQCN, lvl, msg, t ); }
+
+ public void log(MLevel l, String msg)
+ { log( level(l), msg, null); }
+
+ public void log(MLevel l, String msg, Object param)
+ { log( level(l), (msg!=null ? MessageFormat.format(msg, new Object[] { param }) : null), null); }
+
+ public void log(MLevel l,String msg, Object[] params)
+ { log( level(l), (msg!=null ? MessageFormat.format(msg, params) : null), null); }
+
+ public void log(MLevel l, String msg, Throwable t)
+ { log( level(l), msg, t); }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg)
+ { log( level(l), createMessage( srcClass, srcMeth, msg), null); }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object param)
+ { log( level(l), createMessage( srcClass, srcMeth, (msg!=null ? MessageFormat.format(msg, new Object[] {param}) : null) ), null); }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Object[] params)
+ { log( level(l), createMessage( srcClass, srcMeth, (msg!=null ? MessageFormat.format(msg, params) : null) ), null); }
+
+ public void logp(MLevel l, String srcClass, String srcMeth, String msg, Throwable t)
+ { log( level(l), createMessage( srcClass, srcMeth, msg ), t); }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg)
+ { log( level(l), createMessage( srcClass, srcMeth, formatMessage(rb, msg, null) ), null); }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object param)
+ { log( level(l), createMessage( srcClass, srcMeth, formatMessage(rb, msg, new Object[] { param } ) ), null); }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Object[] params)
+ { log( level(l), createMessage( srcClass, srcMeth, formatMessage(rb, msg, params) ), null); }
+
+ public void logrb(MLevel l, String srcClass, String srcMeth, String rb, String msg, Throwable t)
+ { log( level(l), createMessage( srcClass, srcMeth, formatMessage(rb, msg, null) ), t); }
+
+ public void entering(String srcClass, String srcMeth)
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "entering method." ), null); }
+
+ public void entering(String srcClass, String srcMeth, Object param)
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "entering method... param: " + param.toString() ), null); }
+
+ public void entering(String srcClass, String srcMeth, Object params[])
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "entering method... " + LogUtils.createParamsList( params ) ), null); }
+
+ public void exiting(String srcClass, String srcMeth)
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "exiting method." ), null); }
+
+ public void exiting(String srcClass, String srcMeth, Object result)
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "exiting method... result: " + result.toString() ), null); }
+
+ public void throwing(String srcClass, String srcMeth, Throwable t)
+ { log( Level.DEBUG, createMessage( srcClass, srcMeth, "throwing exception... " ), t); }
+
+ public void severe(String msg)
+ { log( Level.ERROR, msg, null); }
+
+ public void warning(String msg)
+ { log( Level.WARN, msg, null); }
+
+ public void info(String msg)
+ { log( Level.INFO, msg, null); }
+
+ public void config(String msg)
+ { log( Level.DEBUG, msg, null); }
+
+ public void fine(String msg)
+ { log( Level.DEBUG, msg, null); }
+
+ public void finer(String msg)
+ { log( Level.DEBUG, msg, null); }
+
+ public void finest(String msg)
+ { log( Level.DEBUG, msg, null); }
+
+ public synchronized void setLevel(MLevel l) throws SecurityException
+ {
+ logger.setLevel( level( l ) );
+ myLevel = l;
+ }
+
+ public synchronized MLevel getLevel()
+ {
+ //System.err.println( logger.getLevel() );
+ if (myLevel == null)
+ myLevel = guessMLevel( logger.getLevel() );
+ return myLevel;
+ }
+
+ public boolean isLoggable(MLevel l)
+ {
+ //System.err.println( "MLevel: " + l + "; isEnabledFor(): " + logger.isEnabledFor( level(l) ) + "; getLevel(): " + getLevel() +
+ //"; MLog.getLogger().getLevel(): " + MLog.getLogger().getLevel());
+ //new Exception("WHADDAFUC").printStackTrace();
+ return logger.isEnabledFor( level(l) );
+ }
+
+ public String getName()
+ { return logger.getName(); }
+
+ public void addHandler(Object h) throws SecurityException
+ {
+ if (! (h instanceof Appender))
+ throw new IllegalArgumentException("The 'handler' " + h + " is not compatible with MLogger " + this);
+ logger.addAppender( (Appender) h );
+ }
+
+ public void removeHandler(Object h) throws SecurityException
+ {
+ if (! (h instanceof Appender))
+ throw new IllegalArgumentException("The 'handler' " + h + " is not compatible with MLogger " + this);
+ logger.removeAppender( (Appender) h );
+ }
+
+ public Object[] getHandlers()
+ {
+ List tmp = new LinkedList();
+ for (Enumeration e = logger.getAllAppenders(); e.hasMoreElements(); )
+ tmp.add( e.nextElement() );
+ return tmp.toArray();
+ }
+
+ public void setUseParentHandlers(boolean uph)
+ { logger.setAdditivity( uph ); }
+
+ public boolean getUseParentHandlers()
+ { return logger.getAdditivity(); }
+ }
+
+ private static String formatMessage( String rbname, String msg, Object[] params )
+ {
+ if ( msg == null )
+ {
+ if (params == null)
+ return "";
+ else
+ return LogUtils.createParamsList( params );
+ }
+ else
+ {
+ ResourceBundle rb = ResourceBundle.getBundle( rbname );
+ if (rb != null)
+ {
+ String check = rb.getString( msg );
+ if (check != null)
+ msg = check;
+ }
+ return (params == null ? msg : MessageFormat.format( msg, params ));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/management/ManagementUtils.java b/src/classes/com/mchange/v2/management/ManagementUtils.java
new file mode 100644
index 0000000..7ca7e6f
--- /dev/null
+++ b/src/classes/com/mchange/v2/management/ManagementUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.management;
+
+import javax.management.*;
+import java.util.Comparator;
+
+public class ManagementUtils
+{
+ public final static Comparator PARAM_INFO_COMPARATOR = new Comparator()
+ {
+ public int compare(Object a, Object b)
+ {
+ MBeanParameterInfo aa = (MBeanParameterInfo) a;
+ MBeanParameterInfo bb = (MBeanParameterInfo) b;
+ int out = aa.getType().compareTo(bb.getType());
+ if (out == 0)
+ {
+ out = aa.getName().compareTo(bb.getName());
+ if (out == 0)
+ {
+ String aDesc = aa.getDescription();
+ String bDesc = bb.getDescription();
+ if (aDesc == null && bDesc == null)
+ out = 0;
+ else if (aDesc == null)
+ out = -1;
+ else if (bDesc == null)
+ out = 1;
+ else
+ out = aDesc.compareTo(bDesc);
+ }
+ }
+ return out;
+ }
+ };
+
+ public final static Comparator OP_INFO_COMPARATOR = new Comparator()
+ {
+ public int compare(Object a, Object b)
+ {
+ MBeanOperationInfo aa = (MBeanOperationInfo) a;
+ MBeanOperationInfo bb = (MBeanOperationInfo) b;
+ String aName = aa.getName();
+ String bName = bb.getName();
+ int out = String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
+ if (out == 0)
+ {
+ if (aName.equals(bName))
+ {
+ MBeanParameterInfo[] aParams = aa.getSignature();
+ MBeanParameterInfo[] bParams = bb.getSignature();
+ if (aParams.length < bParams.length)
+ out = -1;
+ else if (aParams.length > bParams.length)
+ out = 1;
+ else
+ {
+ for (int i = 0, len = aParams.length; i < len; ++i)
+ {
+ out = PARAM_INFO_COMPARATOR.compare(aParams[i], bParams[i]);
+ if (out != 0)
+ break;
+ }
+ }
+ }
+ else
+ {
+ out = aName.compareTo(bName);
+ }
+ }
+ return out;
+ }
+ };
+}
diff --git a/src/classes/com/mchange/v2/naming/JavaBeanObjectFactory.java b/src/classes/com/mchange/v2/naming/JavaBeanObjectFactory.java
new file mode 100644
index 0000000..330cd1c
--- /dev/null
+++ b/src/classes/com/mchange/v2/naming/JavaBeanObjectFactory.java
@@ -0,0 +1,159 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.naming;
+
+import java.beans.*;
+import java.util.*;
+import javax.naming.*;
+import com.mchange.v2.log.*;
+import java.lang.reflect.Method;
+import javax.naming.spi.ObjectFactory;
+import com.mchange.v2.beans.BeansUtils;
+import com.mchange.v2.lang.Coerce;
+import com.mchange.v2.ser.SerializableUtils;
+
+public class JavaBeanObjectFactory implements ObjectFactory
+{
+ private final static MLogger logger = MLog.getLogger( JavaBeanObjectFactory.class );
+
+ final static Object NULL_TOKEN = new Object();
+
+ public Object getObjectInstance(Object refObj, Name name, Context nameCtx, Hashtable env)
+ throws Exception
+ {
+ if (refObj instanceof Reference)
+ {
+ Reference ref = (Reference) refObj;
+ Map refAddrsMap = new HashMap();
+ for (Enumeration e = ref.getAll(); e.hasMoreElements(); )
+ {
+ RefAddr addr = (RefAddr) e.nextElement();
+ refAddrsMap.put( addr.getType(), addr );
+ }
+ Class beanClass = Class.forName( ref.getClassName() );
+ Set refProps = null;
+ RefAddr refPropsRefAddr = (BinaryRefAddr) refAddrsMap.remove( JavaBeanReferenceMaker.REF_PROPS_KEY );
+ if ( refPropsRefAddr != null )
+ refProps = (Set) SerializableUtils.fromByteArray( (byte[]) refPropsRefAddr.getContent() );
+ Map propMap = createPropertyMap( beanClass, refAddrsMap );
+ return findBean( beanClass, propMap, refProps );
+ }
+ else
+ return null;
+ }
+
+ private Map createPropertyMap( Class beanClass, Map refAddrsMap ) throws Exception
+ {
+ BeanInfo bi = Introspector.getBeanInfo( beanClass );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+
+ Map out = new HashMap();
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ String propertyName = pd.getName();
+ Class propertyType = pd.getPropertyType();
+ Object addr = refAddrsMap.remove( propertyName );
+ if (addr != null)
+ {
+ if ( addr instanceof StringRefAddr )
+ {
+ String content = (String) ((StringRefAddr) addr).getContent();
+ if ( Coerce.canCoerce( propertyType ) )
+ out.put( propertyName, Coerce.toObject( content, propertyType ) );
+ else
+ {
+ PropertyEditor pe = BeansUtils.findPropertyEditor( pd );
+ pe.setAsText( content );
+ out.put( propertyName, pe.getValue() );
+ }
+ }
+ else if ( addr instanceof BinaryRefAddr )
+ {
+ byte[] content = (byte[]) ((BinaryRefAddr) addr).getContent();
+ if ( content.length == 0 )
+ out.put( propertyName, NULL_TOKEN ); //we use an empty array to mean null
+ else
+ out.put( propertyName, SerializableUtils.fromByteArray( content ) ); //this will handle "indirectly serialized" objects.
+ }
+ else
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning(this.getClass().getName() + " -- unknown RefAddr subclass: " + addr.getClass().getName());
+ }
+ }
+ }
+ for ( Iterator ii = refAddrsMap.keySet().iterator(); ii.hasNext(); )
+ {
+ String type = (String) ii.next();
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning(this.getClass().getName() + " -- RefAddr for unknown property: " + type);
+ }
+ return out;
+ }
+
+ protected Object createBlankInstance(Class beanClass) throws Exception
+ { return beanClass.newInstance(); }
+
+ protected Object findBean(Class beanClass, Map propertyMap, Set refProps ) throws Exception
+ {
+ Object bean = createBlankInstance( beanClass );
+ BeanInfo bi = Introspector.getBeanInfo( bean.getClass() );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ String propertyName = pd.getName();
+ Object value = propertyMap.get( propertyName );
+ Method setter = pd.getWriteMethod();
+ if (value != null)
+ {
+ if (setter != null)
+ setter.invoke( bean, new Object[] { (value == NULL_TOKEN ? null : value) } );
+ else
+ {
+ //System.err.println(this.getClass().getName() + ": Could not restore read-only property '" + propertyName + "'.");
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning(this.getClass().getName() + ": Could not restore read-only property '" + propertyName + "'.");
+ }
+ }
+ else
+ {
+ if (setter != null)
+ {
+ if (refProps == null || refProps.contains( propertyName ))
+ {
+ //System.err.println(this.getClass().getName() +
+ //": WARNING -- Expected writable property '" + propertyName + "' left at default value");
+ if (logger.isLoggable( MLevel.WARNING ))
+ logger.warning(this.getClass().getName() + " -- Expected writable property ''" + propertyName + "'' left at default value");
+ }
+ }
+ }
+ }
+
+ return bean;
+ }
+}
diff --git a/src/classes/com/mchange/v2/naming/JavaBeanReferenceMaker.java b/src/classes/com/mchange/v2/naming/JavaBeanReferenceMaker.java
new file mode 100644
index 0000000..d9b715e
--- /dev/null
+++ b/src/classes/com/mchange/v2/naming/JavaBeanReferenceMaker.java
@@ -0,0 +1,176 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.naming;
+
+import java.beans.*;
+import java.io.*;
+import java.util.*;
+import javax.naming.*;
+import com.mchange.v2.log.*;
+import java.lang.reflect.Method;
+import com.mchange.v2.lang.Coerce;
+import com.mchange.v2.beans.BeansUtils;
+import com.mchange.v2.ser.SerializableUtils;
+import com.mchange.v2.ser.IndirectPolicy;
+
+public class JavaBeanReferenceMaker implements ReferenceMaker
+{
+ private final static MLogger logger = MLog.getLogger( JavaBeanReferenceMaker.class );
+
+ final static String REF_PROPS_KEY = "com.mchange.v2.naming.JavaBeanReferenceMaker.REF_PROPS_KEY";
+
+ final static Object[] EMPTY_ARGS = new Object[0];
+
+ final static byte[] NULL_TOKEN_BYTES = new byte[0];
+
+ String factoryClassName = "com.mchange.v2.naming.JavaBeanObjectFactory";
+ String defaultFactoryClassLocation = null;
+
+ Set referenceProperties = new HashSet();
+
+ ReferenceIndirector indirector = new ReferenceIndirector();
+
+ public Hashtable getEnvironmentProperties()
+ { return indirector.getEnvironmentProperties(); }
+
+ public void setEnvironmentProperties( Hashtable environmentProperties )
+ { indirector.setEnvironmentProperties( environmentProperties ); }
+
+ public void setFactoryClassName(String factoryClassName)
+ { this.factoryClassName = factoryClassName; }
+
+ public String getFactoryClassName()
+ { return factoryClassName; }
+
+ public String getDefaultFactoryClassLocation()
+ { return defaultFactoryClassLocation; }
+
+ public void setDefaultFactoryClassLocation( String defaultFactoryClassLocation )
+ { this.defaultFactoryClassLocation = defaultFactoryClassLocation; }
+
+ public void addReferenceProperty( String propName )
+ { referenceProperties.add( propName ); }
+
+ public void removeReferenceProperty( String propName )
+ { referenceProperties.remove( propName ); }
+
+ public Reference createReference( Object bean )
+ throws NamingException
+ {
+ try
+ {
+ BeanInfo bi = Introspector.getBeanInfo( bean.getClass() );
+ PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ List refAddrs = new ArrayList();
+ String factoryClassLocation = defaultFactoryClassLocation;
+
+ boolean using_ref_props = referenceProperties.size() > 0;
+
+ // we only include this so that on dereference we are not surprised to find some properties missing
+ if (using_ref_props)
+ refAddrs.add( new BinaryRefAddr( REF_PROPS_KEY, SerializableUtils.toByteArray( referenceProperties ) ) );
+
+ for (int i = 0, len = pds.length; i < len; ++i)
+ {
+ PropertyDescriptor pd = pds[i];
+ String propertyName = pd.getName();
+ //System.err.println("Making Reference: " + propertyName);
+
+ if (using_ref_props && ! referenceProperties.contains( propertyName ))
+ {
+ //System.err.println("Not a ref_prop -- continuing.");
+ continue;
+ }
+
+ Class propertyType = pd.getPropertyType();
+ Method getter = pd.getReadMethod();
+ Method setter = pd.getWriteMethod();
+ if (getter != null && setter != null) //only use properties that are both readable and writable
+ {
+ Object val = getter.invoke( bean, EMPTY_ARGS );
+ //System.err.println( "val: " + val );
+ if (propertyName.equals("factoryClassLocation"))
+ {
+ if (String.class != propertyType)
+ throw new NamingException(this.getClass().getName() + " requires a factoryClassLocation property to be a string, " +
+ propertyType.getName() + " is not valid.");
+ factoryClassLocation = (String) val;
+ }
+
+ if (val == null)
+ {
+ RefAddr addMe = new BinaryRefAddr( propertyName, NULL_TOKEN_BYTES );
+ refAddrs.add( addMe );
+ }
+ else if ( Coerce.canCoerce( propertyType ) )
+ {
+ RefAddr addMe = new StringRefAddr( propertyName, String.valueOf( val ) );
+ refAddrs.add( addMe );
+ }
+ else //other Object properties
+ {
+ RefAddr addMe = null;
+ PropertyEditor pe = BeansUtils.findPropertyEditor( pd );
+ if (pe != null)
+ {
+ pe.setValue( val );
+ String textValue = pe.getAsText();
+ if (textValue != null)
+ addMe = new StringRefAddr( propertyName, textValue );
+ }
+ if (addMe == null) //property editor approach failed
+ addMe = new BinaryRefAddr( propertyName, SerializableUtils.toByteArray( val,
+ indirector,
+ IndirectPolicy.INDIRECT_ON_EXCEPTION ) );
+ refAddrs.add( addMe );
+ }
+ }
+ else
+ {
+// System.err.println(this.getClass().getName() +
+// ": Skipping " + propertyName + " because it is " + (setter == null ? "read-only." : "write-only."));
+
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning(this.getClass().getName() + ": Skipping " + propertyName +
+ " because it is " + (setter == null ? "read-only." : "write-only."));
+ }
+
+ }
+ Reference out = new Reference( bean.getClass().getName(), factoryClassName, factoryClassLocation );
+ for (Iterator ii = refAddrs.iterator(); ii.hasNext(); )
+ out.add( (RefAddr) ii.next() );
+ return out;
+ }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG && logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Exception trying to create Reference.", e);
+
+ throw new NamingException("Could not create reference from bean: " + e.toString() );
+ }
+ }
+
+}
+
diff --git a/src/classes/com/mchange/v2/naming/ReferenceIndirector.java b/src/classes/com/mchange/v2/naming/ReferenceIndirector.java
new file mode 100644
index 0000000..1d08402
--- /dev/null
+++ b/src/classes/com/mchange/v2/naming/ReferenceIndirector.java
@@ -0,0 +1,117 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.naming;
+
+import java.io.InvalidObjectException;
+import java.io.IOException;
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import com.mchange.v2.ser.Indirector;
+import com.mchange.v2.ser.IndirectlySerialized;
+
+public class ReferenceIndirector implements Indirector
+{
+ final static MLogger logger = MLog.getLogger( ReferenceIndirector.class );
+
+ Name name;
+ Name contextName;
+ Hashtable environmentProperties;
+
+ public Name getName()
+ { return name; }
+
+ public void setName( Name name )
+ { this.name = name; }
+
+ public Name getNameContextName()
+ { return contextName; }
+
+ public void setNameContextName( Name contextName )
+ { this.contextName = contextName; }
+
+ public Hashtable getEnvironmentProperties()
+ { return environmentProperties; }
+
+ public void setEnvironmentProperties( Hashtable environmentProperties )
+ { this.environmentProperties = environmentProperties; }
+
+ public IndirectlySerialized indirectForm( Object orig ) throws Exception
+ {
+ Reference ref = ((Referenceable) orig).getReference();
+ return new ReferenceSerialized( ref, name, contextName, environmentProperties );
+ }
+
+ private static class ReferenceSerialized implements IndirectlySerialized
+ {
+ Reference reference;
+ Name name;
+ Name contextName;
+ Hashtable env;
+
+ ReferenceSerialized( Reference reference,
+ Name name,
+ Name contextName,
+ Hashtable env )
+ {
+ this.reference = reference;
+ this.name = name;
+ this.contextName = contextName;
+ this.env = env;
+ }
+
+
+ public Object getObject() throws ClassNotFoundException, IOException
+ {
+ try
+ {
+ Context initialContext;
+ if ( env == null )
+ initialContext = new InitialContext();
+ else
+ initialContext = new InitialContext( env );
+
+ Context nameContext = null;
+ if ( contextName != null )
+ nameContext = (Context) initialContext.lookup( contextName );
+
+ return ReferenceableUtils.referenceToObject( reference, name, nameContext, env );
+ }
+ catch (NamingException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Failed to acquire the Context necessary to lookup an Object.", e );
+ throw new InvalidObjectException( "Failed to acquire the Context necessary to lookup an Object: " + e.toString() );
+ }
+ }
+ }
+}
diff --git a/src/classes/com/mchange/v2/naming/ReferenceMaker.java b/src/classes/com/mchange/v2/naming/ReferenceMaker.java
new file mode 100644
index 0000000..5a28a74
--- /dev/null
+++ b/src/classes/com/mchange/v2/naming/ReferenceMaker.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.naming;
+
+import javax.naming.Reference;
+import javax.naming.NamingException;
+
+public interface ReferenceMaker
+{
+ public Reference createReference( Object o )
+ throws NamingException;
+}
diff --git a/src/classes/com/mchange/v2/naming/ReferenceableUtils.java b/src/classes/com/mchange/v2/naming/ReferenceableUtils.java
new file mode 100644
index 0000000..57c6a93
--- /dev/null
+++ b/src/classes/com/mchange/v2/naming/ReferenceableUtils.java
@@ -0,0 +1,177 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.naming;
+
+import java.net.*;
+import javax.naming.*;
+import com.mchange.v2.log.MLevel;
+import com.mchange.v2.log.MLog;
+import com.mchange.v2.log.MLogger;
+import javax.naming.spi.ObjectFactory;
+import java.util.Hashtable;
+
+public final class ReferenceableUtils
+{
+ final static MLogger logger = MLog.getLogger( ReferenceableUtils.class );
+
+ /* don't worry -- References can have duplicate RefAddrs (I think!) */
+ final static String REFADDR_VERSION = "version";
+ final static String REFADDR_CLASSNAME = "classname";
+ final static String REFADDR_FACTORY = "factory";
+ final static String REFADDR_FACTORY_CLASS_LOCATION = "factoryClassLocation";
+ final static String REFADDR_SIZE = "size";
+
+ final static int CURRENT_REF_VERSION = 1;
+
+ /**
+ * A null string value in a Reference sometimes goes to the literal
+ * "null". Sigh. We convert this string to a Java null.
+ */
+ public static String literalNullToNull( String s )
+ {
+ if (s == null || "null".equals( s ))
+ return null;
+ else
+ return s;
+ }
+
+ public static Object referenceToObject( Reference ref, Name name, Context nameCtx, Hashtable env)
+ throws NamingException
+ {
+ try
+ {
+ String fClassName = ref.getFactoryClassName();
+ String fClassLocation = ref.getFactoryClassLocation();
+
+ ClassLoader cl;
+ if ( fClassLocation == null )
+ cl = ClassLoader.getSystemClassLoader();
+ else
+ {
+ URL u = new URL( fClassLocation );
+ cl = new URLClassLoader( new URL[] { u }, ClassLoader.getSystemClassLoader() );
+ }
+
+ Class fClass = Class.forName( fClassName, true, cl );
+ ObjectFactory of = (ObjectFactory) fClass.newInstance();
+ return of.getObjectInstance( ref, name, nameCtx, env );
+ }
+ catch ( Exception e )
+ {
+ if (Debug.DEBUG)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Could not resolve Reference to Object!", e);
+ }
+ NamingException ne = new NamingException("Could not resolve Reference to Object!");
+ ne.setRootCause( e );
+ throw ne;
+ }
+ }
+
+ /**
+ * @deprecated nesting references seemed useful until I realized that
+ * references are Serializable and can be stored in a BinaryRefAddr.
+ * Oops.
+ */
+ public static void appendToReference(Reference appendTo, Reference orig)
+ throws NamingException
+ {
+ int len = orig.size();
+ appendTo.add( new StringRefAddr( REFADDR_VERSION, String.valueOf( CURRENT_REF_VERSION ) ) );
+ appendTo.add( new StringRefAddr( REFADDR_CLASSNAME, orig.getClassName() ) );
+ appendTo.add( new StringRefAddr( REFADDR_FACTORY, orig.getFactoryClassName() ) );
+ appendTo.add( new StringRefAddr( REFADDR_FACTORY_CLASS_LOCATION,
+ orig.getFactoryClassLocation() ) );
+ appendTo.add( new StringRefAddr( REFADDR_SIZE, String.valueOf(len) ) );
+ for (int i = 0; i < len; ++i)
+ appendTo.add( orig.get(i) );
+ }
+
+ /**
+ * @deprecated nesting references seemed useful until I realized that
+ * references are Serializable and can be stored in a BinaryRefAddr.
+ * Oops.
+ */
+ public static ExtractRec extractNestedReference(Reference extractFrom, int index)
+ throws NamingException
+ {
+ try
+ {
+ int version = Integer.parseInt((String) extractFrom.get(index++).getContent());
+ if (version == 1)
+ {
+ String className = (String) extractFrom.get(index++).getContent();
+ String factoryClassName = (String) extractFrom.get(index++).getContent();
+ String factoryClassLocation = (String) extractFrom.get(index++).getContent();
+
+ Reference outRef = new Reference( className,
+ factoryClassName,
+ factoryClassLocation );
+ int size = Integer.parseInt((String) extractFrom.get(index++).getContent());
+ for (int i = 0; i < size; ++i)
+ outRef.add( extractFrom.get( index++ ) );
+ return new ExtractRec( outRef, index );
+ }
+ else
+ throw new NamingException("Bad version of nested reference!!!");
+ }
+ catch (NumberFormatException e)
+ {
+ if (Debug.DEBUG)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Version or size nested reference was not a number!!!", e);
+ }
+ throw new NamingException("Version or size nested reference was not a number!!!");
+ }
+ }
+
+ /**
+ * @deprecated nesting references seemed useful until I realized that
+ * references are Serializable and can be stored in a BinaryRefAddr.
+ * Oops.
+ */
+ public static class ExtractRec
+ {
+ public Reference ref;
+
+ /**
+ * return the first RefAddr index that the function HAS NOT read to
+ * extract the reference.
+ */
+ public int index;
+
+ private ExtractRec(Reference ref, int index)
+ {
+ this.ref = ref;
+ this.index = index;
+ }
+ }
+
+ private ReferenceableUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/BasicResourcePool.java b/src/classes/com/mchange/v2/resourcepool/BasicResourcePool.java
new file mode 100644
index 0000000..80ec271
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/BasicResourcePool.java
@@ -0,0 +1,2085 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.*;
+import com.mchange.v2.async.*;
+import com.mchange.v2.log.*;
+import com.mchange.v2.lang.ThreadUtils;
+import com.mchange.v2.util.ResourceClosedException;
+
+class BasicResourcePool implements ResourcePool
+{
+ private final static MLogger logger = MLog.getLogger( BasicResourcePool.class );
+
+ final static int AUTO_CULL_FREQUENCY_DIVISOR = 4;
+ final static int AUTO_MAX_CULL_FREQUENCY = (15 * 60 * 1000); //15 mins
+ final static int AUTO_MIN_CULL_FREQUENCY = (1 * 1000); //15 mins
+
+
+ //XXX: temporary -- for selecting between AcquireTask types
+ // remove soon, and use only ScatteredAcquireTask,
+ // presuming no problems appear
+ final static String USE_SCATTERED_ACQUIRE_TASK_KEY = "com.mchange.v2.resourcepool.experimental.useScatteredAcquireTask";
+ final static boolean USE_SCATTERED_ACQUIRE_TASK;
+ static
+ {
+ String checkScattered = com.mchange.v2.cfg.MultiPropertiesConfig.readVmConfig().getProperty(USE_SCATTERED_ACQUIRE_TASK_KEY);
+ if (checkScattered != null && checkScattered.trim().toLowerCase().equals("true"))
+ {
+ USE_SCATTERED_ACQUIRE_TASK = true;
+ if ( logger.isLoggable( MLevel.INFO ) )
+ logger.info(BasicResourcePool.class.getName() + " using experimental ScatteredAcquireTask.");
+ }
+ else
+ USE_SCATTERED_ACQUIRE_TASK = false;
+ }
+ // end temporary switch between acquire task types
+
+ //MT: unchanged post c'tor
+ final Manager mgr;
+
+ final int start;
+ final int min;
+ final int max;
+ final int inc;
+
+ final int num_acq_attempts;
+ final int acq_attempt_delay;
+
+ final long check_idle_resources_delay; //milliseconds
+ final long max_resource_age; //milliseconds
+ final long max_idle_time; //milliseconds
+ final long excess_max_idle_time; //milliseconds
+ final long destroy_unreturned_resc_time; //milliseconds
+ final long expiration_enforcement_delay; //milliseconds
+
+ final boolean break_on_acquisition_failure;
+ final boolean debug_store_checkout_exceptions;
+
+ final long pool_start_time = System.currentTimeMillis();
+
+ //MT: not-reassigned, thread-safe, and independent
+ final BasicResourcePoolFactory factory;
+ final AsynchronousRunner taskRunner;
+ final RunnableQueue asyncEventQueue;
+ final ResourcePoolEventSupport rpes;
+
+ //MT: protected by this' lock
+ Timer cullAndIdleRefurbishTimer;
+ TimerTask cullTask;
+ TimerTask idleRefurbishTask;
+ HashSet acquireWaiters = new HashSet();
+ HashSet otherWaiters = new HashSet();
+
+ int pending_acquires;
+ int pending_removes;
+
+ int target_pool_size;
+
+ /* keys are all valid, managed resources, value is a PunchCard */
+ HashMap managed = new HashMap();
+
+ /* all valid, managed resources currently available for checkout */
+ LinkedList unused = new LinkedList();
+
+ /* resources which have been invalidated somehow, but which are */
+ /* still checked out and in use. */
+ HashSet excluded = new HashSet();
+
+ Map formerResources = new WeakHashMap();
+
+ Set idleCheckResources = new HashSet();
+
+ boolean force_kill_acquires = false;
+
+ boolean broken = false;
+
+// long total_acquired = 0;
+
+ long failed_checkins = 0;
+ long failed_checkouts = 0;
+ long failed_idle_tests = 0;
+
+ Throwable lastCheckinFailure = null;
+ Throwable lastCheckoutFailure = null;
+ Throwable lastIdleTestFailure = null;
+ Throwable lastResourceTestFailure = null;
+
+ Throwable lastAcquisitionFailiure = null;
+
+ //DEBUG only!
+ Object exampleResource;
+
+ public long getStartTime()
+ { return pool_start_time; }
+
+ public long getUpTime()
+ { return System.currentTimeMillis() - pool_start_time; }
+
+ public synchronized long getNumFailedCheckins()
+ { return failed_checkins; }
+
+ public synchronized long getNumFailedCheckouts()
+ { return failed_checkouts; }
+
+ public synchronized long getNumFailedIdleTests()
+ { return failed_idle_tests; }
+
+ public synchronized Throwable getLastCheckinFailure()
+ { return lastCheckinFailure; }
+
+ //must be called from a pre-existing sync'ed block
+ private void setLastCheckinFailure(Throwable t)
+ {
+ assert ( Thread.holdsLock(this));
+
+ this.lastCheckinFailure = t;
+ this.lastResourceTestFailure = t;
+ }
+
+ public synchronized Throwable getLastCheckoutFailure()
+ { return lastCheckoutFailure; }
+
+ //must be called from a pre-existing sync'ed block
+ private void setLastCheckoutFailure(Throwable t)
+ {
+ assert ( Thread.holdsLock(this));
+
+ this.lastCheckoutFailure = t;
+ this.lastResourceTestFailure = t;
+ }
+
+ public synchronized Throwable getLastIdleCheckFailure()
+ { return lastIdleTestFailure; }
+
+ //must be called from a pre-existing sync'ed block
+ private void setLastIdleCheckFailure(Throwable t)
+ {
+ assert ( Thread.holdsLock(this));
+
+ this.lastIdleTestFailure = t;
+ this.lastResourceTestFailure = t;
+ }
+
+ public synchronized Throwable getLastResourceTestFailure()
+ { return lastResourceTestFailure; }
+
+ public synchronized Throwable getLastAcquisitionFailure()
+ { return lastAcquisitionFailiure; }
+
+ // ought not be called while holding this' lock
+ private synchronized void setLastAcquisitionFailure( Throwable t )
+ { this.lastAcquisitionFailiure = t; }
+
+ public synchronized int getNumCheckoutWaiters()
+ { return acquireWaiters.size(); }
+
+ private void addToFormerResources( Object resc )
+ { formerResources.put( resc, null ); }
+
+ private boolean isFormerResource( Object resc )
+ { return formerResources.keySet().contains( resc ); }
+
+ /**
+ * @param factory may be null
+ */
+ public BasicResourcePool(Manager mgr,
+ int start,
+ int min,
+ int max,
+ int inc,
+ int num_acq_attempts,
+ int acq_attempt_delay,
+ long check_idle_resources_delay,
+ long max_resource_age,
+ long max_idle_time,
+ long excess_max_idle_time,
+ long destroy_unreturned_resc_time,
+ long expiration_enforcement_delay,
+ boolean break_on_acquisition_failure,
+ boolean debug_store_checkout_exceptions,
+ AsynchronousRunner taskRunner,
+ RunnableQueue asyncEventQueue,
+ Timer cullAndIdleRefurbishTimer,
+ BasicResourcePoolFactory factory)
+ throws ResourcePoolException
+ {
+ try
+ {
+ this.mgr = mgr;
+ this.start = start;
+ this.min = min;
+ this.max = max;
+ this.inc = inc;
+ this.num_acq_attempts = num_acq_attempts;
+ this.acq_attempt_delay = acq_attempt_delay;
+ this.check_idle_resources_delay = check_idle_resources_delay;
+ this.max_resource_age = max_resource_age;
+ this.max_idle_time = max_idle_time;
+ this.excess_max_idle_time = excess_max_idle_time;
+ this.destroy_unreturned_resc_time = destroy_unreturned_resc_time;
+ //this.expiration_enforcement_delay = expiration_enforcement_delay; -- set up below
+ this.break_on_acquisition_failure = break_on_acquisition_failure;
+ this.debug_store_checkout_exceptions = (debug_store_checkout_exceptions && destroy_unreturned_resc_time > 0);
+ this.taskRunner = taskRunner;
+ this.asyncEventQueue = asyncEventQueue;
+ this.cullAndIdleRefurbishTimer = cullAndIdleRefurbishTimer;
+ this.factory = factory;
+
+ this.pending_acquires = 0;
+ this.pending_removes = 0;
+
+ this.target_pool_size = Math.max(start, min);
+
+ if (asyncEventQueue != null)
+ this.rpes = new ResourcePoolEventSupport(this);
+ else
+ this.rpes = null;
+
+ //start acquiring our initial resources
+ ensureStartResources();
+
+ if (mustEnforceExpiration())
+ {
+ if (expiration_enforcement_delay <= 0)
+ this.expiration_enforcement_delay = automaticExpirationEnforcementDelay();
+ else
+ this.expiration_enforcement_delay = expiration_enforcement_delay;
+
+ this.cullTask = new CullTask();
+ //System.err.println("minExpirationTime(): " + minExpirationTime());
+ //System.err.println("this.expiration_enforcement_delay: " + this.expiration_enforcement_delay);
+ cullAndIdleRefurbishTimer.schedule( cullTask, minExpirationTime(), this.expiration_enforcement_delay );
+ }
+ else
+ this.expiration_enforcement_delay = expiration_enforcement_delay;
+
+ //System.err.println("this.check_idle_resources_delay: " + this.check_idle_resources_delay);
+ if (check_idle_resources_delay > 0)
+ {
+ this.idleRefurbishTask = new CheckIdleResourcesTask();
+ cullAndIdleRefurbishTimer.schedule( idleRefurbishTask,
+ check_idle_resources_delay,
+ check_idle_resources_delay );
+ }
+
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.finer( this + " config: [start -> " + this.start + "; min -> " + this.min + "; max -> " + this.max + "; inc -> " + this.inc +
+ "; num_acq_attempts -> " + this.num_acq_attempts + "; acq_attempt_delay -> " + this.acq_attempt_delay +
+ "; check_idle_resources_delay -> " + this.check_idle_resources_delay + "; mox_resource_age -> " + this.max_resource_age +
+ "; max_idle_time -> " + this.max_idle_time + "; excess_max_idle_time -> " + this.excess_max_idle_time +
+ "; destroy_unreturned_resc_time -> " + this.destroy_unreturned_resc_time +
+ "; expiration_enforcement_delay -> " + this.expiration_enforcement_delay +
+ "; break_on_acquisition_failure -> " + this.break_on_acquisition_failure +
+ "; debug_store_checkout_exceptions -> " + this.debug_store_checkout_exceptions +
+ "]");
+
+ }
+ catch (Exception e)
+ {
+// if ( logger.isLoggable( MLevel.WARNING) )
+// logger.log( MLevel.WARNING, "Could not create resource pool due to Exception!", e );
+
+ throw ResourcePoolUtils.convertThrowable( e );
+ }
+ }
+
+// private boolean timerRequired()
+// { return mustEnforceExpiration() || mustTestIdleResources(); }
+
+ // no need to sync
+ private boolean mustTestIdleResources()
+ { return check_idle_resources_delay > 0; }
+
+ // no need to sync
+ private boolean mustEnforceExpiration()
+ {
+ return
+ max_resource_age > 0 ||
+ max_idle_time > 0 ||
+ excess_max_idle_time > 0 ||
+ destroy_unreturned_resc_time > 0;
+ }
+
+ // no need to sync
+ private long minExpirationTime()
+ {
+ long out = Long.MAX_VALUE;
+ if (max_resource_age > 0)
+ out = Math.min( out, max_resource_age );
+ if (max_idle_time > 0)
+ out = Math.min( out, max_idle_time );
+ if (excess_max_idle_time > 0)
+ out = Math.min( out, excess_max_idle_time );
+ if (destroy_unreturned_resc_time > 0)
+ out = Math.min( out, destroy_unreturned_resc_time );
+ return out;
+ }
+
+ private long automaticExpirationEnforcementDelay()
+ {
+ long out = minExpirationTime();
+ out /= AUTO_CULL_FREQUENCY_DIVISOR;
+ out = Math.min( out, AUTO_MAX_CULL_FREQUENCY );
+ out = Math.max( out, AUTO_MIN_CULL_FREQUENCY );
+ return out;
+ }
+
+ public long getEffectiveExpirationEnforcementDelay()
+ { return expiration_enforcement_delay; }
+
+ private synchronized boolean isBroken()
+ { return broken; }
+
+ // no need to sync
+ private boolean supportsEvents()
+ { return asyncEventQueue != null; }
+
+ public Object checkoutResource()
+ throws ResourcePoolException, InterruptedException
+ {
+ try { return checkoutResource( 0 ); }
+ catch (TimeoutException e)
+ {
+ //this should never happen
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Huh??? TimeoutException with no timeout set!!!", e);
+
+ throw new ResourcePoolException("Huh??? TimeoutException with no timeout set!!!", e);
+ }
+ }
+
+ // must be called from synchronized method, idempotent
+ private void _recheckResizePool()
+ {
+ assert Thread.holdsLock(this);
+
+ if (! broken)
+ {
+ int msz = managed.size();
+ //int expected_size = msz + pending_acquires - pending_removes;
+
+// System.err.print("target: " + target_pool_size);
+// System.err.println(" (msz: " + msz + "; pending_acquires: " + pending_acquires + "; pending_removes: " + pending_removes + ')');
+ //new Exception( "_recheckResizePool() STACK TRACE" ).printStackTrace();
+
+ int shrink_count;
+ int expand_count;
+
+ if ((shrink_count = msz - pending_removes - target_pool_size) > 0)
+ shrinkPool( shrink_count );
+ else if ((expand_count = target_pool_size - (msz + pending_acquires)) > 0)
+ expandPool( expand_count );
+ }
+ }
+
+ private synchronized void incrementPendingAcquires()
+ {
+ ++pending_acquires;
+
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("incremented pending_acquires: " + pending_acquires);
+ //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
+ }
+
+ private synchronized void incrementPendingRemoves()
+ {
+ ++pending_removes;
+
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("incremented pending_removes: " + pending_removes);
+ //new Exception("REMOVE SOURCE STACK TRACE").printStackTrace();
+ }
+
+ private synchronized void decrementPendingAcquires()
+ {
+ --pending_acquires;
+
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("decremented pending_acquires: " + pending_acquires);
+ //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
+ }
+
+ private synchronized void decrementPendingRemoves()
+ {
+ --pending_removes;
+
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("decremented pending_removes: " + pending_removes);
+ //new Exception("ACQUIRE SOURCE STACK TRACE").printStackTrace();
+ }
+
+ // idempotent
+ private synchronized void recheckResizePool()
+ { _recheckResizePool(); }
+
+ // must be called from synchronized method
+ private void expandPool(int count)
+ {
+ assert Thread.holdsLock(this);
+
+ // XXX: temporary switch -- assuming no problems appear, we'll get rid of AcquireTask
+ // in favor of ScatteredAcquireTask
+ if ( USE_SCATTERED_ACQUIRE_TASK )
+ {
+ for (int i = 0; i < count; ++i)
+ taskRunner.postRunnable( new ScatteredAcquireTask() );
+ }
+ else
+ {
+ for (int i = 0; i < count; ++i)
+ taskRunner.postRunnable( new AcquireTask() );
+ }
+ }
+
+ // must be called from synchronized method
+ private void shrinkPool(int count)
+ {
+ assert Thread.holdsLock(this);
+
+ for (int i = 0; i < count; ++i)
+ taskRunner.postRunnable( new RemoveTask() );
+ }
+
+ /*
+ * This function recursively calls itself... under nonpathological
+ * situations, it shouldn't be a problem, but if resources can never
+ * successfully check out for some reason, we might blow the stack...
+ *
+ * by the semantics of wait(), a timeout of zero means forever.
+ */
+ public Object checkoutResource( long timeout )
+ throws TimeoutException, ResourcePoolException, InterruptedException
+ {
+ Object resc = prelimCheckoutResource( timeout );
+
+ boolean refurb = attemptRefurbishResourceOnCheckout( resc );
+
+ synchronized( this )
+ {
+ if (!refurb)
+ {
+ removeResource( resc );
+ ensureMinResources();
+ resc = null;
+ }
+ else
+ {
+ asyncFireResourceCheckedOut( resc, managed.size(), unused.size(), excluded.size() );
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+
+ PunchCard card = (PunchCard) managed.get( resc );
+ if (card == null) //the resource has been removed!
+ {
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.fine("Resource " + resc + " was removed from the pool while it was being checked out " +
+ " or refurbished for checkout.");
+ resc = null;
+ }
+ else
+ {
+ card.checkout_time = System.currentTimeMillis();
+ if (debug_store_checkout_exceptions)
+ card.checkoutStackTraceException = new Exception("DEBUG ONLY: Overdue resource check-out stack trace.");
+ }
+ }
+ }
+
+ // best to do the recheckout while we don't hold this'
+ // lock, so we don't refurbish-on-checkout while holding.
+ if (resc == null)
+ return checkoutResource( timeout );
+ else
+ return resc;
+ }
+
+ private synchronized Object prelimCheckoutResource( long timeout )
+ throws TimeoutException, ResourcePoolException, InterruptedException
+ {
+ try
+ {
+ ensureNotBroken();
+
+ int available = unused.size();
+ if (available == 0)
+ {
+ int msz = managed.size();
+
+ if (msz < max)
+ {
+ // to cover all the load, we need the current size, plus those waiting already for acquisition,
+ // plus the current client
+ int desired_target = msz + acquireWaiters.size() + 1;
+
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "acquire test -- pool size: " + msz + "; target_pool_size: " + target_pool_size + "; desired target? " + desired_target);
+
+ if (desired_target >= target_pool_size)
+ {
+ //make sure we don't grab less than inc Connections at a time, if we can help it.
+ desired_target = Math.max(desired_target, target_pool_size + inc);
+
+ //make sure our target is within its bounds
+ target_pool_size = Math.max( Math.min( max, desired_target ), min );
+
+ _recheckResizePool();
+ }
+ }
+ else
+ {
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "acquire test -- pool is already maxed out. [managed: " + msz + "; max: " + max + "]");
+ }
+
+ awaitAvailable(timeout); //throws timeout exception
+ }
+
+ Object resc = unused.get(0);
+
+ // this is a hack -- but "doing it right" adds a lot of complexity, and collisions between
+ // an idle check and a checkout should be relatively rare. anyway, it should work just fine.
+ if ( idleCheckResources.contains( resc ) )
+ {
+ if (Debug.DEBUG && logger.isLoggable( MLevel.FINER))
+ logger.log( MLevel.FINER,
+ "Resource we want to check out is in idleCheck! (waiting until idle-check completes.) [" + this + "]");
+
+ // we'll move remove() to after the if, so we don't have to add back
+ // unused.add(0, resc );
+
+ // we'll wait for "something to happen" -- probably an idle check to
+ // complete -- then we'll try again and hope for the best.
+ Thread t = Thread.currentThread();
+ try
+ {
+ otherWaiters.add ( t );
+ this.wait( timeout );
+ ensureNotBroken();
+ }
+ finally
+ { otherWaiters.remove( t ); }
+ return prelimCheckoutResource( timeout );
+ }
+ else if ( shouldExpire( resc ) )
+ {
+ removeResource( resc );
+ ensureMinResources();
+ return prelimCheckoutResource( timeout );
+ }
+ else
+ {
+ unused.remove(0);
+ return resc;
+ }
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //System.err.println(this + " -- the pool was found to be closed or broken during an attempt to check out a resource.");
+ //e.printStackTrace();
+ if (logger.isLoggable( MLevel.SEVERE ))
+ logger.log( MLevel.SEVERE, this + " -- the pool was found to be closed or broken during an attempt to check out a resource.", e );
+
+ this.unexpectedBreak();
+ throw e;
+ }
+ catch ( InterruptedException e )
+ {
+ // System.err.println(this + " -- an attempt to checkout a resource was interrupted: some other thread " +
+ // "must have either interrupted the Thread attempting checkout, or close() was called on the pool.");
+ // e.printStackTrace();
+ if (broken)
+ {
+ if (logger.isLoggable( MLevel.FINER ))
+ logger.log(MLevel.FINER,
+ this + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. " +
+ "[Thread: " + Thread.currentThread().getName() + ']',
+ e );
+ else if (logger.isLoggable( MLevel.INFO ))
+ logger.log(MLevel.INFO,
+ this + " -- an attempt to checkout a resource was interrupted, because the pool is now closed. " +
+ "[Thread: " + Thread.currentThread().getName() + ']');
+ }
+ else
+ {
+ if (logger.isLoggable( MLevel.WARNING ))
+ {
+ logger.log(MLevel.WARNING,
+ this + " -- an attempt to checkout a resource was interrupted, and the pool is still live: some other thread " +
+ "must have either interrupted the Thread attempting checkout!",
+ e );
+ }
+ }
+ throw e;
+ }
+ }
+
+ public synchronized void checkinResource( Object resc )
+ throws ResourcePoolException
+ {
+ try
+ {
+ //we permit straggling resources to be checked in
+ //without exception even if we are broken
+ if (managed.keySet().contains(resc))
+ doCheckinManaged( resc );
+ else if (excluded.contains(resc))
+ doCheckinExcluded( resc );
+ else if ( isFormerResource(resc) )
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.finer("Resource " + resc + " checked-in after having been checked-in already, or checked-in after " +
+ " having being destroyed for being checked-out too long.");
+ }
+ else
+ throw new ResourcePoolException("ResourcePool" + (broken ? " [BROKEN!]" : "") + ": Tried to check-in a foreign resource!");
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+// System.err.println(this +
+// " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.");
+// e.printStackTrace();
+
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE,
+ this + " - checkinResource( ... ) -- even broken pools should allow checkins without exception. probable resource pool bug.",
+ e);
+
+ this.unexpectedBreak();
+ throw e;
+ }
+ }
+
+ public synchronized void checkinAll()
+ throws ResourcePoolException
+ {
+ try
+ {
+ Set checkedOutNotExcluded = new HashSet( managed.keySet() );
+ checkedOutNotExcluded.removeAll( unused );
+ for (Iterator ii = checkedOutNotExcluded.iterator(); ii.hasNext(); )
+ doCheckinManaged( ii.next() );
+ for (Iterator ii = excluded.iterator(); ii.hasNext(); )
+ doCheckinExcluded( ii.next() );
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+// System.err.println(this +
+// " - checkinAll() -- even broken pools should allow checkins without exception. probable resource pool bug.");
+// e.printStackTrace();
+
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE,
+ this + " - checkinAll() -- even broken pools should allow checkins without exception. probable resource pool bug.",
+ e );
+
+ this.unexpectedBreak();
+ throw e;
+ }
+ }
+
+ public synchronized int statusInPool( Object resc )
+ throws ResourcePoolException
+ {
+ try
+ {
+ if ( unused.contains( resc ) )
+ return KNOWN_AND_AVAILABLE;
+ else if ( managed.keySet().contains( resc ) || excluded.contains( resc ) )
+ return KNOWN_AND_CHECKED_OUT;
+ else
+ return UNKNOWN_OR_PURGED;
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+// e.printStackTrace();
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, "Apparent pool break.", e );
+ this.unexpectedBreak();
+ throw e;
+ }
+ }
+
+ public synchronized void markBroken(Object resc)
+ {
+ try
+ {
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, "Resource " + resc + " marked broken by pool (" + this + ").");
+
+ _markBroken( resc );
+ ensureMinResources();
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, "Apparent pool break.", e );
+ this.unexpectedBreak();
+ }
+ }
+
+ //min is immutable, no need to synchronize
+ public int getMinPoolSize()
+ { return min; }
+
+ //max is immutable, no need to synchronize
+ public int getMaxPoolSize()
+ { return max; }
+
+ public synchronized int getPoolSize()
+ throws ResourcePoolException
+ { return managed.size(); }
+
+// //i don't think i like the async, no-guarantees approach
+// public synchronized void requestResize( int req_sz )
+// {
+// if (req_sz > max)
+// req_sz = max;
+// else if (req_sz < min)
+// req_sz = min;
+// int sz = managed.size();
+// if (req_sz > sz)
+// postAcquireUntil( req_sz );
+// else if (req_sz < sz)
+// postRemoveTowards( req_sz );
+// }
+
+ public synchronized int getAvailableCount()
+ { return unused.size(); }
+
+ public synchronized int getExcludedCount()
+ { return excluded.size(); }
+
+ public synchronized int getAwaitingCheckinCount()
+ { return managed.size() - unused.size() + excluded.size(); }
+
+ public synchronized void resetPool()
+ {
+ try
+ {
+ for (Iterator ii = cloneOfManaged().keySet().iterator(); ii.hasNext();)
+ markBrokenNoEnsureMinResources(ii.next());
+ ensureMinResources();
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, "Apparent pool break.", e );
+ this.unexpectedBreak();
+ }
+ }
+
+ public synchronized void close()
+ throws ResourcePoolException
+ {
+ //we permit closes when we are already broken, so
+ //that resources that were checked out when the break
+ //occured can still be cleaned up
+ close( true );
+ }
+
+ public void finalize() throws Throwable
+ {
+ //obviously, clients mustn't rely on finalize,
+ //but must close pools ASAP after use.
+ //System.err.println("finalizing..." + this);
+
+ if (! broken )
+ this.close();
+ }
+
+ //no need to sync
+ public void addResourcePoolListener(ResourcePoolListener rpl)
+ {
+ if ( ! supportsEvents() )
+ throw new RuntimeException(this + " does not support ResourcePoolEvents. " +
+ "Probably it was constructed by a BasicResourceFactory configured not to support such events.");
+ else
+ rpes.addResourcePoolListener(rpl);
+ }
+
+ //no need to sync
+ public void removeResourcePoolListener(ResourcePoolListener rpl)
+ {
+ if ( ! supportsEvents() )
+ throw new RuntimeException(this + " does not support ResourcePoolEvents. " +
+ "Probably it was constructed by a BasicResourceFactory configured not to support such events.");
+ else
+ rpes.removeResourcePoolListener(rpl);
+ }
+
+ private synchronized boolean isForceKillAcquiresPending()
+ { return force_kill_acquires; }
+
+ // this is designed as a response to a determination that our resource source is down.
+ // rather than declaring ourselves broken in this case (as we did previously), we
+ // kill all pending acquisition attempts, but retry on new acqusition requests.
+ private synchronized void forceKillAcquires() throws InterruptedException
+ {
+ Thread t = Thread.currentThread();
+
+ try
+ {
+ force_kill_acquires = true;
+ this.notifyAll(); //wake up any threads waiting on an acquire, and force them all to die.
+ while (acquireWaiters.size() > 0) //we want to let all the waiting acquires die before we unset force_kill_acquires
+ {
+ otherWaiters.add( t );
+ this.wait();
+ }
+ force_kill_acquires = false;
+ }
+ finally
+ { otherWaiters.remove( t ); }
+ }
+
+ //same as close(), but we do not destroy checked out
+ //resources
+ private synchronized void unexpectedBreak()
+ {
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, this + " -- Unexpectedly broken!!!", new ResourcePoolException("Unexpected Break Stack Trace!") );
+ close( false );
+ }
+
+ // no need to sync
+ private boolean canFireEvents()
+ { return ( asyncEventQueue != null && !isBroken() ); }
+
+ // no need to sync
+ private void asyncFireResourceAcquired( final Object resc,
+ final int pool_size,
+ final int available_size,
+ final int removed_but_unreturned_size )
+ {
+ if ( canFireEvents() )
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {rpes.fireResourceAcquired(resc, pool_size, available_size, removed_but_unreturned_size);}
+ };
+ asyncEventQueue.postRunnable(r);
+ }
+ }
+
+ // no need to sync
+ private void asyncFireResourceCheckedIn( final Object resc,
+ final int pool_size,
+ final int available_size,
+ final int removed_but_unreturned_size )
+ {
+ if ( canFireEvents() )
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {rpes.fireResourceCheckedIn(resc, pool_size, available_size, removed_but_unreturned_size);}
+ };
+ asyncEventQueue.postRunnable(r);
+ }
+ }
+
+ // no need to sync
+ private void asyncFireResourceCheckedOut( final Object resc,
+ final int pool_size,
+ final int available_size,
+ final int removed_but_unreturned_size )
+ {
+ if ( canFireEvents() )
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {rpes.fireResourceCheckedOut(resc,pool_size,available_size,removed_but_unreturned_size);}
+ };
+ asyncEventQueue.postRunnable(r);
+ }
+ }
+
+ // no need to sync
+ private void asyncFireResourceRemoved( final Object resc,
+ final boolean checked_out_resource,
+ final int pool_size,
+ final int available_size,
+ final int removed_but_unreturned_size )
+ {
+ if ( canFireEvents() )
+ {
+ //System.err.println("ASYNC RSRC REMOVED");
+ //new Exception().printStackTrace();
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {
+ rpes.fireResourceRemoved(resc, checked_out_resource,
+ pool_size,available_size,removed_but_unreturned_size);
+ }
+ };
+ asyncEventQueue.postRunnable(r);
+ }
+ }
+
+ // needn't be called from a sync'ed method
+ private void destroyResource(final Object resc)
+ { destroyResource( resc, false ); }
+
+ // needn't be called from a sync'ed method
+ private void destroyResource(final Object resc, boolean synchronous)
+ {
+ class DestroyResourceTask implements Runnable
+ {
+ public void run()
+ {
+ try
+ {
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log(MLevel.FINER, "Preparing to destroy resource: " + resc);
+
+ mgr.destroyResource(resc);
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX && logger.isLoggable( MLevel.FINER ))
+ logger.log(MLevel.FINER, "Successfully destroyed resource: " + resc);
+ }
+ catch ( Exception e )
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "Failed to destroy resource: " + resc, e );
+
+ // System.err.println("Failed to destroy resource: " + resc);
+ // e.printStackTrace();
+ }
+ }
+ }
+
+ Runnable r = new DestroyResourceTask();
+ if ( synchronous || broken ) //if we're broken, our taskRunner may be dead, so we destroy synchronously
+ {
+ if ( logger.isLoggable(MLevel.FINEST) && !broken && Boolean.TRUE.equals( ThreadUtils.reflectiveHoldsLock( this ) ) )
+ logger.log( MLevel.FINEST,
+ this + ": Destroyiong a resource on an active pool, synchronousy while holding pool's lock! " +
+ "(not a bug, but a potential bottleneck... is there a good reason for this?)",
+ new Exception("DEBUG STACK TRACE") );
+
+ r.run();
+ }
+ else
+ {
+ try { taskRunner.postRunnable( r ); }
+ catch (Exception e)
+ {
+ if (logger.isLoggable(MLevel.FINER))
+ logger.log( MLevel.FINER,
+ "AsynchronousRunner refused to accept task to destroy resource. " +
+ "It is probably shared, and has probably been closed underneath us. " +
+ "Reverting to synchronous destruction. This is not usually a problem.",
+ e );
+ destroyResource( resc, true );
+ }
+ }
+ }
+
+
+ //this method SHOULD NOT be invoked from a synchronized
+ //block!!!!
+ private void doAcquire() throws Exception
+ {
+ assert !Thread.holdsLock( this );
+
+ Object resc = mgr.acquireResource(); //note we acquire the resource while we DO NOT hold the pool's lock!
+
+ boolean destroy = false;
+ int msz;
+
+ synchronized(this) //assimilate resc if we do need it
+ {
+// ++total_acquired;
+
+// if (logger.isLoggable( MLevel.FINER))
+// logger.log(MLevel.FINER, "acquired new resource, total_acquired: " + total_acquired);
+
+ msz = managed.size();
+ if (msz < target_pool_size)
+ assimilateResource(resc);
+ else
+ destroy = true;
+ }
+
+ if (destroy)
+ {
+ mgr.destroyResource( resc ); //destroy resc if superfluous, without holding the pool's lock
+ if (logger.isLoggable( MLevel.FINER))
+ logger.log(MLevel.FINER, "destroying overacquired resource: " + resc);
+ }
+
+ }
+
+ public synchronized void setPoolSize( int sz ) throws ResourcePoolException
+ {
+ try
+ {
+ setTargetPoolSize( sz );
+ while ( managed.size() != sz )
+ this.wait();
+ }
+ catch (Exception e)
+ {
+ String msg = "An exception occurred while trying to set the pool size!";
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.log( MLevel.FINER, msg, e );
+ throw ResourcePoolUtils.convertThrowable( msg, e );
+ }
+ }
+
+ public synchronized void setTargetPoolSize(int sz)
+ {
+ if (sz > max)
+ {
+ throw new IllegalArgumentException("Requested size [" + sz +
+ "] is greater than max [" + max +
+ "].");
+ }
+ else if (sz < min)
+ {
+ throw new IllegalArgumentException("Requested size [" + sz +
+ "] is less than min [" + min +
+ "].");
+ }
+
+ this.target_pool_size = sz;
+
+ _recheckResizePool();
+ }
+
+
+// private void acquireUntil(int num) throws Exception
+// {
+// int msz = managed.size();
+// for (int i = msz; i < num; ++i)
+// assimilateResource();
+// }
+
+ //the following methods should only be invoked from
+ //sync'ed methods / blocks...
+
+// private Object useUnusedButNotInIdleCheck()
+// {
+// for (Iterator ii = unused.iterator(); ii.hasNext(); )
+// {
+// Object maybeOut = ii.next();
+// if (! idleCheckResources.contains( maybeOut ))
+// {
+// ii.remove();
+// return maybeOut;
+// }
+// }
+// throw new RuntimeException("Internal Error -- the pool determined that it did have a resource available for checkout, but was unable to find one.");
+// }
+
+// private int actuallyAvailable()
+// { return unused.size() - idleCheckResources.size(); }
+
+ // must own this' lock
+ private void markBrokenNoEnsureMinResources(Object resc)
+ {
+ assert Thread.holdsLock( this );
+
+ try
+ {
+ _markBroken( resc );
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, "Apparent pool break.", e );
+ this.unexpectedBreak();
+ }
+ }
+
+ // must own this' lock
+ private void _markBroken( Object resc )
+ {
+ assert Thread.holdsLock( this );
+
+ if ( unused.contains( resc ) )
+ removeResource( resc );
+ else
+ excludeResource( resc );
+ }
+
+ //DEBUG
+ //Exception firstClose = null;
+
+ public synchronized void close( boolean close_checked_out_resources )
+ {
+ if (! broken ) //ignore repeated calls to close
+ {
+ //DEBUG
+ //firstClose = new Exception("First close() -- debug stack trace [CRAIG]");
+ //firstClose.printStackTrace();
+
+ this.broken = true;
+ final Collection cleanupResources = ( close_checked_out_resources ? (Collection) cloneOfManaged().keySet() : (Collection) cloneOfUnused() );
+ if ( cullTask != null )
+ cullTask.cancel();
+ if (idleRefurbishTask != null)
+ idleRefurbishTask.cancel();
+
+ // we destroy resources asynchronously, but with a dedicated one-off Thread, rather than
+ // our asynchronous runner, because our asynchrous runner may be shutting down. The
+ // destruction is asynchrounous because destroying a resource might require the resource's
+ // lock, and we already have the pool's lock. But client threads may well have the resource's
+ // lock while they try to check-in to the pool. The async destruction of resources avoids
+ // the possibility of deadlock.
+
+ managed.keySet().removeAll( cleanupResources );
+ unused.removeAll( cleanupResources );
+ Thread resourceDestroyer = new Thread("Resource Destroyer in BasicResourcePool.close()")
+ {
+ public void run()
+ {
+ for (Iterator ii = cleanupResources.iterator(); ii.hasNext();)
+ {
+ try
+ {
+ Object resc = ii.next();
+ //System.err.println("Destroying resource... " + resc);
+
+ destroyResource( resc, true );
+ }
+ catch (Exception e)
+ {
+ if (Debug.DEBUG)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "BasicResourcePool -- A resource couldn't be cleaned up on close()", e );
+ }
+ }
+ }
+ }
+ };
+ resourceDestroyer.start();
+
+ for (Iterator ii = acquireWaiters.iterator(); ii.hasNext(); )
+ ((Thread) ii.next()).interrupt();
+ for (Iterator ii = otherWaiters.iterator(); ii.hasNext(); )
+ ((Thread) ii.next()).interrupt();
+ if (factory != null)
+ factory.markBroken( this );
+
+ // System.err.println(this + " closed.");
+ }
+ else
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.warning(this + " -- close() called multiple times.");
+ //System.err.println(this + " -- close() called multiple times.");
+
+ //DEBUG
+ //firstClose.printStackTrace();
+ //new Exception("Repeat close() [CRAIG]").printStackTrace();
+ }
+ }
+
+ private void doCheckinManaged( final Object resc ) throws ResourcePoolException
+ {
+ assert Thread.holdsLock( this );
+
+ if (unused.contains(resc))
+ {
+ if ( Debug.DEBUG )
+ throw new ResourcePoolException("Tried to check-in an already checked-in resource: " + resc);
+ }
+ else if (broken)
+ removeResource( resc, true ); //synchronous... if we're broken, async tasks might not work
+ else
+ {
+ class RefurbishCheckinResourceTask implements Runnable
+ {
+ public void run()
+ {
+ boolean resc_okay = attemptRefurbishResourceOnCheckin( resc );
+ synchronized( BasicResourcePool.this )
+ {
+ PunchCard card = (PunchCard) managed.get( resc );
+
+ if ( resc_okay && card != null) //we have to check that the resource is still in the pool
+ {
+ unused.add(0, resc );
+
+ card.last_checkin_time = System.currentTimeMillis();
+ card.checkout_time = -1;
+ }
+ else
+ {
+ if (card != null)
+ card.checkout_time = -1; //so we don't see this as still checked out and log an overdue cxn in removeResource()
+
+ removeResource( resc );
+ ensureMinResources();
+
+ if (card == null && logger.isLoggable( MLevel.FINE ))
+ logger.fine("Resource " + resc + " was removed from the pool during its refurbishment for checkin.");
+ }
+
+ asyncFireResourceCheckedIn( resc, managed.size(), unused.size(), excluded.size() );
+ BasicResourcePool.this.notifyAll();
+ }
+ }
+ }
+
+ Runnable doMe = new RefurbishCheckinResourceTask();
+ taskRunner.postRunnable( doMe );
+ }
+ }
+
+ private void doCheckinExcluded( Object resc )
+ {
+ assert Thread.holdsLock( this );
+
+ excluded.remove(resc);
+ destroyResource(resc);
+ }
+
+ /*
+ * by the semantics of wait(), a timeout of zero means forever.
+ */
+ private void awaitAvailable(long timeout) throws InterruptedException, TimeoutException, ResourcePoolException
+ {
+ assert Thread.holdsLock( this );
+
+ if (force_kill_acquires)
+ throw new ResourcePoolException("A ResourcePool cannot acquire a new resource -- the factory or source appears to be down.");
+
+ Thread t = Thread.currentThread();
+ try
+ {
+ acquireWaiters.add( t );
+
+ int avail;
+ long start = ( timeout > 0 ? System.currentTimeMillis() : -1);
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine("awaitAvailable(): " +
+ (exampleResource != null ?
+ exampleResource :
+ "[unknown]") );
+ trace();
+ }
+ while ((avail = unused.size()) == 0)
+ {
+ // the if case below can only occur when 1) a user attempts a
+ // checkout which would provoke an acquire; 2) this
+ // increments the pending acquires, so we go to the
+ // wait below without provoking postAcquireMore(); 3)
+ // the resources are acquired; 4) external management
+ // of the pool (via for instance unpoolResource()
+ // depletes the newly acquired resources before we
+ // regain this' monitor; 5) we fall into wait() with
+ // no acquires being scheduled, and perhaps a managed.size()
+ // of zero, leading to deadlock. This could only occur in
+ // fairly pathological situations where the pool is being
+ // externally forced to a very low (even zero) size, but
+ // since I've seen it, I've fixed it.
+ if (pending_acquires == 0 && managed.size() < max)
+ _recheckResizePool();
+
+ this.wait(timeout);
+ if (timeout > 0 && System.currentTimeMillis() - start > timeout)
+ throw new TimeoutException("A client timed out while waiting to acquire a resource from " + this + " -- timeout at awaitAvailable()");
+ if (force_kill_acquires)
+ throw new CannotAcquireResourceException("A ResourcePool could not acquire a resource from its primary factory or source.");
+ ensureNotBroken();
+ }
+ }
+ finally
+ {
+ acquireWaiters.remove( t );
+ if (acquireWaiters.size() == 0)
+ this.notifyAll();
+ }
+ }
+
+ private void assimilateResource( Object resc ) throws Exception
+ {
+ assert Thread.holdsLock( this );
+
+ managed.put(resc, new PunchCard());
+ unused.add(0, resc);
+ //System.err.println("assimilate resource... unused: " + unused.size());
+ asyncFireResourceAcquired( resc, managed.size(), unused.size(), excluded.size() );
+ this.notifyAll();
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+ if (Debug.DEBUG && exampleResource == null)
+ exampleResource = resc;
+ }
+
+ // should NOT be called from synchronized method
+ private void synchronousRemoveArbitraryResource()
+ {
+ assert !Thread.holdsLock( this );
+
+ Object removeMe = null;
+
+ synchronized ( this )
+ {
+ if (unused.size() > 0)
+ {
+ removeMe = unused.get(0);
+ managed.remove(removeMe);
+ unused.remove(removeMe);
+ }
+ else
+ {
+ Set checkedOut = cloneOfManaged().keySet();
+ if ( checkedOut.isEmpty() )
+ {
+ unexpectedBreak();
+ logger.severe("A pool from which a resource is requested to be removed appears to have no managed resources?!");
+ }
+ else
+ excludeResource( checkedOut.iterator().next() );
+ }
+ }
+
+ if (removeMe != null)
+ destroyResource( removeMe, true );
+ }
+
+ private void removeResource(Object resc)
+ { removeResource( resc, false ); }
+
+ private void removeResource(Object resc, boolean synchronous)
+ {
+ assert Thread.holdsLock( this );
+
+ PunchCard pc = (PunchCard) managed.remove(resc);
+
+ if (pc != null)
+ {
+ if ( pc.checkout_time > 0 && !broken) //this is a checked-out resource in an active pool, must be overdue if we are removing it
+ {
+ if (logger.isLoggable( MLevel.INFO ) )
+ {
+ logger.info("A checked-out resource is overdue, and will be destroyed: " + resc);
+ if (pc.checkoutStackTraceException != null)
+ {
+ logger.log( MLevel.INFO,
+ "Logging the stack trace by which the overdue resource was checked-out.",
+ pc.checkoutStackTraceException );
+ }
+ }
+ }
+ }
+ else if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine("Resource " + resc + " was removed twice. (Lotsa reasons a resource can be removed, sometimes simultaneously. It's okay)");
+
+ unused.remove(resc);
+ destroyResource(resc, synchronous);
+ addToFormerResources( resc );
+ asyncFireResourceRemoved( resc, false, managed.size(), unused.size(), excluded.size() );
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+ //System.err.println("RESOURCE REMOVED!");
+ }
+
+ //when we want to conceptually remove a checked
+ //out resource from the pool
+ private void excludeResource(Object resc)
+ {
+ assert Thread.holdsLock( this );
+
+ managed.remove(resc);
+ excluded.add(resc);
+ if (Debug.DEBUG && unused.contains(resc) )
+ throw new InternalError( "We should only \"exclude\" checked-out resources!" );
+ asyncFireResourceRemoved( resc, true, managed.size(), unused.size(), excluded.size() );
+ }
+
+ private void removeTowards( int new_sz )
+ {
+ assert Thread.holdsLock( this );
+
+ int num_to_remove = managed.size() - new_sz;
+ int count = 0;
+ for (Iterator ii = cloneOfUnused().iterator();
+ ii.hasNext() && count < num_to_remove;
+ ++count)
+ {
+ Object resc = ii.next();
+ removeResource( resc );
+ }
+ }
+
+ private void cullExpired()
+ {
+ assert Thread.holdsLock( this );
+
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.log( MLevel.FINER, "BEGIN check for expired resources. [" + this + "]");
+
+ // if we do not time-out checkedout resources, we only need to test unused resources
+ Collection checkMe = ( destroy_unreturned_resc_time > 0 ? (Collection) cloneOfManaged().keySet() : cloneOfUnused() );
+
+ for ( Iterator ii = checkMe.iterator(); ii.hasNext(); )
+ {
+ Object resc = ii.next();
+ if ( shouldExpire( resc ) )
+ {
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.log( MLevel.FINER, "Removing expired resource: " + resc + " [" + this + "]");
+
+ target_pool_size = Math.max( min, target_pool_size - 1 ); //expiring a resource resources the target size to match
+
+ removeResource( resc );
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+ }
+ }
+ if ( logger.isLoggable( MLevel.FINER ) )
+ logger.log( MLevel.FINER, "FINISHED check for expired resources. [" + this + "]");
+ ensureMinResources();
+ }
+
+ private void checkIdleResources()
+ {
+ assert Thread.holdsLock( this );
+
+ List u = cloneOfUnused();
+ for ( Iterator ii = u.iterator(); ii.hasNext(); )
+ {
+ Object resc = ii.next();
+ if ( idleCheckResources.add( resc ) )
+ taskRunner.postRunnable( new AsyncTestIdleResourceTask( resc ) );
+ }
+
+ if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) trace();
+ }
+
+ private boolean shouldExpire( Object resc )
+ {
+ assert Thread.holdsLock( this );
+
+ boolean expired = false;
+
+ PunchCard pc = (PunchCard) managed.get( resc );
+
+ // the resource has already been removed
+ // we return true, because removing twice does no harm
+ // (false should work as well, but true seems safer.
+ // we certainly don't want to do anything else with
+ // this resource.)
+ if (pc == null)
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.fine( "Resource " + resc + " was being tested for expiration, but has already been removed from the pool.");
+ return true;
+ }
+
+ long now = System.currentTimeMillis();
+
+ if (pc.checkout_time < 0) //resource is not checked out
+ {
+ long idle_age = now - pc.last_checkin_time;
+ if (excess_max_idle_time > 0)
+ {
+ int msz = managed.size();
+ expired = (msz > min && idle_age > excess_max_idle_time);
+ if ( expired && logger.isLoggable( MLevel.FINER ) )
+ logger.log(MLevel.FINER,
+ "EXPIRED excess idle resource: " + resc +
+ " ---> idle_time: " + idle_age +
+ "; excess_max_idle_time: " + excess_max_idle_time +
+ "; pool_size: " + msz +
+ "; min_pool_size: " + min +
+ " [" + this + "]");
+ }
+ if (!expired && max_idle_time > 0)
+ {
+ expired = idle_age > max_idle_time;
+ if ( expired && logger.isLoggable( MLevel.FINER ) )
+ logger.log(MLevel.FINER,
+ "EXPIRED idle resource: " + resc +
+ " ---> idle_time: " + idle_age +
+ "; max_idle_time: " + max_idle_time +
+ " [" + this + "]");
+ }
+ if (!expired && max_resource_age > 0)
+ {
+ long abs_age = now - pc.acquisition_time;
+ expired = ( abs_age > max_resource_age );
+
+ if ( expired && logger.isLoggable( MLevel.FINER ) )
+ logger.log(MLevel.FINER,
+ "EXPIRED old resource: " + resc +
+ " ---> absolute_age: " + abs_age +
+ "; max_absolute_age: " + max_resource_age +
+ " [" + this + "]");
+ }
+ }
+ else //resource is checked out
+ {
+ long checkout_age = now - pc.checkout_time;
+ expired = checkout_age > destroy_unreturned_resc_time;
+ }
+
+ return expired;
+ }
+
+
+// private boolean resourcesInIdleCheck()
+// { return idleCheckresources.size() > 0; }
+
+// private int countAvailable()
+// { return unused.size() - idleCheckResources.size(); }
+
+
+ // we needn't hold this' lock
+ private void ensureStartResources()
+ { recheckResizePool(); }
+
+ // we needn't hold this' lock
+ private void ensureMinResources()
+ { recheckResizePool(); }
+
+ private boolean attemptRefurbishResourceOnCheckout( Object resc )
+ {
+ assert !Thread.holdsLock( this );
+
+ try
+ {
+ mgr.refurbishResourceOnCheckout(resc);
+ return true;
+ }
+ catch (Exception e)
+ {
+ //uh oh... bad resource...
+ if (Debug.DEBUG)
+ {
+ //e.printStackTrace();
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "A resource could not be refurbished for checkout. [" + resc + ']', e );
+ }
+ synchronized (this)
+ {
+ ++failed_checkouts;
+ setLastCheckoutFailure(e);
+ }
+ return false;
+ }
+ }
+
+ private boolean attemptRefurbishResourceOnCheckin( Object resc )
+ {
+ assert !Thread.holdsLock( this );
+
+ try
+ {
+ mgr.refurbishResourceOnCheckin(resc);
+ return true;
+ }
+ catch (Exception e)
+ {
+ //uh oh... bad resource...
+ if (Debug.DEBUG)
+ {
+ //e.printStackTrace();
+ if (logger.isLoggable( MLevel.FINE ))
+ logger.log( MLevel.FINE, "A resource could not be refurbished on checkin. [" + resc + ']', e );
+ }
+ synchronized (this)
+ {
+ ++failed_checkins;
+ setLastCheckinFailure(e);
+ }
+ return false;
+ }
+ }
+
+ private void ensureNotBroken() throws ResourcePoolException
+ {
+ assert Thread.holdsLock( this );
+
+ if (broken)
+ throw new ResourcePoolException("Attempted to use a closed or broken resource pool");
+ }
+
+ private void trace()
+ {
+ assert Thread.holdsLock( this );
+
+ if ( logger.isLoggable( MLevel.FINEST ) )
+ {
+ String exampleResStr = ( exampleResource == null ?
+ "" :
+ " (e.g. " + exampleResource +")");
+ logger.finest("trace " + this + " [managed: " + managed.size() + ", " +
+ "unused: " + unused.size() + ", excluded: " +
+ excluded.size() + ']' + exampleResStr );
+ }
+ }
+
+ private final HashMap cloneOfManaged()
+ {
+ assert Thread.holdsLock( this );
+
+ return (HashMap) managed.clone();
+ }
+
+ private final LinkedList cloneOfUnused()
+ {
+ assert Thread.holdsLock( this );
+
+ return (LinkedList) unused.clone();
+ }
+
+ private final HashSet cloneOfExcluded()
+ {
+ assert Thread.holdsLock( this );
+
+ return (HashSet) excluded.clone();
+ }
+
+ class ScatteredAcquireTask implements Runnable
+ {
+ int attempts_remaining;
+
+ ScatteredAcquireTask()
+ { this ( (num_acq_attempts >= 0 ? num_acq_attempts : -1) , true ); }
+
+ private ScatteredAcquireTask(int attempts_remaining, boolean first_attempt)
+ {
+ this.attempts_remaining = attempts_remaining;
+ if (first_attempt)
+ {
+ incrementPendingAcquires();
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("Starting acquisition series. Incremented pending_acquires [" + pending_acquires + "], " +
+ " attempts_remaining: " + attempts_remaining);
+ }
+ else
+ {
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("Continuing acquisition series. pending_acquires [" + pending_acquires + "], " +
+ " attempts_remaining: " + attempts_remaining);
+ }
+ }
+
+ public void run()
+ {
+ try
+ {
+ boolean fkap = isForceKillAcquiresPending();
+ if (! fkap)
+ {
+ //we don't want this call to be sync'd
+ //on the pool, so that resource acquisition
+ //does not interfere with other pool clients.
+ BasicResourcePool.this.doAcquire();
+ }
+ decrementPendingAcquires();
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("Acquisition series terminated " +
+ (fkap ? "because force-kill-acquires is pending" : "successfully") +
+ ". Decremented pending_acquires [" + pending_acquires + "], " +
+ " attempts_remaining: " + attempts_remaining);
+ }
+ catch (Exception e)
+ {
+ BasicResourcePool.this.setLastAcquisitionFailure(e);
+
+ if (attempts_remaining == 0) //last try in a round...
+ {
+ decrementPendingAcquires();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.log( MLevel.WARNING,
+ this + " -- Acquisition Attempt Failed!!! Clearing pending acquires. " +
+ "While trying to acquire a needed new resource, we failed " +
+ "to succeed more than the maximum number of allowed " +
+ "acquisition attempts (" + num_acq_attempts + "). " +
+ "Last acquisition attempt exception: ",
+ e);
+ }
+ if (break_on_acquisition_failure)
+ {
+ //System.err.println("\tTHE RESOURCE POOL IS PERMANENTLY BROKEN!");
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.severe("A RESOURCE POOL IS PERMANENTLY BROKEN! [" + this + "] " +
+ "(because a series of " + num_acq_attempts + " acquisition attempts " +
+ "failed.)");
+ unexpectedBreak();
+ }
+ else
+ {
+ try { forceKillAcquires(); }
+ catch (InterruptedException ie)
+ {
+ if ( logger.isLoggable(MLevel.WARNING) )
+ logger.log(MLevel.WARNING,
+ "Failed to force-kill pending acquisition attempts after acquisition failue, " +
+ " due to an InterruptedException!",
+ ie );
+
+ // we might still have clients waiting, so we should try
+ // to ensure there are sufficient connections to serve
+ recheckResizePool();
+ }
+ }
+ if (logger.isLoggable(MLevel.FINEST))
+ logger.finest("Acquisition series terminated unsuccessfully. Decremented pending_acquires [" + pending_acquires + "], " +
+ " attempts_remaining: " + attempts_remaining);
+ }
+ else
+ {
+ // if attempts_remaining < 0, we try to acquire forever, so the end-of-batch
+ // log message below will never be triggered if there is a persistent problem
+ // so in this case, it's better flag a higher-than-debug-level message for
+ // each failed attempt. (Thanks to Eric Crahen for calling attention to this
+ // issue.)
+ MLevel logLevel = (attempts_remaining > 0 ? MLevel.FINE : MLevel.INFO);
+ if (logger.isLoggable( logLevel ))
+ logger.log( logLevel, "An exception occurred while acquiring a poolable resource. Will retry.", e );
+
+ TimerTask doNextAcquire = new TimerTask()
+ {
+ public void run()
+ { taskRunner.postRunnable( new ScatteredAcquireTask( attempts_remaining - 1, false ) ); }
+ };
+ cullAndIdleRefurbishTimer.schedule( doNextAcquire, acq_attempt_delay );
+ }
+ }
+ }
+
+ }
+
+ /*
+ * task we post to separate thread to acquire
+ * pooled resources
+ */
+ class AcquireTask implements Runnable
+ {
+ boolean success = false;
+
+ public AcquireTask()
+ { incrementPendingAcquires(); }
+
+ public void run()
+ {
+ try
+ {
+ Exception lastException = null;
+ for (int i = 0; shouldTry( i ); ++i)
+ {
+ try
+ {
+ if (i > 0)
+ Thread.sleep(acq_attempt_delay);
+
+ //we don't want this call to be sync'd
+ //on the pool, so that resource acquisition
+ //does not interfere with other pool clients.
+ BasicResourcePool.this.doAcquire();
+
+ success = true;
+ }
+ catch (InterruptedException e)
+ {
+ // end the whole task on interrupt, regardless of success
+ // or failure
+ throw e;
+ }
+ catch (Exception e)
+ {
+ //e.printStackTrace();
+
+ // if num_acq_attempts <= 0, we try to acquire forever, so the end-of-batch
+ // log message below will never be triggered if there is a persistent problem
+ // so in this case, it's better flag a higher-than-debug-level message for
+ // each failed attempt. (Thanks to Eric Crahen for calling attention to this
+ // issue.)
+ MLevel logLevel = (num_acq_attempts > 0 ? MLevel.FINE : MLevel.INFO);
+ if (logger.isLoggable( logLevel ))
+ logger.log( logLevel, "An exception occurred while acquiring a poolable resource. Will retry.", e );
+
+ lastException = e;
+ setLastAcquisitionFailure(e);
+ }
+ }
+ if (!success)
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.log( MLevel.WARNING,
+ this + " -- Acquisition Attempt Failed!!! Clearing pending acquires. " +
+ "While trying to acquire a needed new resource, we failed " +
+ "to succeed more than the maximum number of allowed " +
+ "acquisition attempts (" + num_acq_attempts + "). " +
+ (lastException == null ? "" : "Last acquisition attempt exception: "),
+ lastException);
+ }
+ if (break_on_acquisition_failure)
+ {
+ //System.err.println("\tTHE RESOURCE POOL IS PERMANENTLY BROKEN!");
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.severe("A RESOURCE POOL IS PERMANENTLY BROKEN! [" + this + "]");
+ unexpectedBreak();
+ }
+ else
+ forceKillAcquires();
+ }
+ else
+ recheckResizePool();
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "a resource pool async thread died.", e );
+ }
+ unexpectedBreak();
+ }
+ catch (InterruptedException e) //from force kill acquires, or by the thread pool during the long task...
+ {
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ {
+ logger.log( MLevel.WARNING,
+ BasicResourcePool.this + " -- Thread unexpectedly interrupted while performing an acquisition attempt.",
+ e );
+ }
+
+// System.err.println(BasicResourcePool.this + " -- Thread unexpectedly interrupted while waiting for stale acquisition attempts to die.");
+// e.printStackTrace();
+
+ recheckResizePool();
+ }
+ finally
+ { decrementPendingAcquires(); }
+ }
+
+ private boolean shouldTry(int attempt_num)
+ {
+ //try if we haven't already succeeded
+ //and someone hasn't signalled that our resource source is down
+ //and not max attempts is set,
+ //or we are less than the set limit
+ return
+ !success &&
+ !isForceKillAcquiresPending() &&
+ (num_acq_attempts <= 0 || attempt_num < num_acq_attempts);
+ }
+ }
+
+ /*
+ * task we post to separate thread to remove
+ * unspecified pooled resources
+ *
+ * TODO: do removal and destruction synchronously
+ * but carefully not synchronized during the
+ * destruction of the resource.
+ */
+ class RemoveTask implements Runnable
+ {
+ public RemoveTask()
+ { incrementPendingRemoves(); }
+
+ public void run()
+ {
+ try
+ {
+ synchronousRemoveArbitraryResource();
+ recheckResizePool();
+ }
+ finally
+ { decrementPendingRemoves(); }
+ }
+ }
+
+ class CullTask extends TimerTask
+ {
+ public void run()
+ {
+ try
+ {
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable( MLevel.FINER ))
+ logger.log( MLevel.FINER, "Checking for expired resources - " + new Date() + " [" + BasicResourcePool.this + "]");
+ synchronized ( BasicResourcePool.this )
+ { cullExpired(); }
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "a resource pool async thread died.", e );
+ }
+ unexpectedBreak();
+ }
+ }
+ }
+
+ // this is run by a single-threaded timer, so we don't have
+ // to worry about multiple threads executing the task at the same
+ // time
+ class CheckIdleResourcesTask extends TimerTask
+ {
+ public void run()
+ {
+ try
+ {
+ //System.err.println("c3p0-JENNIFER: refurbishing idle resources - " + new Date() + " [" + BasicResourcePool.this + "]");
+ if (Debug.DEBUG && Debug.TRACE >= Debug.TRACE_MED && logger.isLoggable(MLevel.FINER))
+ logger.log(MLevel.FINER, "Refurbishing idle resources - " + new Date() + " [" + BasicResourcePool.this + "]");
+ synchronized ( BasicResourcePool.this )
+ { checkIdleResources(); }
+ }
+ catch ( ResourceClosedException e ) // one of our async threads died
+ {
+ //e.printStackTrace();
+ if ( Debug.DEBUG )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "a resource pool async thread died.", e );
+ }
+ unexpectedBreak();
+ }
+ }
+ }
+
+ class AsyncTestIdleResourceTask implements Runnable
+ {
+ // unchanging after ctor
+ Object resc;
+
+ // protected by this' lock
+ boolean pending = true;
+ boolean failed;
+
+ AsyncTestIdleResourceTask( Object resc )
+ { this.resc = resc; }
+
+ public void run()
+ {
+ assert !Thread.holdsLock( BasicResourcePool.this );
+
+ try
+ {
+ try
+ {
+ mgr.refurbishIdleResource( resc );
+ }
+ catch ( Exception e )
+ {
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "BasicResourcePool: An idle resource is broken and will be purged. [" + resc + ']', e);
+
+ synchronized (BasicResourcePool.this)
+ {
+ if ( managed.keySet().contains( resc ) ) //resc might have been culled as expired while we tested
+ {
+ removeResource( resc );
+ ensureMinResources();
+ }
+
+ ++failed_idle_tests;
+ setLastIdleCheckFailure(e);
+ }
+ }
+ }
+ finally
+ {
+ synchronized (BasicResourcePool.this)
+ {
+ idleCheckResources.remove( resc );
+ BasicResourcePool.this.notifyAll();
+ }
+ }
+ }
+ }
+
+ final static class PunchCard
+ {
+ long acquisition_time;
+ long last_checkin_time;
+ long checkout_time;
+ Exception checkoutStackTraceException;
+
+ PunchCard()
+ {
+ this.acquisition_time = System.currentTimeMillis();
+ this.last_checkin_time = acquisition_time;
+ this.checkout_time = -1;
+ this.checkoutStackTraceException = null;
+ }
+ }
+
+// static class CheckInProgressResourceHolder
+// {
+// Object checkResource;
+
+// public synchronized void setCheckResource( Object resc )
+// {
+// this.checkResource = resc;
+// this.notifyAll();
+// }
+
+// public void unsetCheckResource()
+// { setCheckResource( null ); }
+
+// /**
+// * @return true if we actually had to wait
+// */
+// public synchronized boolean awaitNotInCheck( Object resc )
+// {
+// boolean had_to_wait = false;
+// boolean set_interrupt = false;
+// while ( checkResource == resc )
+// {
+// try
+// {
+// had_to_wait = true;
+// this.wait();
+// }
+// catch ( InterruptedException e )
+// {
+// e.printStackTrace();
+// set_interrupt = true;
+// }
+// }
+// if ( set_interrupt )
+// Thread.currentThread().interrupt();
+// return had_to_wait;
+// }
+// }
+}
+
diff --git a/src/classes/com/mchange/v2/resourcepool/BasicResourcePoolFactory.java b/src/classes/com/mchange/v2/resourcepool/BasicResourcePoolFactory.java
new file mode 100644
index 0000000..37f6f1d
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/BasicResourcePoolFactory.java
@@ -0,0 +1,362 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.*;
+import com.mchange.v2.async.*;
+
+public class BasicResourcePoolFactory extends ResourcePoolFactory
+{
+ public static BasicResourcePoolFactory createNoEventSupportInstance( int num_task_threads )
+ { return createNoEventSupportInstance( null, null, num_task_threads ); }
+
+ public static BasicResourcePoolFactory createNoEventSupportInstance( AsynchronousRunner taskRunner,
+ Timer timer )
+ { return createNoEventSupportInstance( taskRunner, timer, ResourcePoolFactory.DEFAULT_NUM_TASK_THREADS ); }
+
+
+ private static BasicResourcePoolFactory createNoEventSupportInstance( AsynchronousRunner taskRunner,
+ Timer timer,
+ int default_num_task_threads )
+ {
+ return new BasicResourcePoolFactory( taskRunner,
+ timer,
+ default_num_task_threads,
+ true );
+ }
+
+ int start = -1; //default to min
+ int min = 1;
+ int max = 12;
+ int inc = 3;
+ int retry_attempts = -1; //by default, retry acquisitions forever
+ int retry_delay = 1000; //1 second
+ long idle_resource_test_period = -1; //milliseconds, by default we don't test idle resources
+ long max_age = -1; //milliseconds, by default resources never expire
+ long max_idle_time = -1; //milliseconds, by default resources never expire
+ long excess_max_idle_time = -1; //milliseconds, by default resources never expire
+ long destroy_overdue_resc_time = -1; //milliseconds
+ long expiration_enforcement_delay = -1; //automatic, we come up with a reasonable default based on time params
+
+ boolean break_on_acquisition_failure = true;
+ boolean debug_store_checkout_stacktrace = false;
+
+ AsynchronousRunner taskRunner;
+ boolean taskRunner_is_external;
+
+ RunnableQueue asyncEventQueue;
+ boolean asyncEventQueue_is_external;
+
+ Timer timer;
+ boolean timer_is_external;
+
+ int default_num_task_threads;
+
+ Set liveChildren;
+
+
+
+ //OLD
+// Set rqUsers = null;
+// SimpleRunnableQueue rq = null;
+
+// Set timerUsers = null;
+// Timer timer = null;
+ //END OLD
+
+ BasicResourcePoolFactory()
+ { this( null, null, null ); }
+
+ BasicResourcePoolFactory( AsynchronousRunner taskRunner,
+ RunnableQueue asyncEventQueue,
+ Timer timer )
+ { this ( taskRunner, asyncEventQueue, timer, DEFAULT_NUM_TASK_THREADS ); }
+
+ BasicResourcePoolFactory( int num_task_threads )
+ { this ( null, null, null, num_task_threads ); }
+
+ BasicResourcePoolFactory( AsynchronousRunner taskRunner,
+ Timer timer,
+ int default_num_task_threads,
+ boolean no_event_support)
+ {
+ this( taskRunner, null, timer, default_num_task_threads );
+ if (no_event_support)
+ asyncEventQueue_is_external = true; //if it's null, and external, it simply won't exist...
+ }
+
+ BasicResourcePoolFactory( AsynchronousRunner taskRunner,
+ RunnableQueue asyncEventQueue,
+ Timer timer,
+ int default_num_task_threads)
+ {
+ this.taskRunner = taskRunner;
+ this.taskRunner_is_external = ( taskRunner != null );
+
+ this.asyncEventQueue = asyncEventQueue;
+ this.asyncEventQueue_is_external = ( asyncEventQueue != null );
+
+ this.timer = timer;
+ this.timer_is_external = ( timer != null );
+
+ this.default_num_task_threads = default_num_task_threads;
+ }
+
+ private void createThreadResources()
+ {
+ if (! taskRunner_is_external )
+ {
+ //taskRunner = new RoundRobinAsynchronousRunner( default_num_task_threads, true );
+ taskRunner = new ThreadPoolAsynchronousRunner( default_num_task_threads, true );
+ if (! asyncEventQueue_is_external)
+ asyncEventQueue = ((Queuable) taskRunner).asRunnableQueue();
+ }
+ if (! asyncEventQueue_is_external)
+ asyncEventQueue = new CarefulRunnableQueue( true, false );
+ if (! timer_is_external )
+ timer = new Timer( true );
+
+ this.liveChildren = new HashSet();
+ }
+
+ private void destroyThreadResources()
+ {
+ if (! taskRunner_is_external )
+ {
+ taskRunner.close();
+ taskRunner = null;
+ }
+ if (! asyncEventQueue_is_external )
+ {
+ asyncEventQueue.close();
+ asyncEventQueue = null;
+ }
+ if (! timer_is_external )
+ {
+ timer.cancel();
+ timer = null;
+ }
+
+ this.liveChildren = null;
+ }
+
+// synchronized RunnableQueue getSharedRunnableQueue( BasicResourcePool pool )
+// {
+// if (rqUsers == null)
+// {
+// rqUsers = new HashSet();
+// rq = new SimpleRunnableQueue(true);
+// }
+// rqUsers.add( pool );
+// return rq;
+// }
+
+// synchronized Timer getTimer( BasicResourcePool pool )
+// {
+// if (timerUsers == null)
+// {
+// timerUsers = new HashSet();
+// timer = new Timer( true );
+// }
+// timerUsers.add( pool );
+// return timer;
+// }
+
+ synchronized void markBroken( BasicResourcePool pool )
+ {
+ //System.err.println("markBroken -- liveChildren: " + liveChildren);
+ if (liveChildren != null) //keep this method idempotent!
+ {
+ liveChildren.remove( pool );
+ if (liveChildren.isEmpty())
+ destroyThreadResources();
+ }
+// rqUsers.remove( pool );
+// if (rqUsers.size() == 0)
+// {
+// rqUsers = null;
+// rq.close();
+// rq = null;
+// }
+
+// timerUsers.remove( pool );
+// if (timerUsers.size() == 0)
+// {
+// timerUsers = null;
+// timer.cancel();
+// timer = null;
+// }
+ }
+
+ /**
+ * If start is less than min, it will
+ * be ignored, and the pool will start
+ * with min.
+ */
+ public synchronized void setStart( int start )
+ throws ResourcePoolException
+ { this.start = start; }
+
+ public synchronized int getStart()
+ throws ResourcePoolException
+ { return start; }
+
+ public synchronized void setMin( int min )
+ throws ResourcePoolException
+ { this.min = min; }
+
+ public synchronized int getMin()
+ throws ResourcePoolException
+ { return min; }
+
+ public synchronized void setMax( int max )
+ throws ResourcePoolException
+ { this.max = max; }
+
+ public synchronized int getMax()
+ throws ResourcePoolException
+ { return max; }
+
+ public synchronized void setIncrement( int inc )
+ throws ResourcePoolException
+ { this.inc = inc; }
+
+ public synchronized int getIncrement()
+ throws ResourcePoolException
+ { return inc; }
+
+ public synchronized void setAcquisitionRetryAttempts( int retry_attempts )
+ throws ResourcePoolException
+ { this.retry_attempts = retry_attempts; }
+
+ public synchronized int getAcquisitionRetryAttempts()
+ throws ResourcePoolException
+ { return retry_attempts; }
+
+ public synchronized void setAcquisitionRetryDelay( int retry_delay )
+ throws ResourcePoolException
+ { this.retry_delay = retry_delay; }
+
+ public synchronized int getAcquisitionRetryDelay()
+ throws ResourcePoolException
+ { return retry_delay; }
+
+ public synchronized void setIdleResourceTestPeriod( long test_period )
+ { this.idle_resource_test_period = test_period; }
+
+ public synchronized long getIdleResourceTestPeriod()
+ { return idle_resource_test_period; }
+
+ public synchronized void setResourceMaxAge( long max_age )
+ throws ResourcePoolException
+ { this.max_age = max_age; }
+
+ public synchronized long getResourceMaxAge()
+ throws ResourcePoolException
+ { return max_age; }
+
+ public synchronized void setResourceMaxIdleTime( long millis )
+ throws ResourcePoolException
+ { this.max_idle_time = millis; }
+
+ public synchronized long getResourceMaxIdleTime()
+ throws ResourcePoolException
+ { return max_idle_time; }
+
+ public synchronized void setExcessResourceMaxIdleTime( long millis )
+ throws ResourcePoolException
+ { this.excess_max_idle_time = millis; }
+
+ public synchronized long getExcessResourceMaxIdleTime()
+ throws ResourcePoolException
+ { return excess_max_idle_time; }
+
+ public synchronized long getDestroyOverdueResourceTime()
+ throws ResourcePoolException
+ { return destroy_overdue_resc_time; }
+
+ public synchronized void setDestroyOverdueResourceTime( long millis )
+ throws ResourcePoolException
+ { this.destroy_overdue_resc_time = millis; }
+
+ public synchronized void setExpirationEnforcementDelay( long expiration_enforcement_delay )
+ throws ResourcePoolException
+ { this.expiration_enforcement_delay = expiration_enforcement_delay; }
+
+ public synchronized long getExpirationEnforcementDelay()
+ throws ResourcePoolException
+ { return expiration_enforcement_delay; }
+
+ public synchronized void setBreakOnAcquisitionFailure( boolean break_on_acquisition_failure )
+ throws ResourcePoolException
+ { this.break_on_acquisition_failure = break_on_acquisition_failure; }
+
+ public synchronized boolean getBreakOnAcquisitionFailure()
+ throws ResourcePoolException
+ { return break_on_acquisition_failure; }
+
+ public synchronized void setDebugStoreCheckoutStackTrace( boolean debug_store_checkout_stacktrace )
+ throws ResourcePoolException
+ { this.debug_store_checkout_stacktrace = debug_store_checkout_stacktrace; }
+
+ public synchronized boolean getDebugStoreCheckoutStackTrace()
+ throws ResourcePoolException
+ { return debug_store_checkout_stacktrace; }
+
+ public synchronized ResourcePool createPool(ResourcePool.Manager mgr)
+ throws ResourcePoolException
+ {
+ if (liveChildren == null)
+ createThreadResources();
+ //System.err.println("Created liveChildren: " + liveChildren);
+ ResourcePool child = new BasicResourcePool( mgr,
+ start,
+ min,
+ max,
+ inc,
+ retry_attempts,
+ retry_delay,
+ idle_resource_test_period,
+ max_age,
+ max_idle_time,
+ excess_max_idle_time,
+ destroy_overdue_resc_time,
+ expiration_enforcement_delay,
+ break_on_acquisition_failure,
+ debug_store_checkout_stacktrace,
+ taskRunner,
+ asyncEventQueue,
+ timer,
+ this );
+ liveChildren.add( child );
+ return child;
+ }
+}
+
+
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/resourcepool/CannotAcquireResourceException.java b/src/classes/com/mchange/v2/resourcepool/CannotAcquireResourceException.java
new file mode 100644
index 0000000..ebf719c
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/CannotAcquireResourceException.java
@@ -0,0 +1,39 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+public class CannotAcquireResourceException extends ResourcePoolException
+{
+ public CannotAcquireResourceException(String msg, Throwable t)
+ {super(msg, t);}
+
+ public CannotAcquireResourceException(Throwable t)
+ {super(t);}
+
+ public CannotAcquireResourceException(String msg)
+ {super(msg);}
+
+ public CannotAcquireResourceException()
+ {super();}
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePool.java b/src/classes/com/mchange/v2/resourcepool/ResourcePool.java
new file mode 100644
index 0000000..79a7920
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePool.java
@@ -0,0 +1,141 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import com.mchange.v1.util.ClosableResource;
+
+public interface ResourcePool extends ClosableResource
+{
+ // status in pool return values
+ final static int KNOWN_AND_AVAILABLE = 0;
+ final static int KNOWN_AND_CHECKED_OUT = 1;
+ final static int UNKNOWN_OR_PURGED = -1;
+
+ public Object checkoutResource()
+ throws ResourcePoolException, InterruptedException;
+
+ public Object checkoutResource( long timeout )
+ throws TimeoutException, ResourcePoolException, InterruptedException;
+
+ public void checkinResource( Object resc )
+ throws ResourcePoolException;
+
+ public void checkinAll()
+ throws ResourcePoolException;
+
+ public int statusInPool( Object resc )
+ throws ResourcePoolException;
+
+ /**
+ * Marks a resource as broken. If the resource is checked in,
+ * it will be destroyed immediately. Otherwise, it will be
+ * destroyed on checkin.
+ */
+ public void markBroken( Object resc )
+ throws ResourcePoolException;
+
+ public int getMinPoolSize()
+ throws ResourcePoolException;
+
+ public int getMaxPoolSize()
+ throws ResourcePoolException;
+
+ public int getPoolSize()
+ throws ResourcePoolException;
+
+ public void setPoolSize(int size)
+ throws ResourcePoolException;
+
+ public int getAvailableCount()
+ throws ResourcePoolException;
+
+ public int getExcludedCount()
+ throws ResourcePoolException;
+
+ public int getAwaitingCheckinCount()
+ throws ResourcePoolException;
+
+ public long getEffectiveExpirationEnforcementDelay()
+ throws ResourcePoolException;
+
+ public long getStartTime()
+ throws ResourcePoolException;
+
+ public long getUpTime()
+ throws ResourcePoolException;
+
+ public long getNumFailedCheckins()
+ throws ResourcePoolException;
+
+ public long getNumFailedCheckouts()
+ throws ResourcePoolException;
+
+ public long getNumFailedIdleTests()
+ throws ResourcePoolException;
+
+ public int getNumCheckoutWaiters()
+ throws ResourcePoolException;
+
+ public Throwable getLastAcquisitionFailure()
+ throws ResourcePoolException;
+
+ public Throwable getLastCheckinFailure()
+ throws ResourcePoolException;
+
+ public Throwable getLastCheckoutFailure()
+ throws ResourcePoolException;
+
+ public Throwable getLastIdleCheckFailure()
+ throws ResourcePoolException;
+
+ public Throwable getLastResourceTestFailure()
+ throws ResourcePoolException;
+
+
+
+ /**
+ * Discards all resources managed by the pool
+ * and reacquires new resources to populate the
+ * pool. Current checked out resources will still
+ * be valid, and should still be checked into the
+ * pool (so the pool can destroy them).
+ */
+ public void resetPool()
+ throws ResourcePoolException;
+
+ public void close()
+ throws ResourcePoolException;
+
+ public void close( boolean close_checked_out_resources )
+ throws ResourcePoolException;
+
+ public interface Manager
+ {
+ public Object acquireResource() throws Exception;
+ public void refurbishIdleResource(Object resc) throws Exception;
+ public void refurbishResourceOnCheckout(Object resc) throws Exception;
+ public void refurbishResourceOnCheckin(Object resc) throws Exception;
+ public void destroyResource(Object resc) throws Exception;
+ }
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolEvent.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolEvent.java
new file mode 100644
index 0000000..5a636ff
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolEvent.java
@@ -0,0 +1,75 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.EventObject;
+
+public class ResourcePoolEvent extends EventObject
+{
+ Object resc;
+ boolean checked_out_resource;
+ int pool_size;
+ int available_size;
+ int removed_but_unreturned_size;
+
+ public ResourcePoolEvent( ResourcePool pool,
+ Object resc,
+ boolean checked_out_resource,
+ int pool_size,
+ int available_size,
+ int removed_but_unreturned_size )
+ {
+ super(pool);
+ this.resc = resc;
+ this.checked_out_resource = checked_out_resource;
+ this.pool_size = pool_size;
+ this.available_size = available_size;
+ this.removed_but_unreturned_size = removed_but_unreturned_size;
+ }
+
+ public Object getResource()
+ { return resc; }
+
+ public boolean isCheckedOutResource()
+ { return checked_out_resource; }
+
+ public int getPoolSize()
+ { return pool_size; }
+
+ public int getAvailableSize()
+ { return available_size; }
+
+ public int getRemovedButUnreturnedSize()
+ { return removed_but_unreturned_size; }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolEventSupport.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolEventSupport.java
new file mode 100644
index 0000000..1afeff8
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolEventSupport.java
@@ -0,0 +1,129 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.*;
+
+public class ResourcePoolEventSupport
+{
+ ResourcePool source;
+ Set mlisteners = new HashSet();
+
+ public ResourcePoolEventSupport(ResourcePool source)
+ { this.source = source; }
+
+ public synchronized void addResourcePoolListener(ResourcePoolListener mlistener)
+ {mlisteners.add(mlistener);}
+
+ public synchronized void removeResourcePoolListener(ResourcePoolListener mlistener)
+ {mlisteners.remove(mlistener);}
+
+ public synchronized void fireResourceAcquired( Object resc,
+ int pool_size,
+ int available_size,
+ int removed_but_unreturned_size )
+ {
+ if (! mlisteners.isEmpty() )
+ {
+ ResourcePoolEvent evt = new ResourcePoolEvent(source,
+ resc,
+ false,
+ pool_size,
+ available_size,
+ removed_but_unreturned_size );
+ for (Iterator i = mlisteners.iterator(); i.hasNext();)
+ {
+ ResourcePoolListener rpl = (ResourcePoolListener) i.next();
+ rpl.resourceAcquired(evt);
+ }
+ }
+ }
+
+ public synchronized void fireResourceCheckedIn( Object resc,
+ int pool_size,
+ int available_size,
+ int removed_but_unreturned_size )
+ {
+ if (! mlisteners.isEmpty() )
+ {
+ ResourcePoolEvent evt = new ResourcePoolEvent(source,
+ resc,
+ false,
+ pool_size,
+ available_size,
+ removed_but_unreturned_size );
+ for (Iterator i = mlisteners.iterator(); i.hasNext();)
+ {
+ ResourcePoolListener rpl = (ResourcePoolListener) i.next();
+ rpl.resourceCheckedIn(evt);
+ }
+ }
+ }
+
+ public synchronized void fireResourceCheckedOut( Object resc,
+ int pool_size,
+ int available_size,
+ int removed_but_unreturned_size )
+ {
+ if (! mlisteners.isEmpty() )
+ {
+ ResourcePoolEvent evt = new ResourcePoolEvent(source,
+ resc,
+ true,
+ pool_size,
+ available_size,
+ removed_but_unreturned_size );
+ for (Iterator i = mlisteners.iterator(); i.hasNext();)
+ {
+ ResourcePoolListener rpl = (ResourcePoolListener) i.next();
+ rpl.resourceCheckedOut(evt);
+ }
+ }
+ }
+
+ public synchronized void fireResourceRemoved( Object resc,
+ boolean checked_out_resource,
+ int pool_size,
+ int available_size,
+ int removed_but_unreturned_size )
+ {
+ if (! mlisteners.isEmpty() )
+ {
+ ResourcePoolEvent evt = new ResourcePoolEvent(source,
+ resc,
+ checked_out_resource,
+ pool_size,
+ available_size,
+ removed_but_unreturned_size );
+ for (Iterator i = mlisteners.iterator(); i.hasNext();)
+ {
+ ResourcePoolListener rpl = (ResourcePoolListener) i.next();
+ rpl.resourceRemoved(evt);
+ }
+ }
+ }
+}
+
+
+
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolException.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolException.java
new file mode 100644
index 0000000..f21c7ec
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolException.java
@@ -0,0 +1,41 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import com.mchange.lang.PotentiallySecondaryException;
+
+public class ResourcePoolException extends PotentiallySecondaryException
+{
+ public ResourcePoolException(String msg, Throwable t)
+ {super(msg, t);}
+
+ public ResourcePoolException(Throwable t)
+ {super(t);}
+
+ public ResourcePoolException(String msg)
+ {super(msg);}
+
+ public ResourcePoolException()
+ {super();}
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolFactory.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolFactory.java
new file mode 100644
index 0000000..eb34a59
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolFactory.java
@@ -0,0 +1,189 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.Timer;
+import com.mchange.v2.async.*;
+
+/**
+ * <P>A Factory for ResourcePools. ResourcePoolFactories may manage
+ * resources (usually threads that perform maintenance tasks) that
+ * are shared by all pools that they create. Clients who require
+ * a large number of pools may wish to create their own factory
+ * instances rather than using the shared instance to control
+ * the degree of resource (Thread) sharing among pools.</P>
+ *
+ * <P>Factories should (and the default implementation does) be careful
+ * to ensure that factories keep resources open only while there
+ * are active ResourcePools that they have created.</P>
+ *
+ * <P>Subclasses must mark all methods synchronized so that clients
+ * may reliably use shared factories to do stuff like...</P>
+ *
+ * <pre>
+ * synchronized (factory)
+ * {
+ * factory.setMin(8);
+ * factory.setMax(24);
+ * ResourcePool rp = factory.createPool();
+ * }
+ * </pre>
+ */
+public abstract class ResourcePoolFactory
+{
+ // okay, 'cuz we don't actually create any threads / resourced
+ // until the factory is used.
+ final static ResourcePoolFactory SHARED_INSTANCE = new BasicResourcePoolFactory();
+
+ final static int DEFAULT_NUM_TASK_THREADS = 3;
+
+ public static ResourcePoolFactory getSharedInstance()
+ throws ResourcePoolException
+ { return SHARED_INSTANCE; }
+
+ public static ResourcePoolFactory createInstance()
+ { return new BasicResourcePoolFactory(); }
+
+ public static ResourcePoolFactory createInstance( int num_task_threads )
+ { return new BasicResourcePoolFactory( num_task_threads ); }
+
+ /**
+ * Any or all of these arguments can be null -- any unspecified resources
+ * will be created and cleaned up internally.
+ */
+ public static ResourcePoolFactory createInstance( AsynchronousRunner taskRunner,
+ RunnableQueue asyncEventQueue,
+ Timer cullTimer )
+ { return new BasicResourcePoolFactory( taskRunner, asyncEventQueue, cullTimer ); }
+
+ public static ResourcePoolFactory createInstance( Queuable taskRunnerEventQueue,
+ Timer cullTimer )
+ {
+ return createInstance( taskRunnerEventQueue,
+ taskRunnerEventQueue == null ?
+ null :
+ taskRunnerEventQueue.asRunnableQueue(),
+ cullTimer );
+ }
+
+ public abstract void setMin( int min )
+ throws ResourcePoolException;
+
+ public abstract int getMin()
+ throws ResourcePoolException;
+
+ public abstract void setMax( int max )
+ throws ResourcePoolException;
+
+ public abstract int getStart()
+ throws ResourcePoolException;
+
+ public abstract void setStart( int start )
+ throws ResourcePoolException;
+
+ public abstract int getMax()
+ throws ResourcePoolException;
+
+ public abstract void setIncrement( int max )
+ throws ResourcePoolException;
+
+ public abstract int getIncrement()
+ throws ResourcePoolException;
+
+ public abstract void setAcquisitionRetryAttempts( int retry_attempts )
+ throws ResourcePoolException;
+
+ public abstract int getAcquisitionRetryAttempts()
+ throws ResourcePoolException;
+
+ public abstract void setAcquisitionRetryDelay( int retry_delay )
+ throws ResourcePoolException;
+
+ public abstract int getAcquisitionRetryDelay()
+ throws ResourcePoolException;
+
+ public abstract void setIdleResourceTestPeriod( long test_period )
+ throws ResourcePoolException;
+
+ public abstract long getIdleResourceTestPeriod()
+ throws ResourcePoolException;
+
+ public abstract void setResourceMaxAge( long millis )
+ throws ResourcePoolException;
+
+ public abstract long getResourceMaxAge()
+ throws ResourcePoolException;
+
+ public abstract void setResourceMaxIdleTime( long millis )
+ throws ResourcePoolException;
+
+ public abstract long getResourceMaxIdleTime()
+ throws ResourcePoolException;
+
+ public abstract void setExcessResourceMaxIdleTime( long millis )
+ throws ResourcePoolException;
+
+ public abstract long getExcessResourceMaxIdleTime()
+ throws ResourcePoolException;
+
+ public abstract long getDestroyOverdueResourceTime()
+ throws ResourcePoolException;
+
+ public abstract void setDestroyOverdueResourceTime( long millis )
+ throws ResourcePoolException;
+
+ public abstract void setExpirationEnforcementDelay( long millis )
+ throws ResourcePoolException;
+
+ public abstract long getExpirationEnforcementDelay()
+ throws ResourcePoolException;
+
+ public abstract void setBreakOnAcquisitionFailure( boolean b )
+ throws ResourcePoolException;
+
+ public abstract boolean getBreakOnAcquisitionFailure()
+ throws ResourcePoolException;
+
+ public abstract void setDebugStoreCheckoutStackTrace( boolean debug_store_checkout_stacktrace )
+ throws ResourcePoolException;
+
+ public abstract boolean getDebugStoreCheckoutStackTrace()
+ throws ResourcePoolException;
+
+// /**
+// * Sets whether or not maxAge should be interpreted
+// * as the maximum age since the resource was first acquired
+// * (age_is_absolute == true) or since the resource was last
+// * checked in (age_is_absolute == false).
+// */
+// public abstract void setAgeIsAbsolute( boolean age_is_absolute )
+// throws ResourcePoolException;
+
+// public abstract boolean getAgeIsAbsolute()
+// throws ResourcePoolException;
+
+ public abstract ResourcePool createPool(ResourcePool.Manager mgr)
+ throws ResourcePoolException;
+}
+
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolListener.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolListener.java
new file mode 100644
index 0000000..5219935
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolListener.java
@@ -0,0 +1,37 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import java.util.EventListener;
+
+public interface ResourcePoolListener extends EventListener
+{
+ public void resourceAcquired(ResourcePoolEvent evt);
+
+ public void resourceCheckedIn(ResourcePoolEvent evt);
+
+ public void resourceCheckedOut(ResourcePoolEvent evt);
+
+ public void resourceRemoved(ResourcePoolEvent evt);
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/ResourcePoolUtils.java b/src/classes/com/mchange/v2/resourcepool/ResourcePoolUtils.java
new file mode 100644
index 0000000..4e468e8
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/ResourcePoolUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+import com.mchange.v2.log.*;
+
+final class ResourcePoolUtils
+{
+ final static MLogger logger = MLog.getLogger( ResourcePoolUtils.class );
+
+ final static ResourcePoolException convertThrowable( String msg, Throwable t )
+ {
+ if (Debug.DEBUG)
+ {
+ //t.printStackTrace();
+ if (logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE , "Converting throwable to ResourcePoolException..." , t );
+ }
+ if ( t instanceof ResourcePoolException)
+ return (ResourcePoolException) t;
+ else
+ return new ResourcePoolException( msg, t );
+ }
+
+ final static ResourcePoolException convertThrowable( Throwable t )
+ { return convertThrowable("Ouch! " + t.toString(), t ); }
+}
diff --git a/src/classes/com/mchange/v2/resourcepool/TimeoutException.java b/src/classes/com/mchange/v2/resourcepool/TimeoutException.java
new file mode 100644
index 0000000..f0b1b74
--- /dev/null
+++ b/src/classes/com/mchange/v2/resourcepool/TimeoutException.java
@@ -0,0 +1,39 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.resourcepool;
+
+public class TimeoutException extends ResourcePoolException
+{
+ public TimeoutException(String msg, Throwable t)
+ {super(msg, t);}
+
+ public TimeoutException(Throwable t)
+ {super(t);}
+
+ public TimeoutException(String msg)
+ {super(msg);}
+
+ public TimeoutException()
+ {super();}
+}
diff --git a/src/classes/com/mchange/v2/ser/IndirectPolicy.java b/src/classes/com/mchange/v2/ser/IndirectPolicy.java
new file mode 100644
index 0000000..3acb929
--- /dev/null
+++ b/src/classes/com/mchange/v2/ser/IndirectPolicy.java
@@ -0,0 +1,39 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.ser;
+
+public final class IndirectPolicy
+{
+ public final static IndirectPolicy DEFINITELY_INDIRECT = new IndirectPolicy("DEFINITELY_INDIRECT");
+ public final static IndirectPolicy INDIRECT_ON_EXCEPTION = new IndirectPolicy("INDIRECT_ON_EXCEPTION");
+ public final static IndirectPolicy DEFINITELY_DIRECT = new IndirectPolicy("DEFINITELY_DIRECT");
+
+ String name;
+
+ private IndirectPolicy(String name)
+ { this.name = name; }
+
+ public String toString()
+ { return "[IndirectPolicy: " + name + ']'; }
+}
diff --git a/src/classes/com/mchange/v2/ser/IndirectlySerialized.java b/src/classes/com/mchange/v2/ser/IndirectlySerialized.java
new file mode 100644
index 0000000..6e46222
--- /dev/null
+++ b/src/classes/com/mchange/v2/ser/IndirectlySerialized.java
@@ -0,0 +1,33 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.ser;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+public interface IndirectlySerialized extends Serializable
+{
+ public Object getObject() throws ClassNotFoundException, IOException;
+}
+
diff --git a/src/classes/com/mchange/v2/ser/Indirector.java b/src/classes/com/mchange/v2/ser/Indirector.java
new file mode 100644
index 0000000..c40fcb4
--- /dev/null
+++ b/src/classes/com/mchange/v2/ser/Indirector.java
@@ -0,0 +1,29 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.ser;
+
+public interface Indirector
+{
+ public IndirectlySerialized indirectForm( Object orig ) throws Exception;
+}
diff --git a/src/classes/com/mchange/v2/ser/SerializableUtils.java b/src/classes/com/mchange/v2/ser/SerializableUtils.java
new file mode 100644
index 0000000..e659d41
--- /dev/null
+++ b/src/classes/com/mchange/v2/ser/SerializableUtils.java
@@ -0,0 +1,171 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.ser;
+
+import java.io.*;
+import com.mchange.v1.io.*;
+import com.mchange.v2.log.*;
+
+public final class SerializableUtils
+{
+ final static MLogger logger = MLog.getLogger( SerializableUtils.class );
+
+ private SerializableUtils()
+ {}
+
+
+ public static byte[] toByteArray(Object obj) throws NotSerializableException
+ { return serializeToByteArray( obj ); }
+
+ public static byte[] toByteArray(Object obj, Indirector indirector, IndirectPolicy policy) throws NotSerializableException
+ {
+ try
+ {
+ if (policy == IndirectPolicy.DEFINITELY_INDIRECT)
+ {
+ if (indirector == null)
+ throw new IllegalArgumentException("null indirector is not consistent with " + policy);
+
+ IndirectlySerialized indirect = indirector.indirectForm( obj );
+ return toByteArray( indirect );
+ }
+ else if ( policy == IndirectPolicy.INDIRECT_ON_EXCEPTION )
+ {
+ if (indirector == null)
+ throw new IllegalArgumentException("null indirector is not consistent with " + policy);
+
+ try { return toByteArray( obj ); }
+ catch ( NotSerializableException e )
+ { return toByteArray( obj, indirector, IndirectPolicy.DEFINITELY_INDIRECT ); }
+ }
+ else if (policy == IndirectPolicy.DEFINITELY_DIRECT)
+ return toByteArray( obj );
+ else
+ throw new InternalError("unknown indirecting policy: " + policy);
+ }
+ catch ( NotSerializableException e )
+ { throw e; }
+ catch ( Exception e )
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.WARNING ) )
+ logger.log( MLevel.WARNING, "An Exception occurred while serializing an Object to a byte[] with an Indirector.", e );
+ throw new NotSerializableException( e.toString() );
+ }
+ }
+
+ /**
+ * @deprecated use SerialializableUtils.toByteArray() [shorter name is better!]
+ */
+ public static byte[] serializeToByteArray(Object obj) throws NotSerializableException
+ {
+ try
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+ out.writeObject(obj);
+ return baos.toByteArray();
+ }
+ catch (NotSerializableException e)
+ {
+ //this is the only IOException that
+ //shouldn't signal a bizarre error...
+ e.fillInStackTrace();
+ throw e;
+ }
+ catch (IOException e)
+ {
+ //e.printStackTrace();
+ if ( logger.isLoggable( MLevel.SEVERE ) )
+ logger.log( MLevel.SEVERE, "An IOException occurred while writing into a ByteArrayOutputStream?!?", e );
+ throw new Error("IOException writing to a byte array!");
+ }
+ }
+
+ /**
+ * By default, unwraps IndirectlySerialized objects, returning the original
+ */
+ public static Object fromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
+ {
+ Object out = deserializeFromByteArray( bytes );
+ if (out instanceof IndirectlySerialized)
+ return ((IndirectlySerialized) out).getObject();
+ else
+ return out;
+ }
+
+ public static Object fromByteArray(byte[] bytes, boolean ignore_indirects) throws IOException, ClassNotFoundException
+ {
+ if (ignore_indirects)
+ return deserializeFromByteArray( bytes );
+ else
+ return fromByteArray( bytes );
+ }
+
+ /**
+ * @deprecated use SerialializableUtils.fromByteArray() [shorter name is better!]
+ */
+ public static Object deserializeFromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
+ {
+ ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
+ return in.readObject();
+ }
+
+
+ public static Object testSerializeDeserialize( Object o ) throws IOException, ClassNotFoundException
+ { return deepCopy( o ); }
+
+ public static Object deepCopy( Object o ) throws IOException, ClassNotFoundException
+ {
+ byte[] bytes = serializeToByteArray( o );
+ return deserializeFromByteArray( bytes );
+ }
+
+ public final static Object unmarshallObjectFromFile(File file)
+ throws IOException, ClassNotFoundException
+ {
+ ObjectInputStream in = null;
+ try
+ {
+ in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
+ return in.readObject();
+ }
+ finally
+ {InputStreamUtils.attemptClose(in);}
+ }
+
+ public final static void marshallObjectToFile(Object o, File file)
+ throws IOException
+ {
+ ObjectOutputStream out = null;
+ try
+ {
+ out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+ out.writeObject(o);
+ }
+ finally
+ {OutputStreamUtils.attemptClose(out);}
+ }
+}
+
diff --git a/src/classes/com/mchange/v2/ser/UnsupportedVersionException.java b/src/classes/com/mchange/v2/ser/UnsupportedVersionException.java
new file mode 100644
index 0000000..ac86db8
--- /dev/null
+++ b/src/classes/com/mchange/v2/ser/UnsupportedVersionException.java
@@ -0,0 +1,35 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.ser;
+
+import java.io.*;
+
+public class UnsupportedVersionException extends InvalidClassException
+{
+ public UnsupportedVersionException(String message)
+ {super(message);}
+
+ public UnsupportedVersionException(Object obj, int version)
+ {this(obj.getClass().getName() + " -- unsupported version: " + version);}
+}
diff --git a/src/classes/com/mchange/v2/sql/SqlUtils.java b/src/classes/com/mchange/v2/sql/SqlUtils.java
new file mode 100644
index 0000000..ee4e99b
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/SqlUtils.java
@@ -0,0 +1,118 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql;
+
+import java.sql.*;
+import com.mchange.v2.log.*;
+
+import java.util.Date;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import com.mchange.lang.ThrowableUtils;
+import com.mchange.v2.lang.VersionUtils;
+
+public final class SqlUtils
+{
+ final static MLogger logger = MLog.getLogger( SqlUtils.class );
+
+ // protected by SqlUtils.class' lock
+ final static DateFormat tsdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
+
+ public final static String DRIVER_MANAGER_USER_PROPERTY = "user";
+ public final static String DRIVER_MANAGER_PASSWORD_PROPERTY = "password";
+
+ public static String escapeBadSqlPatternChars(String s)
+ {
+ StringBuffer sb = new StringBuffer(s);
+ for (int i = 0, len = sb.length(); i < len; ++i)
+ if (sb.charAt(i) == '\'')
+ {
+ sb.insert(i, '\'');
+ ++len;
+ i+=2;
+ }
+ return sb.toString();
+ }
+
+ public synchronized static String escapeAsTimestamp( Date date )
+ { return "{ts '" + tsdf.format( date ) + "'}"; }
+
+ public static SQLException toSQLException(Throwable t)
+ { return toSQLException(null, t ); }
+
+ public static SQLException toSQLException(String msg, Throwable t)
+ { return toSQLException(msg, null, t);}
+
+ public static SQLException toSQLException(String msg, String sqlState, Throwable t)
+ {
+ if (t instanceof SQLException)
+ {
+ if (Debug.DEBUG &&
+ Debug.TRACE == Debug.TRACE_MAX &&
+ logger.isLoggable( MLevel.FINER ))
+ {
+ SQLException s = (SQLException) t;
+ StringBuffer tmp = new StringBuffer(255);
+ tmp.append("Attempted to convert SQLException to SQLException. Leaving it alone.");
+ tmp.append(" [SQLState: ");
+ tmp.append( s.getSQLState() );
+ tmp.append("; errorCode: " );
+ tmp.append( s.getErrorCode() );
+ tmp.append(']');
+ if (msg != null)
+ tmp.append(" Ignoring suggested message: '" + msg + "'.");
+ logger.log( MLevel.FINER, tmp.toString(), t );
+
+ SQLException s2 = s;
+ while ((s2 = s2.getNextException()) != null)
+ logger.log( MLevel.FINER, "Nested SQLException or SQLWarning: ", s2 );
+ }
+ return (SQLException) t;
+ }
+ else
+ {
+ if (Debug.DEBUG)
+ {
+ //t.printStackTrace();
+ if ( logger.isLoggable( MLevel.FINE ) )
+ logger.log( MLevel.FINE, "Converting Throwable to SQLException...", t );
+ }
+
+ if (msg == null)
+ msg = "An SQLException was provoked by the following failure: " + t.toString();
+ if ( VersionUtils.isAtLeastJavaVersion14() )
+ {
+ SQLException out = new SQLException(msg);
+ out.initCause( t );
+ return out;
+ }
+ else
+ return new SQLException( msg + System.getProperty( "line.separator" ) +
+ "[Cause: " + ThrowableUtils.extractStackTrace(t) + ']', sqlState);
+ }
+ }
+
+ private SqlUtils()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterCallableStatement.java b/src/classes/com/mchange/v2/sql/filter/FilterCallableStatement.java
new file mode 100644
index 0000000..5bfcb7c
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterCallableStatement.java
@@ -0,0 +1,523 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+public abstract class FilterCallableStatement implements CallableStatement
+{
+ protected CallableStatement inner;
+
+ public FilterCallableStatement(CallableStatement inner)
+ { this.inner = inner; }
+
+ public FilterCallableStatement()
+ {}
+
+ public void setInner( CallableStatement inner )
+ { this.inner = inner; }
+
+ public CallableStatement getInner()
+ { return inner; }
+
+ public boolean wasNull() throws SQLException
+ { return inner.wasNull(); }
+
+ public BigDecimal getBigDecimal(int a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public BigDecimal getBigDecimal(int a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public BigDecimal getBigDecimal(String a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public Timestamp getTimestamp(String a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public Timestamp getTimestamp(String a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public Timestamp getTimestamp(int a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public Timestamp getTimestamp(int a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public Blob getBlob(String a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public Blob getBlob(int a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public Clob getClob(String a) throws SQLException
+ { return inner.getClob(a); }
+
+ public Clob getClob(int a) throws SQLException
+ { return inner.getClob(a); }
+
+ public void setNull(String a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public void setNull(String a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public void setBigDecimal(String a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public void setBytes(String a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public void setTimestamp(String a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public void setTimestamp(String a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public void setAsciiStream(String a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public void setBinaryStream(String a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public void setObject(String a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public void setObject(String a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public void setObject(String a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public void setCharacterStream(String a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public void registerOutParameter(String a, int b) throws SQLException
+ { inner.registerOutParameter(a, b); }
+
+ public void registerOutParameter(int a, int b) throws SQLException
+ { inner.registerOutParameter(a, b); }
+
+ public void registerOutParameter(int a, int b, int c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public void registerOutParameter(int a, int b, String c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public void registerOutParameter(String a, int b, int c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public void registerOutParameter(String a, int b, String c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public Object getObject(String a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public Object getObject(int a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public Object getObject(int a) throws SQLException
+ { return inner.getObject(a); }
+
+ public Object getObject(String a) throws SQLException
+ { return inner.getObject(a); }
+
+ public boolean getBoolean(int a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public boolean getBoolean(String a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public byte getByte(String a) throws SQLException
+ { return inner.getByte(a); }
+
+ public byte getByte(int a) throws SQLException
+ { return inner.getByte(a); }
+
+ public short getShort(int a) throws SQLException
+ { return inner.getShort(a); }
+
+ public short getShort(String a) throws SQLException
+ { return inner.getShort(a); }
+
+ public int getInt(String a) throws SQLException
+ { return inner.getInt(a); }
+
+ public int getInt(int a) throws SQLException
+ { return inner.getInt(a); }
+
+ public long getLong(int a) throws SQLException
+ { return inner.getLong(a); }
+
+ public long getLong(String a) throws SQLException
+ { return inner.getLong(a); }
+
+ public float getFloat(String a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public float getFloat(int a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public double getDouble(String a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public double getDouble(int a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public byte[] getBytes(int a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public byte[] getBytes(String a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public URL getURL(String a) throws SQLException
+ { return inner.getURL(a); }
+
+ public URL getURL(int a) throws SQLException
+ { return inner.getURL(a); }
+
+ public void setBoolean(String a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public void setByte(String a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public void setShort(String a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public void setInt(String a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public void setLong(String a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public void setFloat(String a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public void setDouble(String a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public String getString(String a) throws SQLException
+ { return inner.getString(a); }
+
+ public String getString(int a) throws SQLException
+ { return inner.getString(a); }
+
+ public Ref getRef(int a) throws SQLException
+ { return inner.getRef(a); }
+
+ public Ref getRef(String a) throws SQLException
+ { return inner.getRef(a); }
+
+ public void setURL(String a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public void setTime(String a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public void setTime(String a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public Time getTime(int a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public Time getTime(String a) throws SQLException
+ { return inner.getTime(a); }
+
+ public Time getTime(int a) throws SQLException
+ { return inner.getTime(a); }
+
+ public Time getTime(String a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public Date getDate(int a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public Date getDate(String a) throws SQLException
+ { return inner.getDate(a); }
+
+ public Date getDate(int a) throws SQLException
+ { return inner.getDate(a); }
+
+ public Date getDate(String a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public void setString(String a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public Array getArray(int a) throws SQLException
+ { return inner.getArray(a); }
+
+ public Array getArray(String a) throws SQLException
+ { return inner.getArray(a); }
+
+ public void setDate(String a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public void setDate(String a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public ResultSet executeQuery() throws SQLException
+ { return inner.executeQuery(); }
+
+ public int executeUpdate() throws SQLException
+ { return inner.executeUpdate(); }
+
+ public void addBatch() throws SQLException
+ { inner.addBatch(); }
+
+ public void setNull(int a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public void setNull(int a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public void setBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public void setBytes(int a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public void setTimestamp(int a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public void setTimestamp(int a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public void setAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public void setUnicodeStream(int a, InputStream b, int c) throws SQLException
+ { inner.setUnicodeStream(a, b, c); }
+
+ public void setBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public void clearParameters() throws SQLException
+ { inner.clearParameters(); }
+
+ public void setObject(int a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public void setObject(int a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public void setObject(int a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public void setCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public void setRef(int a, Ref b) throws SQLException
+ { inner.setRef(a, b); }
+
+ public void setBlob(int a, Blob b) throws SQLException
+ { inner.setBlob(a, b); }
+
+ public void setClob(int a, Clob b) throws SQLException
+ { inner.setClob(a, b); }
+
+ public void setArray(int a, Array b) throws SQLException
+ { inner.setArray(a, b); }
+
+ public ParameterMetaData getParameterMetaData() throws SQLException
+ { return inner.getParameterMetaData(); }
+
+ public void setBoolean(int a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public void setByte(int a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public void setShort(int a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public void setInt(int a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public void setLong(int a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public void setFloat(int a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public void setDouble(int a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public void setURL(int a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public void setTime(int a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public void setTime(int a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public boolean execute() throws SQLException
+ { return inner.execute(); }
+
+ public void setString(int a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public void setDate(int a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public void setDate(int a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public void close() throws SQLException
+ { inner.close(); }
+
+ public boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterConnection.java b/src/classes/com/mchange/v2/sql/filter/FilterConnection.java
new file mode 100644
index 0000000..b470cc0
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterConnection.java
@@ -0,0 +1,160 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.util.Map;
+
+public abstract class FilterConnection implements Connection
+{
+ protected Connection inner;
+
+ public FilterConnection(Connection inner)
+ { this.inner = inner; }
+
+ public FilterConnection()
+ {}
+
+ public void setInner( Connection inner )
+ { this.inner = inner; }
+
+ public Connection getInner()
+ { return inner; }
+
+ public Statement createStatement(int a, int b, int c) throws SQLException
+ { return inner.createStatement(a, b, c); }
+
+ public Statement createStatement(int a, int b) throws SQLException
+ { return inner.createStatement(a, b); }
+
+ public Statement createStatement() throws SQLException
+ { return inner.createStatement(); }
+
+ public PreparedStatement prepareStatement(String a, String[] b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public PreparedStatement prepareStatement(String a) throws SQLException
+ { return inner.prepareStatement(a); }
+
+ public PreparedStatement prepareStatement(String a, int b, int c) throws SQLException
+ { return inner.prepareStatement(a, b, c); }
+
+ public PreparedStatement prepareStatement(String a, int b, int c, int d) throws SQLException
+ { return inner.prepareStatement(a, b, c, d); }
+
+ public PreparedStatement prepareStatement(String a, int b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public PreparedStatement prepareStatement(String a, int[] b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public CallableStatement prepareCall(String a, int b, int c, int d) throws SQLException
+ { return inner.prepareCall(a, b, c, d); }
+
+ public CallableStatement prepareCall(String a, int b, int c) throws SQLException
+ { return inner.prepareCall(a, b, c); }
+
+ public CallableStatement prepareCall(String a) throws SQLException
+ { return inner.prepareCall(a); }
+
+ public String nativeSQL(String a) throws SQLException
+ { return inner.nativeSQL(a); }
+
+ public void setAutoCommit(boolean a) throws SQLException
+ { inner.setAutoCommit(a); }
+
+ public boolean getAutoCommit() throws SQLException
+ { return inner.getAutoCommit(); }
+
+ public void commit() throws SQLException
+ { inner.commit(); }
+
+ public void rollback(Savepoint a) throws SQLException
+ { inner.rollback(a); }
+
+ public void rollback() throws SQLException
+ { inner.rollback(); }
+
+ public DatabaseMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public void setCatalog(String a) throws SQLException
+ { inner.setCatalog(a); }
+
+ public String getCatalog() throws SQLException
+ { return inner.getCatalog(); }
+
+ public void setTransactionIsolation(int a) throws SQLException
+ { inner.setTransactionIsolation(a); }
+
+ public int getTransactionIsolation() throws SQLException
+ { return inner.getTransactionIsolation(); }
+
+ public SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public Map getTypeMap() throws SQLException
+ { return inner.getTypeMap(); }
+
+ public void setTypeMap(Map a) throws SQLException
+ { inner.setTypeMap(a); }
+
+ public void setHoldability(int a) throws SQLException
+ { inner.setHoldability(a); }
+
+ public int getHoldability() throws SQLException
+ { return inner.getHoldability(); }
+
+ public Savepoint setSavepoint() throws SQLException
+ { return inner.setSavepoint(); }
+
+ public Savepoint setSavepoint(String a) throws SQLException
+ { return inner.setSavepoint(a); }
+
+ public void releaseSavepoint(Savepoint a) throws SQLException
+ { inner.releaseSavepoint(a); }
+
+ public void setReadOnly(boolean a) throws SQLException
+ { inner.setReadOnly(a); }
+
+ public boolean isReadOnly() throws SQLException
+ { return inner.isReadOnly(); }
+
+ public void close() throws SQLException
+ { inner.close(); }
+
+ public boolean isClosed() throws SQLException
+ { return inner.isClosed(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterDatabaseMetaData.java b/src/classes/com/mchange/v2/sql/filter/FilterDatabaseMetaData.java
new file mode 100644
index 0000000..3b8d000
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterDatabaseMetaData.java
@@ -0,0 +1,542 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public abstract class FilterDatabaseMetaData implements DatabaseMetaData
+{
+ protected DatabaseMetaData inner;
+
+ public FilterDatabaseMetaData(DatabaseMetaData inner)
+ { this.inner = inner; }
+
+ public FilterDatabaseMetaData()
+ {}
+
+ public void setInner( DatabaseMetaData inner )
+ { this.inner = inner; }
+
+ public DatabaseMetaData getInner()
+ { return inner; }
+
+ public boolean allProceduresAreCallable() throws SQLException
+ { return inner.allProceduresAreCallable(); }
+
+ public boolean allTablesAreSelectable() throws SQLException
+ { return inner.allTablesAreSelectable(); }
+
+ public boolean nullsAreSortedHigh() throws SQLException
+ { return inner.nullsAreSortedHigh(); }
+
+ public boolean nullsAreSortedLow() throws SQLException
+ { return inner.nullsAreSortedLow(); }
+
+ public boolean nullsAreSortedAtStart() throws SQLException
+ { return inner.nullsAreSortedAtStart(); }
+
+ public boolean nullsAreSortedAtEnd() throws SQLException
+ { return inner.nullsAreSortedAtEnd(); }
+
+ public String getDatabaseProductName() throws SQLException
+ { return inner.getDatabaseProductName(); }
+
+ public String getDatabaseProductVersion() throws SQLException
+ { return inner.getDatabaseProductVersion(); }
+
+ public String getDriverName() throws SQLException
+ { return inner.getDriverName(); }
+
+ public String getDriverVersion() throws SQLException
+ { return inner.getDriverVersion(); }
+
+ public int getDriverMajorVersion()
+ { return inner.getDriverMajorVersion(); }
+
+ public int getDriverMinorVersion()
+ { return inner.getDriverMinorVersion(); }
+
+ public boolean usesLocalFiles() throws SQLException
+ { return inner.usesLocalFiles(); }
+
+ public boolean usesLocalFilePerTable() throws SQLException
+ { return inner.usesLocalFilePerTable(); }
+
+ public boolean supportsMixedCaseIdentifiers() throws SQLException
+ { return inner.supportsMixedCaseIdentifiers(); }
+
+ public boolean storesUpperCaseIdentifiers() throws SQLException
+ { return inner.storesUpperCaseIdentifiers(); }
+
+ public boolean storesLowerCaseIdentifiers() throws SQLException
+ { return inner.storesLowerCaseIdentifiers(); }
+
+ public boolean storesMixedCaseIdentifiers() throws SQLException
+ { return inner.storesMixedCaseIdentifiers(); }
+
+ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException
+ { return inner.supportsMixedCaseQuotedIdentifiers(); }
+
+ public boolean storesUpperCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesUpperCaseQuotedIdentifiers(); }
+
+ public boolean storesLowerCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesLowerCaseQuotedIdentifiers(); }
+
+ public boolean storesMixedCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesMixedCaseQuotedIdentifiers(); }
+
+ public String getIdentifierQuoteString() throws SQLException
+ { return inner.getIdentifierQuoteString(); }
+
+ public String getSQLKeywords() throws SQLException
+ { return inner.getSQLKeywords(); }
+
+ public String getNumericFunctions() throws SQLException
+ { return inner.getNumericFunctions(); }
+
+ public String getStringFunctions() throws SQLException
+ { return inner.getStringFunctions(); }
+
+ public String getSystemFunctions() throws SQLException
+ { return inner.getSystemFunctions(); }
+
+ public String getTimeDateFunctions() throws SQLException
+ { return inner.getTimeDateFunctions(); }
+
+ public String getSearchStringEscape() throws SQLException
+ { return inner.getSearchStringEscape(); }
+
+ public String getExtraNameCharacters() throws SQLException
+ { return inner.getExtraNameCharacters(); }
+
+ public boolean supportsAlterTableWithAddColumn() throws SQLException
+ { return inner.supportsAlterTableWithAddColumn(); }
+
+ public boolean supportsAlterTableWithDropColumn() throws SQLException
+ { return inner.supportsAlterTableWithDropColumn(); }
+
+ public boolean supportsColumnAliasing() throws SQLException
+ { return inner.supportsColumnAliasing(); }
+
+ public boolean nullPlusNonNullIsNull() throws SQLException
+ { return inner.nullPlusNonNullIsNull(); }
+
+ public boolean supportsConvert() throws SQLException
+ { return inner.supportsConvert(); }
+
+ public boolean supportsConvert(int a, int b) throws SQLException
+ { return inner.supportsConvert(a, b); }
+
+ public boolean supportsTableCorrelationNames() throws SQLException
+ { return inner.supportsTableCorrelationNames(); }
+
+ public boolean supportsDifferentTableCorrelationNames() throws SQLException
+ { return inner.supportsDifferentTableCorrelationNames(); }
+
+ public boolean supportsExpressionsInOrderBy() throws SQLException
+ { return inner.supportsExpressionsInOrderBy(); }
+
+ public boolean supportsOrderByUnrelated() throws SQLException
+ { return inner.supportsOrderByUnrelated(); }
+
+ public boolean supportsGroupBy() throws SQLException
+ { return inner.supportsGroupBy(); }
+
+ public boolean supportsGroupByUnrelated() throws SQLException
+ { return inner.supportsGroupByUnrelated(); }
+
+ public boolean supportsGroupByBeyondSelect() throws SQLException
+ { return inner.supportsGroupByBeyondSelect(); }
+
+ public boolean supportsLikeEscapeClause() throws SQLException
+ { return inner.supportsLikeEscapeClause(); }
+
+ public boolean supportsMultipleResultSets() throws SQLException
+ { return inner.supportsMultipleResultSets(); }
+
+ public boolean supportsMultipleTransactions() throws SQLException
+ { return inner.supportsMultipleTransactions(); }
+
+ public boolean supportsNonNullableColumns() throws SQLException
+ { return inner.supportsNonNullableColumns(); }
+
+ public boolean supportsMinimumSQLGrammar() throws SQLException
+ { return inner.supportsMinimumSQLGrammar(); }
+
+ public boolean supportsCoreSQLGrammar() throws SQLException
+ { return inner.supportsCoreSQLGrammar(); }
+
+ public boolean supportsExtendedSQLGrammar() throws SQLException
+ { return inner.supportsExtendedSQLGrammar(); }
+
+ public boolean supportsANSI92EntryLevelSQL() throws SQLException
+ { return inner.supportsANSI92EntryLevelSQL(); }
+
+ public boolean supportsANSI92IntermediateSQL() throws SQLException
+ { return inner.supportsANSI92IntermediateSQL(); }
+
+ public boolean supportsANSI92FullSQL() throws SQLException
+ { return inner.supportsANSI92FullSQL(); }
+
+ public boolean supportsIntegrityEnhancementFacility() throws SQLException
+ { return inner.supportsIntegrityEnhancementFacility(); }
+
+ public boolean supportsOuterJoins() throws SQLException
+ { return inner.supportsOuterJoins(); }
+
+ public boolean supportsFullOuterJoins() throws SQLException
+ { return inner.supportsFullOuterJoins(); }
+
+ public boolean supportsLimitedOuterJoins() throws SQLException
+ { return inner.supportsLimitedOuterJoins(); }
+
+ public String getSchemaTerm() throws SQLException
+ { return inner.getSchemaTerm(); }
+
+ public String getProcedureTerm() throws SQLException
+ { return inner.getProcedureTerm(); }
+
+ public String getCatalogTerm() throws SQLException
+ { return inner.getCatalogTerm(); }
+
+ public boolean isCatalogAtStart() throws SQLException
+ { return inner.isCatalogAtStart(); }
+
+ public String getCatalogSeparator() throws SQLException
+ { return inner.getCatalogSeparator(); }
+
+ public boolean supportsSchemasInDataManipulation() throws SQLException
+ { return inner.supportsSchemasInDataManipulation(); }
+
+ public boolean supportsSchemasInProcedureCalls() throws SQLException
+ { return inner.supportsSchemasInProcedureCalls(); }
+
+ public boolean supportsSchemasInTableDefinitions() throws SQLException
+ { return inner.supportsSchemasInTableDefinitions(); }
+
+ public boolean supportsSchemasInIndexDefinitions() throws SQLException
+ { return inner.supportsSchemasInIndexDefinitions(); }
+
+ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException
+ { return inner.supportsSchemasInPrivilegeDefinitions(); }
+
+ public boolean supportsCatalogsInDataManipulation() throws SQLException
+ { return inner.supportsCatalogsInDataManipulation(); }
+
+ public boolean supportsCatalogsInProcedureCalls() throws SQLException
+ { return inner.supportsCatalogsInProcedureCalls(); }
+
+ public boolean supportsCatalogsInTableDefinitions() throws SQLException
+ { return inner.supportsCatalogsInTableDefinitions(); }
+
+ public boolean supportsCatalogsInIndexDefinitions() throws SQLException
+ { return inner.supportsCatalogsInIndexDefinitions(); }
+
+ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException
+ { return inner.supportsCatalogsInPrivilegeDefinitions(); }
+
+ public boolean supportsPositionedDelete() throws SQLException
+ { return inner.supportsPositionedDelete(); }
+
+ public boolean supportsPositionedUpdate() throws SQLException
+ { return inner.supportsPositionedUpdate(); }
+
+ public boolean supportsSelectForUpdate() throws SQLException
+ { return inner.supportsSelectForUpdate(); }
+
+ public boolean supportsStoredProcedures() throws SQLException
+ { return inner.supportsStoredProcedures(); }
+
+ public boolean supportsSubqueriesInComparisons() throws SQLException
+ { return inner.supportsSubqueriesInComparisons(); }
+
+ public boolean supportsSubqueriesInExists() throws SQLException
+ { return inner.supportsSubqueriesInExists(); }
+
+ public boolean supportsSubqueriesInIns() throws SQLException
+ { return inner.supportsSubqueriesInIns(); }
+
+ public boolean supportsSubqueriesInQuantifieds() throws SQLException
+ { return inner.supportsSubqueriesInQuantifieds(); }
+
+ public boolean supportsCorrelatedSubqueries() throws SQLException
+ { return inner.supportsCorrelatedSubqueries(); }
+
+ public boolean supportsUnion() throws SQLException
+ { return inner.supportsUnion(); }
+
+ public boolean supportsUnionAll() throws SQLException
+ { return inner.supportsUnionAll(); }
+
+ public boolean supportsOpenCursorsAcrossCommit() throws SQLException
+ { return inner.supportsOpenCursorsAcrossCommit(); }
+
+ public boolean supportsOpenCursorsAcrossRollback() throws SQLException
+ { return inner.supportsOpenCursorsAcrossRollback(); }
+
+ public boolean supportsOpenStatementsAcrossCommit() throws SQLException
+ { return inner.supportsOpenStatementsAcrossCommit(); }
+
+ public boolean supportsOpenStatementsAcrossRollback() throws SQLException
+ { return inner.supportsOpenStatementsAcrossRollback(); }
+
+ public int getMaxBinaryLiteralLength() throws SQLException
+ { return inner.getMaxBinaryLiteralLength(); }
+
+ public int getMaxCharLiteralLength() throws SQLException
+ { return inner.getMaxCharLiteralLength(); }
+
+ public int getMaxColumnNameLength() throws SQLException
+ { return inner.getMaxColumnNameLength(); }
+
+ public int getMaxColumnsInGroupBy() throws SQLException
+ { return inner.getMaxColumnsInGroupBy(); }
+
+ public int getMaxColumnsInIndex() throws SQLException
+ { return inner.getMaxColumnsInIndex(); }
+
+ public int getMaxColumnsInOrderBy() throws SQLException
+ { return inner.getMaxColumnsInOrderBy(); }
+
+ public int getMaxColumnsInSelect() throws SQLException
+ { return inner.getMaxColumnsInSelect(); }
+
+ public int getMaxColumnsInTable() throws SQLException
+ { return inner.getMaxColumnsInTable(); }
+
+ public int getMaxConnections() throws SQLException
+ { return inner.getMaxConnections(); }
+
+ public int getMaxCursorNameLength() throws SQLException
+ { return inner.getMaxCursorNameLength(); }
+
+ public int getMaxIndexLength() throws SQLException
+ { return inner.getMaxIndexLength(); }
+
+ public int getMaxSchemaNameLength() throws SQLException
+ { return inner.getMaxSchemaNameLength(); }
+
+ public int getMaxProcedureNameLength() throws SQLException
+ { return inner.getMaxProcedureNameLength(); }
+
+ public int getMaxCatalogNameLength() throws SQLException
+ { return inner.getMaxCatalogNameLength(); }
+
+ public int getMaxRowSize() throws SQLException
+ { return inner.getMaxRowSize(); }
+
+ public boolean doesMaxRowSizeIncludeBlobs() throws SQLException
+ { return inner.doesMaxRowSizeIncludeBlobs(); }
+
+ public int getMaxStatementLength() throws SQLException
+ { return inner.getMaxStatementLength(); }
+
+ public int getMaxStatements() throws SQLException
+ { return inner.getMaxStatements(); }
+
+ public int getMaxTableNameLength() throws SQLException
+ { return inner.getMaxTableNameLength(); }
+
+ public int getMaxTablesInSelect() throws SQLException
+ { return inner.getMaxTablesInSelect(); }
+
+ public int getMaxUserNameLength() throws SQLException
+ { return inner.getMaxUserNameLength(); }
+
+ public int getDefaultTransactionIsolation() throws SQLException
+ { return inner.getDefaultTransactionIsolation(); }
+
+ public boolean supportsTransactions() throws SQLException
+ { return inner.supportsTransactions(); }
+
+ public boolean supportsTransactionIsolationLevel(int a) throws SQLException
+ { return inner.supportsTransactionIsolationLevel(a); }
+
+ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException
+ { return inner.supportsDataDefinitionAndDataManipulationTransactions(); }
+
+ public boolean supportsDataManipulationTransactionsOnly() throws SQLException
+ { return inner.supportsDataManipulationTransactionsOnly(); }
+
+ public boolean dataDefinitionCausesTransactionCommit() throws SQLException
+ { return inner.dataDefinitionCausesTransactionCommit(); }
+
+ public boolean dataDefinitionIgnoredInTransactions() throws SQLException
+ { return inner.dataDefinitionIgnoredInTransactions(); }
+
+ public ResultSet getProcedures(String a, String b, String c) throws SQLException
+ { return inner.getProcedures(a, b, c); }
+
+ public ResultSet getProcedureColumns(String a, String b, String c, String d) throws SQLException
+ { return inner.getProcedureColumns(a, b, c, d); }
+
+ public ResultSet getTables(String a, String b, String c, String[] d) throws SQLException
+ { return inner.getTables(a, b, c, d); }
+
+ public ResultSet getSchemas() throws SQLException
+ { return inner.getSchemas(); }
+
+ public ResultSet getCatalogs() throws SQLException
+ { return inner.getCatalogs(); }
+
+ public ResultSet getTableTypes() throws SQLException
+ { return inner.getTableTypes(); }
+
+ public ResultSet getColumnPrivileges(String a, String b, String c, String d) throws SQLException
+ { return inner.getColumnPrivileges(a, b, c, d); }
+
+ public ResultSet getTablePrivileges(String a, String b, String c) throws SQLException
+ { return inner.getTablePrivileges(a, b, c); }
+
+ public ResultSet getBestRowIdentifier(String a, String b, String c, int d, boolean e) throws SQLException
+ { return inner.getBestRowIdentifier(a, b, c, d, e); }
+
+ public ResultSet getVersionColumns(String a, String b, String c) throws SQLException
+ { return inner.getVersionColumns(a, b, c); }
+
+ public ResultSet getPrimaryKeys(String a, String b, String c) throws SQLException
+ { return inner.getPrimaryKeys(a, b, c); }
+
+ public ResultSet getImportedKeys(String a, String b, String c) throws SQLException
+ { return inner.getImportedKeys(a, b, c); }
+
+ public ResultSet getExportedKeys(String a, String b, String c) throws SQLException
+ { return inner.getExportedKeys(a, b, c); }
+
+ public ResultSet getCrossReference(String a, String b, String c, String d, String e, String f) throws SQLException
+ { return inner.getCrossReference(a, b, c, d, e, f); }
+
+ public ResultSet getTypeInfo() throws SQLException
+ { return inner.getTypeInfo(); }
+
+ public ResultSet getIndexInfo(String a, String b, String c, boolean d, boolean e) throws SQLException
+ { return inner.getIndexInfo(a, b, c, d, e); }
+
+ public boolean supportsResultSetType(int a) throws SQLException
+ { return inner.supportsResultSetType(a); }
+
+ public boolean supportsResultSetConcurrency(int a, int b) throws SQLException
+ { return inner.supportsResultSetConcurrency(a, b); }
+
+ public boolean ownUpdatesAreVisible(int a) throws SQLException
+ { return inner.ownUpdatesAreVisible(a); }
+
+ public boolean ownDeletesAreVisible(int a) throws SQLException
+ { return inner.ownDeletesAreVisible(a); }
+
+ public boolean ownInsertsAreVisible(int a) throws SQLException
+ { return inner.ownInsertsAreVisible(a); }
+
+ public boolean othersUpdatesAreVisible(int a) throws SQLException
+ { return inner.othersUpdatesAreVisible(a); }
+
+ public boolean othersDeletesAreVisible(int a) throws SQLException
+ { return inner.othersDeletesAreVisible(a); }
+
+ public boolean othersInsertsAreVisible(int a) throws SQLException
+ { return inner.othersInsertsAreVisible(a); }
+
+ public boolean updatesAreDetected(int a) throws SQLException
+ { return inner.updatesAreDetected(a); }
+
+ public boolean deletesAreDetected(int a) throws SQLException
+ { return inner.deletesAreDetected(a); }
+
+ public boolean insertsAreDetected(int a) throws SQLException
+ { return inner.insertsAreDetected(a); }
+
+ public boolean supportsBatchUpdates() throws SQLException
+ { return inner.supportsBatchUpdates(); }
+
+ public ResultSet getUDTs(String a, String b, String c, int[] d) throws SQLException
+ { return inner.getUDTs(a, b, c, d); }
+
+ public boolean supportsSavepoints() throws SQLException
+ { return inner.supportsSavepoints(); }
+
+ public boolean supportsNamedParameters() throws SQLException
+ { return inner.supportsNamedParameters(); }
+
+ public boolean supportsMultipleOpenResults() throws SQLException
+ { return inner.supportsMultipleOpenResults(); }
+
+ public boolean supportsGetGeneratedKeys() throws SQLException
+ { return inner.supportsGetGeneratedKeys(); }
+
+ public ResultSet getSuperTypes(String a, String b, String c) throws SQLException
+ { return inner.getSuperTypes(a, b, c); }
+
+ public ResultSet getSuperTables(String a, String b, String c) throws SQLException
+ { return inner.getSuperTables(a, b, c); }
+
+ public boolean supportsResultSetHoldability(int a) throws SQLException
+ { return inner.supportsResultSetHoldability(a); }
+
+ public int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public int getDatabaseMajorVersion() throws SQLException
+ { return inner.getDatabaseMajorVersion(); }
+
+ public int getDatabaseMinorVersion() throws SQLException
+ { return inner.getDatabaseMinorVersion(); }
+
+ public int getJDBCMajorVersion() throws SQLException
+ { return inner.getJDBCMajorVersion(); }
+
+ public int getJDBCMinorVersion() throws SQLException
+ { return inner.getJDBCMinorVersion(); }
+
+ public int getSQLStateType() throws SQLException
+ { return inner.getSQLStateType(); }
+
+ public boolean locatorsUpdateCopy() throws SQLException
+ { return inner.locatorsUpdateCopy(); }
+
+ public boolean supportsStatementPooling() throws SQLException
+ { return inner.supportsStatementPooling(); }
+
+ public String getURL() throws SQLException
+ { return inner.getURL(); }
+
+ public boolean isReadOnly() throws SQLException
+ { return inner.isReadOnly(); }
+
+ public ResultSet getAttributes(String a, String b, String c, String d) throws SQLException
+ { return inner.getAttributes(a, b, c, d); }
+
+ public Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public ResultSet getColumns(String a, String b, String c, String d) throws SQLException
+ { return inner.getColumns(a, b, c, d); }
+
+ public String getUserName() throws SQLException
+ { return inner.getUserName(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterPreparedStatement.java b/src/classes/com/mchange/v2/sql/filter/FilterPreparedStatement.java
new file mode 100644
index 0000000..1d8ff68
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterPreparedStatement.java
@@ -0,0 +1,285 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+public abstract class FilterPreparedStatement implements PreparedStatement
+{
+ protected PreparedStatement inner;
+
+ public FilterPreparedStatement(PreparedStatement inner)
+ { this.inner = inner; }
+
+ public FilterPreparedStatement()
+ {}
+
+ public void setInner( PreparedStatement inner )
+ { this.inner = inner; }
+
+ public PreparedStatement getInner()
+ { return inner; }
+
+ public ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public ResultSet executeQuery() throws SQLException
+ { return inner.executeQuery(); }
+
+ public int executeUpdate() throws SQLException
+ { return inner.executeUpdate(); }
+
+ public void addBatch() throws SQLException
+ { inner.addBatch(); }
+
+ public void setNull(int a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public void setNull(int a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public void setBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public void setBytes(int a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public void setTimestamp(int a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public void setTimestamp(int a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public void setAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public void setUnicodeStream(int a, InputStream b, int c) throws SQLException
+ { inner.setUnicodeStream(a, b, c); }
+
+ public void setBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public void clearParameters() throws SQLException
+ { inner.clearParameters(); }
+
+ public void setObject(int a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public void setObject(int a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public void setObject(int a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public void setCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public void setRef(int a, Ref b) throws SQLException
+ { inner.setRef(a, b); }
+
+ public void setBlob(int a, Blob b) throws SQLException
+ { inner.setBlob(a, b); }
+
+ public void setClob(int a, Clob b) throws SQLException
+ { inner.setClob(a, b); }
+
+ public void setArray(int a, Array b) throws SQLException
+ { inner.setArray(a, b); }
+
+ public ParameterMetaData getParameterMetaData() throws SQLException
+ { return inner.getParameterMetaData(); }
+
+ public void setBoolean(int a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public void setByte(int a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public void setShort(int a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public void setInt(int a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public void setLong(int a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public void setFloat(int a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public void setDouble(int a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public void setURL(int a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public void setTime(int a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public void setTime(int a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public boolean execute() throws SQLException
+ { return inner.execute(); }
+
+ public void setString(int a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public void setDate(int a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public void setDate(int a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public void close() throws SQLException
+ { inner.close(); }
+
+ public boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterResultSet.java b/src/classes/com/mchange/v2/sql/filter/FilterResultSet.java
new file mode 100644
index 0000000..fdb1777
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterResultSet.java
@@ -0,0 +1,479 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+public abstract class FilterResultSet implements ResultSet
+{
+ protected ResultSet inner;
+
+ public FilterResultSet(ResultSet inner)
+ { this.inner = inner; }
+
+ public FilterResultSet()
+ {}
+
+ public void setInner( ResultSet inner )
+ { this.inner = inner; }
+
+ public ResultSet getInner()
+ { return inner; }
+
+ public ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public boolean wasNull() throws SQLException
+ { return inner.wasNull(); }
+
+ public BigDecimal getBigDecimal(int a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public BigDecimal getBigDecimal(String a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public BigDecimal getBigDecimal(int a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public BigDecimal getBigDecimal(String a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public Timestamp getTimestamp(int a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public Timestamp getTimestamp(String a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public Timestamp getTimestamp(int a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public Timestamp getTimestamp(String a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public InputStream getAsciiStream(String a) throws SQLException
+ { return inner.getAsciiStream(a); }
+
+ public InputStream getAsciiStream(int a) throws SQLException
+ { return inner.getAsciiStream(a); }
+
+ public InputStream getUnicodeStream(String a) throws SQLException
+ { return inner.getUnicodeStream(a); }
+
+ public InputStream getUnicodeStream(int a) throws SQLException
+ { return inner.getUnicodeStream(a); }
+
+ public InputStream getBinaryStream(int a) throws SQLException
+ { return inner.getBinaryStream(a); }
+
+ public InputStream getBinaryStream(String a) throws SQLException
+ { return inner.getBinaryStream(a); }
+
+ public String getCursorName() throws SQLException
+ { return inner.getCursorName(); }
+
+ public Reader getCharacterStream(int a) throws SQLException
+ { return inner.getCharacterStream(a); }
+
+ public Reader getCharacterStream(String a) throws SQLException
+ { return inner.getCharacterStream(a); }
+
+ public boolean isBeforeFirst() throws SQLException
+ { return inner.isBeforeFirst(); }
+
+ public boolean isAfterLast() throws SQLException
+ { return inner.isAfterLast(); }
+
+ public boolean isFirst() throws SQLException
+ { return inner.isFirst(); }
+
+ public boolean isLast() throws SQLException
+ { return inner.isLast(); }
+
+ public void beforeFirst() throws SQLException
+ { inner.beforeFirst(); }
+
+ public void afterLast() throws SQLException
+ { inner.afterLast(); }
+
+ public boolean absolute(int a) throws SQLException
+ { return inner.absolute(a); }
+
+ public void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public int getConcurrency() throws SQLException
+ { return inner.getConcurrency(); }
+
+ public boolean rowUpdated() throws SQLException
+ { return inner.rowUpdated(); }
+
+ public boolean rowInserted() throws SQLException
+ { return inner.rowInserted(); }
+
+ public boolean rowDeleted() throws SQLException
+ { return inner.rowDeleted(); }
+
+ public void updateNull(int a) throws SQLException
+ { inner.updateNull(a); }
+
+ public void updateNull(String a) throws SQLException
+ { inner.updateNull(a); }
+
+ public void updateBoolean(int a, boolean b) throws SQLException
+ { inner.updateBoolean(a, b); }
+
+ public void updateBoolean(String a, boolean b) throws SQLException
+ { inner.updateBoolean(a, b); }
+
+ public void updateByte(int a, byte b) throws SQLException
+ { inner.updateByte(a, b); }
+
+ public void updateByte(String a, byte b) throws SQLException
+ { inner.updateByte(a, b); }
+
+ public void updateShort(int a, short b) throws SQLException
+ { inner.updateShort(a, b); }
+
+ public void updateShort(String a, short b) throws SQLException
+ { inner.updateShort(a, b); }
+
+ public void updateInt(String a, int b) throws SQLException
+ { inner.updateInt(a, b); }
+
+ public void updateInt(int a, int b) throws SQLException
+ { inner.updateInt(a, b); }
+
+ public void updateLong(int a, long b) throws SQLException
+ { inner.updateLong(a, b); }
+
+ public void updateLong(String a, long b) throws SQLException
+ { inner.updateLong(a, b); }
+
+ public void updateFloat(String a, float b) throws SQLException
+ { inner.updateFloat(a, b); }
+
+ public void updateFloat(int a, float b) throws SQLException
+ { inner.updateFloat(a, b); }
+
+ public void updateDouble(String a, double b) throws SQLException
+ { inner.updateDouble(a, b); }
+
+ public void updateDouble(int a, double b) throws SQLException
+ { inner.updateDouble(a, b); }
+
+ public void updateBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.updateBigDecimal(a, b); }
+
+ public void updateBigDecimal(String a, BigDecimal b) throws SQLException
+ { inner.updateBigDecimal(a, b); }
+
+ public void updateString(String a, String b) throws SQLException
+ { inner.updateString(a, b); }
+
+ public void updateString(int a, String b) throws SQLException
+ { inner.updateString(a, b); }
+
+ public void updateBytes(int a, byte[] b) throws SQLException
+ { inner.updateBytes(a, b); }
+
+ public void updateBytes(String a, byte[] b) throws SQLException
+ { inner.updateBytes(a, b); }
+
+ public void updateDate(String a, Date b) throws SQLException
+ { inner.updateDate(a, b); }
+
+ public void updateDate(int a, Date b) throws SQLException
+ { inner.updateDate(a, b); }
+
+ public void updateTimestamp(int a, Timestamp b) throws SQLException
+ { inner.updateTimestamp(a, b); }
+
+ public void updateTimestamp(String a, Timestamp b) throws SQLException
+ { inner.updateTimestamp(a, b); }
+
+ public void updateAsciiStream(String a, InputStream b, int c) throws SQLException
+ { inner.updateAsciiStream(a, b, c); }
+
+ public void updateAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.updateAsciiStream(a, b, c); }
+
+ public void updateBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.updateBinaryStream(a, b, c); }
+
+ public void updateBinaryStream(String a, InputStream b, int c) throws SQLException
+ { inner.updateBinaryStream(a, b, c); }
+
+ public void updateCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.updateCharacterStream(a, b, c); }
+
+ public void updateCharacterStream(String a, Reader b, int c) throws SQLException
+ { inner.updateCharacterStream(a, b, c); }
+
+ public void updateObject(String a, Object b) throws SQLException
+ { inner.updateObject(a, b); }
+
+ public void updateObject(int a, Object b) throws SQLException
+ { inner.updateObject(a, b); }
+
+ public void updateObject(int a, Object b, int c) throws SQLException
+ { inner.updateObject(a, b, c); }
+
+ public void updateObject(String a, Object b, int c) throws SQLException
+ { inner.updateObject(a, b, c); }
+
+ public void insertRow() throws SQLException
+ { inner.insertRow(); }
+
+ public void updateRow() throws SQLException
+ { inner.updateRow(); }
+
+ public void deleteRow() throws SQLException
+ { inner.deleteRow(); }
+
+ public void refreshRow() throws SQLException
+ { inner.refreshRow(); }
+
+ public void cancelRowUpdates() throws SQLException
+ { inner.cancelRowUpdates(); }
+
+ public void moveToInsertRow() throws SQLException
+ { inner.moveToInsertRow(); }
+
+ public void moveToCurrentRow() throws SQLException
+ { inner.moveToCurrentRow(); }
+
+ public Statement getStatement() throws SQLException
+ { return inner.getStatement(); }
+
+ public Blob getBlob(String a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public Blob getBlob(int a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public Clob getClob(String a) throws SQLException
+ { return inner.getClob(a); }
+
+ public Clob getClob(int a) throws SQLException
+ { return inner.getClob(a); }
+
+ public void updateRef(String a, Ref b) throws SQLException
+ { inner.updateRef(a, b); }
+
+ public void updateRef(int a, Ref b) throws SQLException
+ { inner.updateRef(a, b); }
+
+ public void updateBlob(String a, Blob b) throws SQLException
+ { inner.updateBlob(a, b); }
+
+ public void updateBlob(int a, Blob b) throws SQLException
+ { inner.updateBlob(a, b); }
+
+ public void updateClob(int a, Clob b) throws SQLException
+ { inner.updateClob(a, b); }
+
+ public void updateClob(String a, Clob b) throws SQLException
+ { inner.updateClob(a, b); }
+
+ public void updateArray(String a, Array b) throws SQLException
+ { inner.updateArray(a, b); }
+
+ public void updateArray(int a, Array b) throws SQLException
+ { inner.updateArray(a, b); }
+
+ public Object getObject(int a) throws SQLException
+ { return inner.getObject(a); }
+
+ public Object getObject(String a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public Object getObject(String a) throws SQLException
+ { return inner.getObject(a); }
+
+ public Object getObject(int a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public boolean getBoolean(int a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public boolean getBoolean(String a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public byte getByte(String a) throws SQLException
+ { return inner.getByte(a); }
+
+ public byte getByte(int a) throws SQLException
+ { return inner.getByte(a); }
+
+ public short getShort(String a) throws SQLException
+ { return inner.getShort(a); }
+
+ public short getShort(int a) throws SQLException
+ { return inner.getShort(a); }
+
+ public int getInt(String a) throws SQLException
+ { return inner.getInt(a); }
+
+ public int getInt(int a) throws SQLException
+ { return inner.getInt(a); }
+
+ public long getLong(int a) throws SQLException
+ { return inner.getLong(a); }
+
+ public long getLong(String a) throws SQLException
+ { return inner.getLong(a); }
+
+ public float getFloat(String a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public float getFloat(int a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public double getDouble(int a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public double getDouble(String a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public byte[] getBytes(String a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public byte[] getBytes(int a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public boolean next() throws SQLException
+ { return inner.next(); }
+
+ public URL getURL(int a) throws SQLException
+ { return inner.getURL(a); }
+
+ public URL getURL(String a) throws SQLException
+ { return inner.getURL(a); }
+
+ public int getType() throws SQLException
+ { return inner.getType(); }
+
+ public boolean previous() throws SQLException
+ { return inner.previous(); }
+
+ public void close() throws SQLException
+ { inner.close(); }
+
+ public String getString(String a) throws SQLException
+ { return inner.getString(a); }
+
+ public String getString(int a) throws SQLException
+ { return inner.getString(a); }
+
+ public Ref getRef(String a) throws SQLException
+ { return inner.getRef(a); }
+
+ public Ref getRef(int a) throws SQLException
+ { return inner.getRef(a); }
+
+ public Time getTime(int a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public Time getTime(String a) throws SQLException
+ { return inner.getTime(a); }
+
+ public Time getTime(int a) throws SQLException
+ { return inner.getTime(a); }
+
+ public Time getTime(String a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public Date getDate(String a) throws SQLException
+ { return inner.getDate(a); }
+
+ public Date getDate(int a) throws SQLException
+ { return inner.getDate(a); }
+
+ public Date getDate(int a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public Date getDate(String a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public boolean first() throws SQLException
+ { return inner.first(); }
+
+ public boolean last() throws SQLException
+ { return inner.last(); }
+
+ public Array getArray(String a) throws SQLException
+ { return inner.getArray(a); }
+
+ public Array getArray(int a) throws SQLException
+ { return inner.getArray(a); }
+
+ public boolean relative(int a) throws SQLException
+ { return inner.relative(a); }
+
+ public void updateTime(String a, Time b) throws SQLException
+ { inner.updateTime(a, b); }
+
+ public void updateTime(int a, Time b) throws SQLException
+ { inner.updateTime(a, b); }
+
+ public int findColumn(String a) throws SQLException
+ { return inner.findColumn(a); }
+
+ public int getRow() throws SQLException
+ { return inner.getRow(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/FilterStatement.java b/src/classes/com/mchange/v2/sql/filter/FilterStatement.java
new file mode 100644
index 0000000..50f3161
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/FilterStatement.java
@@ -0,0 +1,159 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+public abstract class FilterStatement implements Statement
+{
+ protected Statement inner;
+
+ public FilterStatement(Statement inner)
+ { this.inner = inner; }
+
+ public FilterStatement()
+ {}
+
+ public void setInner( Statement inner )
+ { this.inner = inner; }
+
+ public Statement getInner()
+ { return inner; }
+
+ public SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public void close() throws SQLException
+ { inner.close(); }
+
+ public boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/RecreatePackage.java b/src/classes/com/mchange/v2/sql/filter/RecreatePackage.java
new file mode 100644
index 0000000..95d0286
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/RecreatePackage.java
@@ -0,0 +1,97 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.*;
+import java.sql.*;
+import java.lang.reflect.*;
+import com.mchange.v2.codegen.intfc.*;
+import com.mchange.v1.lang.ClassUtils;
+import javax.sql.DataSource;
+
+public final class RecreatePackage
+{
+ final static Class[] intfcs
+ = new Class[]
+ {
+ Connection.class,
+ ResultSet.class,
+ DatabaseMetaData.class,
+ Statement.class,
+ PreparedStatement.class,
+ CallableStatement.class,
+ DataSource.class
+ };
+
+ public static void main( String[] argv )
+ {
+ try
+ {
+ DelegatorGenerator dg = new DelegatorGenerator();
+ String thisClassName = RecreatePackage.class.getName();
+ String pkg = thisClassName.substring(0, thisClassName.lastIndexOf('.'));
+ for (int i = 0; i < intfcs.length; ++i)
+ {
+ Class intfcl = intfcs[i];
+ String sin = ClassUtils.simpleClassName( intfcl );
+ String sgenclass1 = "Filter" + sin;
+ String sgenclass2 = "SynchronizedFilter" + sin;
+
+ Writer w = null;
+ try
+ {
+ w = new BufferedWriter( new FileWriter( sgenclass1 + ".java" ) );
+ dg.setMethodModifiers( Modifier.PUBLIC );
+ dg.writeDelegator( intfcl, pkg + '.' + sgenclass1, w );
+ System.err.println( sgenclass1 );
+ }
+ finally
+ {
+ try { if (w != null) w.close(); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ try
+ {
+ w = new BufferedWriter( new FileWriter( sgenclass2 + ".java" ) );
+ dg.setMethodModifiers( Modifier.PUBLIC | Modifier.SYNCHRONIZED );
+ dg.writeDelegator( intfcl, pkg + '.' + sgenclass2, w );
+ System.err.println( sgenclass2 );
+ }
+ finally
+ {
+ try { if (w != null) w.close(); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+ }
+ }
+ catch ( Exception e )
+ { e.printStackTrace(); }
+ }
+
+ private RecreatePackage()
+ {}
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterCallableStatement.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterCallableStatement.java
new file mode 100644
index 0000000..48db0fb
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterCallableStatement.java
@@ -0,0 +1,523 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+public abstract class SynchronizedFilterCallableStatement implements CallableStatement
+{
+ protected CallableStatement inner;
+
+ public SynchronizedFilterCallableStatement(CallableStatement inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterCallableStatement()
+ {}
+
+ public synchronized void setInner( CallableStatement inner )
+ { this.inner = inner; }
+
+ public synchronized CallableStatement getInner()
+ { return inner; }
+
+ public synchronized boolean wasNull() throws SQLException
+ { return inner.wasNull(); }
+
+ public synchronized BigDecimal getBigDecimal(int a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public synchronized BigDecimal getBigDecimal(int a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public synchronized BigDecimal getBigDecimal(String a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public synchronized Timestamp getTimestamp(String a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public synchronized Timestamp getTimestamp(String a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public synchronized Timestamp getTimestamp(int a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public synchronized Timestamp getTimestamp(int a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public synchronized Blob getBlob(String a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public synchronized Blob getBlob(int a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public synchronized Clob getClob(String a) throws SQLException
+ { return inner.getClob(a); }
+
+ public synchronized Clob getClob(int a) throws SQLException
+ { return inner.getClob(a); }
+
+ public synchronized void setNull(String a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public synchronized void setNull(String a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public synchronized void setBigDecimal(String a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public synchronized void setBytes(String a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public synchronized void setTimestamp(String a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public synchronized void setTimestamp(String a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public synchronized void setAsciiStream(String a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public synchronized void setBinaryStream(String a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public synchronized void setObject(String a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public synchronized void setObject(String a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public synchronized void setObject(String a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public synchronized void setCharacterStream(String a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public synchronized void registerOutParameter(String a, int b) throws SQLException
+ { inner.registerOutParameter(a, b); }
+
+ public synchronized void registerOutParameter(int a, int b) throws SQLException
+ { inner.registerOutParameter(a, b); }
+
+ public synchronized void registerOutParameter(int a, int b, int c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public synchronized void registerOutParameter(int a, int b, String c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public synchronized void registerOutParameter(String a, int b, int c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public synchronized void registerOutParameter(String a, int b, String c) throws SQLException
+ { inner.registerOutParameter(a, b, c); }
+
+ public synchronized Object getObject(String a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public synchronized Object getObject(int a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public synchronized Object getObject(int a) throws SQLException
+ { return inner.getObject(a); }
+
+ public synchronized Object getObject(String a) throws SQLException
+ { return inner.getObject(a); }
+
+ public synchronized boolean getBoolean(int a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public synchronized boolean getBoolean(String a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public synchronized byte getByte(String a) throws SQLException
+ { return inner.getByte(a); }
+
+ public synchronized byte getByte(int a) throws SQLException
+ { return inner.getByte(a); }
+
+ public synchronized short getShort(int a) throws SQLException
+ { return inner.getShort(a); }
+
+ public synchronized short getShort(String a) throws SQLException
+ { return inner.getShort(a); }
+
+ public synchronized int getInt(String a) throws SQLException
+ { return inner.getInt(a); }
+
+ public synchronized int getInt(int a) throws SQLException
+ { return inner.getInt(a); }
+
+ public synchronized long getLong(int a) throws SQLException
+ { return inner.getLong(a); }
+
+ public synchronized long getLong(String a) throws SQLException
+ { return inner.getLong(a); }
+
+ public synchronized float getFloat(String a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public synchronized float getFloat(int a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public synchronized double getDouble(String a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public synchronized double getDouble(int a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public synchronized byte[] getBytes(int a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public synchronized byte[] getBytes(String a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public synchronized URL getURL(String a) throws SQLException
+ { return inner.getURL(a); }
+
+ public synchronized URL getURL(int a) throws SQLException
+ { return inner.getURL(a); }
+
+ public synchronized void setBoolean(String a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public synchronized void setByte(String a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public synchronized void setShort(String a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public synchronized void setInt(String a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public synchronized void setLong(String a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public synchronized void setFloat(String a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public synchronized void setDouble(String a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public synchronized String getString(String a) throws SQLException
+ { return inner.getString(a); }
+
+ public synchronized String getString(int a) throws SQLException
+ { return inner.getString(a); }
+
+ public synchronized Ref getRef(int a) throws SQLException
+ { return inner.getRef(a); }
+
+ public synchronized Ref getRef(String a) throws SQLException
+ { return inner.getRef(a); }
+
+ public synchronized void setURL(String a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public synchronized void setTime(String a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public synchronized void setTime(String a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public synchronized Time getTime(int a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public synchronized Time getTime(String a) throws SQLException
+ { return inner.getTime(a); }
+
+ public synchronized Time getTime(int a) throws SQLException
+ { return inner.getTime(a); }
+
+ public synchronized Time getTime(String a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public synchronized Date getDate(int a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public synchronized Date getDate(String a) throws SQLException
+ { return inner.getDate(a); }
+
+ public synchronized Date getDate(int a) throws SQLException
+ { return inner.getDate(a); }
+
+ public synchronized Date getDate(String a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public synchronized void setString(String a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public synchronized Array getArray(int a) throws SQLException
+ { return inner.getArray(a); }
+
+ public synchronized Array getArray(String a) throws SQLException
+ { return inner.getArray(a); }
+
+ public synchronized void setDate(String a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public synchronized void setDate(String a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public synchronized ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public synchronized ResultSet executeQuery() throws SQLException
+ { return inner.executeQuery(); }
+
+ public synchronized int executeUpdate() throws SQLException
+ { return inner.executeUpdate(); }
+
+ public synchronized void addBatch() throws SQLException
+ { inner.addBatch(); }
+
+ public synchronized void setNull(int a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public synchronized void setNull(int a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public synchronized void setBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public synchronized void setBytes(int a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public synchronized void setTimestamp(int a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public synchronized void setTimestamp(int a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public synchronized void setAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public synchronized void setUnicodeStream(int a, InputStream b, int c) throws SQLException
+ { inner.setUnicodeStream(a, b, c); }
+
+ public synchronized void setBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public synchronized void clearParameters() throws SQLException
+ { inner.clearParameters(); }
+
+ public synchronized void setObject(int a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public synchronized void setObject(int a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public synchronized void setObject(int a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public synchronized void setCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public synchronized void setRef(int a, Ref b) throws SQLException
+ { inner.setRef(a, b); }
+
+ public synchronized void setBlob(int a, Blob b) throws SQLException
+ { inner.setBlob(a, b); }
+
+ public synchronized void setClob(int a, Clob b) throws SQLException
+ { inner.setClob(a, b); }
+
+ public synchronized void setArray(int a, Array b) throws SQLException
+ { inner.setArray(a, b); }
+
+ public synchronized ParameterMetaData getParameterMetaData() throws SQLException
+ { return inner.getParameterMetaData(); }
+
+ public synchronized void setBoolean(int a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public synchronized void setByte(int a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public synchronized void setShort(int a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public synchronized void setInt(int a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public synchronized void setLong(int a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public synchronized void setFloat(int a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public synchronized void setDouble(int a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public synchronized void setURL(int a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public synchronized void setTime(int a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public synchronized void setTime(int a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public synchronized boolean execute() throws SQLException
+ { return inner.execute(); }
+
+ public synchronized void setString(int a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public synchronized void setDate(int a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public synchronized void setDate(int a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public synchronized SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public synchronized void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public synchronized void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public synchronized int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public synchronized void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public synchronized int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public synchronized int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public synchronized ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public synchronized int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public synchronized int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public synchronized void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public synchronized int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public synchronized void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public synchronized void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public synchronized int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public synchronized void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public synchronized void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public synchronized ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public synchronized int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public synchronized boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public synchronized boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public synchronized int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public synchronized int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public synchronized void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public synchronized void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public synchronized int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public synchronized ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public synchronized void close() throws SQLException
+ { inner.close(); }
+
+ public synchronized boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public synchronized boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public synchronized void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterConnection.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterConnection.java
new file mode 100644
index 0000000..ed1bfd4
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterConnection.java
@@ -0,0 +1,160 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.util.Map;
+
+public abstract class SynchronizedFilterConnection implements Connection
+{
+ protected Connection inner;
+
+ public SynchronizedFilterConnection(Connection inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterConnection()
+ {}
+
+ public synchronized void setInner( Connection inner )
+ { this.inner = inner; }
+
+ public synchronized Connection getInner()
+ { return inner; }
+
+ public synchronized Statement createStatement(int a, int b, int c) throws SQLException
+ { return inner.createStatement(a, b, c); }
+
+ public synchronized Statement createStatement(int a, int b) throws SQLException
+ { return inner.createStatement(a, b); }
+
+ public synchronized Statement createStatement() throws SQLException
+ { return inner.createStatement(); }
+
+ public synchronized PreparedStatement prepareStatement(String a, String[] b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public synchronized PreparedStatement prepareStatement(String a) throws SQLException
+ { return inner.prepareStatement(a); }
+
+ public synchronized PreparedStatement prepareStatement(String a, int b, int c) throws SQLException
+ { return inner.prepareStatement(a, b, c); }
+
+ public synchronized PreparedStatement prepareStatement(String a, int b, int c, int d) throws SQLException
+ { return inner.prepareStatement(a, b, c, d); }
+
+ public synchronized PreparedStatement prepareStatement(String a, int b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public synchronized PreparedStatement prepareStatement(String a, int[] b) throws SQLException
+ { return inner.prepareStatement(a, b); }
+
+ public synchronized CallableStatement prepareCall(String a, int b, int c, int d) throws SQLException
+ { return inner.prepareCall(a, b, c, d); }
+
+ public synchronized CallableStatement prepareCall(String a, int b, int c) throws SQLException
+ { return inner.prepareCall(a, b, c); }
+
+ public synchronized CallableStatement prepareCall(String a) throws SQLException
+ { return inner.prepareCall(a); }
+
+ public synchronized String nativeSQL(String a) throws SQLException
+ { return inner.nativeSQL(a); }
+
+ public synchronized void setAutoCommit(boolean a) throws SQLException
+ { inner.setAutoCommit(a); }
+
+ public synchronized boolean getAutoCommit() throws SQLException
+ { return inner.getAutoCommit(); }
+
+ public synchronized void commit() throws SQLException
+ { inner.commit(); }
+
+ public synchronized void rollback(Savepoint a) throws SQLException
+ { inner.rollback(a); }
+
+ public synchronized void rollback() throws SQLException
+ { inner.rollback(); }
+
+ public synchronized DatabaseMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public synchronized void setCatalog(String a) throws SQLException
+ { inner.setCatalog(a); }
+
+ public synchronized String getCatalog() throws SQLException
+ { return inner.getCatalog(); }
+
+ public synchronized void setTransactionIsolation(int a) throws SQLException
+ { inner.setTransactionIsolation(a); }
+
+ public synchronized int getTransactionIsolation() throws SQLException
+ { return inner.getTransactionIsolation(); }
+
+ public synchronized SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public synchronized void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public synchronized Map getTypeMap() throws SQLException
+ { return inner.getTypeMap(); }
+
+ public synchronized void setTypeMap(Map a) throws SQLException
+ { inner.setTypeMap(a); }
+
+ public synchronized void setHoldability(int a) throws SQLException
+ { inner.setHoldability(a); }
+
+ public synchronized int getHoldability() throws SQLException
+ { return inner.getHoldability(); }
+
+ public synchronized Savepoint setSavepoint() throws SQLException
+ { return inner.setSavepoint(); }
+
+ public synchronized Savepoint setSavepoint(String a) throws SQLException
+ { return inner.setSavepoint(a); }
+
+ public synchronized void releaseSavepoint(Savepoint a) throws SQLException
+ { inner.releaseSavepoint(a); }
+
+ public synchronized void setReadOnly(boolean a) throws SQLException
+ { inner.setReadOnly(a); }
+
+ public synchronized boolean isReadOnly() throws SQLException
+ { return inner.isReadOnly(); }
+
+ public synchronized void close() throws SQLException
+ { inner.close(); }
+
+ public synchronized boolean isClosed() throws SQLException
+ { return inner.isClosed(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterDatabaseMetaData.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterDatabaseMetaData.java
new file mode 100644
index 0000000..ac92c80
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterDatabaseMetaData.java
@@ -0,0 +1,542 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public abstract class SynchronizedFilterDatabaseMetaData implements DatabaseMetaData
+{
+ protected DatabaseMetaData inner;
+
+ public SynchronizedFilterDatabaseMetaData(DatabaseMetaData inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterDatabaseMetaData()
+ {}
+
+ public synchronized void setInner( DatabaseMetaData inner )
+ { this.inner = inner; }
+
+ public synchronized DatabaseMetaData getInner()
+ { return inner; }
+
+ public synchronized boolean allProceduresAreCallable() throws SQLException
+ { return inner.allProceduresAreCallable(); }
+
+ public synchronized boolean allTablesAreSelectable() throws SQLException
+ { return inner.allTablesAreSelectable(); }
+
+ public synchronized boolean nullsAreSortedHigh() throws SQLException
+ { return inner.nullsAreSortedHigh(); }
+
+ public synchronized boolean nullsAreSortedLow() throws SQLException
+ { return inner.nullsAreSortedLow(); }
+
+ public synchronized boolean nullsAreSortedAtStart() throws SQLException
+ { return inner.nullsAreSortedAtStart(); }
+
+ public synchronized boolean nullsAreSortedAtEnd() throws SQLException
+ { return inner.nullsAreSortedAtEnd(); }
+
+ public synchronized String getDatabaseProductName() throws SQLException
+ { return inner.getDatabaseProductName(); }
+
+ public synchronized String getDatabaseProductVersion() throws SQLException
+ { return inner.getDatabaseProductVersion(); }
+
+ public synchronized String getDriverName() throws SQLException
+ { return inner.getDriverName(); }
+
+ public synchronized String getDriverVersion() throws SQLException
+ { return inner.getDriverVersion(); }
+
+ public synchronized int getDriverMajorVersion()
+ { return inner.getDriverMajorVersion(); }
+
+ public synchronized int getDriverMinorVersion()
+ { return inner.getDriverMinorVersion(); }
+
+ public synchronized boolean usesLocalFiles() throws SQLException
+ { return inner.usesLocalFiles(); }
+
+ public synchronized boolean usesLocalFilePerTable() throws SQLException
+ { return inner.usesLocalFilePerTable(); }
+
+ public synchronized boolean supportsMixedCaseIdentifiers() throws SQLException
+ { return inner.supportsMixedCaseIdentifiers(); }
+
+ public synchronized boolean storesUpperCaseIdentifiers() throws SQLException
+ { return inner.storesUpperCaseIdentifiers(); }
+
+ public synchronized boolean storesLowerCaseIdentifiers() throws SQLException
+ { return inner.storesLowerCaseIdentifiers(); }
+
+ public synchronized boolean storesMixedCaseIdentifiers() throws SQLException
+ { return inner.storesMixedCaseIdentifiers(); }
+
+ public synchronized boolean supportsMixedCaseQuotedIdentifiers() throws SQLException
+ { return inner.supportsMixedCaseQuotedIdentifiers(); }
+
+ public synchronized boolean storesUpperCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesUpperCaseQuotedIdentifiers(); }
+
+ public synchronized boolean storesLowerCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesLowerCaseQuotedIdentifiers(); }
+
+ public synchronized boolean storesMixedCaseQuotedIdentifiers() throws SQLException
+ { return inner.storesMixedCaseQuotedIdentifiers(); }
+
+ public synchronized String getIdentifierQuoteString() throws SQLException
+ { return inner.getIdentifierQuoteString(); }
+
+ public synchronized String getSQLKeywords() throws SQLException
+ { return inner.getSQLKeywords(); }
+
+ public synchronized String getNumericFunctions() throws SQLException
+ { return inner.getNumericFunctions(); }
+
+ public synchronized String getStringFunctions() throws SQLException
+ { return inner.getStringFunctions(); }
+
+ public synchronized String getSystemFunctions() throws SQLException
+ { return inner.getSystemFunctions(); }
+
+ public synchronized String getTimeDateFunctions() throws SQLException
+ { return inner.getTimeDateFunctions(); }
+
+ public synchronized String getSearchStringEscape() throws SQLException
+ { return inner.getSearchStringEscape(); }
+
+ public synchronized String getExtraNameCharacters() throws SQLException
+ { return inner.getExtraNameCharacters(); }
+
+ public synchronized boolean supportsAlterTableWithAddColumn() throws SQLException
+ { return inner.supportsAlterTableWithAddColumn(); }
+
+ public synchronized boolean supportsAlterTableWithDropColumn() throws SQLException
+ { return inner.supportsAlterTableWithDropColumn(); }
+
+ public synchronized boolean supportsColumnAliasing() throws SQLException
+ { return inner.supportsColumnAliasing(); }
+
+ public synchronized boolean nullPlusNonNullIsNull() throws SQLException
+ { return inner.nullPlusNonNullIsNull(); }
+
+ public synchronized boolean supportsConvert() throws SQLException
+ { return inner.supportsConvert(); }
+
+ public synchronized boolean supportsConvert(int a, int b) throws SQLException
+ { return inner.supportsConvert(a, b); }
+
+ public synchronized boolean supportsTableCorrelationNames() throws SQLException
+ { return inner.supportsTableCorrelationNames(); }
+
+ public synchronized boolean supportsDifferentTableCorrelationNames() throws SQLException
+ { return inner.supportsDifferentTableCorrelationNames(); }
+
+ public synchronized boolean supportsExpressionsInOrderBy() throws SQLException
+ { return inner.supportsExpressionsInOrderBy(); }
+
+ public synchronized boolean supportsOrderByUnrelated() throws SQLException
+ { return inner.supportsOrderByUnrelated(); }
+
+ public synchronized boolean supportsGroupBy() throws SQLException
+ { return inner.supportsGroupBy(); }
+
+ public synchronized boolean supportsGroupByUnrelated() throws SQLException
+ { return inner.supportsGroupByUnrelated(); }
+
+ public synchronized boolean supportsGroupByBeyondSelect() throws SQLException
+ { return inner.supportsGroupByBeyondSelect(); }
+
+ public synchronized boolean supportsLikeEscapeClause() throws SQLException
+ { return inner.supportsLikeEscapeClause(); }
+
+ public synchronized boolean supportsMultipleResultSets() throws SQLException
+ { return inner.supportsMultipleResultSets(); }
+
+ public synchronized boolean supportsMultipleTransactions() throws SQLException
+ { return inner.supportsMultipleTransactions(); }
+
+ public synchronized boolean supportsNonNullableColumns() throws SQLException
+ { return inner.supportsNonNullableColumns(); }
+
+ public synchronized boolean supportsMinimumSQLGrammar() throws SQLException
+ { return inner.supportsMinimumSQLGrammar(); }
+
+ public synchronized boolean supportsCoreSQLGrammar() throws SQLException
+ { return inner.supportsCoreSQLGrammar(); }
+
+ public synchronized boolean supportsExtendedSQLGrammar() throws SQLException
+ { return inner.supportsExtendedSQLGrammar(); }
+
+ public synchronized boolean supportsANSI92EntryLevelSQL() throws SQLException
+ { return inner.supportsANSI92EntryLevelSQL(); }
+
+ public synchronized boolean supportsANSI92IntermediateSQL() throws SQLException
+ { return inner.supportsANSI92IntermediateSQL(); }
+
+ public synchronized boolean supportsANSI92FullSQL() throws SQLException
+ { return inner.supportsANSI92FullSQL(); }
+
+ public synchronized boolean supportsIntegrityEnhancementFacility() throws SQLException
+ { return inner.supportsIntegrityEnhancementFacility(); }
+
+ public synchronized boolean supportsOuterJoins() throws SQLException
+ { return inner.supportsOuterJoins(); }
+
+ public synchronized boolean supportsFullOuterJoins() throws SQLException
+ { return inner.supportsFullOuterJoins(); }
+
+ public synchronized boolean supportsLimitedOuterJoins() throws SQLException
+ { return inner.supportsLimitedOuterJoins(); }
+
+ public synchronized String getSchemaTerm() throws SQLException
+ { return inner.getSchemaTerm(); }
+
+ public synchronized String getProcedureTerm() throws SQLException
+ { return inner.getProcedureTerm(); }
+
+ public synchronized String getCatalogTerm() throws SQLException
+ { return inner.getCatalogTerm(); }
+
+ public synchronized boolean isCatalogAtStart() throws SQLException
+ { return inner.isCatalogAtStart(); }
+
+ public synchronized String getCatalogSeparator() throws SQLException
+ { return inner.getCatalogSeparator(); }
+
+ public synchronized boolean supportsSchemasInDataManipulation() throws SQLException
+ { return inner.supportsSchemasInDataManipulation(); }
+
+ public synchronized boolean supportsSchemasInProcedureCalls() throws SQLException
+ { return inner.supportsSchemasInProcedureCalls(); }
+
+ public synchronized boolean supportsSchemasInTableDefinitions() throws SQLException
+ { return inner.supportsSchemasInTableDefinitions(); }
+
+ public synchronized boolean supportsSchemasInIndexDefinitions() throws SQLException
+ { return inner.supportsSchemasInIndexDefinitions(); }
+
+ public synchronized boolean supportsSchemasInPrivilegeDefinitions() throws SQLException
+ { return inner.supportsSchemasInPrivilegeDefinitions(); }
+
+ public synchronized boolean supportsCatalogsInDataManipulation() throws SQLException
+ { return inner.supportsCatalogsInDataManipulation(); }
+
+ public synchronized boolean supportsCatalogsInProcedureCalls() throws SQLException
+ { return inner.supportsCatalogsInProcedureCalls(); }
+
+ public synchronized boolean supportsCatalogsInTableDefinitions() throws SQLException
+ { return inner.supportsCatalogsInTableDefinitions(); }
+
+ public synchronized boolean supportsCatalogsInIndexDefinitions() throws SQLException
+ { return inner.supportsCatalogsInIndexDefinitions(); }
+
+ public synchronized boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException
+ { return inner.supportsCatalogsInPrivilegeDefinitions(); }
+
+ public synchronized boolean supportsPositionedDelete() throws SQLException
+ { return inner.supportsPositionedDelete(); }
+
+ public synchronized boolean supportsPositionedUpdate() throws SQLException
+ { return inner.supportsPositionedUpdate(); }
+
+ public synchronized boolean supportsSelectForUpdate() throws SQLException
+ { return inner.supportsSelectForUpdate(); }
+
+ public synchronized boolean supportsStoredProcedures() throws SQLException
+ { return inner.supportsStoredProcedures(); }
+
+ public synchronized boolean supportsSubqueriesInComparisons() throws SQLException
+ { return inner.supportsSubqueriesInComparisons(); }
+
+ public synchronized boolean supportsSubqueriesInExists() throws SQLException
+ { return inner.supportsSubqueriesInExists(); }
+
+ public synchronized boolean supportsSubqueriesInIns() throws SQLException
+ { return inner.supportsSubqueriesInIns(); }
+
+ public synchronized boolean supportsSubqueriesInQuantifieds() throws SQLException
+ { return inner.supportsSubqueriesInQuantifieds(); }
+
+ public synchronized boolean supportsCorrelatedSubqueries() throws SQLException
+ { return inner.supportsCorrelatedSubqueries(); }
+
+ public synchronized boolean supportsUnion() throws SQLException
+ { return inner.supportsUnion(); }
+
+ public synchronized boolean supportsUnionAll() throws SQLException
+ { return inner.supportsUnionAll(); }
+
+ public synchronized boolean supportsOpenCursorsAcrossCommit() throws SQLException
+ { return inner.supportsOpenCursorsAcrossCommit(); }
+
+ public synchronized boolean supportsOpenCursorsAcrossRollback() throws SQLException
+ { return inner.supportsOpenCursorsAcrossRollback(); }
+
+ public synchronized boolean supportsOpenStatementsAcrossCommit() throws SQLException
+ { return inner.supportsOpenStatementsAcrossCommit(); }
+
+ public synchronized boolean supportsOpenStatementsAcrossRollback() throws SQLException
+ { return inner.supportsOpenStatementsAcrossRollback(); }
+
+ public synchronized int getMaxBinaryLiteralLength() throws SQLException
+ { return inner.getMaxBinaryLiteralLength(); }
+
+ public synchronized int getMaxCharLiteralLength() throws SQLException
+ { return inner.getMaxCharLiteralLength(); }
+
+ public synchronized int getMaxColumnNameLength() throws SQLException
+ { return inner.getMaxColumnNameLength(); }
+
+ public synchronized int getMaxColumnsInGroupBy() throws SQLException
+ { return inner.getMaxColumnsInGroupBy(); }
+
+ public synchronized int getMaxColumnsInIndex() throws SQLException
+ { return inner.getMaxColumnsInIndex(); }
+
+ public synchronized int getMaxColumnsInOrderBy() throws SQLException
+ { return inner.getMaxColumnsInOrderBy(); }
+
+ public synchronized int getMaxColumnsInSelect() throws SQLException
+ { return inner.getMaxColumnsInSelect(); }
+
+ public synchronized int getMaxColumnsInTable() throws SQLException
+ { return inner.getMaxColumnsInTable(); }
+
+ public synchronized int getMaxConnections() throws SQLException
+ { return inner.getMaxConnections(); }
+
+ public synchronized int getMaxCursorNameLength() throws SQLException
+ { return inner.getMaxCursorNameLength(); }
+
+ public synchronized int getMaxIndexLength() throws SQLException
+ { return inner.getMaxIndexLength(); }
+
+ public synchronized int getMaxSchemaNameLength() throws SQLException
+ { return inner.getMaxSchemaNameLength(); }
+
+ public synchronized int getMaxProcedureNameLength() throws SQLException
+ { return inner.getMaxProcedureNameLength(); }
+
+ public synchronized int getMaxCatalogNameLength() throws SQLException
+ { return inner.getMaxCatalogNameLength(); }
+
+ public synchronized int getMaxRowSize() throws SQLException
+ { return inner.getMaxRowSize(); }
+
+ public synchronized boolean doesMaxRowSizeIncludeBlobs() throws SQLException
+ { return inner.doesMaxRowSizeIncludeBlobs(); }
+
+ public synchronized int getMaxStatementLength() throws SQLException
+ { return inner.getMaxStatementLength(); }
+
+ public synchronized int getMaxStatements() throws SQLException
+ { return inner.getMaxStatements(); }
+
+ public synchronized int getMaxTableNameLength() throws SQLException
+ { return inner.getMaxTableNameLength(); }
+
+ public synchronized int getMaxTablesInSelect() throws SQLException
+ { return inner.getMaxTablesInSelect(); }
+
+ public synchronized int getMaxUserNameLength() throws SQLException
+ { return inner.getMaxUserNameLength(); }
+
+ public synchronized int getDefaultTransactionIsolation() throws SQLException
+ { return inner.getDefaultTransactionIsolation(); }
+
+ public synchronized boolean supportsTransactions() throws SQLException
+ { return inner.supportsTransactions(); }
+
+ public synchronized boolean supportsTransactionIsolationLevel(int a) throws SQLException
+ { return inner.supportsTransactionIsolationLevel(a); }
+
+ public synchronized boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException
+ { return inner.supportsDataDefinitionAndDataManipulationTransactions(); }
+
+ public synchronized boolean supportsDataManipulationTransactionsOnly() throws SQLException
+ { return inner.supportsDataManipulationTransactionsOnly(); }
+
+ public synchronized boolean dataDefinitionCausesTransactionCommit() throws SQLException
+ { return inner.dataDefinitionCausesTransactionCommit(); }
+
+ public synchronized boolean dataDefinitionIgnoredInTransactions() throws SQLException
+ { return inner.dataDefinitionIgnoredInTransactions(); }
+
+ public synchronized ResultSet getProcedures(String a, String b, String c) throws SQLException
+ { return inner.getProcedures(a, b, c); }
+
+ public synchronized ResultSet getProcedureColumns(String a, String b, String c, String d) throws SQLException
+ { return inner.getProcedureColumns(a, b, c, d); }
+
+ public synchronized ResultSet getTables(String a, String b, String c, String[] d) throws SQLException
+ { return inner.getTables(a, b, c, d); }
+
+ public synchronized ResultSet getSchemas() throws SQLException
+ { return inner.getSchemas(); }
+
+ public synchronized ResultSet getCatalogs() throws SQLException
+ { return inner.getCatalogs(); }
+
+ public synchronized ResultSet getTableTypes() throws SQLException
+ { return inner.getTableTypes(); }
+
+ public synchronized ResultSet getColumnPrivileges(String a, String b, String c, String d) throws SQLException
+ { return inner.getColumnPrivileges(a, b, c, d); }
+
+ public synchronized ResultSet getTablePrivileges(String a, String b, String c) throws SQLException
+ { return inner.getTablePrivileges(a, b, c); }
+
+ public synchronized ResultSet getBestRowIdentifier(String a, String b, String c, int d, boolean e) throws SQLException
+ { return inner.getBestRowIdentifier(a, b, c, d, e); }
+
+ public synchronized ResultSet getVersionColumns(String a, String b, String c) throws SQLException
+ { return inner.getVersionColumns(a, b, c); }
+
+ public synchronized ResultSet getPrimaryKeys(String a, String b, String c) throws SQLException
+ { return inner.getPrimaryKeys(a, b, c); }
+
+ public synchronized ResultSet getImportedKeys(String a, String b, String c) throws SQLException
+ { return inner.getImportedKeys(a, b, c); }
+
+ public synchronized ResultSet getExportedKeys(String a, String b, String c) throws SQLException
+ { return inner.getExportedKeys(a, b, c); }
+
+ public synchronized ResultSet getCrossReference(String a, String b, String c, String d, String e, String f) throws SQLException
+ { return inner.getCrossReference(a, b, c, d, e, f); }
+
+ public synchronized ResultSet getTypeInfo() throws SQLException
+ { return inner.getTypeInfo(); }
+
+ public synchronized ResultSet getIndexInfo(String a, String b, String c, boolean d, boolean e) throws SQLException
+ { return inner.getIndexInfo(a, b, c, d, e); }
+
+ public synchronized boolean supportsResultSetType(int a) throws SQLException
+ { return inner.supportsResultSetType(a); }
+
+ public synchronized boolean supportsResultSetConcurrency(int a, int b) throws SQLException
+ { return inner.supportsResultSetConcurrency(a, b); }
+
+ public synchronized boolean ownUpdatesAreVisible(int a) throws SQLException
+ { return inner.ownUpdatesAreVisible(a); }
+
+ public synchronized boolean ownDeletesAreVisible(int a) throws SQLException
+ { return inner.ownDeletesAreVisible(a); }
+
+ public synchronized boolean ownInsertsAreVisible(int a) throws SQLException
+ { return inner.ownInsertsAreVisible(a); }
+
+ public synchronized boolean othersUpdatesAreVisible(int a) throws SQLException
+ { return inner.othersUpdatesAreVisible(a); }
+
+ public synchronized boolean othersDeletesAreVisible(int a) throws SQLException
+ { return inner.othersDeletesAreVisible(a); }
+
+ public synchronized boolean othersInsertsAreVisible(int a) throws SQLException
+ { return inner.othersInsertsAreVisible(a); }
+
+ public synchronized boolean updatesAreDetected(int a) throws SQLException
+ { return inner.updatesAreDetected(a); }
+
+ public synchronized boolean deletesAreDetected(int a) throws SQLException
+ { return inner.deletesAreDetected(a); }
+
+ public synchronized boolean insertsAreDetected(int a) throws SQLException
+ { return inner.insertsAreDetected(a); }
+
+ public synchronized boolean supportsBatchUpdates() throws SQLException
+ { return inner.supportsBatchUpdates(); }
+
+ public synchronized ResultSet getUDTs(String a, String b, String c, int[] d) throws SQLException
+ { return inner.getUDTs(a, b, c, d); }
+
+ public synchronized boolean supportsSavepoints() throws SQLException
+ { return inner.supportsSavepoints(); }
+
+ public synchronized boolean supportsNamedParameters() throws SQLException
+ { return inner.supportsNamedParameters(); }
+
+ public synchronized boolean supportsMultipleOpenResults() throws SQLException
+ { return inner.supportsMultipleOpenResults(); }
+
+ public synchronized boolean supportsGetGeneratedKeys() throws SQLException
+ { return inner.supportsGetGeneratedKeys(); }
+
+ public synchronized ResultSet getSuperTypes(String a, String b, String c) throws SQLException
+ { return inner.getSuperTypes(a, b, c); }
+
+ public synchronized ResultSet getSuperTables(String a, String b, String c) throws SQLException
+ { return inner.getSuperTables(a, b, c); }
+
+ public synchronized boolean supportsResultSetHoldability(int a) throws SQLException
+ { return inner.supportsResultSetHoldability(a); }
+
+ public synchronized int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public synchronized int getDatabaseMajorVersion() throws SQLException
+ { return inner.getDatabaseMajorVersion(); }
+
+ public synchronized int getDatabaseMinorVersion() throws SQLException
+ { return inner.getDatabaseMinorVersion(); }
+
+ public synchronized int getJDBCMajorVersion() throws SQLException
+ { return inner.getJDBCMajorVersion(); }
+
+ public synchronized int getJDBCMinorVersion() throws SQLException
+ { return inner.getJDBCMinorVersion(); }
+
+ public synchronized int getSQLStateType() throws SQLException
+ { return inner.getSQLStateType(); }
+
+ public synchronized boolean locatorsUpdateCopy() throws SQLException
+ { return inner.locatorsUpdateCopy(); }
+
+ public synchronized boolean supportsStatementPooling() throws SQLException
+ { return inner.supportsStatementPooling(); }
+
+ public synchronized String getURL() throws SQLException
+ { return inner.getURL(); }
+
+ public synchronized boolean isReadOnly() throws SQLException
+ { return inner.isReadOnly(); }
+
+ public synchronized ResultSet getAttributes(String a, String b, String c, String d) throws SQLException
+ { return inner.getAttributes(a, b, c, d); }
+
+ public synchronized Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public synchronized ResultSet getColumns(String a, String b, String c, String d) throws SQLException
+ { return inner.getColumns(a, b, c, d); }
+
+ public synchronized String getUserName() throws SQLException
+ { return inner.getUserName(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterPreparedStatement.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterPreparedStatement.java
new file mode 100644
index 0000000..65062f2
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterPreparedStatement.java
@@ -0,0 +1,285 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+public abstract class SynchronizedFilterPreparedStatement implements PreparedStatement
+{
+ protected PreparedStatement inner;
+
+ public SynchronizedFilterPreparedStatement(PreparedStatement inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterPreparedStatement()
+ {}
+
+ public synchronized void setInner( PreparedStatement inner )
+ { this.inner = inner; }
+
+ public synchronized PreparedStatement getInner()
+ { return inner; }
+
+ public synchronized ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public synchronized ResultSet executeQuery() throws SQLException
+ { return inner.executeQuery(); }
+
+ public synchronized int executeUpdate() throws SQLException
+ { return inner.executeUpdate(); }
+
+ public synchronized void addBatch() throws SQLException
+ { inner.addBatch(); }
+
+ public synchronized void setNull(int a, int b, String c) throws SQLException
+ { inner.setNull(a, b, c); }
+
+ public synchronized void setNull(int a, int b) throws SQLException
+ { inner.setNull(a, b); }
+
+ public synchronized void setBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.setBigDecimal(a, b); }
+
+ public synchronized void setBytes(int a, byte[] b) throws SQLException
+ { inner.setBytes(a, b); }
+
+ public synchronized void setTimestamp(int a, Timestamp b, Calendar c) throws SQLException
+ { inner.setTimestamp(a, b, c); }
+
+ public synchronized void setTimestamp(int a, Timestamp b) throws SQLException
+ { inner.setTimestamp(a, b); }
+
+ public synchronized void setAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.setAsciiStream(a, b, c); }
+
+ public synchronized void setUnicodeStream(int a, InputStream b, int c) throws SQLException
+ { inner.setUnicodeStream(a, b, c); }
+
+ public synchronized void setBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.setBinaryStream(a, b, c); }
+
+ public synchronized void clearParameters() throws SQLException
+ { inner.clearParameters(); }
+
+ public synchronized void setObject(int a, Object b) throws SQLException
+ { inner.setObject(a, b); }
+
+ public synchronized void setObject(int a, Object b, int c, int d) throws SQLException
+ { inner.setObject(a, b, c, d); }
+
+ public synchronized void setObject(int a, Object b, int c) throws SQLException
+ { inner.setObject(a, b, c); }
+
+ public synchronized void setCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.setCharacterStream(a, b, c); }
+
+ public synchronized void setRef(int a, Ref b) throws SQLException
+ { inner.setRef(a, b); }
+
+ public synchronized void setBlob(int a, Blob b) throws SQLException
+ { inner.setBlob(a, b); }
+
+ public synchronized void setClob(int a, Clob b) throws SQLException
+ { inner.setClob(a, b); }
+
+ public synchronized void setArray(int a, Array b) throws SQLException
+ { inner.setArray(a, b); }
+
+ public synchronized ParameterMetaData getParameterMetaData() throws SQLException
+ { return inner.getParameterMetaData(); }
+
+ public synchronized void setBoolean(int a, boolean b) throws SQLException
+ { inner.setBoolean(a, b); }
+
+ public synchronized void setByte(int a, byte b) throws SQLException
+ { inner.setByte(a, b); }
+
+ public synchronized void setShort(int a, short b) throws SQLException
+ { inner.setShort(a, b); }
+
+ public synchronized void setInt(int a, int b) throws SQLException
+ { inner.setInt(a, b); }
+
+ public synchronized void setLong(int a, long b) throws SQLException
+ { inner.setLong(a, b); }
+
+ public synchronized void setFloat(int a, float b) throws SQLException
+ { inner.setFloat(a, b); }
+
+ public synchronized void setDouble(int a, double b) throws SQLException
+ { inner.setDouble(a, b); }
+
+ public synchronized void setURL(int a, URL b) throws SQLException
+ { inner.setURL(a, b); }
+
+ public synchronized void setTime(int a, Time b) throws SQLException
+ { inner.setTime(a, b); }
+
+ public synchronized void setTime(int a, Time b, Calendar c) throws SQLException
+ { inner.setTime(a, b, c); }
+
+ public synchronized boolean execute() throws SQLException
+ { return inner.execute(); }
+
+ public synchronized void setString(int a, String b) throws SQLException
+ { inner.setString(a, b); }
+
+ public synchronized void setDate(int a, Date b, Calendar c) throws SQLException
+ { inner.setDate(a, b, c); }
+
+ public synchronized void setDate(int a, Date b) throws SQLException
+ { inner.setDate(a, b); }
+
+ public synchronized SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public synchronized void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public synchronized void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public synchronized int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public synchronized void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public synchronized int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public synchronized int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public synchronized ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public synchronized int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public synchronized int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public synchronized void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public synchronized int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public synchronized void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public synchronized void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public synchronized int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public synchronized void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public synchronized void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public synchronized ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public synchronized int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public synchronized boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public synchronized boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public synchronized int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public synchronized int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public synchronized void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public synchronized void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public synchronized int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public synchronized ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public synchronized void close() throws SQLException
+ { inner.close(); }
+
+ public synchronized boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public synchronized boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public synchronized void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterResultSet.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterResultSet.java
new file mode 100644
index 0000000..6dbbc27
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterResultSet.java
@@ -0,0 +1,479 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.Object;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+public abstract class SynchronizedFilterResultSet implements ResultSet
+{
+ protected ResultSet inner;
+
+ public SynchronizedFilterResultSet(ResultSet inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterResultSet()
+ {}
+
+ public synchronized void setInner( ResultSet inner )
+ { this.inner = inner; }
+
+ public synchronized ResultSet getInner()
+ { return inner; }
+
+ public synchronized ResultSetMetaData getMetaData() throws SQLException
+ { return inner.getMetaData(); }
+
+ public synchronized SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public synchronized void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public synchronized boolean wasNull() throws SQLException
+ { return inner.wasNull(); }
+
+ public synchronized BigDecimal getBigDecimal(int a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public synchronized BigDecimal getBigDecimal(String a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public synchronized BigDecimal getBigDecimal(int a, int b) throws SQLException
+ { return inner.getBigDecimal(a, b); }
+
+ public synchronized BigDecimal getBigDecimal(String a) throws SQLException
+ { return inner.getBigDecimal(a); }
+
+ public synchronized Timestamp getTimestamp(int a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public synchronized Timestamp getTimestamp(String a) throws SQLException
+ { return inner.getTimestamp(a); }
+
+ public synchronized Timestamp getTimestamp(int a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public synchronized Timestamp getTimestamp(String a, Calendar b) throws SQLException
+ { return inner.getTimestamp(a, b); }
+
+ public synchronized InputStream getAsciiStream(String a) throws SQLException
+ { return inner.getAsciiStream(a); }
+
+ public synchronized InputStream getAsciiStream(int a) throws SQLException
+ { return inner.getAsciiStream(a); }
+
+ public synchronized InputStream getUnicodeStream(String a) throws SQLException
+ { return inner.getUnicodeStream(a); }
+
+ public synchronized InputStream getUnicodeStream(int a) throws SQLException
+ { return inner.getUnicodeStream(a); }
+
+ public synchronized InputStream getBinaryStream(int a) throws SQLException
+ { return inner.getBinaryStream(a); }
+
+ public synchronized InputStream getBinaryStream(String a) throws SQLException
+ { return inner.getBinaryStream(a); }
+
+ public synchronized String getCursorName() throws SQLException
+ { return inner.getCursorName(); }
+
+ public synchronized Reader getCharacterStream(int a) throws SQLException
+ { return inner.getCharacterStream(a); }
+
+ public synchronized Reader getCharacterStream(String a) throws SQLException
+ { return inner.getCharacterStream(a); }
+
+ public synchronized boolean isBeforeFirst() throws SQLException
+ { return inner.isBeforeFirst(); }
+
+ public synchronized boolean isAfterLast() throws SQLException
+ { return inner.isAfterLast(); }
+
+ public synchronized boolean isFirst() throws SQLException
+ { return inner.isFirst(); }
+
+ public synchronized boolean isLast() throws SQLException
+ { return inner.isLast(); }
+
+ public synchronized void beforeFirst() throws SQLException
+ { inner.beforeFirst(); }
+
+ public synchronized void afterLast() throws SQLException
+ { inner.afterLast(); }
+
+ public synchronized boolean absolute(int a) throws SQLException
+ { return inner.absolute(a); }
+
+ public synchronized void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public synchronized int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public synchronized void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public synchronized int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public synchronized int getConcurrency() throws SQLException
+ { return inner.getConcurrency(); }
+
+ public synchronized boolean rowUpdated() throws SQLException
+ { return inner.rowUpdated(); }
+
+ public synchronized boolean rowInserted() throws SQLException
+ { return inner.rowInserted(); }
+
+ public synchronized boolean rowDeleted() throws SQLException
+ { return inner.rowDeleted(); }
+
+ public synchronized void updateNull(int a) throws SQLException
+ { inner.updateNull(a); }
+
+ public synchronized void updateNull(String a) throws SQLException
+ { inner.updateNull(a); }
+
+ public synchronized void updateBoolean(int a, boolean b) throws SQLException
+ { inner.updateBoolean(a, b); }
+
+ public synchronized void updateBoolean(String a, boolean b) throws SQLException
+ { inner.updateBoolean(a, b); }
+
+ public synchronized void updateByte(int a, byte b) throws SQLException
+ { inner.updateByte(a, b); }
+
+ public synchronized void updateByte(String a, byte b) throws SQLException
+ { inner.updateByte(a, b); }
+
+ public synchronized void updateShort(int a, short b) throws SQLException
+ { inner.updateShort(a, b); }
+
+ public synchronized void updateShort(String a, short b) throws SQLException
+ { inner.updateShort(a, b); }
+
+ public synchronized void updateInt(String a, int b) throws SQLException
+ { inner.updateInt(a, b); }
+
+ public synchronized void updateInt(int a, int b) throws SQLException
+ { inner.updateInt(a, b); }
+
+ public synchronized void updateLong(int a, long b) throws SQLException
+ { inner.updateLong(a, b); }
+
+ public synchronized void updateLong(String a, long b) throws SQLException
+ { inner.updateLong(a, b); }
+
+ public synchronized void updateFloat(String a, float b) throws SQLException
+ { inner.updateFloat(a, b); }
+
+ public synchronized void updateFloat(int a, float b) throws SQLException
+ { inner.updateFloat(a, b); }
+
+ public synchronized void updateDouble(String a, double b) throws SQLException
+ { inner.updateDouble(a, b); }
+
+ public synchronized void updateDouble(int a, double b) throws SQLException
+ { inner.updateDouble(a, b); }
+
+ public synchronized void updateBigDecimal(int a, BigDecimal b) throws SQLException
+ { inner.updateBigDecimal(a, b); }
+
+ public synchronized void updateBigDecimal(String a, BigDecimal b) throws SQLException
+ { inner.updateBigDecimal(a, b); }
+
+ public synchronized void updateString(String a, String b) throws SQLException
+ { inner.updateString(a, b); }
+
+ public synchronized void updateString(int a, String b) throws SQLException
+ { inner.updateString(a, b); }
+
+ public synchronized void updateBytes(int a, byte[] b) throws SQLException
+ { inner.updateBytes(a, b); }
+
+ public synchronized void updateBytes(String a, byte[] b) throws SQLException
+ { inner.updateBytes(a, b); }
+
+ public synchronized void updateDate(String a, Date b) throws SQLException
+ { inner.updateDate(a, b); }
+
+ public synchronized void updateDate(int a, Date b) throws SQLException
+ { inner.updateDate(a, b); }
+
+ public synchronized void updateTimestamp(int a, Timestamp b) throws SQLException
+ { inner.updateTimestamp(a, b); }
+
+ public synchronized void updateTimestamp(String a, Timestamp b) throws SQLException
+ { inner.updateTimestamp(a, b); }
+
+ public synchronized void updateAsciiStream(String a, InputStream b, int c) throws SQLException
+ { inner.updateAsciiStream(a, b, c); }
+
+ public synchronized void updateAsciiStream(int a, InputStream b, int c) throws SQLException
+ { inner.updateAsciiStream(a, b, c); }
+
+ public synchronized void updateBinaryStream(int a, InputStream b, int c) throws SQLException
+ { inner.updateBinaryStream(a, b, c); }
+
+ public synchronized void updateBinaryStream(String a, InputStream b, int c) throws SQLException
+ { inner.updateBinaryStream(a, b, c); }
+
+ public synchronized void updateCharacterStream(int a, Reader b, int c) throws SQLException
+ { inner.updateCharacterStream(a, b, c); }
+
+ public synchronized void updateCharacterStream(String a, Reader b, int c) throws SQLException
+ { inner.updateCharacterStream(a, b, c); }
+
+ public synchronized void updateObject(String a, Object b) throws SQLException
+ { inner.updateObject(a, b); }
+
+ public synchronized void updateObject(int a, Object b) throws SQLException
+ { inner.updateObject(a, b); }
+
+ public synchronized void updateObject(int a, Object b, int c) throws SQLException
+ { inner.updateObject(a, b, c); }
+
+ public synchronized void updateObject(String a, Object b, int c) throws SQLException
+ { inner.updateObject(a, b, c); }
+
+ public synchronized void insertRow() throws SQLException
+ { inner.insertRow(); }
+
+ public synchronized void updateRow() throws SQLException
+ { inner.updateRow(); }
+
+ public synchronized void deleteRow() throws SQLException
+ { inner.deleteRow(); }
+
+ public synchronized void refreshRow() throws SQLException
+ { inner.refreshRow(); }
+
+ public synchronized void cancelRowUpdates() throws SQLException
+ { inner.cancelRowUpdates(); }
+
+ public synchronized void moveToInsertRow() throws SQLException
+ { inner.moveToInsertRow(); }
+
+ public synchronized void moveToCurrentRow() throws SQLException
+ { inner.moveToCurrentRow(); }
+
+ public synchronized Statement getStatement() throws SQLException
+ { return inner.getStatement(); }
+
+ public synchronized Blob getBlob(String a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public synchronized Blob getBlob(int a) throws SQLException
+ { return inner.getBlob(a); }
+
+ public synchronized Clob getClob(String a) throws SQLException
+ { return inner.getClob(a); }
+
+ public synchronized Clob getClob(int a) throws SQLException
+ { return inner.getClob(a); }
+
+ public synchronized void updateRef(String a, Ref b) throws SQLException
+ { inner.updateRef(a, b); }
+
+ public synchronized void updateRef(int a, Ref b) throws SQLException
+ { inner.updateRef(a, b); }
+
+ public synchronized void updateBlob(String a, Blob b) throws SQLException
+ { inner.updateBlob(a, b); }
+
+ public synchronized void updateBlob(int a, Blob b) throws SQLException
+ { inner.updateBlob(a, b); }
+
+ public synchronized void updateClob(int a, Clob b) throws SQLException
+ { inner.updateClob(a, b); }
+
+ public synchronized void updateClob(String a, Clob b) throws SQLException
+ { inner.updateClob(a, b); }
+
+ public synchronized void updateArray(String a, Array b) throws SQLException
+ { inner.updateArray(a, b); }
+
+ public synchronized void updateArray(int a, Array b) throws SQLException
+ { inner.updateArray(a, b); }
+
+ public synchronized Object getObject(int a) throws SQLException
+ { return inner.getObject(a); }
+
+ public synchronized Object getObject(String a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public synchronized Object getObject(String a) throws SQLException
+ { return inner.getObject(a); }
+
+ public synchronized Object getObject(int a, Map b) throws SQLException
+ { return inner.getObject(a, b); }
+
+ public synchronized boolean getBoolean(int a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public synchronized boolean getBoolean(String a) throws SQLException
+ { return inner.getBoolean(a); }
+
+ public synchronized byte getByte(String a) throws SQLException
+ { return inner.getByte(a); }
+
+ public synchronized byte getByte(int a) throws SQLException
+ { return inner.getByte(a); }
+
+ public synchronized short getShort(String a) throws SQLException
+ { return inner.getShort(a); }
+
+ public synchronized short getShort(int a) throws SQLException
+ { return inner.getShort(a); }
+
+ public synchronized int getInt(String a) throws SQLException
+ { return inner.getInt(a); }
+
+ public synchronized int getInt(int a) throws SQLException
+ { return inner.getInt(a); }
+
+ public synchronized long getLong(int a) throws SQLException
+ { return inner.getLong(a); }
+
+ public synchronized long getLong(String a) throws SQLException
+ { return inner.getLong(a); }
+
+ public synchronized float getFloat(String a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public synchronized float getFloat(int a) throws SQLException
+ { return inner.getFloat(a); }
+
+ public synchronized double getDouble(int a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public synchronized double getDouble(String a) throws SQLException
+ { return inner.getDouble(a); }
+
+ public synchronized byte[] getBytes(String a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public synchronized byte[] getBytes(int a) throws SQLException
+ { return inner.getBytes(a); }
+
+ public synchronized boolean next() throws SQLException
+ { return inner.next(); }
+
+ public synchronized URL getURL(int a) throws SQLException
+ { return inner.getURL(a); }
+
+ public synchronized URL getURL(String a) throws SQLException
+ { return inner.getURL(a); }
+
+ public synchronized int getType() throws SQLException
+ { return inner.getType(); }
+
+ public synchronized boolean previous() throws SQLException
+ { return inner.previous(); }
+
+ public synchronized void close() throws SQLException
+ { inner.close(); }
+
+ public synchronized String getString(String a) throws SQLException
+ { return inner.getString(a); }
+
+ public synchronized String getString(int a) throws SQLException
+ { return inner.getString(a); }
+
+ public synchronized Ref getRef(String a) throws SQLException
+ { return inner.getRef(a); }
+
+ public synchronized Ref getRef(int a) throws SQLException
+ { return inner.getRef(a); }
+
+ public synchronized Time getTime(int a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public synchronized Time getTime(String a) throws SQLException
+ { return inner.getTime(a); }
+
+ public synchronized Time getTime(int a) throws SQLException
+ { return inner.getTime(a); }
+
+ public synchronized Time getTime(String a, Calendar b) throws SQLException
+ { return inner.getTime(a, b); }
+
+ public synchronized Date getDate(String a) throws SQLException
+ { return inner.getDate(a); }
+
+ public synchronized Date getDate(int a) throws SQLException
+ { return inner.getDate(a); }
+
+ public synchronized Date getDate(int a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public synchronized Date getDate(String a, Calendar b) throws SQLException
+ { return inner.getDate(a, b); }
+
+ public synchronized boolean first() throws SQLException
+ { return inner.first(); }
+
+ public synchronized boolean last() throws SQLException
+ { return inner.last(); }
+
+ public synchronized Array getArray(String a) throws SQLException
+ { return inner.getArray(a); }
+
+ public synchronized Array getArray(int a) throws SQLException
+ { return inner.getArray(a); }
+
+ public synchronized boolean relative(int a) throws SQLException
+ { return inner.relative(a); }
+
+ public synchronized void updateTime(String a, Time b) throws SQLException
+ { inner.updateTime(a, b); }
+
+ public synchronized void updateTime(int a, Time b) throws SQLException
+ { inner.updateTime(a, b); }
+
+ public synchronized int findColumn(String a) throws SQLException
+ { return inner.findColumn(a); }
+
+ public synchronized int getRow() throws SQLException
+ { return inner.getRow(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterStatement.java b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterStatement.java
new file mode 100644
index 0000000..a7fd352
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/filter/SynchronizedFilterStatement.java
@@ -0,0 +1,159 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.filter;
+
+import java.lang.String;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+public abstract class SynchronizedFilterStatement implements Statement
+{
+ protected Statement inner;
+
+ public SynchronizedFilterStatement(Statement inner)
+ { this.inner = inner; }
+
+ public SynchronizedFilterStatement()
+ {}
+
+ public synchronized void setInner( Statement inner )
+ { this.inner = inner; }
+
+ public synchronized Statement getInner()
+ { return inner; }
+
+ public synchronized SQLWarning getWarnings() throws SQLException
+ { return inner.getWarnings(); }
+
+ public synchronized void clearWarnings() throws SQLException
+ { inner.clearWarnings(); }
+
+ public synchronized void setFetchDirection(int a) throws SQLException
+ { inner.setFetchDirection(a); }
+
+ public synchronized int getFetchDirection() throws SQLException
+ { return inner.getFetchDirection(); }
+
+ public synchronized void setFetchSize(int a) throws SQLException
+ { inner.setFetchSize(a); }
+
+ public synchronized int getFetchSize() throws SQLException
+ { return inner.getFetchSize(); }
+
+ public synchronized int getResultSetHoldability() throws SQLException
+ { return inner.getResultSetHoldability(); }
+
+ public synchronized ResultSet executeQuery(String a) throws SQLException
+ { return inner.executeQuery(a); }
+
+ public synchronized int executeUpdate(String a, int b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, String[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a, int[] b) throws SQLException
+ { return inner.executeUpdate(a, b); }
+
+ public synchronized int executeUpdate(String a) throws SQLException
+ { return inner.executeUpdate(a); }
+
+ public synchronized int getMaxFieldSize() throws SQLException
+ { return inner.getMaxFieldSize(); }
+
+ public synchronized void setMaxFieldSize(int a) throws SQLException
+ { inner.setMaxFieldSize(a); }
+
+ public synchronized int getMaxRows() throws SQLException
+ { return inner.getMaxRows(); }
+
+ public synchronized void setMaxRows(int a) throws SQLException
+ { inner.setMaxRows(a); }
+
+ public synchronized void setEscapeProcessing(boolean a) throws SQLException
+ { inner.setEscapeProcessing(a); }
+
+ public synchronized int getQueryTimeout() throws SQLException
+ { return inner.getQueryTimeout(); }
+
+ public synchronized void setQueryTimeout(int a) throws SQLException
+ { inner.setQueryTimeout(a); }
+
+ public synchronized void setCursorName(String a) throws SQLException
+ { inner.setCursorName(a); }
+
+ public synchronized ResultSet getResultSet() throws SQLException
+ { return inner.getResultSet(); }
+
+ public synchronized int getUpdateCount() throws SQLException
+ { return inner.getUpdateCount(); }
+
+ public synchronized boolean getMoreResults() throws SQLException
+ { return inner.getMoreResults(); }
+
+ public synchronized boolean getMoreResults(int a) throws SQLException
+ { return inner.getMoreResults(a); }
+
+ public synchronized int getResultSetConcurrency() throws SQLException
+ { return inner.getResultSetConcurrency(); }
+
+ public synchronized int getResultSetType() throws SQLException
+ { return inner.getResultSetType(); }
+
+ public synchronized void addBatch(String a) throws SQLException
+ { inner.addBatch(a); }
+
+ public synchronized void clearBatch() throws SQLException
+ { inner.clearBatch(); }
+
+ public synchronized int[] executeBatch() throws SQLException
+ { return inner.executeBatch(); }
+
+ public synchronized ResultSet getGeneratedKeys() throws SQLException
+ { return inner.getGeneratedKeys(); }
+
+ public synchronized void close() throws SQLException
+ { inner.close(); }
+
+ public synchronized boolean execute(String a, int b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a) throws SQLException
+ { return inner.execute(a); }
+
+ public synchronized boolean execute(String a, int[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized boolean execute(String a, String[] b) throws SQLException
+ { return inner.execute(a, b); }
+
+ public synchronized Connection getConnection() throws SQLException
+ { return inner.getConnection(); }
+
+ public synchronized void cancel() throws SQLException
+ { inner.cancel(); }
+}
diff --git a/src/classes/com/mchange/v2/sql/junit/SqlUtilsJUnitTestCase.java b/src/classes/com/mchange/v2/sql/junit/SqlUtilsJUnitTestCase.java
new file mode 100644
index 0000000..168fe92
--- /dev/null
+++ b/src/classes/com/mchange/v2/sql/junit/SqlUtilsJUnitTestCase.java
@@ -0,0 +1,48 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.sql.junit;
+
+import java.sql.*;
+import junit.framework.*;
+import com.mchange.v2.sql.SqlUtils;
+
+public class SqlUtilsJUnitTestCase extends TestCase
+{
+ public void testGoodDebugLoggingOfNestedExceptions()
+ {
+ // this is only supposed to complete (in response to a bug where logging of
+ // nested Exceptions was an infinite loop.
+ SQLException original = new SQLException("Original.");
+ SQLWarning nestedWarning = new SQLWarning("Nested Warning.");
+ original.setNextException( nestedWarning );
+ SqlUtils.toSQLException( original );
+ }
+
+ public static void main(String[] argv)
+ {
+ junit.textui.TestRunner.run( new TestSuite( SqlUtilsJUnitTestCase.class ) );
+ //junit.swingui.TestRunner.run( SqlUtilsJUnitTestCase.class );
+ //new SqlUtilsJUnitTestCase().testGoodDebugLoggingOfNestedExceptions();
+ }
+}
\ No newline at end of file
diff --git a/src/classes/com/mchange/v2/util/DoubleWeakHashMap.java b/src/classes/com/mchange/v2/util/DoubleWeakHashMap.java
new file mode 100644
index 0000000..472f5c1
--- /dev/null
+++ b/src/classes/com/mchange/v2/util/DoubleWeakHashMap.java
@@ -0,0 +1,577 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.util;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+
+import com.mchange.v1.util.AbstractMapEntry;
+import com.mchange.v1.util.WrapperIterator;
+
+//TODO -- ensure that cleanCleared() gets called only once, even in methods implemented
+// as loops. (cleanCleared() is idempotent, so the repeated calls are okay,
+// but they're wasteful.
+
+/**
+ * <p>This class is <u>not</u> Thread safe.
+ * Use in single threaded contexts, or contexts where
+ * single threaded-access can be guaranteed, or
+ * wrap with Collections.synchronizedMap().</p>
+ *
+ * <p>This class does not accept null keys or values.</p>
+ */
+public class DoubleWeakHashMap implements Map
+{
+ HashMap inner;
+ ReferenceQueue keyQ = new ReferenceQueue();
+ ReferenceQueue valQ = new ReferenceQueue();
+
+ CheckKeyHolder holder = new CheckKeyHolder();
+
+ Set userKeySet = null;
+ Collection valuesCollection = null;
+
+ public DoubleWeakHashMap()
+ { this.inner = new HashMap(); }
+
+ public DoubleWeakHashMap(int initialCapacity)
+ { this.inner = new HashMap( initialCapacity ); }
+
+ public DoubleWeakHashMap(int initialCapacity, float loadFactor)
+ { this.inner = new HashMap( initialCapacity, loadFactor ); }
+
+ public DoubleWeakHashMap(Map m)
+ {
+ this();
+ putAll(m);
+ }
+
+ public void cleanCleared()
+ {
+ WKey wk;
+ while ((wk = (WKey) keyQ.poll()) != null)
+ inner.remove(wk);
+
+ WVal wv;
+ while ((wv = (WVal) valQ.poll()) != null)
+ inner.remove(wv.getWKey());
+ }
+
+ public void clear()
+ {
+ cleanCleared();
+ inner.clear();
+ }
+
+ public boolean containsKey(Object key)
+ {
+ cleanCleared();
+ try
+ { return inner.containsKey( holder.set(key) ); }
+ finally
+ { holder.clear(); }
+ }
+
+ public boolean containsValue(Object val)
+ {
+ for (Iterator ii = inner.values().iterator(); ii.hasNext();)
+ {
+ WVal wval = (WVal) ii.next();
+ if (val.equals(wval.get()))
+ return true;
+ }
+ return false;
+ }
+
+ public Set entrySet()
+ {
+ cleanCleared();
+ return new UserEntrySet();
+ }
+
+ public Object get(Object key)
+ {
+ try
+ {
+ cleanCleared();
+ WVal wval = (WVal) inner.get(holder.set(key));
+ return (wval == null ? null : wval.get());
+ }
+ finally
+ { holder.clear(); }
+ }
+
+ public boolean isEmpty()
+ {
+ cleanCleared();
+ return inner.isEmpty();
+ }
+
+ public Set keySet()
+ {
+ cleanCleared();
+ if (userKeySet == null)
+ userKeySet = new UserKeySet();
+ return userKeySet;
+ }
+
+ public Object put(Object key, Object val)
+ {
+ cleanCleared();
+ WVal wout = doPut(key, val);
+ if (wout != null)
+ return wout.get();
+ else
+ return null;
+ }
+
+ private WVal doPut(Object key, Object val)
+ {
+ WKey wk = new WKey(key, keyQ);
+ WVal wv = new WVal(wk, val, valQ);
+ return (WVal) inner.put(wk, wv);
+ }
+
+ public void putAll(Map m)
+ {
+ cleanCleared();
+ for (Iterator ii = m.entrySet().iterator(); ii.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) ii.next();
+ this.doPut( entry.getKey(), entry.getValue() );
+ }
+ }
+
+ public Object remove(Object key)
+ {
+ try
+ {
+ cleanCleared();
+ WVal wv = (WVal) inner.remove( holder.set(key) );
+ return (wv == null ? null : wv.get());
+ }
+ finally
+ { holder.clear(); }
+ }
+
+ public int size()
+ {
+ cleanCleared();
+ return inner.size();
+ }
+
+ public Collection values()
+ {
+ if (valuesCollection == null)
+ this.valuesCollection = new ValuesCollection();
+ return valuesCollection;
+ }
+
+ final static class CheckKeyHolder
+ {
+ Object checkKey;
+
+ public Object get()
+ {return checkKey; }
+
+ public CheckKeyHolder set(Object ck)
+ {
+ assert this.checkKey == null : "Illegal concurrenct use of DoubleWeakHashMap!";
+
+ this.checkKey = ck;
+ return this;
+ }
+
+ public void clear()
+ { checkKey = null; }
+
+ public int hashCode()
+ { return checkKey.hashCode(); }
+
+ public boolean equals(Object o)
+ {
+ assert this.get() != null : "CheckedKeyHolder should never do an equality check while its value is null." ;
+
+ if (this == o)
+ return true;
+ else if (o instanceof CheckKeyHolder)
+ return this.get().equals( ((CheckKeyHolder) o).get() );
+ else if (o instanceof WKey)
+ return this.get().equals( ((WKey) o).get() );
+ else
+ return false;
+ }
+ }
+
+ final static class WKey extends WeakReference
+ {
+ int cachedHash;
+
+ WKey(Object keyObj, ReferenceQueue rq)
+ {
+ super(keyObj, rq);
+ this.cachedHash = keyObj.hashCode();
+ }
+
+ public int hashCode()
+ { return cachedHash; }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ else if (o instanceof WKey)
+ {
+ WKey oo = (WKey) o;
+ Object myVal = this.get();
+ Object ooVal = oo.get();
+ if (myVal == null || ooVal == null)
+ return false;
+ else
+ return myVal.equals(ooVal);
+ }
+ else if (o instanceof CheckKeyHolder)
+ {
+ CheckKeyHolder oo = (CheckKeyHolder) o;
+ Object myVal = this.get();
+ Object ooVal = oo.get();
+ if (myVal == null || ooVal == null)
+ return false;
+ else
+ return myVal.equals(ooVal);
+ }
+ else
+ return false;
+ }
+ }
+
+ final static class WVal extends WeakReference
+ {
+ WKey key;
+ WVal(WKey key, Object valObj, ReferenceQueue rq)
+ {
+ super(valObj, rq);
+ this.key = key;
+ }
+
+ public WKey getWKey()
+ { return key; }
+ }
+
+
+ private final class UserEntrySet extends AbstractSet
+ {
+ private Set innerEntrySet()
+ {
+ cleanCleared();
+ return inner.entrySet();
+ }
+
+ public Iterator iterator()
+ {
+ return new WrapperIterator(innerEntrySet().iterator(), true)
+ {
+ protected Object transformObject(Object o)
+ {
+ Entry innerEntry = (Entry) o;
+ Object key = ((WKey) innerEntry.getKey()).get();
+ Object val = ((WVal) innerEntry.getValue()).get();
+
+ if (key == null || val == null)
+ return WrapperIterator.SKIP_TOKEN;
+ else
+ return new UserEntry( innerEntry, key, val );
+ }
+ };
+ }
+
+ public int size()
+ { return innerEntrySet().size(); }
+ }
+
+ class UserEntry extends AbstractMapEntry
+ {
+ Entry innerEntry;
+ Object key;
+ Object val;
+
+ UserEntry(Entry innerEntry, Object key, Object val)
+ {
+ this.innerEntry = innerEntry;
+ this.key = key;
+ this.val = val;
+ }
+
+ public final Object getKey()
+ { return key; }
+
+ public final Object getValue()
+ { return val; }
+
+ public final Object setValue(Object value)
+ { return innerEntry.setValue( new WVal( (WKey) innerEntry.getKey() ,value, valQ) ); }
+ }
+
+ class UserKeySet implements Set
+ {
+ public boolean add(Object o)
+ {
+ cleanCleared();
+ throw new UnsupportedOperationException("You cannot add to a Map's key set.");
+ }
+
+ public boolean addAll(Collection c)
+ {
+ cleanCleared();
+ throw new UnsupportedOperationException("You cannot add to a Map's key set.");
+ }
+
+ public void clear()
+ { DoubleWeakHashMap.this.clear(); }
+
+ public boolean contains(Object o)
+ {
+ return DoubleWeakHashMap.this.containsKey(o);
+ }
+
+ public boolean containsAll(Collection c)
+ {
+ for (Iterator ii = c.iterator(); ii.hasNext();)
+ if (! this.contains(ii.next()))
+ return false;
+ return true;
+ }
+
+ public boolean isEmpty()
+ { return DoubleWeakHashMap.this.isEmpty(); }
+
+ public Iterator iterator()
+ {
+ cleanCleared();
+ return new WrapperIterator(DoubleWeakHashMap.this.inner.keySet().iterator(), true)
+ {
+ protected Object transformObject(Object o)
+ {
+ Object key = ((WKey) o).get();
+
+ if (key == null)
+ return WrapperIterator.SKIP_TOKEN;
+ else
+ return key;
+ }
+ };
+ }
+
+ public boolean remove(Object o)
+ {
+ return (DoubleWeakHashMap.this.remove(o) != null);
+ }
+
+ public boolean removeAll(Collection c)
+ {
+ boolean out = false;
+ for (Iterator ii = c.iterator(); ii.hasNext();)
+ out |= this.remove(ii.next());
+ return out;
+ }
+
+ public boolean retainAll(Collection c)
+ {
+ //we implicitly cleanCleared() by calling iterator()
+ boolean out = false;
+ for (Iterator ii = this.iterator(); ii.hasNext();)
+ {
+ if (!c.contains(ii.next()))
+ {
+ ii.remove();
+ out = true;
+ }
+ }
+ return out;
+ }
+
+ public int size()
+ { return DoubleWeakHashMap.this.size(); }
+
+ public Object[] toArray()
+ {
+ cleanCleared();
+ return new HashSet( this ).toArray();
+ }
+
+ public Object[] toArray(Object[] array)
+ {
+ cleanCleared();
+ return new HashSet( this ).toArray(array);
+ }
+ }
+
+ class ValuesCollection implements Collection
+ {
+
+ public boolean add(Object o)
+ {
+ cleanCleared();
+ throw new UnsupportedOperationException("DoubleWeakHashMap does not support adding to its values Collection.");
+ }
+
+ public boolean addAll(Collection c)
+ {
+ cleanCleared();
+ throw new UnsupportedOperationException("DoubleWeakHashMap does not support adding to its values Collection.");
+ }
+
+ public void clear()
+ { DoubleWeakHashMap.this.clear(); }
+
+ public boolean contains(Object o)
+ { return DoubleWeakHashMap.this.containsValue(o); }
+
+ public boolean containsAll(Collection c)
+ {
+ for (Iterator ii = c.iterator(); ii.hasNext();)
+ if (!this.contains(ii.next()))
+ return false;
+ return true;
+ }
+
+ public boolean isEmpty()
+ { return DoubleWeakHashMap.this.isEmpty(); }
+
+ public Iterator iterator()
+ {
+ return new WrapperIterator(inner.values().iterator(), true)
+ {
+ protected Object transformObject(Object o)
+ {
+ Object val = ((WVal) o).get();
+
+ if (val == null)
+ return WrapperIterator.SKIP_TOKEN;
+ else
+ return val;
+ }
+ };
+ }
+
+ public boolean remove(Object o)
+ {
+ cleanCleared();
+ return removeValue(o);
+ }
+
+ public boolean removeAll(Collection c)
+ {
+ cleanCleared();
+ boolean out = false;
+ for (Iterator ii = c.iterator(); ii.hasNext();)
+ out |= removeValue(ii.next());
+ return out;
+ }
+
+ public boolean retainAll(Collection c)
+ {
+ cleanCleared();
+ return retainValues(c);
+ }
+
+ public int size()
+ { return DoubleWeakHashMap.this.size(); }
+
+ public Object[] toArray()
+ {
+ cleanCleared();
+ return new ArrayList(this).toArray();
+ }
+
+ public Object[] toArray(Object[] array)
+ {
+ cleanCleared();
+ return new ArrayList(this).toArray(array);
+ }
+
+ private boolean removeValue(Object val)
+ {
+ boolean out = false;
+ for (Iterator ii = inner.values().iterator(); ii.hasNext();)
+ {
+ WVal wv = (WVal) ii.next();
+ if (val.equals(wv.get()))
+ {
+ ii.remove();
+ out = true;
+ }
+ }
+ return out;
+ }
+
+ private boolean retainValues(Collection c)
+ {
+ boolean out = false;
+ for (Iterator ii = inner.values().iterator(); ii.hasNext();)
+ {
+ WVal wv = (WVal) ii.next();
+ if (! c.contains(wv.get()) )
+ {
+ ii.remove();
+ out = true;
+ }
+ }
+ return out;
+ }
+ }
+
+ /*
+ public static void main(String[] argv)
+ {
+ DoubleWeakHashMap m = new DoubleWeakHashMap();
+ //Set keySet = new HashSet();
+ //Set valSet = new HashSet();
+
+ while (true)
+ {
+ System.err.println( m.inner.size() );
+
+ //if (Math.random() < 0.1f)
+ // valSet.clear();
+
+ Object key = new Object();
+ Object val = new long[100000];
+ //keySet.add(key);
+ //valSet.add(val);
+ m.put( key, val );
+ }
+ }
+ */
+}
diff --git a/src/classes/com/mchange/v2/util/ResourceClosedException.java b/src/classes/com/mchange/v2/util/ResourceClosedException.java
new file mode 100644
index 0000000..44d59d1
--- /dev/null
+++ b/src/classes/com/mchange/v2/util/ResourceClosedException.java
@@ -0,0 +1,67 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.util;
+
+import com.mchange.v2.lang.VersionUtils;
+
+public class ResourceClosedException extends RuntimeException
+{
+ //retaining 1.3.x compatability for now
+
+// public ResourceClosedException(String msg, Throwable t)
+// { super( msg, t ); }
+
+// public ResourceClosedException(Throwable t)
+// { super(t); }
+
+ Throwable rootCause;
+
+ public ResourceClosedException(String msg, Throwable t)
+ {
+ super( msg );
+ setRootCause( t );
+ }
+
+ public ResourceClosedException(Throwable t)
+ {
+ super();
+ setRootCause( t );
+ }
+
+ public ResourceClosedException(String msg)
+ { super( msg ); }
+
+ public ResourceClosedException()
+ { super(); }
+
+ public Throwable getCause()
+ { return rootCause; }
+
+ private void setRootCause( Throwable t )
+ {
+ this.rootCause = t;
+ if ( VersionUtils.isAtLeastJavaVersion14() )
+ this.initCause( t );
+ }
+}
diff --git a/src/classes/com/mchange/v2/util/junit/DoubleWeakHashMapJUnitTestCase.java b/src/classes/com/mchange/v2/util/junit/DoubleWeakHashMapJUnitTestCase.java
new file mode 100644
index 0000000..9ffed70
--- /dev/null
+++ b/src/classes/com/mchange/v2/util/junit/DoubleWeakHashMapJUnitTestCase.java
@@ -0,0 +1,108 @@
+/*
+ * Distributed as part of c3p0 v.0.9.1.2
+ *
+ * Copyright (C) 2005 Machinery For Change, Inc.
+ *
+ * Author: Steve Waldman <swaldman at mchange.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1, as
+ * published by the Free Software Foundation.
+ *
+ * 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; see the file LICENSE. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+package com.mchange.v2.util.junit;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import com.mchange.v2.util.DoubleWeakHashMap;
+
+import junit.framework.TestCase;
+
+public class DoubleWeakHashMapJUnitTestCase extends TestCase
+{
+ public void testGetNeverAdded()
+ {
+ Map m = new DoubleWeakHashMap();
+ assertNull( m.get("foo") );
+ }
+
+ public void testHardAdds()
+ {
+ Integer a = new Integer(1);
+ Integer b = new Integer(2);
+ Integer c = new Integer(3);
+
+ String poop = new String("poop");
+ String scoop = new String("scoop");
+ String doop = new String("dcoop");
+
+ Map m = new DoubleWeakHashMap();
+ m.put(a, poop);
+ m.put(b, scoop);
+ m.put(c, doop);
+ assertEquals("Size should be three, viewed via Map directly.", m.size(), 3);
+ assertEquals("Size should be three, viewed via keySet .", m.keySet().size(), 3);
+ assertEquals("Size should be three, viewed via values Collection.", m.values().size(), 3);
+
+ int count = 0;
+ for (Iterator ii = m.keySet().iterator(); ii.hasNext();)
+ {
+ count += ((Integer) ii.next()).intValue();
+ }
+ assertEquals("Count should be six, viewed via values Collection.", count, 6);
+
+ Integer d = new Integer(4);
+ m.put(d, poop);
+ m.values().remove(poop);
+ assertEquals("After removing a doubled value, size should be 2", m.size(), 2);
+ }
+
+ public void testWeakness()
+ {
+ Integer a = new Integer(1);
+ Integer b = new Integer(2);
+ Integer c = new Integer(3);
+
+ String poop = new String("poop");
+
+ Map m = new DoubleWeakHashMap();
+ m.put(a, poop);
+ m.put(b, new Object());
+ m.put(c, new Object());
+
+ //race condition... b & c might already have been removed... but i doubt it
+ assertEquals("1) Weak values should not yet have been removed (but not guaranteed! sometimes fails without a defect!)", m.size(), 3);
+
+ // we are relying that a full, synchronous GC occurs,
+ // which is not guaranteed in all VMs
+ System.gc();
+
+ // let's see if we can force a deeper gc via a big array creation
+ byte[] bArray = new byte[1024 * 1024];
+
+ assertEquals("2) Weak values should have been automatically removed (but not guaranteed! sometimes fails without a defect!)", m.size(), 1);
+
+ m.put( new Object(), b);
+
+ //race condition... b & c might already have been removed... but i doubt it
+ assertEquals("3) Weak key should not yet have been removed (but not guaranteed! sometimes fails without a defect!)", m.size(), 2);
+
+ System.gc();
+ // let's see if we can force a deeper gc via a big array creation
+ bArray = new byte[1024 * 1024];
+
+ assertEquals("4) Weak key should have been automatically removed (but not guaranteed! sometimes fails without a defect!)", m.size(), 1);
+ }
+}
diff --git a/src/codegen/com/mchange/v2/c3p0/impl/DriverManagerDataSourceBase.beangen-xml b/src/codegen/com/mchange/v2/c3p0/impl/DriverManagerDataSourceBase.beangen-xml
new file mode 100644
index 0000000..aa0e9f7
--- /dev/null
+++ b/src/codegen/com/mchange/v2/c3p0/impl/DriverManagerDataSourceBase.beangen-xml
@@ -0,0 +1,71 @@
+<beangen>
+ <package>com.mchange.v2.c3p0.impl</package>
+ <imports>
+ <general>java.util</general>
+ <specific>com.mchange.v2.c3p0.impl.AuthMaskingProperties</specific>
+ <specific>com.mchange.v2.c3p0.cfg.C3P0Config</specific>
+ </imports>
+ <modifiers>
+ <modifier>public</modifier>
+ <modifier>abstract</modifier>
+ </modifiers>
+ <output-class>DriverManagerDataSourceBase</output-class>
+ <extends>IdentityTokenResolvable</extends>
+ <properties>
+ <property>
+ <variable><modifiers><modifier>protected</modifier></modifiers></variable>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ <type>String</type>
+ <name>driverClass</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("driverClass", C3P0Defaults.driverClass())</default-value>
+ <bound/>
+ </property>
+ <property>
+ <variable><modifiers><modifier>protected</modifier></modifiers></variable>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ <type>String</type>
+ <name>jdbcUrl</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("jdbcUrl", C3P0Defaults.jdbcUrl())</default-value>
+ </property>
+ <property>
+ <variable><modifiers><modifier>protected</modifier></modifiers></variable>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ <type>Properties</type>
+ <name>properties</name>
+ <defensive-copy>
+ (Properties) AuthMaskingProperties.fromAnyProperties( properties )
+ </defensive-copy>
+ <default-value>new AuthMaskingProperties()</default-value>
+ <bound/>
+ </property>
+ <property>
+ <variable><modifiers><modifier>protected</modifier></modifiers></variable>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ <type>String</type>
+ <name>description</name>
+ </property>
+ <property>
+ <type>String</type>
+ <name>identityToken</name>
+ <bound/> <!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <variable><modifiers><modifier>protected</modifier></modifiers></variable>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ <type>String</type>
+ <name>factoryClassLocation</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("factoryClassLocation", C3P0Defaults.factoryClassLocation())</default-value>
+ </property>
+ </properties>
+</beangen>
+
+
+
+
diff --git a/src/codegen/com/mchange/v2/c3p0/impl/JndiRefDataSourceBase.beangen-xml b/src/codegen/com/mchange/v2/c3p0/impl/JndiRefDataSourceBase.beangen-xml
new file mode 100644
index 0000000..ec32c73
--- /dev/null
+++ b/src/codegen/com/mchange/v2/c3p0/impl/JndiRefDataSourceBase.beangen-xml
@@ -0,0 +1,53 @@
+<!-- DON'T FORGET TO CALL C3P0Registry.register( this ) AT CONSTRUCTION OF CONCRETE SUBCLASS -->
+
+<beangen>
+ <package>com.mchange.v2.c3p0.impl</package>
+ <imports>
+ <specific>java.util.Hashtable</specific>
+ <specific>javax.naming.Name</specific>
+ <specific>com.mchange.v2.c3p0.cfg.C3P0Config</specific>
+ </imports>
+ <output-class>JndiRefDataSourceBase</output-class>
+ <extends>IdentityTokenResolvable</extends>
+ <properties>
+ <property>
+ <type>Object</type>
+ <name>jndiName</name>
+ <defensive-copy>
+ (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName /* String */)
+ </defensive-copy>
+ <constrained />
+ <bound />
+ </property>
+ <property>
+ <type>Hashtable</type>
+ <name>jndiEnv</name>
+ <defensive-copy>
+ (jndiEnv != null ? (Hashtable) jndiEnv.clone() : null)
+ </defensive-copy>
+ <bound />
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>caching</name>
+ <default-value>true</default-value>
+ <bound />
+ </property>
+ <property>
+ <type>String</type>
+ <name>identityToken</name>
+ <bound/> <!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>factoryClassLocation</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("factoryClassLocation", C3P0Defaults.factoryClassLocation())</default-value>
+ <bound />
+ </property>
+ </properties>
+</beangen>
+
+
+
diff --git a/src/codegen/com/mchange/v2/c3p0/impl/PoolBackedDataSourceBase.beangen-xml b/src/codegen/com/mchange/v2/c3p0/impl/PoolBackedDataSourceBase.beangen-xml
new file mode 100644
index 0000000..4749a83
--- /dev/null
+++ b/src/codegen/com/mchange/v2/c3p0/impl/PoolBackedDataSourceBase.beangen-xml
@@ -0,0 +1,56 @@
+<!-- DON'T FORGET TO CALL C3P0Registry.register( this ) AT CONSTRUCTION OF CONCRETE SUBCLASS -->
+
+<beangen>
+ <package>com.mchange.v2.c3p0.impl</package>
+ <imports>
+ <specific>javax.sql.DataSource</specific>
+ <specific>javax.sql.ConnectionPoolDataSource</specific>
+ <specific>com.mchange.v2.c3p0.cfg.C3P0Config</specific>
+ </imports>
+ <output-class>PoolBackedDataSourceBase</output-class>
+ <extends>IdentityTokenResolvable</extends>
+ <properties>
+ <property>
+ <type>ConnectionPoolDataSource</type>
+ <name>connectionPoolDataSource</name>
+ <bound/><!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <constrained/><!-- ComboPooledDataSource requires the nested DataSources to be of c3p0 types -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>numHelperThreads</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("numHelperThreads", C3P0Defaults.numHelperThreads())</default-value>
+ <bound/><!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>identityToken</name>
+ <bound/> <!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>dataSourceName</name>
+ <default-value>null</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+
+ <property>
+ <type>String</type>
+ <name>factoryClassLocation</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("factoryClassLocation", C3P0Defaults.factoryClassLocation())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ </properties>
+</beangen>
+
+
+
+
diff --git a/src/codegen/com/mchange/v2/c3p0/impl/WrapperConnectionPoolDataSourceBase.beangen-xml b/src/codegen/com/mchange/v2/c3p0/impl/WrapperConnectionPoolDataSourceBase.beangen-xml
new file mode 100644
index 0000000..ac224d9
--- /dev/null
+++ b/src/codegen/com/mchange/v2/c3p0/impl/WrapperConnectionPoolDataSourceBase.beangen-xml
@@ -0,0 +1,252 @@
+<beangen>
+ <package>com.mchange.v2.c3p0.impl</package>
+ <imports>
+ <specific>javax.sql.DataSource</specific>
+ <specific>com.mchange.v2.c3p0.cfg.C3P0Config</specific>
+ </imports>
+ <modifiers>
+ <modifier>public</modifier>
+ <modifier>abstract</modifier>
+ </modifiers>
+ <output-class>WrapperConnectionPoolDataSourceBase</output-class>
+ <extends>IdentityTokenResolvable</extends>
+ <properties>
+ <property>
+ <type>DataSource</type>
+ <name>nestedDataSource</name>
+ <bound/>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxStatements</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxStatements", C3P0Defaults.maxStatements())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxStatementsPerConnection</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxStatementsPerConnection", C3P0Defaults.maxStatementsPerConnection())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>initialPoolSize</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("initialPoolSize", C3P0Defaults.initialPoolSize())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>minPoolSize</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("minPoolSize", C3P0Defaults.minPoolSize())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxPoolSize</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxPoolSize", C3P0Defaults.maxPoolSize())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxAdministrativeTaskTime</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxAdministrativeTaskTime", C3P0Defaults.maxAdministrativeTaskTime())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxIdleTimeExcessConnections</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxIdleTimeExcessConnections", C3P0Defaults.maxIdleTimeExcessConnections())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxConnectionAge</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxConnectionAge", C3P0Defaults.maxConnectionAge())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>unreturnedConnectionTimeout</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("unreturnedConnectionTimeout", C3P0Defaults.unreturnedConnectionTimeout())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>idleConnectionTestPeriod</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("idleConnectionTestPeriod", C3P0Defaults.idleConnectionTestPeriod())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>maxIdleTime</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("maxIdleTime", C3P0Defaults.maxIdleTime())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>propertyCycle</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("propertyCycle", C3P0Defaults.propertyCycle())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>acquireIncrement</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("acquireIncrement", C3P0Defaults.acquireIncrement())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>acquireRetryAttempts</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("acquireRetryAttempts", C3P0Defaults.acquireRetryAttempts())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>acquireRetryDelay</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("acquireRetryDelay", C3P0Defaults.acquireRetryDelay())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>automaticTestTable</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("automaticTestTable", C3P0Defaults.automaticTestTable())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>connectionTesterClassName</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("connectionTesterClassName", C3P0Defaults.connectionTesterClassName())</default-value>
+ <constrained/>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>connectionCustomizerClassName</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("connectionCustomizerClassName", C3P0Defaults.connectionCustomizerClassName())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>debugUnreturnedConnectionStackTraces</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("debugUnreturnedConnectionStackTraces", C3P0Defaults.debugUnreturnedConnectionStackTraces())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>testConnectionOnCheckout</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("testConnectionOnCheckout", C3P0Defaults.testConnectionOnCheckout())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>testConnectionOnCheckin</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("testConnectionOnCheckin", C3P0Defaults.testConnectionOnCheckin())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>preferredTestQuery</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("preferredTestQuery", C3P0Defaults.preferredTestQuery())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>autoCommitOnClose</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("autoCommitOnClose", C3P0Defaults.autoCommitOnClose())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>forceIgnoreUnresolvedTransactions</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("forceIgnoreUnresolvedTransactions", C3P0Defaults.forceIgnoreUnresolvedTransactions())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>int</type>
+ <name>checkoutTimeout</name>
+ <default-value>C3P0Config.initializeIntPropertyVar("checkoutTimeout", C3P0Defaults.checkoutTimeout())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>breakAfterAcquireFailure</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("breakAfterAcquireFailure", C3P0Defaults.breakAfterAcquireFailure())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>boolean</type>
+ <name>usesTraditionalReflectiveProxies</name>
+ <default-value>C3P0Config.initializeBooleanPropertyVar("usesTraditionalReflectiveProxies", C3P0Defaults.usesTraditionalReflectiveProxies())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>overrideDefaultUser</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("overrideDefaultUser", C3P0Defaults.overrideDefaultUser())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>overrideDefaultPassword</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("overrideDefaultPassword", C3P0Defaults.overrideDefaultPassword())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>userOverridesAsString</name>
+ <default-value>C3P0Config.initializeUserOverridesAsString()</default-value>
+ <constrained/>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>identityToken</name>
+ <bound/> <!-- the C3P0PooledConnectionPoolManager will need to be reset when this changes -->
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ <property>
+ <type>String</type>
+ <name>factoryClassLocation</name>
+ <default-value>C3P0Config.initializeStringPropertyVar("factoryClassLocation", C3P0Defaults.factoryClassLocation())</default-value>
+ <getter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></getter>
+ <setter><modifiers><modifier>public</modifier><modifier>synchronized</modifier></modifiers></setter>
+ </property>
+ </properties>
+</beangen>
+
+
+
+
diff --git a/src/dist-static/CHANGELOG b/src/dist-static/CHANGELOG
new file mode 100644
index 0000000..475e17f
--- /dev/null
+++ b/src/dist-static/CHANGELOG
@@ -0,0 +1,1179 @@
+c3p0-0.9.1.2
+ -- Fixed a variety of latent, hidden bugs discovered by "findbugs". Many thanks to Carsten Heyl
+ for running this utility and reporting the issues.
+ -- Added lastAcquisitionFailureDefaultUser to ComboPooledDataSource's TO_STRING_IGNORE_PROPS,
+ as some moments' Exception doesn't belong in a DataSource's toString(), and as trying to
+ find this when stringifying close()ed DataSources provoked an infinte recursion (as an Exception
+ was triggered trying to get the last failure info, which tries to include the stringified
+ DataSource in its message, again provoking an Exception, ad infinitum.) Many thanks to Santiago
+ Aguiar for finding and diagnosing this problem.
+ -- Added better documentation of the com.mchange.v2.c3p0.cfg.xml configuration property.
+ [Thanks to Legolas Wood for calling attention to this shortcoming.]
+ -- Calling getHoldability() to find the default Connection holdability provokes an Error
+ on some DB2 drivers. Added a "careful" check which catches and works around this.
+ [Thanks to Lari Hotari for finding the problem, and providing a solution!]
+ -- Refactored NewPooledConnection to not call event multicaster methods while holding
+ the NewPooledConnection's lock. Holding locks while firing events is an old-fashioned
+ way of provoking deadlocks, and it turns out c3p0 was not immune. Whoops! Another
+ embarrassing one. Many thanks to Rhythm Tyagi and Pappu Sharadavani for finding this
+ deadlock!
+c3p0-0.9.1.1
+ -- Marked variable "logger" in Log4jMLogger. Rare null pointer exceptions apparently
+ result from a stale value of the previously set variable. Did the same for
+ Log4jMLogger, although no analoguous issue has thus far been reported. [Thanks to
+ Noa Resare and Denis on sourceforge for calling attention to this issue.]
+ -- Hid the attribute "properties" from access via JMX, since it often contains password
+ information. I don't think properties is particularly useful via JMX, and this seems
+ like a better solution than returning properties with a hidden/modified password
+ property, since users might break things unintuitively be resetting a property to
+ a value that, according to the mbean, was the original value. [Thanks to John Sumsion
+ for calling attention to this issue.]
+ -- Fixed bug whereby setting any positive unreturnedConnectionTimeout forced c3p0's default
+ effectivePropertyCycle down to 1 second. [Many thanks to Luke Dang for tracking down
+ this bug.]
+ -- Fixed an embarrassing bug whereby c3p0 was reflective testing for methods (setHoldability
+ and setReadOnly) whose argument types are primitive int and boolean, by looking for
+ Integer.class instead of int.class and Boolean.class instead of boolean.class. [Many
+ thanks to John Kristian for tracking down this subtle, and embarrassing issue very
+ precisely and suggesting the fix.]
+c3p0-0.9.1
+ -- Modified logging of uncleared SQLWarnings, so that warnings are emitted at INFO rather
+ than WARNING, and via a dedicated logger [com.mchange.v2.c3p0.SQLWarnings], so that users
+ can easily turn this behavior off. [Thanks to Oleksandr Alesinskyy for calling attention
+ to the annoyingness of unconditionally logging all of this at WARNING.]
+c3p0-0.9.1-pre12a
+ -- Fixed a NullPointerException in DoubleWeakHashMap that showed up thanks to the newly
+ implemented canonicalization of Jdk14MLog instances. [Many thanks to Carlos Cajina for
+ finding and reporting this bug.]
+c3p0-0.9.1-pre12
+ -- Modified Jdk14MLog to canonicalize named MLogger instances. In doing so, put the getLogger()
+ method behind a synchronization barrier, which hopefully will resolve an odd visibility issue
+ bug reported by a user (wherein the underlying jdk Logger appeared occasionally to be null
+ in threads that might have initialized prior to the logger) [Thanks to Denis on Sourcefore
+ for reporting this subtle and interesting issue.]
+ -- Added a lastAcquisitionFailure property to PooledDataSource (and the JMX view thereof).
+ Thanks to Troy Whittier for the suggestion, and the patch!
+ -- SQL Warnings present at Connection acquisition, or uncleared upon Connection checkin
+ are now logged (at WARNING level) and cleared prior to being made available as part
+ of the pool. This is necessary to sustain the invariant that all pooled Connections
+ are identical and interchangible; warnings are Connection state that must be managed
+ and homogenized. [Thanks to Naxe on the hibernate forums for calling attention to this
+ issue!]
+ -- Resolved a rare NullPointerException which occurs when a Statement cache is closed
+ beneath an active, checked out Connection. A client tries to prepare a Statement,
+ but the closed Statement cache throws an NPE. Resolved this by having the closed
+ Statement cache throw a ResourceClosedException, and having c3p0's proxy Statement
+ recover by generating an uncached PreparedStatement. [Thanks to Sven / ford_perfect
+ on sourceforge for finding and calling attention to this issue.]
+ -- Added c3p0 version to C3P0RegistryManagerMBean [Thanks to Sven / ford_perfect
+ on sourceforge for the suggestion.]
+c3p0-0.9.1-pre11
+ -- Documentation updates, and style-sheet fixes for FireFox.
+
+ -- Despite the ugliness, added logic to prepend a VMID to identity
+ tokens generated for c3p0 DataSources. See the docs (Appendix A,
+ other configuration properties) for more information. Though there's
+ little harm in the longer, uglier identityTokens, users who dislike
+ them can turn them off using the property com.mchange.v2.c3p0.VMID.
+
+ -- Wrote ScatteringAcquireTask, which represents a significant change to
+ how c3p0's underlying resource pool deals with Connections acquisition
+ failures. c3p0 is designed to retry Connection acquisition up to
+ "c3p0.acquireRetryAttempts" times (30 by default) with a delay of
+ "c3p0.acquireRetryDelay" milliseconds (1000 by default) between tries.
+ Should no Connection be successfully acquired after acquireRetryAttempts,
+ c3p0 gives up, logs an error, and frees clients waiting to check out
+ a Connection with an Exception (declaring the pool permanently broken
+ iff c3p0.breakOnAcquireFailure is set to true).
+
+ Connection acquisition is performed asynchronously by c3p0's thread
+ pool. Under the old implementation, when database Connections could
+ not be acquired, a single task dispatched to the thread pool would
+ hog a thread for the full length of the cycle -- 30 seconds by
+ default -- spending most of that time sleeping between retries.
+ Under the new implementation, each acquisition attempt is its own
+ separate task, and if an acquire attempt fails, a timer is used to
+ schedule a retry adter acquireRetryDelay without hogging any thread.
+
+ I think the new implementation is unambiguously superior to the old,
+ as it permits clients to use smaller thread pools (c3p0.numHelperThreads)
+ and diminishes the likelihood of apparent thread pool deadlocks.
+ However, it is too significant a change to introduce this late
+ in the "mature" c3p0-0.9.1 series, so the old implementation is enabled
+ by default until the c3p0-0.9.2 development cycle begins.
+
+ TO USE THE NEW IMPLEMENTATION WITH c3p0-0.9.1, set...
+
+ com.mchange.v2.resourcepool.experimental.useScatteredAcquireTask=true
+
+ in a c3p0.properties file or as a system property. Users are strongly
+ encouraged to set this property. You'll see better resource utilization,
+ and I'll get to hear issue reports if I've screwed anything up in
+ the implementation.
+ -- Modified BasicResourcePool so that checkin (and removal) of resources
+ is always synchronous if the pool is marked broken. [Otherwise the
+ check-ins to a closed or broken pool may fail, as they try to use an
+ already close()ed asynchronous task runner.]
+ -- Modified BasicResourcePool.AcquireTask so that an interrupt always
+ forces the whole task to end, not just a single acquisition
+ attempt.
+ -- Exposed numThreadsAwaitingCheckout in PooledDataSource interface
+ and its associated MBean. [Thanks to John Sumsion for suggesting
+ this property.]
+ -- Prevented password from being exposed via
+ DynamicPooledDataSourceManagerMBean. [Thanks to John Sumsion
+ for calling attention to this oversight!]
+ -- Fixed an error whereby DataSources.pooledDataSource( ... ) methods
+ failed to properly set configuration properties defined on
+ PoolBackedDataSource [e.g. numHelperThreads]. Many thanks to
+ John Sumsion for noticing the problem and sending the fix!
+ -- Fixed AbstractConnectionCustomizer to implement (with no-op) methods
+ the current definition of the ConnectionCustomizer interface. (Failed
+ to update after modifying an early version of this interface.)
+ -- Modified NewPooledConnection to only collect stack trace information
+ on close() when we are debugging at FINEST or DEBUG (log4j) level.
+ Previously, stack trace information was collected anytime a
+ c3p0 PooledConnection was close()ed. This was unnecessary, although
+ the performance cost was probably small to negligible, since
+ PooledConnections are typically reused many times before they are
+ close()ed. Stack trace information is still captured when
+ PooledConnections are destroyed due to an error, rather than an
+ ordinary call to close().
+ -- Hid the getLastXXXFailure(user, password) from the operations list of
+ DynamicPooledDataSourceManagerMBean. The stack traces of failures
+ are visible via the sampleLastXXXFailureStackTrace(); there's no
+ need to expose the Throwbales as well via JMX.
+ -- Added convenience methods getNumPooledDataSources() and getNumPoolsAllDataSources()
+ to C3P0Registry and the related MBean. [Thanks to lukeda on hibernate
+ forums for calling attention to the need for a pool count, since
+ DataSources can wrap multiple pools associated with different
+ authentications.]
+ -- Eliminated the "trial Connection acquisition" added to c3p0-0.9.1-pre10
+ when PooledDataSources construct the pool with their default authentification
+ information. Effectively we trust that the default auntentification is
+ good, while we distrust client-specified (or JMX-queried) username/password
+ combinations, and perform the extra check. This is a compromise, as users
+ who frequently create and destroy DataSources don't like the extra overhead
+ of the trial Connection acquisition, but we really want to avoid the initialization
+ of bad pools when users misspecify authentification information. We've also prevented
+ mere queries of pool status from triggering the initialization of a
+ non-default-authentification pool. Only calls to getConnection(user, password)
+ will provoke the initialization of a non-default-auth pool. Querying
+ a non-default pool -- e.g. getNumBusyConnections( "steve", "test" ) -- will
+ throw an Exception if no Conections have ever been requested for ("steve", "test")
+ rather than initializing a new pool. [Thanks to Jiji Sasidharan for calling
+ attention to this issue.]
+ -- Finally rationalized C3P0Registry's Object retention policy. C3P0Registry
+ retains references to all C3P0 DataSources, to provide a central
+ point of client access for users, and to ensure that multiple JNDI
+ lookups within a single VM result in the same DataSource instance.
+ This is both intuitive, and avoids multiplying Threads and pools.
+ In previous versions of c3p0, hard references were retained indefinitely.
+ Closed DataSources hold no non-memory resources, and are small Objects,
+ but in unusual scenarios wherein very many c3p0 DataSources are created
+ and destroyed within the lifetime of a single VM, this amounts to a
+ memory leak. C3P0Registry now retains hard references to DataSources
+ so long as they have not been close()ed (or DataSources.destroy()ed).
+ When they have been close()ed, only weak references to c3p0 DataSources
+ persist, and they are eligible for garbage collection. [Thanks to
+ Jeremy Grodberg for calling attention to this as a real-world issue.]
+ -- Removed some of the new pool stat params which distractingly appear in
+ ComboPooledDataSource's toString() method (and c3p0 pools' config dump).
+c3p0-0.9.1-pre10
+ -- Exposed statement pool stats via PooledDataSource interface and MBean.
+ Added accessors to the statement cache, pool, pool manager, and
+ AbstractPoolBackedDataSource.
+ -- Fixed a bug whereby multiple near-simultaneous Connection requests failed
+ to provoke sufficient pool growth/Connection acquisition. This is a bug
+ reintroduced in c3p0-0.9.1-pre4, and is a duplicate of an early issue,
+ resolved under the old pool size management scheme. c3p0 must (and does)
+ keep track not only of its current size and pending growth, but also the
+ number of potentially unserved recent Connection requests. c3p0-0.9.1-pre4
+ thru c3p0-0.9.1-pre9 failed to make use of the incoming request count when
+ deciding whether and how much to grow the pool. Many thanks to John Kristian
+ for calling attention to this subtle issue.
+ -- Exposed information about Connection test failures (failure count of
+ each type of test, stack trace of last failure and last failure of each
+ type) via the PooledDataSource interface and its assiciated MBean.
+ -- Fixed a rare situation where a Connection test on check-out could
+ be performed by a Thread holding the pool's lock, if an initial
+ attempt to checkout a Connection failed and a second checkout attempt
+ is initiated internally. The recursive checkout is now performed
+ without the pool's lock.
+ -- Established a consisent policy re logging of Connection test failures.
+ They are now logged at FINE level (DEBUG under log4j). Previously tests
+ on checkin and checkout were logged at FINE, but idle tests were logged
+ at INFO. Is it worth letting this be configurable, as some users may prefer
+ to see test failures under normal operation?
+ -- Modified C3P0PooledConnectionPoolManager to perform a trial Connection
+ acquisition before trying to establish a pool, as pools established
+ on bad database or authentification information churn indefinitely trying
+ to acquire minPoolSize Connections, clogging up the thread pool and
+ sometimes leading to "APPARENT DEADLOCKS" if users touch a pool with
+ bad authentification information.
+ -- Defined UnifiedConnectionTester (and AbstractConnectionTester) so that
+ ConnectionTesters can provide any Exception associated with a failure,
+ which will show up as the "root cause" of the Exception eventually
+ thrown to signal the failure.
+ -- Added logging at WARNING level when a Connection tester reports the
+ database is invalid, triggering a reset of the Connection pool.
+c3p0-0.9.1-pre9
+ -- Updated the documentation. It is now fully current.
+ -- Changed the name of the jboss configuration mbean from
+ com.mchange.v2.c3p0.mbean.C3P0PooledDataSource to com.mchange.v2.c3p0.jboss.C3P0PooledDataSource,
+ in order to distinguish the jboss configuration stuff from the normal management
+ beans, which really are quite different. The old com.mchange.v2.c3p0.mbean.C3P0PooledDataSource
+ still exists, so that existing users don't see anything break, but only the new version will
+ be updated to support new parameters.
+ -- Added a means by which users can suppress JMX MBean registration if they
+ wish. Setting the property com.mchange.v2.c3p0.management.ManagementCoordinator
+ to com.mchange.v2.c3p0.management.NullManagementCoordinator will
+ prevent mbean registration. (Advanced users can also use this to install
+ their own implementations of the ManagementCoordinator interface, though
+ I doubt many people will find this useful.)
+ -- Fixed infinitely loop in ComboPooledDataSource.setFactoryClassLocation( ... ) [introduced with
+ the reorganization of ComboPooledDataSource to inherit from AbstractPoolBackedDataSource (?)]
+ -- Added some code to warn on common spelling errors of c3p0.properties
+ and c3p0-config.xml (Users have frequently mistaken the digit 0 for
+ the letter O in c3p0.)
+ -- Wrote a DynamicMBean for PooledDataSources. It seems to work,
+ covers both ComboPooledDataSource and "wrapping" PoolBackedDataSources,
+ and should cover new config parameters and accessors automatically.
+ -- More refinements to connection test logic. We test proxy Connections
+ when there's a statement cache and a test query (to save time, since
+ the test query is likely cached), we test physical connections if we'll
+ need to do the slow, default test (statement caching doesn't help), or
+ if there is no statement cache.
+ -- Removed the stub servlet I'd previously added. Although it jdk1.5+, the
+ JMX approach is a better one for monitoring and modifying c3p0 pools at
+ runtime, and I'm going to focus on that for now. (If there's a lot of
+ clamor for it, I'll add back and flesh out the servlet. It just doesn't
+ seem like a great use of my time for now.)
+ -- Modified build to create a separate jdk1.3 compatable jar by stripping
+ source of lines beginning with "assert". I want to start using assertions,
+ but will keep them to recognizable, strippable one-liners.
+ -- Modified DriverManagerDataSource to handle its driverClass parameter
+ in a less fragile way. Rather than attempting to load the driverClass on
+ property set, DriverManagerDataSource now lazily tries to load the class
+ upon its first call to getConnection() after the property is initialized
+ or updated.
+c3p0-0.9.1-pre8
+ -- Fixed bug whereby jdbcUrl and driverClass were not initialized properly
+ if set in config files rather than in code. [Thanks to Felipe Dominguez
+ for calling attention to this problem!]
+ -- Changed AbstractPoolBackedDataSource from a non-public class in
+ com.mchange.v2.c3p0 to a public class in com.mchange.v2.c3p0.impl to
+ workaround fragilities associated with sun bug id #6342411, wherein
+ reflective invocation of public methods on a non-public class fail,
+ even when involed via a public base-class.
+ -- Fixed NullPointerException (introduced on -pre7) on initializing with
+ a named config.
+ -- Continuing along the same lines, modified C3P0Registry and
+ C3P0ManagementCoordinator to unregister (both from C3P0Registry and
+ associated MBeans on DataSource close. When all PooledDataSources
+ are unregistered, the C3P0RegistryManager MBean unregisters as well.
+ -- Modified ActiveManagementCoordinator to check for, and unregister, the
+ C3P0Registry mbean prior to registration, to prevent conflicts on
+ undeploy/redepoloy cycle. [Thanks to Ben Groeneveld for calling attention
+ to this problem, and suggesting the fix.]
+ -- Modified C3P0PooledConnectionPool to perform Connection tests on the raw
+ physical Connection rather than the proxy Connection, when possible (i.e.
+ when we're using a C3P0 PooledConnection implementation that we know how to
+ get underneath of). This is better because 1) it's slightly faster; 2) it's
+ significantly faster on failure, as C3P0 PooledConnections test Connections
+ after a failure with statusOnException(), leading to an unnecessary
+ double-check; and 3) authors of custom ConnectionTesters can use vendor-specific
+ API to test Connections if they wish.
+c3p0-0.9.1-pre7
+ -- Defined the ConnectionCustomizer interface as a hook through which clients
+ can "set up" newly acquired or checked out connections (and clean up prior
+ to check-in or destruction). Added config param connectionCustomizerClassName.
+ -- Formerly unused config parameter propertyCycle now controls how frequently
+ c3p0 checks timestamps against expiration parameters (maxIdleTime,
+ maxIdleTimeExcessConnections, maxConnectionAge, unreturnedConnectionTimeout)
+ -- Added config parameters unreturnedConnectionTimeout and
+ debugUnreturnedConnectionStackTraces, reluctantly but by popular demand.
+ If unreturnedConnectionTimeout is set, Connections will be automatically
+ destroyed and removed from the pool if not returned in the given number
+ of seconds. If debugUnreturnedConnectionStackTraces is also set, stack
+ traces will be captured on check-out of any Connection, and the stack traces
+ that were unreturned and cleaned up by the pool will be logged. Any clean-up
+ of an unreturned Connection is logged at INFO level, in hopes of annoying
+ people into cleaning up their code and close()-ing their Connections reliably.
+ -- Added config parameter maxIdleTimeExcessConnections. Connections in excess of
+ the pool's minimum size will be expired out if they remain idle for longer
+ than maxIdleTimeExcessConnections. [Thanks to Navin M. and Ben Groeneveld for
+ the suggestion.]
+ -- Added configuration parameter maxConnectionAge to limit the absolute age
+ of Connections (age in seconds from acquisition, rather than in seconds
+ form last check-in).
+ -- Put more status accessors in ThreadPoolAsynchronousRunner and exposed
+ thread pool state via JMX (PooledDataSourceManagerMBean).
+ -- Preliminary support for management via mbeans/jmx under jdk1.5+ is
+ implemented. (These mbeans are distinct from the mbean designed for
+ configuring c3p0 under jboss, and are in the package
+ com.mchange.c3p0.management.) Currently, pool statistics are instrumented,
+ as well as pool reset operations, but viewing and modifying the pool
+ configuration parameters is not supported. (I'll write a dynamic mbean to
+ capture this stuff; there are too many config properties I'm already
+ updating by hand in too many places. Later.) [Thanks to Ben Groeneveld for
+ the suggestion, including some nice sample code!]
+ -- Added configuration parameter maxAdministrativeTaskTime, which forces
+ interrupt() to be called on pool administrative tasks (Connection acquisition,
+ destruction and testing; prepared statement destruction) if these tasks seem
+ to hang. This is one strategy for dealing with database that hang and lead to
+ deadlock messages and eventually large memory footprints. It's useful only if
+ the hanging operations respond to interrupt()s, not thread-monitor related
+ deadlocks (which hopefully don't ever happen). c3p0 should recover from any
+ Exceptions provoked by the interrupt() calls. (Hopefully improvements to
+ GooGooStatementCache have rendered this parameter largely unnecessary by
+ resolving the most common source of deadlocks.)
+ -- Major revision to GooGooStatementCache that may eliminate most APPARENT
+ DEADLOCK messages. c3p0-0.9.0 introduced a complicated implementation
+ of closeAll(), in order to ensure that Statement and Connection close did
+ not happen simultaneously. Upon review, there was a significant bug in the
+ implementation, and even when corrected, the strategy is itself prone to
+ deadlocks should the ThreadPool become saturated with destroy tasks, which
+ block awaiting statement remove tasks. Rewrote closeAll() as a simple
+ synchronous method, rather than simulating synchrony with wait()/notifyAll(),
+ but with care not to hold the StatementCache's lock during Statement.close()
+ calls. There is a slight loss of asynchrony, as Statement.close() methods
+ are executed sequentially rather than simultaneously, but as a practical
+ matter, since the sequential destruction occurs asynchronously in the
+ thread pool and is invisible to clients, both client-observed and over-all
+ performance should not suffer. (It may even be improved, as the 0.9.0.x
+ strategy was complicated and had some overhead.) Note: This revision should
+ also eliminate occasional ConcurrentModificationExceptions associated
+ with the Statement cache.
+ -- Changed asynchronous tasks implemented as anonymous inner classes to named
+ inner classes, so that users reviewing active and pending tasks (usually
+ in trying to make sense of an APPARENT DEADLOCK) have a better idea of
+ what's going on.
+ -- Major revision to GooGooStatementCache that may eliminate most APPARENT
+ DEADLOCK messages. c3p0-0.9.0 introduced a complicated implementation
+ of closeAll(), in order to ensure that Statement and Connection close did
+ not happen simultaneously. Upon review, there was a significant bug in the
+ implementation, and even when corrected, the strategy is itself prone to
+ deadlocks should the ThreadPool become saturated with destroy tasks, which
+ block awaiting statement remove tasks. Rewrote closeAll() as a simple
+ synchronous method, rather than simulating synchrony with wait()/notifyAll(),
+ but with care not to hold the StatementCache's lock during Statement.close()
+ calls. There is a slight loss of asynchrony, as Statement.close() methods
+ are executed sequentially rather than simultaneously, but as a practical
+ matter, since the sequential destruction occurs asynchronously in the
+ thread pool and is invisible to clients, both client-observed and over-all
+ performance should not suffer. (It may even be improved, as the 0.9.0.x
+ strategy was complicated and had some overhead.)
+ -- Users still occasionally report seeing "APPARENT DEADLOCK" messages
+ from ThreadPoolAsynchronousRunner. Previously, most reports had to
+ do with cached PreparedStatements that hang when closed. Previous
+ fixes (in 0.9.0) seemed to resolve these problems. Most new reports
+ have to do with Connection acquisition or Connection close() tasks
+ hanging indefinitely, with statement cacheing not necessarily involved.
+ It's unclear whether these reports stem from a c3p0 bug, or rare
+ situations in which various JDBC driver hangs hang c3p0. (If the
+ condition is temporary, c3p0 recovers after the "APPARENT DEADLOCK"
+ warning. In any case, ThreadPoolAsynchronousRunner has been modified
+ to provide much better debug information, particularly in jdk1.5
+ environments, where hung Thread stack traces are dumped.
+ -- Modified the build file and refactored C3P0Config / C3P0ConfigUtils / C3P0ConfigXmlUtils
+ to make sure that c3p0 still runs under jdk1.3, even though it only builds
+ under jdk 1.5. JMX support is 1.5 only, and XML config depends on the
+ availability of standard XML extensions under jdk 1.3. (This is going to get harder,
+ as I hope to start using assertions. For the moment, assertions are
+ commented out. I think I'll have ant filter away the assertions for 1.3
+ builds and leave them in otherwise, based on a build property.
+ -- Significantly reorganized (hopefully simplified) of C3P0Registry and
+ ComboPooledDataSource. Much of the complication of all this is supporting
+ the most annoying, least used, but nevertheless very important feature
+ of supporting JNDI lookups across JVM boundaries via Serialization or JNDI
+ References.
+ -- Reorganized ComboPooledDataSource to inherit from PoolBackedDataSource.
+ In doing so, changed setConnectionPoolDataSource() of PoolBackedDataSource
+ to throw a PropertyVetoException. (It is a "constrained property" now.)
+ The very, very few users who call this method directly may have to update
+ their code to handle the potential Exception. This change does not affect
+ compatability for clients that use ComboPooledDataSource or who create
+ PoolBackedDataSources via the c3p0's factories (e.g. DataSources).
+ -- Fixed bug whereby config parameter breakAfterAcquireFailure did not take
+ effect at the ResourcePool level.
+ -- Fixed bug whereby initialPoolSize parameter failed to take effect at the
+ ResourcePool level.
+ -- Fixed a bug whereby Connection tests on checkout held the pool's lock,
+ preventing other activity during the Connection test. [Thanks to John
+ Sumsion for calling attention to this subtle problem.]
+ -- ThreadPoolAsynchronousRunner has seen some minor efficiency improvements
+ (a task that calls interrupt() long-running tasks on post-deadlock blocked,
+ replaced threads no longer runs when there are no post deadlock blocked,
+ replaced threads).
+ -- Previous versions used a Stringified identityHashCode() as a unique ID
+ for various C3P0 objects. This was not correct (and bit at least one
+ user in practice), as identityHashCode()s are not guaranteed unique,
+ and in practice are not on 64-bit VMs. c3p0 now checks identityHashCodes
+ to make sure they are not reused, and if seen before appends a count to
+ keep unique IDs unique. [Many thanks to Prishan Makandurage for calling
+ attention to this issue.]
+ -- Fixed fact that statusOnException() in NewPooledConnection() ignored
+ preferredTestQuery parameter, always using the very slow default
+ connection test rather than the user-defined test. [Thanks to Andrea
+ Luciano for calling attention to this issue.]
+ -- C3P0PooledConnection (part of the "traditionalReflectiveProxies"
+ codebase that's largely been superceded, but that is still nominally
+ supported) held a static reference to a ClassLoader, in some cases
+ preventing the ClassLoader from being garbage collected (for example
+ when webapp contexts, with context specific Classloaders are
+ undeployed by some app servers). This was entirely unnecessary, as
+ the cached reference was used exactly once in the lifecycle of the
+ class. The reference to the ClassLoader is no longer stored. [Many
+ thanks to Michael Fortin for very specifically tracking down this
+ subtle problem!]
+ -- Added logic to log individual acquisition failures at INFO level
+ if acquireRetryAttempts <= 0. if num_acq_attempts <= 0, we try to
+ acquire forever, so the end-of-batch log message below will never
+ be triggered if there is a persistent problem so in this case,
+ it's better flag a higher-than-debug-level message for
+ each failed attempt. [Thanks to Eric Crahen for calling attention
+ to this issue.]
+c3p0-0.9.1-pre6
+ -- Wrote a stub of a status-monitoring servlet. Not yet documented, or
+ even usefully functional, but hopefully will be soon.
+ -- Updated documentation to include new config file format, including
+ named and per-user config. Docs could still use some work, but the
+ functionality is now described.
+ -- For consistency, the parameters user and password are now configurable
+ via config files and System properties.
+ -- Modified DefaultC3P0ConfigFinder to properly give greater precedence to
+ System properties than to XML-defined unspecified user, default config
+ values.
+ -- Added WARNING logging of create SQL and original database Exception when
+ creation of an automaticTestTable fails. [Thanks to Alexander Grivnin.]
+ -- Added logic so that the Exception assoicated with the last failed acquisition
+ attempt in a round of attempts is logged along with the failure if the
+ acquisition attempts. [Thanks to Barthel Steckemetz and Patrick Eger for
+ calling attention to this issue.]
+ -- Fixed a problem whereby modifying the config of a ComboPooledDataSource
+ programmatically, while in use, causes previously checked-out Connections
+ to be close()ed underneath the user. Changing configs midstream still
+ causes a complete resetting of the pool (because the pool holds most
+ config params as immutable to avoid having to synchronized for every
+ config param read), but old Connections from superceded pools will remain
+ valid until they are rechecked into the pool. [Thanks to hhu for noticing
+ this problem!]
+ -- Fixed a resource leak whereby Connections which could not be reset after
+ client-initiated close() were properly "excluded" from future inclusion
+ in the pool, but never checked back into the pool to be destroyed. (Excluded
+ resources are simply marked for destruction rather than reassimilation on
+ checkin.) [Many thanks to Levi Purvis for tracking this down!]
+c3p0-0.9.1-pre5a
+ -- "test-classpath" was erroneously specified in build.xml to include some
+ extra jar files, which rendered undetectable (to my tests) the fact
+ that com.mchange.v1.lang.BooleanUtils was not jarred up into
+ c3p0-0.9.1-pre5.jar as it needed to be. Very, very embarrrassing. [Thanks
+ to Goksel Uyulmaz for calling attention to this gaffe.]
+c3p0-0.9.1-pre5
+ -- reorganized synchronization of getConnection() in DriverManagerDataSource,
+ so that a lock-up in one Connection acquisition attempt does not prevent
+ other threads from accessing the DataSource. [Thanks to Fabian Gonzalez
+ for calling attention to this occasional deadlock.]
+c3p0-0.9.1-pre4
+ -- added overrideDefaultUser and overrideDefaultPassword parameters, and
+ a pooledDataSource( ) factory method which specifies override
+ authentification info, regardless of the user and password properties
+ set on the DataSource.
+ -- Reimplemented resource acquisition in BasicResourcePool so that each
+ Connection acquisition is a separate asynchronous task, to avoid tying
+ up the pool with very long, multiple acquisition tasks. Ended up being
+ a total rewrite of pool size management. Pools should be much more
+ conservative in acquiring new Connections. They won't grow to maxPoolSize
+ as easily as before. This is a big change. We'll see how it holds up as
+ people beat the crap out of it.
+c3p0-0.9.1-pre3
+ -- Added some extra logging of resource destruction, to help debug cases
+ where the resources are removed from the pool, but appear not to have
+ been properly cleaned up.
+ -- Finished implementation of new C3P0Config, added constructor methods to
+ various DataSource implementations that accept a configName parameter for
+ named configurations, and modified the DataSources class to use the new
+ configuration approach instead of PoolConfig wherever possible.
+c3p0-0.9.1-pre2
+ -- Continued implementation of new C3P0Config, including XML parsing
+ and setting of default values. Still have to test XML config, and
+ implement named and per-user configs.
+ -- Changed policy of BasicResourcePool to keep accidentally overacquired
+ resources so long as the pool is kept below maxPoolSize, rather than
+ immediately discarding Connections beyond the number we intended to
+ acquire.
+ -- Fixed two issues whereby checking in resources to closed pools (both
+ Connection and Statement pools) provoked Exceptions. Check-ins should
+ succeed, even to close()ed pools, with resources silently destroyed if
+ the pools ae closed. [Thanks to Chris Kessel for finding both of these
+ issues.]
+ -- Modified to a one-statement-cache-per-pool model, rather than a
+ global-statement-cache-for-all-pools, consistent with the general
+ philosophy that config parameters are on a per-auth-pool basis.
+ -- Fixed a deadlock which could occur if a pool is closed at the same time as
+ one of its Connections is closed. One thread gets the pool's lock, then tries
+ to get a PooledConnections lock to close it. An ordinary client closes a
+ Connection, which acquires the PooledConnection's lock, and then tries to
+ check back in to the pool, requiring the pool's lock. Broke the deadlock by
+ making the destruction of resources on pool close asynchronous, by a Thread
+ without the resource pool's lock.. [Many thanks to Roger Westerlund for
+ sending a full thread-stack dump capturing this deadlock!]
+ -- Completed transformation to one C3P0PooledConnectionPoolManager per
+ PoolBackedDataSource model.
+c3p0-0.9.1-pre1
+ [This is a half-assed internal release... it compiles and works, but mostly
+ it's just a checkpoint as c3p0 undergoes significant transformations.]
+ -- Deprecated PoolConfig, and began implementation of new C3P0Config approach
+ to configuration.
+ -- Partial transformation of C3P0PooledConnectionPoolManager from shared-manager
+ for several DataSources model to simple one-pool-per-DataSource model enabled
+ by canonicalization of DataSources.
+c3p0-0.9.0.4
+ -- In a subtle but major modification of BasicResourcePool, changed checkin/checkout
+ behavior from FIFO to LIFO to increase the likelihood that unnecessary Connections
+ pooled at peak times idle long enough to expire. [Many thanks to Vlad Ilyushchenko
+ and hontvari on sourceforge for noticing this important issue and suggesting the
+ fix.]
+c3p0-0.9.0.3
+ -- Added code to mbean C3P0PooledDataSource to automatically create JNDI
+ subcontexts if a JNDI name is specified for which the full path has not
+ been created (analogous to mkdir -p in UNIX). [Thanks to David D. Kilzer
+ for noticing the issue, and for submitting the fix!]
+ -- Added logic to GooGooStatementCache to ensure that multiple threads do not
+ try to close the same PreparedStatement, in order to try to resolve longstanding
+ PreparedStatement.close() freeze issues. [Thanks to cardgames on sourceforge for
+ noticing that the freezes seem to occur when several threads are trying to close()
+ just one statement!]
+ -- Added some checks in DriverManagerDataSource (as well as WrapperConnectionPoolDataSource)
+ so that a mismatch between JDBC URL and driver does not lead to null Connection
+ references and downstrem NullPointerExceptions. [Thanks to Richard Maher for
+ calling attention to this issue.]
+ -- Undid some unnecessary synchronization in WrapperConnectionPoolDataSource,
+ which would cause deadlocks when the acquisition of a Connection from the
+ underlying driver froze for some Thread. (Other threads would then block
+ trying to access the WrapperConnectionPoolDataSource.) [Thanks to Fabian
+ Gonzales for calling attention to this issue, and for providing a VM thread
+ stack dump to help chase it down!]
+ -- modified BasicMultiPropertiesConfig to never attempt to read the resource "/",
+ representing (in this case) System properties, as a stream, which under some
+ classloaders caused ClassCastExceptions. [Thanks to Joost Schouten for calling
+ attention to this issue.]
+ -- fixed initial check of the readOnly and typeMap properties of Connections,
+ so that users don't see disconcerting Exceptions at debug log levels if their drivers don't
+ support these parameters.
+ -- Added jar attributes Extension-Name, Specification-Vendor, Specification-Version,
+ Implementation-Vendor-Id, Implementation-Vendor, and Implementation-Version
+ [per request of Pascal Grange].
+ -- Added some extra debugging information for peculiar situation where an IdentityTokenized
+ is constructed and registered with C3P0Registry, but coalesces to a different Object.
+ This should never happen, as freshly constructed IdentityTokenizeds should have String
+ versions of their identity hashcodes as tokens, and upon their registration, no copies
+ should yet have been created and registered. [Thanks to Jose Rodriguez for noting this
+ issue.]
+c3p0-0.9.0.2
+ -- added debug-level logging of connection tests and results
+ -- tightened up means by which the readOnly and typeMap properties of Connections are reset,
+ so that users don't see disconcerting Exceptions at debug log levels if their drivers don't
+ support these parameters.
+ -- fixed problem in BasicResourcePool that left the immediately-close-checked-out-connections option
+ in the close() method ineffective, meaning Connections checked-out from a pool would
+ leak unless clients remembered to check them into pool for destruction.
+ -- fixed bug wherein connection-testing on checking occured in a thread holding the pool's
+ lock, leading to pool hangs or deadlocks if a connection hangs. [Thanks to Tobias Jenkner
+ for calling attention to this problem!]
+c3p0-0.9.0.1
+ -- modify BasicPropertiesConfig to not throw class cast Exceptions in the rare case that
+ a Properties object (presumably System properties rather than a file derived props Object)
+ contains (via the Map/Hastable API) non-String keys or values. [Thanks to Prima Upot for
+ calling attention to this issue.]
+ -- fixed a build issue where some classes required for code generation were not necessarily
+ compiled prior to the code generation step, causing builds to fail. [Thanks to James
+ Neville for calling attention to this issue.]
+c3p0-0.9.0
+ -- under some circumstances, the current list of ThreadPoolAsynchronousRunner's deadlock detector
+ seems to hold onto a lot of memory... presumably this is when long pending task lists build up
+ for some reason... nevertheless, we now dereference the "current" list in between deadlocker detector
+ invocations. The "last" list has to be retained between invocations, but the "current" list need
+ not be. [Thanks to Venkatesh Seetharamaiah for calling attention to this
+ issue, and for documenting the apparent source of object retention.]
+ -- modified log4j and jdk14logging MLog implementations to test for library classes upon
+ classload (MLog class only tests for the presence of the mlog implementations, which
+ doesn't notice if their dependant classes aren't there -- dependent classes must test
+ for the presence of the libraries.)
+ -- Modified GooGooStatementCache to warn as INFO (and with a descriptive message in debug)
+ of multple PreparedStatement preparation. [Thanks to mxnmatch on hibernate's forum for
+ calling attention to this issue.]
+ -- Modified BasicResourcePool to not warn on expected InterruptExceptions that occur if
+ clients are waiting for a Connection when the pool is closed. [Thanks to Blunted on
+ hibernate's forum for calling attention to this issue.]
+c3p0-0.9.0-pre6
+ -- Modified thread pool's strategy for dealing with deadlocks to prevent OutOfMemoryErrors
+ from sudden spawning of multiple threads to clear backlogged tasks. [Thanks to Greg Whalin
+ for calling attention to this problem.]
+ -- Fixed bugs in debug reporting of nested Exceptions/Warnings when converting or
+ rethrowing SQLExceptions [thanks to an anonymous sourceforge poster for finding this]
+ -- modified BasicResourcePool to shorten the period during which the pool's lock
+ is held during resource acquisition
+ -- maxIdleTime is generally enforced every (maxIdleTime / 8) seconds. Added logic to ensure that
+ even for very long maxIdleTimes, the longest maxIdleTime will ever go unenforced is 15 minutes.
+ -- Very tentative initial support in build for JUnit tests
+ -- Fixed documentation typos and added sample config for Tomcat 5.5. [Thanks to David Newcomb
+ for finding the typos and to Carl F. Hall for the sample config!]
+ -- ProxyConnections were not properly resetting readOnly, holdability, catalog, and typeMap
+ properties on Connection close. Fixed. [Thanks to Andy (bjorkmann?) for calling attention
+ to this bug.]
+ -- Added a complicated workaround to issues that may occur when a Connection
+ and its child statements simultaneously try to close. GooGooStatementCache's closeAll(Connection c)
+ method now wait()s for all Statements to close prior to returning, avoiding such issues (Oracle deadlock,
+ mysql NullPointerException), though dramatically complicating the code. Grrr. Theoretically, drivers should
+ be robust to the simultaneous closing of Statements and Connections, but they're not, so
+ my code gets to be more complicated. [Thanks to Juan Perez, Daniel Edberg, and Damien Evans for calling
+ attention to this issue.]
+c3p0-0.9.0-pre5
+ -- Proxy classes were overaggressively invalidating themselves in response to a signalled
+ Connection error. This problem was very similar to the problem fixed in 0.8.5-pre9, except
+ for rather than the PooledConnection too quickly closing its inner Connection, the proxies
+ too quickly detached themselves from the pooled Connection, considering themselves broken,
+ after a Connection error event. Users expect to be able to work with their Connections even
+ after c3p0 has in its conservatism decided they no are no longer worthy of a place in the
+ pool. [Thanks to "Geoff Fortytwo" for finding and helping to track down this issue.]
+ -- Fixed issue that prevented user-defined ConnectionTesters from being used.
+c3p0-0.9.0-pre4
+ -- Fixed NullPointerException on double-close of proxy Statements. [Thanks to Stephen Waud
+ for finding this bug and tracking it down.]
+ -- Modified C3P0PooledConnectionPool to generate a more informative Exception (one that
+ includes the stack trace of the initial problem) when a Connection test fails. [Thanks
+ to novotny from the hibernate forums for calling attention to this issue.]
+` -- PooledDataSources now set SQLState 08001 on SQLExceptions resulting from a determination
+ by the pool that the database is down or unavailable. [Thanks to ml2709 on the hibernate
+ forums for calling attention to this issue.]
+ -- Modified ThreadPoolAsynchronousRunner to enforce its max task time even on one-off
+ "emergency threads" used to flush the pool of tasks after an apparent deadlock.
+ Hopefully this will help to eliminate the OutOfMemoryErrors that users occasionally
+ see if some Database operation starts to fail by blocking indefinitely, and is attempted
+ many many times. As long as these operations are susceptable to interrupt(), this should
+ resolve the problem. (If tasks block in a way that is immune to interrupt(), there is
+ little c3p0 can do to recover the Threads and prevent the number of blocked Threads
+ from growing until memory runs out.) [Thanks to Jean-Christophe Rousseau and Arup Vidyerthy
+ for calling attention to this issue.]
+ -- Wrapped System.getProperties() calls in try / catch blocks that catch and log
+ the SecurityExceptions, and ignore System properties if access to them is forbidden.
+ c3p0.properties configuration should work fine in security-controlled apps, but you
+ won't be able to configure c3p0 using "java -Dc3p0.xxxx=yyy ..." Since most users use
+ c3p0.properties for configuration, I don't think this will be a major problem. In the
+ future, I may be able to work around the restriction by having ask only for c3p0-specific
+ System properties, rather than calling the unrestricted System.getProperties() method.
+ [Thanks to zambak on SourceForge for calling attention to this issue.]
+c3p0-0.9.0-pre3
+ -- Modified generated proxy classes to properly report the type of the closed Object
+ if used after call to close().
+ -- Fixed a really embarrassing oversight, wherein cached PreparedStatements
+ were being physically close()ed prior to their return to the cache! Tests
+ on psql failed to reveal this, because psql-7.4 Statement.close() does nothing
+ to invalidate the Statement! [Many thanks to tbayboy on hibernate's forum for
+ discovering this issue, and to fassev, also on hibernate's forum, for correctly
+ deducing the cause!]
+ -- Fixed an embarrassing oversight where operations of proxy Statements
+ and ResultSets failed to mark transactions as potentially dirty, leading
+ to the possibility that Connections with unresolved transactional work could
+ be checked into the pool.
+ -- Modified c3p0 Statements to no longer permit calls to getConnection()
+ after Statement close, and to null out the backreference to the
+ parent Connection. [Thanks to Edward Bridges for pointing out the issue.]
+ -- NewPooledConnection threw a NullPointerException on close() of
+ a ResultSet whose creating Statement had already been closed.
+ Fixed. [Thanks to Edward Bridges for pointing out the problem.]
+ -- Modified PoolConfig to trim() read-in c3p0.properties to avoid
+ NumberFormatExceptions if there is extra spacing in the file.
+ [Thanks to johnchan for calling attention to this issue.]
+c3p0-0.9.0-pre2
+ -- Documentation updates and improvements
+ -- Integrated configurable wrapper logging library, so that
+ c3p0 can log to log4j or jdk14logging.
+c3p0-0.9.0-pre1
+ -- First pass at documentation of JBoss integration.
+ -- Better one-per-JVM canonicalization (IdentityTokenized,
+ IdentityTokenResolvable, C3P0Registry)
+ -- First implementation of MBean for JBoss compatability
+ -- Fixed JNDI ref-based DataSources
+c3p0-0.8.5.1
+ -- Fixed a really embarrassing bug that made Statement pooling worse than
+ useless where users have set maxStatements but not maxStatementsPerConnection.
+ maxStatementsPerConnection -- that is, 0 -- was being used in place of maxStatements
+ on Statement cache initialization. [Thanks to Gwendal Tanguy for not only,
+ calling attention to this issue, but tracking down the precise location of
+ the bug!]
+c3p0-0.8.5
+ -- Documentation updates and improvements
+ -- Fixed an issue where failures on reset() of a pooled Connection caused
+ a Connection error to be signalled, without updating the status of the
+ PooledConnection to indicate that it is broken. Thanks to Henry Le for
+ calling attention to this issue.
+c3p0-0.8.5-pre9
+ -- Modified c3p0's behavior on detecting an apparently broken Connection.
+ Previously c3p0 would very aggressively close any Connections that, after
+ an Exception, failed to pass a Connection test. This sometimes surprised
+ and confused users, when Conections they expected to remain open were
+ closed from underneath them. Under normal circumstances, a Connection
+ which has failed its Connection test is broken anyway, so closing it
+ does no harm. But under some circumstances, a Connection may fail its
+ Connection test, but still be partly functional from the application's
+ point of view. So, now c3p0 simply marks Connections that fail their
+ Connection test following an Exception as broken, and excludes them from
+ future re-entry into the pool, but leaves those Connections open for users
+ to continue working with them prior to their explicit close() of the
+ Connection. [Thanks to Julian Legeny for calling attention to this issue.]
+ -- Modified c3p0's logging behavior to be a little bit less annoying. Rather than
+ frequently printing duplicate stack traces to be sure root cause Exceptions are
+ logged as well as converted, rethrown Exceptions, c3p0 now checks the Java
+ version, and uses the initCause() method to log the root cause in versions 1.4
+ and above. Also, some Exceptions that were expected and thrown without information
+ as to the cause now use the initCause() method to provide this in JDK 1.4 and
+ above. [Thanks to Ruslan Gainutdinov, Anthiny Pereira, Carsten ????, and
+ Eric Hsieh for calling attention to these issues.]
+ -- Fixed bug whereby proxy wrappers were sometimes placed around null return values
+ from native Objects, particularly Statement's getResultSets() method. [Thanks to
+ Ruslan Gainutdinov for calling attention to this issue.]
+ -- Fixed bug in NewProxyConnection whereby setTransactionIsolation was called even
+ if the Connections' isolation level had not been changed from its default. [Thanks
+ to Levent Aksu for reporting this bug.]
+c3p0-0.8.5-pre8
+ -- Added C3P0ProxyStatement and rawStatementOperations for accessing vendor-specific
+ Statement API.
+ -- Fixed bug whereby recently added properties could not be configured properly
+ via a PoolConfig Object passed to the DataSources factory method. [Thanks to
+ Julian Legeny for finding this bug.]
+ -- Undid new behavior whereby c3p0 warns when it detects that a transaction
+ has not been comitted or rolled-back prior to close and must be automatically
+ rolled-back. Because c3p0 autorollsback conservatively, whenever it is possible
+ that there may be any transactional resources held by the Connection being
+ checked in, users who make read-only queries, then close without commit or
+ rollback, saw the warning, and became annoyed. Warning when it's "right" is hard,
+ and would involve paying attention to the substance of user-queries, as well
+ as the transaction's isolation level. In the future, when c3p0 offers better
+ control over logging, we'll make the warning optional. Note that this release
+ only undoes the warning -- the new logic that detects potential transactional work
+ and only rollsback when there has been some remains. [Thanks to David Graham for
+ calling attention to this issue.]
+ -- Synchronized StatementCacheKey.find( ... ) method. Subclasses of StatementCacheKey
+ have always explicitly relied upon this method being synchronized in the parent class,
+ but somehow it was not... [Thanks to Manfred Hutt for reporting this bug.]
+ -- Modified both PooledConnection implementations to reset the transaction
+ isolation level on PooledConnection.reset()
+ -- Finalized PooledDataSource api for pool stats and resets.
+c3p0-0.8.5-pre7a
+ -- Fixed some bugs in new Proxy classes relating to close(), methods,
+ especially NullPointerExceptions on duplicate close() operations.
+ -- Fixed some misleading documentation re: testConnectionOnCheckin and
+ automaticTestTable vs. preferredTestQuery
+ -- Changed wording of warning message on purge of idle-test-failing
+ resource to no longer suggest that some user action is required.
+ [Thanks to Krishna Kuchibhotla for calling attention to this
+ issue.]
+ -- Modified GooGooStatementCache so that its close() is synchronous,
+ since when the Statement cache is closed, it's asynchronous
+ runner has often been closed already underneath it. (Actually
+ changed as of c3p0-0.8.5-pre7, but forgot to log it.)
+c3p0-0.8.5-pre7
+ -- Added flag (in both new and tradional reflective proxies) to
+ prevent automatic rollbacks on Connection close()es immediately post-
+ commit(), rollback(), or setAutoCommit().
+ -- DatabaseMetaData now properly returns proxy rather than naked
+ raw Connection. [Thanks to jhoeller on the hibernate forums]
+ -- In response to an apparent BEA WebLogic specific problem with
+ DriverManager.getConnection( ... ), DriverManagerDataSource now
+ caches its driver and uses Driver.connect( ... ) rather than
+ DriverManager.getConnection() when acquiring Connections. Thanks to
+ Lars Torunski for calling attention to this issue.
+ -- Fixed some missing synchroniztion that really should be provided for accessors
+ and mutators of DataSource bean properties.
+ -- Added config parameter automaticTestTable, which takes the name of a table
+ that c3p0 will create and use as the basis for Connection tests. It's easier
+ to use than preferred test query, because you don't have to ensure the existance
+ of any tables in the schema prior to using a Pooled data source.
+ -- Fixed idiotic oversight whereby Connections didn't get their transaction status
+ resolved and reset on check-in...
+c3p0-0.8.5-pre6
+ -- Modified resource pools to only conditionally support async ResourcePoolEvent
+ generation. Since c3p0 doesn't use ResourcePoolEvents, we can do without the
+ superfluous CarefulRunnableQueue, and its associated Thread, which was serving
+ as an event queue.
+ -- Fixed bug where asynchronous refurbishment (testing) of resources would be
+ attempted even on check-in to a closed pool, whose async threads had terminated.
+ -- c3p0 now supports the following new configuraion parameters: preferredTestQuery,
+ testConnectionOnCheckin, checkoutTimeout, and maxStatementsPerConnection.
+ preferredTestQuery should allow c3p0 to test Connections (whether on checkout,
+ checkin, or after a specified idle time) much more efficiently than with
+ it's default test [a call to getTables(...) on the Connection's DatabaseMetaData.].
+ checkoutTimeout allows clients to break out of a getConnection() call after a
+ a specified time period, rather than waiting (potentially indefinitely) for
+ a new Connection to be acquired from the database, or a checked-out Connection
+ to be rechecked in. testConnectionOnCheckin should be self-explanatory.
+ maxStatementsPerConnections allows users to specify how many statements to cache
+ on a per-connection base instead of, or in addition to, the global limit maxStatements.
+ -- Fixed various bugs relating to the referenceMaker and getReference() method of
+ ComboPooledDataSource.
+ -- Added TestUtils class to util package (modified from an old C3P0TestUtils
+ class now removed from the test directory), which now contains facilities
+ for establishing the identity of the physical Connection beneath a proxy,
+ so that tests can check to see when they are seeing the same physical
+ Connections recycled. [Thanks to Julian Legeny for calling attention to this
+ issue.]
+ -- Modified code generators so that inner objects of all NewProxy classes
+ go to null on object close(). Closed proxies should be hopelessly broken.
+ -- Added missing parameter "usesTraditionalReflectiveProxies"
+ to ComboPooledDataSource
+ -- Fixed bug whereby CarefulRunnableQueue threads linger indefinitely, even
+ though their close() method has been called. [Thanks to muirwa on the
+ hibernate forums for calling attention to this issue.]
+c3p0-0.8.5-pre5
+ -- added utility class for Oracle users who wish to access vendor specific
+ Connection API relating to BLOBs and CLOBs. [Thanks to Dave Smith for calling
+ attention to this issue.]
+ -- modified ThreadPoolAsynchronousRunner to provide more information should an
+ "apparent deadlock" occur, and fixed that class' recovery strategy from such
+ an event. [Thanks to Damien Evans for calling attention to this issue.]
+c3p0-0.8.5-pre4
+ -- added config parameter usesTraditionalReflectiveProxies, which defaults to false,
+ and stitched in the 'new' codegenerated, non-reflective proxy implementation for
+ the default case.
+c3p0-0.8.5-pre3
+ -- Added new config parameters breakAfterAcquireFailure, acquireRetryAttempts, and
+ acquireRetryDelay to ComboPoolBackedDataSource (forget to modify this class when
+ I added the params earlier.)
+ -- Added description of raw connection operations to docs.
+c3p0-0.8.5-pre2
+ -- Defined AuthMaskedProperties object, and used this to prevent usernames
+ and passwords from being dumped to logs (a security issue). [Thanks to Zac Jacobson
+ for calling attention to this issue.]
+ -- Made BasicResourcePool's refurbishment of checked-in resources
+ asynchronous, and used this to add a test of the resource on check-in.
+ But since the extra test may slow stuff down substantially, I've disabled it
+ pending a testConnectionsOnCheckin config parameter.
+ -- Fixed NullPointerException in C3P0ImplUtils.findAuth(Object o) which
+ occurred when the Object at issue had write-only properties. [Thanks to
+ mrfekson for calling attention to this issue.]
+ -- Added acquireRetryAttempts, acquireRetryDelay, and breakAfterAcquireFailure
+ as configurable properties. Supporting breakAfterAcquireFailure == false
+ required substantial changes to BasicResourcePool. [Thanks to Zac Jacobson
+ for suggesting the behavior for breakAfterAcquireFailure == false.]
+ -- Added poolOwnerIdentityToken property to PoolBackedDataSource,
+ which becomes part of the state of C3P0PooledConectionPoolManager,
+ to avoid possibility that multiple, identically configured pools
+ that users create separately and consider distinct would be closed
+ if any one of them were closed. This parameter ensures that for
+ each user-created DataSource, there will be a distinct
+ C3P0PooledConectionPoolManager. Multiple copies, as often arise
+ via deserialization and/or dereferencing JNDI DataSources, will
+ still share a single pool and configuration.
+ -- Added reset methods, hard and soft, to PooledDataSource and
+ its implementations. Also added some extra aggregate pool statistics
+ methods. [Thanks to Travis Reeder for suggesting the hardReset() behavior.]
+ -- Fixed bug that PoolBackedDataSource always provided statistics for the
+ default authentication pool, even when alternate usernames and passwords
+ were provided.
+c3p0-0.8.5-pre1
+ -- Defined the C3P0ProxyConnection interface, and modified both reflective and unreflective
+ proxy Connection code to implement it. The interface provides a method through which
+ advanced users can work with the raw, unproxied database Connection. This was motivated
+ by some Oracle-specific API that could not be passed through the generic Connections
+ returned by c3p0 pools. Thanks to Dave Smith for calling attention to this issue.
+ -- Fixed a NullPointerException that resulted when users called DriverManagerDataSource's
+ getConnection( username, password ) with either username or password as null. Users
+ will now see the SQLException provided by their database for bad authentication, which
+ should make the problem more obvious.
+c3p0-0.8.4.5
+ -- Modified all variants of StatementCacheKey, as well as C3P0PooledConnection (proxy Statements)
+ to support jdbc3.0 API for autogenerated keys and ResultSet holdability. All jdbc 3.0 API
+ is now supported.
+c3p0-0.8.4.2
+ -- Fixed bug in which proxy Statements and ResultSets returned their
+ naked, unproxied parents from their getConnection() / getStatement()
+ methods. Thanks to Christian Josefsson for noticing this!
+ -- Added check to ensure that ResourcePools are not broken prior to
+ posting asynchronous events. It'd be better to factor the (as yet
+ unutilized async event stuff into a subclass to simplify
+ BasicResourcePool).
+ -- Fixed documentation typo that said default numHelperThreads was "false"...
+ now it's correctly set to "3".
+ -- Added extra System.err information to instrument the causes of broken
+ resource pools.
+ -- finalize() method of BasicResourcePool checks to be sure user has not
+ already closed the pool before closing, to avoid spurious multiple
+ close warnings. Thanks to Gavin King.
+c3p0-0.8.4.1
+ -- Added (hopefully not too annoying) dump of DataSource configuration
+ on pool initialization to assist user debugging.
+ -- Added methods to force destroy all resources used by a PooledDataSource,
+ even if other DataSource instances are sharing those resources. This is
+ intended primarily for applications that wish to discard the ClassLoader that
+ loaded c3p0, regardless of whatever's currently going on.
+ -- Turned off annoying trace messages when a C3P0PooledConnection closes.
+ -- Fixed issue in statement cache whereby statements that fail to check-in
+ properly (that throw an exception in "clearParameters") cause an exception
+ in removeStatement, because removeStatement sees a statement that appears
+ neither to be checked-out, nor in the deathmarch for checked-in statements.
+ Resolved by re-adding truculent statement to the checkedOut set, and then
+ destroying using removeStatement's codepath for removing and (force-)destroying
+ checked-out statements. Thanks to Zach Scott for calling attention to
+ this issue.
+c3p0-0.8.4
+ -- Updated and HTML-ized documentation, updated READMEs.
+c3p0-0.8.4-test5
+ -- Includes, but does not yet use, cleaner, nonreflective
+ reimplementation of C3P0PooledConnection and all of the
+ JDBC proxy wrappers. (c3p0-0.8.4 will stick with the old,
+ tested implementation, c3p0-0.8.5 will replace.)
+ -- Removed temporary debug wrapper around acquired Connections
+ added in c3p0-0.8.4-test4-3.
+c3p0-0.8.4-test4-3
+ -- Fixed race condition in BasicResourcePool close(), whereby
+ pooled resources were to be destroyed by an asynchronous thread
+ that itself was shut down by the close() method, so the resource
+ closures did not necessarily occur. Pooled resources are now
+ destroyed synchronously in BasicResourcePool.close()
+ -- Fixed erroneous call to C3P0PooledConnection reset() even when
+ the PooledConnection is known to be broken.
+ -- Added temporary debug wrapper that dumps a stack trace on physical
+ Connection close() to help Adrian track down a stubborn issue.
+c3p0-0.8.4-test4
+ -- Made sure PooledConnection's that are known to be broken are not reset()
+ when the ProxyConnection that noticed the break cleans itself up.
+ -- Fixed a problem in C3P0PooledConnection's ProxyConnectionInvocationHandler
+ when Connection-invalidating exceptions occurred inside the factored-out
+ doSilentClose() method rather than in the invoke() method. This could lead to
+ broken PooledConnections not being noticed and expunged from the pool
+ prior to recheck-out.
+ -- Ensured stack-trace of any Connection-invalidating Exceptions are
+ logged. (Previously only the messages were logged.)
+ -- Access to ProxyConnections is now synchronized to avoid a possible
+ race condition where a Connection could close while another method
+ was in progress.
+ -- Cleaned up C3P0PooledConnection a bit, got rid of no longer used
+ reflective code, modified to use new SQL interface filter classes.
+ -- Reorganized bean generation stuff to sit under com.mchange.v2.codegen
+ -- Added com.mchange.v2.sql.filter package, and code generation stuff so
+ that abstract filter classes that implement JDBC interfaces can be
+ easily regenerated from current versions. (This is preparation for
+ JDK 1.4.x build support.)
+c3p0-0.8.4-test3
+ -- Reorganized C3P0PooledConnection and C3P0PooledConnectionPool so that
+ PooledConnections only fire connectionErrorOccurred events on errors that
+ signal the Connection is invalid. (Previously we fired the event in all
+ cases, then tested the validity of the connection in the event handler.)
+ -- Fixed a bug where SQLExceptions that do not signal invalid Connections
+ (such as an exception on transaction commit under optimistic concurrency)
+ caused Connections to be closed, but still returned to the pool, leaving
+ the pool corrupt. [Thanks to Adrian Petru Dimulescu for discovering and
+ reporting this subtle problem!]
+c3p0-0.8.4-test2
+ -- Backed off a binary incompatible change to DataSources API. Users
+ interested in statistics about running DataSources will need to
+ cast the result of DataSources.pooledDataSource() to a
+ com.mchange.v2.c3p0.PooledDataSource object. Other users should
+ be able to use DataSources without recompilinbg libraries that
+ depend upon it.
+ -- Defined com.mchange.v2.c3p0.ComboPooledDataSource, a single, directly-instantiable
+ JavaBean-style DataSource backed by a c3p0 pool, which makes integration
+ with various app servers easier (e.g. works with Tomcat), and which may
+ provide a more straightforward API for users than DataSources and PoolConfig.
+c3p0-0.8.4-test1
+ -- Switched various classes from using RoundRobinAsynchronousRunner
+ to a new ThreadPoolAsynchronousRunner.
+ -- Defined PoolingDataSource interface, which permits clients to access
+ statistics about the state of underlying pools, and made C3P0 pool-backed
+ DataSources implement that interface.
+ -- fixed a subtle bug with respect to ownership of shared resources in which
+ finalize() methods for discarded DataSources could cause helper threads and
+ other resources to be cleaned up prematurely, while they are still in use
+ by other DataSources.
+ -- major reorganization of code generation for DataSources and the
+ resulting objects.
+ -- reorganization of Serializable and Referenceable code of DataSources
+ for JNDI Binding
+ -- made PoolConfig defaults (which are set by system properties, a
+ c3p0.properties file, or else hardcoded defaults) apply to DataSources
+ that are directly instantiated rather than created via the
+ com.mchange.v2.c3p0.DataSources factory class.
+ -- organized scattered stuff into a self-contained, distributable build
+ directly, and divided source and binary distributions
+ -- relicensed library; now available under LGPL.
+ -- THERE ARE SOME MAJOR REORGANIZATIONS IN THIS RELEASE. ANY HELP IN
+ TESTING AND FEEDBACK WOULD BE APPRECIATED! THANKS!
+c3p0-0.8.3.1
+ -- take greater care to abort if a helper thread wakes from wait()
+ inside of a broken resource pool
+ -- log more debug information if a pool unexpectedly breaks
+c3p0-0.8.3
+ -- resolved a deadlock arising from ConnectionEventSupport objects
+ calling event listener methods while holding the ConnectionEventSupport's
+ lock. (Thanks to an anonymous Sourceforge bug submitter for tracking this
+ down.)
+ -- added two configuration parameters to modify how c3p0 deals with
+ potentially unresolved transactions on transaction close --
+ autoCommitOnClose and forceIgnoreUnresolvedTransactions. c3p0's
+ default behavior is as it always was (and as I think it nearly
+ always should be) -- by default we roll back any uncommitted work
+ on close. See PoolConfig class for complete documentation.
+ -- fixed stupid build error that made unzipping c3p0 archives messy
+ since c3p0-0.8.2. (SORRY!)
+c3p0-0.8.3-pre-travis
+ -- fixed problem with c3p0 trying endlessly to connect to a database
+ that is down or not present. c3p0 now tries thirty time, once per
+ second, then gives up. if there's demand, I'll make the delay and
+ number of attempts user configurable. Thanks to Alfonso da Silva
+ for calling attention to this problem.
+ -- modified RoundRobinAsynchronousRunner to die more gracefully if,
+ somehow, one of its CarefulAsynchronousRunners somehow dies. Thanks to
+ Travis Reeder for calling attention to this issue.
+ -- modified ResourcePools to allow resources to be tested periodically
+ and asynchronously while they are idle in the pool.
+ -- added to a new configuration property to PoolConfig and related
+ classes, idleConnectionTestPeriod, which if set to a greater-than-zero
+ value will cause c3p0 to periodically test idle, pooled connection,
+ and remove them from the pool if they are broken. Thanks to
+ Travis Reeder for calling attention to this issue.
+ -- removed SQLState 08S01 from the set of SQL states presumed to mean
+ all connections in the pool have become invalid. Apparently MySQL
+ uses this SQLState to indicate the timing out of idle, individual
+ connections. Thanks to Travis Reeder for calling attention to this issue.
+ -- many (!) bug-fixes, small tweaks and improvements.
+ -- temporarily sprinkled some debugging output prefixed "c3p0-TRAVIS" to
+ try to resolve some issues for Travis Reeder
+c3p0-0.8.2
+ -- Addressed a rare problem that ocurred when application servers or
+ other external code calls interrupt() on c3p0 helper threads. Thanks
+ to Travis Reeder for reporting this issue! The endless cascade of
+ ArrayIndexOutOfBoundsException that would occasionallY result has
+ been fixed, and c3p0 helper threads now ignore interupts,
+ which should almost completely prevent such interrupts()
+ from leaving c3p0 pools in a corrupt state. [In the very, very
+ unlikely event that multiple, well-timed interrupts() cause
+ a pool to diagnose itself broken, c3p0 will start throwing clear
+ exceptions to indicate that -- there shouldn't be any deadlock or
+ any subtle kind of corruption.]
+ -- Deprecated PoolBackedDataSourceFactory and DriverManagerDataSourceFactory
+ in favor of the newer DataSources class.
+ -- Updated example code and documentation.
+ -- Moved constants from DataSources to PoolConfig where they belong!
+c3p0-0.8.2-pre10
+ -- Removed use of properties default mechanism in setting up JDBC
+ properties for DriverManagerDataSource, as some drivers use get()
+ rather than getProperties(), and the Properties.get() method ignores
+ defaults. [Thanks go to Michael Jakl <mj at int-x.org> for both finding
+ and fixing this problem!!!]
+ -- Fixed subtle problem whereby the introspective constructor of
+ C3P0PooledConnectionPoolManager would inadvertently check out an
+ extra pooled connection, by treating the getPooledConnection() as
+ a simple property read and invoking it. [Thanks go to Michael Jakl
+ <mj at int-x.org> for both finding and fixing this bug!!!]
+c3p0-0.8.2-pre9
+ -- Finished DataSources static factory for, um, DataSources.
+ -- Added PoolConfig class to encapsulate all configuration
+ information from factories
+ -- Set up PoolConfig to pay attention to (in order):
+ 1) explicit, user-defined parameters
+ 2) parameters defined as System properties
+ 3) parameters defined in a Properties file resource, at
+ resource pasth /c3p0.properties in PoolConfig's classloader
+ 4) Hardcoded defaults from the C3P0Defaults class
+ -- Defined a new pool configuration property, "testConnectionOnCheckout".
+ If set to true (it defaults to false), each pooled Connection will be
+ verified at checkout time, before being supplied to clients. THIS SLOWS
+ CONNECTION CHECKOUT DRAMATICALLY (by an order of magnitude on my machine),
+ but some folks may want it. The expensive, verified Connections are still
+ faster to acquire than brand new ones.
+ -- If a Connection experiences an SQLException with a defined SQL State of
+ 08001, 08007, or 08S01, the entire database is considered to have been
+ shutdown, and the pool whose connection experienced the problem is reset
+ -- i.e. all existing Connections are discarded, and new Connections are
+ acquired for the pool. (Thanks! to Gavin King for suggesting the SQL State
+ approach to detecting disconnects.)
+ -- ConnectionTester interface and its default implementation were modified
+ to support reporting DATABASE_IS_INVALID (rather than simply testing a
+ disconnected, individual Connection).
+c3p0-0.8.2-pre8
+ -- Fixed some really stupid bugs from pre-7, in particular
+ the static factory methods of the DataSources class were
+ not declared static. Oops.
+c3p0-0.8.2-pre7
+ -- Major reorganization of data source implementations. These
+ are now generated from XML templates, because it was becoming
+ difficult to keep multiple versions of multiple types of
+ datasources in sync with one another
+ -- a new factory / utility class has been added,
+ com.mchange.v2.c3p0.DataSources.
+ Soon the older factories (DriverManagerDataSourceFactory and
+ PoolBackedDataSourceFactory) will be deprecated in favor of this.
+ -- Began adding support for wrapper DataSources that lazily bind to
+ an unpooled DataSource specified via JNDI. (Current wrappers require
+ an actual instance, not a mere reference in order to construct a
+ PoolBacked or ConnectionPoolDataSource).
+c3p0-0.8.2-pre6
+ -- Fixed bug that led to ConcurrentModificationException
+ when Connection.close() iterates through and closes
+ its unclosed Statements.
+ -- Fixed bug that caused uncached Statements to sometimes
+ be closed twice, as they were not properly removed from
+ the set of Connection-associated statements on user-
+ initiated Statement.close(), and would thus be closed
+ again on Connection.close().
+ -- Reorganized classes to clean up top-level com.mchange.v2.c3p0
+ package. The gory innards of c3p0 now live in an impl package.
+ -- Reorganized DataSources so that there are "Base" versions suitable
+ for DBMS-specific subclassing.
+c3p0-0.8.2-pre5
+ -- DataSources looked up by JNDI now "coalesce" -- i.e., if
+ two separate calls look-up the same JNDI bound DataSource,
+ they will both see the same DataSource instance in the local
+ VM. This is especially important for PoolBackedDataSources, as they
+ own "helper threads" that help to manage the pool. Now each
+ logical DataSource shares the same pool of helper threads,
+ regardless of whether it is looked up once and cached, or
+ looked up multiple times and at multiple places by an application.
+ -- Number of helper-threads managing a PoolBackedDataSource is now
+ user-configurable
+c3p0-0.8.2-pre4
+ -- Fixed BAD memory leak -- cachedStatements were being retained
+ on close. This brings a performance improvement to
+ the StatementCache as well.
+ -- Set-up StatementCacheKey to coalesce, such that its equals
+ method can look like {return (this == o);}. Unfortunately, I
+ did not see the kind of performance increase I'd anticipated
+ from this... in fact, whatever performance changes I did see were
+ pretty negligable. Performed some other, mostly useless, optimizations
+ to the process of acquiring StatementCacheKeys. (I can't decide...
+ I've got 3 usuable, broadly similar versions of StatementCacheKey now.)
+ -- Fixed bugs having to do with when to create and the daemon status
+ of Timers
+ -- Appropriately synchronized access to PoolBackedDataSource. (This
+ should permit me to pull back on synchronization / marking of
+ volatile in some dependent classes, as access to these is now
+ controlled via PoolBackedDataSource.)
+ -- Made instantion of C3P0PooledConnectionPoolManager
+ by WrapperPoolBackedDataSource "lazy", so that Timer / Async task
+ threads are only started up when a Connection is actually requested
+ from a DataSource. This is useful especially for the instance residing
+ in the JNDI server VM, whose purpose is likely only to serve as an
+ instance to be looked up over the network. There's no reason why
+ it should keep several stalled Threads open.
+ -- Fixed DefaultConnectionTester, whose logic was bass-ackwards.
+c3p0-0.8.2-pre3
+ -- Factory method for StatementCacheKeys now "coalesce" equivalent
+ instances to conserve memory and to enable a fast (this == o) test
+ to usually work in equals methods. [Value test remains for when
+ identity test fails... maybe later I'll be more comfortable with a
+ 1 instance per value guarantee, and remove the rest of the test.
+ As the constructor is private, the factory method coalesces, and the
+ class is not Serializable, an identity test should suffice. But
+ I'm paranoid.]
+
+ -- All asynchronous tasks are now distributed to 3 helper threads per
+ DataSource rather than queuing on just one. (Culing of expired
+ Connections is still managed by a single java.util.Timer... I don't
+ think this will be a problem.)
+
+c3p0-0.8.2-pre2
+ -- Total rewite of Statement cache!
+
diff --git a/src/dist-static/LICENSE b/src/dist-static/LICENSE
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/src/dist-static/LICENSE
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/src/dist-static/README b/src/dist-static/README
new file mode 100644
index 0000000..2a71093
--- /dev/null
+++ b/src/dist-static/README
@@ -0,0 +1,20 @@
+ _____________________________________
+ .................................
+ ..(c3p0).........................
+ ............a.........fresh......
+ ....coat.......of........stucco..
+ .........over.......that.....old.
+ ...jdbc........driver............
+ .................................
+ -------------------------------------
+ versions 0.8.4 and above
+ 23-Dec-2003
+
+-> Feedback to Steve Waldman <swaldman at mchange.com> <-
+-> Please! :) <-
+
+Hi. c3p0's docs have been HTML-ized, and moved to the "doc"
+directory of this distribution. Please check that out.
+
+Sorry all you plain text fans!
+
diff --git a/src/dist-static/TODO b/src/dist-static/TODO
new file mode 100644
index 0000000..783f84d
--- /dev/null
+++ b/src/dist-static/TODO
@@ -0,0 +1,28 @@
+full status dump operations from JMX beans (two methods, one std err onr to logger at INFO level?)
+
+Statement cache statistics exposed via JMX.
+Interface to Statement-caching statistics
+
+make asynchronous checkins optional, let the default depend on the value of testConnectionOnCheckin.
+
+A shortcut in acquire tasks that bails before attempting an acquire if the acquisition
+is no longer necessary.
+
+Make visible rootCause exceptions when Connection test fails. (Looks implemented, but I don't see them. Debug.)
+
+Less confusing hibernate ConnectionProvider.
+
+JUnit testing regime.
+
+some validation stuff in C3P0PooledConnectionPoolManager...
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dist-static/examples/JndiBindDataSource.java b/src/dist-static/examples/JndiBindDataSource.java
new file mode 100644
index 0000000..0b2b95a
--- /dev/null
+++ b/src/dist-static/examples/JndiBindDataSource.java
@@ -0,0 +1,80 @@
+import java.sql.*;
+import javax.naming.*;
+import javax.sql.DataSource;
+import com.mchange.v2.c3p0.DataSources;
+
+
+/**
+ * This example shows how to acquire a c3p0 DataSource and
+ * bind it to a JNDI name service.
+ */
+public final class JndiBindDataSource
+{
+ // be sure to load your database driver class, either via
+ // Class.forName() [as shown below] or externally (e.g. by
+ // using -Djdbc.drivers when starting your JVM).
+ static
+ {
+ try
+ { Class.forName( "org.postgresql.Driver" ); }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ // let a command line arg specify the name we will
+ // bind our DataSource to.
+ String jndiName = argv[0];
+
+ // acquire the DataSource using default pool params...
+ // this is the only c3p0 specific code here
+ DataSource unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/test",
+ "swaldman",
+ "test");
+ DataSource pooled = DataSources.pooledDataSource( unpooled );
+
+ // Create an InitialContext, and bind the DataSource to it in
+ // the usual way.
+ //
+ // We are using the no-arg version of InitialContext's constructor,
+ // therefore, the jndi environment must be first set via a jndi.properties
+ // file, System properties, or by some other means.
+ InitialContext ctx = new InitialContext();
+ ctx.rebind( jndiName, pooled );
+ System.out.println("DataSource bound to nameservice under the name \"" +
+ jndiName + '\"');
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static void attemptClose(ResultSet o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Statement o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Connection o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ private JndiBindDataSource()
+ {}
+}
diff --git a/src/dist-static/examples/UseJndiDataSource.java b/src/dist-static/examples/UseJndiDataSource.java
new file mode 100644
index 0000000..f63d19a
--- /dev/null
+++ b/src/dist-static/examples/UseJndiDataSource.java
@@ -0,0 +1,86 @@
+import java.sql.*;
+import javax.naming.*;
+import javax.sql.DataSource;
+
+/**
+ * This example shows how to programmatically get and directly use
+ * an unpooled DataSource
+ */
+public final class UseJndiDataSource
+{
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ // let a command line arg specify the name we will
+ // lookup our DataSource.
+ String jndiName = argv[0];
+
+ // Create an InitialContext, and lookup the DataSource in
+ // the usual way.
+ //
+ // We are using the no-arg version of InitialContext's constructor,
+ // therefore, the jndi environment must be first set via a jndi.properties
+ // file, System properties, or by some other means.
+ InitialContext ctx = new InitialContext();
+
+ // acquire the DataSource... this is the only c3p0 specific code here
+ DataSource ds = (DataSource) ctx.lookup( jndiName );
+
+ // get hold of a Connection an do stuff, in the usual way
+ Connection con = null;
+ Statement stmt = null;
+ ResultSet rs = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ rs = stmt.executeQuery("SELECT * FROM foo");
+ while (rs.next())
+ System.out.println( rs.getString(1) );
+ }
+ finally
+ {
+ // i try to be neurotic about ResourceManagement,
+ // explicitly closing each resource
+ // but if you are in the habit of only closing
+ // parent resources (e.g. the Connection) and
+ // letting them close their children, all
+ // c3p0 DataSources will properly deal.
+ attemptClose(rs);
+ attemptClose(stmt);
+ attemptClose(con);
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static void attemptClose(ResultSet o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Statement o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Connection o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ private UseJndiDataSource()
+ {}
+}
diff --git a/src/dist-static/examples/UsePoolBackedDataSource.java b/src/dist-static/examples/UsePoolBackedDataSource.java
new file mode 100644
index 0000000..3eb6531
--- /dev/null
+++ b/src/dist-static/examples/UsePoolBackedDataSource.java
@@ -0,0 +1,83 @@
+import java.sql.*;
+import javax.sql.DataSource;
+import com.mchange.v2.c3p0.DataSources;
+
+
+/**
+ * This example shows how to programmatically get and directly use
+ * an pool-backed DataSource
+ */
+public final class UsePoolBackedDataSource
+{
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+ // Note: your JDBC driver must be loaded [via Class.forName( ... ) or -Djdbc.properties]
+ // prior to acquiring your DataSource!
+
+ // Acquire the DataSource... this is the only c3p0 specific code here
+ DataSource unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/test",
+ "swaldman",
+ "test");
+ DataSource pooled = DataSources.pooledDataSource( unpooled );
+
+
+
+ // get hold of a Connection an do stuff, in the usual way
+ Connection con = null;
+ Statement stmt = null;
+ ResultSet rs = null;
+ try
+ {
+ con = pooled.getConnection();
+ stmt = con.createStatement();
+ rs = stmt.executeQuery("SELECT * FROM foo");
+ while (rs.next())
+ System.out.println( rs.getString(1) );
+ }
+ finally
+ {
+ //i try to be neurotic about ResourceManagement,
+ //explicitly closing each resource
+ //but if you are in the habit of only closing
+ //parent resources (e.g. the Connection) and
+ //letting them close their children, all
+ //c3p0 DataSources will properly deal.
+ attemptClose(rs);
+ attemptClose(stmt);
+ attemptClose(con);
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static void attemptClose(ResultSet o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Statement o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Connection o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ private UsePoolBackedDataSource()
+ {}
+}
diff --git a/src/dist-static/examples/UseUnpooledDataSource.java b/src/dist-static/examples/UseUnpooledDataSource.java
new file mode 100644
index 0000000..72b9637
--- /dev/null
+++ b/src/dist-static/examples/UseUnpooledDataSource.java
@@ -0,0 +1,81 @@
+import java.sql.*;
+import javax.sql.DataSource;
+import com.mchange.v2.c3p0.DataSources;
+
+
+/**
+ * This example shows how to programmatically get and directly use
+ * an unpooled DataSource
+ */
+public final class UseUnpooledDataSource
+{
+
+ public static void main(String[] argv)
+ {
+ try
+ {
+
+ // Note: your JDBC driver must be loaded [via Class.forName( ... ) or -Djdbc.properties]
+ // prior to acquiring your DataSource!
+
+ // Acquire the DataSource... this is the only c3p0 specific code here
+ DataSource ds = DataSources.unpooledDataSource("jdbc:postgresql://localhost/test",
+ "swaldman",
+ "test");
+
+ // get hold of a Connection an do stuff, in the usual way
+ Connection con = null;
+ Statement stmt = null;
+ ResultSet rs = null;
+ try
+ {
+ con = ds.getConnection();
+ stmt = con.createStatement();
+ rs = stmt.executeQuery("SELECT * FROM foo");
+ while (rs.next())
+ System.out.println( rs.getString(1) );
+ }
+ finally
+ {
+ //i try to be neurotic about ResourceManagement,
+ //explicitly closing each resource
+ //but if you are in the habit of only closing
+ //parent resources (e.g. the Connection) and
+ //letting them close their children, all
+ //c3p0 DataSources will properly deal.
+ attemptClose(rs);
+ attemptClose(stmt);
+ attemptClose(con);
+ }
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ }
+
+ static void attemptClose(ResultSet o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Statement o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ static void attemptClose(Connection o)
+ {
+ try
+ { if (o != null) o.close();}
+ catch (Exception e)
+ { e.printStackTrace();}
+ }
+
+ private UseUnpooledDataSource()
+ {}
+}
diff --git a/src/dist-static/examples/c3p0-service.xml b/src/dist-static/examples/c3p0-service.xml
new file mode 100644
index 0000000..826a43f
--- /dev/null
+++ b/src/dist-static/examples/c3p0-service.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE server>
+
+<server>
+
+ <mbean code="com.mchange.v2.c3p0.mbean.C3P0PooledDataSource"
+ name="jboss:service=C3P0PooledDataSource">
+
+ <attribute name="JndiName">java:PooledDS</attribute>
+ <attribute name="JdbcUrl">jdbc:postgresql://localhost/c3p0-test</attribute>
+ <attribute name="DriverClass">org.postgresql.Driver</attribute>
+ <attribute name="User">swaldman</attribute>
+ <attribute name="Password">test</attribute>
+
+ <!-- Uncomment and set any of the optional parameters below -->
+ <!-- See c3p0's docs for more info. -->
+
+ <!-- <attribute name="AcquireIncrement">3</attribute> -->
+ <!-- <attribute name="AcquireRetryAttempts">30</attribute> -->
+ <!-- <attribute name="AcquireRetryDelay">1000</attribute> -->
+ <!-- <attribute name="AutoCommitOnClose">false</attribute> -->
+ <!-- <attribute name="AutomaticTestTable"></attribute> -->
+ <!-- <attribute name="BreakAfterAcquireFailure">false</attribute> -->
+ <!-- <attribute name="CheckoutTimeout">0</attribute> -->
+ <!-- <attribute name="ConnectionTesterClassName">0</attribute> -->
+ <!-- <attribute name="Description">A pooled c3p0 DataSource</attribute> -->
+ <!-- <attribute name="FactoryClassLocation"></attribute> -->
+ <!-- <attribute name="ForceIgnoreUnresolvedTransactions">true</attribute> -->
+ <!-- <attribute name="IdleConnectionTestPeriod">-1</attribute> -->
+ <!-- <attribute name="InitialPoolSize">3</attribute> -->
+ <!-- <attribute name="MaxIdleTime">0</attribute> -->
+ <!-- <attribute name="MaxPoolSize">15</attribute> -->
+ <!-- <attribute name="MaxStatements">0</attribute> -->
+ <!-- <attribute name="MaxStatementsPerConnection">0</attribute> -->
+ <!-- <attribute name="MinPoolSize">0</attribute> -->
+ <!-- <attribute name="NumHelperThreads">3</attribute> -->
+ <!-- <attribute name="PreferredTestQuery"></attribute> -->
+ <!-- <attribute name="TestConnectionOnCheckin">false</attribute> -->
+ <!-- <attribute name="TestConnectionOnCheckout">false</attribute> -->
+ <!-- <attribute name="UsesTraditionalReflectiveProxies">false</attribute> -->
+
+
+ <depends>jboss:service=Naming</depends>
+ </mbean>
+
+</server>
+
+
diff --git a/src/doc/arrow_sm.png b/src/doc/arrow_sm.png
new file mode 100644
index 0000000..9c97fdc
Binary files /dev/null and b/src/doc/arrow_sm.png differ
diff --git a/src/doc/index.html b/src/doc/index.html
new file mode 100644
index 0000000..3fbc297
--- /dev/null
+++ b/src/doc/index.html
@@ -0,0 +1,2474 @@
+<html>
+ <head>
+ <title>c3p0-v at c3p0.version@ - JDBC3 Connection and Statement Pooling - Documentation</title>
+ <script language="text/javascript">
+ function toggleDisplay( id1, id2 )
+ {
+ if (document.getElementById( id1 ).style.display == 'block')
+ {
+ document.getElementById( id1 ).style.display = 'none';
+ document.getElementById( id2 ).style.display = 'block';
+ }
+ else
+ {
+ document.getElementById( id2 ).style.display = 'none';
+ document.getElementById( id1 ).style.display = 'block';
+ }
+ return false;
+ }
+ </script>
+ <style type="text/css">
+ a.cfg_param {
+ font-family: monospace;
+ }
+ body {
+ font-family: optima, helvetica, arial, sans-serif;
+ }
+ dl.properties {
+ padding: 0em 3em 0em 3em;
+ }
+ dl.properties dd {
+ padding-bottom: 0.5em;
+ font-size: smaller;
+ }
+ dl.properties dt {
+ padding-top: 1em;
+ border-top-width: 1;
+ border-top-style: dashed;
+ border-top-color: black;
+ font-family: monospace;
+ color: red;
+ }
+ dl.properties div.default {
+ padding-top: 0.5em;
+ text-decoration: underline;
+ padding-bottom: 0.5em;
+ }
+ dl.properties div.per-user {
+ text-align: center;
+ margin-top: 0.5em;
+ font-weight: bold;
+ border-width: 1;
+ border-style: solid;
+ border-color: black;
+ background-color: #FFCCCC;
+ }
+ dl.log_properties {
+ border-width: 2;
+ border-style: solid;
+ border-color: black;
+ padding: 0.5em 0.5em 0.5em 0.5em;
+ font-size: smaller;
+ background-color: #DDDDDD;
+ }
+ dl.log_properties dd {
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ /* font-size: smaller; */
+ }
+ dl.log_properties dt {
+ padding-top: 1em;
+ font-weight: bold;
+ font-family: monospace;
+ color: black;
+ }
+ div.boxed {
+ border-width: 1;
+ border-style: solid;
+ border-color: black;
+ padding: 1em;
+ font-size: smaller;
+ background-color: #CCFFCC;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ }
+ div.boxed h4 {
+ margin-top: 0;
+ padding-top: 0;
+ font-weight: bold;
+ text-decoration: underline;
+ }
+ div.deprecated {
+ display: none;
+ padding-top: 0;
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-bottom: 1em;
+ background-color: #FFAAAA;
+ border: 2px solid red;
+ }
+ div.deprecated h5 {
+ font-weight: bold;
+ color: #CC0000;
+ margin-top: 0em;
+ padding-top: 1em;
+ }
+ div.example {
+ white-space: pre;
+ font-family: monospace;
+ font-size: smaller;
+ background-color: #CCCCFF;
+ margin: 3em 3em 3em 3em;
+ padding: 1em 0em 1em 2em;
+ border-top-width: 2;
+ border-top-color: black;
+ border-top-style: solid;
+ border-bottom-width: 2;
+ border-bottom-color: black;
+ border-bottom-style: solid;
+ /*
+ doesn't show under Firefox
+ border-top: 2 solid black;
+ border-bottom: 2 solid black;
+ */
+ }
+ div.example_properties {
+ white-space: pre;
+ font-family: monospace;
+ font-size: smaller;
+ background-color: #CCCCFF;
+ margin: 3em 3em 3em 3em;
+ padding: 1em 0em 1em 0em;
+ border-top-width: 2;
+ border-top-color: black;
+ border-top-style: solid;
+ border-bottom-width: 2;
+ border-bottom-color: black;
+ border-bottom-style: solid;
+ }
+ div.example_properties strong {
+ font-weight: bold;
+ color: red;
+ }
+ div.hibernate_props {
+ text-align: center;
+ }
+ div.other_properties_desc {
+ padding-top: 2px;
+ padding-left: 2em;
+ padding-right: 2em;
+ padding-bottom: 5px;
+ margin-bottom: 2em;
+ text-align: justify;
+ background: #ddf;
+ }
+ div.sectiontext {
+ margin-left: 2em;
+ margin-right: 2em;
+ }
+ div.subtitle {
+ font-size: 12pt;
+ background-color: #FFFFFF;
+ }
+ div.top_boilerplate {
+ margin-left: 8em;
+ margin-right: 8em;
+ }
+
+ h1 {
+ text-align: center;
+ background-color: #FFCCAA;
+ }
+ h2 {
+ background-color: #FFFFAA;
+ margin-top: 2em;
+ border-bottom-width: 2;
+ border-bottom-color: black;
+ border-bottom-style: solid;
+ }
+ h3 {
+ margin-right: -2em;
+ }
+ img {
+ border: 0;
+ }
+ ol.contents ol {
+ list-style-type: lower-roman;
+ }
+ ol.contents ol li {
+ font-size: smaller;
+ }
+ ol.contents > ul.box {
+ list-style: circle;
+ }
+ ol.precedence {
+ width: 75%;
+ border-width: 2;
+ border-color: black;
+ border-style: solid;
+ padding-left: 3em;
+ padding-top: 1em;
+ padding-bottom: 1em;
+ padding-right: 1em;
+ text-align: left;
+ }
+
+ ol.precedence > li {
+ font-size: smaller;
+ margin-top: 0.5em;
+ }
+ span.hibparam_comment {
+ font-family: optima, helvetica, arial, sans-serif;
+ font-style: italic;
+ text-decoration: underline;
+ }
+ span.toplink {
+ font-size: 12;
+ font-style: plain;
+ float: right;
+ }
+ table.beanPropSummaryTable {
+ width: 100%;
+ border: none;
+ }
+ table.beanPropSummaryTable tr td {
+ width: 33%;
+ border: none;
+ background: #ddf;
+ font-family: monospace;
+ line-height: 1.5;
+ padding: 8px;
+ }
+
+ table.beanPropSummaryTable tr td a {
+ color: black;
+ }
+
+ table.hibernate_props {
+ border-width: 2;
+ border-color: black;
+ border-style: solid;
+ font-size: smaller;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ table.hibernate_props th {
+ background-color: #FFCCAA;
+ }
+ table.hibernate_props td {
+ font-family: monospace;
+ }
+ ul.other_props_list {
+ list-style: none;
+ font-family: monospace;
+ color: #A00;
+ }
+ ul.pointerlist {
+ list-style: square;
+ }
+
+ </style>
+ </head>
+ <body>
+ <h1>
+ <div>c3p0 - JDBC3 Connection and Statement Pooling</div>
+ <div class="subtitle">version @c3p0.version@</div>
+ </h1>
+ <div class="top_boilerplate">
+ <p>by Steve Waldman <<a href="mailto:swaldman at mchange.com">swaldman at mchange.com</a>></p>
+ <p>© 2006 Machinery For Change, Inc.</p>
+ <p><i>
+ This software is made available for use, modification, and redistribution,
+ under the terms of the <a href="../LICENSE">Lesser GNU Public License (LGPL)</a>, which you should
+ have received with this distribution.
+ </i></p>
+ <ul class="pointerlist">
+ <li>API docs for c3p0 are <a href="apidocs/index.html">here</a>.</li>
+ <li>Download the latest version from <a href="http://sourceforge.net/projects/c3p0/">c3p0's site on SourceForge</a></li>
+ <li>Looking for the definition of <a href="#configuration_properties">configuration properties</a>?</li>
+ <li>Looking for advice in <a href="#hibernate-specific">using c3p0 with hibernate</a>?</li>
+ </ul>
+ </div>
+ <hr/>
+ <h2><a name="contents">Contents</a></h2>
+ <ol class="contents">
+ <li><a href="#contents">Contents</a></li>
+ <li><a href="#quickstart">Quickstart</a></li>
+ <li><a href="#what_is">What is c3p0?</a></li>
+ <li><a href="#prerequisites">Prerequisites</a></li>
+ <li><a href="#installation">Installation</a></li>
+ <li>
+ <a href="#using_c3p0">Using c3p0</a>
+ <ol class="contents">
+ <li><a href="#using_combopooleddatasource">Using ComboPooledDataSource</a></li>
+ <li><a href="#using_datasources_factory">Using the DataSouces factory class</a></li>
+ <ul class="box">
+ <li><a href="#forceOverrideBox">Box: Overriding authentication information (from non-c3p0 DataSources)</a></li>
+ </ul>
+ <li><a href="#querying">Querying Pool Status</a></li>
+ <ul class="box">
+ <li><a href="#using_c3p0_registry_box">Box: Using C3P0Registry to find a reference to a DataSource</a></li>
+ </ul>
+ <li><a href="#cleaning">Cleaning Up Pool Resources</a></li>
+ <li><a href="#build_your_own">Advanced: Building Your Own PoolBackedDataSource</a></li>
+ <li><a href="#raw_connection_ops">Advanced: Raw Connection and Statement Operations</a></li>
+ </ol>
+ </li>
+ <li>
+ <a href="#configuration">Configuration</a>
+ <ol class="contents">
+ <li><a href="#configuration_introduction">Introduction</a></li>
+ <li><a href="#basic_pool_configuration">Basic Pool Configuration</a></li>
+ <li><a href="#managing_pool_size">Managing Pool Size and Connection Age</a></li>
+ <li><a href="#configuring_connection_testing">Configuring Connection Testing</a></li>
+ <li><a href="#configuring_statement_pooling">Configuring Statement Pooling</a></li>
+ <li><a href="#configuring_recovery">Configuring Recovery From Database Outages</a></li>
+ <li><a href="#connection_customizers">Managing Connection Lifecycles with Connection Customizers</a></li>
+ <li><a href="#configuring_unresolved">Configuring Unresolved Transaction Handling</a></li>
+ <li><a href="#configuring_to_debug_and_workaround_broken_clients">Configuring to Debug and Workaround Broken Client Applications</a></li>
+ <li><a href="#other_ds_configuration">Other DataSource Configuration</a></li>
+ <li><a href="#jmx_configuration_and_management">Configuring and Managing c3p0 via JMX</a></li>
+ <li><a href="#configuring_logging">Configuring Logging</a></li>
+ </ol>
+ </li>
+ <li><a href="#known_shortcomings">Performance</a></li>
+ <li><a href="#performance">Known shortcomings</a></li>
+ <li><a href="#feedback_and_support">Feedback and support</a></li>
+ <li><a href="#configuration_properties">Appendix A: Configuration Properties</a></li>
+ <li><a href="#configuration_files">Appendix B: Configuation Files, etc.</a></li>
+ <ol class="contents">
+ <li><a href="#c3p0_properties">Overriding c3p0 defaults with a c3p0.properties file</a></li>
+ <li><a href="#system_properties">Overriding c3p0 defaults with a System properties</a></li>
+ <li><a href="#c3p0-config.xml">Named and Per-User configuration: Overriding c3p0 defaults via c3p0-config.xml</a></li>
+ <li><a href="#configuration_precedence">Precedence of Configuration Settings</a></li>
+ </ol>
+ <li><a href="#hibernate-specific">Appendix C: Hibernate-specific notes</a></li>
+ <li><a href="#tomcat-specific">Appendix D: Configuring c3p0 pooled DataSources for Apache Tomcat</a></li>
+ <li><a href="#jboss-specific">Appendix E: JBoss-specific notes</a></li>
+ <li><a href="#oracle-specific">Appendix F: Oracle-specific API: createTemporaryBLOB() and createTemporaryCLOB()</a></li>
+ </ol>
+ (See also the API Documentation <a href="apidocs/index.html">here</a>)
+ <hr/>
+ <h2><a name="quickstart">Quickstart</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ c3p0 was designed to be butt-simple to use. Just put
+ the jar file [<tt>lib/c3p0- at c3p0.version@.jar</tt>] in your application's
+ effective <tt>CLASSPATH</tt>, and make a DataSource like this:
+ </p>
+ <div class="example">
+import com.mchange.v2.c3p0.*;
+
+...
+
+ComboPooledDataSource cpds = new ComboPooledDataSource();
+cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver
+cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
+cpds.setUser("dbuser");
+cpds.setPassword("dbpassword");
+ </div>
+ <p>
+ <b>[Optional]</b> If you want to turn on PreparedStatement pooling, you must also set <tt>maxStatements</tt>
+ and/or <tt>maxStatementsPerConnection</tt> (both default to 0):
+ </p>
+ <div class="example">
+cpds.setMaxStatements( 180 );
+ </div>
+ <p>
+ Do whatever you want with your DataSource, which will be backed
+ by a Connection pool set up with default parameters. You
+ can bind the DataSource to a JNDI name service, or use it
+ directly, as you prefer.
+ </p>
+ <p>
+ When you are done, you can clean up the DataSource you've created
+ like this:
+ </p>
+ <div class="example">
+DataSources.destroy( cpds );
+ </div>
+ <p>
+ That's it! The rest is detail.
+ </p>
+ </div>
+ <h2><a name="basics">What is c3p0?</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ c3p0 is an easy-to-use library for making traditional JDBC drivers
+ "enterprise-ready" by augmenting them with functionality defined by
+ the jdbc3 spec and the optional extensions to jdbc2. In particular,
+ c3p0 provides several useful services:
+ </p>
+ <ul>
+ <li>
+ Classes which adapt traditional DriverManager-based JDBC
+ drivers to the new <tt>javax.sql.DataSource</tt> scheme for acquiring
+ database Connections.
+ </li>
+ <li>
+ Transparent pooling of Connection and PreparedStatements
+ behind DataSources which can "wrap" around traditional
+ drivers or arbitrary unpooled DataSources.
+ </li>
+ </ul>
+ <p>
+ The library tries hard to get the details right:
+ </p>
+ <ul>
+ <li>
+ c3p0 DataSources are both <tt>Referenceable</tt> and <tt>Serializable</tt>, and are thus
+ suitable for binding to a wide-variety of JNDI-based naming services.
+ </li>
+ <li>
+ Statement and ResultSets are carefully cleaned up when pooled
+ Connections and Statements are checked in, to prevent resource-
+ exhaustion when clients use the lazy but common resource-management
+ strategy of only cleaning up their Connections....
+ </li>
+ <li>
+ The library adopts the approach
+ defined by the JDBC 2 and 3 specification (even where these
+ conflict with the library author's preferences). DataSources
+ are written in the JavaBean style, offering all the required and
+ most of the optional properties (as well as some non-standard ones),
+ and no-arg constructors. All JDBC-defined internal interfaces are
+ implemented (ConnectionPoolDataSource, PooledConnection,
+ ConnectionEvent-generating Connections, etc.)
+ You can mix c3p0 classes with compliant third-party implementations
+ (although not all c3p0 features will work with external implementations).
+ </li>
+ </ul>
+ <p>
+ c3p0 hopes to provide DataSource implementations more than suitable for
+ use by high-volume "J2EE enterprise applications". Please provide feedback, bug-fixes, etc.!
+ </p>
+ </div>
+ <h2><a name="prerequisites">Prerequisites</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ c3p0 requires a level 1.3.x or above Java Runtime Environment, and
+ the JDBC 2.x or above javax.sql libraries. c3p0 works fine under
+ Java 1.4.x and 1.5.x as well.
+ </p>
+ </div>
+ <h2><a name="installation">Installation</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ Put the file <tt>lib/c3p0- at c3p0.version@.jar</tt>
+ somewhere in your CLASSPATH (or any other place where your application's
+ classloader will find it). That's it!
+ </p>
+ </div>
+ <h2><a name="using_c3p0">Using c3p0</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ From a users' perspective, c3p0 simply provides standard jdbc2 DataSource
+ objects. When creating these DataSources, users can control pooling-related,
+ naming-related, and other properties (See <a href="#configuration_properties">Appendix A</a>). All pooling is entirely
+ transparent to users once a DataSource has been created.
+ </p>
+ <p>
+ There are three ways of acquiring c3p0 pool-backed DataSources: 1) directly instantiate and configure a
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>ComboPooledDataSource</tt></a> bean;
+ 2) use the DataSources factory class; or 3) "build your own" pool-backed
+ DataSource by directly instantiating <tt>PoolBackedDataSource</tt> and setting its <tt>ConectionPoolDataSource</tt>. Most
+ users will probably find instantiating <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>ComboPooledDataSource</tt></a>
+ to be the most convenient approach. Once instantiated,
+ c3p0 DataSources can be bound to nearly any JNDI-compliant name service.
+ </p>
+ <p>
+ Regardless of how you create your DataSource, c3p0 will use defaults for any configuration parameters that
+ you do not specify programmatically. c3p0 has built-in, hard-coded defaults, but you can override these by creating
+ a file called <tt>c3p0.properties</tt> and storing it as a top-level resource in the same <tt>CLASSPATH</tt> (or ClassLoader)
+ that loads c3p0's jar file. As of c3p0-0.9.1, you can also supply a file called <tt>c3p0-config.xml</tt> for more advanced
+ configuration. See <a href="#configuration">Configuration</a> below.
+ </p>
+ <h3>
+ <a name="using_combopooleddatasource">Instantiating and Configuring a ComboPooledDataSource</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Perhaps the most straightforward way to create a c3p0 pooling DataSource is to instantiate an instance of
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>com.mchange.v2.c3p0.ComboPooledDataSource</tt></a>.
+ This is a JavaBean-style class with a public, no-arg constructor,
+ but before you use the DataSource, you'll have to be sure to set at least the property <tt>jdbcUrl</tt>. You may also
+ want to set <tt>user</tt> and <tt>password</tt>, and if you have not externally preloaded the old-style JDBC driver you'll
+ use you should set the <tt>driverClass</tt>.
+ </p>
+<div class="example">
+ComboPooledDataSource cpds = new ComboPooledDataSource();
+cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver
+cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
+cpds.setUser("swaldman");
+cpds.setPassword("test-password");
+
+// the settings below are optional -- c3p0 can work with defaults
+cpds.setMinPoolSize(5);
+cpds.setAcquireIncrement(5);
+cpds.setMaxPoolSize(20);
+
+// The DataSource cpds is now a fully configured and usable pooled DataSource
+
+...
+</div>
+ <p>
+ The defaults of any c3p0 DataSource are determined by configuration you supply, or
+ else revert to hard-coded defaults [see <a href="#configuration_properties">configuration properties</a>].
+ c3p0-0.9.1 and above supports <a href="#c3p0-config.xml">multiple, named configurations</a>.
+ If you wish to use a named configuration, construct your
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>com.mchange.v2.c3p0.ComboPooledDataSource</tt></a>
+ with the configuration name as a constructor agument:
+ </p>
+ <div class="example">
+ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
+ </div>
+ <p>
+ Of course, you can still override any configuration properties programmatically, as above.
+ </p>
+ <h3>
+ <a name="using_datasources_factory">Using the DataSources factory class</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Alternatively, you can use the static factory class
+ <a href="apidocs/com/mchange/v2/c3p0/DataSources.html"><tt>com.mchange.v2.c3p0.DataSources</tt></a> to build unpooled DataSources
+ from traditional JDBC drivers, and to build pooled DataSources from unpooled DataSources:
+ </p>
+ <div class="example">
+DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb",
+ "swaldman",
+ "test-password");
+DataSource ds_pooled = DataSources.pooledDataSource( ds_unpooled );
+
+// The DataSource ds_pooled is now a fully configured and usable pooled DataSource.
+// The DataSource is using a default pool configuration, and Postgres' JDBC driver
+// is presumed to have already been loaded via the jdbc.drivers system property or an
+// explicit call to Class.forName("org.postgresql.Driver") elsewhere.
+
+...
+ </div>
+ <p>
+ If you use the <a href="apidocs/com/mchange/v2/c3p0/DataSources.html">DataSources</a>
+ factory class, and you want to programmatically override default configuration
+ parameters, you can supply a map of override properties:
+ </p>
+ <div class="example">
+DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb",
+ "swaldman",
+ "test-password");
+
+Map overrides = new HashMap();
+overrides.put("maxStatements", "200"); //Stringified property values work
+overrides.put("maxPoolSize", new Integer(50)); //"boxed primitives" also work
+
+// create the PooledDataSource using the default configuration and our overrides
+ds_pooled = DataSources.pooledDataSource( ds_unpooled, overrides );
+
+// The DataSource ds_pooled is now a fully configured and usable pooled DataSource,
+// with Statement caching enabled for a maximum of up to 200 statements and a maximum
+// of 50 Connections.
+
+...
+ </div>
+ <p>
+ If you are using <a href="#c3p0-config.xml">named configurations</a>, you can specify the configuration
+ that defines the default configuration for your DataSource:
+ </p>
+ <div class="example">
+// create the PooledDataSource using the a named configuration and specified overrides
+ds_pooled = DataSources.pooledDataSource( ds_unpooled, "intergalactoAppConfig", overrides );
+ </div>
+
+ <p><a id="showDataSourcesWithPoolConfig"
+ style="display: block"
+ href="#"
+ onClick="return toggleDisplay('showDataSourcesWithPoolConfig', 'DataSourcesWithPoolConfig');"
+ >Show deprecated PoolConfig approach...</a></p>
+ <div class="deprecated" style="display: none" id="DataSourcesWithPoolConfig">
+ <h5>Deprecated! Programmatic configuration via PoolConfig</h5>
+ <p>
+ If you use the <a href="apidocs/com/mchange/v2/c3p0/DataSources.html">DataSources</a>
+ factory class, and you want to programmatically override default configuration
+ parameters, make use of the <a href="apidocs/com/mchange/v2/c3p0/PoolConfig.html"><tt>PoolConfig</tt></a> class:
+ </p>
+ <div class="example">
+DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb",
+ "swaldman",
+ "test-password");
+
+PoolConfig pc = new PoolConfig();
+pc.setMaxStatements(200); //turn on Statement pooling
+
+// pass our overriding PoolConfig to the DataSources.pooledDataSource() factory method.
+
+ds_pooled = DataSources.pooledDataSource( ds_unpooled, pc );
+
+// The DataSource ds_pooled is now a fully configured and usable pooled DataSource,
+// with Statement caching enabled for a maximum of up to 200 statements.
+
+...
+ </div>
+ <a id="hideDataSourcesWithPoolConfig"
+ href="#"
+ onClick="return toggleDisplay('showDataSourcesWithPoolConfig', 'DataSourcesWithPoolConfig');"
+ >Hide deprecated PoolConfig approach</a>
+ </div>
+
+ <div class="boxed">
+ <a name="forceOverrideBox" />
+ <h4>RARE: Forcing authentication information, regardless of (mis)configuration of the underlying (unpooled) DataSource</h4>
+ <p>
+ You can wrap any DataSouce using <tt>DataSource.pooledDataSource( ... )</tt>, usually with no
+ problem whatsoever. DataSources are supposed to indicate the username and password associated
+ by default with Connections via standard properties <tt>user</tt> and <tt>password</tt>.
+ Some DataSource implementations do not offer these properties. Usually this is not at all
+ a problem. <tt>c3p0</tt> works around this
+ by acquiring "default" Connections from the DataSource if it can't find default authentication
+ information, and a client has not specified the authentification information via
+ <tt>getConnection( user, password )</tt>.
+ </p>
+ <p>
+ However, in rare circumstances, non-c3p0 unpooled DataSources provide a <tt>user</tt> property,
+ but not a password property, or you have access to a DataSource that you wish to wrap behind a pool,
+ but you wish to override its build-in authentification defaults without actually modifying the <tt>user</tt>
+ or <tt>password</tt> properties.
+ </p>
+ <p>
+ <tt>c3p0</tt> provides configuation properties <tt>overrideDefaultUser</tt> and <tt>overrideDefaultPassword</tt>.
+ If you set these properties, programmatically as above, or via any of c3p0's <a href="#configuration">configuration mechanisms</a>,
+ <tt>c3p0</tt> PooledDataSources will ignore the user and password property associated with the underlying DataSource,
+ and use the specified overrides instead.
+ </p>
+ </div>
+ <h3>
+ <a name="querying">Querying a PooledDataSource's current status</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 DataSources backed by a pool, which include implementations of
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>ComboPooledDataSource</tt></a> and
+ the objects returned by <tt><a href="apidocs/com/mchange/v2/c3p0/DataSources.html">DataSources</a>.pooledDataSource( ... )</tt>,
+ all implement the interface
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>com.mchange.v2.c3p0.PooledDataSource</tt></a>,
+ which makes available a number of methods for querying the status of
+ DataSource Connection pools. Below is sample code that queries a DataSource for its
+ status:
+ </p>
+ <div class="example">
+// fetch a JNDI-bound DataSource
+InitialContext ictx = new InitialContext();
+DataSource ds = (DataSource) ictx.lookup( "java:comp/env/jdbc/myDataSource" );
+
+// make sure it's a c3p0 PooledDataSource
+if ( ds instanceof PooledDataSource)
+{
+ PooledDataSource pds = (PooledDataSource) ds;
+ System.err.println("num_connections: " + pds.getNumConnectionsDefaultUser());
+ System.err.println("num_busy_connections: " + pds.getNumBusyConnectionsDefaultUser());
+ System.err.println("num_idle_connections: " + pds.getNumIdleConnectionsDefaultUser());
+ System.err.println();
+}
+else
+ System.err.println("Not a c3p0 PooledDataSource!");
+ </div>
+ <p>
+ The status querying methods all come in three overloaded forms, such as:
+ </p>
+ <ul>
+ <li><tt>public int getNumConnectionsDefaultUser()</tt></li>
+ <li><tt>public int getNumConnections(String username, String password)</tt></li>
+ <li><tt>public int getNumConnectionsAllUsers()</tt></li>
+ </ul>
+ <p>
+ c3p0 maintains separate pools for Connections with distinct
+ authentications. The various methods let you query the status of pools individually,
+ or aggregate statistics for all authentifications for which your DataSource is maintaining
+ pools. <i>Note that pool configuration parmeters such as <tt>maxPoolSize</tt> are enforced
+ on a per-authentification basis!</i> For example, if you have set <tt>maxPoolSize</tt> to
+ 20, and if the DataSource is managing connections under two username-password pairs [the
+ default, and one other pair established via a call to <tt>getConnection(user, password)</tt>,
+ you should expect to see as many as 40 Connections from <tt>getNumConnectionsAllUsers()</tt>.
+ </p>
+ <p>
+ <i>
+ Most applications only acquire default-authenticated Connections from DataSources, and
+ can typically just use the <tt>getXXXDefaultUser()</tt> to gather Connection statistics.
+ </i>
+ </p>
+ <p>
+ As well as Connection pool realted statistics, you can retrieve status information about each
+ DataSource's Thread pool.
+ Please see <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>PooledDataSource</tt>
+ for a complete list of available operations.</a>
+ </p>
+ <div class="boxed">
+ <a name="using_c3p0_registry_box" />
+ <h4>Using C3P0Registry to get a reference to a DataSource</h4>
+ <p>
+ If it's inconvenient or impossible to get a reference to your DataSource via JNDI or some other means,
+ you can find all live c3p0 DataSources using the
+ <a href="apidocs/com/mchange/v2/c3p0/C3P0Registry.html"><tt>C3P0Registry</tt></a> class, which includes
+ three static methods to help you out:
+ </p>
+ <ul>
+ <li><tt>public static Set getPooledDataSources()</tt></li>
+ <li><tt>public static Set pooledDataSourcesByName( String dataSourceName )</tt></li>
+ <li><tt>public static <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html">PooledDataSource</a>
+ pooledDataSourceByName( String dataSourceName )</tt></li>
+ </ul>
+ <p>
+ The first method will hand you the Set of all live c3p0
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html">PooledDataSources</a>. If you are sure
+ your application only makes one <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html">PooledDataSources</a>,
+ or you can distinguish between the DataSources by their configuration properties (inspected via "getters"), the
+ first method may be sufficient. Because this will not always be the case, c3p0
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html">PooledDataSources</a> have a special property called
+ <tt>dataSourceName</tt>. You can set the <tt>dataSourceName</tt> property directly when you construct your
+ DataSource, or dataSourceName can be set like any other property in a named or the default config.
+ Otherwise, <tt>dataSourceName</tt> will default to either 1) the name of your DataSource's configuration, if
+ you constructed it with a <a href="#c3p0-config.xml">named configuration</a>; or 2) a unique (but unpredicatble)
+ name if you are using the default configuration.
+ </p>
+ <p>
+ There is no guarantee that a <tt>dataSourceName</tt> will be unique. For example, if two c3p0 DataSources share
+ the same <a href="#c3p0-config.xml">named configuration</a>, and you have not set the <tt>dataSourceName</tt> programmatically, the two
+ data sources will both share the name of the configuration. To get all of the DataSources with a particular
+ <tt>dataSourceName</tt>, use <tt>pooledDataSourcesByName( ... )</tt>. If you've ensured that your DataSource's
+ name is unique (as you will generally want to do, if you intend to use
+ <a href="apidocs/com/mchange/v2/c3p0/C3P0Registry.html"><tt>C3P0Registry</tt></a> to find your DataSources),
+ you can use the convenience method <tt>pooledDataSourceByName( ... )</tt>, which will return your DataSource
+ directly, or <tt>null</tt> if no DataSource with that name is available. If you use <tt>pooledDataSourceByName( ... )</tt>
+ and more than one DataSource shares the name supplied, which one it will return is undefined.
+ </p>
+ </div>
+ <h3>
+ <a name="cleaning">Cleaning up after c3p0 PooledDataSources</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ The easy way to clean up after c3p0-created DataSources is to use the static destroy method
+ defined by the class <a href="apidocs/com/mchange/v2/c3p0/DataSources.html">DataSources</a>. Only
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>PooledDataSource</tt></a>s
+ need to be cleaned up, but
+ <a href="apidocs/com/mchange/v2/c3p0/DataSources.html">DataSources</a>.destroy( ... ) does no harm if it is called on an unpooled or non-c3p0
+ DataSource.
+ </p>
+ <div class="example">
+DataSource ds_pooled = null;
+
+try
+{
+ DataSource ds_unpooled = DataSources.unpooledDataSource("jdbc:postgresql://localhost/testdb",
+ "swaldman",
+ "test-password");
+ ds_pooled = DataSources.pooledDataSource( ds_unpooled );
+
+ // do all kinds of stuff with that sweet pooled DataSource...
+}
+finally
+{
+ DataSources.destroy( ds_pooled );
+}
+ </div>
+ <p>
+ Alternatively, c3p0's <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>PooledDataSource</tt></a>
+ interface contains a <tt>close()</tt> method
+ that you can call when you know you are finished with a DataSource. So, you can cast a c3p0
+ derived DataSource to a <tt>PooledDataSource</tt> and close it:
+ </p>
+ <div class="example">
+static void cleanup(DataSource ds) throws SQLException
+{
+ // make sure it's a c3p0 PooledDataSource
+ if ( ds instanceof PooledDataSource)
+ {
+ PooledDataSource pds = (PooledDataSource) ds;
+ pds.close();
+ }
+ else
+ System.err.println("Not a c3p0 PooledDataSource!");
+}
+ </div>
+ <p>
+ Unreferenced instances of <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>PooledDataSource</tt></a>
+ that are not <tt>close()</tt>ed by clients
+ <tt>close()</tt> themselves prior to garbage collection in their <tt>finalize()</tt> methods.
+ As always, finalization should be considered
+ a backstop and not a prompt or sure approach to resource cleanup.
+ </p>
+ <h3>
+ <a name="build_your_own">Advanced: Building your own PoolBackedDataSource</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ There is little reason for most programmers to do this, but you can build a PooledDataSource in a
+ step-by-step way by instantiating and configuring an unpooled
+ <a href="apidocs/com/mchange/v2/c3p0/DriverManagerDataSource.html"><tt>DriverManagerDataSource</tt></a>, instantiating a
+ <a href="apidocs/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.html"><tt>WrapperConnectionPoolDataSource</tt></a>
+ and setting the unpooled DataSource as its <tt>nestedDataSource</tt> property,
+ and then using that to set the <tt>connectionPoolDataSource</tt> property of a new
+ <a href="apidocs/com/mchange/v2/c3p0/PoolBackedDataSource.html"><tt>PoolBackedDataSource</tt></a>.
+ </p>
+ <p>
+ This sequence of events is primarily interesting if your driver offers an implementation of ConnectionPoolDataSource, and you'd
+ like c3p0 to use that. Rather than using c3p0's
+ <a href="apidocs/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.html"><tt>WrapperConnectionPoolDataSource</tt></a>,
+ you can create a <a href="apidocs/com/mchange/v2/c3p0/PoolBackedDataSource.html"><tt>PoolBackedDataSource</tt></a>
+ and set its <tt>connectionPoolDataSource</tt> property. Statement pooling,
+ <a href="apidocs/com/mchange/v2/c3p0/ConnectionCustomizer.html"><tt>ConnectionCustomizers</tt></a>, and many c3p0-specific properties
+ are unsupported with third party implementations of <tt>ConnectionPoolDataSource</tt>. (Third-party <tt>DataSource</tt> implementations
+ can be substituted for c3p0's
+ <a href="apidocs/com/mchange/v2/c3p0/DriverManagerDataSource.html"><tt>DriverManagerDataSource</tt></a> with no significant loss of
+ functionality.)
+ </p>
+ <h3>
+ <a name="raw_connection_ops">Advanced: Raw Connection and Statement Operations</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ JDBC drivers sometimes define vendor-specific, non-standard API on Connection and Statement implementations. C3P0 wraps
+ these Objects behind a proxies, so you cannot cast C3P0-returned Connections or Statements to the vendor-specific implementation
+ classes. C3P0 does not provide any means of accessing the raw Connections and Statements directly, because C3P0 needs to keep
+ track of Statements and ResultSets created in order to prevent resource leaks and pool
+ corruption.
+ </p>
+ <p>
+ C3P0 does provide an API that allows you to invoke non-standard methods reflectively on an underlying
+ Connection. To use it, first cast the returned Connection to a
+ <a href="apidocs/com/mchange/v2/c3p0/C3P0ProxyConnection.html"><tt>C3P0ProxyConnection</tt></a>. Then call
+ the method <tt>rawConnectionOperation</tt>, supplying the <tt>java.lang.reflect.Method</tt> object for
+ the non-standard method you wish to call as an argument. The <tt>Method</tt> you supply will be invoked
+ on the target you provide on the second argument (null for static methods), and using the arguments you
+ supply in the third argument to that function. For the target, and for any of the method arguments, you
+ can supply the special token <tt>C3P0ProxyConnection.RAW_CONNECTION</tt>, which will be replaced with
+ the underlying vendor-specific Connection object before the <tt>Method</tt> is invoked.
+ </p>
+ <p>
+ <a href="apidocs/com/mchange/v2/c3p0/C3P0ProxyStatement.html"><tt>C3P0ProxyStatement</tt></a> offers
+ an exactly analogous API.
+ </p>
+ <p>
+ Any Statements (including Prepared and CallableStatements) and ResultSets returned by raw operations
+ will be c3p0-managed, and will be properly cleaned-up on <tt>close()</tt> of the parent proxy Connection.
+ Users must take care to clean up any non-standard resources returned by a vendor-specific method.
+ </p>
+ <p>
+ Here's an example of using Oracle-specific API to call a static method on a raw Connection:
+ </p>
+ <div class="example">
+C3P0ProxyConnection castCon = (C3P0ProxyConnection) c3p0DataSource.getConnection();
+Method m = CLOB.class.getMethod("createTemporary", new Class[]{Connection.class, boolean.class, int.class});
+Object[] args = new Object[] {C3P0ProxyConnection.RAW_CONNECTION, Boolean.valueOf( true ), new Integer( 10 )};
+CLOB oracleCLOB = (CLOB) castCon.rawConnectionOperation(m, null, args);
+ </div>
+ <p>
+ <i>Note: C3P0 now includes special support for some Oracle-specific methods.
+ See <a href="#oracle-specific">Appendix F</a>.
+ </i>
+ </p>
+ <h2><a name="configuration">Configuration</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <h3>
+ <a name="programmatic_configuration"></a><a name="configuration_introduction">Introduction</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ While c3p0 does not <i>require</i> very much configuration, it is very tweakable. Most of the interesting
+ knobs and dials are represented as JavaBean properties. Following JavaBean conventions, we note that
+ if an Object has a property of type <tt>T</tt> called <tt>foo</tt>, it will have methods that look
+ like...
+ <blockquote><tt>
+ public T getFoo();<br/>
+ public void setFoo(T foo);
+ </tt></blockquote>
+ ...or both, depending upon whether the property is read-only, write-only, or read-writable.
+ </p>
+ <p>
+ There are several ways to modify c3p0 properties:
+ You can directly alter the property values associated with a particular DataSource in your code,
+ or you can configure c3p0 externally,
+ via a <a href="#c3p0_properties">simple Java properties file</a>,
+ via an <a href="#c3p0-config.xml">XML configuration file</a>, or
+ via <a href="#system_properties">System properties</a>. Configuration files
+ are normally looked up under standard names (<tt>c3p0.properties</tt> or <tt>c3p0-config.xml</tt>)
+ at the top level of an application's classpath, but the XML configuration can be placed
+ anywhere in an application's file system, if the system property com.mchange.v2.c3p0.cfg.xml is set
+ (to an absolute filesystem path).
+ </p>
+ <p>
+ DataSources are usually configured before they are used, either
+ during or immediately following their construction. c3p0 does
+ support property modifications midstream, however.
+ </p>
+ <p>
+ If you obtain a DataSource by instantiating a
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>ComboPooledDataSource</tt></a>,
+ configure it by simply calling appropriate setter methods offered by that class
+ before attempting a call to <tt>getConnection()</tt>. See the example above.
+ </p>
+ <p>
+ If you obtain a DataSource by using factory methods of
+ the utility class <a href="apidocs/com/mchange/v2/c3p0/DataSources.html"><tt>com.mchange.v2.c3p0.DataSources</tt></a>,
+ and wish to use a non-default configuration, you can supply a Map of property names (beginning with lower-case letters)
+ to property values (either as Strings or "boxed" Java primitives like Integer or Boolean).
+ </p>
+ <p>
+ All tweakable properties are documented for reference
+ in <a href="#configuration_properties">Appendix A</a>. The most basic and important c3p0 configuration
+ topics are discussed below.
+ </p>
+ <h3>
+ <a name="basic_pool_configuration">Basic Pool Configuration</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 Connection pools are very easy to configure via the following basic parameters:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#acquireIncrement">acquireIncrement</a></li>
+ <li><a class="cfg_param" href="#initialPoolSize">initialPoolSize</a></li>
+ <li><a class="cfg_param" href="#maxPoolSize">maxPoolSize</a></li>
+ <li><a class="cfg_param" href="#maxIdleTime">maxIdleTime</a></li>
+ <li><a class="cfg_param" href="#minPoolSize">minPoolSize</a></li>
+ </ul>
+ <p>
+ <tt>initialPoolSize</tt>, <tt>minPoolSize</tt>, <tt>maxPoolSize</tt>
+ define the number of Connections that will be pooled. Please ensure that
+ <tt>minPoolSize <= maxPoolSize</tt>. Unreasonable values of <tt>initialPoolSize</tt> will
+ be ignored, and <tt>minPoolSize</tt> will be used instead.
+ </p>
+ <p>
+ Within the range between <tt>minPoolSize</tt> and <tt>maxPoolSize</tt>, the number of Connections in
+ a pool varies according to usage patterns. The number of Connections increases whenever a Connection
+ is requested by a user, no Connections are available, and the pool has not yet reached <tt>maxPoolSize</tt>
+ in the number of Connections managed. Since Connection acquisition is very slow, it is almost always useful to
+ increase the number of Connections eagerly, in batches, rather than forcing each client to wait for a new
+ Connection to provoke a single acquisition when the load is increasing. <tt>acquireIncrement</tt> determines
+ how many Connections a c3p0 pool will attempt to acquire when the pool has run out of Connections. (Regardless
+ of <tt>acquireIncrement</tt>, the pool will never allow <tt>maxPoolSize</tt> to be exceeded.)
+ </p>
+ <p>
+ The number of Connections in a pool decreases whenever a pool tests a Connection and finds it to be broken (see
+ <a href="#configuring_connection_testing">Configuring Connection Testing</a> below), or when a Connection is expired
+ by the pool after sitting idle for a period of time, or for being too old (See <a href="#managing_pool_size">Managing Pool Size and Connection Age</a>.)
+ </p>
+ <h3>
+ <a name="managing_pool_size">Managing Pool Size and Connection Age</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Different applications have different needs with regard to trade-offs between performance, footprint, and reliability. C3P0
+ offers a wide variety of options for controlling how quickly pools that have grown large under load revert to <tt>minPoolSize</tt>,
+ and whether "old" Connections in the pool should be proactively replaced to maintain their reliablity.
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#maxConnectionAge">maxConnectionAge</a></li>
+ <li><a class="cfg_param" href="#maxIdleTime">maxIdleTime</a></li>
+ <li><a class="cfg_param" href="#maxIdleTimeExcessConnections">maxIdleTimeExcessConnections</a></li>
+ </ul>
+ <p>
+ By default, pools will never expire Connections. If you wish
+ Connections to be expired over time in order to maintain "freshness",
+ set <tt>maxIdleTime</tt> and/or <tt>maxConnectionAge</tt>. <tt>maxIdleTime</tt> defines how many seconds a
+ Connection should be permitted to go unused before being culled from the pool. <tt>maxConnectionAge</tt>
+ forces the pool to cull any Connections that were acquired from the database more than the set number of
+ seconds in the past.
+ </p>
+ <p>
+ <tt>maxIdleTimeExcessConnections</tt> is about minimizing the number of Connections held by c3p0 pools
+ when the pool is not under load. By default, c3p0 pools grow under load, but only shrink if Connections
+ fail a Connection test or are expired away via the parameters described above. Some users want their
+ pools to quickly release unnecessary Connections after a spike in usage that forces a large pool size.
+ You can achieve this by setting <tt>maxIdleTimeExcessConnections</tt> to a value much shorter than
+ <tt>maxIdleTime</tt>, forcing Connections beyond your set minimum size to be released if they sit idle
+ for more than a short period of time.
+ </p>
+ <p>
+ Some general advice about all of these timeout parameters: Slow down! The point of Connection pooling is to
+ bear the cost of acquiring a Connection only once, and then to reuse the Connection many, many times.
+ Most databases support Connections that remain open for hours at a time. There's no need to churn through
+ all your Connections every few seconds or minutes. Setting <tt>maxConnectionAge</tt> or <tt>maxIdleTime</tt> to 1800 (30 minutes)
+ is quite aggressive. For most databases, several hours may be more appropriate. You can ensure the reliability
+ of your Connections by testing them, rather than by tossing them. (see
+ <a href="#configuring_connection_testing">Configuring Connection Testing</a>.) The only one of these parameters
+ that should generally be set to a few minutes or less is <tt>maxIdleTimeExcessConnections</tt>.
+ </p>
+ <h3>
+ <a name="configuring_connection_testing">Configuring Connection Testing</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 can be configured to test the Connections that it pools in a variety of ways, to
+ minimize the likelihood that your application will see broken or "stale" Connections.
+ Pooled Connections can go bad for a variety of reasons -- some JDBC drivers intentionally
+ "time-out" long-lasting database Connections; back-end databases or networks sometimes go down
+ "stranding" pooled Connections; and Connections can simply become corrupted over time and use due
+ to resource leaks, driver bugs, or other causes.
+ </p>
+ <p>
+ c3p0 provides users a great deal of flexibility in testing Connections, via the following
+ configuration parameters:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#automaticTestTable">automaticTestTable</a></li>
+ <li><a class="cfg_param" href="#connectionTesterClassName">connectionTesterClassName</a></li>
+ <li><a class="cfg_param" href="#idleConnectionTestPeriod">idleConnectionTestPeriod</a></li>
+ <li><a class="cfg_param" href="#preferredTestQuery">preferredTestQuery</a></li>
+ <li><a class="cfg_param" href="#testConnectionOnCheckin">testConnectionOnCheckin</a></li>
+ <li><a class="cfg_param" href="#testConnectionOnCheckout">testConnectionOnCheckout</a></li>
+ </ul>
+ <p>
+ <tt>idleConnectionTestPeriod</tt>, <tt>testConnectionOnCheckout</tt>, and
+ <tt>testConnectionOnCheckin</tt> control <u>when</u> Connections will be tested.
+ <tt>automaticTestTable</tt>, <tt>connectionTesterClassName</tt>, and <tt>preferredTestQuery</tt> control <u>how</u> they will be tested.
+ </p>
+ <p>
+ When configuring
+ Connection testing, first try to minimize the cost of each test. By default, Connections are tested
+ by calling the <tt>getTables()</tt> method on a Connection's associated <tt>DatabaseMetaData</tt>
+ object. This has the advantage of working with any database, and regardless of the database schema. However, empirically
+ a <tt>DatabaseMetaData.getTables()</tt> call is often much slower than a simple database query.
+ </p>
+ <p>
+ The most convenient way to speed up Connection testing is to define the parameter <tt>automaticTestTable</tt>. Using the name
+ you provide, c3p0 will create an empty table, and make a simple query against it to test the database. Alternatively, if your
+ database schema
+ is fixed prior to your application's use of the database, you can simply define a test query with the <tt>preferredTestQuery</tt>
+ parameter. Be careful, however. Setting <tt>preferredTestQuery</tt> will lead to errors as Connection tests fail
+ if the query target table does not exist in your database table <i>prior to initialization of your DataSource</i>.
+ </p>
+ <p>
+ Advanced users may define any kind of Connection testing they wish, by implementing a
+ <a href="apidocs/com/mchange/v2/c3p0/ConnectionTester.html">ConnectionTester</a> and supplying the
+ fully qualified name of the class as <tt>connectionTesterClassName</tt>. If you'd like your custom ConnectionTesters
+ to honor and support the <tt>preferredTestQuery</tt> and <tt>automaticTestTable</tt> parameters, implement
+ <a href="apidocs/com/mchange/v2/c3p0/UnifiedConnectionTester.html">UnifiedConnectionTester</a>, most conveniently by extending
+ <a href="apidocs/com/mchange/v2/c3p0/AbstractConnectionTester.html">AbstractConnectionTester</a>. See the <a href="apidocs/index.html">api docs</a>
+ for more information.
+ </p>
+ <p>
+ The most reliable time to test Connections is on check-out. But this is also the most costly choice
+ from a client-performance perspective. Most applications should work quite reliably using a combination of
+ <tt>idleConnectionTestPeriod</tt> and <tt>testConnectionsOnCheckIn</tt>. Both the idle test and the check-in
+ test are performed asynchronously, which leads to better performance, both perceived and actual.
+ </p>
+ <p>
+ Note that for many applications, high performance is more important than the risk of an occasional database exception.
+ In its default configuration, c3p0 does no Connection testing at all. Setting a fairly long
+ <tt>idleConnectionTestPeriod</tt>, and not testing on checkout and check-in at all is an excellent, high-performance
+ approach.
+ </p>
+ <h3>
+ <a name="configuring_statement_pooling">Configuring Statement Pooling</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 implements transparent PreparedStatement pooling as defined by the JDBC spec. Under some circumstances,
+ statement pooling can dramatically improve application performance. Under other circumstances, the overhead of
+ statement pooling can slightly harm
+ performance. Whether and how much statement pooling will help depends on how much
+ parsing, planning, and optimizing of queries your databases does when the statements are prepared.
+ Databases (and JDBC drivers) vary widely
+ in this respect. It's a good idea to benchmark your application with and without statement pooling to
+ see if and how much it helps.
+ </p>
+ <p>
+ You configure statement pooling in c3p0 via the following
+ configuration parameters:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#maxStatements">maxStatements</a></li>
+ <li><a class="cfg_param" href="#maxStatementsPerConnection">maxStatementsPerConnection</a></li>
+ </ul>
+ <p>
+ <tt>maxStatements</tt> is JDBC's standard parameter for controlling statement pooling. <tt>maxStatements</tt> defines the
+ total number <tt>PreparedStatements</tt> a DataSource will cache. The pool will destroy the least-recently-used PreparedStatement
+ when it hits this limit. This sounds simple, but it's actually a strange approach, because
+ cached statements conceptually belong to individual Connections; they are not global resources. To figure out a size
+ for <tt>maxStatements</tt> that does not "churn" cached statements, you need to consider the number of <i>frequently used</i>
+ PreparedStatements in your application, and multiply that by the number of Connections you expect in the pool (<tt>maxPoolSize</tt>
+ in a busy application).
+ </p>
+ <p>
+ <tt>maxStatementsPerConnection</tt> is a non-standard configuration parameter that makes a bit more
+ sense conceptually. It defines how many statements each pooled Connection is allowed to own.
+ You can set this to a bit more than the number of <tt>PreparedStatements</tt> your application <i>frequently</i>
+ uses, to avoid churning.
+ </p>
+ <p>
+ If either of these parameters are greater than zero, statement pooling will be enabled. If both
+ parameters are greater than zero, both limits will be enforced. If only one is greater than zero, statement pooling
+ will be enabled, but only one limit will be enforced.
+ </p>
+ <h3>
+ <a name="configuring_recovery">Configuring Recovery From Database Outages</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 DataSources are designed (and configured by default) to recover from temporary database outages, such as
+ those which occur during a database restart or brief loss of network connectivity.
+ You can affect how c3p0 handles errors in acquiring Connections via the following
+ configurable properties:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#acquireRetryAttempts">acquireRetryAttempts</a></li>
+ <li><a class="cfg_param" href="#acquireRetryDelay">acquireRetryDelay</a></li>
+ <li><a class="cfg_param" href="#breakAfterAcquireFailure">breakAfterAcquireFailure</a></li>
+ </ul>
+ <p>
+ When a c3p0 DataSource attempts and fails to acquire a Connection, it will retry up
+ to <tt>acquireRetryAttempts</tt> times, with a delay of <tt>acquireRetryDelay</tt>
+ between each attempt. If all attempts fail, any clients waiting for Connections from
+ the DataSource will see an Exception, indicating that a Connection could not be acquired.
+ Note that clients do not see any Exception until a full round of attempts fail, which
+ may be some time after the initial Connection attempt. If <tt>acquireRetryAttempts</tt>
+ is set to 0, c3p0 will attempt to acquire new Connections indefinitely, and calls to
+ <tt>getConnection()</tt> may block indefinitely waiting for a successful acquisition.
+ </p>
+ <p>
+ Once a full round of acquisition attempts fails, there are two possible policies. By
+ default, the c3p0 DataSource will remain active, and will try again to acquire Connections
+ in response to future requests for Connections. If you set <tt>breakAfterAcquireFailure</tt>
+ to <tt>true</tt>, the DataSource will consider itself broken after a failed round of
+ Connection attempts, and future client requests will fail immediately.
+ </p>
+ <p>
+ Note that if a database restart occurs, a pool may contain previously acquired but now
+ stale Connections. By default, these stale Connections will only be detected and
+ purged lazily, when an application attempts to use them, and sees an Exception. Setting
+ <tt>maxIdleTime</tt> or <tt>maxConnectionAge</tt> can help speed up the replacement of
+ broken Connections. (See <a href="#managing_pool_size">Managing ConnectionAge</a>.)
+ If you wish to avoid application Exceptions entirely, you must adopt a connection testing strategy that
+ is likely to detect stale Connections prior to their delivery to clients. (See
+ "<a href="#configuring_connection_testing">Configuring Connection Testing</a>".) Even
+ with active Connection testing (<tt>testConnectionsOnCheckout</tt> set to <tt>true</tt>, or
+ <tt>testConnectionsOnCheckin</tt> and a short <tt>idleConnectionTestPeriod</tt>), your
+ application may see occasional Exceptions on database restart, for example if the restart
+ occurs after a Connection to the database has already been checked out.
+ </p>
+ <h3>
+ <a name="connection_customizers">Managing Connection Lifecycles with Connection Customizer</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Application frequently wish to set up Connections in some standard, reusable way immediately after
+ Connection acquisitions. Examples of this include setting-up character encodings, or date and time
+ related behavior, using vendor-specific APIs or non-standard SQL statement executions. Occasionally
+ it is useful to override the default values of standard Connection properties such as <tt>transactionIsolation</tt>,
+ <tt>holdability</tt>, or <tt>readOnly</tt>. c3p0 provides a "hook" interface that you can implement,
+ which gives you the opportunity to modify or track Connections just after they are checked out from the
+ database, immediately just prior to being handed to clients on checkout, just prior to being returned
+ to the pool on check-in, and just prior to final destruction by the pool. The Connections handed
+ to ConnectionCustomizers are raw, physical Connections, with all vendor-specific API accessible.
+ See the API docs for <a href="apidocs/com/mchange/v2/c3p0/ConnectionCustomizer.html"><tt>ConnectionCustomizer</tt></a>.
+ </p>
+ <p>
+ To install a <tt>ConnectionCustomizer</tt> just implement the interface, make your class accessible
+ to c3p0's ClassLoader, and set the configuration parameter below:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#connectionCustomizerClassName">connectionCustomizerClassName</a></li>
+ </ul>
+ <p>
+ ConnectionCustomizers are required to be immutable classes with public no argument constructors.
+ They shouldn't store any state. For (rare) applications that wish to track the behavior of individual
+ DataSources with ConnectionCustomizers, the lifecycle methods each accept a DataSource-specific
+ "identityToken", which is unique to each PooledDataSource.
+ </p>
+ <p>
+ Below is a sample <tt>ConnectionCustomizer</tt>. Implementations that do not need to override all
+ four <tt>ConnectionCustomizer</tt> methods can extend
+ <a href="apidocs/com/mchange/v2/c3p0/AbstractConnectionCustomizer.html"><tt>AbstractConnectionCustomizer</tt></a>
+ to inherit no-op implementations of all methods.
+ </p>
+ <div class="example">
+import com.mchange.v2.c3p0.*;
+import java.sql.Connection;
+
+public class VerboseConnectionCustomizer
+{
+ public void onAcquire( Connection c, String pdsIdt )
+ {
+ System.err.println("Acquired " + c + " [" + pdsIdt + "]");
+
+ // override the default transaction isolation of
+ // newly acquired Connections
+ c.setTransactionIsolation( Connection.REPEATABLE_READ );
+ }
+
+ public void onDestroy( Connection c, String pdsIdt )
+ { System.err.println("Destroying " + c + " [" + pdsIdt + "]"); }
+
+ public void onCheckOut( Connection c, String pdsIdt )
+ { System.err.println("Checked out " + c + " [" + pdsIdt + "]"); }
+
+ public void onCheckIn( Connection c, String pdsIdt )
+ { System.err.println("Checking in " + c + " [" + pdsIdt + "]"); }
+}
+ </div>
+
+ <h3>
+ <a name="configuring_unresolved">Configuring Unresolved Transaction Handling</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Connections checked into a pool cannot have any unresolved transactional work associated with them.
+ If users have set <tt>autoCommit</tt> to <tt>false</tt> on a Connection, and c3p0 cannot guarantee
+ that there is no pending transactional work, c3p0 must either <tt>rollback()</tt> or <tt>commit()</tt>
+ on check-in (when a user calls <tt>close()</tt>). The JDBC spec is (unforgivably) silent on the question
+ of whether unresolved work should be committed or rolled back on Connection close. By default, c3p0
+ rolls back unresolved transactional work when a user calls <tt>close()</tt>.
+ </p>
+ <p>
+ You can adjust this behavior via the following configuration properties:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#autoCommitOnClose">autoCommitOnClose</a></li>
+ <li><a class="cfg_param" href="#forceIgnoreUnresolvedTransactions">forceIgnoreUnresolvedTransactions</a></li>
+ </ul>
+ <p>
+ If you wish c3p0 to allow unresolved transactional work to commit on checkin, set <tt>autoCommitOnClose</tt>
+ to true. If you wish c3p0 to leave transaction management to you, and neither commit nor rollback (nor modify
+ the state of Connection <tt>autoCommit</tt>), you may set <tt>forceIgnoreUnresolvedTransactions</tt> to true. Setting
+ <tt>forceIgnoreUnresolvedTransactions</tt> is strongly discouraged, because if clients are not careful to
+ commit or rollback themselves prior to close(), or do not set Connection <tt>autoCommit</tt> consistently, bizarre
+ unreproduceable behavior and database lockups can occur.
+ </p>
+ <h3>
+ <a name="configuring_to_debug_and_workaround_broken_clients">Configuring to Debug and Workaround Broken Client Applications</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ Sometimes client applications are sloppy about close()ing all Connections they check out. Eventually,
+ the pool grows to <tt>maxPoolSize</tt>, and then runs out of Connections, because of these bad clients.
+ </p>
+ <p>
+ The right way to address this problem is to fix the client application. <tt>c3p0</tt> can help you debug,
+ by letting you know where Connections are checked out that occasionally don't get checked in. In rare and
+ unfortunate situations, development of the client application is closed, and even though it is buggy, you cannot fix it.
+ c3p0 can help you work around the broken application, preventing it from exhausting the pool.
+ </p>
+ <p>
+ The following parameters can help you debug or workaround broken client applications.
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#debugUnreturnedConnectionStackTraces">debugUnreturnedConnectionStackTraces</a></li>
+ <li><a class="cfg_param" href="#unreturnedConnectionTimeout">unreturnedConnectionTimeout</a></li>
+ </ul>
+ <p>
+ <tt>unreturnedConnectionTimeout</tt> defines a limit (in seconds) to how long a Connection may remain checked out.
+ If set to a nozero value, unreturned, checked-out Connections that exceed this limit will be summarily destroyed,
+ and then replaced in the pool.
+ Obviously, you must take care to set this parameter to a value large enough that all intended operations
+ on checked out Connections have time to complete.
+ You can use this parameter to merely workaround unreliable client apps that fail to
+ close() Connections.
+ </p>
+ <p>
+ Much better than working-around is fixing. If, <i>in addition to setting</i> <tt>unreturnedConnectionTimeout</tt>,
+ you set <tt>debugUnreturnedConnectionStackTraces</tt> to <tt>true</tt>,
+ then a stack trace will be captured
+ each time a Connection is checked-out. Whenever an unreturned Connection times out, that stack trace will be
+ printed, revealing where a Connection was checked out that was not checked in promptly. <tt>debugUnreturnedConnectionStackTraces</tt>
+ is intended to be used only for debugging, as capturing a stack trace can slow down Connection check-out.
+ </p>
+ <h3>
+ <a name="other_ds_configuration">Other DataSource Configuration</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ See <a href="#configuration_properties">Appendix A</a> for information about the following configuration properties:
+ </p>
+ <ul>
+ <li><a class="cfg_param" href="#checkoutTimeout">checkoutTimeout</a></li>
+ <li><a class="cfg_param" href="#factoryClassLocation">factoryClassLocation</a></li>
+ <li><a class="cfg_param" href="#maxAdministrativeTaskTime">maxAdministrativeTaskTime</a></li>
+ <li><a class="cfg_param" href="#numHelperThreads">numHelperThreads</a></li>
+ <li><a class="cfg_param" href="#usesTraditionalReflectiveProxies">usesTraditionalReflectiveProxies</a></li>
+ </ul>
+ <p>
+ <tt>numHelperThreads</tt> and <tt>maxAdministrativeTaskTime</tt> help to configure the behavior
+ of DataSource thread pools. By default, each DataSource has only three associated helper threads.
+ If performance seems to drag under heavy load, or if you observe via JMX or direct inspection of
+ a <tt>PooledDataSource</tt>, that the number of "pending tasks" is usually greater than zero, try
+ increasing <tt>numHelperThreads</tt>. <tt>maxAdministrativeTaskTime</tt> may be useful for users
+ experiencing tasks that hang indefinitely and "APPARENT DEADLOCK" messages. (See Appendix A for more.)
+ </p>
+ <p>
+ <tt>checkoutTimeout</tt> limits how long a client will wait for a Connection, if all Connections are
+ checked out and one cannot be supplied immediately. <tt>usesTraditionalReflectiveProxies</tt>, which is
+ of little practical use, permits you to use an old, now superceded implementation of C3P0-generated proxy objects. (C3P0
+ used to use reflective, dynamic proxies. Now, for enhanced performance, it uses code-generated, nonrefective
+ implementations.) <tt>factoryClassLocation</tt> can be used to indicate where a URL from which c3p0 classes
+ can be downloaded, if c3p0 DataSources will be retrieved as References from a JNDI DataSource by clients
+ who do not have c3p0 locally installed.
+ </p>
+ <h3>
+ <a name="jmx_configuration_and_management">Configuring and Managing c3p0 via JMX</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ If JMX libraries and a JMX MBeanServer are available in your environment (they are include in JDK 1.5 and above),
+ you can inspect and configure your c3p0 datasources via a JMX administration tool (such as jconsole, bundled with
+ jdk 1.5). You will find that c3p0 registers MBeans under <tt>com.mchange.v2.c3p0</tt>, one with statistics about the
+ library as a whole (called <tt>C3P0Registry</tt>), and an MBean for each <tt>PooledDataSource</tt> you deploy. You can view and
+ modify your DataSource's configuration properties, track the activity of Connection, Statement, and Thread pools, and reset
+ pools and DataSources via the <tt>PooledDataSource</tt> MBean. (You may wish to view the API docs of
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html"><tt>PooledDataSource</tt></a> for documentation of
+ the available operations.)
+ </p>
+ <p>
+ If you do not want c3p0 to register MBeans with your JMX environment, you can suppress this
+ behavior with the following, set either as a System property or in <tt>c3p0.properties</tt>:
+ </p>
+ <div class="example">
+com.mchange.v2.c3p0.management.ManagementCoordinator=com.mchange.v2.c3p0.management.NullManagementCoordinator
+ </div>
+ <h3>
+ <a name="configuring_logging">Configuring Logging</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 uses a custom logging library similar to jakarta commons-logging. Log messages can be directed to
+ the popular log4j logging library, to the standard logging facility introduced with jdk1.4, or to
+ <tt>System.err</tt>. Nearly all configuration should be done at the level of your preferred logging
+ library. There are a very few configuration options specific to c3p0's logging, and usually the defaults
+ will be fine. Logging-related parameters
+ may be placed in your <tt>c3p0.properties</tt> file, in a file called <tt>mchange-log.properties</tt> at
+ the top-level of your classpath, or they may be defined as System properties. (The logging properties defined
+ below may <u>not</u> be defined in <tt>c3p0-config.xml</tt>!) See the
+ <a href="#log_properties_box">box</a> below.
+ </p>
+ <p>
+ c3p0's logging behavior is affected by certain build-time options. If build-option <tt>c3p0.debug</tt> is set
+ to <tt>false</tt>, all messages at a logging level below INFO will be suppressed. Build-option <tt>c3p0.trace</tt> controls how fine-grained c3p0's below
+ INFO level reporting will be. For the moment, distributed
+ c3p0 binaries are compiled with <tt>debug</tt> set to <tt>true</tt> and <tt>trace</tt> set to its maximum level of <tt>10</tt>.
+ But binaries may eventually be
+ distributed with <tt>debug</tt> set to <tt>false</tt>. (For the moment, the performance impact of the logging level-checks seems
+ very small, and it's most flexible to compile in all the messages, and let your logging library control which are emitted.) When
+ c3p0 starts up, it emits the build-time values of debug and trace, along with the version and build time.
+ </p>
+ <dl class="log_properties">
+ <a name="log_properties_box"></a>
+ <dt><a name="com.mchange.v2.log.MLog" />com.mchange.v2.log.MLog</dt>
+ <dd>
+ <div class="propdesc">
+ Determines which library c3p0 will output log messages to. By default, if log4j is available,
+ it will use that library, otherwise if jdk1.4 logging apis are available it will use those,
+ and if neither are available, it will use a simple fallback that logs to <tt>System.err</tt>.
+ If you want to directly control which library is used, you may set this property to one of:
+ <ul>
+ <li><tt>com.mchange.v2.log.log4j.Log4jMLog</tt></li>
+ <li><tt>com.mchange.v2.log.jdk14logging.Jdk14MLog</tt></li>
+ <li><tt>com.mchange.v2.log.FallbackMLog</tt></li>
+ </ul>
+ You may also set this property to a comma separated list of the above alternatives, to
+ define an order of preference among logging libraries.
+ </div>
+ </dd>
+ <dt><a name="com.mchange.v2.log.NameTransformer" />com.mchange.v2.log.NameTransformer</dt>
+ <dd>
+ <div class="propdesc">
+ By default, c3p0 uses very fine-grained logging, in general with one logger for each
+ c3p0 class. For a variety of reasons, some users may prefer fewer, more global loggers.
+ You may opt for one-logger-per-package by setting <tt>com.mchange.v2.log.NameTransformer</tt>
+ to the value <tt>com.mchange.v2.log.PackageNames</tt>. Advanced users can also define
+ other strategies for organizing the number and names of loggers by setting this variable
+ to the fully-qualified class name of a custom implementation of the
+ <tt>com.mchange.v2.log.NameTransformer</tt> interface.
+ </div>
+ </dd>
+ <dt><a name="com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL" />com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL</dt>
+ <dd>
+ <div class="propdesc">
+ If, whether by choice or by necessity, you are using c3p0's <tt>System.err</tt> fallback logger, you can
+ use this parameter to control how detailed c3p0's logging should be. Any of the following values (taken
+ from the jdk1.4 logging library) are acceptable:
+ <ul>
+ <li><tt>OFF</tt></li>
+ <li><tt>SEVERE</tt></li>
+ <li><tt>WARNING</tt></li>
+ <li><tt>INFO</tt></li>
+ <li><tt>CONFIG</tt></li>
+ <li><tt>FINE</tt></li>
+ <li><tt>FINER</tt></li>
+ <li><tt>FINEST</tt></li>
+ <li><tt>ALL</tt></li>
+ </ul>
+ This property defaults to <tt>INFO</tt>.
+ </div>
+ </dd>
+ </dl>
+ </div>
+ <h2><a name="performance">Performance</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ Enhanced performance is the purpose of Connection and Statement pooling, and a
+ major goal of the c3p0 library. For most applications, Connection pooling
+ will provide a significant performance gain, especially if you are acquiring
+ an unpooled Connection for each client access. If you are letting a single,
+ shared Connection serve many clients to avoid Connection acquisition overhead,
+ you may suffer performance issues and problems managing transactions when
+ your Connection is under concurrent load; Connection pooling will enable you
+ to switch to a one Connection-per-client model with little or no cost.
+ If you are writing Enterprise Java Beans, you may be tempted to acquire a
+ Connection once and not return it until the bean is about to be destroyed or
+ passivated. But this can be resource-costly, as dormant pooled
+ beans needlessly hold the Connection's network and database resources.
+ Connection pooling permits beans to only "own"
+ a Connection while they are using it.
+ </p>
+ <p>
+ But, there are performance costs to c3p0 as well. In order to implement
+ automatic cleanup of unclosed <tt>ResultSets</tt> and <tt>Statements</tt> when parent resources
+ are returned to pools, all client-visible <tt>Connections</tt>, <tt>ResultSets</tt>, <tt>Statements</tt>
+ are really wrappers around objects provided by an underlying unpooled DataSource
+ or "traditional" JDBC driver. Thus, there is some extra overhead to all JDBC calls.
+ </p>
+ <p>
+ Some attention has been paid to minimizing the "wrapper" overhead of c3p0. In
+ my environment, the wrapper overhead amounts from several hundreths to several
+ thousandths of the cost of Connection acquisition, so unless you are making
+ many, many JDBC calls in fast succession, there will be a net
+ gain in performance and resource-utilization efficiency.
+ Significantly, the overhead associated with ResultSet operations (where
+ one might iterate through a table with thousands of records) appears to be
+ negligibly small.
+ </p>
+ </div>
+ <h2><a name="known_shortcomings">Known Shortcomings</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <ul>
+ <li>
+ <p>
+ Connections and Statements are pooled on a per-authentication basis.
+ So, if one pool-backed DataSource is used to acquire Connections both
+ for [<tt>user</tt>=alice, <tt>password</tt>=secret1] and [<tt>user</tt>=bob, <tt>password</tt>=secret2],
+ there will be two distinct pools, and the DataSource might in the
+ worst case manage twice the number of Connections specified by the
+ <tt>maxPoolSize</tt> property.
+ </p>
+ <p>
+ This fact is a natural consequence of the definition of the DataSource spec (which
+ allows Connections to be acquired with multiple user authentications), and the
+ requirement that all Connections in a single pool be functionally identical.
+ This "issue" will not be changed or fixed. It's noted here just so you understand
+ what's going on.
+ </p>
+ </li>
+ <li>
+ <p>
+ The overhead of Statement pooling is too high. For drivers that
+ do not perform significant preprocessing of PreparedStatements, the
+ pooling overhead outweighs any savings. Statement pooling is thus
+ turned off by default. If your driver does preprocess <tt>PreparedStatements</tt>,
+ especially if it does so via IPC with the RDBMS, you will probably
+ see a significant performance gain by turning Statement pooling on. (Do this by
+ setting the configuration property <tt>maxStatements</tt> or <tt>maxStatementsPerConnection</tt>
+ to a value greater than zero.).
+ </p>
+ </li>
+ </ul>
+ </div>
+ <h2><a name="feedback_and_support">Feedback and Support</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ Please provide any and all feedback to <<a href="mailto:swaldman at mchange.com">swaldman at mchange.com</a>>!
+ Also, feel free to join and ask questions on the <tt>c3p0-users</tt> mailing list.
+ Sign up at <a href="http://sourceforge.net/projects/c3p0/">http://sourceforge.net/projects/c3p0/</a>
+ </p>
+ <p>
+ Thank you for using c3p0!!!
+ </p>
+ </div>
+ <h2><a name="configuration_properties">Appendix A: Configuration Properties</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ c3p0 configuration properties can be divided into <a href="#javabeans-style-properties">JavaBeans-style Properties</a> and
+ <a href="#other-properties">Other Properties</a>.
+ </p>
+ <h3>
+ <a name="javabeans-style-properties">JavaBeans-style Properties</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ The following properties can be set directly in code as JavaBeans properties,
+ via a <a href="#system_properties">System properties</a> or a <a href="#c3p0_properties"><tt>c3p0.properties</tt></a> file
+ (with <tt>c3p0.</tt> prepended to
+ the property name), or in a <a href="#c3p0-config.xml"><tt>c3p0-config.xml</tt></a> file. See the section on
+ <a href="#configuration">Configuration</a> above.
+ Click on the property name for a full description.
+ </p>
+ <table class="beanPropSummaryTable">
+ <tr>
+ <td>
+ <a href="#acquireIncrement">acquireIncrement</a><br/>
+ <a href="#acquireRetryAttempts">acquireRetryAttempts</a><br/>
+ <a href="#acquireRetryDelay">acquireRetryDelay</a><br/>
+ <a href="#autoCommitOnClose">autoCommitOnClose</a><br/>
+ <a href="#automaticTestTable">automaticTestTable</a><br/>
+ <a href="#breakAfterAcquireFailure">breakAfterAcquireFailure</a><br/>
+ <a href="#checkoutTimeout">checkoutTimeout</a><br/>
+ <a href="#connectionCustomizerClassName">connectionCustomizerClassName</a><br/>
+ <a href="#connectionTesterClassName">connectionTesterClassName</a><br/>
+ <a href="#debugUnreturnedConnectionStackTraces">debugUnreturnedConnectionStackTraces</a><br/>
+ <a href="#factoryClassLocation">factoryClassLocation</a><br/>
+ </td>
+ <td>
+ <a href="#forceIgnoreUnresolvedTransactions">forceIgnoreUnresolvedTransactions</a><br/>
+ <a href="#idleConnectionTestPeriod">idleConnectionTestPeriod</a><br/>
+ <a href="#initialPoolSize">initialPoolSize</a><br/>
+ <a href="#maxAdministrativeTaskTime">maxAdministrativeTaskTime</a><br/>
+ <a href="#maxConnectionAge">maxConnectionAge</a><br/>
+ <a href="#maxIdleTime">maxIdleTime</a><br/>
+ <a href="#maxIdleTimeExcessConnections">maxIdleTimeExcessConnections</a><br/>
+ <a href="#maxPoolSize">maxPoolSize</a><br/>
+ <a href="#maxStatements">maxStatements</a><br/>
+ <a href="#maxStatementsPerConnection">maxStatementsPerConnection</a><br/>
+ <a href="#minPoolSize">minPoolSize</a><br/>
+ </td>
+ <td>
+ <a href="#numHelperThreads">numHelperThreads</a><br/>
+ <a href="#overrideDefaultUser">overrideDefaultUser</a><br/>
+ <a href="#overrideDefaultPassword">overrideDefaultPassword</a><br/>
+ <a href="#password">password</a><br/>
+ <a href="#preferredTestQuery">preferredTestQuery</a><br/>
+ <a href="#propertyCycle">propertyCycle</a><br/>
+ <a href="#testConnectionOnCheckin">testConnectionOnCheckin</a><br/>
+ <a href="#testConnectionOnCheckout">testConnectionOnCheckout</a><br/>
+ <a href="#unreturnedConnectionTimeout">unreturnedConnectionTimeout</a><br/>
+ <a href="#user">user</a><br/>
+ <a href="#usesTraditionalReflectiveProxies">usesTraditionalReflectiveProxies</a><br/>
+ </td>
+ </tr>
+ </table>
+ <dl class="properties">
+ <dt><a name="acquireIncrement" />acquireIncrement</dt>
+ <dd>
+ <div class="default">Default: 3</div>
+ <div class="propdesc">
+ Determines how many connections at a time c3p0 will try to acquire when the pool is exhausted.
+ [See <a href="#basic_pool_configuration">"Basic Pool Configuration"</a>]
+ </div>
+ </dd>
+ <dt><a name="acquireRetryAttempts" />acquireRetryAttempts</dt>
+ <dd>
+ <div class="default">Default: 30</div>
+ <div class="propdesc">
+ Defines how many times c3p0 will try to acquire a new Connection from the database before giving up. If
+ this value is less than or equal to zero, c3p0 will keep trying to fetch a Connection indefinitely.
+ [See <a href="#configuring_recovery">"Configuring Recovery From Database Outages"</a>]
+ </div>
+ </dd>
+ <dt><a name="acquireRetryDelay" />acquireRetryDelay</dt>
+ <dd>
+ <div class="default">Default: 1000</div>
+ <div class="propdesc">
+ Milliseconds, time c3p0 will wait between acquire attempts.
+ [See <a href="#configuring_recovery">"Configuring Recovery From Database Outages"</a>]
+ </div>
+ </dd>
+ <dt><a name="autoCommitOnClose" />autoCommitOnClose</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ The JDBC spec is unforgivably silent on what should happen to unresolved, pending
+ transactions on Connection close. C3P0's default policy is to rollback any uncommitted, pending
+ work. (I think this is absolutely, undeniably the right policy, but there is no consensus among JDBC driver vendors.)
+ Setting <tt>autoCommitOnClose</tt> to true causes uncommitted pending work to be committed, rather than rolled
+ back on Connection close. [<i>Note: Since the spec is absurdly unclear on this question, application authors who wish
+ to avoid bugs and inconsistent behavior should ensure that all transactions are explicitly either committed or
+ rolled-back before close is called.</i>]
+ [See <a href="#configuring_unresolved">"Configuring Unresolved Transaction Handling"</a>]
+ </div>
+ </dd>
+ <dt><a name="automaticTestTable" />automaticTestTable</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ If provided, c3p0 will create an empty table of the specified name, and use queries against that table to
+ test the Connection. If <tt>automaticTestTable</tt> is provided, c3p0 will generate its own test query, therefore
+ any <tt>preferredTestQuery</tt> set will be ignored. You should not work with the named table after c3p0 creates
+ it; it should be strictly for c3p0's use in testing your Connection. (If you define your own ConnectionTester, it
+ must implement the <a href="apidocs/com/mchange/v2/c3p0/QueryConnectionTester.html">QueryConnectionTester</a>
+ interface for this parameter to be useful.) [See <a href="#configuring_connection_testing">"Configuring Connection Testing"</a>]
+ </div>
+ </dd>
+ <dt><a name="breakAfterAcquireFailure" />breakAfterAcquireFailure</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ If true, a pooled DataSource will declare itself broken and be permanently closed if
+ a Connection cannot be obtained from the database after making <tt>acquireRetryAttempts</tt> to acquire one.
+ If false, failure to obtain a Connection will cause all Threads waiting for the pool to acquire a Connection
+ to throw an Exception, but the DataSource will remain valid, and will attempt to acquire again following
+ a call to <tt>getConnection()</tt>.
+ [See <a href="#configuring_recovery">"Configuring Recovery From Database Outages"</a>]
+ </div>
+ </dd>
+ <dt><a name="checkoutTimeout" />checkoutTimeout</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or acquired
+ when the pool is exhausted. Zero means wait indefinitely. Setting any positive value will cause the getConnection()
+ call to time-out and break with an <tt>SQLException</tt> after the specified number of milliseconds.
+ </div>
+ </dd>
+ <dt><a name="connectionCustomizerClassName" />connectionCustomizerClassName</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ The fully qualified class-name of an implememtation of the <a href="apidocs/com/mchange/v2/c3p0/ConnectionCustomizer.html"><tt>ConnectionCustomizer</tt></a>
+ interface, which users can implement to set up Connections when they are acquired from the database, or on check-out, and potentially
+ to clean things up on check-in and Connection destruction. If standard Connection properties (holdability, readOnly, or transactionIsolation)
+ are set in the ConnectionCustomizer's onAcquire() method, these will override the Connection default values.
+ </div>
+ </dd>
+ <dt><a name="connectionTesterClassName" />connectionTesterClassName</dt>
+ <dd>
+ <div class="default">Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester</div>
+ <div class="propdesc">
+ The fully qualified class-name of an implememtation of the <a href="apidocs/com/mchange/v2/c3p0/ConnectionTester.html"><tt>ConnectionTester</tt></a>
+ interface, or <a href="apidocs/com/mchange/v2/c3p0/QueryConnectionTester.html"><tt>QueryConnectionTester</tt></a> if you would like instances
+ to have access to a user-configured <tt>preferredTestQuery</tt>. This can be used to customize how c3p0 DataSources test Connections, but with
+ the introduction of <tt>automaticTestTable</tt> and <tt>preferredTestQuery</tt> configuration parameters, "rolling your own" should be overkill
+ for most users.
+ [See <a href="#configuring_connection_testing">"Configuring Connection Testing"]</a>
+ </div>
+ </dd>
+ <dt><a name="debugUnreturnedConnectionStackTraces" />debugUnreturnedConnectionStackTraces</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ If true, and if <tt><a href="#unreturnedConnectionTimeout">unreturnedConnectionTimeout</a></tt> is set to a positive value,
+ then the pool will capture the stack trace (via an Exception) of all Connection checkouts, and the stack traces will be
+ printed when unreturned checked-out Connections timeout. This is intended to debug applications with Connection leaks, that
+ is applications that occasionally fail to return Connections, leading to pool growth, and eventually exhaustion (when the
+ pool hits <tt>maxPoolSize</tt> with all Connections checked-out and lost). This parameter should only be set while debugging,
+ as capturing the stack trace will slow down every Connection check-out.
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="factoryClassLocation" />factoryClassLocation</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ DataSources that will be bound by JNDI and use that API's Referenceable interface
+ to store themselves may specify a URL from which the class capable of dereferencing
+ a them may be loaded. If (as is usually the case) the c3p0 libraries will be locally
+ available to the JNDI service, leave this set as null.
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="forceIgnoreUnresolvedTransactions" />forceIgnoreUnresolvedTransactions</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ <b><i>Strongly disrecommended. Setting this to <tt>true</tt> may lead to subtle and bizarre bugs.</i></b>
+ This is a terrible setting, leave it alone unless absolutely necessary. It is here to workaround
+ broken databases / JDBC drivers that do not properly support transactions, but that allow Connections'
+ <tt>autoCommit</tt> flags to go to false regardless. If you are using a database that supports transactions
+ "partially" (this is oxymoronic, as the whole point of transactions is to perform operations reliably and
+ completely, but nonetheless such databases are out there), if you feel comfortable ignoring the fact that Connections
+ with <tt>autoCommit == false</tt> may be in the middle of transactions and may hold locks and other resources,
+ you may turn off c3p0's wise default behavior, which is to protect itself, as well as the usability and consistency
+ of the database, by either rolling back (default) or committing (see <tt>c3p0.autoCommitOnClose</tt> <i>above</i>)
+ unresolved transactions. <b>This should only be set to true when you are sure you are using a database that
+ allows Connections' autoCommit flag to go to false, but offers no other meaningful support of transactions. Otherwise
+ setting this to true is just a bad idea.</b>
+ [See <a href="#configuring_unresolved">"Configuring Unresolved Transaction Handling"</a>]
+ </div>
+ </dd>
+ <dt><a name="idleConnectionTestPeriod"/>idleConnectionTestPeriod</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections,
+ every this number of seconds. [See <a href="#configuring_connection_testing">"Configuring Connection Testing"</a>]
+ </div>
+ </dd>
+ <dt><a name="initialPoolSize"/>initialPoolSize</dt>
+ <dd>
+ <div class="default">Default: 3</div>
+ <div class="propdesc">
+ Number of Connections a pool will try to acquire upon startup. Should be between <tt>minPoolSize</tt> and
+ <tt>maxPoolSize</tt>.
+ [See <a href="#basic_pool_configuration">"Basic Pool Configuration"</a>]
+ </div>
+ </dd>
+ <dt><a name="maxAdministrativeTaskTime" />maxAdministrativeTaskTime</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Seconds before c3p0's thread pool will try to interrupt an apparently hung task. <u>Rarely useful.</u> Many of c3p0's functions
+ are not performed by client threads, but asynchronously by an internal thread pool. c3p0's asynchrony enhances
+ client performance directly, and minimizes the length of time that critical locks are held by ensuring that slow
+ jdbc operations are performed in non-lock-holding threads. If, however, some of these tasks "hang", that is
+ they neither succeed nor fail with an Exception for a prolonged period of time, c3p0's thread pool can become
+ exhausted and administrative tasks backed up. If the tasks are simply slow, the best way to resolve the problem
+ is to increase the number of threads, via <a href="#numHelperThreads">numHelperThreads</a>. But if tasks
+ sometimes hang indefinitely, you can use this parameter to force a call to the task thread's <tt>interrupt()</tt>
+ method if a task exceeds a set time limit. [c3p0 will eventually recover from hung tasks anyway by signalling an "APPARENT
+ DEADLOCK" (you'll see it as a warning in the logs), replacing the thread pool task threads, and interrupt()ing the
+ original threads. But letting the pool go into APPARENT DEADLOCK and then recover means that for some periods,
+ c3p0's performance will be impaired. So if you're seeing these messages, increasing <a href="#numHelperThreads">numHelperThreads</a>
+ and setting <tt>maxAdministrativeTaskTime</tt> might help. <tt>maxAdministrativeTaskTime</tt> should be large enough
+ that any resonable attempt to acquire a Connection from the database, to test a Connection, or two destroy a Connection,
+ would be expected to succeed or fail within the time set. Zero (the default) means tasks are never interrupted,
+ which is the best and safest policy under most circumstances. If tasks are just slow, allocate more threads. If tasks
+ are hanging forever, try to figure out why, and maybe setting <tt>maxAdministrativeTaskTime</tt> can help in the meantime.
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="maxConnectionAge" />maxConnectionAge</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Seconds, effectively a time to live. A Connection older than <tt>maxConnectionAge</tt> will be destroyed and
+ purged from the pool. This differs from <tt>maxIdleTime</tt> in that it refers to absolute age. Even a Connection
+ which has not been much idle will be purged from the pool if it exceeds <tt>maxConnectionAge</tt>. Zero means
+ no maximum absolute age is enforced.
+ </div>
+ </dd>
+ <dt><a name="maxIdleTime" />maxIdleTime</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.
+ [See <a href="#basic_pool_configuration">"Basic Pool Configuration"</a>]
+ </div>
+ </dd>
+ <dt><a name="maxIdleTimeExcessConnections" />maxIdleTimeExcessConnections</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Number of seconds that Connections in excess of <tt>minPoolSize</tt> should be permitted to remain idle in the pool
+ before being culled. Intended for applications that wish to aggressively minimize the number of open Connections,
+ shrinking the pool back towards minPoolSize if, following a spike, the load level diminishes and Connections
+ acquired are no longer needed. If <tt>maxIdleTime</tt> is set, <tt>maxIdleTimeExcessConnections</tt> should be
+ smaller if the parameter is to have any effect. Zero means no enforcement, excess Connections are not idled out.
+ </div>
+ </dd>
+ <dt><a name="maxPoolSize"/>maxPoolSize</dt>
+ <dd>
+ <div class="default">Default: 15</div>
+ <div class="propdesc">
+ Maximum number of Connections a pool will maintain at any given time.
+ [See <a href="#basic_pool_configuration">"Basic Pool Configuration"</a>]
+ </div>
+ </dd>
+ <dt><a name="maxStatements" />maxStatements</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ The size of c3p0's global PreparedStatement cache. If both <tt>maxStatements</tt> and <tt>maxStatementsPerConnection</tt>
+ are zero, statement caching will not be enabled. If <tt>maxStatements</tt> is zero but <tt>maxStatementsPerConnection</tt>
+ is a non-zero value, statement caching will be enabled, but no global limit will be enforced, only the per-connection maximum.
+ <tt>maxStatements</tt> controls the total number of Statements cached,
+ for all Connections. If set, it should be a fairly large number, as each pooled Connection requires its own,
+ distinct flock of cached statements. As a guide, consider how many distinct PreparedStatements are used
+ <i>frequently</i> in your application, and multiply that number by <tt>maxPoolSize</tt> to arrive at an appropriate
+ value. Though <tt>maxStatements</tt> is the JDBC standard parameter for controlling statement caching, users may
+ find c3p0's alternative <tt>maxStatementsPerConnection</tt> more intuitive to use.
+ [See <a href="#configuring_statement_pooling">"Configuring Statement Pooling"</a>]
+ </div>
+ </dd>
+ <dt><a name="maxStatementsPerConnection" />maxStatementsPerConnection</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ The number of PreparedStatements c3p0 will cache for a single pooled Connection.
+ If both <tt>maxStatements</tt> and <tt>maxStatementsPerConnection</tt>
+ are zero, statement caching will not be enabled. If <tt>maxStatementsPerConnection</tt> is zero but <tt>maxStatements</tt>
+ is a non-zero value, statement caching will be enabled, and a global limit enforced, but otherwise no limit will be set
+ on the number of cached statements for a single Connection.
+ If set, maxStatementsPerConnection should be set to about the number distinct PreparedStatements that are used
+ <i>frequently</i> in your application, plus two or three extra so infrequently statements don't force the more common
+ cached statements to be culled. Though <tt>maxStatements</tt> is the JDBC standard parameter for controlling statement caching,
+ users may find <tt>maxStatementsPerConnection</tt> more intuitive to use.
+ [See <a href="#configuring_statement_pooling">"Configuring Statement Pooling"</a>]
+ </div>
+ </dd>
+ <dt><a name="minPoolSize"/>minPoolSize</dt>
+ <dd>
+ <div class="default">Default: 3</div>
+ <div class="propdesc">
+ Minimum number of Connections a pool will maintain at any given time.
+ [See <a href="#basic_pool_configuration">"Basic Pool Configuration"</a>]
+ </div>
+ </dd>
+ <dt><a name="numHelperThreads" />numHelperThreads</dt>
+ <dd>
+ <div class="default">Default: 3</div>
+ <div class="propdesc">
+ c3p0 is very asynchronous. Slow JDBC operations are generally
+ performed by helper threads that don't hold contended locks. Spreading
+ these operations over multiple threads can significantly improve performance
+ by allowing multiple operations to be performed simultaneously.
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="overrideDefaultUser" />overrideDefaultUser</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ Forces the username that should by PooledDataSources when a user calls the default
+ getConnection() method. This is primarily useful when applications are pooling Connections
+ from a non-c3p0 unpooled DataSource. Applications that use <tt>ComboPooledDataSource</tt>,
+ or that wrap any c3p0-implemented unpooled DataSource can use the simple
+ <a href="#user">user</a> property.
+<!--
+ C3P0 PooledDataSources, implicitly or explicity, are wrappers around unpooled DataSources.
+ For most users, the unpooled DataSource is c3p0's own DriverManagerDataSource, but users are
+ welcome to wrap other DataSource implementations. Per the JDBC 2.0 spec, DataSources may offer
+ standard properties "user" and "password". If present, c3p0 PooledDataSources use these properties
+ to determine what kind of authentication to use. If absent, PooledDataSources use the unpooled
+ DataSource's default getConnection() method. Some DataSources offer one but not both of the
+ standard properties, which confuses c3p0.
+-->
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="overrideDefaultPassword" />overrideDefaultPassword</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ Forces the password that should by PooledDataSources when a user calls the default
+ getConnection() method. This is primarily useful when applications are pooling Connections
+ from a non-c3p0 unpooled DataSource. Applications that use <tt>ComboPooledDataSource</tt>,
+ or that wrap any c3p0-implemented unpooled DataSource can use the simple
+ <a href="#password">password</a> property.
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="password" />password</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ For applications using <tt>ComboPooledDataSource</tt> or any
+ c3p0-implemented unpooled DataSources — <tt>DriverManagerDataSource</tt> or the
+ DataSource returned by <tt>DataSources.unpooledDataSource( ... )</tt> —
+ defines the password that will be used for the DataSource's default
+ <tt>getConnection()</tt> method. (See also <a href="#user">user</a>.)
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ </dd>
+ <dt><a name="preferredTestQuery" />preferredTestQuery</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ Defines the query that will be executed for all connection tests, if the default ConnectionTester (or some
+ other implementation of <a href="apidocs/com/mchange/v2/c3p0/QueryConnectionTester.html">QueryConnectionTester</a>,
+ or better yet <a href="apidocs/com/mchange/v2/c3p0/FullQueryConnectionTester.html">FullQueryConnectionTester</a>)
+ is being used. Defining a <tt>preferredTestQuery</tt>
+ that will execute quickly in your database may dramatically speed up Connection tests. (If no <tt>preferredTestQuery</tt>
+ is set, the default ConnectionTester executes a <tt>getTables()</tt> call on the Connection's DatabaseMetaData.
+ Depending on your database, this may execute more slowly than a "normal" database query.)
+ <b>NOTE: The table against
+ which your <tt>preferredTestQuery</tt> will be run must exist in the database schema <i>prior</i> to your initialization
+ of your DataSource. If your application defines its own schema, try <tt>automaticTestTable</tt> instead.</b>
+ [See <a href="#configuring_connection_testing">"Configuring Connection Testing"</a>]
+ </div>
+ </dd>
+ <dt><a name="propertyCycle" />propertyCycle</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Maximum time in seconds before user configuration constraints are enforced.
+ Determines how frequently <tt>maxConnectionAge</tt>, <tt>maxIdleTime</tt>, <tt>maxIdleTimeExcessConnections</tt>,
+ <tt>unreturnedConnectionTimeout</tt> are enforced. c3p0 periodically checks the age of Connections to
+ see whether they've timed out. This parameter determines the period. Zero means automatic: A suitable period
+ will be determined by c3p0. [You can call <tt>getEffectivePropertyCycle...()</tt> methods on a c3p0
+ <a href="apidocs/com/mchange/v2/c3p0/PooledDataSource.html">PooledDataSource</a> to find the period
+ automatically chosen.]
+ </div>
+ </dd>
+ <dt><a name="testConnectionOnCheckin" />testConnectionOnCheckin</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ If true, an operation will be performed asynchronously at every connection checkin to verify that the connection is valid.
+ Use in combination with <tt>idleConnectionTestPeriod</tt> for quite reliable, always asynchronous Connection testing.
+ Also, setting an <tt>automaticTestTable</tt> or <tt>preferredTestQuery</tt> will usually speed up all connection tests.
+ [See <a href="#configuring_connection_testing">"Configuring Connection Testing"</a>]
+ </div>
+ </dd>
+ <dt><a name="testConnectionOnCheckout" />testConnectionOnCheckout</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ <b><i>Use only if necessary. Expensive.</i></b>
+ If true, an operation will be performed at every connection checkout to verify that the connection is valid.
+ <b>Better choice:</b> verify connections periodically using <tt>idleConnectionTestPeriod</tt>. Also, setting an
+ <tt>automaticTestTable</tt> or <tt>preferredTestQuery</tt> will usually speed up all connection tests.
+ [See <a href="#configuring_connection_testing">"Configuring Connection Testing"</a>]
+ </div>
+ </dd>
+ <dt><a name="unreturnedConnectionTimeout" />unreturnedConnectionTimeout</dt>
+ <dd>
+ <div class="default">Default: 0</div>
+ <div class="propdesc">
+ Seconds. If set, if an application checks out but then fails to check-in [i.e. close()] a Connection
+ within the specified period of time, the pool will unceremoniously destroy() the Connection. This permits
+ applications with occasional Connection leaks to survive, rather than eventually exhausting the Connection
+ pool. And that's a shame. Zero means no timeout, applications are expected to close() their own Connections.
+ Obviously, if a non-zero value is set, it should be to a value longer than any Connection should reasonably
+ be checked-out. Otherwise, the pool will occasionally kill Connections in active use, which is bad.
+ <b><i>This is basically a bad idea, but it's a commonly requested feature. Fix your $%!@% applications
+ so they don't leak Connections! Use this temporarily in combination with
+ <tt>debugUnreturnedConnectionStackTraces</tt> to figure out
+ where Connections are being checked-out that don't make it back into the pool!</i></b>
+ </div>
+ </dd>
+ <dt><a name="user" />user</dt>
+ <dd>
+ <div class="default">Default: null</div>
+ <div class="propdesc">
+ For applications using <tt>ComboPooledDataSource</tt> or any
+ c3p0-implemented unpooled DataSources — <tt>DriverManagerDataSource</tt> or the
+ DataSource returned by <tt>DataSources.unpooledDataSource()</tt> —
+ defines the username that will be used for the DataSource's default
+ <tt>getConnection()</tt> method. (See also <a href="#password">password</a>.)
+ </div>
+ <div class="per-user">Does Not Support Per-User Overrides.</div>
+ <dt><a name="usesTraditionalReflectiveProxies" />usesTraditionalReflectiveProxies</dt>
+ <dd>
+ <div class="default">Default: false</div>
+ <div class="propdesc">
+ c3p0 originally used reflective dynamic proxies for implementations of Connections and other JDBC
+ interfaces. As of c3p0-0.8.5, non-reflective, code-generated implementations are used instead. As
+ this was a major change, and the old codebase had been extensively used and tested, this parameter
+ was added to allow users to revert of they had problems. The new, non-reflexive implementation is
+ faster, and has now been widely deployed and tested, so it is unlikely that this parameter will be useful.
+ Both the old reflective and newer non-reflective codebases are being maintained, but support for the
+ older codebase may (or may not) be dropped in the future.
+ </div>
+ </dd>
+ </dl>
+ <h3>
+ <a name="other-properties">Other Properties</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ The following configuration properties affect the behavior of the c3p0 library as a whole. They
+ may be set as system properties, or in a <a href="#c3p0_properties"><tt>c3p0.properties</tt></a> file.
+ </p>
+ <div class="other_properties_desc">
+ <h4>Locating Configuration Information</h4>
+ <p>
+ Normally, c3p0's configuration information is placed in a either a c3p0-config.xml or c3p0.properties file
+ at the top-level of an application's CLASSPATH. However, if you wish to place configuration information
+ elsewhere, you may place c3p0 configuration information (in the <a href="#c3p0-config.xml">XML file format</a> only!) anywhere you'd like
+ in the filesystem visible to your application. Just set the following property to the full, absolute path
+ of the XML config file:
+ </p>
+ <ul class="other_props_list">
+ <li>com.mchange.v2.c3p0.cfg.xml</li>
+ </ul>
+ <h4>Logging-related properties</h4>
+ <p>
+ The following properties affect c3p0's logging behavior. Please see <a href="#configuring_logging">Configuring Logging</a>
+ above for specific information.
+ </p>
+ <ul class="other_props_list">
+ <li>com.mchange.v2.log.MLog</li>
+ <li>com.mchange.v2.log.NameTransformer</li>
+ <li>com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL</li>
+ </ul>
+ <h4>Configuring JMX</h4>
+ <p>
+ The following property controls c3p0's JMX management interface. Plese see
+ <a href="#jmx_configuration_and_management">Configuring and Managing c3p0 via JMX</a> above
+ for more information.
+ </p>
+ <ul class="other_props_list">
+ <li>com.mchange.v2.c3p0.management.ManagementCoordinator</li>
+ </ul>
+ <h4>Configuring the VMID</h4>
+ <p>
+ Is it better to be beautiful or correct? Beginning with c3p0-0.9.1, c3p0 opts somewhat reluctantly for correctness.
+ </p>
+ <p>
+ Here's the deal. Every c3p0 DataSource is allocated a unique "identity token", which is used to ensure that multiple
+ JNDI lookups of the same PooledDataSource always return the same instance, even if the JNDI name-server stores a
+ Serialized or Referenced instance. Previously, c3p0 was happy for generated IDs to be unique within a single VM (and it
+ didn't even get that quite right, before c3p0-0.9.1). But in theory, one VM might look up two different DataSources,
+ generated by two different VMs, that by unlikely coincidence have the same "identity token", leading to errors as one
+ of the two DataSources sneakily substitutes for the second. Though this hypothetical issue has never been reported in practice,
+ c3p0 resolves it by prepending a VMID to its identity tokens. This makes them long and ugly, but correct.
+ </p>
+ <p>
+ If you don't like the long and ugly VMID, you can set your own, or you can turn off this solution to a hypothetical
+ non-problem entirely with the following property:
+ </p>
+ <ul class="other_props_list">
+ <li>com.mchange.v2.c3p0.VMID</li>
+ </ul>
+ <p>
+ Set it to <tt>NONE</tt> to turn off the VMID, set it to <tt>AUTO</tt> to let c3p0 generate a VMID,
+ or provide any other String to set the VMID that will be used directly. The default is <tt>AUTO</tt>.
+ </p>
+ <h4>Experimental properties</h4>
+ <p>
+ c3p0-0.9.1 includes a new implementation of asynchronous Connection acquisition that
+ should improve c3p0's performance and resource utilization in cases where database
+ acquisition attempts, for whatever reason, occasionally fail. The new implementation
+ should be significantly better than the "traditional" Connection acquisition strategy,
+ but was added too late in the c3p0-0.9.1 development cycle to be fully tested and
+ enabled by default. Users are encouraged to try the new implementation, both because
+ it is better, and to help iron out any unanticipated problems.
+ </p>
+ <p>
+ For a full description of the new implementation and the resource bottleneck it is
+ designed to overcome, please see the <tt>CHANGELOG</tt> entry for <tt>c3p0-0.9.1-pre11</tt>.
+ To enable the new implementation, set the following parameter to "<tt>true</tt>".
+ </p>
+ <ul class="other_props_list">
+ <li>com.mchange.v2.resourcepool.experimental.useScatteredAcquireTask</li>
+ </ul>
+ <p>
+ This feature is expected to be enabled by default in c3p0-0.9.2 and above.
+ </p>
+ </div>
+ </div>
+ <hr/>
+ <h2>
+ <a name="configuration_files">Appendix B: Configuration Files, etc.</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h2>
+ <div class="sectiontext">
+ <p>
+ c3p0 configuration parameters can be set
+ <a href="#programmatic_configuration">directly in Java code</a>,
+ via a <a href="#c3p0_properties">simple Java properties file</a>,
+ via an <a href="#c3p0-config.xml">XML configuration file</a>, or
+ via <a href="#system_properties">System properties</a>.
+ Any which way
+ works (the the XML configuration is most powerful, though, as it supports multiple named configurations and
+ per-user overrides. Choose whatever works best for you.
+ </p>
+ <h3>
+ <a name="c3p0_properties">Overriding c3p0 defaults via <tt>c3p0.properties</tt></a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ To override the library's built-in defaults, create a file called <tt>c3p0.properties</tt>
+ and place it at the "root" of your classpath or classloader. For a typical standalone
+ application, that means place the file in a directory named in your <tt>CLASSPATH</tt>
+ environment variable. For a typical web-application, the file should be placed in
+ <tt>WEB-INF/classes</tt>. In general, the file must be available as a classloader
+ resource under the name <tt>/c3p0.properties</tt>, in the classloader that loaded
+ c3p0's jar file. Review the API docs (especilly <tt>getResource...</tt> methods) of
+ <tt>java.lang.Class</tt>, <tt>java.lang.ClassLoader</tt>, and <tt>java.util.ResourceBundle</tt>
+ if this is unfamiliar.
+ </p>
+ <p>
+ The format of <tt>c3p0.properties</tt> should be a normal Java Properties file format,
+ whose keys are c3p0 configurable properties. See <a href="#configuration_properties">Appendix A</a>.
+ for the specifics. An example <tt>c3p0.properties</tt> file is produced below:
+ </p>
+ <div class="example">
+# turn on statement pooling
+c3p0.maxStatements=150
+
+# close pooled Connections that go unused for
+# more than half an hour
+c3p0.maxIdleTime=1800
+ </div>
+ <h3>
+ <a name="system_properties">Overriding c3p0 defaults with a System properties</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 properties can also be defined as System properties, using the same "c3p0." prefix for properties
+ specified in a <tt>c3p0.properties</tt> file.
+ </p>
+ <div class="example">
+swaldman% java -Dc3p0.maxStatements=150 -Dc3p0.maxIdleTime=1800 example.MyC3P0App
+ </div>
+ <p>
+ System properties override settings in c3p0.properties. Please see
+ <a href="#configuration_precedence">Precedence of Configuration Settings</a> for more information.
+ </p>
+ <h3>
+ <a name="c3p0-config.xml">Named and Per-User configuration: Overriding c3p0 defaults via <tt>c3p0-config.xml</tt></a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ As of c3p0-0.9.1, you can define multiple configurations in an XML configuration file, and specify in your code which
+ configuration to use. For any configurations (including the unnamed default configuration), you can define overrides
+ for a particular database user. For example, if several applications access your database under different authentication
+ credentials, you might define <tt>maxPoolSize</tt> to be 100 for user <tt>highVolumeApp</tt>, but only 10 for user
+ <tt>lowLoadApp</tt>. (Recall that Connections associated with different authentication credentials
+ are of necessity separated into separate pools, so it makes sense that these could be configured separately.)
+ </p>
+ <p>
+ You can use the XML config file for all c3p0 configuration, including configuration of defaults. However, for users who
+ don't want or need the extra complexity, the c3p0.properties file will continue to be supported.
+ </p>
+ <p>
+ By default, c3p0 will look for an XML configuration file in its classloader's resource path under the name "/c3p0-config.xml".
+ That means the XML file should be placed in a directly or jar file directly named in your applications CLASSPATH, in WEB-INF/classes,
+ or some similar location.
+ </p>
+ <p>
+ If you prefer not to bundle your configuration with your code, you can specify an ordinary filesystem location for c3p0's
+ configuration file via the system property <tt>com.mchange.v2.c3p0.cfg.xml</tt>.
+ </p>
+ <p>
+ Here is an example <tt>c3p0-config.xml</tt> file:
+ </p>
+ <div class="example">
+<c3p0-config>
+ <default-config>
+ <property name="automaticTestTable">con_test</property>
+ <property name="checkoutTimeout">30000</property>
+ <property name="idleConnectionTestPeriod">30</property>
+ <property name="initialPoolSize">10</property>
+ <property name="maxIdleTime">30</property>
+ <property name="maxPoolSize">100</property>
+ <property name="minPoolSize">10</property>
+ <property name="maxStatements">200</property>
+
+ <user-overrides user="test-user">
+ <property name="maxPoolSize">10</property>
+ <property name="minPoolSize">1</property>
+ <property name="maxStatements">0</property>
+ </user-overrides>
+
+ </default-config>
+
+ <!-- This app is massive! -->
+ <named-config name="intergalactoApp">
+ <property name="acquireIncrement">50</property>
+ <property name="initialPoolSize">100</property>
+ <property name="minPoolSize">50</property>
+ <property name="maxPoolSize">1000</property>
+
+ <!-- intergalactoApp adopts a different approach to configuring statement caching -->
+ <property name="maxStatements">0</property>
+ <property name="maxStatementsPerConnection">5</property>
+
+ <!-- he's important, but there's only one of him -->
+ <user-overrides user="master-of-the-universe">
+ <property name="acquireIncrement">1</property>
+ <property name="initialPoolSize">1</property>
+ <property name="minPoolSize">1</property>
+ <property name="maxPoolSize">5</property>
+ <property name="maxStatementsPerConnection">50</property>
+ </user-overrides>
+ </named-config>
+</c3p0-config>
+ </div>
+ <p>
+ To use a named configuration, you specify the config name when creating your DataSource. For example, using
+ <a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html"><tt>ComboPooledDataSource</tt></a>:
+ </p>
+ <div class="example">
+ComboPooledDataSource cpds = new ComboPooledDataSource("intergalactoApp");
+ </div>
+ <p>
+ Or using the
+ <a href="apidocs/com/mchange/v2/c3p0/DataSources.html"><tt>DataSources</tt></a> factory class:
+ </p>
+ <div class="example">
+DataSource ds_pooled = DataSources.pooledDataSource( ds_unpooled, "intergalactoApp" );
+ </div>
+<!--
+ <h3>
+ <a name="programmatic_configuration">Programmatic configuration of DataSource instances</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ -->
+ <h3>
+ <a name="configuration_precedence">Precedence of Configuration Settings</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h3>
+ <p>
+ c3p0 now permits configuration parameters to be set in a number of different ways and places. Fortunately,
+ there is a clear order of precedence that determines which configuration will "take" in the event of conflicting
+ settings. Conceptually, c3p0 goes down this list from top to bottom, using the first setting it finds.
+ </p>
+ <p>
+ Most applications will never use per-user or named configurations. For these applications, we
+ present a simplified precedence hierarchy:
+ </p>
+ <div align="center">
+ <ol class="precedence">
+ <li>Configuration values programmatically set.</li>
+ <li>System property setting of configuration value.</li>
+ <li>Configuration values taken from the default configuration of a <tt>c3p0-config.xml</tt> file.</li>
+ <li>Configuration values specified in a <tt>c3p0.properties</tt> file</li>
+ <li>c3p0's hard-coded default values.</li>
+ </ol>
+ </div>
+ <p>
+ For applications that do use named and per-user configurations, here is the complete, normative precedence hierarchy:
+ </p>
+ <div align="center">
+ <ol class="precedence">
+ <li>
+ User-specific overrides programmatically set via:
+ <ul>
+ <li><a href="apidocs/com/mchange/v2/c3p0/ComboPooledDataSource.html#setUserOverridesAsString"><tt>ComboPooledDataSource.setUserOverridesAsString()</tt></a></li>
+ <li><a href="apidocs/com/mchange/v2/c3p0/WrapperConnectionPoolDataSource.html#setUserOverridesAsString"><tt>WrapperConnectionPoolDataSource.setUserOverridesAsString()</tt></a></li>
+ </ul>
+ Note that programmatically setting user-specific overrides <u>replaces</u> all user-specific configuration taken from
+ other sources. If you want to merge programmatic changes with preconfigured overrides, you'll have to use <tt>getUserOverridesAsString()</tt>
+ and modify the original settings before replacing.
+ </li>
+ <li>User-specific overrides taken from a DataSource's named configuration (specified in <tt>c3p0-config.xml</tt>)</li>
+ <li>User-specific overrides taken from the default configuration (specified in <tt>c3p0-config.xml</tt>)</li>
+ <li>Non-user-specific values programmatically set.</li>
+ <li>Non-user-specific values taken from a DataSource's named configuration (specified in <tt>c3p0-config.xml</tt>)</li>
+ <li>System property setting of configuration value.</li>
+ <li>Non-user-specific values taken from the default configuration (specified in <tt>c3p0-config.xml</tt>)</li>
+ <li>Values specified in a <tt>c3p0.properties</tt> file</li>
+ <li>c3p0's hard-coded default values.</li>
+ </ol>
+ </div>
+ </div>
+ <h2>
+ <a name="hibernate-specific">Appendix C: Hibernate-specific notes</a>
+ <span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span>
+ </h2>
+ <div class="sectiontext">
+ <p>
+ Hibernate's C3P0ConnectionProvider explicitly sets 7 c3p0 configuration properties, based on your hibernate
+ configuration, <b>overriding</b> any configuration you may have set in a <tt>c3p0.properties</tt> file. If you
+ are using Hibernate's C3P0ConnectionProvider you <b>must</b> set the following
+ properties in your hibernate configuration, using hibernate-specific configuration keys. All other properties
+ must be defined as usual in a <tt>c3p0.properties</tt> file. This is confusing, and will hopefully be simplified
+ some time in the future, but for now...
+ </p>
+ <p>
+ The following properties must be set in your hibernate configuration:
+ </p>
+ <div class="hibernate_props">
+ <table class="hibernate_props">
+ <tr>
+ <th>c3p0-native property name</th><th>hibernate configuration key</th>
+ </tr>
+ <tr>
+ <td>c3p0.acquireIncrement</td><td>hibernate.c3p0.acquire_increment</td>
+ </tr>
+ <tr>
+ <td>c3p0.idleConnectionTestPeriod</td><td>hibernate.c3p0.idle_test_period</td>
+ </tr>
+ <tr>
+ <td>c3p0.initialPoolSize</td><td><span class="hibparam_comment">not available -- uses minimum size</span></td>
+ </tr>
+ <tr>
+ <td>c3p0.maxIdleTime</td><td>hibernate.c3p0.timeout</td>
+ </tr>
+ <tr>
+ <td>c3p0.maxPoolSize</td><td>hibernate.c3p0.max_size</td>
+ </tr>
+ <tr>
+ <td>c3p0.maxStatements</td><td>hibernate.c3p0.max_statements</td>
+ </tr>
+ <tr>
+ <td>c3p0.minPoolSize</td><td>hibernate.c3p0.min_size</td>
+ </tr>
+ <tr>
+ <td>c3p0.testConnectionsOnCheckout </td><td>hibernate.c3p0.validate <span class="hibparam_comment">hibernate 2.x only!</span></td>
+ </tr>
+ </table>
+ </div>
+ <p>
+ Remember -- these, and <i>only these</i>, properties must be defined in your hibernate configuration, or else
+ they will be set to hibernate-specified defaults. All other configuration properties that you wish to set
+ should be defined in a <tt>c3p0.properties</tt> file. (See <a href="#c3p0_properties">"Overriding c3p0 defaults via c3p0.properties"</a>.)
+ </p>
+ </div>
+ <hr/>
+ <h2><a name="tomcat-specific">Appendix D: Configuring c3p0 DataSources in Tomcat</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ You can easily configure Apache's Tomcat web application server to use c3p0 pooled DataSources.
+ Below is a Tomcat 5.0 sample config to get you started. It's a fragment of Tomcat's <tt>conf/server.xml</tt> file,
+ which should be modified to suit and placed inside a <tt><Context></tt> element.
+ </p>
+ <div class="example">
+<Resource name="jdbc/pooledDS" auth="Container" type="com.mchange.v2.c3p0.ComboPooledDataSource" />
+<ResourceParams name="jdbc/pooledDS">
+ <parameter>
+ <name>factory</name>
+ <value>org.apache.naming.factory.BeanFactory</value>
+ </parameter>
+ <parameter>
+ <name>driverClass</name>
+ <value>org.postgresql.Driver</value>
+ </parameter>
+ <parameter>
+ <name>jdbcUrl</name>
+ <value>jdbc:postgresql://localhost/c3p0-test</value>
+ </parameter>
+ <parameter>
+ <name>user</name>
+ <value>swaldman</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>test</value>
+ </parameter>
+ <parameter>
+ <name>minPoolSize</name>
+ <value>5</value>
+ </parameter>
+ <parameter>
+ <name>maxPoolSize</name>
+ <value>15</value>
+ </parameter>
+ <parameter>
+ <name>acquireIncrement</name>
+ <value>5</value>
+ </parameter>
+</ResourceParams>
+ </div>
+ <p>For Tomcat 5.5, try something like the following (thanks to Carl F. Hall for the sample!):</p>
+ <div class="example">
+ <Resource auth="Container"
+ description="DB Connection"
+ driverClass="com.mysql.jdbc.Driver"
+ maxPoolSize="4"
+ minPoolSize="2"
+ acquireIncrement="1"
+ name="jdbc/TestDB"
+ user="test"
+ password="ready2go"
+ factory="org.apache.naming.factory.BeanFactory"
+ type="com.mchange.v2.c3p0.ComboPooledDataSource"
+ jdbcUrl="jdbc:mysql://localhost:3306/test?autoReconnect=true" />
+ </div>
+ <p>
+ The rest is standard J2EE stuff: You'll need to declare your DataSource reference in your <tt>web.xml</tt>
+ file:
+ </p>
+
+ <div class="example">
+<resource-ref>
+ <res-ref-name>jdbc/pooledDS</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+</resource-ref>
+ </div>
+ <p>
+ And you can access your DataSource from code within your web application like this:
+ </p>
+ <div class="example">
+InitialContext ic = new InitialContext();
+DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/pooledDS");
+ </div>
+ <p>
+ That's it!
+ </p>
+ </div>
+ <hr/>
+ <h2><a name="jboss-specific">Appendix E: JBoss-specific notes</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ To use c3p0 with JBoss:
+ <ol>
+ <li>
+ Place c3p0's jar file in the <tt>lib</tt> directory of your
+ jboss server instance (e.g. <tt>${JBOSS_HOME}/server/default/lib</tt>)
+ </li>
+ <li>
+ Modify the file below, and save it as <tt>c3p0-service.xml</tt> in the
+ <tt>deploy</tt> directory of your jboss server (e.g. <tt>${JBOSS_HOME}/server/default/deploy</tt>).
+ Note that parameters must be capitalized in this file, but otherwise they are defined as described
+ above.
+ </li>
+ </ol>
+ <p>
+ <div id="showDeprecatedJbossConfigName" style="display: block">
+ <b>Note:</b> <a href="#" onClick="return toggleDisplay('showDeprecatedJbossConfigName', 'DeprecatedJbossConfigName');">
+ Users of c3p0 jboss support prior to c3p0-0.9.1 please click here!</a>
+ </div>
+ </p>
+ <div class="deprecated" id="DeprecatedJbossConfigName">
+ <p>
+ <b>Please note: As of c3p0-0.9.1, the class name of the jboss configuration mbean has changed
+ to <tt>com.mchange.v2.c3p0.jboss.C3P0PooledDataSource</tt> (from <tt>com.mchange.v2.c3p0.mbean.C3P0PooledDataSource</tt>),
+ in order to distinguish what is really jboss-specific functionality from c3p0's more general JMX
+ support.</b>
+ </p>
+ <p>
+ The old jboss config mbeans are deprecated, but will still work. However, support for new configuration
+ parameters will only be added under the new name. Updating requires a one-word change to your <tt>c3p0-service.xml</tt>,
+ change "mbean" to "jboss" where your old file says 'code="com.mchange.v2.c3p0.mbean.C3P0PooledDataSource"'. Just do it!
+ </p>
+ <p>
+ <a id="hideDataSourcesWithPoolConfig"
+ href="#"
+ onClick="return toggleDisplay('showDeprecatedJbossConfigName', 'DeprecatedJbossConfigName');"
+ >Hide box.</a>
+ <p>
+ </div>
+
+ </div>
+ <div class="example">
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE server>
+
+<server>
+
+ <mbean code="com.mchange.v2.c3p0.jboss.C3P0PooledDataSource"
+ name="jboss:service=C3P0PooledDataSource">
+
+ <attribute name="JndiName">java:PooledDS</attribute>
+ <attribute name="JdbcUrl">jdbc:postgresql://localhost/c3p0-test</attribute>
+ <attribute name="DriverClass">org.postgresql.Driver</attribute>
+ <attribute name="User">swaldman</attribute>
+ <attribute name="Password">test</attribute>
+
+ <!-- Uncomment and set any of the optional parameters below -->
+ <!-- See c3p0's docs for more info. -->
+
+ <!-- <attribute name="AcquireIncrement">3</attribute> -->
+ <!-- <attribute name="AcquireRetryAttempts">30</attribute> -->
+ <!-- <attribute name="AcquireRetryDelay">1000</attribute> -->
+ <!-- <attribute name="AutoCommitOnClose">false</attribute> -->
+ <!-- <attribute name="AutomaticTestTable"></attribute> -->
+ <!-- <attribute name="BreakAfterAcquireFailure">false</attribute> -->
+ <!-- <attribute name="CheckoutTimeout">0</attribute> -->
+ <!-- <attribute name="ConnectionCustomizerClassName"></attribute> -->
+ <!-- <attribute name="ConnectionTesterClassName"></attribute> -->
+ <!-- <attribute name="Description">A pooled c3p0 DataSource</attribute> -->
+ <!-- <attribute name="DebugUnreturnedConnectionStackTraces">false</attribute> -->
+ <!-- <attribute name="FactoryClassLocation"></attribute> -->
+ <!-- <attribute name="ForceIgnoreUnresolvedTransactions">false</attribute> -->
+ <!-- <attribute name="IdleConnectionTestPeriod">0</attribute> -->
+ <!-- <attribute name="InitialPoolSize">3</attribute> -->
+ <!-- <attribute name="MaxAdministrativeTaskTime">0</attribute> -->
+ <!-- <attribute name="MaxConnectionAge">0</attribute> -->
+ <!-- <attribute name="MaxIdleTime">0</attribute> -->
+ <!-- <attribute name="MaxIdleTimeExcessConnections">0</attribute> -->
+ <!-- <attribute name="MaxPoolSize">15</attribute> -->
+ <!-- <attribute name="MaxStatements">0</attribute> -->
+ <!-- <attribute name="MaxStatementsPerConnection">0</attribute> -->
+ <!-- <attribute name="MinPoolSize">0</attribute> -->
+ <!-- <attribute name="NumHelperThreads">3</attribute> -->
+ <!-- <attribute name="PreferredTestQuery"></attribute> -->
+ <!-- <attribute name="TestConnectionOnCheckin">false</attribute> -->
+ <!-- <attribute name="TestConnectionOnCheckout">false</attribute> -->
+ <!-- <attribute name="UnreturnedConnectionTimeout">0</attribute> -->
+ <!-- <attribute name="UsesTraditionalReflectiveProxies">false</attribute> -->
+
+
+ <depends>jboss:service=Naming</depends>
+ </mbean>
+
+</server>
+ </div>
+ </p>
+ </div>
+ <hr/>
+ <h2><a name="oracle-specific">Appendix F: Oracle-specific API: createTemporaryBLOB() and createTemporaryCLOB()</a><span class="toplink"><a href="#contents"><img src="arrow_sm.png" width="20" alt="Go To Top"/></a></span></h2>
+ <div class="sectiontext">
+ <p>
+ The Oracle thin JDBC driver provides a non-standard API for creating temporary BLOBs and CLOBs that
+ requires users to call methods on the raw, Oracle-specific Connection implementation. Advanced users
+ might use the <a href="#raw_connection_ops">raw connection operations</a> described above to access this
+ functionality, but a convenience class is available in a separate jar file (<tt>c3p0-oracle-thin-extras- at c3p0.version@.jar</tt>)
+ for easier access to this functionality. Please see the
+ <a href="apidocs-oracle-thin/index.html">API docs for <tt>com.mchange.v2.c3p0.dbms.OracleUtils</tt></a>
+ for details.
+ </p>
+ </div>
+ <hr/>
+ <a href="#contents">Back to Contents</a>
+ </body>
+</html>
diff --git a/src/docweb/docwebapp/WEB-INF/jboss-web.xml b/src/docweb/docwebapp/WEB-INF/jboss-web.xml
new file mode 100644
index 0000000..89f7d68
--- /dev/null
+++ b/src/docweb/docwebapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+
+<!DOCTYPE jboss-web
+ PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
+ "http://www.jboss.org/j2ee/dtd/jboss-web_3_0.dtd">
+
+<jboss-web>
+ <virtual-host>@virtual.host@</virtual-host>
+</jboss-web>
diff --git a/src/docweb/docwebapp/WEB-INF/web.xml b/src/docweb/docwebapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c76f1a4
--- /dev/null
+++ b/src/docweb/docwebapp/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE web-app
+ PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app />
diff --git a/src/docweb/docwebear/META-INF/application.xml b/src/docweb/docwebear/META-INF/application.xml
new file mode 100644
index 0000000..badffa2
--- /dev/null
+++ b/src/docweb/docwebear/META-INF/application.xml
@@ -0,0 +1,15 @@
+<!DOCTYPE application PUBLIC
+ "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
+ "http://java.sun.com/dtd/application_1_3.dtd">
+
+
+<application>
+ <display-name>www.mchange.com</display-name>
+ <module>
+ <web>
+ <web-uri>@web.uri@</web-uri>
+ <context-root>@context.root@</context-root>
+ </web>
+ </module>
+</application>
+
diff --git a/src/resources/com/mchange/v2/cfg/junit/a.properties b/src/resources/com/mchange/v2/cfg/junit/a.properties
new file mode 100644
index 0000000..604534c
--- /dev/null
+++ b/src/resources/com/mchange/v2/cfg/junit/a.properties
@@ -0,0 +1 @@
+user.home=/a/home
diff --git a/src/resources/com/mchange/v2/cfg/junit/b.properties b/src/resources/com/mchange/v2/cfg/junit/b.properties
new file mode 100644
index 0000000..ad69d7f
--- /dev/null
+++ b/src/resources/com/mchange/v2/cfg/junit/b.properties
@@ -0,0 +1 @@
+user.home=/b/home
diff --git a/src/resources/com/mchange/v2/cfg/vmConfigResourcePaths.txt b/src/resources/com/mchange/v2/cfg/vmConfigResourcePaths.txt
new file mode 100644
index 0000000..2de37a2
--- /dev/null
+++ b/src/resources/com/mchange/v2/cfg/vmConfigResourcePaths.txt
@@ -0,0 +1,11 @@
+#
+# note that later files "shadow" earlier ones, and that
+# the name '/' is reserved as a special token for
+# System properties.
+#
+
+/mchange-commons.properties
+/mchange-log.properties
+/c3p0.properties
+/
+
diff --git a/src/resources/com/mchange/v2/log/default-mchange-log.properties b/src/resources/com/mchange/v2/log/default-mchange-log.properties
new file mode 100644
index 0000000..8013b18
--- /dev/null
+++ b/src/resources/com/mchange/v2/log/default-mchange-log.properties
@@ -0,0 +1,3 @@
+com.mchange.v2.log.MLog=com.mchange.v2.log.log4j.Log4jMLog,com.mchange.v2.log.jdk14logging.Jdk14MLog
+
+
diff --git a/test-properties/c3p0-config.xml b/test-properties/c3p0-config.xml
new file mode 100644
index 0000000..586b8ed
--- /dev/null
+++ b/test-properties/c3p0-config.xml
@@ -0,0 +1,38 @@
+<c3p0-config>
+ <default-config>
+ <!-- <property name="automaticTestTable">con_test</property> -->
+ <!-- <property name="checkoutTimeout">30000</property> -->
+ <!-- <property name="idleConnectionTestPeriod">30</property> -->
+ <!-- <property name="initialPoolSize">10</property> -->
+ <!-- <property name="maxIdleTime">30</property> -->
+ <!-- <property name="maxIdleTimeExcessConnections">10</property> -->
+ <!-- <property name="maxConnectionAge">60</property> -->
+ <!-- <property name="propertyCycle">1</property> -->
+ <!-- <property name="maxPoolSize">25</property> -->
+ <!-- <property name="minPoolSize">5</property> -->
+ <!-- <property name="maxStatements">0</property> -->
+ <!-- <property name="maxStatementsPerConnection">5</property> -->
+ <!-- <property name="maxAdministrativeTaskTime">4</property> -->
+ <!-- <property name="connectionCustomizerClassName">com.mchange.v2.c3p0.test.TestConnectionCustomizer</property> -->
+ <!-- <property name="unreturnedConnectionTimeout">15</property> -->
+ <!-- <property name="debugUnreturnedConnectionStackTraces">true</property> -->
+
+ <!--
+ <user-overrides user="swaldman">
+ <property name="debugUnreturnedConnectionStackTraces">true</property>
+ </user-overrides>
+ -->
+
+ </default-config>
+
+<!--
+ <named-config name="dumbTestConfig">
+ <property name="maxStatements">200</property>
+ <property name="jdbcUrl">jdbc:test</property>
+ <user-overrides user="poop">
+ <property name="maxStatements">300</property>
+ </user-overrides>
+ </named-config>
+-->
+
+</c3p0-config>
diff --git a/test-properties/c3p0.properties b/test-properties/c3p0.properties
new file mode 100644
index 0000000..5161eff
--- /dev/null
+++ b/test-properties/c3p0.properties
@@ -0,0 +1,54 @@
+#
+# This file is detritus from various testing attempts
+# the values below may change, and often do not represent
+# reasonable values for the parameters.
+#
+
+#c3p0.testConnectionOnCheckout=true
+#c3p0.testConnectionOnCheckin=true
+#c3p0.minPoolSize=3
+#c3p0.maxPoolSize=20
+#c3p0.checkoutTimeout=2000
+#c3p0.idleConnectionTestPeriod=5
+#c3p0.maxConnectionAge=10
+#c3p0.maxIdleTime=2
+#c3p0.maxIdleTimeExcessConnections=1
+#c3p0.propertyCycle=1
+#c3p0.numHelperThreads=10
+#c3p0.unreturnedConnectionTimeout=15
+#c3p0.debugUnreturnedConnectionStackTraces=true
+#c3p0.maxStatements=30
+#c3p0.maxStatementsPerConnection=5
+#c3p0.maxAdministrativeTaskTime=3
+#c3p0.preferredTestQuery=SELECT 1
+#c3p0.preferredTestQuery=SELECT a FROM emptyyukyuk WHERE a = 5
+#c3p0.preferredTestQuery=SELECT a FROM testpbds WHERE a = 5
+#c3p0.usesTraditionalReflectiveProxies=true
+#c3p0.automaticTestTable=PoopyTestTable
+#c3p0.acquireIncrement=4
+#c3p0.acquireRetryDelay=1000
+#c3p0.acquireRetryAttempts=60
+#c3p0.connectionTesterClassName=com.mchange.v2.c3p0.test.AlwaysFailConnectionTester
+#c3p0.initialPoolSize=10
+
+c3p0.jdbcUrl=jdbc:postgresql://localhost/c3p0-test
+c3p0.driverClass=org.postgresql.Driver
+c3p0.user=swaldman
+c3p0.password=test
+#c3p0.user=poop
+#c3p0.password=scoop
+
+#com.mchange.v2.log.MLog=com.mchange.v2.log.log4j.Log4jMLog
+#com.mchange.v2.log.MLog=com.mchange.v2.log.jdk14logging.Jdk14MLog
+#com.mchange.v2.log.MLog=com.mchange.v2.log.FallbackMLog
+#com.mchange.v2.log.NameTransformer=com.mchange.v2.log.PackageNames
+#com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL=ALL
+
+
+#com.mchange.v2.c3p0.VMID=poop
+
+
+
+
+
+
diff --git a/test-properties/log4j.properties b/test-properties/log4j.properties
new file mode 100644
index 0000000..0198201
--- /dev/null
+++ b/test-properties/log4j.properties
@@ -0,0 +1,17 @@
+
+#log4j.rootLogger=ALL,A1
+log4j.rootLogger=INFO,A1
+#log4j.logger.com.mchange.v2.resourcepool=ALL,A1
+#log4j.logger.com.mchange.v2.c3p0.stmt=ALL
+#log4j.logger.com.mchange.v2.c3p0.impl=ALL
+#log4j.logger.com.mchange.v2.c3p0.management=ALL
+#log4j.logger.com.mchange.v2.resourcepool=ALL
+
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+log4j.appender.A1.layout=org.apache.log4j.SimpleLayout
+#log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+
+#log4j.appender.A1.layout.ConversionPattern=%r [%t] %-5p %c %x - %m\n
+#log4j.appender.A1.layout.ConversionPattern=%d %-5p %c [%t] %C %M --> %m%n
+#log4j.appender.A1.layout.ConversionPattern=%-5p %c %C.%M() --> %m%n
diff --git a/test-properties/logging.properties b/test-properties/logging.properties
new file mode 100644
index 0000000..42f01be
--- /dev/null
+++ b/test-properties/logging.properties
@@ -0,0 +1,66 @@
+############################################################
+# Default Logging Configuration File
+#
+# For example java -Djava.util.logging.config.file=myfile
+############################################################
+
+############################################################
+# Global properties
+############################################################
+
+# "handlers" specifies a comma separated list of log Handler
+# classes. These handlers will be installed during VM startup.
+# Note that these classes must be on the system classpath.
+# By default we only configure a ConsoleHandler, which will only
+# show messages at the INFO and above levels.
+handlers=java.util.logging.ConsoleHandler
+
+# To also add the FileHandler, use the following line instead.
+#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
+
+# Default global logging level.
+# This specifies which kinds of events are logged across
+# all loggers. For any given facility this global level
+# can be overriden by a facility specific level
+# Note that the ConsoleHandler also has a separate level
+# setting to limit messages printed to the console.
+
+#.level=ALL
+#.level=FINER
+#.level=OFF
+.level=INFO
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+# default file output is in user's home directory.
+java.util.logging.FileHandler.pattern=%h/java%u.log
+java.util.logging.FileHandler.limit=50000
+java.util.logging.FileHandler.count=1
+java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter
+
+# Limit the message that are printed on the console to INFO and above.
+java.util.logging.ConsoleHandler.level=ALL
+java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+# For example, set the com.xyz.foo logger to only log SEVERE
+# messages:
+
+#com.xyz.foo.level = SEVERE
+#com.mchange.v2.resourcepool.level=INFO
+#com.mchange.v2.resourcepool.level=FINER
+com.mchange.v2.resourcepool.level=ALL
+#com.mchange.v2.c3p0.impl.level=ALL
+#com.mchange.level=FINE
+#com.mchange.level=ALL
+#com.mchange.v2.c3p0.impl.level=ALL
+#com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.level=ALL
+
diff --git a/version.properties b/version.properties
new file mode 100644
index 0000000..5b84063
--- /dev/null
+++ b/version.properties
@@ -0,0 +1,3 @@
+#autogenerated -- do not edit!
+#Mon May 21 15:04:56 EDT 2007
+c3p0.version=0.9.1.2
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/c3p0.git
More information about the pkg-java-commits
mailing list